diff --git a/pufferlib/config/ocean/drive.ini b/pufferlib/config/ocean/drive.ini index 571b2ea582..4c14e9baec 100644 --- a/pufferlib/config/ocean/drive.ini +++ b/pufferlib/config/ocean/drive.ini @@ -54,6 +54,10 @@ collision_behavior = 1 offroad_behavior = 1 ; Traffic light behavior - options: 0 - Ignore, 1 - Stop, 2 - Remove traffic_light_behavior = 1 +; Goal placement (gigaflow) - options: 0 - route (forward waypoints), 1 - random (anywhere on map) +goal_placement = 0 +; Share static map geometry (roads/grid/lane-graph) across envs using the same map - 0 off, 1 on +use_map_cache = 0 ; Number of steps before reset scenario_length = 1280 ; Frequency of resampling scenario (in steps), 0 to disable @@ -270,6 +274,9 @@ eval.verify_coverage = true [eval.validation_replay] inherits = "validation_defaults" +; declared explicitly (same as the inherited default) so it can be toggled from +; the CLI, e.g. --eval.validation-replay.enabled 0 +enabled = true type = "multi_scenario" render = true render_backend = "triage_html" @@ -298,6 +305,33 @@ env.resample_frequency = 500 eval.render_num_scenarios = 8 eval.render_max_steps = 300 +; Manual obs_html render of the single-agent speed-run task (Town02, one agent, +; random goals anywhere, lights off). enabled=false so it never runs inline during +; training; invoke standalone, e.g.: +; puffer eval puffer_drive --evaluator single_agent_obs --load-model-path +[eval.single_agent_obs] +inherits = "validation_gigaflow" +enabled = false +type = "multi_scenario" +render = true +render_backend = "obs_html" +env.map_dir = "pufferlib/resources/drive/binaries/carla_town02" +env.num_maps = 1 +env.num_agents = 8 +env.min_agents_per_env = 1 +env.max_agents_per_env = 1 +env.goal_placement = 1 +env.traffic_light_behavior = 0 +env.max_traffic_control_observations = 0 +env.num_target_waypoints = 1 +env.min_waypoint_spacing = 0 +env.max_waypoint_spacing = 1000000 +env.max_goal_position = 1000 +env.scenario_length = 1280 +eval.num_scenarios = 8 +eval.render_num_scenarios = 8 +eval.render_max_steps = 1280 + ; --------------------------------------------------------------------------- ; Driving-behaviour evaluation: nuPlan scenes labeled by scene type. Each ; behavior is one [eval.behaviors_*] section. All inherit from the template diff --git a/pufferlib/ocean/drive/binding.c b/pufferlib/ocean/drive/binding.c index f67243a32d..9b5ce3d536 100644 --- a/pufferlib/ocean/drive/binding.c +++ b/pufferlib/ocean/drive/binding.c @@ -1796,6 +1796,8 @@ static int my_init(Env *env, PyObject *args, PyObject *kwargs) { env->collision_behavior = (int) unpack(kwargs, "collision_behavior"); env->offroad_behavior = (int) unpack(kwargs, "offroad_behavior"); env->traffic_light_behavior = (int) unpack(kwargs, "traffic_light_behavior"); + env->goal_placement = (int) unpack(kwargs, "goal_placement"); + env->use_map_cache = (int) unpack(kwargs, "use_map_cache"); env->emit_completed_episodes = (int) unpack(kwargs, "emit_completed_episodes"); env->next_episode_index = 0; env->completed_episodes_count = 0; diff --git a/pufferlib/ocean/drive/drive.h b/pufferlib/ocean/drive/drive.h index 329e1bdf76..8786343256 100644 --- a/pufferlib/ocean/drive/drive.h +++ b/pufferlib/ocean/drive/drive.h @@ -121,6 +121,10 @@ #define TARGET_STATIC 0 #define TARGET_DYNAMIC 1 +// GOAL_PLACEMENT modes (controls how gigaflow goals are sampled) +#define GOAL_PLACEMENT_ROUTE 0 // goals lie ahead along a forward random-walk route +#define GOAL_PLACEMENT_RANDOM 1 // goals are random drivable points anywhere on the map + // Observation feature counts #define EGO_FEATURES_CLASSIC 8 #define EGO_FEATURES_JERK 10 @@ -284,6 +288,30 @@ struct GridMap { int num_drivable_grid_cell; }; +// Static, read-only map geometry shared across environments that load the same +// map file when use_map_cache is set. Holds only data that is never mutated after +// load: road geometry, the spatial grid (cells + neighbor cache), and the lane +// graph. Per-env mutable data (agents, traffic-light states) is never shared. +// Reference-counted; freed when the last borrowing env in the owning process +// closes. See init() / c_close(). +struct SharedMapData { + char *map_name; + RoadMapElement *road_elements; + int num_road_elements; + GridMap *grid_map; + int *neighbor_offsets; + struct LaneGraph lane_graph; + int ref_count; +}; + +// Per-process map cache. Built lazily in init(). g_map_cache_pid stamps the +// process that built it: a forked worker inherits these pointers via copy-on-write +// and must never free them (it would corrupt the parent's heap), so the free path +// in c_close is guarded by getpid() == g_map_cache_pid. +static struct SharedMapData **g_map_cache = NULL; +static int g_map_cache_count = 0; +static pid_t g_map_cache_pid = 0; + struct Drive { Client *client; // Render mode: RENDER_WINDOW (0) for interactive viewer, RENDER_HEADLESS (1) @@ -354,6 +382,9 @@ struct Drive { int collision_behavior; // 0 = none, 1=stop, 2 = remove int offroad_behavior; // 0 = none, 1=stop, 2 = remove int traffic_light_behavior; // 0 = none, 1=stop, 2 = remove + int goal_placement; // 0 = route (forward waypoints), 1 = random (anywhere on map) + int use_map_cache; // 0 = each env owns its map copy, 1 = share static geometry across envs + struct SharedMapData *shared_map; // non-NULL when this env borrows cached geometry // Metadata fields char scenario_id[128]; char dataset_name[32]; @@ -1862,10 +1893,95 @@ static int compute_new_route(Drive *env, int agent_idx, int current_lane_idx) { return 1; // Success } +// Place each target waypoint at a random drivable point whose distance from the +// agent falls in [min_waypoint_spacing, max_waypoint_spacing]. With a very large +// max this is effectively "anywhere on the map"; a small max keeps goals nearby. +// Goals can land in any direction, including behind the agent. The route and path +// are still built at spawn (used for lane observations and progression); only the +// goal positions are overridden here. +static void compute_goals_random(Drive *env, int agent_idx) { + Agent *agent = &env->agents[agent_idx]; + + int num_target_waypoints = env->num_target_waypoints; + if (num_target_waypoints <= 0 || num_target_waypoints > MAX_TARGET_WAYPOINTS) { + num_target_waypoints = MAX_TARGET_WAYPOINTS; + } + + float min_dist_sq = env->min_waypoint_spacing * env->min_waypoint_spacing; + float max_dist_sq = env->max_waypoint_spacing * env->max_waypoint_spacing; + const int MAX_GOAL_ATTEMPTS = 30; + + for (int i = 0; i < num_target_waypoints; i++) { + // Fall back to the agent's own position if no drivable point is sampled at all. + float goal_x = agent->sim_x; + float goal_y = agent->sim_y; + float goal_z = agent->sim_z; + float best_band_miss = -1.0f; // <0: nothing sampled yet; else closest-to-band distance + + for (int attempt = 0; attempt < MAX_GOAL_ATTEMPTS; attempt++) { + int list_idx = rand() % env->grid_map->num_drivable_grid_cell; + int grid_idx = env->grid_map->grid_index_drivable[list_idx]; + + GridMapEntity cell_candidates[MAX_ENTITIES_PER_CELL]; + int candidate_count = 0; + for (int j = 0; j < env->grid_map->cell_entities_count[grid_idx]; j++) { + GridMapEntity entity = env->grid_map->cells[grid_idx][j]; + if (is_drivable_road_lane(env->road_elements[entity.entity_idx].type)) { + cell_candidates[candidate_count++] = entity; + } + } + if (candidate_count == 0) { + continue; + } + + GridMapEntity chosen = cell_candidates[rand() % candidate_count]; + RoadMapElement *lane = &env->road_elements[chosen.entity_idx]; + float cx = lane->x[chosen.geometry_idx]; + float cy = lane->y[chosen.geometry_idx]; + float cz = lane->z[chosen.geometry_idx]; + float dx = cx - agent->sim_x; + float dy = cy - agent->sim_y; + float d2 = dx * dx + dy * dy; + + if (d2 >= min_dist_sq && d2 <= max_dist_sq) { + // Inside the [min_waypoint_spacing, max_waypoint_spacing] band: take it. + goal_x = cx; + goal_y = cy; + goal_z = cz; + break; + } + + // Out of band: keep the point closest to the band so a tight max still + // yields a nearby goal instead of falling back to the agent's position. + float miss = (d2 > max_dist_sq) ? (d2 - max_dist_sq) : (min_dist_sq - d2); + if (best_band_miss < 0.0f || miss < best_band_miss) { + best_band_miss = miss; + goal_x = cx; + goal_y = cy; + goal_z = cz; + } + } + + agent->goal_positions_x[i] = goal_x; + agent->goal_positions_y[i] = goal_y; + agent->goal_positions_z[i] = goal_z; + } + + agent->current_goal_idx = 0; + agent->goal_position_x = agent->goal_positions_x[0]; + agent->goal_position_y = agent->goal_positions_y[0]; + agent->goal_position_z = agent->goal_positions_z[0]; +} + static void compute_goals(Drive *env, int agent_idx) { Agent *agent = &env->agents[agent_idx]; struct Path *path = agent->path; + if (env->goal_placement == GOAL_PLACEMENT_RANDOM) { + compute_goals_random(env, agent_idx); + return; + } + // Validate path exists if (path == NULL || path->num_waypoints == 0) { printf("[GIGAFLOW WARNING] -> Agent %d has no valid path\n", agent_idx); @@ -3358,18 +3474,117 @@ void remove_bad_trajectories(Drive *env) { env->timestep = 0; } +static struct SharedMapData *map_cache_lookup(const char *map_name) { + for (int i = 0; i < g_map_cache_count; i++) { + if (g_map_cache[i] != NULL && strcmp(g_map_cache[i]->map_name, map_name) == 0) { + return g_map_cache[i]; + } + } + return NULL; +} + +static void map_cache_insert(struct SharedMapData *entry) { + if (g_map_cache_pid == 0) { + g_map_cache_pid = getpid(); + } + // Reuse a slot vacated by free_shared_map_data before growing the array, so a + // resample cycle (vec_close frees every entry, then the rebuild re-inserts) + // keeps g_map_cache_count bounded by the number of distinct maps instead of + // appending past NULL holes on every cycle. + for (int i = 0; i < g_map_cache_count; i++) { + if (g_map_cache[i] == NULL) { + g_map_cache[i] = entry; + return; + } + } + g_map_cache = + (struct SharedMapData **) realloc(g_map_cache, (g_map_cache_count + 1) * sizeof(struct SharedMapData *)); + g_map_cache[g_map_cache_count++] = entry; +} + +// Free a shared geometry entry and everything it owns, and clear its cache slot. +// Mirrors the owned-geometry branch of c_close. Only call in the building process. +static void free_shared_map_data(struct SharedMapData *shared) { + if (shared == NULL) { + return; + } + for (int i = 0; i < shared->num_road_elements; i++) { + free_road_element(&shared->road_elements[i]); + } + free(shared->road_elements); + int grid_cell_count = shared->grid_map->grid_cols * shared->grid_map->grid_rows; + for (int grid_index = 0; grid_index < grid_cell_count; grid_index++) { + free(shared->grid_map->cells[grid_index]); + } + free(shared->grid_map->cells); + free(shared->grid_map->cell_entities_count); + free(shared->grid_map->grid_index_drivable); + for (int i = 0; i < grid_cell_count; i++) { + free(shared->grid_map->neighbor_cache_entities[i]); + } + free(shared->grid_map->neighbor_cache_entities); + free(shared->grid_map->neighbor_cache_count); + free(shared->grid_map); + free(shared->neighbor_offsets); + free_lane_graph(&shared->lane_graph); + free(shared->map_name); + for (int i = 0; i < g_map_cache_count; i++) { + if (g_map_cache[i] == shared) { + g_map_cache[i] = NULL; + break; + } + } + free(shared); +} + void init(Drive *env) { env->human_agent_idx = 0; env->timestep = 0; - load_map_binary(env->map_name, env); + env->shared_map = NULL; + + struct SharedMapData *shared = env->use_map_cache ? map_cache_lookup(env->map_name) : NULL; + if (shared != NULL) { + // Cache hit: load only the per-env data (agents, traffic-control elements), + // then discard the freshly-loaded geometry and borrow the shared copy. + load_map_binary(env->map_name, env); + for (int i = 0; i < env->num_road_elements; i++) { + free_road_element(&env->road_elements[i]); + } + free(env->road_elements); + free_lane_graph(&env->lane_graph); + env->road_elements = shared->road_elements; + env->num_road_elements = shared->num_road_elements; + env->grid_map = shared->grid_map; + env->neighbor_offsets = shared->neighbor_offsets; + env->lane_graph = shared->lane_graph; + env->shared_map = shared; + shared->ref_count++; + } else { + // Cache miss (or caching off): load and build the geometry as usual. + load_map_binary(env->map_name, env); + init_grid_map(env); + int vision_half_range = (int) ceilf( + fmaxf(fmaxf(env->road_obs_front_dist, env->road_obs_behind_dist), env->road_obs_side_dist) / GRID_CELL_SIZE); + env->grid_map->vision_range = 2 * vision_half_range + 1; + init_neighbor_offsets(env); + cache_neighbor_offsets(env); + if (env->use_map_cache) { + // Transfer the just-built geometry into a shared, ref-counted entry that + // this env borrows (ref_count starts at 1). + struct SharedMapData *entry = (struct SharedMapData *) calloc(1, sizeof(struct SharedMapData)); + entry->map_name = strdup(env->map_name); + entry->road_elements = env->road_elements; + entry->num_road_elements = env->num_road_elements; + entry->grid_map = env->grid_map; + entry->neighbor_offsets = env->neighbor_offsets; + entry->lane_graph = env->lane_graph; + entry->ref_count = 1; + map_cache_insert(entry); + env->shared_map = entry; + } + } env->road_dropout_enabled = (env->obs_lane_segment_count < env->max_lane_segment_observations) || (env->obs_boundary_segment_count < env->max_boundary_segment_observations); - init_grid_map(env); - int vision_half_range = (int) ceilf( - fmaxf(fmaxf(env->road_obs_front_dist, env->road_obs_behind_dist), env->road_obs_side_dist) / GRID_CELL_SIZE); - env->grid_map->vision_range = 2 * vision_half_range + 1; - init_neighbor_offsets(env); - cache_neighbor_offsets(env); env->logs_capacity = 0; set_active_agents(env); env->logs_capacity = env->active_agent_count; @@ -3442,38 +3657,51 @@ void c_close(Drive *env) { for (int i = 0; i < env->num_total_agents; i++) { free_agent(&env->agents[i]); } - for (int i = 0; i < env->num_road_elements; i++) { - free_road_element(&env->road_elements[i]); - } for (int i = 0; i < env->num_traffic_elements; i++) { free_traffic_element(&env->traffic_elements[i]); } free(env->agents); - free(env->road_elements); free(env->traffic_elements); free(env->active_agent_indices); free(env->logs); - // GridMap cleanup - int grid_cell_count = env->grid_map->grid_cols * env->grid_map->grid_rows; - for (int grid_index = 0; grid_index < grid_cell_count; grid_index++) { - free(env->grid_map->cells[grid_index]); - } - free(env->grid_map->cells); - free(env->grid_map->cell_entities_count); - free(env->grid_map->grid_index_drivable); - free(env->neighbor_offsets); - for (int i = 0; i < grid_cell_count; i++) { - free(env->grid_map->neighbor_cache_entities[i]); + if (env->shared_map != NULL) { + // Geometry is borrowed from the cache: release our reference. Free the + // shared entry only when it is the last reference AND we are the process + // that built it (a forked worker must not free the parent's copy-on-write + // pages — it would corrupt the parent's heap). + env->shared_map->ref_count--; + if (env->shared_map->ref_count <= 0 && g_map_cache_pid == getpid()) { + free_shared_map_data(env->shared_map); + } + env->shared_map = NULL; + } else { + // Geometry is owned by this env: free it. + for (int i = 0; i < env->num_road_elements; i++) { + free_road_element(&env->road_elements[i]); + } + free(env->road_elements); + int grid_cell_count = env->grid_map->grid_cols * env->grid_map->grid_rows; + for (int grid_index = 0; grid_index < grid_cell_count; grid_index++) { + free(env->grid_map->cells[grid_index]); + } + free(env->grid_map->cells); + free(env->grid_map->cell_entities_count); + free(env->grid_map->grid_index_drivable); + free(env->neighbor_offsets); + for (int i = 0; i < grid_cell_count; i++) { + free(env->grid_map->neighbor_cache_entities[i]); + } + free(env->grid_map->neighbor_cache_entities); + free(env->grid_map->neighbor_cache_count); + free(env->grid_map); + free_lane_graph(&env->lane_graph); } - free(env->grid_map->neighbor_cache_entities); - free(env->grid_map->neighbor_cache_count); - free(env->grid_map); + free(env->static_agent_indices); free(env->expert_static_agent_indices); free(env->objects_of_interest); free(env->tracks_to_predict); - free_lane_graph(&env->lane_graph); free(env->map_name); free(env->ini_file); } diff --git a/pufferlib/ocean/drive/drive.py b/pufferlib/ocean/drive/drive.py index d49563ebf6..8fd492ee39 100644 --- a/pufferlib/ocean/drive/drive.py +++ b/pufferlib/ocean/drive/drive.py @@ -47,6 +47,8 @@ def __init__( collision_behavior=0, offroad_behavior=0, traffic_light_behavior=0, + goal_placement=0, + use_map_cache=0, # emit_completed_episodes=True: env emits one summary dict per # completed episode via info (drained from a per-env C-side queue). # capture_compact_replay=True additionally records per-step agent and @@ -146,6 +148,12 @@ def __init__( self.collision_behavior = collision_behavior self.offroad_behavior = offroad_behavior self.traffic_light_behavior = traffic_light_behavior + if goal_placement not in (0, 1): + raise ValueError(f"goal_placement must be 0 (route) or 1 (random). Got: {goal_placement}") + self.goal_placement = goal_placement + if use_map_cache not in (0, 1): + raise ValueError(f"use_map_cache must be 0 (off) or 1 (on). Got: {use_map_cache}") + self.use_map_cache = use_map_cache self.capture_compact_replay = bool(capture_compact_replay) # capture_compact_replay implies emit_completed_episodes, since the # bundle rides on the per-episode summary. @@ -381,6 +389,8 @@ def _env_init_kwargs(self, map_file, max_agents): "collision_behavior": self.collision_behavior, "offroad_behavior": self.offroad_behavior, "traffic_light_behavior": self.traffic_light_behavior, + "goal_placement": self.goal_placement, + "use_map_cache": self.use_map_cache, "emit_completed_episodes": int(self.emit_completed_episodes), "goal_radius": self.goal_radius, "min_waypoint_spacing": self.min_waypoint_spacing, diff --git a/scripts/cluster_configs/single_agent_no_lane_vel.yaml b/scripts/cluster_configs/single_agent_no_lane_vel.yaml new file mode 100644 index 0000000000..7eacccb88a --- /dev/null +++ b/scripts/cluster_configs/single_agent_no_lane_vel.yaml @@ -0,0 +1,64 @@ +# Single-agent "speed run" ablation: lane + velocity reward shaping removed. +# Same as single_agent_speed_run.yaml (Town02, one agent, random goals, lights +# off) but with all lane-keeping and velocity rewards zeroed, so the policy is +# shaped only by goal-reaching + the safety penalties (collision/offroad/comfort). +# Program configs don't inherit, so this duplicates the base; keep the two in sync. +# +# Launch (per-launch knobs like the seed stay on --args): +# source /scratch/$USER/venvs/pufferdrive/bin/activate +# python scripts/submit_cluster.py \ +# --save_dir /scratch/$USER/runs --prefix $(date +%Y-%m-%d)_no_lane_vel \ +# --compute_config scripts/cluster_configs/nyu_greene.yaml \ +# --program_config scripts/cluster_configs/single_agent_no_lane_vel.yaml \ +# --container --heartbeat --account --partition --time 720 \ +# --args train.seed=0:1:2 + +# Environment: single agent per env, Town02 only, shared map cache +env.simulation_mode: gigaflow +env.map_dir: pufferlib/resources/drive/binaries/carla_town02 +env.num_maps: 1 +env.min_agents_per_env: 1 +env.max_agents_per_env: 1 +env.num_agents: 1024 +env.use_map_cache: 1 + +# Goals: one random goal anywhere, no upper distance cap. +env.goal_placement: 1 +env.num_target_waypoints: 1 +env.min_waypoint_spacing: 0 +env.max_waypoint_spacing: 1000000 +env.max_goal_position: 1000 + +# Traffic lights fully off: not observed, not scored, no reward penalty. +env.traffic_light_behavior: 0 +env.max_traffic_control_observations: 0 + +# Ablation: lane-keeping + velocity reward shaping removed (goal + safety only). +env.reward_lane_align: 0 +env.reward_lane_center: 0 +env.reward_vel_align: 0 +env.reward_velocity: 0 +env.reward_overspeed: 0 + +# Training +train.total_timesteps: 1_000_000_000 + +# Disable the nuPlan-based evaluators (keep validation_gigaflow, which is CARLA). +eval.validation_replay.enabled: 0 +eval.behaviors_full_dir.enabled: 0 +eval.behaviors_hard_stop.enabled: 0 +eval.behaviors_highway_straight.enabled: 0 +eval.behaviors_lane_change.enabled: 0 +eval.behaviors_merge.enabled: 0 +eval.behaviors_parked_cars.enabled: 0 +eval.behaviors_roundabout.enabled: 0 +eval.behaviors_stopped_traffic.enabled: 0 +eval.behaviors_traffic_light_green.enabled: 0 +eval.behaviors_traffic_light_stop.enabled: 0 +eval.behaviors_unprotected_left.enabled: 0 +eval.behaviors_unprotected_right.enabled: 0 + +# W&B (group has no space; see single_agent_speed_run.yaml note) +wandb: True +wandb_project: puffer_drive +wandb_group: no_lane_vel diff --git a/scripts/cluster_configs/single_agent_speed_run.yaml b/scripts/cluster_configs/single_agent_speed_run.yaml new file mode 100644 index 0000000000..1916431c9e --- /dev/null +++ b/scripts/cluster_configs/single_agent_speed_run.yaml @@ -0,0 +1,60 @@ +# Single-agent "speed run" training program config (for submit_cluster.py). +# One controlled agent per environment on a single CARLA map (Town02), with many +# parallel environments and goals sampled at random drivable points anywhere on +# the map. Traffic lights are fully disabled (no observation, no metric, no reward +# penalty). Keys here override pufferlib/config/ocean/drive.ini; submit_cluster.py +# converts underscores in each key to dashes for the CLI. +# +# Launch (3 seeds via the colon sweep): +# source /scratch/$USER/venvs/pufferdrive/bin/activate +# python scripts/submit_cluster.py \ +# --save_dir /scratch/$USER/runs --prefix $(date +%Y-%m-%d)_single_agent \ +# --compute_config scripts/cluster_configs/nyu_greene.yaml \ +# --program_config scripts/cluster_configs/single_agent_speed_run.yaml \ +# --container --heartbeat --account --partition --time 720 \ +# --args train.seed=0:1:2 + +# Environment: single agent per env, Town02 only, shared map cache +env.simulation_mode: gigaflow +env.map_dir: pufferlib/resources/drive/binaries/carla_town02 +env.num_maps: 1 +env.min_agents_per_env: 1 +env.max_agents_per_env: 1 +env.num_agents: 1024 +env.use_map_cache: 1 + +# Goals: one random goal anywhere (near or far, any direction incl. behind); +# max_waypoint_spacing huge = no upper distance cap. +env.goal_placement: 1 +env.num_target_waypoints: 1 +env.min_waypoint_spacing: 0 +env.max_waypoint_spacing: 1000000 +env.max_goal_position: 1000 + +# Traffic lights fully off: not observed, not scored, no reward penalty. +env.traffic_light_behavior: 0 +env.max_traffic_control_observations: 0 + +# Training +train.total_timesteps: 1_000_000_000 + +# Disable the nuPlan-based evaluators (keep validation_gigaflow, which is CARLA). +eval.validation_replay.enabled: 0 +eval.behaviors_full_dir.enabled: 0 +eval.behaviors_hard_stop.enabled: 0 +eval.behaviors_highway_straight.enabled: 0 +eval.behaviors_lane_change.enabled: 0 +eval.behaviors_merge.enabled: 0 +eval.behaviors_parked_cars.enabled: 0 +eval.behaviors_roundabout.enabled: 0 +eval.behaviors_stopped_traffic.enabled: 0 +eval.behaviors_traffic_light_green.enabled: 0 +eval.behaviors_traffic_light_stop.enabled: 0 +eval.behaviors_unprotected_left.enabled: 0 +eval.behaviors_unprotected_right.enabled: 0 + +# W&B. Group has no space: submit_cluster.py joins the inner command into a +# bash -c string without quoting arg values, so a space would split the arg. +wandb: True +wandb_project: puffer_drive +wandb_group: Nightly_Test