ratbot : le point d’entrée unique

./scripts/ratbot est la seule commande du projet. Elle est identique sur la machine de développement et sur les robots : c’est la machine qui décide de ce qu’elle installe, construit et lance.

./scripts/ratbot role        # ce que la machine croit être

Pourquoi un point d’entrée unique

Un seul dépôt, un seul jeu de sources, une seule commande. Ce choix évite trois dérives que l’on paie toujours plus tard.

Les procédures ne peuvent pas diverger. La commande lancée à la main est exactement celle qu’exécute systemd — ratbot.service appelle ratbot boot, pas une ligne recopiée. Une correction profite aux deux d’un coup.

Un robot neuf n’a rien à configurer. Le nom de machine porte déjà le numéro du robot, et tout en découle. Il suffit qu’il soit bien nommé.

On ne se trompe pas de rôle. La machine de développement ne construit pas les paquets du robot, et le robot ne construit pas ceux de la simulation.

Comment le rôle est déterminé

Dans cet ordre, la première réponse gagne :

Source

Usage

RATBOT_ROLE

variable d’environnement, priorité maximale — pour un essai ponctuel

le nom de machine

ratbot-3 → rôle robot, robot_id 3. C’est le cas normal

sinon

rôle dev

Le numéro du robot, lui, est toujours lu dans le nom de machine, même quand le rôle est forcé : forcer le rôle ne fait jamais perdre le numéro. À défaut de nom conforme, il vient de RATBOT_ID, puis vaut 1.

Note

Avant le 2026-10-11, le numéro n’était lu dans le nom que lorsque ce nom décidait du rôle. Un rôle forcé sur ratbot-2 donnait donc le robot 1 — et une commande visait le journal, les réglages ou le mode banc d’un autre robot, sans un message.

Un rôle autre que robot ou dev fait sortir la commande avec le code 2 : mieux vaut s’arrêter que d’agir sur une supposition.

La chaîne des conventions

digraph conventions { rankdir=LR; bgcolor="transparent"; node [shape=box, style="rounded,filled", fontname="sans-serif", fontsize=10, margin="0.18,0.10"]; edge [fontname="sans-serif", fontsize=9, color="#999999"]; host [label="hostname\\nratbot-3", fillcolor="#dae8fc", color="#6c8ebf"]; id [label="robot_id = 3", fillcolor="#f5f5f5", color="#999999"]; ns [label="namespace\\n/ratbot_3/", fillcolor="#d5e8d4", color="#82b366"]; tf [label="frames TF\\nratbot_3/…", fillcolor="#d5e8d4", color="#82b366"]; ip [label="192.168.1.173\\n= 170 + N", fillcolor="#ffe6cc", color="#d79b00"]; host -> id; id -> ns; id -> tf; id -> ip; }

Tout découle du nom de machine.

Le domaine DDS vaut 77 pour tout le parc. Il est figé dans le firmware de la carte, donc elle ne peut pas se tromper de domaine même si l’environnement de la Raspberry est mal réglé.

Le PC est réglé sur le même domaine : ~/.bashrc en natif, compose.yaml pour le conteneur Docker. Une simulation y croiserait les robots réels sous les mêmes noms ; ratbot up refuse donc de démarrer si un /ratbot_N répond déjà dans le domaine ROBOTS_DOMAIN.

Les commandes

Commande

Rôle

Ce qu’elle fait

role

les deux

affiche ce que la machine croit être : rôle, numéro, workspace, distribution, domaine DDS. À lancer en premier quand quelque chose surprend

install <fiche>

robot

met en service une Raspberry neuve ou clonée, depuis une fiche d’identité — voir Bringup. network.gateway est transmise à ratbot network (RATBOT_PASSERELLE), après comparaison à la route par défaut en service ; network.address n’existe plus (l’adresse vient de robot_id), une valeur contraire est refusée

deps

les deux

les paquets ROS du rôle, par apt

build

les deux

colcon build, en écartant les paquets de simulation sur un robot. Récupère d’abord, s’ils manquent, les paquets externes de ros2_ws/ratbot.repos (vcs import) ; ne touche ni à vcstool ni au réseau s’ils sont présents. Dans le conteneur Docker, build/, install/ et log/ sont les dossiers *_docker/ du PC, montés par compose.yaml : la compilation du PC n’y fonctionne pas

up [DECOR] [N] [args]

les deux

lance la pile du rôle. Sur le PC, la simulation : décor par son nom court, N robots (1 à 4) posés sur les places du décor (worlds/NOM.spawn.yaml, voir ratbot_gazebo/worlds.py), vérité terrain activée — --list lit les décors installés. Dans le domaine 77, refuse si un /ratbot_N y répond déjà (sim_hors_des_robots, 2 s d’écoute). Sur un robot, refuse si ratbot.service tourne déjà (il porte toute la pile)

boot

robot

la pile embarquée séquencée (boot.launch.py) : agent, attente de la carte, puis le reste. C’est ce que lance systemd — voir Bringup

agent

robot

l’agent micro-ROS seul, sur /dev/ttyAMA0 à 3 Mbaud, dans /ratbot_N — pour la mise au point, service arrêté. Même définition que celle de boot (micro_ros_agent.launch.py)

services

robot

installe les services systemd, la source de temps, le groupe dialout, et arrête les mises à jour automatiques

network

robot

l’adresse fixe netplan

time

robot

heure de secours depuis la RTC de la carte, en root, au démarrage — voir L’heure : une chaîne à trois maillons

config [N] show|save|load

les deux

paramètres de la carte : show, save, load — voir Les topics du robot

log [N]

les deux

journal de bord de la carte, --clear pour l’effacer

signals

robot

voyant et sons côté Raspberry, seuls — pour la mise au point. Le service les lance déjà, avec la pile

bench [N] on|off

les deux

mode banc : suspend la sécurité inertielle, pour faire tourner les roues robot posé sur sa boîte. Jamais enregistré dans la carte

slam N

PC

cartographie du robot N : vérifie qu’il est visible, remet son odométrie à zéro (l’origine de la carte), lance slam_toolbox et RViz. En simulation (/clock publié, fonction en_simulation) : use_sim_time:=true, pas de remise à zéro (l’origine est la place de départ), et refus si la vérité terrain tourne (nœud world_to_odom_ratbot_N)

map save [N] F

PC

enregistre dans F.yaml et F.pgm la carte du robot N, publiée par slam_toolbox sur /ratbot_N/map (map_name de slam.launch.py : une carte par robot). Sans N, /map, celle de l’exploration. F sans / ni .yaml : un nom, rangé dans MAPS_DIR (ros2_ws/src/ratbot_maps/maps, les SOURCES : pas de recompilation). Refus d’écraser une carte suivie par git

map list

PC

les cartes de MAPS_DIR, et le décor du même nom s’il existe

nav N [carte]

PC

navigation du robot N sur une carte : localisation, Nav2, RViz. Heure réelle, ou heure de la simulation si /clock est publié ; en simulation, même refus que slam si la vérité terrain tourne. La carte : un nom (carte_de) ou un chemin ; omise en simulation, celle du décor en cours (decor_en_cours : le monde Gz, /world/NOM/)

explore N [args]

PC

ratbot_slam/launch/explore.launch.py (slam_toolbox sur /map, cœur Nav2, explore_lite). Mêmes contrôles que slam : robot visible, simulation reconnue, refus si la vérité terrain tourne ; sur un robot réel, remise à zéro de l’odométrie (origine_ici) et avertissement : l’exploration réelle n’a été essayée qu’une fois, dans un couloir (2026-10-19)

fleet [show|status|battery|time|diag|who|identify N]

PC

python3 -m ratbot_bringup.fleet sur deploy/fleet.yaml : le fichier est validé (numéros uniques de 1 à 19, états connus, adresses hors de la plage des robots, interventions datées et dans l’ordre), puis chaque robot en service est interrogé en parallèle, une connexion SSH par robot en BatchMode (service, git rev-parse HEAD comparé en entier au PC, chronyc tracking ; un refus SSH est expliqué d’après stderr) et sensors/battery par ros2 topic echo --once, en best effort pour ne pas dépendre de la découverte. Les campagnes sont trouvées par ratbot_calibration.campaigns.find, le code de ratbot-calibrate (_exclus, abandonnées et simulations écartées ; robot reconnu au nom à défaut de campagne.yaml) ; une date applied illisible donne une alerte, pas une exception. Une intervention invalide les réglages de son champ affects (par défaut umbmark, gyro), pas le même jour. Code de retour 1 si un robot en service a une alerte. battery, diag, who et identify passent par une seule participation DDS (ros_snapshot : rclpy, domain_id du fichier ou --domain, abonnements best effort, 3 s), au lieu d’un ros2 topic echo par robot ; status lit aussi la batterie ainsi. time mesure l’écart de chaque Pi au PC par SSH (« ready », puis un seul aller-retour chronométré, date +%s.%N rapporté au milieu), lit chrony des deux côtés (timesyncd sur le PC à défaut), et l’écart RTC/Pi dans status/rtc (time_ref - header.stamp). diag garde le dernier état de chaque bloc et range par hardware_id (ratbot_N, que status_manager porte aussi désormais). who croise le graphe ROS (nœuds d’activité, éditeurs des entrées du multiplexeur) et les ps du PC et de la Pi (__node:= et __ns:=, ou le module d’un script). identify répète l’état identify de led_states.yaml sur status/set_state (maintenu 2 s) et SOUND_BEEP chaque seconde, après avoir attendu ses abonnés. Tests : ratbot_bringup/test/test_fleet.py, dont un qui vérifie que le domaine DDS du fichier est celui du firmware et du script

navtest [épreuves] [-n N] [-j J]

PC

python3 -m ratbot_gazebo.navtest : banc de navigation en simulation, chaque passage dans une simulation neuve, domaine 100 + place (voir Simulation, « Banc de navigation »). Refusé sur un robot

rviz N [args]

PC

ratbot_nav/launch/rviz.launch.py : RViz seul, robot.rviz recopié pour le robot N, repère ratbot_N/odom (publié par l’EKF dès le démarrage). use_sim_time vaut true si /clock est publié, c’est-à-dire en simulation

joy [args]

PC

lance joy_fleet.launch.py (une manette, robots 1 à 4) ; refuse si /dev/input/jsN est absent (device_id:=N, 0 par défaut) ou illisible (conteneur sans device_cgroup_rules, groupe input), car joy_node échouerait sans rien signaler

key N

PC

ratbot_bringup/key_teleop.py (nœud ratbot_key_teleop) remappé sur /ratbot_N/base/cmd_vel_teleop (la voie de la manette, priorité 100), 10 cm/s au départ ; vérifie d’abord que le robot est visible. Commande retenue, renvoyée à 10 Hz jusqu’à k ; une fois arrêté, l’arrêt est répété 0,5 s puis plus rien, pour rendre la main à twist_mux. Remplace teleop_twist_keyboard depuis le 2026-10-19 (à-coups : répétition du clavier à ~600 ms, chien de garde de la carte à 500 ms). rclpy sans gestion de SIGINT, pour que l’arrêt final parte. Tests : test/test_key_teleop.py

route record N NOM

PC

ratbot_nav route_record : pose map → ratbot_N/base_footprint tous les 10 cm ou 15°, écrite dans ROUTES_DIR (ros2_ws/src/ratbot_maps/routes, les SOURCES) au Ctrl-C ; refuse un nom déjà pris ; la carte est notée (décor en cours en simulation)

route follow N NOM [exact]

PC

ratbot_nav route_follow : retour au départ, puis NavigateThroughPoses (points tous les 50 cm) ou FollowPath (exact) ; voir Carte et localisation, « Trajets appris ». Un Ctrl-C entre l’envoi et l’acceptation attend la réponse pour annuler le but accepté (SIGINT rétabli au démarrage, même hérité ignoré) ; un trajet en boucle (arrivée à 30 cm du départ) est envoyé en deux buts, jusqu’à sa pose la plus éloignée du départ puis retour, car le contrôleur juge l’arrivée sur la fin du chemin, déjà atteinte ; l’attente de la pose du robot se compte en temps réel (l’horloge simulée saute à l’arrivée de /clock) ; route.load refuse un YAML qui n’est pas un dictionnaire et les valeurs non finies. Le RemovePassedGoals de l’arbre navigate_through_poses.xml a un rayon de 0,2 m (0,7 jusqu’au 2026-10-18 : avec des points tous les 50 cm, chacun était retiré avant d’être atteint)

route list

PC

les trajets de ROUTES_DIR, avec robot, carte, longueur et date

Le numéro du robot se donne en premier : slam 1, nav 1 …, log 2, bench 3 off. Sur un robot, il est facultatif pour log et bench et vaut le sien ; sur le PC, il est obligatoire. Un numéro par défaut, avec plusieurs robots sur le réseau, viserait le mauvais sans rien dire.

Ce que la commande délègue

Trois sujets sont assez gros pour vivre dans leur propre fichier, et ratbot leur passe la main sans rien réimplémenter :

Fichier

Appelé par

scripts/ratbot-install

ratbot install — mise en service d’une machine

scripts/ratbot-config

ratbot config — sauvegarde et restauration des paramètres

scripts/ratbot-profile

autonome — mesure le coût réel de la boucle micro-ROS

scripts/ratbot-calibrate

autonome — calibration guidée (N lidar, N gyro, N umbmark, N fusion) et consultation (list, stats, show, compare, compute, apply, exclude). compare lit le témoin enregistré par umbmark-run (witness_end, robot_localization quand le robot se pilote sur ses roues) : E_max et erreur moyenne des deux estimations, sur les mêmes carrés ; rien n’est modifié. Chaque campagne porte un campagne.yaml (robot, test, date d’application, notée dès l’écriture, avant les sauvegardes) ; les sauvegardes de réglages d’une campagne sont horodatées (reglages_avant_HHMMSS.yaml…), sauf le reglages_avant.yaml d’une mesure UMBmark, écrit une fois et lu par l’analyse. « Déjà dans la carte » n’est conclu que si chaque valeur a pu être LUE : une lecture ratée n’empêche pas d’écrire ; ratbot_calibration/campaigns.py (ratbot-campaigns) les retrouve par robot, les résume et recalcule E_max avec umbmark-analyze. Une mesure UMBmark finie est fermée (CLOSE) : la suivante part dans un dossier neuf. Enchaîne les outils de ratbot_calibration sans les réécrire, range les résultats dans cahier_de_labo/campagnes/ et attend un « o » avant chaque mouvement. gyro sauvegarde les réglages avant, propose la moyenne des deux sens et ne l’écrit (ros2 param set) qu’après accord. umbmark reprend une campagne interrompue, même un autre jour (le dossier ouvert le plus récent du robot), après confirmation et vérification du firmware et du côté ; sinon la mesure est abandonnée (abandoned dans campagne.yaml, CLOSE) et ne compte plus. Le côté est celui des mesures réelles du robot (ratbot-campaigns side), imposé ; stats ne compare que des côtés égaux (sinon « autre côté »). Avant tout mouvement, refus si controller_server, bt_navigator, explore_node ou slam_toolbox tourne dans /ratbot_N/. apply compare les réglages de la carte à ceux de la mesure (ratbot-campaigns reference) et refuse une campagne _simulation sur un robot réel. RATBOT_UMB_RUNS et RATBOT_UMB_SIDE raccourcissent une campagne, en simulation seulement (ignorées sur un robot réel). À part de ratbot : on ne calibre qu’à la mise en service

Ils sont en Python, parce qu’ils valident des données et rendent des rapports, sauf ratbot-calibrate, en bash comme ratbot : il enchaîne des commandes. ratbot lui-même reste en bash, pour la même raison.

Ce qu’on peut forcer

Toutes ces variables ont un défaut raisonnable ; elles existent pour l’essai ponctuel, pas pour l’usage courant.

Variable

Défaut

Effet

RATBOT_ROLE

d’après le nom

force robot ou dev

RATBOT_ID

1

numéro du robot, si le nom de machine ne le donne pas. Le nom l’emporte toujours

ROS_DISTRO

humble

la distribution visée

ROS_DOMAIN_ID

77

le domaine DDS

RATBOT_AGENT_DEV

/dev/ttyAMA0

le port série vers la carte

RATBOT_AGENT_BAUD

3000000

la vitesse de la liaison

Note

ROS_DOMAIN_ID doit vivre à deux endroits sur un robot : /etc/environment et le fichier du service. /etc/environment n’est lu que par PAM, donc par les sessions de connexion — une unité systemd ne le voit pas. Ce n’est pas une redondance, les deux sont nécessaires.

Voir aussi