From 541d27400b252f8c09e15f2ca0edb9ead7649b91 Mon Sep 17 00:00:00 2001 From: rakadu1 Date: Tue, 31 Mar 2026 14:07:45 +0530 Subject: [PATCH] docs: finalize scenario-centric architecture documentation - Updated context.md with new CLI, framework contracts, and Z-axis safety rules - Added .cursorrules to codify PowerShell and environment requirements - Added braking.md deep-dive post-mortem on spawning challenges - Formally marked scenario parameterization as a core feature --- .cursorrules | 25 ++++++++++++++ intel/braking.md | 50 ++++++++++++++++++++++++++++ intel/context.md | 84 +++++++++++++++++++++++++----------------------- 3 files changed, 118 insertions(+), 41 deletions(-) create mode 100644 .cursorrules create mode 100644 intel/braking.md diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..501840e --- /dev/null +++ b/.cursorrules @@ -0,0 +1,25 @@ +# AI Rules for BATL_CARLA_SIM + +This document contains rules and instructions for AI agents working on this project. + +## Simulation Pipeline +- This project uses CARLA 0.9.16 and the Foxglove integration. +- Core logic is in `src/`. +- Scenarios are in `scenarios/`. + +## Environment Rules +- **Terminal**: Use **PowerShell** for all command executions. +- **Python**: Always use the `carla312` conda environment. +- **Activation**: `C:\ProgramData\miniconda3\Scripts\activate.bat carla312` +- **One-Click Runner**: Use `./run.bat` for most tasks as it handles activation and orchestration automatically. + +## Command Execution Rules +- **Direct Execution**: Background command capture works perfectly in PowerShell. You can run `python`, `git`, and `./run.bat` directly. +- **Wait Policy**: Use a moderate `WaitMsBeforeAsync` (e.g. `2000` to `5000`) for non-simulation commands to see immediate output. +- **Example**: `./run.bat --list-scenarios` +- **Example**: `python src/main.py --no-record --frames 10` + +## Project Architecture +- **Scenario-agnostic Core**: All scenario-specific logic must be exclusively in the `scenarios/` directory. +- **Global Config**: Use `config.py` only for global simulation settings (FPS, Sensors, Default Model/Weather). Do NOT add scenario parameters here. +- **Parameter Tuning**: Use the `--params "KEY=VAL"` CLI flag to tune scenarios from the command line. diff --git a/intel/braking.md b/intel/braking.md new file mode 100644 index 0000000..51b120a --- /dev/null +++ b/intel/braking.md @@ -0,0 +1,50 @@ +# Braking Scenario: Hard Stop Evaluation (Deterministic) + +This document provides a technical guide to the **Lead Vehicle Hard Braking** scenario, including its architectural design, common CARLA-specific pitfalls, and the robust fixes implemented to ensure 100% repeatability. + +--- + +## ๐Ÿ—๏ธ 1. Scenario Architecture + +The Braking scenario consists of two main actors on a multi-lane road (default: **Town10HD_Opt**): +1. **Ego Vehicle**: The vehicle under test, tracking a set of target coordinates or following a lead vehicle. +2. **Lead Vehicle**: An NPC spawn 25m ahead on the same lane. + +**Success Metric**: The Ego ADNC stack must detect the lead vehicle's emergency deceleration and brake safely without collision. + +--- + +## ๐ŸŽ๏ธ 2. Critical Spawning Fixes (Lessons Learned) + +During implementation, we identified several subtle CARLA behaviors that led to `RuntimeError: Spot occupied` failures. These are now documented for all future scenario developers. + +### ๐Ÿ›‘ Issue A: Stale Physics (Synchronous Mode Delay) +- **Problem**: When spawning a new actor in **Synchronous Mode**, its location (`get_location()`) often defaults to stale values (like `0,0,0`) until the world's physics engine has processed at least one frame. +- **Impact**: Calculating NPC spawn points based on the Ego's position immediately after spawning resulted in NPCs appearing at the map origin instead of ahead of the Ego. +- **Fix**: Added an explicit **`world.tick()`** call in the core orchestrator immediately after the Ego is spawned and before the scenario's `setup()` method is called. This "settles" the Ego and guarantees accurate coordinate data. + +### ๐Ÿ›‘ Issue B: Ground-Mesh Clipping & Bounding Box Overlap +- **Problem**: CARLA waypoints sit exactly on the road surface (`Z = 0.0`). Spawning a vehicle's center-point at this height causes its collision box (chassis and tires) to overlap with the road mesh. +- **Impact**: CARLA identifies this overlap as an "obstacle" and fails the spawn with a "Spot occupied" error. +- **Fix**: Implemented a mandatory **0.5m Z-offset (lift)** for all NPC spawn transforms. NPCs are spawned slightly in the air and "dropped" onto the road, which is the industry-standard way to ensure collision-free actor placement in CARLA. + +### ๐Ÿ›‘ Issue C: Deterministic "Spawn-and-Move" Pattern +- **Problem**: Directly spawning an actor at an absolute intersection coordinate (`P2` from our showcase) is fragile and frequently fails due to microscopic geometry conflicts in Town10. +- **Fix**: Developed a two-stage **Spawn-and-Move** pattern: + 1. Spawn the Ego at a "safe" map spawn index (provided by `map.get_spawn_points()`). + 2. Immediately translate it to the target scenario coordinates using **`set_transform()`**. + - **Result**: `set_transform` is significantly more lenient than `try_spawn_actor`, ensuring we never fail to reach our starting intersection. + +--- + +## ๐Ÿงช 3. Parametric Tuning via CLI + +The orchestrator now supports dynamic parameter injection for the braking scenario: +```powershell +./run.bat braking --params "BRAKE_FRAME=120,LEAD_DISTANCE_M=30" +``` +- **BRAKE_FRAME**: The exact simulation frame where the lead vehicle disengages autopilot and applies a `brake=1.0` override. +- **LEAD_DISTANCE_M**: The initial separation between Ego and Lead vehicle. + +--- +*Created: March 2026 | Intel Repository โ€” ADAS Development* diff --git a/intel/context.md b/intel/context.md index 50bbf4d..40fe2e3 100644 --- a/intel/context.md +++ b/intel/context.md @@ -33,10 +33,11 @@ Fox/ โ”‚ โ”œโ”€โ”€ main.py โ† Orchestrator โ€” scenario-agnostic entry point โ”‚ โ”œโ”€โ”€ sensors.py โ† SensorManager (camera, radar, lidar setup + sync queues) โ”‚ โ”œโ”€โ”€ recorder.py โ† Recorder (writes PNG / NPY / JSONL per frame) -โ”‚ โ””โ”€โ”€ scenario_loader.py โ† Dynamic scenario loader via importlib +โ”‚ โ”œโ”€โ”€ scenario_loader.py โ† Dynamic scenario loader via importlib +โ”‚ โ””โ”€โ”€ utils.py โ† Shared project helpers (weather mapping, etc.) โ”‚ โ”œโ”€โ”€ scenarios/ -โ”‚ โ”œโ”€โ”€ __init__.py โ† Package marker +โ”‚ โ”œโ”€โ”€ __init__.py โ† Package marker (empty to avoid side-effect imports) โ”‚ โ”œโ”€โ”€ base.py โ† ScenarioBase abstract class (the plugin contract) โ”‚ โ”œโ”€โ”€ braking.py โ† Lead vehicle hard braking scenario โ”‚ โ”œโ”€โ”€ cutin.py โ† Adjacent lane cut-in scenario @@ -50,7 +51,9 @@ Fox/ โ”‚ โ””โ”€โ”€ frames.jsonl โ† One JSON record per frame (metadata + scenario info) โ”‚ โ””โ”€โ”€ intel/ - โ””โ”€โ”€ context.md โ† This file + โ”œโ”€โ”€ context.md โ† This file + โ”œโ”€โ”€ showcase.md โ† Showcase scenario deep-dive + โ””โ”€โ”€ braking.md โ† Spawning & physics post-mortem ``` --- @@ -58,25 +61,19 @@ Fox/ ## Key Files โ€” Detailed Reference ### `config.py` -Single source of truth for all constants. Every module imports from here via `import config`. +Single source of truth for all simulation-wide defaults. It NO LONGER contains scenario-specific constants. | Key | Default | Purpose | |---|---|---| -| `FPS` | 20 | Simulation tick rate | -| `DELTA_SECONDS` | 0.05 | CARLA fixed delta (= 1/FPS) | +| `FPS` | 30 | Simulation tick rate | +| `DELTA_SECONDS` | 0.033 | CARLA fixed delta (= 1/FPS) | +| `DEFAULT_EGO_MODEL` | `"tesla.model3"` | Ego vehicle blueprint | +| `DEFAULT_WEATHER` | `"ClearNoon"` | Global weather preset | | `CAM_WIDTH/HEIGHT/FOV` | 1280ร—720, 90ยฐ | Camera resolution & field of view | -| `RADAR_HORIZONTAL_FOV` | 120ยฐ | Radar angular sweep | | `RADAR_RANGE` | 100 m | Radar max range | | `LIDAR_CHANNELS` | 32 | LiDAR beam count | -| `LIDAR_POINTS_PER_SECOND` | 100 000 | LiDAR density | | `MAX_FRAMES` | 200 | Default run length | | `DEFAULT_SCENARIO` | `"braking"` | Used when no `--scenario` flag provided | -| `SCENARIO_LEAD_DISTANCE` | 25 m | Lead vehicle spawn distance (braking) | -| `SCENARIO_BRAKE_FRAME` | 80 | Frame at which lead brakes | -| `SCENARIO_CUTIN_DISTANCE` | 15 m | NPC spawn distance (cutin) | -| `SCENARIO_CUTIN_FRAME` | 60 | Frame at which NPC changes lane | -| `SCENARIO_OBSTACLE_DISTANCE` | 30 m | Cone spawn distance (obstacle) | -| `SCENARIO_OBSTACLE_PROP` | `static.prop.trafficcone01` | CARLA blueprint name | --- @@ -85,22 +82,29 @@ Single source of truth for all constants. Every module imports from here via `im **Does NOT contain any scenario-specific logic.** **CLI:** -``` -python src/main.py --scenario braking -python src/main.py --scenario cutin --frames 120 +```powershell +python src/main.py --scenario braking --params "BRAKE_FRAME=100" +python src/main.py --scenario cutin --frames 120 --weather Rain python src/main.py --list-scenarios ``` +**New Flags:** +- `--params`: Scenario-specific adjustments (e.g. `SPEED=40`). +- `--weather`: Override scenario default (e.g. `Sunset`, `Wet`). +- `--no-record`: Dry-run simulation tracking with no disk I/O. + **Execution flow:** 1. Parse args โ†’ `load_scenario(name)` โ†’ returns `ScenarioBase` instance -2. Connect CARLA (`localhost:2000`), enable sync mode at `DELTA_SECONDS` -3. Clear existing actors, spawn ego (`vehicle.tesla.model3`) -4. `SensorManager.spawn_sensors()` โ†’ attach camera / radar / lidar to ego -5. `Recorder(scenario_name=scenario.name)` โ†’ creates `data/_/` -6. `scenario.setup(world, ego, traffic_manager)` -7. **Main loop** per frame: `world.tick()` โ†’ `sensor_manager.get_data()` โ†’ - `recorder.save(..., extra_meta=scenario.get_scenario_metadata())` โ†’ `scenario.step(frame, ego)` -8. `finally`: `scenario.cleanup()` โ†’ `sensor_manager.destroy()` โ†’ restore async mode +2. **Handle Parameter Injection**: Call `scenario.apply_parameters(args.params)` +3. Connect CARLA, settle sync mode, apply chosen weather (CLI > Scenario > Config) +4. Clear existing actors, spawn ego at **`ego_spawn_point`** (deterministic index or Transform) +5. `SensorManager.spawn_sensors()` โ†’ attach camera / radar / lidar to ego +6. `Recorder(scenario_name=scenario.name)` โ†’ creates `data/_/` +7. **Settle Physics**: Call `world.tick()` once to synchronize Ego location before setup +8. `scenario.setup(world, ego, traffic_manager)` +9. **Main loop** per frame: `world.tick()` โ†’ `sensor_manager.get_data()` โ†’ + `recorder.save(..., extra_meta=scenario.get_scenario_metadata())` โ†’ `scenario.step(frame, ego, pbar)` +10. `finally`: `scenario.cleanup()` โ†’ `sensor_manager.destroy()` โ†’ restore async mode > **Key invariant:** `main.py` never imports a scenario module by name. > The CARLA import itself is deferred until after `--list-scenarios` early-exit so the dry-run @@ -190,30 +194,29 @@ The **plugin contract** all scenarios must fulfil. ``` Required abstracts: name (property), setup(), step(), cleanup() -Optional overrides: on_ego_spawned(), get_scenario_metadata() -Shared helpers: _destroy_actors(), _get_waypoint_ahead(distance, lane_offset) +Optional overrides: ego_spawn_point, weather, max_frames (properties), on_ego_spawned(), get_scenario_metadata() +Shared helpers: _destroy_actors(), _get_waypoint_ahead(distance, lane_offset), apply_parameters(params) Protected state: self._world, self._ego, self._tm, self._actors (list) -``` -`_get_waypoint_ahead(distance, lane_offset)`: -- `lane_offset=0` โ†’ same lane as ego -- `lane_offset=1` โ†’ right lane -- `lane_offset=-1` โ†’ left lane -- Returns `carla.Waypoint` or `None` +**Deterministic Spawning:** +Subclasses should override `ego_spawn_point` (return a `carla.Transform`) to ensure the scenario always starts at a specific intersection or road segment, regardless of the map's default spawn points. -**All NPC placement must go through this helper** โ€” never use hardcoded world coordinates. +**Z-Axis Safety:** +NPCs should be spawned with a **0.5m Z-offset (lift)** relative to the road waypoint to prevent bounding-box collision with the ground mesh (fixed "Spot occupied" errors). --- ### Implemented Scenarios | File | Class | Trigger | Effect | +| File | Class | Default Effect | Deterministic? | |---|---|---|---| -| `braking.py` | `BrakingScenario` | Frame 80 | Lead vehicle (25 m ahead) applies emergency stop | -| `cutin.py` | `CutInScenario` | Frame 60 | NPC in left lane (15 m ahead) forced into ego lane | -| `obstacle.py` | `ObstacleScenario` | None (static) | Traffic cone placed 30 m ahead on ego lane | +| `braking.py` | `BrakingScenario` | Lead vehicle brakes at frame 80 | Yes (Spawn-and-Move) | +| `cutin.py` | `CutInScenario` | NPC cuts into lane at frame 60 | Yes (Spawn-and-Move) | +| `obstacle.py` | `ObstacleScenario` | Cone placed 30 m ahead | Yes (Spawn-and-Move) | +| `showcase.py` | `ShowcaseScenario` | Complex Left-Turn Across Path demo | Yes (Manual Pathing) | -All trigger frames and distances are driven by `config.py` constants. +All scenarios now encapsulate their own defaults and support CLI injection via `--params`. --- @@ -309,12 +312,11 @@ class MyScenario(ScenarioBase): | Area | Status | Notes | |---|---|---| | MCAP encoding | JSON (functional) | Should migrate to Protobuf/typed schemas for performance | -| Intersection scenario | Not implemented | Needs `waypoint.is_junction` awareness | -| Scenario parameterization | Config-based | Future: CLI `--param key=val` support | +| Intersection scenario | In Progress | See `scenarios/showcase.py` for LTAP implementation | | Foxglove layouts | Manual | Future: `.json` layout presets per scenario | | Multi-ego support | Not implemented | Single ego vehicle assumed throughout | | Radar Foxglove schema | Re-uses PointCloud | Correct but non-semantic; dedicated radar schema planned | --- -*Last updated: 2026-03-27 | Pipeline version: modular scenario-driven (post-refactor)* +*Last updated: 2026-03-31 | Pipeline version: Scenario-Centric Deterministic Architecture*