Carte et localisation
Dans sa configuration de base, le RatBot n’embarque aucun capteur
extéroceptif : sa seule source de localisation propre est l’odométrie,
qui dérive au fil du déplacement. Un lidar RPLidar optionnel peut être
monté au sommet du robot (activable via le paramètre use_lidar de la
description) ; il ouvre la voie au SLAM embarqué décrit plus bas. Cette page décrit comment le robot peut malgré tout disposer
d’une carte et d’une pose corrigée, selon le contexte d’utilisation.
Carte fournie par un système extérieur
Le robot peut recevoir une carte d’un système hôte. Par exemple, avec un système de caméras fixes :
les caméras détectent le marqueur ArUco du robot et recalent sa pose (l’odométrie ne sert qu’entre deux observations) ;
le robot peut demander la carte globale au système central et l’utiliser pour naviguer.
TODO : interface exacte côté RatBot (souscription à une carte de type
nav_msgs/OccupancyGrid, service de requête…), à définir de façon
générique pour ne dépendre d’aucun hôte en particulier.
Carte propre du robot : SLAM
Avec le lidar, le RatBot construit sa propre carte par SLAM
(slam_toolbox). L’architecture est en couches optionnelles, jamais
lancées par le bringup du robot :
ratbot_slam— produit des cartes (dépend deslam_toolbox) ;ratbot_nav— consomme des cartes (dépend denav2) ;ratbot_maps— paquet de données (pgm/yaml + posegraph), zéro code :ratbot_slamy écrit,ratbot_navy lit. Les deux restent découplés.
Le SLAM tourne dans le namespace ratbot_N : il s’abonne à
ratbot_N/scan, publie ratbot_N/map et la TF
ratbot_N/map → ratbot_N/odom.
Important
Un seul parent par frame TF. En simulation, spawn_robot.launch.py
publie une TF de vérité terrain world → ratbot_N/odom. Le SLAM (et
plus tard AMCL) publie ratbot_N/map → ratbot_N/odom : odom
aurait alors deux parents. Il faut donc lancer la simulation avec
ground_truth_tf:=false quand on fait du SLAM ou de la nav.
Workflow (en simulation, robot 1) :
# 1. simulation SANS vérité terrain
ros2 launch ratbot_gazebo gazebo.launch.py ground_truth_tf:=false \
world:=$(ros2 pkg prefix ratbot_gazebo)/share/ratbot_gazebo/worlds/hampton_signature.sdf
# 2. SLAM (+ RViz)
ros2 launch ratbot_slam slam.launch.py robot_id:=1
# 3. téléopérer pour couvrir tout le labyrinthe
ros2 run teleop_twist_keyboard teleop_twist_keyboard \
--ros-args -r cmd_vel:=/ratbot_1/base/cmd_vel
# 4. sauvegarder la carte dans le dossier source de ratbot_maps
# (depuis ros2_ws/ ; le fichier devient committable)
ros2 run nav2_map_server map_saver_cli \
-f src/ratbot_maps/maps/hampton_signature \
--ros-args -r map:=/map
La carte est un artefact partageable : un autre robot ne refait pas de SLAM, il charge le même fichier et se localise dedans (AMCL, voir la navigation). Sur un système hôte doté d’un serveur central, cette carte peut aussi lui être partagée.
Exploration autonome (ratbot_slam)
ratbot_slam explore.launch.py fait cartographier le robot seul, en
réutilisant le cœur nav2 au-dessus de la carte vivante du SLAM :
slam_toolboxconstruit/mapen direct et publiemap → ratbot_N/odom(icimap_frame='map', la frame globale de nav2 — pas demap_serverni d’AMCL) ;bringup_nav2.launch.py: le cœur nav2, dont la costmap globale s’abonne au/mapvivant ;explore_lite(m-explore-ros2) détecte les frontières (limite connu/inconnu) sur la carte du SLAM,/map, et envoie des buts surnavigate_to_pose. Il s’arrête quand il n’y a plus de frontière (enceinte fermée).
Pourquoi la carte du SLAM et pas la costmap globale : la recherche de
frontières d’explore_lite ne progresse que vers des cases de coût égal ou
inférieur. Avec l’inflation de navigation de 0,50 m, le milieu d’une porte de
0,8 m porte déjà un coût, et la recherche s’arrêtait au seuil des portes : 44 %
du décor lisv exploré, couloirs seulement, contre 95 % sur la carte du
SLAM, Nav2 gardant ses 50 cm (campagnes du 2026-10-26 et du 2026-10-27).
explore_lite vient de notre fork (ratbot.repos, branche
fix/blacklist-reached-frontier), qui ajoute le paramètre
blacklist_reached. Sans lui, une frontière que le lidar ne peut pas lever
(au pied du robot, sous sa portée minimale de 0,2 m) restait choisie : Nav2
annonçait le but atteint sans bouger, et explore_lite, qui ne mettait en
liste noire que les buts abandonnés, le renvoyait sans fin. Le robot restait
figé, parfois bien avant la fin : quatre explorations de lisv sur quatre
s’y sont arrêtées, entre 75 et 97 % du plan. Avec blacklist_reached, la
frontière atteinte passe en liste noire (campagne du 2026-10-27 au cahier).
Correction à proposer au projet d’origine.
Réglage du choix des frontières (campagne du 2026-10-27/28, décor lisv,
3 passages par réglage, arrêt à 3000 s simulées) :
|
couverture, médiane (min–max) |
frontières restantes |
|---|---|---|
0,1 Hz / 0,15 m |
92,6 % (87,6–94,0) |
55 à 62 |
0,33 Hz / 0,15 m |
93,3 % (88,1–95,2) |
65 à 81 |
0,1 Hz / 0,75 m (retenu) |
95,2 % (91,2–96,5) |
6 à 8 |
À 0,15 m, les trous de quelques cases au milieu des pièces devenaient des frontières que le robot allait voir une à une, et l’exploration ne finissait jamais. À 0,75 m (valeur conseillée par le projet d’origine sur une carte de SLAM), un passage sur trois s’est arrêté de lui-même (« All frontiers traversed »), et les demi-tours tombent de 36 à 27 en médiane. 0,33 Hz explore plus vite à mi-parcours mais envoie 70 % de buts en plus. Limite : une frontière de moins de 0,75 m est ignorée ; à vérifier dans les labyrinthes de Hampton (couloirs de 0,6 m), pour lesquels 0,15 m avait été choisi.
C’est ce que permet le split : la même brique bringup_nav2 sert la
navigation sur carte statique (AMCL) et l’exploration sur carte
vivante (SLAM).
explore_lite n’est pas dans l’apt : il est vendoré via
ros2_ws/ratbot.repos (vcs import), depuis notre fork
hdd-robot/m-explore-ros2, branche fix/blacklist-reached-frontier
(un commit au-dessus de main d’origine, pour une future pull request).
vcs import --skip-existing ne remplace pas un clone déjà présent :
ratbot build compare l’adresse du clone à celle de ratbot.repos et
avertit ; supprimer src/m-explore-ros2 et relancer le build. Pour
travailler sur la correction : git -C ros2_ws/src/m-explore-ros2 log,
commits en anglais (destinés au dépôt d’origine). Le paquet multirobot_map_merge
du même dépôt (fusion multi-robots) est ignoré pour l’instant
(COLCON_IGNORE) — il resservira pour l’exploration multi-robots.
cd ros2_ws
vcs import src < ratbot.repos
touch src/m-explore-ros2/map_merge/COLCON_IGNORE
colcon build && source install/setup.bash
ros2 launch ratbot_gazebo gazebo.launch.py ground_truth_tf:=false \
world:=$(ros2 pkg prefix ratbot_gazebo)/share/ratbot_gazebo/worlds/rooms.sdf
ros2 launch ratbot_slam explore.launch.py robot_id:=1
Trajets appris
« Apprendre puis refaire » (page utilisateur « Apprendre et refaire un
trajet ») : ratbot route record lance ratbot_route_record, qui lit la
TF map → ratbot_N/base_footprint à 10 Hz et garde une pose tous les 10 cm
ou 15° (ratbot_nav/route.py, testé dans test/test_route.py) ; Ctrl-C
écrit ratbot_maps/routes/NOM.yaml. ratbot route follow lance
ratbot_route_follow :
il ramène d’abord le robot au départ s’il en est à plus de 30 cm (
NavigateToPose). Sans cela, un robot déjà à l’arrivée voyait Nav2 déclarer le trajet fait aussitôt ;mode
points:NavigateThroughPoses, un point tous les 50 cm. L’arbre de Nav2 pour ce cas a été copié (ratbot_nav/behavior_trees/) pour une seule raison : son nœudRemovePassedGoalscherche le robot dansbase_linkpar défaut, et aucun trajet par points ne démarrait ; le marqueur__ROBOT_NAMESPACE__y est remplacé au lancement (pas de<dans un attribut XML) ;mode
exact:FollowPathsur la ligne enregistrée, sans replanifier ;les poses sont datées à zéro (« la plus récente » pour tf2) : datées à l’heure du PC, la simulation les rejetait — « Transform data too old » — et le contrôleur se disait arrivé sans avoir bougé ;
l’arrivée est vérifiée par le nœud (moins de 30 cm du but), pas seulement crue sur parole ;
Ctrl-C annule le but : rclpy est initialisé sans son gestionnaire de signaux, sinon il coupait la liaison avant l’annulation et Nav2 aurait continué de conduire.
Essais du 2026-10-15, décor rooms, trajet de 2 m passant une porte :
points 6 cm d’écart moyen à la ligne apprise (10 cm au plus), exact
3 cm (6 cm). Échecs restants, tous en repartant de l’encadrement de la porte
en virage : la limite de Pure Pursuit notée dans Simulation.