Module L20 · Partie I · Robotique et systèmes embarqués
Où suis-je ? Localisation, cartographie, SLAM, et l’architecture d’un robot.
Un robot autonome doit répondre à trois questions : où suis-je (localisation), à quoi ressemble le monde (cartographie), et comment faire les deux en même temps sans connaître ni l’un ni l’autre (SLAM — le problème qui a occupé la robotique pendant 30 ans). Ce module construit un filtre particulaire, une carte d’occupation, un SLAM par graphe, puis présente ROS 2, le système qui assemble tout cela sur un vrai robot.
Durée : 4 séances · Prérequis : L05 (graphes, A*), L11 (Bayes), L19 (EKF). Objectifs : localisation de Markov et filtre particulaire (MCL), modèle de télémètre, cartes d’occupation (log-odds), scan matching (ICP, idée), SLAM par graphe (optimisation de poses), fermeture de boucle, ROS 2 (nœuds, topics, services, tf2, Nav2), et où en est la recherche (SLAM visuel, cartes sémantiques).
Ce que vous saurez faire à la fin
- Localiser un robot simulé dans une carte connue avec 500 particules et un lidar.
- Construire une carte d’occupation à partir de scans et de poses.
- Formuler et résoudre un petit SLAM par graphe avec Gauss-Newton.
- Lire l’architecture ROS 2 d’un robot et écrire un nœud.
Références : Probabilistic Robotics chapitres 4-8 et 9-11, cours « SLAM » de Cyrill Stachniss (YouTube, Bonn), documentation ROS 2 et Nav2, « A Tutorial on Graph-Based SLAM » (Grisetti et al.).
Fiche de cours · Définitions
Localisation, cartographie, ROS : définitions
Fiche de cours · Formules
Formules à connaître
Fiche de cours · Théorèmes et démonstrations
Démonstrations à savoir refaire (1/2)
Fiche de cours · Théorèmes et démonstrations
Démonstrations à savoir refaire (2/2)
Fiche de cours · Méthodes
Méthodes et pièges
Pièges : angles non normalisés ; multiplier 360 vraisemblances de rayons (produit → 0, confiance excessive : sous-échantillonner ou lisser) ; carte et scan dans des repères différents (map vs odom) ; horodatages du lidar et de l’odométrie non synchronisés (à 1 m/s, 50 ms = 5 cm) ; ICP sans initialisation ; fermeture de boucle fausse (deux couloirs identiques) qui détruit la carte.
Fiche de cours · Exercices corrigés
Exercices corrigés
01 / Localiser
Le monde simulé : une carte, un robot, un lidar
Le lancer de rayon est le modèle de capteur : « si j’étais en (x, y, θ), que verrais-je ? ». Toute la localisation probabiliste repose sur la comparaison entre ce que le robot verrait à une pose hypothétique et ce qu’il voit vraiment.
01 / Localiser
Filtre particulaire (Monte-Carlo Localization) : 300 hypothèses qui votent
Pourquoi des particules
Le filtre de Kalman suppose une croyance gaussienne : une seule bosse. Un robot kidnappé dans un couloir symétrique a une croyance à plusieurs bosses, et la carte rend le modèle de mesure violemment non linéaire. Le filtre particulaire (module L11 : Bayes séquentiel) représente n’importe quelle distribution par des échantillons : prédiction = bouger les particules, correction = les pondérer, rééchantillonnage = concentrer. Coût : O(N × rayons) par pas — d’où les astuces réelles : rééchantillonner seulement quand les poids dégénèrent (N_eff), rayons sous-échantillonnés, tables de distance précalculées, KLD-sampling pour adapter N. C’est l’algorithme d’AMCL dans ROS, utilisé par des dizaines de milliers de robots.
02 / Cartographier
Carte d’occupation : chaque case accumule des preuves (log-odds)
Log-odds : additionner au lieu de multiplier
Bayes met à jour P(occupé | mesures) par des produits ; en log-odds l = ln(p/(1−p)), chaque mesure ajoute une constante : rapide, stable, et l’inconnu vaut 0. La carte d’occupation (Moravec & Elfes, 1985) est la représentation standard pour la navigation 2D ; en 3D, OctoMap (octrees) ; pour les robots récents, des cartes de distances signées (TSDF) ou des champs neuronaux (NeRF). Sa limite : elle suppose les poses connues. Si les poses viennent de l’odométrie, la carte se déforme avec la dérive — d’où le SLAM.
03 / SLAM
Le problème de la poule et de l’œuf, et le SLAM par graphe
Pourquoi ça marche, et à quelle échelle
Chaque mesure relative est une contrainte élastique entre deux poses ; la fermeture de boucle ajoute un ressort entre la fin et le début, qui « tire » toute la trajectoire vers sa forme cohérente. On minimise une somme de carrés pondérés par l’information Ω = Σ⁻¹ (module L12, Gauss-Newton). La matrice H est creuse (chaque arête ne touche que 6 lignes) : avec Cholesky creux, on optimise des graphes de 100 000 poses en secondes (g2o, GTSAM, Ceres). Ajouter des amers (positions de balises, points 3D) donne le SLAM complet ; le module « frontal » qui détecte les fermetures de boucle (reconnaissance de lieux par images ou scans) est aussi crucial que le « dorsal » qui optimise. Cartographer (Google), ORB-SLAM3 et RTAB-Map sont construits ainsi.
03 / SLAM
Aligner deux scans : ICP, le scan matching de base
ICP (Besl & McKay, 1992) est l’algorithme qui produit les contraintes entre poses successives à partir de scans (ou de nuages 3D). Le sous-problème « meilleure transformation rigide pour des appariements donnés » a une solution exacte par SVD (Procrustes orthogonal) — l’un des joyaux de l’algèbre linéaire appliquée. Limites : bassin de convergence étroit (il faut une bonne initialisation, l’odométrie), sensibilité aux valeurs aberrantes (variantes robustes, point-à-plan, NDT).
04 / ROS 2
ROS 2 : le système d’exploitation (qui n’en est pas un) des robots
# Un robot ROS 2 = des NŒUDS (processus) qui échangent des MESSAGES sur des TOPICS (patron Observateur, module L02)
$ ros2 node list
/lidar_driver /imu_driver /amcl /map_server /planner_server /controller_server /motor_driver /camera /detector
$ ros2 topic list
/scan /imu/data /odom /tf /map /cmd_vel /camera/image_raw /detections /goal_pose
$ ros2 topic echo /scan --once # voir un message LaserScan : angle_min, ranges[…]
$ ros2 topic hz /scan # 10 Hz
$ ros2 run teleop_twist_keyboard teleop_twist_keyboard # publier /cmd_vel au clavier
$ ros2 bag record /scan /odom /tf # enregistrer pour rejouer hors ligne (indispensable pour déboguer)
$ rviz2 # visualiser carte, scans, particules, plan# Un nœud minimal en Python (rclpy) : s'abonne au lidar, publie une commande d'arrêt d'urgence
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist
class ArretUrgence(Node):
def __init__(self):
super().__init__("arret_urgence")
self.pub = self.create_publisher(Twist, "/cmd_vel", 10)
self.create_subscription(LaserScan, "/scan", self.sur_scan, 10)
self.declare_parameter("distance_min", 0.3) # réglable sans recompiler : ros2 param set
def sur_scan(self, msg):
devant = [r for r in msg.ranges[len(msg.ranges)//2 - 10 : len(msg.ranges)//2 + 10] if msg.range_min < r < msg.range_max]
if devant and min(devant) < self.get_parameter("distance_min").value:
self.pub.publish(Twist()) # vitesses nulles
self.get_logger().warn(f"obstacle à {min(devant):.2f} m : STOP")
rclpy.init(); rclpy.spin(ArretUrgence())Les concepts
| Concept | Rôle |
|---|---|
| Nœud | Un processus avec une responsabilité (pilote, algorithme, interface). |
| Topic | Flux de messages typés, publication/abonnement, plusieurs à plusieurs. |
| Service | Requête/réponse synchrone (« recharge la carte »). |
| Action | Tâche longue avec retour de progression et annulation (« va à ce but »). |
| Paramètre | Configuration modifiable à chaud. |
| tf2 | Arbre des repères (map → odom → base_link → lidar) et leurs transformations dans le temps (module L10). |
| Launch | Fichier Python qui démarre et configure des dizaines de nœuds. |
| DDS / QoS | Couche de transport (UDP multicast) avec qualité de service : fiable ou « best effort », historique, échéances. |
ROS 2 apporte ce que la super-boucle ne peut pas : composer des logiciels écrits par des équipes différentes (un lidar Velodyne, un planificateur Nav2, votre détecteur), en C++ ou Python, sur plusieurs machines. Le prix : latences de quelques millisecondes et une complexité qu’il faut apprivoiser. Le microcontrôleur, lui, garde le temps réel dur (module L18) et parle à ROS par micro-ROS ou un pont série.
04 / ROS 2
La pile de navigation : de la carte au mouvement
| Étage | Nœud Nav2 | Ce module / le cours |
|---|---|---|
| Carte | map_server, slam_toolbox | Carte d’occupation, SLAM par graphe |
| Localisation | amcl | Filtre particulaire |
| Planification globale | planner_server (NavFn, Smac) | A* / Dijkstra sur la grille (L05), avec coûts d’inflation autour des obstacles |
| Planification locale / contrôle | controller_server (DWB, MPPI, RPP) | Fenêtre dynamique : échantillonner (v, ω), simuler 1 s, scorer, choisir (L21) |
| Comportements | bt_navigator (arbre de comportement) | Machine à états de la séance 28, en plus structuré : « si bloqué, reculer, replanifier » |
| Sécurité | collision_monitor, velocity_smoother | Arrêt d’urgence par lidar, limitation d’accélération |
05 / Frontière
Où en est la recherche
- SLAM visuel et visuel-inertiel (ORB-SLAM3, VINS, Kimera) : une caméra et une IMU suffisent ; c’est ce qui tourne dans les casques de réalité augmentée et les drones. Questions ouvertes : robustesse à la lumière, aux scènes dynamiques, à long terme (le monde change).
- Cartes neuronales : NeRF et gaussian splatting représentent la scène par un réseau ou des gaussiennes 3D ; on peut y localiser une caméra et y planifier. Compromis mémoire / précision / temps réel encore débattus.
- Cartes sémantiques : ne pas cartographier des cases occupées mais des objets (« la tasse est sur la table ») avec des modèles de vision-langage ; nécessaire pour les instructions en langage naturel (module L15).
- Multi-robots : fusionner les cartes de plusieurs robots avec des communications intermittentes.
- Apprentissage de bout en bout : des politiques (module L16) qui vont des pixels aux moteurs sans carte explicite. Marche pour certaines tâches, mais difficile à vérifier et à déboguer — le débat « carte explicite ou implicite » est vif.
- Certification : comment prouver (module L09) qu’un système de localisation probabiliste est sûr ? Sujet central pour la voiture autonome.
Un projet de TIPE ou de stage de L3 accessible : implémenter un SLAM 2D par graphe sur un robot à lidar bon marché (RPLidar A1) avec ROS 2, et comparer à slam_toolbox. C’est faisable en un semestre avec les outils de ce module.
Cours
Cours 1 — Le filtre bayésien général et ses quatre incarnations
L’équation unique. Croyance bel(xt) = P(xt | z1..t, u1..t). Deux étapes récursives : prédiction bel⁻(xt) = ∫ P(xt | xt−1, ut) bel(xt−1) dxt−1 (le modèle de mouvement étale la croyance) ; correction bel(xt) = η P(zt | xt) bel⁻(xt) (le modèle de mesure la concentre). Tout dépend de comment on représente bel :
| Représentation de bel | Filtre | Prédiction | Correction | Forces / limites |
|---|---|---|---|---|
| Gaussienne (μ, Σ), modèles linéaires | Kalman (L19) | μ ← Fμ + Bu ; Σ ← FΣFᵀ + Q | Gain K | Exact et optimal ; unimodal, linéaire |
| Gaussienne, modèles non linéaires | EKF / UKF | Linéarisation / points sigma | Idem | Rapide ; peut diverger ; unimodal |
| Histogramme sur une grille | Filtre à grille (localisation de Markov) | Convolution par le modèle de mouvement | Multiplication case par case | Multimodal, exact sur la grille ; coût exponentiel en dimension |
| Échantillons pondérés | Particulaire (MCL) | Déplacer chaque particule | Pondérer, rééchantillonner | Multimodal, tout modèle ; coût en nombre de particules, dégénérescence |
Choisir. État de petite dimension, croyance possiblement multimodale (localisation globale, symétries) → particulaire. État de grande dimension (SLAM avec 1000 amers), croyance unimodale → EKF/UKF ou, mieux, optimisation par graphe. Beaucoup de systèmes combinent : particules pour initialiser, Kalman pour suivre (Rao-Blackwellisation : FastSLAM).
Cours
Cours 2 — Exemple travaillé : le modèle de vraisemblance d’un télémètre, et pourquoi le gaussien naïf échoue
Le cours utilise P(z | x) ∝ exp(−Σ(zi − ẑi)²/2σ²) : gaussienne, rayons indépendants. C’est faux de trois façons, et chaque défaut a une parade classique (Probabilistic Robotics, ch. 6) :
- Aberrations : un rayon qui touche un objet non cartographié (personne, chaise) donne une distance plus courte. Parade : mélange de lois — gaussienne + exponentielle décroissante (objet inattendu) + pic à la portée max (rien vu) + uniforme (bruit). Poids ≈ 0,7 / 0,1 / 0,1 / 0,1 : une aberration ne tue plus la particule.
- Indépendance : 360 rayons ne sont pas indépendants (un mur = des centaines de rayons corrélés) ; la vraisemblance devient sur-confiante (10−300). Parade : sous-échantillonner (30 rayons), ou élever la vraisemblance à une puissance < 1.
- Coût : le lancer de rayon pour chaque particule est lent. Parade : modèle de champ de vraisemblance : précalculer la distance de chaque case au mur le plus proche (transformée de distance, BFS L05) ; la vraisemblance d’un impact = gaussienne de cette distance ; pas de lancer de rayon.
Cours
Cours 3 — Anatomie d’un système ROS 2 de navigation : fichiers, nœuds, repères, débogage
mon_robot/ # un « workspace » ROS 2 (colcon)
└── src/
├── mon_robot_description/ # URDF : géométrie, repères (base_link, laser, imu), inertie ; visualisable dans RViz
├── mon_robot_bringup/ # launch/ : robot.launch.py démarre pilotes + tf + nav ; config/ : YAML des paramètres
├── mon_robot_driver/ # nœud C++/Python qui parle au microcontrôleur (série, L22) : publie /odom, /imu, souscrit /cmd_vel
├── mon_robot_nav/ # config Nav2 (planificateur, contrôleur, coûts), cartes, comportements
└── mon_robot_perception/ # vos nœuds : détecteur, filtre, ...
$ colcon build --symlink-install && source install/setup.bash
$ ros2 launch mon_robot_bringup robot.launch.py
$ ros2 run tf2_tools view_frames # PDF de l'arbre : map → odom → base_link → laser
$ ros2 topic hz /scan ; ros2 topic echo /tf ; ros2 param list /amcl
$ ros2 bag record -a ; ros2 bag play ma_session| Repère | Publié par | Signification |
|---|---|---|
| map → odom | AMCL / SLAM | Correction lente, peut sauter (fermeture de boucle) |
| odom → base_link | Odométrie (pilote, EKF robot_localization) | Continu, lisse, dérive |
| base_link → laser / imu / camera | URDF (statique) | Où sont les capteurs sur le châssis : à mesurer au millimètre |
Les bugs ROS les plus fréquents : horloges non synchronisées (use_sim_time, horodatages : « message too old ») ; repères manquants ou mal nommés (tf : « could not transform ») ; QoS incompatibles (l’abonné ne reçoit rien : « best effort » vs « reliable ») ; deux nœuds publiant le même tf ; unités (degrés au lieu de radians, mm au lieu de m). Outils : rqt_graph, rqt_tf_tree, ros2 doctor, rviz2.
TP guidé
TP — SLAM et navigation avec ROS 2 en simulation, puis sur robot (sur PC, 4 h)
- Installation. Ubuntu 22.04 (natif, VM, ou WSL2 avec WSLg) ; ROS 2 Humble ou Jazzy (paquets
ros-humble-desktop,ros-humble-navigation2,ros-humble-turtlebot3*,ros-humble-slam-toolbox). Tutoriels « Beginner: CLI tools » (2 h) : nœuds, topics, services, paramètres, launch. - Simulation TurtleBot3.
export TURTLEBOT3_MODEL=burger ; ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py. Pilotez au clavier ;ros2 topic echo /scan; enregistrez un bag de 2 minutes en explorant. - Cartographie.
ros2 launch slam_toolbox online_async_launch.py; explorez ; observez la carte se construire dans RViz ; sauvegardez (ros2 run nav2_map_server map_saver_cli -f ma_carte). Rejouez le bag avec SLAM : même carte ? Retirez la fermeture de boucle (paramètre) : constatez la dérive. - Localisation et navigation.
ros2 launch turtlebot3_navigation2 navigation2.launch.py map:=ma_carte.yaml; pose initiale dans RViz ; envoyez un but ; observez le plan global, le chemin local, les particules d’AMCL. Kidnappez le robot (téléportation dans Gazebo) : AMCL récupère-t-il ? Réglezmax_particles,alphaet mesurez. - Votre nœud. Un nœud Python qui souscrit
/scan, publie unMarkerRViz sur l’obstacle le plus proche, et un service « aller au point le plus dégagé » qui envoie un but à Nav2 via l’actionNavigateToPose. Paramètres déclarés, QoS explicites, tests aveclaunch_testing. - Robot réel (si disponible). Un châssis + RPLidar A1 + Raspberry Pi 4 : pilote série vers le microcontrôleur (L22), URDF avec la position du lidar,
robot_localizationpour fusionner odométrie et IMU, slam_toolbox, Nav2. Objectif : cartographier une pièce et naviguer d’un point à l’autre de façon autonome. - Livrable. Workspace sur GitHub, carte, vidéo/gif de navigation, arbre tf (PDF),
RESULTATS.mdavec les réglages AMCL et leurs effets, et un schéma des nœuds (rqt_graph).
Exercices
Exercices auto-corrigés — filtres à grille et particules
Exercice 1 — Filtre à grille 2D avec modèle de mouvement incertain
Sur la carte du module (grille, cases libres), écrivez predire2d(bel, du, dv) (déplacement demandé (du, dv) réalisé avec probabilité 0,8, glissement ±1 case perpendiculaire avec 0,1 chacun, murs = on reste sur place) et corriger2d(bel, scan_obs, sigma) avec le lidar du module (4 rayons suffisent) ; le robot part inconnu, fait 8 pas ; vérifiez que l’argmax converge vers la vraie position (à 1 case près).
Correction
def predire2d(bel, du, dv):
nb = np.zeros_like(bel); perp = (dv, du)
for (dx, dy), p in (((du, dv), 0.8), ((du + perp[0], dv + perp[1]), 0.1), ((du - perp[0], dv - perp[1]), 0.1)):
for y, x in zip(*np.where(bel > 0)):
ny, nx = y + dy, x + dx
if 0 <= ny < H and 0 <= nx < W and libres[ny, nx]: nb[ny, nx] += p * bel[y, x]
else: nb[y, x] += p * bel[y, x]
return nb / nb.sum()
def corriger2d(bel, scan_obs, sigma=0.5):
v = np.zeros_like(bel)
for y, x in zip(*np.where(libres)):
att = lidar((x + 0.5, y + 0.5, 0), n_rayons=4, bruit=0.0); v[y, x] = np.exp(-0.5 * np.sum((scan_obs - att)**2) / sigma**2)
b = bel * v; return b / b.sum()Exercice 2 — Rééchantillonnage systématique et N_eff
Écrivez n_eff(poids) et reechantillonner_systematique(particules, poids) (une seule valeur aléatoire, variance minimale : algorithme du « peigne »). Vérifiez : la moyenne pondérée est conservée en espérance ; avec des poids uniformes, chaque particule est copiée exactement une fois ; N_eff vaut N pour des poids uniformes et 1 pour un poids concentré.
Correction
def n_eff(w): return 1.0 / np.sum(np.asarray(w)**2)
def reechantillonner_systematique(P, w):
N = len(w); u = (rng.random() + np.arange(N)) / N; cum = np.cumsum(w); idx = np.searchsorted(cum, u)
return P[np.minimum(idx, N - 1)]Exercices
Exercices auto-corrigés — cartes et graphes de poses
Exercice 3 — Transformations de poses 2D : composer, inverser, relatif
Écrivez composer(a, b) (pose b exprimée dans le repère a → pose dans le repère monde), inverse(a) et relatif(a, b) = composer(inverse(a), b), avec angles ramenés dans ]−π, π]. Vérifiez les identités : composer(a, relatif(a, b)) = b ; composer(a, inverse(a)) = identité ; associativité.
Correction
def composer(a, b):
c, s = np.cos(a[2]), np.sin(a[2])
return np.array([a[0] + c * b[0] - s * b[1], a[1] + s * b[0] + c * b[1], (a[2] + b[2] + np.pi) % (2 * np.pi) - np.pi])
def inverse(a):
c, s = np.cos(a[2]), np.sin(a[2])
return np.array([-c * a[0] - s * a[1], s * a[0] - c * a[1], (-a[2] + np.pi) % (2 * np.pi) - np.pi])
def relatif(a, b): return composer(inverse(a), b)Exercice 4 — Détecter une fermeture de boucle par comparaison de scans
Le robot enregistre des scans à 36 rayons à chaque pose. Écrivez signature(scan) invariante par rotation (par exemple l’histogramme des distances, ou le spectre d’amplitude de la FFT du scan) et candidats_boucle(scans, i, seuil) qui renvoie les indices j ≤ i − 10 dont la signature est proche de celle de i. Vérifiez sur des scans simulés depuis la carte du module : deux scans pris au même endroit avec un cap différent doivent être détectés ; deux endroits différents non.
Correction
def signature(scan):
return np.abs(np.fft.rfft(scan))[1:8] # amplitudes des basses fréquences : invariantes par rotation du scan (décalage circulaire)
def candidats_boucle(scans, i, seuil):
s = signature(scans[i]); return [j for j in range(i - 9) if np.linalg.norm(signature(scans[j]) - s) < seuil]Un décalage circulaire du scan (changement de cap) ne change pas le module de sa transformée de Fourier : c’est la propriété qui rend la signature invariante. Les vrais systèmes utilisent des descripteurs plus riches (Scan Context, sacs de mots visuels), puis vérifient géométriquement par ICP avant d’ajouter la contrainte au graphe — sinon une fausse fermeture détruit la carte (défi ★★★).
06 / Défis
Défi ★ — Le robot kidnappé et le rééchantillonnage adaptatif
Consigne
1) Après convergence du MCL, téléportez le robot ailleurs : combien de pas pour que le filtre le retrouve ? Pourquoi peut-il échouer (toutes les particules sont concentrées au mauvais endroit) ? 2) Ajoutez l’injection de 5 % de particules aléatoires à chaque pas : mesurez le temps de récupération. 3) Implémentez le nombre effectif de particules N_eff = 1/Σw² et ne rééchantillonnez que si N_eff < N/2 : quel effet sur la stabilité ? 4) Placez le robot dans un couloir symétrique de la carte et montrez l’ambiguïté (deux nuages).
Piste
La dégénérescence : après rééchantillonnage, toutes les particules descendent de quelques ancêtres ; si l’ancêtre était faux, plus rien ne peut corriger. L’injection aléatoire (ou proportionnelle à la baisse soudaine de la vraisemblance moyenne, « augmented MCL ») est la parade standard. N_eff évite de perdre la diversité quand les poids sont déjà uniformes.
06 / Défis
Défi ★★ — SLAM complet en 2D : poses + amers
Consigne
Étendez le SLAM par graphe : le robot parcourt une trajectoire et observe 8 balises (position relative bruitée, identité connue) ; les inconnues sont les 20 poses ET les 8 positions de balises. 1) Écrivez les deux types d’arêtes (pose-pose, pose-balise) avec leurs jacobiennes numériques. 2) Optimisez ; comparez la carte des balises à la vérité. 3) Retirez la fermeture de boucle : que devient l’erreur ? 4) Vérifiez que H est creuse (affichez sa structure) et mesurez le temps avec scipy.sparse.linalg.spsolve si disponible, sinon commentez.
Piste
Arête pose-balise : e = R(θ_i)ᵀ(l_j − p_i) − z_ij (position de la balise dans le repère du robot). Les blocs de H pour la balise sont 2×2. Indexation : poses aux indices 3i, balises à 3n + 2j. Ce défi est exactement l’exercice du cours de Stachniss « least squares SLAM » ; le résoudre vous met au niveau d’un M1 en robotique.
06 / Défis
Défi ★★★ — Esprit prépa : consistance, jauge et fermeture de boucle robuste
Consigne
1) Montrez que sans fixer la première pose, la matrice H du SLAM par graphe est singulière (noyau de dimension 3 : translation et rotation globales) — expliquez pourquoi et vérifiez numériquement les valeurs propres. 2) Fausse fermeture de boucle : ajoutez une contrainte erronée (le frontal s’est trompé de lieu). Montrez que Gauss-Newton déforme toute la carte. Implémentez une fonction de coût robuste (Huber, ou « switchable constraints » : un poids s_ij ∈ [0, 1] optimisé avec un prior vers 1) et montrez que la fausse contrainte est désactivée. 3) Prouvez que l’EKF-SLAM (état = pose + toutes les balises, covariance pleine) a un coût O(n²) par mise à jour, et expliquez pourquoi le SLAM par graphe le supplante. 4) Discutez : quelle garantie peut-on donner qu’un SLAM ne « diverge » pas ? Cherchez « SLAM consistency » et résumez en 10 lignes l’état de l’art.
Piste (Huber)
Remplacer e ᵀΩe par ρ(‖e‖_Ω) avec ρ(x) = x² si x < δ, 2δx − δ² sinon : au-delà de δ, le coût croît linéairement et le gradient est borné, donc une contrainte aberrante ne « tire » plus que d’une force constante. En pratique (g2o, GTSAM) : IRLS — repondérer chaque arête par ρ′(x)/x à chaque itération. Le noyau de H : ajouter (δx, δy, 0) à toutes les poses, ou une rotation globale, ne change aucune erreur relative ; ces 3 directions sont les degrés de liberté de jauge.
07 / Vérification
Pourquoi AMCL utilise-t-il un filtre particulaire plutôt qu’un EKF pour localiser un robot dans une carte ?
Deux questions supplémentaires
1. Pourquoi borner les log-odds d’une carte d’occupation ? Pour qu’une case puisse changer d’état (porte ouverte/fermée) en un nombre raisonnable de mesures.
2. Que fait une fermeture de boucle dans un SLAM par graphe ? Elle ajoute une contrainte entre deux poses éloignées dans le temps, ce qui corrige la dérive accumulée sur tout le tour.
Référence
Les mots à retenir
| Mot | Définition |
|---|---|
| Lancer de rayon | Modèle de capteur : ce qu’on verrait depuis une pose. |
| MCL / filtre particulaire | Localisation par échantillons : prédire, pondérer, rééchantillonner. |
| N_eff, injection | Parades à la dégénérescence des particules. |
| Carte d’occupation | Grille de probabilités mises à jour en log-odds. |
| SLAM | Localisation et cartographie simultanées. |
| SLAM par graphe | Poses = nœuds, mesures = arêtes ; moindres carrés creux. |
| Fermeture de boucle | Reconnaître un lieu déjà visité ; contrainte qui corrige la dérive. |
| ICP | Aligner deux nuages de points par appariements itératifs et SVD. |
| Jauge | Degrés de liberté non observables (repère global) ; fixer une pose. |
| ROS 2 | Nœuds, topics, services, actions, tf2, launch, DDS. |
| Nav2 | Pile de navigation : carte, AMCL, planificateurs, contrôleur, arbre de comportement. |
Pour continuer
Le robot sait où il est
Module suivant : planification et contrôle — cinématique, planification de mouvement (RRT), commande optimale (LQR, MPC) : décider comment bouger.
À faire chez soi
- Installer ROS 2 (Ubuntu 22.04/24.04 ou Docker), faire les tutoriels « Beginner: CLI tools » et « Client libraries », lancer TurtleBot3 en simulation (Gazebo) avec Nav2.
- Regarder les cours SLAM de Cyrill Stachniss (« Graph-based SLAM », « Particle filter »).
- Lire Probabilistic Robotics chapitres 7 et 8 ; refaire les exercices en Python.