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 : :: /// 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é ------------------- .. list-table:: :header-rows: 1 :widths: 22 26 8 12 32 * - 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_vel`` → ``base/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. .. code-block:: bash 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 : .. code-block:: bash 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.