CoVAPSy (Course de Voitures Autonomes Paris-Saclay) is a race for 1/10-scale autonomous cars on an indoor track. This is our team's car: a LiDAR, a camera, a Raspberry Pi 5 and an STM32 on a 1/10 chassis, with no ROS. All the code is in Python and C.
Two processors, split by deadline
Raspberry Pi 5 (Python)
STM32G431 (C)
Job
perception and decisions
real-time actuation
Rate
15 Hz control loop
50 Hz servo/ESC PWM
Does
reads the RPLIDAR A2M12, runs follow-the-gap, race state machine, start/stop over TCP
steering and ESC pulses, ESC arming, rear IR sensor, wheel encoder, BNO055 IMU, OLED dashboard
They talk over a serial line with a short ASCII protocol: S<deg> for steering, V<m/s> for speed, X to stop. Every command is acknowledged, and the STM32 sends a heartbeat line with its pulse widths, rear sensor, heading and wheel speed every 500 ms. The STM32 also runs a watchdog: if no valid command arrives for 250 ms, it puts the throttle and steering to neutral on its own, whatever the Pi is doing.
Driving
The driving logic is one pure Python function, gap_follow(). It cleans the scan, widens obstacles by half a car width at every depth jump, carves a safety bubble around the nearest point, picks the widest open gap, and aims between its centre and its deepest point. Speed falls with steering angle and with clearance ahead. In a dead end it reverses toward the more open side, but only if the rear IR sensor says the way back is clear. The blog post walks through it step by step.
Same code in simulation
The Webots controller imports that same function unchanged, so a change can be tried in simulation before it touches the car: alone on the track or against three other cars.
Failsafes
The car only drives while the LiDAR is healthy (a full scan in the last 0.5 s).
Stuck detection: if it is commanded forward but the encoder reads under 5 pulses/s for 3 s, it reverses at full lock for 1.5 s.
Reverse needs fresh telemetry from the rear sensor; stale data counts as blocked.
Any exit path sends a stop.
Limits
Speed is open-loop: "m/s" maps to a pulse width rather than being regulated with the encoder, which is not yet calibrated. The camera, which spots driving the wrong way from the coloured track borders, is advisory only.