Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/src/interact-with-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,30 @@ then launch:

This will run `demo()` with an existing model checkpoint.

## Arguments & Configuration

The `drive` tool supports similar CLI arguments as the visualizer to control the environment and rendering. It also reads the `pufferlib/config/ocean/drive.ini` file for default environment settings.

### Command Line Arguments

| Argument | Description | Default |
| :--- | :--- | :--- |
| `--map-name <path>` | Path to the map binary file (e.g., `resources/drive/binaries/training/map_000.bin`). If omitted, picks a random map out of `num_maps` from `map_dir` in `drive.ini`. | Random |
| `--policy-name <path>` | `Path to the policy weights file (.bin).` | `resources/drive/puffer_drive_weights.bin` |
| `--view <mode>` | Selects which views to render: `agent`, `topdown`, or `both`. | `both` |
| `--frame-skip <n>` | Renders every Nth frame to speed up simulation (framerate remains 30fps). | `1` |
| `--num-maps <n>` | Overrides the number of maps to sample from if `--map-name` is not set. | `drive.ini` value |

### Visualization Flags

| Flag | Description |
| :--- | :--- |
| `--show-grid` | Draws the underlying nav-graph/grid on the map. |
| `--obs-only` | Hides objects not currently visible to the agent's sensors (fog of war). |
| `--lasers` | Visualizes the raycast sensor lines from the agent. |
| `--log-trajectories` | Draws the ground-truth "human" expert trajectories as green lines. |
| `--zoom-in` | Zooms the camera mainly on the active region rather than the full map bounds. |

### Controls

**General:**
Expand Down
21 changes: 19 additions & 2 deletions docs/src/simulator.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,28 @@ A high-performance autonomous driving simulator in C with Python bindings.

- `control_vehicles`: Only vehicles
- `control_agents`: All agent types (vehicles, cyclists, pedestrians)
- `control_tracks_to_predict`: WOMD evaluation mode
- `control_wosac`: WOSAC evaluation mode (controls all valid agents ignoring expert flag and start to goal distance)
- `control_sdc_only`: Self-driving car only

> [!NOTE]
> `control_vehicles` filters out agents marked as "expert" and those too close to their goal (<2m). For full WOMD evaluation, use `control_tracks_to_predict`.
> `control_vehicles` filters out agents marked as "expert" and those too close to their goal (<2m). For full WOMD evaluation, use `control_wosac`.

> [!IMPORTANT]
> **Agent Dynamics:** The simulator supports three types of agents:
> 1. **Policy-Controlled:** Stepped by your model's actions.
> 2. **Experts:** Stepped using ground-truth log trajectories.
> 3. **Static:** Remain frozen in place.
>
> In the simulator, agents not selected for policy control will be treated as **Static** by default. To make them follow their **Expert trajectories**, you must set `mark_as_expert=true` for those agents in the jsons. This is critical for `control_sdc_only` to ensure the environment behaves realistically around the policy-controlled agents.
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The term "jsons" should be "JSON files" to maintain consistency with the rest of the documentation. Throughout the codebase, "JSON" is consistently capitalized (e.g., data.md:3, data.md:10, scene-editor.md:13). The phrase "in the jsons" should be replaced with "in the JSON files" for consistency.

Copilot uses AI. Check for mistakes.

### Init modes

- **`create_all_valid`** (Default): Initializes every valid agent present in the map file. This includes policy-controlled agents, experts (if marked), and static agents.

- **`create_only_controlled`**: Initializes **only** the agents that are directly controlled by the policy.

> [!NOTE]
> In `create_only_controlled` mode, the environment will contain **no static or expert agents**. Only the policy-controlled agents will exist.

### Goal behaviors

Expand Down
38 changes: 36 additions & 2 deletions docs/src/visualizer.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,45 @@ bash scripts/build_ocean.sh visualize local

If you need to force a rebuild, remove the cached binary first (`rm ./visualize`).

## Run headless
Launch the visualizer with a virtual display and export an `.mp4`:
## Rendering a Video
Launch the visualizer with a virtual display and export an `.mp4` for the binary scenario:

```bash
xvfb-run -s "-screen 0 1280x720x24" ./visualize
```

Adjust the screen size and color depth as needed. The `xvfb-run` wrapper allows Raylib to render without an attached display, which is convenient for servers and CI jobs.

## Arguments & Configuration

The `visualize` tool supports several CLI arguments to control the rendering output. It also reads the `pufferlib/config/ocean/drive.ini` file for default environment settings(For more details on these settings, refer to [Configuration](simulator.md#configuration)).

### Command Line Arguments

| Argument | Description | Default |
| :--- | :--- | :--- |
| `--map-name <path>` | Path to the map binary file (e.g., `resources/drive/binaries/training/map_000.bin`). If omitted, picks a random map out of `num_maps` from `map_dir` in `drive.ini`. | Random |
| `--policy-name <path>` | Path to the policy weights file (`.bin`). | `resources/drive/puffer_drive_weights.bin` |
| `--view <mode>` | Selects which views to render: `agent`, `topdown`, or `both`. | `both` |
| `--output-agent <path>` | Output filename for agent view video. | `<policy>_agent.mp4` |
| `--output-topdown <path>` | Output filename for top-down view video. | `<policy>_topdown.mp4` |
| `--frame-skip <n>` | Renders every Nth frame to speed up generation (framerate remains 30fps). | `1` |
| `--num-maps <n>` | Overrides the number of maps to sample from if `--map-name` is not set. | `drive.ini` value |

### Visualization Flags

| Flag | Description |
| :--- | :--- |
| `--show-grid` | Draws the underlying nav-graph/grid on the map. |
| `--obs-only` | Hides objects not currently visible to the agent's sensors (fog of war). |
| `--lasers` | Visualizes the raycast sensor lines from the agent. |
| `--log-trajectories` | Draws the ground-truth "human" expert trajectories as green lines. |
| `--zoom-in` | Zooms the camera mainly on the active region rather than the full map bounds. |

### Key `drive.ini` Settings
The visualizer initializes the environment using `pufferlib/config/ocean/drive.ini`. Important settings include:

- `[env] dynamics_model`: `classic` or `jerk`. Must match the trained policy.
- `[env] episode_length`: Duration of the playback. defaults to 91 if set to 0.
- `[env] control_mode`: Determines which agents are active (`control_vehicles` vs `control_sdc_only`).
- `[env] goal_behavior`: Defines agent behavior upon reaching goals (respawn vs stop).
5 changes: 5 additions & 0 deletions docs/theme/extra.css
Original file line number Diff line number Diff line change
Expand Up @@ -463,3 +463,8 @@ blockquote {
margin: 1rem 0;
border-radius: 0 8px 8px 0;
}

/* Fix table visibility - remove alternating row colors */
table tr:nth-child(2n) {
background-color: transparent !important;
}
163 changes: 136 additions & 27 deletions pufferlib/ocean/drive/drive.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include "drivenet.h"
#include "error.h"
#include "libgen.h"
#include "../env_config.h"
#include <string.h>

// Use this test if the network changes to ensure that the forward pass
Expand Down Expand Up @@ -31,37 +34,58 @@ void test_drivenet() {
free(weights);
}

void demo() {
int demo(const char *map_name, const char *policy_name, int show_grid, int obs_only, int lasers, int show_human_logs,
int frame_skip, const char *view_mode, const char *output_topdown, const char *output_agent, int num_maps,
int zoom_in) {

// Note: The settings below are hardcoded for demo purposes. Since the policy was
// trained with these exact settings, that changing them may lead to
// weird behavior.
// Parse configuration from INI file
env_init_config conf = {0};
const char *ini_file = "pufferlib/config/ocean/drive.ini";
if (ini_parse(ini_file, handler, &conf) < 0) {
fprintf(stderr, "Error: Could not load %s. Cannot determine environment configuration.\n", ini_file);
return -1;
}

char map_buffer[100];
if (map_name == NULL) {
srand(time(NULL));
int random_map = rand() % num_maps;
sprintf(map_buffer, "%s/map_%03d.bin", conf.map_dir, random_map);
map_name = map_buffer;
}

// Initialize environment with all config values from INI [env] section
Drive env = {
.human_agent_idx = 0,
.action_type = 0, // Discrete
.dynamics_model = CLASSIC, // Classic dynamics
.reward_vehicle_collision = -1.0f,
.reward_offroad_collision = -1.0f,
.reward_goal = 1.0f,
.reward_goal_post_respawn = 0.25f,
.goal_radius = 2.0f,
.goal_behavior = 1,
.goal_target_distance = 30.0f,
.goal_speed = 10.0f,
.dt = 0.1f,
.episode_length = 300,
.termination_mode = 0,
.collision_behavior = 0,
.offroad_behavior = 0,
.init_steps = 0,
.init_mode = 0,
.control_mode = 0,
.map_name = "resources/drive/map_town_02_carla.bin",
.action_type = conf.action_type,
.dynamics_model = conf.dynamics_model,
.reward_vehicle_collision = conf.reward_vehicle_collision,
.reward_offroad_collision = conf.reward_offroad_collision,
.reward_goal = conf.reward_goal,
.reward_goal_post_respawn = conf.reward_goal_post_respawn,
.goal_radius = conf.goal_radius,
.goal_behavior = conf.goal_behavior,
.goal_target_distance = conf.goal_target_distance,
.goal_speed = conf.goal_speed,
.dt = conf.dt,
.episode_length = conf.episode_length,
.termination_mode = conf.termination_mode,
.collision_behavior = conf.collision_behavior,
.offroad_behavior = conf.offroad_behavior,
.init_steps = conf.init_steps,
.init_mode = conf.init_mode,
.control_mode = conf.control_mode,
.map_name = (char *)map_name,
};
allocate(&env);
if (env.active_agent_count == 0) {
fprintf(stderr, "Error: No active agents found in map '%s' with init_mode=%d. Cannot run demo.\n", env.map_name,
conf.init_mode);
free_allocated(&env);
return -1;
}
c_reset(&env);
c_render(&env);
Weights *weights = load_weights("resources/drive/puffer_drive_weights_carla_town12.bin");
Weights *weights = load_weights((char *)policy_name);
DriveNet *net = init_drivenet(weights, env.active_agent_count, env.dynamics_model);

int accel_delta = 2;
Expand Down Expand Up @@ -134,6 +158,7 @@ void demo() {
free_allocated(&env);
free_drivenet(net);
free(weights);
return 0;
}

void performance_test() {
Expand Down Expand Up @@ -177,9 +202,93 @@ void performance_test() {
free_allocated(&env);
}

int main() {
int main(int argc, char *argv[]) {
// Visualization-only parameters (not in [env] section)
int show_grid = 0;
int obs_only = 0;
int lasers = 0;
int show_human_logs = 0;
int frame_skip = 1;
int zoom_in = 0;
const char *view_mode = "both";

// File paths and num_maps (not in [env] section)
const char *map_name = NULL;
const char *policy_name = "resources/drive/puffer_drive_weights.bin";
const char *output_topdown = NULL;
const char *output_agent = NULL;
int num_maps = 1;

// Parse command line arguments
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--show-grid") == 0) {
show_grid = 1;
} else if (strcmp(argv[i], "--obs-only") == 0) {
obs_only = 1;
} else if (strcmp(argv[i], "--lasers") == 0) {
lasers = 1;
} else if (strcmp(argv[i], "--log-trajectories") == 0) {
show_human_logs = 1;
} else if (strcmp(argv[i], "--frame-skip") == 0) {
if (i + 1 < argc) {
frame_skip = atoi(argv[i + 1]);
i++;
if (frame_skip <= 0) {
frame_skip = 1;
}
}
} else if (strcmp(argv[i], "--zoom-in") == 0) {
zoom_in = 1;
} else if (strcmp(argv[i], "--view") == 0) {
if (i + 1 < argc) {
view_mode = argv[i + 1];
i++;
if (strcmp(view_mode, "both") != 0 && strcmp(view_mode, "topdown") != 0 &&
strcmp(view_mode, "agent") != 0) {
fprintf(stderr, "Error: --view must be 'both', 'topdown', or 'agent'\n");
return 1;
}
} else {
fprintf(stderr, "Error: --view option requires a value (both/topdown/agent)\n");
return 1;
}
} else if (strcmp(argv[i], "--map-name") == 0) {
if (i + 1 < argc) {
map_name = argv[i + 1];
i++;
} else {
fprintf(stderr, "Error: --map-name option requires a map file path\n");
return 1;
}
} else if (strcmp(argv[i], "--policy-name") == 0) {
if (i + 1 < argc) {
policy_name = argv[i + 1];
i++;
} else {
fprintf(stderr, "Error: --policy-name option requires a policy file path\n");
return 1;
}
} else if (strcmp(argv[i], "--output-topdown") == 0) {
if (i + 1 < argc) {
output_topdown = argv[i + 1];
i++;
}
} else if (strcmp(argv[i], "--output-agent") == 0) {
if (i + 1 < argc) {
output_agent = argv[i + 1];
i++;
}
} else if (strcmp(argv[i], "--num-maps") == 0) {
if (i + 1 < argc) {
num_maps = atoi(argv[i + 1]);
i++;
}
}
}

// performance_test();
demo();
demo(map_name, policy_name, show_grid, obs_only, lasers, show_human_logs, frame_skip, view_mode, output_topdown,
output_agent, num_maps, zoom_in);
// test_drivenet();
return 0;
}
6 changes: 3 additions & 3 deletions pufferlib/ocean/drive/drive.h
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ int collision_check(Drive *env, int agent_idx) {
int index = -1;
if (i < env->active_agent_count) {
index = env->active_agent_indices[i];
} else if (i < env->num_actors) {
} else if (i < env->num_actors && env->static_agent_count > 0) {
index = env->static_agent_indices[i - env->active_agent_count];
}
if (index == -1)
Expand Down Expand Up @@ -1791,7 +1791,7 @@ void compute_observations(Drive *env) {
int index = -1;
if (j < env->active_agent_count) {
index = env->active_agent_indices[j];
} else if (j < env->num_actors) {
} else if (j < env->num_actors && env->static_agent_count > 0) {
index = env->static_agent_indices[j - env->active_agent_count];
}
if (index == -1)
Expand Down Expand Up @@ -2803,7 +2803,7 @@ void c_render(Drive *env) {
handle_camera_controls(env->client);
draw_scene(env, client, 0, 0, 0, 0);

if (IsKeyPressed(KEY_TAB)) {
if (IsKeyPressed(KEY_TAB) && env->active_agent_count > 0) {
env->human_agent_idx = (env->human_agent_idx + 1) % env->active_agent_count;
}

Expand Down