From 7089c35d094ea57594612962c38e69b44f7686bf Mon Sep 17 00:00:00 2001 From: rakadu1 Date: Tue, 31 Mar 2026 14:01:55 +0530 Subject: [PATCH] refactor: generic orchestrator with CLI param injection - Added --params CLI flag for dynamic scenario tuning - Implemented orchestration priority chain: CLI > Scenario > Global Config - Added world.tick() before setup to ensure physics synchronization - Implemented robust ego 'Spawn-and-Move' strategy for absolute coordinates - Cleaned up config.py from scenario-specific constants --- config.py | 18 ++------- src/main.py | 109 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 76 insertions(+), 51 deletions(-) diff --git a/config.py b/config.py index 95c4181..0cce269 100644 --- a/config.py +++ b/config.py @@ -22,18 +22,8 @@ LIDAR_ROTATION_FREQUENCY = FPS MAX_FRAMES = 200 # ----------------------------------------------------------------------- -# Scenario defaults (scenarios read these via getattr(config, KEY, default)) +# Global Defaults # ----------------------------------------------------------------------- -DEFAULT_SCENARIO = "braking" - -# BrakingScenario -SCENARIO_LEAD_DISTANCE = 25 # metres ahead of ego for lead vehicle -SCENARIO_BRAKE_FRAME = 80 # frame on which the lead vehicle emergency-brakes - -# CutInScenario -SCENARIO_CUTIN_DISTANCE = 15 # metres ahead of ego for NPC in adjacent lane -SCENARIO_CUTIN_FRAME = 60 # frame on which NPC is forced to change lane - -# ObstacleScenario -SCENARIO_OBSTACLE_DISTANCE = 30 # metres ahead of ego for static prop -SCENARIO_OBSTACLE_PROP = "static.prop.trafficcone01" \ No newline at end of file +DEFAULT_SCENARIO = "braking" +DEFAULT_EGO_MODEL = "vehicle.tesla.model3" +DEFAULT_WEATHER = "ClearNoon" \ No newline at end of file diff --git a/src/main.py b/src/main.py index 56f05e3..b78f01d 100644 --- a/src/main.py +++ b/src/main.py @@ -59,8 +59,14 @@ def parse_args(): parser.add_argument( "--weather", "-w", type=str, - default="ClearNoon", - help="Weather preset (ClearNoon, SoftRain, Rain, Sunset, Night, Wet, etc.)" + default=None, + help="Weather preset (ClearNoon, Rain, etc.). If omitted, scenario or config default is used." + ) + parser.add_argument( + "--params", + type=str, + default=None, + help="Scenario parameters as key=val pairs, e.g. 'BRAKE_FRAME=100,SPEED=50'" ) parser.add_argument( "--list-scenarios", "-l", @@ -88,12 +94,31 @@ def main(): from sensors import SensorManager from recorder import Recorder - max_frames = args.frames if args.frames is not None else config.MAX_FRAMES - # ------------------------------------------------------------------ - # 1. Load scenario (no CARLA needed yet) + # 1. Load & Parameterize scenario # ------------------------------------------------------------------ scenario = load_scenario(args.scenario) + + if args.params: + # Parse "KEY=VAL,KEY2=VAL2" + try: + # Strip outer quotes and spaces before split-processing + p_str = args.params.strip().strip('"').strip("'") + p_dict = {} + for item in p_str.split(","): + if "=" in item: + k, v = item.split("=", 1) + p_dict[k.strip().strip('"').strip("'")] = v.strip().strip('"').strip("'") + scenario.apply_parameters(p_dict) + except Exception as e: + print(f"[ERROR] Failed to parse --params '{args.params}': {e}") + + # Determine simulation duration (CLI > Scenario > Config) + max_frames = args.frames + if max_frames is None: + max_frames = scenario.max_frames + if max_frames is None: + max_frames = config.MAX_FRAMES # ------------------------------------------------------------------ # 2. Connect to CARLA @@ -101,26 +126,19 @@ def main(): client = carla.Client("localhost", 2000) client.set_timeout(10.0) - world = client.get_world() + world = client.get_world() + + # Apply Weather (CLI > Scenario > Config) + from utils import get_weather_preset - # Apply Weather - weather_presets = { - "Clear": carla.WeatherParameters.ClearNoon, - "Cloudy": carla.WeatherParameters.CloudyNoon, - "Wet": carla.WeatherParameters.WetNoon, - "SoftRain": carla.WeatherParameters.SoftRainNoon, - "Rain": carla.WeatherParameters.HardRainNoon, - "Sunset": carla.WeatherParameters.ClearSunset, - "Night": carla.WeatherParameters(sun_altitude_angle=-90.0, cloudiness=0.0, precipitation=10.0, precipitation_deposits=10.0, wind_intensity=0.0, fog_density=0.0, fog_distance=0.0, wetness=0.0) - } - # Match partial string (e.g. "Rain" matches "HardRainNoon") - chosen_weather = carla.WeatherParameters.ClearNoon - for k, v in weather_presets.items(): - if k.lower() in args.weather.lower(): - chosen_weather = v - break - world.set_weather(chosen_weather) - print(f"[INFO] Applied weather: {args.weather}") + weather_name = args.weather + if weather_name is None: + weather_name = scenario.weather + if weather_name is None: + weather_name = config.DEFAULT_WEATHER + + world.set_weather(get_weather_preset(weather_name)) + print(f"[INFO] Applied weather: {weather_name}") blueprint_library = world.get_blueprint_library() map_ = world.get_map() @@ -148,32 +166,46 @@ def main(): # ------------------------------------------------------------------ # 4. Spawn ego vehicle # ------------------------------------------------------------------ - vehicle_bp = blueprint_library.filter("model3")[0] + vehicle_bp = blueprint_library.filter(config.DEFAULT_EGO_MODEL)[0] spawn_points = map_.get_spawn_points() ego_vehicle = None - # Try specified spawn point first if provided - if args.spawn_point is not None: - if args.spawn_point < len(spawn_points): - sp = spawn_points[args.spawn_point] - ego_vehicle = world.try_spawn_actor(vehicle_bp, sp) + # Priority: CLI Argument > Scenario Preference + sp_req = args.spawn_point + if sp_req is None: + sp_req = scenario.ego_spawn_point + + if sp_req is not None: + if isinstance(sp_req, int): + if sp_req < len(spawn_points): + sp = spawn_points[sp_req] + ego_vehicle = world.try_spawn_actor(vehicle_bp, sp) + if ego_vehicle: + print(f"[INFO] Ego spawned at requested point index {sp_req}") + else: + print(f"[WARN] Spawn index {sp_req} out of range (max {len(spawn_points)-1})") + elif isinstance(sp_req, carla.Transform): + ego_vehicle = world.try_spawn_actor(vehicle_bp, sp_req) if ego_vehicle: - print(f"[INFO] Ego spawned at requested spawn point index {args.spawn_point}") - else: - print(f"[WARN] Spawn point index {args.spawn_point} out of range (max {len(spawn_points)-1})") - - # Fallback to searching if not specified or failed + print(f"[INFO] Ego spawned at requested absolute transform: {sp_req.location}") + + # Fallback search if no request was made if ego_vehicle is None: for i, sp in enumerate(spawn_points): ego_vehicle = world.try_spawn_actor(vehicle_bp, sp) if ego_vehicle: - print(f"[INFO] Ego spawned at spawn point index {i}") + print(f"[INFO] Ego spawned at fallback spawn point index {i}") break - + if ego_vehicle is None: raise RuntimeError("Failed to spawn ego vehicle") + # If scenario requested a specific absolute transform, move the spawned vehicle there + if sp_req is not None and isinstance(sp_req, carla.Transform): + ego_vehicle.set_transform(sp_req) + print(f"[INFO] Ego moved to requested absolute transform: {sp_req.location}") + ego_vehicle.set_autopilot(True, traffic_manager.get_port()) # Notify scenario of ego spawn (optional hook) @@ -194,6 +226,9 @@ def main(): # ------------------------------------------------------------------ # 6. Scenario setup # ------------------------------------------------------------------ + # Tick once to settle ego physics before scenario setup starts calculating waypoints + world.tick() + scenario.setup(world, ego_vehicle, traffic_manager) print(f"[INFO] Running scenario: '{scenario.name}' for {max_frames} frames")