Browse Source

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
1843_integration
RUSHIL AMBARISH KADU 2 months ago
parent
commit
541d27400b
  1. 25
      .cursorrules
  2. 50
      intel/braking.md
  3. 84
      intel/context.md

25
.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.

50
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*

84
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/<scenario>_<ts>/`
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/<scenario>_<ts>/`
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*
Loading…
Cancel
Save