diff --git a/docs/src/interact-with-agents.md b/docs/src/interact-with-agents.md index ade1f3028..671f3bd53 100644 --- a/docs/src/interact-with-agents.md +++ b/docs/src/interact-with-agents.md @@ -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 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 to the policy weights file (.bin).` | `resources/drive/puffer_drive_weights.bin` | +| `--view ` | Selects which views to render: `agent`, `topdown`, or `both`. | `both` | +| `--frame-skip ` | Renders every Nth frame to speed up simulation (framerate remains 30fps). | `1` | +| `--num-maps ` | 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:** diff --git a/docs/src/simulator.md b/docs/src/simulator.md index a500840bb..96e37247a 100644 --- a/docs/src/simulator.md +++ b/docs/src/simulator.md @@ -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. + +### 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 diff --git a/docs/src/visualizer.md b/docs/src/visualizer.md index c6b73c90e..c6c6bc855 100644 --- a/docs/src/visualizer.md +++ b/docs/src/visualizer.md @@ -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 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 to the policy weights file (`.bin`). | `resources/drive/puffer_drive_weights.bin` | +| `--view ` | Selects which views to render: `agent`, `topdown`, or `both`. | `both` | +| `--output-agent ` | Output filename for agent view video. | `_agent.mp4` | +| `--output-topdown ` | Output filename for top-down view video. | `_topdown.mp4` | +| `--frame-skip ` | Renders every Nth frame to speed up generation (framerate remains 30fps). | `1` | +| `--num-maps ` | 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). diff --git a/docs/theme/extra.css b/docs/theme/extra.css index cb90a97ed..d593693e3 100644 --- a/docs/theme/extra.css +++ b/docs/theme/extra.css @@ -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; +} diff --git a/pufferlib/ocean/drive/drive.c b/pufferlib/ocean/drive/drive.c index 8bd3c7388..7a714ac4a 100644 --- a/pufferlib/ocean/drive/drive.c +++ b/pufferlib/ocean/drive/drive.c @@ -1,4 +1,7 @@ #include "drivenet.h" +#include "error.h" +#include "libgen.h" +#include "../env_config.h" #include // Use this test if the network changes to ensure that the forward pass @@ -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; @@ -134,6 +158,7 @@ void demo() { free_allocated(&env); free_drivenet(net); free(weights); + return 0; } void performance_test() { @@ -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; } diff --git a/pufferlib/ocean/drive/drive.h b/pufferlib/ocean/drive/drive.h index 4df3bf021..d19f363f0 100644 --- a/pufferlib/ocean/drive/drive.h +++ b/pufferlib/ocean/drive/drive.h @@ -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) @@ -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) @@ -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; }