ros tutorial

URDF, Joint State Publisher y Robot State Publisher

Qué es URDF ?? Qué son el nodo Joint state Publisher y el nodo Robot State Publisher?

En esta entrada veremos estos conceptos básicos, explicaremos qué es URDF y para que se usa. Sin embargo con URDF sólo, no definimos nuestro robot. Por ello trataremos de entender unos nodos de ROS que sirven para eso.

Antes que nada recalcar que aunque en esta entrada no desarrollemos ejemplos largos, queda a cargo del lector el trabajo de ejercitarse repitiendo y sobre todo entendiendo estos ejemplos además de creando otros nuevos. Ya que es fundamental la práctica de estos conocimientos para su correcto aprendizaje. Para más info

 

rosss_urdf
ruleta

URDF

Esta entrada quiere ser una introducción sencilla a URDF, ya que si bien este aspecto es fundamental para los diseñadores de robots, no lo es tanto para el que usa ROS con orientación industrial, pues normalmente los fabricantes de robots industriales ya nos dan estos archivos URDF.

No obstante, conviene tener conocimientos, ya que nos puede tocar unir varios robots o hacer modificaciones a estos.

Pues bien, vayamos al grano. Un archivo URDF no es más que un archivo, donde viene descrito cómo es físicamente el robot con el que estamos trabajando

Te recuerdo que puedes acceder al código de esta lección en el siguiente enlace

En este archivo se indican:

Las medidas, pesos, formas, fricciones de cada elemento que compone el robot. Estos elementos se llaman links 

Las uniones entre elementos, llamadas joints; definiremos el origen del joint, si estos joints tienen límites, que tipo de joint es, etc….

 – Definiremos transmisiones (esto lo dejaremos para otra entrada)

Este archivo URDF en el que viene descrito nuestro robot, es uno de los archivos más importantes en ROS.

Deberemos meter este archivo URDF que explica la estructura de nuestro robot, dentro de nuestro servidor de parámetros, para poder así ser interpretado por ROS.

 

Estos archivos URDF pueden venir en formato .urdf o en formato .xacro 

 

Siendo este último cada vez más usado, ya que nos va permitir hacer macros (estas macros nos on objeto de este tutorial) que nos reducirán y simplificarán nuestro archivo urdf.  En caso de usar estos archivos xacro, debemos  “parsearel xacro, es decir, debemos usar un traductor de xacro a urdf normal este traductor no es más que una línea en un archivo launch. 

Para ello usaremos el parámetro robot description, parámetro muy muy importante que usaremos por ejemplo para cargar el robot en simuladores como Gazebo, Rviz, etc… Esto lo haremos mediante un archivo launch.

Veamos un ejemplo, recordad que el código también lo podéis obtener en el siguiente enlace. Se trata de un simple robot con una base cuadrada,que tiene un primer brazo que rota perpendicular la base y que tiene otro elemento que gira tal y como podéis ver en la imagen :

mi_robot

Para ello hemos creado un paquete llamado mi_robot con el comando:

$ catkin_create_pkg mi_robot roscpp

Y dentro de este paquete, en la carpeta src, creamos otra carpeta llamada launch donde crearemos el siguiente archivo .launch, llamado basico.launch :

<?xml version="1.0" ?>
<robot name="mi_robot" xmlns:xacro="http://ros.org/wiki/xacro">
  
  <link name="world"/>

  <link name="base_link">
    <visual>
      <geometry>
        <box size="1.0 1.0 0.5"/>
      </geometry>
    </visual>
    <collision>
      <geometry>
        <box size="1.0 1.0 0.5"/>
      </geometry>
    </collision>
  </link>

  <joint name="world_to_base_link" type="fixed">
    <parent link="world"/>
    <child link="base_link"/>
    <origin xyz="0 0 0.25" rpy="0 0 0"/>
  </joint>

  <link name="link_0">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <cylinder length="1.1" radius="0.2"/>
      </geometry>
    </visual>
    <collision>
      <geometry>
        <cylinder length="1.1" radius="0.2"/>
      </geometry>
    </collision>
  </link>

  <joint name="base_link_to_link_0" type="revolute">
    <axis xyz="0 0 1"/>
    <limit lower="-3.1416" upper="3.1416" effort="10" velocity="3"/>
    <parent link="base_link"/>
    <child link="link_0"/>
    <origin xyz="0 0 0.8" rpy="0 0 0"/>
  </joint>

  <link name="link_1">
    <visual>
      <origin xyz="0 0 -0.55" rpy="0 0 0"/>
      <geometry>
        <cylinder length="1.1" radius="0.2"/>
      </geometry>
    </visual>
    <collision>
      <geometry>
        <cylinder length="1.1" radius="0.2"/>
      </geometry>
    </collision>
  </link>

  <joint name="link_0_to_link_1" type="revolute">
    <axis xyz="0 1 0"/>
    <limit lower="-3.1416" upper="3.1416" effort="10" velocity="3"/>
    <parent link="link_0"/>
    <child link="link_1"/>
    <origin xyz="0 0 0.55" rpy="0 0 0"/>
  </joint>

</robot>

En las dos primeras líneas simplemente le estamos diciendo que nuestro robot se llamará «mi_robot». Y que el archivo que estamos construyendo será del tipo «xacro» .

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

Ahora creamos un link, pero este es especial, es un link imaginario del entorno de nuestro robot al que llamaremos «world».

<link name="world"/>

Y ahora nos ponemos a crear un link como Dios manda, un link se define con el tag link.A su vez dentro tiene varios tags, en los links estos tags son : visualcollision y origin

El primero representa el aspecto geométrico visual del robot, es decir, de que forma va ser el elemento.

El segundo representa el áurea de colisión que va a tener el objeto, si no ponemos nada, osea 0, independientemente de los valores que hallamos puesto en la parte visual,nuestro elemento a la hora de cruzarse con otro no colisionará, ya que no tiene esta propiedad, lo normal es poner los mismos valores que en visual.

El último parámetro origin, representa dónde estará situado el eje de coordenadas en nuestra pieza. Si no ponemos nada, por defecto el centro del eje estará en el centro de la pieza,

 

Este primer link va a representar la base rectangular que llamaremos «base_link», para ello:                      

 <link name="base_link">
    <visual>
      <geometry>
        <box size="1.0 1.0 0.5"/>
      </geometry>
    </visual>
    <collision>
      <geometry>
        <box size="1.0 1.0 0.5"/>
      </geometry>
    </collision>
  </link>

En el tag visual hemos creado una pieza con forma «box», es decir un cubo, con las medidas 1 en el eje x, 1 en ele eje y, 0.5 en el eje z.

En el tag collision hacemos lo mismo.

Ahora vamos a unir nuestro cubo con el «mundo», para que este en el suelo. Para ello creamos un «joint» al que llamaremos «world_to_base_link» y que es del tipo fixed, pues no se va a mover esta base del suelo con respecto al suelo.

 

  <joint name="world_to_base_link" type="fixed">
    <parent link="world"/>
    <child link="base_link"/>
    <origin xyz="0 0 0.25" rpy="0 0 0"/>
  </joint>

Definimos un link padre, que será el link «world», y definimos un link hijo, que en este caso va a ser «base_link»

Y ahora decimos que el centro del joint va a estar a 0 en x, 0 en y, y 0.25 en z del origen del link padre (importante este dato). En este caso estamos diciendo que el centro de nuestro cubo (ya que en el apartado visual del base_link  no hemos dicho dónde está el centro, por lo que estará en 0,0,0), va a aparecer a 0.25 en z del centro de nuestro link padre, en este caso world.

Viene bien visualizar lo que hemos hecho hasta ahora, especialmente los centros de coordenadas, para ello usaremos Rviz. Sin embargo te diré cómo lanzar Rviz con el URDF de nuestro robot al final de la entrada así que sigue leyendo.

Ahora crearemos otro link, llamado link_0, que será el brazo que girará alrededor del eje z.

 <link name="link_0">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <cylinder length="1.1" radius="0.2"/>
      </geometry>
    </visual>
    <collision>
      <geometry>
        <cylinder length="1.1" radius="0.2"/>
      </geometry>
    </collision>
  </link>

Este link tiene su origen de coordenadas en el medio de su geometría, ya que en origin ponemos 0, 0, 0

En el apartado visual, definimos un cilindro de longitud 1.1 y radio 0.2. Este cilindro se creará a partir del origin definido arriba.

En collision repetimos lo mismo

Ahora deberíamos crear el joint que una la base cuadrada con el «link_0 «

  <joint name="base_link_to_link_0" type="revolute">
    <axis xyz="0 0 1"/>
    <limit lower="-3.1416" upper="3.1416" effort="10" velocity="3"/>
    <parent link="base_link"/>
    <child link="link_0"/>
    <origin xyz="0 0 0.8" rpy="0 0 0"/>
  </joint>

Este joint se llamará base_link_to_link0 y es del tipo revolute. Acto seguido declaramos en la etiqueta axis en torno a que eje va a girar nuestra pieza, en este caso girará en torno al eje z de la pieza, ya que hemos puesto 0,0,1.

Luego definimos el límite inferior y superior, aunque esto lo podríamos omitir.

Definimos «base_link» como «link padre». Y el «link_0» como «link hijo».  Definimos el origen a 0 de x, a 0 de y, y a 0.8 de z, con respecto al  del eje de coordenadas  del link padre, es decir respecto a «base_link».

 

Ahora crearemos otro link, llamado «link_1», que será el brazo que girará alrededor del eje y del elemento anterior.

<link name="link_1">
    <visual>
      <origin xyz="0 0 -0.55" rpy="0 0 0"/>
      <geometry>
        <cylinder length="1.1" radius="0.2"/>
      </geometry>
    </visual>
    <collision>
      <geometry>
        <cylinder length="1.1" radius="0.2"/>
      </geometry>
    </collision>
  </link>

Definimos el origen del eje de coordenadas. Como hemos dicho por defecto el eje se crea en 0,0,0. Es decir en el medio de la pieza, en el medio del volumen. Por lo que ahora vamos a poner el eje en la parte de abajo del cilindro, por eso bajamos el eje la mitad de la longitud del cilindro, para que este abajo.

eje

Ahora definimos el joint que unirá link_0 con link_1, este joint será tipo «revolute» y al poner 0, 1, 0. Le estamos permitiendo el giro respecto al eje y del eje del link padre.

 Ahora decimos que la unión con el elemento «link_1», esté a 0.55 en z respecto al eje z del link_padre, es decir de link_0.

el argumento rpy, es por si queremos girar el elemento (roll,pitch,yaw), esto se debe introducir en radianes.

  <joint name="link_0_to_link_1" type="revolute">
    <axis xyz="0 1 0"/>
    <limit lower="-3.1416" upper="3.1416" effort="10" velocity="3"/>
    <parent link="link_0"/>
    <child link="link_1"/>
    <origin xyz="0 0 0.55" rpy="0 0 0"/>
  </joint>

</robot>

Perfecto ya tenemos el archivo urdf que define a nuestro robot, pero n falta algo para que nuestro robot quede definido. Falta que apliquemos el nodo Joint State Publisher y el Robot State Publisher.  

Joint State Publisher

Joint State Publisher es un nodo de ROS, que va a permitir obtener la posición de todos los “joints” de un robot, a partir del cálculo de sus transformadas.  Es decir, nos va a decir en qué posición se encuentra cada elemento, y va a publicar dicha posición de cada joint en el topic joint_states 

Se usa especialmente para Rviz, sirve para representar en este programa el robot completo, se suele usar con un GUI (interfaz visual que permite cambiar los valores) 

Robot State Publisher

Robot State Publisher es un nodo de ROS que va a coger la información de:

 

  • archivo URDF
  • valores de joint_state_publisher.

Este nodo va ser capaz de sacar la posición real del robot

 

Estos dos nodos se usan conjuntamente y se lanzan de la siguiente manera:

<launch>

    <!-- Metemos la "robot_description" en el parameter server -->
        <param name="robot_description" command="$(find xacro)/xacro.py $(find mi_robot)/robot_description/mi_robot.urdf.xacro" />
    
    <!-- Iniciamos robot state publisher -->
        <node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher"/>
    <!-- Iniciamos joint state publisher, iniciamos el GUI-->
        <node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher">
            <param name="use_gui" value="True"/>
        </node>

    <!-- metemos el robot en Rviz, iniciamos Rviz   -->
     <node name="rviz" pkg="rviz" type="rviz"/>
    

</launch>

 

Aquí vemos cómo sería el archivo launch  en el que pasamos el archivo xacro al servidor de parámetros, más concretamente al parámetro robot_description. Para en ese mismo archivo ejecutar el joint_state_publisher y el robot state Publisher. 

 

En las primeras lineas metemos la ruta de nuestro archivo xacroy el «parseador» lo transforma para meterlo en el parámetro robot_description.

 

<param name="robot_description" command="$(find xacro)/xacro.py $(find mi_robot)/robot_description/mi_robot.urdf.xacro" />

En esta linea iniciamos el nodo robot_state_publisher:

 

<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher"/>

Y aquí iniciamos el nodo joint_state_publisher, además inicializamos un «gui» que nos permitirá mover los joints en Rviz  :

<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher">
     <param name="use_gui" value="True"/>

Por último esta línea ejecuta Rviz

   <node name="rviz" pkg="rviz" type="rviz"/>

Ahora deberíamos ejecutar el archivo launch, para ello escribimos en la terminal:

$ roslaunch mi_robot basico.launch

Donde mi_robot es el nombre del paquete y basico.launch el archivo launch recien creado

Si todo ha ido bien, se cragará Rviz, pero vamos a dejar esto de lado y volvamos a una terminal, en la que escrbiremos: 

$ rosparam list

Ahora veremos como tenemos un parámetro que antes no teníamos, el parámetro robot description, por lo que vamos a ver su valor, para ello volvemos a escribir en otra terminal (manteniendo el archivo launch ejecutado )

$ rosparam get /robot_description

Se nos mostrará el contenido del archivo URDF. Eso significa que el archivo se ha cargado en el servidor de parámetros correctamente y puede ser usado por otra aplicación. 

Ahora volvemos a Rviz, sin embargo por defecto no sale nada, saldrá la pantalla vacía:

vacio

Es normal, eso pasa por que a pesar de que tengamos el parámetro robot_description cargado,  no hemos añadido aun el robot en Rviz. Para ello pulsamos sobre el boton Add y elegimos Robot Model 

add

Ahora veremos el robot cargado, pero aun nos falta elegir un plano de referencia para verlo correctamente:

En Fixed Frame elegimos el link «world» como plano de referencia y ya está:

Además como hemos lanzado un GUI al iniciar Rviz veremos la siguiente ventana, en  la que moviendo las barras podemos mover cada uno de los joints del robot.

Así e esta entrada hemos aprendido qué es un URDF y como funciona. Se deja y se recomienda al lector como trabajo, el practicar con estos archivos que podéis encontrar en nuestro GitHub. Esto se puede complicar mucho más, pero esto se hará más adelante cuando veamos casos prácticos.

Además hemos explicado que es el joint state publisher y el robot state publisher. También hemos visto como ejecutar nuestro URDF en Rviz.

 

Hasta la siguiente entrada!!

Subscríbete a nuestro blog

ROS_custom_message

10 ROS Custom Message

ROS Custom Message Fundamentos de ROS Accede a otros posts En tutoriales anteriores a la hora de la comunicación pub/sub, siempre hemos usado mensajes predefinidos

Read More »

This website uses cookies to ensure you get the best experience on our website. By continuing to browse on this website, you accept the use of cookies for the above purposes.