From c5f2eeb42bc6363b1efe21e9bd68089a59337e69 Mon Sep 17 00:00:00 2001 From: rakadu1 Date: Tue, 31 Mar 2026 14:05:01 +0530 Subject: [PATCH] feat: scenario-centric migration for Town10HD_Opt - Updated braking, cutin, and obstacle scenarios with class-held constants - Added deterministic ego_spawn_point (Showcase P2) for all scenarios - Added 0.5m Z-lift to NPC spawns to prevent ground clipping in Town10 - Refactored showcase.py to use the new standardized ego_spawn_point property --- scenarios/braking.py | 24 ++++++++++++++++-------- scenarios/cutin.py | 25 +++++++++++++++++-------- scenarios/obstacle.py | 13 ++++++++----- scenarios/showcase.py | 8 ++++++-- 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/scenarios/braking.py b/scenarios/braking.py index 13de74c..33094cb 100644 --- a/scenarios/braking.py +++ b/scenarios/braking.py @@ -15,7 +15,6 @@ import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import carla -import config from scenarios.base import ScenarioBase @@ -29,9 +28,9 @@ class BrakingScenario(ScenarioBase): Frame BRAKE_FRAME+ : Lead vehicle applies emergency stop. """ - LEAD_DISTANCE_M = getattr(config, "SCENARIO_LEAD_DISTANCE", 25) - BRAKE_FRAME = getattr(config, "SCENARIO_BRAKE_FRAME", 80) - CRUISE_SPEED = 30 # km/h, set via TM percentage of speed limit + LEAD_DISTANCE_M = 25 # metres ahead of ego for lead vehicle + BRAKE_FRAME = 80 # frame on which the lead vehicle emergency-brakes + CRUISE_SPEED = 30 # km/h, set via TM percentage of speed limit def __init__(self): super().__init__() @@ -46,6 +45,11 @@ class BrakingScenario(ScenarioBase): def name(self) -> str: return "braking" + @property + def ego_spawn_point(self): + # Using the known-working Town10 intersection point + return carla.Transform(carla.Location(x=107.412, y=45.309, z=0.5), carla.Rotation(yaw=-87.7)) + def setup(self, world, ego_vehicle, traffic_manager) -> None: self._world = world self._ego = ego_vehicle @@ -67,11 +71,15 @@ class BrakingScenario(ScenarioBase): "Ensure ego is on a driveable lane." ) - self._lead_vehicle = world.try_spawn_actor(lead_bp, spawn_wp.transform) + # Lift actor slightly to avoid ground clipping + spawn_transform = spawn_wp.transform + spawn_transform.location.z += 0.5 + + self._lead_vehicle = world.try_spawn_actor(lead_bp, spawn_transform) if self._lead_vehicle is None: raise RuntimeError( "[BrakingScenario] Failed to spawn lead vehicle at waypoint " - f"{spawn_wp.transform.location}. Spot may be occupied." + f"{spawn_transform.location}. Spot may be occupied." ) self._actors.append(self._lead_vehicle) @@ -90,7 +98,7 @@ class BrakingScenario(ScenarioBase): ) def step(self, frame_count: int, ego_vehicle, pbar=None) -> None: - if frame == self.BRAKE_FRAME and not self._braked: + if frame_count == self.BRAKE_FRAME and not self._braked: if self._lead_vehicle and self._lead_vehicle.is_alive: # Disengage TM autopilot and apply full brake self._lead_vehicle.set_autopilot(False) @@ -100,7 +108,7 @@ class BrakingScenario(ScenarioBase): control.hand_brake = True self._lead_vehicle.apply_control(control) self._braked = True - print(f"[{self.name}] EMERGENCY BRAKE applied at frame {frame}.") + print(f"[{self.name}] EMERGENCY BRAKE applied at frame {frame_count}.") def cleanup(self) -> None: self._destroy_actors() diff --git a/scenarios/cutin.py b/scenarios/cutin.py index 0b9b12a..414246c 100644 --- a/scenarios/cutin.py +++ b/scenarios/cutin.py @@ -13,7 +13,6 @@ import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -import config from scenarios.base import ScenarioBase @@ -27,9 +26,9 @@ class CutInScenario(ScenarioBase): Frame CUTIN_FRAME+ : TM forced lane change into ego lane. """ - NPC_DISTANCE_M = getattr(config, "SCENARIO_CUTIN_DISTANCE", 15) - CUTIN_FRAME = getattr(config, "SCENARIO_CUTIN_FRAME", 60) - LANE_OFFSET = -1 # -1 = left lane, 1 = right lane + NPC_DISTANCE_M = 15 # metres ahead of ego for NPC in adjacent lane + CUTIN_FRAME = 60 # frame on which NPC is forced to change lane + LANE_OFFSET = -1 # -1 = left lane, 1 = right lane def __init__(self): super().__init__() @@ -44,6 +43,12 @@ class CutInScenario(ScenarioBase): def name(self) -> str: return "cutin" + @property + def ego_spawn_point(self): + # Working Town10 intersection point (Southbound) + import carla + return carla.Transform(carla.Location(x=107.412, y=45.309, z=0.5), carla.Rotation(yaw=-87.7)) + def setup(self, world, ego_vehicle, traffic_manager) -> None: self._world = world self._ego = ego_vehicle @@ -66,11 +71,15 @@ class CutInScenario(ScenarioBase): "Try a map with multiple lanes." ) - self._npc = world.try_spawn_actor(npc_bp, spawn_wp.transform) + # Lift actor slightly to avoid ground clipping + spawn_transform = spawn_wp.transform + spawn_transform.location.z += 0.5 + + self._npc = world.try_spawn_actor(npc_bp, spawn_transform) if self._npc is None: raise RuntimeError( "[CutInScenario] Failed to spawn NPC at " - f"{spawn_wp.transform.location}. Spot may be occupied." + f"{spawn_transform.location}. Spot may be occupied." ) self._actors.append(self._npc) @@ -85,13 +94,13 @@ class CutInScenario(ScenarioBase): ) def step(self, frame_count: int, ego_vehicle, pbar=None) -> None: - if frame == self.CUTIN_FRAME and not self._cut_triggered: + if frame_count == self.CUTIN_FRAME and not self._cut_triggered: if self._npc and self._npc.is_alive: # force_lane_change: True = left, False = right force_left = (self.LANE_OFFSET < 0) self._tm.force_lane_change(self._npc, force_left) self._cut_triggered = True - print(f"[{self.name}] Lane change triggered at frame {frame}.") + print(f"[{self.name}] Lane change triggered at frame {frame_count}.") def cleanup(self) -> None: self._destroy_actors() diff --git a/scenarios/obstacle.py b/scenarios/obstacle.py index 58f89e6..6b51b3e 100644 --- a/scenarios/obstacle.py +++ b/scenarios/obstacle.py @@ -13,7 +13,6 @@ import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -import config from scenarios.base import ScenarioBase @@ -25,11 +24,9 @@ class ObstacleScenario(ScenarioBase): autopilot and must navigate around or brake for the obstacle. """ - OBSTACLE_DISTANCE_M = getattr(config, "SCENARIO_OBSTACLE_DISTANCE", 30) + OBSTACLE_DISTANCE_M = 30 # metres ahead of ego for static prop # CARLA blueprint name for the static prop - PROP_BLUEPRINT = getattr( - config, "SCENARIO_OBSTACLE_PROP", "static.prop.trafficcone01" - ) + PROP_BLUEPRINT = "static.prop.trafficcone01" def __init__(self): super().__init__() @@ -43,6 +40,12 @@ class ObstacleScenario(ScenarioBase): def name(self) -> str: return "obstacle" + @property + def ego_spawn_point(self): + # Working Town10 intersection point (Southbound) + import carla + return carla.Transform(carla.Location(x=107.412, y=45.309, z=0.5), carla.Rotation(yaw=-87.7)) + def setup(self, world, ego_vehicle, traffic_manager) -> None: self._world = world self._ego = ego_vehicle diff --git a/scenarios/showcase.py b/scenarios/showcase.py index a53f35c..661add8 100644 --- a/scenarios/showcase.py +++ b/scenarios/showcase.py @@ -16,6 +16,11 @@ class ShowcaseScenario(ScenarioBase): def name(self): return "showcase" + @property + def ego_spawn_point(self): + # Working Town10 intersection point (Southbound) + return carla.Transform(carla.Location(x=107.412, y=45.309, z=0.5), carla.Rotation(yaw=-87.7)) + def setup(self, world, ego_vehicle, traffic_manager) -> None: """ Step 11: Deep Intersection Guidance for Ego. @@ -38,8 +43,7 @@ class ShowcaseScenario(ScenarioBase): self.EGO_MID = carla.Location(x=107.412, y=32.0, z=0.5) self._ego_reached_mid = False - # 1. Setup Ego - self._ego.set_transform(self.P2) + # 1. Setup Ego (Already spawned at ego_spawn_point by main.py) self._ego.set_autopilot(False) # 2. Spawn NPC