Runtime Loop and Entrypoint
The Raspberry Pi 5 controller is a 60 Hz arbitration loop owned by run() in code/controller/current/rc_car_app/runtime.py. The loop reads already-available state, applies control priority, writes hardware commands, and publishes telemetry. Slow device and network work must not execute synchronously in this loop.
code/controller/current/rc_car.py is the minimal executable entrypoint. It calls runtime.run(); model selection happens on the dashboard or through RC_CAR_STEERING_MODEL, with v3.4 as the checked-in default. Startup initializes controller input, hardware, sensors, camera, Jetson Orin Nano inference, logging, and dashboard telemetry. If no joystick is detected, startup exits rather than enabling a car without its manual-control interface.
One Iteration
- Measure loop time and report any pause of at least
CONTROL_LOOP_STALL_WARN_SEC(0.10 s). - Drain Pygame controller events and update stick, trigger, button, D-pad, and page state.
- Read the latest LiDAR scan snapshot and evaluate the center-corridor policy once.
- Run
update_gpio()to arbitrate manual/autonomous throttle, AEB, yaw correction, steering, brake, and gear behavior. - Read the latest sensor and navigation state and update dashboard page selection.
- Queue the newest dashboard payload for its sender worker.
- Append periodic CSV telemetry.
- Call
clock.tick(60)to cap the loop rate.
Worker Ownership
| Work | Owner | Main-loop behavior |
|---|---|---|
| Raspberry Pi 5 camera capture | WebcamVisionProcessor thread |
Copy latest BGR frame |
| Photo JPEG writes | Camera save worker | Queue frame or report queue full |
| LiDAR USB-serial parsing | LidarParser thread |
Read latest scan |
| GPS UART parsing | GpsReader thread |
Read latest fix |
| IMU serial parsing | ImuReader thread |
Read latest yaw rate |
| Jetson Orin Nano TCP/JPEG/inference | AsyncJetsonSteeringClient thread |
Submit the latest frame and read the latest fresh result |
| Dashboard UDP/JSON | AsyncDashboardSender thread |
Replace pending payload with newest state |
| Influx telemetry | InfluxLogger worker |
Queue measurement |
| Interruption clip encoding | Recorder worker | Queue captured JPEGs |
Both Jetson Orin Nano and dashboard workers are latest-value boundaries. If consumption is slower than capture, old pending work is replaced instead of building an increasingly stale queue.
Offline Jetson Orin Nano Behavior
Jetson Orin Nano uses 10.42.0.2:8770. A failed TCP connect can consume the complete 0.4 s socket timeout, but that wait occurs only in AsyncJetsonSteeringClient. Manual steering and dashboard updates continue while Jetson Orin Nano is powered off. Autonomous mode receives no fresh model result and hard-stops rather than reusing an old command.
JETSON_RESULT_MAX_AGE_SEC is 0.25 s. A result older than that is unavailable for control even if it remains stored in the client.
Timing Diagnostics
The runtime emits:
[loop-stall] control loop paused 412ms (auto=0)
for pauses at or above 100 ms, rate-limited to one message per second. A repeating message with Jetson Orin Nano off indicates a regression: network I/O has reached the control thread again. Camera, LiDAR, dashboard, and servo updates pausing together is also evidence of a main-loop stall rather than Bluetooth latency.
Bench Check
- Keep the wheels clear of the ground.
- Power the Raspberry Pi 5 and Zero 2 W, but leave Jetson Orin Nano off.
- Start the controller service and move the steering stick continuously.
- Change dashboard pages while watching the camera/LiDAR pages.
- Confirm servo and display motion stay smooth and
journalctlhas no repeating loop-stall pattern.
sudo systemctl restart sidewalkpilot-rpi-car.service
journalctl -u sidewalkpilot-rpi-car.service -f
Regression coverage:
python3 code/test_files/controller/test_async_jetson_client.py
python3 code/test_files/lidar/test_lidar_center_aeb.py
Cleanup requests zero motor output, centers or releases owned devices as implemented, stops workers, closes sensors and files, and sends the linked dashboard shutdown packet. A second interrupt can still cut across cleanup, so field shutdown should be observed rather than assumed.
See Model Inference and Dashboard Runtime.