Aller au contenu

Découverte du bras SO-101

Dans cette première partie, vous prenez en main le bras SO-101 : vous lisez sa description (URDF), vous le visualisez dans RViz, puis vous le pilotez en simulation Gazebo Ionic via ros2_control. À la fin, vous saurez bouger le bras et la pince à la main — la seconde partie automatisera tout cela avec MoveIt 2.

Le SO-101 est un bras robotique open-source à 6 articulations motorisées (5 DOF pour positionner le poignet + 1 pour la pince) conçu par la communauté LeRobot / Hugging Face, motorisé par des servos Feetech STS3215. Il est suffisamment léger pour être posé sur une table ou monté sur la base mobile LeKiwi.

Le SO-101 enchaîne 6 axes en rotation :

JointTypeRôle
shoulder_panrevoluteRotation horizontale de l’épaule (base)
shoulder_liftrevoluteLevée verticale de l’épaule
elbow_flexrevoluteFlexion du coude
wrist_flexrevoluteFlexion du poignet
wrist_rollrevoluteRotation du poignet
gripperrevoluteOuverture/fermeture de la pince

C’est une chaîne cinématique série (chaque joint dépend du précédent), classique pour un bras de manipulation.

Dans ROS 2, le robot est décrit par un URDF (XML). Le package so101_description (dans so_arm101_ros2) le découpe en deux fichiers complémentaires :

  • so101.urdf — la description cinématique pure : les <link> et <joint> du bras, indépendante du contexte d’exécution ;
  • so101.urdf.xacro — un wrapper Xacro qui assemble so101.urdf, la transmission et — selon les arguments — la configuration ros2_control.

Générez l’URDF à plat (ici en mode visualisation) et vérifiez sa cohérence :

Fenêtre de terminal
ros2 run xacro xacro \
$(ros2 pkg prefix --share so101_description)/urdf/so101.urdf.xacro \
joint_states_gui:=true \
> /tmp/so101.urdf
check_urdf /tmp/so101.urdf

check_urdf affiche l’arbre des liens : vous devez y retrouver la chaîne base_link → … → gripper.

Ouvrez la description so101.urdf et repérez-y les deux éléments de base :

ÉlémentRôle
<link>Un segment physique (épaule, avant-bras…) : géométrie visuelle, géométrie de collision, inertie
<joint>La liaison entre deux liens : axe de rotation, limites (lower/upper/effort/velocity), dynamique
Arbre des repères du SO-101 dans RViz : chaque joint relie deux links (base_link → shoulder_link → upper_arm_link → lower_arm_link → wrist_link → gripper_link), formant la chaîne cinématique.
Chaque repère affiché est un <link>, relié au précédent par un <joint>.

Côté Xacro (so101.urdf.xacro), observez plutôt la composition : <xacro:include> pour assembler les fichiers, <xacro:arg> pour les paramètres et <xacro:if> pour activer ros2_control selon le mode.

Le package fournit un launch prêt à l’emploi :

Fenêtre de terminal
ros2 launch so101_description view.launch.py

Un launch démarre des nœuds (des exécutables ROS 2) ; ici trois :

  • robot_state_publisher — lit l’URDF et les /joint_states, publie la tf ;
  • joint_state_publisher_gui — publie les /joint_states à partir de sliders ;
  • rviz2 — le visualiseur 3D, chargé avec la config display.rviz.

La fenêtre JointStatePublisher est l’interface de ce deuxième nœud : bougez ses sliders pour voir le bras se déplacer en temps réel.

Pendant que le launch tourne, ouvrez un second terminal (workspace sourcé) et listez les topics apparus :

Fenêtre de terminal
ros2 topic list

On y retrouve l’état des articulations et la chaîne tf. La description URDF complète, elle, est publiée sur un topic dédié — inspectez-les :

Fenêtre de terminal
ros2 topic echo /robot_description --once # le contenu de l'URDF
ros2 topic hz /joint_states # la fréquence des états de joints

RViz est notre outil d’interaction (planification MoveIt à la partie suivante), mais pour inspecter le bras — le voir en 3D et lire les valeurs des articulations — Foxglove est plus confortable (interface web, panneaux Raw Messages et Plot).

Gardez le launch de découverte du bras actif et démarrez le pont dans un autre terminal :

Fenêtre de terminal
ros2 launch foxglove_bridge foxglove_bridge_launch.xml # ws://localhost:8765

Connectez-vous à ws://localhost:8765 dans Foxglove (cf. Installation), puis ajoutez :

  • un panneau 3D : le bras et ses repères. Ajoutez une couche URDF (Custom layers → + → URDF) avec Source = Parameter, Parameter = robot_description ;
  • un panneau Raw Messages sur /joint_states : les angles articulaires en direct.

Quand vous publiez les joint_states, robot_state_publisher calcule automatiquement la chaîne de transformations tf entre tous les frames du robot (base_linkshoulder_linkupper_arm_link → … → gripper_link).

Cette chaîne est ce que MoveIt 2 utilisera dans la partie suivante pour planifier les trajectoires.

Jusqu’ici, le bras ne fait que suivre des sliders : aucune physique, aucun contrôleur. Pour le commander comme un vrai robot (en simulation ou réel), ROS 2 utilise ros2_control, l’architecture standard pour piloter du matériel de façon uniforme et temps réel.

Comment lire ce schéma — suivez le trajet d’une commande, de gauche à droite, puis le retour :

  1. Vous envoyez une commande (action / topic) à un contrôleur (arm_controller, gripper_controller).
  2. Le controller manager orchestre les contrôleurs et transmet leurs ordres au matériel via les command_interfaces (la flèche qui descend vers le hardware).
  3. Le hardware interface applique ces ordres au robot (Gazebo en sim) et relit l’état réel des joints, qu’il renvoie au manager via les state_interfaces (la flèche qui remonte).
  4. En parallèle, le joint_state_broadcaster publie cet état sur /joint_states — c’est la boucle qui se referme.

Les trois briques, de bas en haut :

  • Hardware interface : le pont bas-niveau. Il lit l’état (positions, vitesses) et applique les commandes. En sim Gazebo Ionic (gz-sim 9), c’est gz_ros2_control qui le fait automatiquement ; sur le vrai robot, ce serait le driver Feetech.
  • Controller manager : le chef d’orchestre. Il charge / décharge les contrôleurs et arbitre les accès aux command_interfaces (deux contrôleurs ne peuvent pas commander le même joint en même temps).
  • Contrôleur : la politique de contrôle (suivre une trajectoire, maintenir une position…). Pour le bras, un joint_trajectory_controller (JTC) ; pour la pince, un contrôleur de gripper dédié.

La distinction clé : command_interfaces = ce qu’on écrit vers le matériel (consignes), state_interfaces = ce qu’on lit du matériel (mesures). Tout ros2_control tourne autour de cette boucle écrire / lire.

La sim charge trois contrôleurs (visibles via ros2 control list_controllers) :

  • joint_state_broadcaster — publie /joint_states ; ce n’est pas un contrôleur de commande, juste un broadcaster d’état.
  • arm_controller — un joint_trajectory_controller/JointTrajectoryController sur les 5 joints du bras : shoulder_pan, shoulder_lift, elbow_flex, wrist_flex, wrist_roll.
  • gripper_controller — un parallel_gripper_action_controller/GripperActionController sur le joint gripper.

Le arm_controller accepte des trajectoires (liste de points avec positions, vitesses, temps) sur le topic / action :

/arm_controller/follow_joint_trajectory (action)
/arm_controller/joint_trajectory (topic)

Il interpole entre les points et envoie les commandes au hardware interface à fréquence fixe (souvent 100 Hz ou plus). La pince, elle, se pilote via l’action control_msgs/action/ParallelGripperCommand sur /gripper_controller/gripper_cmd — ses champs name et position sont des listes, cohérent avec le type parallel_gripper_action_controller.

Pour tester sans MoveIt, on publie une trajectoire à la main. Le bras et la pince se commandent séparément : le bras via le topic / action du arm_controller (5 joints), la pince via l’action du gripper_controller.

Fenêtre de terminal
ros2 launch so101_bringup gazebo.launch.py

→ La fenêtre Gazebo Ionic s’ouvre, le bras SO-101 apparaît posé sur le sol. Attendez ~10-20 s (premier rendu) que les trois contrôleurs se chargent.

Le bras doit rejoindre la position cible en 2 secondes (vous le voyez bouger dans Gazebo) ; la pince s’ouvre ou se ferme selon la position demandée.

Vous savez décrire, visualiser et piloter le bras à la main. Place à la planification automatique de trajectoires : MoveIt & pick and place.

Cours conçu et animé par Etienne Schmitz