Construire un modèle de robot visuel à partir de zéro

Objectif : Apprendre à construire un modèle visuel d’un robot que vous pouvez voir dans Rviz

Niveau du didacticiel : Intermédiaire

Durée : 20 minutes

Note

Ce tutoriel suppose que vous savez écrire du code XML bien formaté

Dans ce tutoriel, nous allons construire un modèle visuel d’un robot qui ressemble vaguement à R2D2. Dans les didacticiels ultérieurs, vous apprendrez à articuler le modèle, ajouter des propriétés physiques, et générer un code plus propre avec xacro, mais pour l’instant, nous allons nous concentrer sur l’obtention d’une géométrie visuelle correcte.

Avant de continuer, assurez-vous que le package joint_state_publisher est installé. Si vous avez installé les binaires urdf_tutorial, cela devrait déjà être le cas. Si ce n’est pas le cas, veuillez mettre à jour votre installation pour inclure ce paquet (utilisez rosdep pour vérifier).

Tous les modèles de robots mentionnés dans ce didacticiel (et les fichiers sources) se trouvent dans le package urdf_tutorial.

Une forme

Tout d’abord, nous allons juste explorer une forme simple. Voici à peu près aussi simple qu’un urdf que vous pouvez faire. [Source : 01-myfirst.urdf]

<?xml version="1.0"?>
<robot name="myfirst">
  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
    </visual>
  </link>
</robot>

Pour traduire le XML en anglais, il s’agit d’un robot nommé myfirst, qui ne contient qu’un seul lien (c’est-à-dire une partie), dont le composant visuel est juste un cylindre de 0,6 mètre de long avec un rayon de 0,2 mètre. Cela peut sembler beaucoup de balises englobantes pour un simple exemple de type « hello world », mais cela deviendra plus compliqué, croyez-moi.

Pour examiner le modèle, lancez le fichier display.launch.py :

ros2 launch urdf_tutorial display.launch.py model:=urdf/01-myfirst.urdf

Cela fait trois choses :

  • Charge le modèle spécifié et l’enregistre en tant que paramètre

  • Exécute des nœuds pour publier sensor_msgs/msg/JointState et transforme (plus sur ceux-ci plus tard)

  • Démarre Rviz avec un fichier de configuration

Notez que la commande de lancement ci-dessus suppose que vous l’exécutez à partir du répertoire urdf_tutorial <https://index.ros.org/p/urdf_tutorial>`_ package'' (c'est-à-dire : le répertoire ``urdf est un enfant direct de le répertoire de travail courant). Si ce n’est pas le cas, le chemin relatif vers 01-myfirst.urdf ne sera pas valide, et vous recevrez une erreur dès que le lanceur essaiera de charger l’urdf en paramètre.

Un argument légèrement modifié permet à cela de fonctionner quel que soit le répertoire de travail actuel :

ros2 launch urdf_tutorial display.launch.py model:=`ros2 pkg prefix --share urdf_tutorial`/urdf/01-myfirst.urdf

Vous devrez modifier tous les exemples de commandes de lancement donnés dans ces tutoriels si vous ne les exécutez pas à partir de l’emplacement du package urdf_tutorial.

Après avoir lancé display.launch.py, vous devriez vous retrouver avec RViz vous montrant ce qui suit :

ma première image
À noter :
  • Le cadre fixe est le cadre de transformation où se trouve le centre de la grille. Ici, c’est un cadre défini par notre seul lien, base_link.

  • L’élément visuel (le cylindre) a son origine au centre de sa géométrie par défaut. Par conséquent, la moitié du cylindre est sous la grille.

Formes multiples

Voyons maintenant comment ajouter plusieurs formes/liens. Si nous ajoutons simplement plus d’éléments de lien à l’urdf, l’analyseur ne saura pas où les mettre. Il faut donc rajouter des joints. Les éléments de joint peuvent faire référence à la fois aux joints flexibles et inflexibles. Nous commencerons par des joints inflexibles ou fixes. [Source : 02-multipleshapes.urdf]

<?xml version="1.0"?>
<robot name="multipleshapes">
  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
    </visual>
  </link>

  <link name="right_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
    </visual>
  </link>

  <joint name="base_to_right_leg" type="fixed">
    <parent link="base_link"/>
    <child link="right_leg"/>
  </joint>

</robot>
  • Notez comment nous avons défini une boîte de 0,6 m x 0,1 m x 0,2 m

  • L’articulation est définie en termes d’un parent et d’un enfant. URDF est finalement une structure arborescente avec un lien racine. Cela signifie que la position de la jambe dépend de la position de base_link.

ros2 launch urdf_tutorial display.launch.py model:=urdf/02-multipleshapes.urdf
Formes multiples

Les deux formes se chevauchent, car elles partagent la même origine. Si nous voulons qu’ils ne se chevauchent pas, nous devons définir plus d’origines.

Origines

La jambe de R2D2 s’attache à la moitié supérieure de son torse, sur le côté. C’est donc là que nous spécifions l’origine du JOINT. De plus, il ne s’attache pas au milieu de la jambe, il s’attache à la partie supérieure, il faut donc décaler l’origine pour la jambe également. Nous faisons également pivoter la jambe pour qu’elle soit droite. [Source : 03-origins.urdf]

<?xml version="1.0"?>
<robot name="origins">
  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
    </visual>
  </link>

  <link name="right_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
    </visual>
  </link>

  <joint name="base_to_right_leg" type="fixed">
    <parent link="base_link"/>
    <child link="right_leg"/>
    <origin xyz="0 -0.22 0.25"/>
  </joint>

</robot>
  • Commençons par examiner l’origine de l’articulation. Il est défini en fonction du cadre de référence du parent. Nous sommes donc à -0,22 mètre dans la direction y (à notre gauche, mais à droite par rapport aux axes) et à 0,25 mètre dans la direction z (vers le haut). Cela signifie que l’origine du lien enfant sera en haut et à droite, quelle que soit la balise d’origine visuelle du lien enfant. Comme nous n’avons pas spécifié d’attribut rpy (roll pitch lacet), le cadre enfant aura par défaut la même orientation que le cadre parent.

  • Maintenant, en regardant l’origine visuelle de la jambe, elle a à la fois un décalage xyz et rpy. Cela définit où le centre de l’élément visuel doit être, par rapport à son origine. Puisque nous voulons que la jambe s’attache en haut, nous décalons l’origine vers le bas en définissant le décalage z sur -0,3 mètre. Et puisque nous voulons que la partie longue de la jambe soit parallèle à l’axe z, nous faisons pivoter la partie visuelle PI/2 autour de l’axe Y.

ros2 launch urdf_tutorial display.launch.py model:=urdf/03-origins.urdf
Capture d'écran des origines
  • Le fichier de lancement exécute des packages qui créeront des cadres TF pour chaque lien de votre modèle en fonction de votre URDF. Rviz utilise ces informations pour déterminer où afficher chaque forme.

  • Si une trame TF n’existe pas pour un lien URDF donné, alors elle sera placée à l’origine en blanc (réf. question connexe).

Fille matérialiste

« D’accord », je vous entends dire. « C’est très mignon, mais tout le monde ne possède pas un B21. Mon robot et R2D2 ne sont pas rouges ! C’est un bon point. Jetons un coup d’œil à la balise de matériau. [Source : 04-materials.urdf]

<?xml version="1.0"?>
<robot name="materials">

  <material name="blue">
    <color rgba="0 0 0.8 1"/>
  </material>

  <material name="white">
    <color rgba="1 1 1 1"/>
  </material>

  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
      <material name="blue"/>
    </visual>
  </link>

  <link name="right_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
      <material name="white"/>
    </visual>
  </link>

  <joint name="base_to_right_leg" type="fixed">
    <parent link="base_link"/>
    <child link="right_leg"/>
    <origin xyz="0 -0.22 0.25"/>
  </joint>

  <link name="left_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
      <material name="white"/>
    </visual>
  </link>

  <joint name="base_to_left_leg" type="fixed">
    <parent link="base_link"/>
    <child link="left_leg"/>
    <origin xyz="0 0.22 0.25"/>
  </joint>

</robot>
  • Le corps est maintenant bleu. Nous avons défini un nouveau matériau appelé « bleu », avec les canaux rouge, vert, bleu et alpha définis comme 0, 0, 0,8 et 1 respectivement. Toutes les valeurs peuvent être dans la plage [0,1]. Ce matériel est ensuite référencé par l’élément visuel de base_link. Le matériau blanc est défini de la même manière.

  • Vous pouvez également définir la balise de matériau à partir de l’élément visuel et même la référencer dans d’autres liens. Personne ne se plaindra même si vous le redéfinissez.

  • Vous pouvez également utiliser une texture pour spécifier un fichier image à utiliser pour colorer l’objet

ros2 launch urdf_tutorial display.launch.py model:=urdf/04-materials.urdf
Capture d'écran des matériaux

Finition du modèle

Maintenant, nous terminons le modèle avec quelques formes supplémentaires : pieds, roues et tête. Plus particulièrement, nous ajoutons une sphère et quelques mailles. Nous ajouterons également quelques autres pièces que nous utiliserons plus tard. [Source : 05-visual.urdf]

<?xml version="1.0"?>
<robot name="visual">

  <material name="blue">
    <color rgba="0 0 0.8 1"/>
  </material>
  <material name="black">
    <color rgba="0 0 0 1"/>
  </material>
  <material name="white">
    <color rgba="1 1 1 1"/>
  </material>

  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
      <material name="blue"/>
    </visual>
  </link>

  <link name="right_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
      <material name="white"/>
    </visual>
  </link>

  <joint name="base_to_right_leg" type="fixed">
    <parent link="base_link"/>
    <child link="right_leg"/>
    <origin xyz="0 -0.22 0.25"/>
  </joint>

  <link name="right_base">
    <visual>
      <geometry>
        <box size="0.4 0.1 0.1"/>
      </geometry>
      <material name="white"/>
    </visual>
  </link>

  <joint name="right_base_joint" type="fixed">
    <parent link="right_leg"/>
    <child link="right_base"/>
    <origin xyz="0 0 -0.6"/>
  </joint>

  <link name="right_front_wheel">
    <visual>
      <origin rpy="1.57075 0 0" xyz="0 0 0"/>
      <geometry>
        <cylinder length="0.1" radius="0.035"/>
      </geometry>
      <material name="black"/>
    </visual>
  </link>
  <joint name="right_front_wheel_joint" type="fixed">
    <parent link="right_base"/>
    <child link="right_front_wheel"/>
    <origin rpy="0 0 0" xyz="0.133333333333 0 -0.085"/>
  </joint>

  <link name="right_back_wheel">
    <visual>
      <origin rpy="1.57075 0 0" xyz="0 0 0"/>
      <geometry>
        <cylinder length="0.1" radius="0.035"/>
      </geometry>
      <material name="black"/>
    </visual>
  </link>
  <joint name="right_back_wheel_joint" type="fixed">
    <parent link="right_base"/>
    <child link="right_back_wheel"/>
    <origin rpy="0 0 0" xyz="-0.133333333333 0 -0.085"/>
  </joint>

  <link name="left_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
      <material name="white"/>
    </visual>
  </link>

  <joint name="base_to_left_leg" type="fixed">
    <parent link="base_link"/>
    <child link="left_leg"/>
    <origin xyz="0 0.22 0.25"/>
  </joint>

  <link name="left_base">
    <visual>
      <geometry>
        <box size="0.4 0.1 0.1"/>
      </geometry>
      <material name="white"/>
    </visual>
  </link>

  <joint name="left_base_joint" type="fixed">
    <parent link="left_leg"/>
    <child link="left_base"/>
    <origin xyz="0 0 -0.6"/>
  </joint>

  <link name="left_front_wheel">
    <visual>
      <origin rpy="1.57075 0 0" xyz="0 0 0"/>
      <geometry>
        <cylinder length="0.1" radius="0.035"/>
      </geometry>
      <material name="black"/>
    </visual>
  </link>
  <joint name="left_front_wheel_joint" type="fixed">
    <parent link="left_base"/>
    <child link="left_front_wheel"/>
    <origin rpy="0 0 0" xyz="0.133333333333 0 -0.085"/>
  </joint>

  <link name="left_back_wheel">
    <visual>
      <origin rpy="1.57075 0 0" xyz="0 0 0"/>
      <geometry>
        <cylinder length="0.1" radius="0.035"/>
      </geometry>
      <material name="black"/>
    </visual>
  </link>
  <joint name="left_back_wheel_joint" type="fixed">
    <parent link="left_base"/>
    <child link="left_back_wheel"/>
    <origin rpy="0 0 0" xyz="-0.133333333333 0 -0.085"/>
  </joint>

  <joint name="gripper_extension" type="fixed">
    <parent link="base_link"/>
    <child link="gripper_pole"/>
    <origin rpy="0 0 0" xyz="0.19 0 0.2"/>
  </joint>

  <link name="gripper_pole">
    <visual>
      <geometry>
        <cylinder length="0.2" radius="0.01"/>
      </geometry>
      <origin rpy="0 1.57075 0 " xyz="0.1 0 0"/>
    </visual>
  </link>

  <joint name="left_gripper_joint" type="fixed">
    <origin rpy="0 0 0" xyz="0.2 0.01 0"/>
    <parent link="gripper_pole"/>
    <child link="left_gripper"/>
  </joint>

  <link name="left_gripper">
    <visual>
      <origin rpy="0.0 0 0" xyz="0 0 0"/>
      <geometry>
        <mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
      </geometry>
    </visual>
  </link>

  <joint name="left_tip_joint" type="fixed">
    <parent link="left_gripper"/>
    <child link="left_tip"/>
  </joint>

  <link name="left_tip">
    <visual>
      <origin rpy="0.0 0 0" xyz="0.09137 0.00495 0"/>
      <geometry>
        <mesh filename="package://urdf_tutorial/meshes/l_finger_tip.dae"/>
      </geometry>
    </visual>
  </link>
  <joint name="right_gripper_joint" type="fixed">
    <origin rpy="0 0 0" xyz="0.2 -0.01 0"/>
    <parent link="gripper_pole"/>
    <child link="right_gripper"/>
  </joint>

  <link name="right_gripper">
    <visual>
      <origin rpy="-3.1415 0 0" xyz="0 0 0"/>
      <geometry>
        <mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
      </geometry>
    </visual>
  </link>

  <joint name="right_tip_joint" type="fixed">
    <parent link="right_gripper"/>
    <child link="right_tip"/>
  </joint>

  <link name="right_tip">
    <visual>
      <origin rpy="-3.1415 0 0" xyz="0.09137 0.00495 0"/>
      <geometry>
        <mesh filename="package://urdf_tutorial/meshes/l_finger_tip.dae"/>
      </geometry>
    </visual>
  </link>

  <link name="head">
    <visual>
      <geometry>
        <sphere radius="0.2"/>
      </geometry>
      <material name="white"/>
    </visual>
  </link>
  <joint name="head_swivel" type="fixed">
    <parent link="base_link"/>
    <child link="head"/>
    <origin xyz="0 0 0.3"/>
  </joint>

  <link name="box">
    <visual>
      <geometry>
        <box size="0.08 0.08 0.08"/>
      </geometry>
      <material name="blue"/>
    </visual>
  </link>

  <joint name="tobox" type="fixed">
    <parent link="head"/>
    <child link="box"/>
    <origin xyz="0.1814 0 0.1414"/>
  </joint>
</robot>
ros2 launch urdf_tutorial display.launch.py model:=urdf/05-visual.urdf
Capture d'écran visuelle

Comment ajouter la sphère devrait être assez explicite :

<link name="head">
  <visual>
    <geometry>
      <sphere radius="0.2"/>
    </geometry>
    <material name="white"/>
  </visual>
</link>

Les mailles ici ont été empruntées au PR2. Ce sont des fichiers séparés dont vous devez spécifier le chemin. Vous devez utiliser la notation package://NAME_OF_PACKAGE/path. Les maillages de ce tutoriel sont situés dans le package urdf_tutorial, dans un dossier appelé meshes.

<link name="left_gripper">
  <visual>
    <origin rpy="0.0 0 0" xyz="0 0 0"/>
    <geometry>
      <mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
    </geometry>
  </visual>
</link>
  • Les maillages peuvent être importés dans un certain nombre de formats différents. STL est assez courant, mais le moteur prend également en charge DAE, qui peut avoir ses propres données de couleur, ce qui signifie que vous n’avez pas à spécifier la couleur/le matériau. Souvent, ceux-ci sont dans des fichiers séparés. Ces maillages référencent les fichiers .tif également dans le dossier des maillages.

  • Les maillages peuvent également être dimensionnés à l’aide de paramètres d’échelle relatifs ou d’une taille de boîte englobante.

  • Nous aurions également pu faire référence à des maillages dans un package complètement différent.

Voilà. Un modèle URDF de type R2D2. Vous pouvez maintenant passer à l’étape suivante, le faire bouger.