Les topics du robot

Le contrat d’interface

Le RatBot expose son matériel exclusivement sous forme de topics ROS 2 standards (geometry_msgs, nav_msgs, sensor_msgs, std_msgs…). C’est son contrat d’interface : tout projet hôte peut le piloter et l’observer sans dépendre du moindre paquet du RatBot, et réciproquement le RatBot ne dépend d’aucun paquet du projet hôte.

Le schéma à trois niveaux

Les topics suivent un nommage à trois niveaux porteurs de sens, inspiré de la convention du projet Ubimap :

/<instance>/<sous-système>/<interface>
 ratbot_N    base, imu,     cmd_vel, odom,
             health, ui     temperature, ...

Les sous-systèmes reflètent l’organisation matérielle du robot (et les threads FreeRTOS du firmware) :

/ratbot_1/
├── base/                    la traction
│   ├── cmd_vel          ⬇   geometry_msgs/Twist
│   ├── odom             ⬆   nav_msgs/Odometry
│   └── joint_states     ⬆   sensor_msgs/JointState
├── imu/                     centrale inertielle
│   └── data_raw         ⬆   sensor_msgs/Imu
├── health/                  santé du robot
│   ├── temperature      ⬆   sensor_msgs/Temperature
│   ├── battery          ⬆   sensor_msgs/BatteryState
│   └── diagnostics      ⬆   diagnostic_msgs/DiagnosticArray
└── ui/                      interface homme-machine
    ├── buttons          ⬆   sensor_msgs/Joy
    ├── leds             ⬇   std_msgs/UInt8
    ├── buzzer/play      ⬇   std_msgs/String
    └── display/text     ⬇   std_msgs/String

Règles lexicales : topics = substantifs, tout en snake_case ; les données remontent (⬆) et les commandes descendent (⬇) dans le namespace de l’entité.

Inventaire détaillé

Topic

Type

Sens

Cadence

Rôle

base/cmd_vel

geometry_msgs/Twist

à la demande

Commande de vitesse. Watchdog dans le firmware : arrêt des moteurs si silence de plus de ~500 ms.

base/odom

nav_msgs/Odometry

30–50 Hz

Odométrie intégrée des encodeurs dynamixel.

base/joint_states

sensor_msgs/JointState

~10 Hz

Position/vitesse des roues (animation du modèle dans RViz).

imu/data_raw

sensor_msgs/Imu

~100 Hz

Gyroscope + accéléromètre bruts (convention : data_raw = non fusionné ; la fusion se fait sur la Pi et publie imu/data).

health/temperature

sensor_msgs/Temperature

1 Hz

Capteur TMP102 placé entre les deux régulateurs de courant.

health/battery

sensor_msgs/BatteryState

1 Hz

État de la batterie.

health/diagnostics

diagnostic_msgs/DiagnosticArray

1 Hz

Santé agrégée : dynamixels (température, charge), régulateurs, watchdog, alertes du buzzer actif… Visualisable dans rqt_robot_monitor.

ui/buttons

sensor_msgs/Joy

sur événement

Les 4 boutons utilisateur (tableau buttons[]).

ui/leds

std_msgs/UInt8

sur événement

Bitmask des 4 LEDs utilisateur.

ui/buzzer/play

std_msgs/String

sur événement

Buzzer passif : nom de mélodie prédéfinie ("startup", "obstacle", "approach", "error"…) ou partition RTTTL.

ui/display/text

std_msgs/String

sur événement

Affichage d’un message sur l’écran embarqué.

Services : reset_odometry (std_srvs/Trigger).

Arrêt global

Unique exception au nommage par namespace : le topic absolu /emergency_stop (std_msgs/Bool), commun à tous les robots.

  • true = tous les robots forcent leur commande de vitesse à zéro, et restent arrêtés tant que false n’a pas été publié ;

  • côté robot, le nœud cmd_vel_gate (paquet ratbot_bringup, lancé par robot.launch.py) filtre base/cmd_velbase/cmd_vel_safe, le topic interne auquel le firmware s’abonne ;

  • limitation assumée : un robot qui démarre pendant un arrêt d’urgence ne reçoit pas l’état courant (il démarre passant). Si le besoin apparaît, un petit relais à mémoire (latch) sur la machine centrale réglera ça.

ros2 topic pub --once /emergency_stop std_msgs/msg/Bool "data: true"

Important

Ce n’est pas un arrêt d’urgence au sens sécurité : il transite par le WiFi et du logiciel. La mise en sécurité réelle est assurée en dessous, par le watchdog du firmware (silence de cmd_vel > ~500 ms → arrêt des moteurs), qui fonctionne même WiFi coupé.

Pas de paquet de messages custom

Le contrat n’utilise que des messages standards : il n’existe pas de paquet ratbot_interfaces, et c’est volontaire. Un message custom recréerait la dépendance que le contrat cherche à éliminer (un hôte devrait compiler les interfaces du RatBot juste pour lui parler), et chaque message custom impose de régénérer la bibliothèque micro-ROS du firmware.

Si un vrai besoin sort un jour des messages standards (messages compacts pour économiser la liaison série, état détaillé des dynamixels…), la règle sera : un unique paquet ratbot_interfaces ne contenant que des .msg/.srv, jamais de code. Il ne sera créé qu’à ce moment-là.

Hors contrat : la sécurité interne

Le buzzer actif (alertes température, batterie, danger) n’apparaît pas dans le contrat : c’est une décision autonome du firmware, qui doit fonctionner même si la Raspberry Pi ou ROS est hors service. ROS n’en voit que le reflet dans health/diagnostics, jamais la commande. Même philosophie pour le watchdog de cmd_vel : la mise en sécurité des moteurs ne dépend d’aucun logiciel extérieur à la carte.

Les 6 boutons de navigation de l’écran (enter, cancel, 4 directions) restent eux aussi internes au firmware : c’est le menu local du robot, pas une interface ROS.

Arbre TF

/tf est un topic global partagé : la séparation entre robots s’y fait par les noms de frames, préfixés par l’identité du robot (paramètre frame_prefix de robot_state_publisher) :

ratbot_1/odom ──► ratbot_1/base_footprint ──► ratbot_1/base_link ──► …
  • ratbot_N/odom ratbot_N/base_footprint : publié à partir de l’odométrie (TODO : préciser qui publie cette transformation — firmware ou nœud sur la Pi).

  • Les transformations internes au robot sont publiées par robot_state_publisher à partir de l’URDF.

Multi-robot : les namespaces

ROS 2 est conçu pour faire cohabiter plusieurs robots identiques : le code utilise des noms de topics relatifs (base/odom, jamais /base/odom), et c’est au lancement qu’on pousse les nœuds dans le namespace du robot :

ros2 launch ratbot_bringup robot.launch.py robot_id:=1   # → /ratbot_1/…
ros2 launch ratbot_bringup robot.launch.py robot_id:=2   # → /ratbot_2/…

Le même code, non modifié, produit des graphes disjoints. Points d’attention :

  • Firmware : le namespace d’un nœud micro-ROS se fixe à sa création ; la carte doit donc connaître son numéro N (TODO : DIP switch, option bytes ou configuration au boot). Côté Pi, le plus simple est de nommer les machines ratbot-1, ratbot-2… et de dériver robot_id du hostname.

  • ROS_DOMAIN_ID : il ne sépare pas les robots — au contraire, toutes les machines qui doivent se voir partagent la même valeur. C’est le namespace qui sépare, le domaine qui réunit.

  • Projets hôtes : si un hôte attend un autre préfixe (Ubimap attend /ubimap/agents/ratbot_1/cmd_vel), le remapping au lancement fait la traduction sans modifier le code du robot.

Note

Certains outils de l’écosystème (nav2, teleop_twist_keyboard…) s’attendent par défaut à trouver cmd_vel et odom directement à la racine du namespace du robot. Le choix du sous-système base/ est assumé pour la lisibilité : nos launch files portent les quelques remappings nécessaires.