Utiliser Xacro pour nettoyer votre code

Objectif : Apprendre quelques astuces pour réduire la quantité de code dans un fichier URDF à l’aide de Xacro

Niveau du didacticiel : Intermédiaire

Durée : 20 minutes

À présent, si vous suivez toutes ces étapes à la maison avec votre propre conception de robot, vous en aurez peut-être assez de faire toutes sortes de calculs pour obtenir des descriptions de robots très simples à analyser correctement. Heureusement, vous pouvez utiliser le package xacro pour vous simplifier la vie. Il fait trois choses qui sont très utiles.

  • Constantes

  • Mathématiques simples

  • Macros

Dans ce didacticiel, nous examinons tous ces raccourcis pour aider à réduire la taille globale du fichier URDF et à en faciliter la lecture et la maintenance.

Utilisation de Xacro

Comme son nom l’indique, xacro est un langage macro pour XML. Le programme xacro exécute toutes les macros et affiche le résultat. L’utilisation typique ressemble à ceci :

xacro model.xacro > model.urdf

Vous pouvez également générer automatiquement l’urdf dans un fichier de lancement. C’est pratique car il reste à jour et n’utilise pas d’espace sur le disque dur. Cependant, sa génération prend du temps, alors sachez que votre fichier de lancement peut prendre plus de temps à démarrer.

path_to_urdf = get_package_share_path('pr2_description') / 'robots' / 'pr2.urdf.xacro'
robot_state_publisher_node = launch_ros.actions.Node(
    package='robot_state_publisher',
    executable='robot_state_publisher',
    parameters=[{
        'robot_description': ParameterValue(
            Command(['xacro ', str(path_to_urdf)]), value_type=str
        )
    }]
)

En haut du fichier URDF, vous devez spécifier un espace de noms pour que le fichier soit correctement analysé. Par exemple, voici les deux premières lignes d’un fichier xacro valide :

<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="firefighter">

Constantes

Jetons un coup d’œil à notre base_link dans R2D2.

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

Les informations ici sont un peu redondantes. Nous spécifions la longueur et le rayon du cylindre deux fois. Pire encore, si nous voulons changer cela, nous devons le faire à deux endroits différents.

Heureusement, xacro vous permet de spécifier des propriétés qui agissent comme des constantes. Au lieu du code ci-dessus, nous pouvons écrire ceci.

<xacro:property name="width" value="0.2" />
<xacro:property name="bodylen" value="0.6" />
<link name="base_link">
    <visual>
        <geometry>
            <cylinder radius="${width}" length="${bodylen}"/>
        </geometry>
        <material name="blue"/>
    </visual>
    <collision>
        <geometry>
            <cylinder radius="${width}" length="${bodylen}"/>
        </geometry>
    </collision>
</link>
  • Les deux valeurs sont spécifiées dans les deux premières lignes. Ils peuvent être définis à peu près n’importe où (en supposant un XML valide), à ​​n’importe quel niveau, avant ou après leur utilisation. Habituellement, ils vont au sommet.

  • Au lieu de spécifier le rayon réel dans l’élément géométrique, nous utilisons un signe dollar et des accolades pour signifier la valeur.

  • Ce code générera le même code indiqué ci-dessus.

La valeur du contenu de la construction ${} est ensuite utilisée pour remplacer le ${}. Cela signifie que vous pouvez le combiner avec un autre texte dans l’attribut.

<xacro:property name=”robotname” value=”marvin” />
<link name=”${robotname}s_leg” />

Cela va générer

<link name=”marvins_leg” />

Cependant, le contenu de ${} ne doit pas être uniquement une propriété, ce qui nous amène au point suivant…

Mathématiques

Vous pouvez construire des expressions arbitrairement complexes dans la construction ${} en utilisant les quatre opérations de base (+,-,*,/), le moins unaire et les parenthèses. Exemples:

<cylinder radius="${wheeldiam/2}" length="0.1"/>
<origin xyz="${reflect*(width+.02)} 0 0.25" />

Vous pouvez également utiliser plus que les opérations mathématiques de base, comme sin et cos.

Macros

Voici le composant le plus important et le plus utile du package xacro.

Macro simple

Jetons un coup d’œil à une simple macro inutile.

<xacro:macro name="default_origin">
    <origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:macro>
<xacro:default_origin />

(Ceci est inutile, car si l’origine n’est pas spécifiée, elle a la même valeur que this.) Ce code générera ce qui suit.

<origin rpy="0 0 0" xyz="0 0 0"/>
  • Le nom n’est techniquement pas un élément obligatoire, mais vous devez le spécifier pour pouvoir l’utiliser.

  • Chaque instance de <xacro:$NAME /> est remplacée par le contenu de la balise xacro:macro.

  • Notez que même si ce n’est pas exactement le même (les deux attributs ont changé d’ordre), le XML généré est équivalent.

  • Si le xacro avec un nom spécifié n’est pas trouvé, il ne sera pas développé et ne générera PAS d’erreur.

Macro paramétrée

Vous pouvez également paramétrer les macros afin qu’elles ne génèrent pas le même texte exact à chaque fois. Lorsqu’il est combiné avec la fonctionnalité mathématique, c’est encore plus puissant.

Prenons d’abord un exemple de macro simple utilisée dans R2D2.

<xacro:macro name="default_inertial" params="mass">
    <inertial>
            <mass value="${mass}" />
            <inertia ixx="1e-3" ixy="0.0" ixz="0.0"
                 iyy="1e-3" iyz="0.0"
                 izz="1e-3" />
    </inertial>
</xacro:macro>

Ceci peut être utilisé avec le code

<xacro:default_inertial mass="10"/>

Les paramètres agissent comme des propriétés et vous pouvez les utiliser dans des expressions

Vous pouvez également utiliser des blocs entiers comme paramètres.

<xacro:macro name="blue_shape" params="name *shape">
    <link name="${name}">
        <visual>
            <geometry>
                <xacro:insert_block name="shape" />
            </geometry>
            <material name="blue"/>
        </visual>
        <collision>
            <geometry>
                <xacro:insert_block name="shape" />
            </geometry>
        </collision>
    </link>
</xacro:macro>

<xacro:blue_shape name="base_link">
    <cylinder radius=".42" length=".01" />
</xacro:blue_shape>
  • Pour spécifier un paramètre de bloc, incluez un astérisque avant son nom de paramètre.

  • Un bloc peut être inséré à l’aide de la commande insert_block

  • Insérez le bloc autant de fois que vous le souhaitez.

Utilisation pratique

Le langage xacro est plutôt flexible dans ce qu’il vous permet de faire. Voici quelques façons utiles d’utiliser xacro dans le modèle R2D2, en plus du modèle par défaut macro inertielle illustrée ci-dessus.

Pour voir le modèle généré par un fichier xacro, exécutez la même commande qu’avec les tutoriels précédents :

ros2 launch urdf_tutorial display.launch.py model:=urdf/08-macroed.urdf.xacro

(Le fichier de lancement a exécuté la commande xacro tout ce temps, mais comme il n’y avait pas de macros à développer, cela n’avait pas d’importance)

Macro jambe

Souvent, vous souhaitez créer plusieurs objets similaires à différents endroits. Vous pouvez utiliser une macro et quelques calculs simples pour réduire la quantité de code que vous devez écrire, comme nous le faisons avec les deux jambes de R2.

<xacro:macro name="leg" params="prefix reflect">
    <link name="${prefix}_leg">
        <visual>
            <geometry>
                <box size="${leglen} 0.1 0.2"/>
            </geometry>
            <origin xyz="0 0 -${leglen/2}" rpy="0 ${pi/2} 0"/>
            <material name="white"/>
        </visual>
        <collision>
            <geometry>
                <box size="${leglen} 0.1 0.2"/>
            </geometry>
            <origin xyz="0 0 -${leglen/2}" rpy="0 ${pi/2} 0"/>
        </collision>
        <xacro:default_inertial mass="10"/>
    </link>

    <joint name="base_to_${prefix}_leg" type="fixed">
        <parent link="base_link"/>
        <child link="${prefix}_leg"/>
        <origin xyz="0 ${reflect*(width+.02)} 0.25" />
    </joint>
    <!-- A bunch of stuff cut -->
</xacro:macro>
<xacro:leg prefix="right" reflect="1" />
<xacro:leg prefix="left" reflect="-1" />
  • Astuce commune 1 : utilisez un préfixe de nom pour obtenir deux objets portant le même nom.

  • Astuce commune 2 : Utilisez les mathématiques pour calculer les origines des articulations. Dans le cas où vous modifiez la taille de votre robot, la modification d’une propriété avec quelques calculs pour calculer le décalage de l’articulation vous évitera beaucoup de problèmes.

  • Astuce commune 3 : Utiliser un paramètre de réflexion et le régler sur 1 ou -1. Voyez comment nous utilisons le paramètre reflect pour placer les jambes de chaque côté du corps dans l’origine base_to_${prefix}_leg.