TLSe Racing, Formula Student driverless team. 2025 to 2026, with a follow-up in October 2026.
The simulation layer of a driverless race car
I wrote the 2D simulator, with its field-of-view sensor model, in which driving logic can be tried before anything runs on a car. In October 2026 I closed the loop: a controller that drives laps while seeing only what that sensor lets it see. All of it is 2D simulation.
In three lines
- Problem
- The driverless team needed a place to try out driving logic before anything runs on a car, with a sensor that only shows the cones in front of the car.
- What I built
- A 2D Pygame simulator with a fit-to-track camera, a typed CSV track loader and a configurable field-of-view sensor model (2025), then a closed loop with cone memory, pure pursuit and a referee, and a benchmark of my teammates’ planners (October 2026).
- Result
- With the default sensor the car completes three valid laps on each of the four bundled tracks and touches no cone on three of them; on the hairpin track it touches 11 per lap. 2D simulation: no lap time here predicts a car.
Context
In the 2025 to 2026 season I was a member of the driverless team of TLSe Racing, a Formula Student team. In November 2025 two of its members started a small simulator in Python and Pygame to try out driving logic before anything runs on a car: I wrote the simulation layer, a teammate the first reactive controller.
Two repositories came out of it, both team prototypes hosted on my account. TLSe_Racing_Driverless is the simulator. PathPlanning (November and December 2025) holds three offline planners that build a closed reference line around a cone track, and a viewer that drives a car along it. The planning code is my teammates’ work. The simulator, the viewer, the camera and the track loader are mine.
In October 2026 I came back to both with what they lacked: measurements. In the simulator, a controller that completes laps from what the sensor sees. In PathPlanning, a command line, measures of the planned lines, a benchmark with committed results, tests and CI. My teammates’ code is left as they wrote it.
What I built
November 2025
The simulation layer
A 2D Pygame simulator for Formula Student cone tracks: a typed CSV loader with a schema check, a camera that fits the track and zooms about the cursor, a car drawn at Formula Student scale, and a configurable field-of-view sensor model, a range and an opening angle, that selects the cones the car can see. The sensor model is purely geometric: no occlusion, no image processing.
A teammate wrote the first reactive controller on top of it: it aims at the midpoint of the nearest visible blue and yellow cones.
Repository: TLSe_Racing_Driverlessthe simulator
November and December 2025
A viewer for the planners
In PathPlanning, three planners build a closed reference line around a cone track from the full cone map: a midpoint centre line fitted with a B-spline (one teammate), and two RRT* variants with smoothing (another teammate). My part is the Pygame viewer that moves a car along the line, its 2D camera, and the CSV loader for the 26 bundled cone maps.
The planners see every cone: there is no perception in this repository. By git blame at the end of 2025: 634 lines by the teammate who wrote the RRT* planners, 355 by me, 146 by the teammate who wrote the midpoint planner.
Repository: PathPlanningoffline planners and viewer
October 2026
The closed loop
A controller that never reads the map. Every 20 ms it senses the cones inside the sensor sector, remembers them, pairs blue and yellow cones into gates, chains the gates ahead of the car into a local centre line and steers by pure pursuit with a speed target. A kinematic bicycle model moves the car, and a referee times the laps and counts cone contacts against the true map.
Written with AI coding assistance: the commits carry a Co-Authored-By trailer. It lives in its own package, separate from the 2025 files.
Repository: TLSe_Racing_Driverlessclosed_loop/, scripts/, tests/
October 2026
Measuring the planners
A command line to run any planner on any of the 26 tracks, measures of the planned lines (closest approach to a cone, share of the line that stays between the two rows), a benchmark of 78 runs with its results committed, tests and CI. The planners themselves were not changed.
Same AI assistance, same trailer.
Repository: PathPlanningplanner registry, metrics, benchmark
The closed loop: what the car computes
- 01Track CSVthe full cone map: only the sensor and the referee read it
Every 20 ms: sense, remember, pair, order, steer, move. The new pose goes back to the sensor.
- 02Field-of-view sensorrange and opening angle (2025)
- 03Cone memorymerged within 0.5 m, used after three sightings, forgotten 6 s after the last one
- 04Gates and local centre lineblue and yellow pairs 2 m to 6.5 m wide that pass the Gabriel test, chained ahead of the car
- 05Pure pursuit and speed targetlookahead 2 m to 5 m, speed limited by lateral acceleration
- 06Kinematic bicycle modelsteering and acceleration limits
- 07Refereelap timer, cone-hit counter, off-course check, on the true state
Results
4 / 4
bundled tracks driven for three valid laps with the default sensor, 4 m range and 100° field of view. No cone was touched on three of them. A lap is valid when the car went through at least 95% of the track’s reference gates.
25.3→19.5s
Best simulated lap on belgium as the sensor range grows from 4 m to 12 m (and the field of view from 100° to 120°). The speed rule only lets the car go as fast as it can slow down within the line it knows. The car is a kinematic bicycle without tyres: this compares settings of the simulator and does not predict a car.
| What was measured | Value | How to read it |
|---|---|---|
| Default sensor, 4 m range and 100°: valid laps on belgium, the hairpin track, peanut and small_track | 3 / 3 on each | Cones touched per lap: 0 on belgium, peanut and small_track, 11 on the hairpin track. |
| Best lap on belgium with a 4 m / 100°, an 8 m / 120° and a 12 m / 120° sensor | 25.28 s, 20.32 s, 19.54 s | Lap times fall as the range grows. The range does not change the cone contacts. |
| No cone memory, 4 m / 100°, on the hairpin track | left the track at 77 s | Without memory the car also touches cones on two other tracks (3.0 per lap on peanut, 2.0 on small_track). |
| No one-sided fallback, 4 m / 100°, on the four tracks | left all four | At 29 s, 28 s, 8 s and 32 s: the fallback is what keeps the line going when the far edge of the track is out of sight. |
| Detection noise of 0.1 m, then 0.2 m per axis, 4 m / 100° | no change, then 2 tracks lost | At 0.2 m the car leaves belgium and the hairpin track. The noise is independent from one cycle to the next, which the running mean of the memory averages away; a biased or drifting error would be harder. |

| What was measured | Value | How to read it |
|---|---|---|
| Median planning time over the 26 tracks: midpoint, rrt, rrt-lsq | 63 ms, 27.7 s, 34.1 s | The midpoint planner takes 0.2 s at most. The RRT* planners take up to 58.8 s and 73.1 s on one track. |
| Median closest approach to a cone centre: midpoint, rrt, rrt-lsq | 0.94 m, 0.15 m, 0.04 m | Tracks where the line passes closer than 0.7 m to a cone, half the width of a 1.4 m car (an assumption): 6 of 26, 23 of 26 and 25 of 26. |
| RRT* searches that returned a path | 585 / 11,998 | All of them on the three tracks of the original menu (31 of 31, 489 of 491 and 65 of 65). None of 11,411 on the 23 other maps. |
| small_track, closest approach to a cone: midpoint against rrt-lsq | 1.92 m → 0.58 m | The RRT* line is shorter (100.1 m against 104.1 m) but passes closer to the cones. |
What failed or is unfinished
11
cones touched per lap on the hairpin track, with the default sensor. The laps are completed, but the contacts are all on the last five hairpins.
3 / 26
tracks on which the RRT* planners find a path between waypoints: 585 of 11,998 searches returned one, all on the three tracks of the original menu.
The hairpin track is not driven cleanly. There the centre line tightens to a radius of 2.13 m, below the 2.65 m minimum turning radius of the simulated car, whose footprint then sweeps over the cones. Pure pursuit only follows the centre line: avoiding the contacts would need a planner that uses the width of the track, which the repository does not have.
The RRT* planners solve only three of the 26 tracks. Every cone is a disc of 1.2 m in the search, so the middle of a gate narrower than 2.4 m lies inside the discs of its own two cones. On the 23 other maps the median gate is 2.20 m to 2.22 m wide. Their lines are then straight segments between the midpoint waypoints, smoothed without any knowledge of the cones: a median closest approach of 0.14 m for rrt and 0.03 m for rrt-lsq on those maps, against 0.94 m for midpoint over all 26 tracks.
The first reactive prototype strays. The November 2025 controller aims at the midpoint of the nearest visible blue and yellow cones at a constant speed, keeps no memory and has nothing to do when no cone is in view. Replayed off-screen, the car strays more than 4 m from the middle of the track on all four bundled tracks within 40 simulated seconds. The closed loop adds the memory and the pairing that it lacks.
The first sensor setting was too narrow. The 7 m, 60° sector of the sensor model, as I first committed it in November 2025, is enough on three tracks. On the hairpin track the car leaves the track after 52 s: the narrow sector does not show enough of a tight bend.
Simulation only. The car knows its exact pose, and perception is a visibility test on the true map: no camera or LiDAR model, no occlusion, no missed or false detection. The cone memory would need a real estimate of the car’s position to work on a vehicle. Camera-based cone detection, localisation and mapping, a tyre model, a racing line and a ROS interface are not in these repositories, and nothing here has run on a car.

Credits
One teammate wrote the first reactive controller and the offline centre-line prototype of the simulator repository, and the midpoint planner of PathPlanning. Another teammate wrote the two RRT* planners and their smoothing, and the original French note of PathPlanning. The commit history of both repositories shows who wrote what.
I wrote the track loader, the camera, the viewer and the field-of-view sensor model in November 2025, then the closed-loop package, the referee, the benchmarks, the figures, the tests and the CI in October 2026.
The October 2026 work was written with AI coding assistance, and those commits carry a Co-Authored-By trailer. Every number and every picture on this page is rewritten by a script of the repository.
The origin of the four tracks of the simulator repository and of the 26 maps of PathPlanning is not documented in the repositories.
Beyond these repositories, I also worked on cone-detection models in PyTorch and on image-processing and control modules with ROS, Python and C++. That work is not in them, and this page shows none of it.
Pure pursuit follows R. C. Coulter (1992), the pairing test is the Gabriel graph (Gabriel and Sokal, 1969), RRT* is Karaman and Frazzoli (2011). The GIFs are recorded with ffmpeg.