Guilhem Carmouze

TLSe Racing, équipe driverless de Formula Student. 2025-2026, avec un prolongement en octobre 2026.

La couche de simulation d’une voiture de course driverless

J’ai écrit le simulateur 2D, avec son modèle de capteur à champ de vision, dans lequel on peut essayer une logique de conduite avant tout essai sur une vraie voiture. En octobre 2026, j’ai fermé la boucle : un contrôleur qui enchaîne des tours en ne voyant que ce que ce capteur lui laisse voir. Tout cela est de la simulation 2D.

Rôle
Membre de l’équipe driverless. Auteur de la couche de simulation et d’outillage (novembre 2025), puis de la boucle fermée, des bancs d’essai, des tests et de l’intégration continue (octobre 2026).
Équipe
Un coéquipier a écrit le premier contrôleur réactif et le planificateur par milieux, un autre les planificateurs RRT* et le lissage.
Période
Novembre et décembre 2025, puis octobre 2026
Organisation
TLSe Racing, équipe driverless de Formula Student. Les deux dépôts sont des prototypes d’équipe hébergés sur mon compte GitHub, pas le logiciel officiel de l’équipe.
Technologies
Python, Pygame (pygame-ce), NumPy, SciPy, pytest, intégration continue, ffmpeg pour les clips
Un tour de la piste belgium conduit par la boucle fermée avec le capteur par défaut (portée de 4 m, champ de vision de 100°), joué à deux fois la vitesse simulée. À gauche : toute la piste. À droite : une vue de suivi. Les cônes estompés sont inconnus de la voiture, les cônes en pleine couleur sont dans sa mémoire, les cônes avec un anneau blanc sont dans le secteur du capteur à cet instant. La ligne rouge est la ligne centrale construite à partir des paires bleu-jaune. Simulation 2D, enregistrée hors écran par un script du dépôt.

En trois lignes

Problème
L’équipe driverless avait besoin d’un endroit où essayer une logique de conduite avant tout essai sur une vraie voiture, avec un capteur qui ne montre que les cônes devant la voiture.
Ce que j’ai réalisé
Un simulateur Pygame 2D avec une caméra qui s’ajuste à la piste, un chargeur de pistes CSV typé et un modèle de capteur à champ de vision configurable (2025), puis une boucle fermée avec mémoire des cônes, poursuite pure (pure pursuit) et un arbitre, et un banc d’essai des planificateurs de mes coéquipiers (octobre 2026).
Résultat
Avec le capteur par défaut, la voiture réalise trois tours valides sur chacune des quatre pistes fournies et ne touche aucun cône sur trois d’entre elles ; sur la piste à épingles, elle en touche 11 par tour. Simulation 2D : aucun temps au tour ici ne prédit une voiture.

Contexte

Pendant la saison 2025-2026, j’étais membre de l’équipe driverless de TLSe Racing, une équipe de Formula Student. En novembre 2025, deux de ses membres ont commencé un petit simulateur en Python et Pygame pour essayer une logique de conduite avant tout essai sur une vraie voiture : j’ai écrit la couche de simulation, un coéquipier le premier contrôleur réactif.

Deux dépôts en sont sortis, tous deux des prototypes d’équipe hébergés sur mon compte. TLSe_Racing_Driverless est le simulateur. PathPlanning (novembre et décembre 2025) contient trois planificateurs hors ligne qui construisent une ligne de référence fermée autour d’une piste de cônes, et un visualiseur qui y conduit une voiture. Le code de planification est le travail de mes coéquipiers. Le simulateur, le visualiseur, la caméra et le chargeur de pistes sont les miens.

En octobre 2026, je suis revenu sur les deux avec ce qui leur manquait : des mesures. Dans le simulateur, un contrôleur qui enchaîne des tours à partir de ce que voit le capteur. Dans PathPlanning, une ligne de commande, des mesures des lignes planifiées, un banc d’essai avec des résultats versionnés, des tests et de l’intégration continue. Le code de mes coéquipiers est laissé tel qu’ils l’ont écrit.

Ce que j’ai réalisé

  1. Novembre 2025

    La couche de simulation

    Un simulateur Pygame 2D pour pistes de cônes de Formula Student : un chargeur CSV typé avec vérification du schéma, une caméra qui s’ajuste à la piste et zoome autour du curseur, une voiture dessinée à l’échelle Formula Student, et un modèle de capteur à champ de vision configurable, une portée et un angle d’ouverture, qui sélectionne les cônes que la voiture peut voir. Le modèle de capteur est purement géométrique : pas d’occlusion, pas de traitement d’image.

    Un coéquipier a écrit, au-dessus de cette couche, le premier contrôleur réactif : il vise le milieu des cônes bleu et jaune visibles les plus proches.

    Dépôt : TLSe_Racing_Driverlessle simulateur

  2. Novembre et décembre 2025

    Un visualiseur pour les planificateurs

    Dans PathPlanning, trois planificateurs construisent une ligne de référence fermée autour d’une piste de cônes à partir de la carte complète des cônes : une ligne centrale par milieux ajustée par une B-spline (un coéquipier), et deux variantes de RRT* avec lissage (un autre coéquipier). Ma part est le visualiseur Pygame qui déplace une voiture le long de la ligne, sa caméra 2D, et le chargeur CSV des 26 cartes de cônes fournies.

    Les planificateurs voient tous les cônes : il n’y a pas de perception dans ce dépôt. Par git blame fin 2025 : 634 lignes du coéquipier qui a écrit les planificateurs RRT*, 355 de moi, 146 de celui qui a écrit le planificateur par milieux.

    Dépôt : PathPlanningplanificateurs hors ligne et visualiseur

  3. Octobre 2026

    La boucle fermée

    Un contrôleur qui ne lit jamais la carte. Toutes les 20 ms, il détecte les cônes dans le secteur du capteur, les mémorise, apparie les cônes bleus et jaunes en portes, enchaîne les portes devant la voiture en une ligne centrale locale et suit cette ligne par poursuite pure (pure pursuit) avec une consigne de vitesse. Un modèle bicyclette cinématique déplace la voiture, et un arbitre chronomètre les tours et compte les contacts avec les cônes sur la vraie carte.

    Écrit avec l’aide d’un assistant de code IA : les commits portent la mention Co-Authored-By. Il vit dans son propre paquet Python, séparé des fichiers de 2025.

    Dépôt : TLSe_Racing_Driverlessclosed_loop/, scripts/, tests/

  4. Octobre 2026

    Mesurer les planificateurs

    Une ligne de commande pour lancer n’importe quel planificateur sur n’importe laquelle des 26 pistes, des mesures des lignes planifiées (approche minimale d’un cône, part de la ligne qui reste entre les deux rangées), un banc d’essai de 78 exécutions avec ses résultats versionnés, des tests et de l’intégration continue. Les planificateurs eux-mêmes n’ont pas été modifiés.

    Même assistance IA, même mention.

    Dépôt : PathPlanningregistre de planificateurs, métriques, banc d’essai

La boucle fermée : ce que calcule la voiture

  1. 01CSV de la pistela carte complète des cônes : seuls le capteur et l’arbitre la lisent
  2. Toutes les 20 ms : détecter, mémoriser, apparier, ordonner, braquer, avancer. La nouvelle pose retourne au capteur.

    1. 02Capteur à champ de visionportée et angle d’ouverture (2025)
    2. 03Mémoire des cônesfusion à moins de 0,5 m, utilisés après trois observations, oubliés 6 s après la dernière
    3. 04Portes et ligne centrale localepaires bleu et jaune de 2 m à 6,5 m de large qui passent le test de Gabriel, enchaînées devant la voiture
    4. 05Poursuite pure et consigne de vitessedistance d’anticipation de 2 m à 5 m, vitesse limitée par l’accélération latérale
    5. 06Modèle bicyclette cinématiquelimites de braquage et d’accélération
  3. 07Arbitrechronomètre de tour, compteur de cônes touchés, contrôle de sortie de piste, sur l’état réel
Mon codeDonnées d’entréeLe modèle de capteur est le fichier de 2025 ; le reste de la boucle date d’octobre 2026. Le simulateur donne à la voiture sa pose exacte, donc il n’y a pas d’erreur d’odométrie, et la perception est un test de visibilité sur la vraie carte : il n’y a ni modèle de caméra ni modèle de LiDAR. Un cône sans partenaire prolonge quand même la ligne, décalé d’une demi-largeur de piste vers l’intérieur, ce qui compte avec une courte portée de capteur.
Les trois planificateurs de PathPlanning sur small_track, avec la même graine aléatoire que le banc d’essai. La voiture avance à la même vitesse constante sur chaque ligne, donc la ligne la plus courte finit la première. C’est une animation du visualiseur, pas un modèle de véhicule.

Résultats

4 / 4

pistes fournies conduites pour trois tours valides avec le capteur par défaut, portée de 4 m et champ de vision de 100°. Aucun cône touché sur trois d’entre elles. Un tour est valide quand la voiture est passée par au moins 95 % des portes de référence de la piste.

25,3→19,5s

Meilleur tour simulé sur belgium quand la portée du capteur passe de 4 m à 12 m (et le champ de vision de 100° à 120°). La règle de vitesse ne laisse la voiture aller qu’aussi vite qu’elle peut ralentir sur la ligne qu’elle connaît. La voiture est une bicyclette cinématique sans pneus : cela compare des réglages du simulateur et ne prédit pas une voiture.

Ce qui a été mesuréValeurComment le lire
Capteur par défaut, 4 m de portée et 100° : tours valides sur belgium, la piste à épingles, peanut et small_track3 / 3 sur chacuneCônes touchés par tour : 0 sur belgium, peanut et small_track, 11 sur la piste à épingles.
Meilleur tour sur belgium avec un capteur de 4 m / 100°, de 8 m / 120° et de 12 m / 120°25,28 s, 20,32 s, 19,54 sLes temps au tour baissent quand la portée augmente. La portée ne change pas les contacts avec les cônes.
Sans mémoire des cônes, 4 m / 100°, sur la piste à épinglessortie de piste à 77 sSans mémoire, la voiture touche aussi des cônes sur deux autres pistes (3,0 par tour sur peanut, 2,0 sur small_track).
Sans repli sur un seul côté, 4 m / 100°, sur les quatre pistessortie sur les quatreÀ 29 s, 28 s, 8 s et 32 s : le repli est ce qui maintient la ligne quand le bord lointain de la piste est hors de vue.
Bruit de détection de 0,1 m, puis 0,2 m par axe, 4 m / 100°aucun changement, puis 2 pistes perduesÀ 0,2 m, la voiture sort de belgium et de la piste à épingles. Le bruit est indépendant d’un cycle à l’autre, ce que la moyenne glissante de la mémoire efface ; une erreur biaisée ou qui dérive serait plus difficile.
Boucle fermée, 32 essais de trois tours sur les quatre pistes fournies, 3 387 s de conduite simulée. La simulation est déterministe ; les seuls nombres aléatoires sont le bruit de détection de deux ablations, avec une graine fixe. Les nombres sont écrits par scripts/benchmark.py et versionnés avec le dépôt. Les temps au tour découlent des limites choisies et d’un modèle sans pneus.
Quatre cartes de pistes de cônes avec la trajectoire parcourue en magenta : belgium, peanut, small_track et la longue piste à épingles. Des cônes bleus d’un côté, jaunes de l’autre. Sur la piste à épingles, quelques cônes près des dernières épingles sont entourés en blanc.
Trois tours par piste avec le capteur par défaut, dessinés par scripts/render_laps.py. Les cônes que la voiture a touchés sont entourés en blanc : tous sont sur les cinq dernières épingles de la piste à épingles.
Ce qui a été mesuréValeurComment le lire
Temps de planification médian sur les 26 pistes : midpoint, rrt, rrt-lsq63 ms, 27,7 s, 34,1 sLe planificateur midpoint met 0,2 s au plus. Les planificateurs RRT* mettent jusqu’à 58,8 s et 73,1 s sur une piste.
Approche minimale médiane d’un centre de cône : midpoint, rrt, rrt-lsq0,94 m, 0,15 m, 0,04 mPistes où la ligne passe à moins de 0,7 m d’un cône, la moitié de la largeur d’une voiture de 1,4 m (une hypothèse) : 6 sur 26, 23 sur 26 et 25 sur 26.
Recherches RRT* qui ont retourné un chemin585 / 11 998Toutes sur les trois pistes du menu d’origine (31 sur 31, 489 sur 491 et 65 sur 65). Aucune des 11 411 recherches sur les 23 autres cartes.
small_track, approche minimale d’un cône : midpoint contre rrt-lsq1,92 m → 0,58 mLa ligne RRT* est plus courte (100,1 m contre 104,1 m) mais passe plus près des cônes.
Planificateurs de PathPlanning, 78 exécutions (trois planificateurs sur 26 pistes), graine 0, planification hors ligne sur la carte complète des cônes : pas de perception, pas de modèle de véhicule. Les temps de planification ont été pris pendant que les exécutions partageaient la machine, ils sont donc indicatifs. Il n’existe pas de meilleure ligne de référence : une plus grande marge ne fait pas un tour plus rapide.

Ce qui a échoué ou reste inachevé

11

cônes touchés par tour sur la piste à épingles, avec le capteur par défaut. Les tours sont terminés, mais les contacts sont tous sur les cinq dernières épingles.

3 / 26

pistes sur lesquelles les planificateurs RRT* trouvent un chemin entre les points de passage : 585 recherches sur 11 998 en ont retourné un, toutes sur les trois pistes du menu d’origine.

  • La piste à épingles n’est pas conduite proprement. Là, la ligne centrale se resserre jusqu’à un rayon de 2,13 m, sous le rayon de braquage minimal de 2,65 m de la voiture simulée, dont l’empreinte balaie alors les cônes. La poursuite pure ne fait que suivre la ligne centrale : éviter les contacts demanderait un planificateur qui exploite la largeur de la piste, ce que le dépôt n’a pas.

  • Les planificateurs RRT* ne résolvent que trois pistes sur 26. Chaque cône est un disque de 1,2 m dans la recherche, donc le milieu d’une porte plus étroite que 2,4 m se trouve dans les disques de ses propres deux cônes. Sur les 23 autres cartes, la porte médiane fait 2,20 m à 2,22 m de large. Leurs lignes sont alors des segments droits entre les points de passage des milieux, lissés sans aucune connaissance des cônes : une approche minimale médiane de 0,14 m pour rrt et de 0,03 m pour rrt-lsq sur ces cartes, contre 0,94 m pour midpoint sur les 26 pistes.

  • Le premier prototype réactif dérive. Le contrôleur de novembre 2025 vise le milieu des cônes bleu et jaune visibles les plus proches à vitesse constante, ne garde aucune mémoire et n’a rien à faire quand aucun cône n’est en vue. Rejoué hors écran, la voiture s’écarte de plus de 4 m du milieu de la piste sur les quatre pistes fournies en moins de 40 secondes simulées. La boucle fermée ajoute la mémoire et l’appariement qui lui manquent.

  • Le premier réglage du capteur était trop étroit. Le secteur de 7 m et 60° du modèle de capteur, tel que je l’ai d’abord versionné en novembre 2025, suffit sur trois pistes. Sur la piste à épingles, la voiture sort de la piste après 52 s : le secteur étroit ne montre pas assez d’un virage serré.

  • Simulation uniquement. La voiture connaît sa pose exacte, et la perception est un test de visibilité sur la vraie carte : pas de modèle de caméra ni de LiDAR, pas d’occlusion, pas de détection manquée ou fausse. La mémoire des cônes aurait besoin d’une vraie estimation de la position de la voiture pour fonctionner sur un véhicule. La détection de cônes par caméra, la localisation et la cartographie, un modèle de pneus, une trajectoire de course et une interface ROS ne sont pas dans ces dépôts, et rien ici n’a roulé sur une voiture.

Neuf petites cartes, trois planificateurs sur trois pistes. Ligne du haut small_track et ligne du milieu peanut : une ligne rouge entre les cônes bleus et jaunes pour midpoint, rrt et rrt-lsq. Ligne du bas Zandvoort_cones : la ligne suit de très près les rangées de cônes pour les trois planificateurs, avec 0 recherche RRT* résolue sur 476.
Midpoint, rrt et rrt-lsq sur small_track, peanut et Zandvoort_cones, dessinés par scripts/render_planners.py. Ligne du bas : sur une carte en forme de circuit, aucune recherche RRT* ne retourne de chemin, et la ligne lissée passe alors à quelques centimètres des cônes.

Crédits

  • Un coéquipier a écrit le premier contrôleur réactif et le prototype hors ligne de ligne centrale du dépôt du simulateur, ainsi que le planificateur midpoint de PathPlanning. Un autre coéquipier a écrit les deux planificateurs RRT* et leur lissage, ainsi que la note française d’origine de PathPlanning. L’historique des commits des deux dépôts montre qui a écrit quoi.

  • J’ai écrit le chargeur de pistes, la caméra, le visualiseur et le modèle de capteur à champ de vision en novembre 2025, puis le paquet de boucle fermée, l’arbitre, les bancs d’essai, les figures, les tests et l’intégration continue en octobre 2026.

  • Le travail d’octobre 2026 a été écrit avec l’aide d’un assistant de code IA, et ces commits portent la mention Co-Authored-By. Chaque nombre et chaque image de cette page est réécrit par un script du dépôt.

  • L’origine des quatre pistes du dépôt du simulateur et des 26 cartes de PathPlanning n’est pas documentée dans les dépôts.

  • En dehors de ces dépôts, j’ai aussi travaillé sur des modèles de détection de cônes en PyTorch et sur des modules de traitement d’image et de commande avec ROS, Python et C++. Ce travail n’y figure pas, et cette page n’en montre rien.

  • La poursuite pure suit R. C. Coulter (1992), le test d’appariement est le graphe de Gabriel (Gabriel et Sokal, 1969), RRT* est de Karaman et Frazzoli (2011). Les GIF sont enregistrés avec ffmpeg.