Phase 1 : Acquisition de Données & Protocoles
La phase d'acquisition de données est la fondation de tout notre système de conduite autonome. En apprentissage par imitation (Behavioral Cloning), le modèle d'IA ne peut pas "inventer" un comportement sûr qu'il n'a jamais vu : il ne fait que reproduire statistiquement ce qui lui a été présenté.
Principe Clé : "Garbage In, Garbage Out" Si les données d'entrée sont bruitées, incohérentes ou biaisées, le modèle final sera intrinsèquement défaillant, quelle que soit la complexité de son architecture neuronale.
Spoiler Alert : On soulève le capot ! 🔧
Attention les pilotes ! Cette page est dédiée à la théorie, aux architectures des nœuds et aux mathématiques qui font rouler notre bolide.
Si vous cherchez comment lancer les scripts et faire vrombir le moteur concrètement, patience... cela se passe dans la section suivante Exécution & Dataset. Ici, on comprend pourquoi ça marche avant de faire marcher le truc !
Cette section détaille les outils et méthodologies rigoureuses que nous avons mis en place pour garantir la constitution d'un dataset de haute qualité, alliant précision géométrique (pour la fluidité) et diversité situationnelle (pour la robustesse).
1. Stratégie de Collecte
Pour répondre à ce besoin de diversité, nous avons développé et intégré plusieurs méthodes de collecte distinctes : le MPC (Model Predictive Control), le PID (Proportionnel-Intégral-Dérivé) et le Teleop (pilotage manuel).
Chaque méthode présente ses propres avantages et inconvénients (fluidité vs simplicité vs réalisme humain). Avant de choisir la meilleure stratégie, nous avons analysé le comportement de ces protocoles pour identifier celui offrant la meilleure "vérité terrain" pour le réseau de neurones.
Les sections suivantes détaillent le fonctionnement de chacun de ces contrôleurs, leurs performances respectives, et les raisons de nos choix finaux.
2. Protocoles de Contrôle
Nous avons implémenté et testé trois approches complémentaires. Bien que toutes les méthodes soient fonctionnelles, c'est finalement le MPC qui a été retenu comme générateur principal de données pour sa capacité à produire des trajectoires "expertes" et fluides. Les deux autres méthodes (Teleop et PID) jouent des rôles de support (sécurité et benchmark).
A. Teleop (Manuel) - L'Humain
C'est le mode de conduite manuelle classique, utilisant une manette (type Xbox/Logitech).

Pourquoi est-il essentiel ?
- Recoveries : Indispensable pour enregistrer les manœuvres de récupération (ramener la voiture au centre après une sortie de route provoquée).
- Sécurité : Agit comme un "kil-switch" humain. Si l'IA fait n'importe quoi, l'humain reprend la main instantanément.
Fonctionnement Technique (iliar_solution/teleop_command.py) :

| Composant | Description Technique (Logique Interne) |
|---|---|
| Deadman Switch | Sécurité "Homme-Mort" (Bouton A, idx 0). Tant qu'il est maintenu, le nœud calcule et publie les commandes. Dès qu'il est relâché, le nœud cesse toute publication, garantissant qu'aucune commande "fantôme" ne soit envoyée. |
| Direction (Axe) | Mape l'axe horizontal du joystick directement vers l'angle de braquage (steering_cmd), limité par le paramètre max_steer. |
| Pédales (Split) | Utilisation d'un seul axe vertical pour deux actions : • Valeur > 0 : Génère une commande d'accélération ( throttle_cmd).• Valeur < 0 : Génère une commande de freinage ( brake_cmd) proportionnelle. |
| Boîte de Vitesse | Gestion d'état interne via machine à états simple. • Bouton Y : Levier en Drive (Marche Avant). • Bouton B : Levier en Reverse (Marche Arrière). |
| Flux de Sortie | Publie sur 4 topics simultanés : • steering_cmd (Volant), • throttle_cmd (Gaz), • brake_cmd (Frein) et • /telemetry (État complet synchronisé pour l'enregistrement). |
B. PID - Le Classique
Le PID représente l'approche géométrique standard pour le suivi de trajectoire. Contrairement au Teleop où l'humain anticipe, le PID réagit mathématiquement à l'erreur instantanée. Son implémentation dans notre projet repose sur une architecture distribuée entre deux nœuds distincts qui collaborent en temps réel.

Architecture et Calcul d'Erreur (dist_to_path.py)
La première étape consiste à quantifier l'erreur du véhicule. Le nœud dist_to_path s'abonne à la trajectoire globale de référence (/road_path) et écoute la position du véhicule via TF2. Au cœur de ce nœud, nous utilisons la librairie geom2d, fournie dans le cadre du cours, pour effectuer des calculs vectoriels précis. Cette librairie permet de projeter orthogonalement la position de la voiture sur le segment de route le plus proche, calculant ainsi la CTE (Cross Track Error). Cette valeur, qui représente la distance signée en mètres par rapport au centre de la voie, est ensuite publiée sur le topic /dist_to_path.
La Boucle de Contrôle (path_follower.py)
Le second nœud, path_follower, agit comme le pilote automatique. Il écoute cette erreur latérale et cherche à la réduire à zéro (ou à une valeur cible définie). Pour ce faire, il applique l'équation classique du PID :
Le terme proportionnel (\(K_p\)) corrige l'erreur présente, le terme dérivé (\(K_d\)) amortit les oscillations en anticipant la variation de l'erreur, et le terme intégral (\(K_i\)) élimine l'erreur statique résiduelle. Le résultat de cette équation est directement converti en une commande d'angle de braquage envoyée sur /audibot/steering_cmd. En parallèle, un contrôleur proportionnel simple indépendant régule l'accélérateur pour maintenir une vitesse constante, découplant ainsi la gestion latérale et longitudinale.
Bien que fonctionnelle et capable de produire des données "propres" sur des segments simples, cette méthode reste purement réactive et tend à produire une conduite mécanique, manquant de la fluidité naturelle et de l'anticipation d'un pilote expert.
C. Model Predictive Control
Si le PID conduit "avec les yeux fixés sur le capot", le MPC conduit "en regardant la route au loin". C'est l'algorithme qui génère la vérité terrain de haute qualité que nous souhaitons cloner. Son fonctionnement repose sur une simulation physique avancée et une optimisation mathématique en temps réel.

Le Modèle "Bicyclette" Cinématique (mpc.py)
Au lieu de considérer la voiture comme un simple point, le MPC utilise un modèle cinématique complet (le modèle "Bicyclette"). Il prend en compte non seulement la position (\(x, y\)) et le cap (\(\psi\)), mais aussi la vitesse (\(v\)), l'erreur latérale (\(CTE\)) et l'erreur de cap (\(e\psi\)). Ce modèle permet de prédire avec précision comment la voiture réagira à une commande de volant donnée, en respectant les contraintes physiques du véhicule (comme l'empattement).
L'Horizon de Prédiction et l'Optimisation
À chaque itération (20 fois par seconde), le contrôleur effectue une simulation mentale sur un horizon futur de 30 pas (\(N=30\)). Il cherche la séquence de commande optimale qui minimise la fonction de coût globale \(J\), définie comme la somme de trois composantes :
Voici le détail de chaque composante :
-
Suivi de Trajectoire (\(J_{track}\)) :
\[ J_{track}(k) = w_{cte} \cdot cte(k)^2 + w_{e\psi} \cdot e\psi(k)^2 + w_{v} \cdot (v(k) - v_{ref})^2 \]Pénalise les écarts de position, de cap et de vitesse.
-
Usage des Actionneurs (\(J_{usage}\)) :
\[ J_{usage}(k) = w_{\delta} \cdot \delta(k)^2 + w_{a} \cdot a(k)^2 \]Minimise l'amplitude des commandes (évite de braquer ou d'accélérer à fond inutilement).
-
Confort et Fluidité (\(J_{smooth}\)) :
\[ J_{smooth}(k) = w_{\Delta\delta} \cdot (\delta(k+1) - \delta(k))^2 + w_{\Delta a} \cdot (a(k+1) - a(k))^2 \]C'est le terme le plus important : il interdit les changements brusques de volant (\(\Delta\delta\)). Dans notre configuration, \(w_{\Delta\delta} = 100000.0\), ce qui garantit une conduite ultra-lisse.
Réglage des Hyperparamètres
Le tableau ci-dessous détaille les poids (\(weights\)) spécifiques que nous avons choisis pour définir le comportement "expert" du MPC.
| Paramètre | Valeur (Poids) | Rôle et justification |
|---|---|---|
| \(w_{cte}\) | 2000.0 |
Précision Latérale. Poids élevé pour forcer le véhicule à coller au centre de la voie. Indispensable pour éviter le "flou" dans le dataset. |
| \(w_{e\psi}\) | 2000.0 |
Précision de Cap. Force la voiture à être bien alignée avec la route. Empêche la voiture de rouler "en crabe". |
| \(w_{v}\) | 2000.0 |
Suivi de Vitesse. Maintient la vitesse cible. Moins critique que la position, mais important pour la régularité temporelle. |
| \(w_{\delta}\) | 10000.0 |
Usage Volant. Pénalise les grands angles de braquage. Cela incite l'optimiseur à prendre les virages le plus large possible ("racing line"). |
| \(w_{a}\) | 2000.0 |
Usage Gaz/Frein. Pénalise l'accélération/freinage excessif pour économiser l'énergie et éviter les à-coups longitudinaux. |
| \(w_{\Delta\delta}\) | 100000.0 |
Fluidité Volant (Priorité Absolue). C'est la valeur la plus extrême. Elle rend tout mouvement brusque du volant "interdit" aux yeux du coût. C'est ce paramètre qui donne l'aspect "sur des rails". |
| \(w_{\Delta a}\) | 2000.0 |
Fluidité Gaz. Assure des transitions douces entre accélération et freinage. |
La Compensation de Latence : Le Secret de la Stabilité
Un détail technique fait toute la différence dans notre nœud : la compensation explicite de latence. Nous avons mesuré un délai d'environ 450ms entre l'envoi d'une commande et la réaction effective des actionneurs dans le simulateur. Le MPC intègre cette donnée : avant même de lancer son optimisation, il projette l'état actuel de la voiture 450ms dans le futur. Ainsi, il ne calcule pas la commande pour "maintenant", mais pour "le moment où la commande sera exécutée". Cette anticipation élimine les oscillations et permet une conduite stable même à haute vitesse, surpassant largement les capacités du PID.
3. Visualisation & Analyse
Au-delà des algorithmes de contrôle, la compréhension du système passe par une visualisation performante. Nous avons développé un nœud dédié, dashboard.py, qui agit comme une tour de contrôle en temps réel.

A. Une Architecture Découplée
Contrairement à une simple fenêtre de debug intégrée au code de contrôle, le Dashboard est un nœud ROS 2 totalement indépendant. Il écoute passivement les données via des topics dédiés (/road_path et /telemetry). Cette architecture a un avantage majeur : la visualisation ne ralentit pas le contrôle.
Le topic /telemetry est standardisé : que ce soit le MPC, le PID ou le Teleop, tous publient leur état interne sur ce canal unique. Le Dashboard est ainsi agnostique à la méthode de contrôle utilisée.
B. Les 4 Piliers de l'Analyse
L'interface est divisée en quatre panneaux stratégiques, chacun répondant à une question précise sur l'état du véhicule :
I. Carte Globale & Prédiction
C'est la vue "aérienne". Elle affiche la voiture (bleu), le tracé de la route (noir) et l'élément le plus précieux pour le debug : la Ligne Verte.
Cette ligne verte n'est pas la trajectoire passée, mais la prédiction du futur calculée par le MPC. Si la voiture quitte la route, regarder cette ligne permet de savoir instantanément si l'IA savait qu'elle allait sortir (ligne verte hors piste = mauvaise planification) ou si elle a été victime d'une physique imprévue (ligne verte sur la piste mais voiture dehors = problème de modèle/actionneurs).
Note
En mode PID ou Teleop, cette ligne verte peut être absente ou représenter une projection géométrique simple.

II. Commandes Actuateurs
Ce graphique superpose les trois entrées du véhicule : - Volant (Rouge) : Permet de vérifier la fluidité de la conduite. Une courbe hachée indique un contrôleur instable ou mal réglé (oscillations). - Gaz (Bleu) & Frein (Vert) : Permet de visualiser les transitions. Un bon contrôleur ne doit jamais accélérer et freiner en même temps.

III. Vitesse
Un graphique simple mais essentiel pour vérifier la stabilité de la consigne de vitesse. Il permet de voir si le véhicule parvient à maintenir sa vitesse cible ou s'il oscille autour.

IV. Erreur Latérale CTE
C'est le juge de paix. Ce graphique trace l'écart entre la voiture et le centre de la voie en mètres. Pour faciliter la lecture rapide, nous avons intégré des Zones de Danger visuelles : - Background Rouge (> 5m) : Si la courbe entre dans cette zone, c'est une sortie de route critique. - Zone Blanche (< 5m) : Conduite nominale.

C. Le flux de données
Le point fort de cette architecture est l'unification des messages. Le MPC, le PID et le Teleop packagent toutes leurs données vitales (Commandes, Vitesse, CTE, Pose, Prédictions) dans un même format Float64MultiArray sur le topic /telemetry.
Le Dashboard n'a pas besoin de savoir qui conduit. Il s'abonne simplement à /telemetry et affiche ce qu'il reçoit.
4. Infrastructure d'Enregistrement
Pour constituer notre dataset d'entraînement, nous ne pouvions pas nous contenter d'enregistrer des "rosbags" ou des dossiers d'images JPEGs classiques, qui saturent rapidement les IOPS du disque dur. Nous avons conçu un nœud sur-mesure : record_optimized.py.

A. Stratégie "Chunking" Binaire
Au lieu d'écrire 3 fichiers images (Gauche, Centre, Droite) + 1 ligne CSV pour chaque frame (ce qui ferait 60 fichiers/seconde à 20Hz !), le recorder accumule les données en mémoire vive (RAM).
Tous les 500 frames, il décharge le buffer d'un seul coup dans un gros fichier binaire unique (.bin).
Cette approche réduit l'activité du disque d'un facteur 1500, permettant d'enregistrer en haute résolution sans aucun lag, même sur des machines modestes.
B. Synchronisation Temporelle
Le nœud utilise un ApproximateTimeSynchronizer pour garantir que les trois images (Gauche, Centre, Droite) correspondent exactement au même instant \(t\). Il y associe ensuite le dernier paquet /telemetry reçu.
Ainsi, chaque échantillon du dataset est une paire parfaite : \(X\) (3 Images) \(\leftrightarrow\) \(Y\) (Commandes Volant/Gaz + Vitesse + CTE).
C. Format de Fichier (.bin)
Les fichiers générés sont structurés pour une lecture ultra-rapide lors de l'entraînement. Voici la représentation mémoire exacte du format :
| Offset (Bytes) | Contenu (Description) |
|---|---|
| HEADER GLOBAL | |
| \(0\) | Nombre de frames \(N\) (uint64, 8 bytes) |
| TABLE D'INDEX | |
| \(8\) | Index \(i_0\) (Pointeur vers Sample 0) |
| \(16\) | Index \(i_1\) (Pointeur vers Sample 1) |
| \(\vdots\) | \(\vdots\) |
| \(8(N-1) + 8\) | Index \(i_{N-1}\) (Pointeur vers dernier Sample) |
| SAMPLE 0 (à l'adresse \(i_0\)) | |
| \(i_0\) | Télémétrie (6 doubles) : 1. Steering (\(\delta\)) 2. Gaz (Throttle) 3. Frein (Brake) 4. Vitesse (\(v\)) 5. Erreur Latérale (\(CTE\)) 6. Dérivée Erreur (\(dCTE\)) |
| \(i_0 + 48\) | Tailles Images (\(L_{len}, C_{len}, R_{len}\)) (3 x uint64) |
| \(i_0 + 72\) | Données Brutes Images (JPEG Bytes) |
| SAMPLE 1 (à l'adresse \(i_1\)) | |
| \(i_1\) | Télémétrie (Structure identique au Sample 0) |
| \(i_1 + 48\) | Tailles Images (\(L_{len}, C_{len}, R_{len}\)) |
| \(i_1 + 72\) | Données Brutes Images (JPEG Bytes) |
| \(\vdots\) | \(\vdots\) (Répétition jusqu'au Sample \(N-1\)) |
Ce format compact est la clé pour entraîner des modèles sur des centaines de milliers d'images sans goulot d'étranglement CPU/Disque.
Niveau Théorique Terminé !
Bon, assez parlé de vecteurs et de bits ! Vous avez survécu à la théorie du MPC, des PIDs et des structures binaires.
Il est temps de passer aux choses sérieuses : Faire rouler cette voiture. Rendez-vous à la page suivante pour la Pratique (aka "Comment lancer les scripts sans tout casser").