23
TUTORIALES ROS

TUTORIALES ROS.pdf

Embed Size (px)

Citation preview

Page 1: TUTORIALES ROS.pdf

TUTORIALES ROS

Page 2: TUTORIALES ROS.pdf

2

INTRODUCCIÓN

ROS es un meta-sistema operativo para robots, es decir, una librería de código que debe ser

instalada en un sistema operativo y que será invocada desde un programa ejecutable. Entre sus ventajas destaca que se abstrae del hardware, controla los dispositivos a bajo nivel, implementa las funcionalidades más comunes en el manejo de robots, intercambia mensajes entre procesos y es capaz de administrar paquetes de código. También proporciona librerías y herramientas para obtener, construir, escribir y ejecutar código a través de múltiples ordenadores.

ROS engloba una estructura o red del tipo p2p (peer to peer o puesto a puesto) donde los

procesos se comunican entre ellos usando la infraestructura de comunicaciones de ROS. Esta comunicación es síncrona cuando se utilizan servicios (comunicación tipo petición/respuesta). Es asíncrona cuando se usan topics o temas; un topic es un bus por donde circulan mensajes intercambiados por nodos, que son pequeños programas que hacen tareas.

Para programar elementos ROS pueden usarse 3 lenguajes: c++, python y Lisp.

SISTEMAS DE FICHEROS ROS

En cuanto a sus archivos, ROS se compone de: - Paquetes (packages): Un paquete puede contener nodos (procesos ejecutables), librerías,

conjuntos de datos (datasets), archivos de configuración, etc. - Manifiestos (manifests): son archivos con extensión .xml que proporcionan información

(metadatos), sobre todo de dependencias del paquete. - Pilas (stacks): Son colecciones de paquetes. - Manifiestos de Pila: Igual que los de paquetes. - Tipos de Mensajes: Archivos con extensión .msg. Las descripciones de los mensajes están

almacenadas en my_package/msg/MyMessageType.msg y describen la estructura de los mensajes enviados en ROS

- Tipos de Servicios: Archivos con extensión .srv. Las descripciones de los servicios están almacenadas en my_package/srv/MyServiceType.srv y definen las peticiones y respuestas de las estructuras de datos para los servicios en ROS. ELEMENTOS DE COMPUTACIÓN DE ROS

- Nodos: Los nodos son procesos ejecutables. ROS está diseñado para ser modular. Por ejemplo, un robot tendrá un nodo para controlar el láser, otro nodo para controlar los motores de desplazamiento, otro nodo para calcular el camino correcto, etc… Un nodo se programa utilizando una librería cliente ROS, como por ejemplo ‘roscpp’ o ‘rospy’, si se va a programar en c++ o pyhton respectivamente.

- Maestro (master): asigna nombres a todos los elementos que conforman el modelo del robot. Sin él, los nodos no se “encontrarían” ni podrían intercambiar mensajes entre ellos.

- Servidor de Parámetros: Forma parte del Maestro y permite almacenar datos por clave en una localización centralizada.

- Mensajes: Los nodos los utilizan para comunicarse entre ellos. Los mensajes son simples estructuras de datos compuestas por campos tipificados.

- Temas (topics): Son mensajes transportados mediante semántica de publicación/subscripción. Un nodo publica un mensaje en un topic dado; El topic es un nombre usado para identificar el contenido del mensaje; Un nodo que esté interesado en un cierto tipo de datos, se subscribirá al topic apropiado. Puede haber múltiple publicadores y subscriptores para un único topic y un único nodo puede publicar y/o subscribirse a múltiples topics. La idea es desconectar los proveedores de información de los consumidores.

Page 3: TUTORIALES ROS.pdf

3

- Servicios: Este sistema atiende al modelo petición/respuesta. Un nodo ofrece un servicio bajo un nombre determinado y un nodo cliente usa el servicio enviando un mensaje de petición y esperando un mensaje de respuesta.

- Bolsas (bags): La bolsa es un mecanismo para guardar mensajes y reproducirlos posteriormente. Es de una importancia vital por ejemplo para guardar datos de sensores.

El Maestro actúa como un servidor de nombres. Los nodos se comunican con el Maestro para

facilitarle sus informaciones de registro. Como esos nodos se comunican con el maestro, pueden recibir información de otros nodos registrados y establecer comunicación.

El protocolo más común usado para comunicar nodos directamente es TCPROS, basado en los

sockets estándar TCP/IP. Los nombres son muy importantes en ROS. Todos los elementos computacionales en ROS

tienen nombre: los nodos, los topics, los servicios y los parámetros. Todas las librerías cliente de ROS soportan el remapeo de nombres por línea de comandos, lo que permite que un programa ya compilado pueda ser reconfigurado en tiempo real para operar en una topología computacional diferente.

Por ejemplo, para controlar un láser Hokuyo, podemos iniciar el nodo-driver hokuyo, el cual

dialoga con el dispositivo láser y publica mensajes del tipo ‘LaserScan’ en el topic ‘scan’. Para procesar esos datos, debemos programar un nodo usando filtros laser que se suscriben a los mensajes del topic ‘scan’. Tras la subscripción, nuestro filtro podría automáticamente iniciarse recibiendo mensajes desde el láser.

Observa como los dos extremos están desconectados. El nodo hokuyo publica datos del láser

sin saber quién está subscrito. El nodo filtro está subscrito al sensor sin saber qué nodo publica sus datos. Los dos nodos pueden ser iniciados, finalizados y reiniciados, en cualquier orden, sin que se produzcan errores.

Si más tarde se añade otro láser a nuestro robot, necesitaremos reconfigurar nuestro sistema.

Todo lo que necesitamos es remapear los nombres que son usados. Cuando iniciamos nuestro primer nodo hokuyo, podemos indicarle que remapee ‘scan’ a ‘base_scan’ y hacer lo mismo con el nodo filtro. Ahora esos nodos se comunicarán usando el topic ‘base_scan’ sin escuchar ya mensajes del topic ‘scan’. Hecho esto podemos iniciar un nuevo nodo hokuyo.

Page 4: TUTORIALES ROS.pdf

4

COMENZANDO A TRABAJAR CON ROS. ENTORNO DE TRABAJO

En primer lugar nos aseguramos que ROS está correctamente instalado y definido de forma correcta. Ejecutamos en un terminal la siguiente instrucción:

$ export | grep ROS

Como resultado, debe darnos una serie de definiciones como el path de ROS, el host_name, el local host, etc… El entorno o espacio de trabajo (Workspace) está definido en la variable de entorno ROS_PACKAGE_PAT. Podemos ver su valor:

$ echo $ROS_PACKAGE_PATH

En la distribución OpenQbo será: /opt/ros/electric/stacks La estructura de las carpetas en el disco duro parte de la raíz del entorno de trabajo (/opt/ros/electric/stacks) Todas las carpetas que cuelguen de ahí, son pilas o stacks. Dentro de cada pila puede haber varios paquetes (varias subcarpetas) o un solo paquete. En el caso de que la pila contenga un único paquete veremos que directamente el directorio tiene carpetas como bin, config, launch o src. Además, contendrá archivos como manifest.xml, etc. En el caso de la distribución OpenQbo existe la carpeta /qbo_stack que cuelga del directorio /stacks de ROS. Existen varios comandos para “moverse” entre pilas y paquetes, como rospack, roscd, etc… pero es más cómodo utilizar la ventana del S.O.

Hablemos ahora de roscore. Para poder usar ROS hay que iniciar su estructura mediante la instrucción roscore. En la distribución de OpenQbo, roscore se autoejecuta al iniciar el sistema. Si intentamos ejecutar roscore, el sistema nos advertirá (en rojo) que ya se encuentra en ejecución (es el caso de la distribución OpenQbo).

ENTENDIENDO LOS NODOS

A partir de ahora entendemos que vamos a trabajar en la distribución de Ubuntu preparada por

TheCorpora, OpenQbo. Bien. Ya tenemos roscore ejecutándose en un terminal. Ahora debemos abrir otro terminal (sin cerrar el de roscore) para probar el comando rosnode. Ejecutamos:

$ rosnode list

Veremos que hay varios paquetes (no nodos) en ejecución: - /qbo_arduqbo - /qbo_audio_control - /qbo_listen - /qbo_talk - /qbo_video_record - /qbo_webi - /rosout - /stereo/stereo_proc - /stereo/uvc_camera_stereo

Page 5: TUTORIALES ROS.pdf

5

Si queremos tener más información sobre un paquete concreto escribimos “rosnode info

/nombre_paquete”:

$ rosnode info /qbo_audio_control Podremos ver sus publicaciones, subscripciones, servicios, etc… Para ejecutar un nodo se utiliza el comando “rosrun [paquete] [nodo]”. Se puede ejecutar un

nodo sin conocer la ruta de su paquete mediante el comando rosrun. NOTA: Para poder practicar con los ejemplos de ROS hay que instalarlos:

$ sudo apt-get install ros-electric-ros-tutorials

Con esto habremos instalado los tutoriales de la versión electric de ROS desde los repositorios

de ROS. Podemos comprobarlo testeando la existencia del directorio /ros_tutorials colgando de la carpeta /stacks. Ahora, para probar “rosrun [paquete] [nodo]” ejecutamos:

$ rosrun turtlesim turtlesim_node Veremos una ventana con una tortuga. Si abrimos un nuevo terminal y ejecutamos en él “rosnode list”, veremos que el paquete turtlesim se ha añadido a la lista de procesos en ejecución.

Veamos ahora cómo se trabaja con topics.

ENTENDIENDO LOS TOPICS

Para comprender el funcionamiento de los topics, abrimos 2 terminales nuevos. En uno ejecutamos un nodo:

$ rosrun turtlesim turtlesim_node

Y en el otro ejecutamos otro nodo:

$ rosrun turtlesim turtle_teleop_key El primero presenta una ventana con una tortuga y el segundo nodo permite mover dicha tortuga por la ventana con las teclas de flecha (este terminal debe estar activo). Los dos nodos se comunican mediante un topic: el nodo turtle_teleop_key publica las pulsaciones de teclas en un topic. A su vez, el nodo turtlesim recibe dichas pulsaciones de ese topic al que está subscrito. ROS tiene una herramienta muy útil para ver gráficamente los nodos y topics en ejecución.

$ rxgraph

Page 6: TUTORIALES ROS.pdf

6

El esquema inicial de OpenQbo es el siguiente:

Otra herramienta es rostopic.

Page 7: TUTORIALES ROS.pdf

7

Si al ejecutar lo anterior no ocurre nada, es porque no se está emitiendo ningún mensaje en el

topic. En caso contrario se visualizan los mensajes en pantalla mostrando los valores lineales y angulares del movimiento de la tortuga. Esto funciona porque “rostopic echo” es en realidad un nodo que se ha subscrito al topic y visualiza sus mensajes:

El comando de la figura superior visualiza una lista de todos los topics actuales

Veamos ahora el comando “rostopic type” utilizado para ver el tipo de un topic:

Page 8: TUTORIALES ROS.pdf

8

Otro comando es “rosmsg show” que visualiza los campos de un mensaje:

La combinación de ambos comandos muestra los mensajes de un topic. $ rostopic type /turtle1/command_velocity | rosmsg show

ENTENDIENDO LOS SERVICIOS Y LOS PARÁMETROS

Un servicio es otra forma en la que un nodo puede comunicarse con el resto. Los servicios

permiten a los nodos enviar una petición y recibir una respuesta. Veamos el comando rosservice:

El comando list muestra los servicios que proporciona un nodo. En el caso del nodo turtlesim son 9:

Veamos de qué tipo es el servicio “clear”:

Al ejecutar el comando type sobre el servicio clear, rosservice devuelve que el servicio está vacío. Esto ocurre cuando la llamada a un servicio se hace sin argumentos (no envía datos al hacer una

Page 9: TUTORIALES ROS.pdf

9

petición y no recibe datos al recibir una respuesta). Simplemente el servicio actúa como un programa que hace una determinada tarea sin necesidad de recibir argumentos y sin devolver datos. Para ejecutar un servicio se usa el siguiente comando:

Veamos un servicio con parámetros, por ejemplo “spawn”. Este es un servicio utilizado por el nodo turtlesim_node, así que vamos a ejecutarlo primero en un terminal: $ rosrun turtlesim turtlesim_node Ahora vamos a ver cuáles son sus parámetros o argumentos (teclear en otro terminal):

Obsérvese que este servicio tiene 4 parámetros de entrada y uno de salida. Al hacer:

Hemos llamado al servicio spawn indicando sus 4 argumentos. La llamada al servicio devuelve el parámetro name (que es el nombre de una nueva tortuga): Veamos ahora el comando “rosparam”:

Este comando permite almacenar y manipular datos en el servidor de parámetros ROS. El servidor de parámetros puede almacenar enteros, flotantes, booleanos, diccionarios y listas. Un diccionario se representa como {a: b, c: d}. Una lista se representa como [1, 2, 3]. NOTA: El servidor de parámetros es un diccionario compartido accesible por todos los nodos ROS. Los nodos usan este servidor para almacenar y recuperar datos en tiempo real. Suele utilizarse como parámetros de configuración. Hemos comentado que el servidor de parámetros es un diccionario. Veamos un ejemplo de cómo funciona:

Arriba vemos 4 parámetros con sus correspondientes valores. Los parámetros, de la misma

forma que ocurre con los nodos y topics, se ordenan por jerarquías:

Page 10: TUTORIALES ROS.pdf

10

El parámetro /camera/left/name tiene un valor de leftcamera. El parámetro/camera/left tiene un valor (en forma de diccionario) de:

Y el parámetro /camera tiene un valor (en forma de árbol) de:

Sigamos con el comando “rosparam”.

En el ejemplo superior observamos que hay 3 parámetros (los 3 primeros). Vamos a modificar el valor de uno de ellos:

Con esto hemos modificado el valor del parámetro. Si queremos ver el contenido del servidor de parámetros al completo, escribimos:

Finalmente, decir que podemos guardar el servidor de parámetros en un archivo y recuperarlo después.

Arriba hemos guardado el servidor de parámetros en el archivo params.yaml. Abajo, cargamos el archivo en un nuevo espacio de nombres (copy):

Page 11: TUTORIALES ROS.pdf

11

CREANDO UN PAQUETE ROS

Un paquete ROS se compone de diferentes archivos: archivos de manifiesto .xml, CMakeLists.txt, Makefiles, mainpage.dox, … Existe un comando ROS que automatiza la tarea de compilación de un paquete: roscreate-pkg. Para crear un paquete en el directorio actual se utiliza:

Si dicho paquete tiene dependencias de otros paquetes se utiliza:

Como ejemplo, vamos a crear un paquete llamado “beginner_tutorials” que tendrá dependencias de std_msgs, roscpp y rospy. Por defecto, la distribución OpenQbo no permite modificar la carpeta /ros. Debemos por tanto habilitar esta función. Lo primero es permitir a nuestro usuario el acceso a dicha carpeta: $ sudo chown –R nombredeUsuario /opt/ros/electric/stacks

Nos desplazamos a la carpeta /stacks: $ cd /opt/ros/electric/stacks Y creamos el paquete:

Veremos una ventana que nos informará de la creación de los directorios y archivos que forman el paquete:

Page 12: TUTORIALES ROS.pdf

12

Al final recomienda echar una mirada al archivo manifest.xml que es donde se describe la estructura del paquete. Si nos desplazamos al directorio del paquete recién creado podemos ver que ha creado una estructura como la de la siguiente figura:

Ahora vamos a asegurarnos que ROS puede ver el paquete. A menudo es útil ejecutar “rospack profile” para que se reflejen los cambios de directorios en nuestro PATH. Luego podemos buscar el paquete:

Respecto a las dependencias, se denominan dependencias de primer orden a las que hemos indicado en el momento de creación del paquete:

Estas dependencias quedan reflejadas en el archivo manifest.xml. También existen las llamadas dependencias indirectas, que son las dependencias de otras dependencias. Por ejemplo, nuestro paquete ha heredado las dependencias del paquete rospy.

Así, si testeamos las dependencias de nuestro paquete, se verán las directas e indirectas:

Page 13: TUTORIALES ROS.pdf

13

CREANDO ARCHIVOS MSG Y ARCHIVOS SRV

Estos archivos de texto se utilizan para describir los elementos y parámetros de los mensajes y

servicios. Si por ejemplo programamos un nodo servicio con 2 parámetros de entrada y uno de salida, estos parámetros hay que definirlos en un archivo .srv asociado a dicho servicio. Los archivos msg son simples archivos de texto que describen los campos de un mensaje ROS. Estos archivos son utilizados para generar código fuente de los mensajes en diferentes lenguajes durante el proceso de compilación. Los archivos srv describen un servicio. Se componen de dos partes separadas por guiones: una petición y una respuesta. Los archivos msg se almacenan en el directorio /msg dentro del paquete y los archivos srv dentro del directorio /srv Dentro de un archivo msg se pueden utilizar los tipos int8, int16, int32, int64, uint8, uint16, … También los float32, float64, string, time, duration, array[] así como otros archivos msg. Se comienza por el tipo especial de ROS llamado Header. Un ejemplo de archivo msg:

Los archivos srv son similares a los msg, excepto que tienen dos partes: una petición al principio y una respuesta tras los tres guiones. Veamos un ejemplo:

Page 14: TUTORIALES ROS.pdf

14

Para crear un mensaje se crea un fichero .msg en la carpeta /msg del paquete con el contenido deseado. Para nuestro ejemplo creamos el archivo “Num.msg” con el siguiente contenido: int64 num Podemos crear el archivo utilizando nuestro editor favorito o bien desde un terminal de la siguiente forma:

Para asegurarnos que se generará el mensaje cuando compilemos el paquete, hay que abrir el archivo CMakeLists.txt en un editor y quitar el símbolo de comentario (#) de la siguiente línea: # rosbuild_genmsg() Creado el mensaje, podemos comprobar que ROS lo ‘ve’ utilizando el comando “rosmsg show” Ejemplo:

Si no sabemos el nombre del paquete donde está el mensaje podemos hacer:

Pasemos ahora a los servicios. Para crear un servicio generamos previamente un archivo dentro de la carpeta /srv con el contenido de los parámetros a utilizar por dicho servicio. Para nuestro ejemplo, creamos un archivo llamado AddTwoInts.srv con el siguiente contenido: int64 a int64 b --- int64 sum

Finalmente tenemos que quitar del archivo CMakeLists.txt el # de la línea: # rosbuild_gensrv() Al igual que antes podemos asegurarnos que ROS lo ve.

Finalmente, al haber creado un archivo .msg y/o un archivo .srv, hay que volver a compilar el paquete: $ rosmake beginner_tutorials

Page 15: TUTORIALES ROS.pdf

15

Tras la compilación, ya tenemos introducido en el paquete los dos archivos creados.

ESCRIBIENDO UN NODO PUBLICADOR EN C++

Nodo es el término ROS para un ejecutable que conecta a la red ROS. Vamos a crear un nodo

publicador que emitirá continuamente un mensaje a dicha red. Lo primero que debemos hacer es desplazarnos con roscd al directorio de nuestro paquete:

Ahora, dentro de la carpeta /src, vamos a crear un archivo (para este ejemplo, talker.cpp) con el siguiente contenido: #include "ros/ros.h" //Obligatorio ponerlo para trabajar con ROS #include "std_msgs/String.h" //El tipo String de ROS #include <sstream> /** * Este tutorial muestra cómo enviar mensajes al sistema ROS */ int main(int argc, char **argv) { /** * La función ros::init() inicializa el nodo “talker” en el sistema ROS. Le pasamos los argumentos para la línea de comandos * y un tercer argumento con el nombre del nodo. */ ros::init(argc, argv, "talker"); /** * NodeHandle es el principal punto de acceso para comunicarse con el sistema ROS */ ros::NodeHandle n; /** * La función advertise() sirve para decirle a ROS que queremos publicar en un topic determinado. * (en este caso el topic se llama “chatter” y estamos indicando que el mensaje será de tipo String) * Con esto hacemos una llamada al nodo maestro de ROS, el cual lleva un registro de qué nodos están * publicando y qué nodos están subscritos. advertise() devuelve un objeto publicador que te permite * publicar mensajes en el topic mediante una llamada a la función publish(). * * El segundo parámetro de advertise() es el tamaño de la cola del mensaje usada para publicar mensajes. * Si los mensajes son publicados más deprisa de lo que puedan enviarse, se guardarán en la cola. */ ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); ros::Rate loop_rate(10); //Define la frecuencia en hercios de polling (10 veces por segundo). /** * Contador con el número de mensajes enviados. Se usa para crear un únic string por cada mensaje. */ int count = 0; while (ros::ok()) //Mientras el sistema ROS esté operativo { /** * Objeto mensaje donde cargamos los datos para después publicarlo. */

Page 16: TUTORIALES ROS.pdf

16

std_msgs::String msg; std::stringstream ss; ss << "hello world " << count; msg.data = ss.str(); //data es un miembro del tipo String. String es un archivo .msg ROS_INFO("%s", msg.data.c_str()); //este es el equivalente ROS a printf y cout /** * Con la función publish() enviamos los mensajes. El parámetro es el objeto mensaje * El tipo del objeto debe coincidir con el que pusimos en advertise() */ chatter_pub.publish(msg); /** * Aquí no es necesaria, pero spinOnce() se pone para atender a las funciones callback. * Si hubiésemos añadido una subscripción en esta aplicación, no funcionaría sin esta llamada. */ ros::spinOnce(); loop_rate.sleep(); //Aplica el sleep que definimos antes. Publica 10 veces/seg ++count; } return 0; }

NOTA FINAL: Para crear este nodo en el paquete, debemos editar el archivo “CMakeLists.txt” del directorio y añadir al final la siguiente línea: Rosbuild_add_executable(talker src/talker.cpp)

Esto creará un ejecutable “talker”, que por defecto se ubicará en la carpeta /bin.

Ahora, ejecutar: $ make

ESCRIBIENDO UN NODO SUBSCRIPTOR EN C++

Al igual que antes, nos desplazamos al directorio de nuestro paquete usando roscd y creamos el archivo “listener.cpp” dentro de la carpeta /src. #include "ros/ros.h" //Obligatorio ponerlo para trabajar con ROS #include "std_msgs/String.h" //El tipo String de ROS /** * Este tutorial muestra un simple nodo receptor de mensajes. */ void chatterCallback(const std_msgs::String::ConstPtr& msg) { ROS_INFO("I heard: [%s]", msg->data.c_str()); } int main(int argc, char **argv) { /** * init() inicializa el nodo “listener” en el sistema ROS */ ros::init(argc, argv, "listener");

Page 17: TUTORIALES ROS.pdf

17

/** * n es el manejador del nodo. */ ros::NodeHandle n; /** * Mediante la función subscribe() indicamos a ROS que queremos recibir los mensajes del topic “chatter” * Los mensajes se pasan a una función de callback (aquí llamada chatterCallback) * El número 1000 indica el tamaño de la cola de mensajes recibidos. */ ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); /** * ros::spin() hace que se entre en un polling buscando mensajes. Cuando llega un mensaje al topic, se llama a la función * chatterCallback() indicada antes. * Hay otras formas de hacer esto. */ ros::spin(); return 0; }

NOTA FINAL: Para crear este nodo en el paquete, debemos editar el archivo “CMakeLists.txt” del directorio y añadir al final la siguiente línea: rosbuild_add_executable(listener src/listener.cpp)

Esto creará un ejecutable “listener”, que por defecto se ubicará en la carpeta /bin.

Ahora, ejecutar: $ make

ESCRIBIENDO UN NODO PUBLICADOR EN PYTHON

A diferencia de C++, el código fuente escrito en python se denomina script. Hay que guardarlo en la carpeta /scripts del paquete. Creamos el archivo “talker.py” con el siguiente contenido: #!/usr/bin/env python #Esta linea asegura que el script será ejecutable import roslib; roslib.load_manifest('beginner_tutorials') #importa las librerías import rospy #imprescindible para un nodo python from std_msgs.msg import String #importa el tipo String def talker(): #describe el interfaz de talker pub = rospy.Publisher('chatter', String) #declara que el nodo publicará un String en el topic chatter rospy.init_node('talker') #indica el nombre del nodo while not rospy.is_shutdown(): #mientras el sistema esté en funcionamiento str = "hello world %s" % rospy.get_time() #construye el mensaje rospy.loginfo(str) pub.publish(String(str)) #publica el mensaje rospy.sleep(1.0) #Hace una pausa if __name__ == '__main__': #Para finalizar la ejecución tras un Ctrl-C o finalización try: talker() except rospy.ROSInterruptException:

Page 18: TUTORIALES ROS.pdf

18

pass

Para hacer ejecutable un nodo escrito en python:

$ chmod +x scripts/talker.py $ make

ESCRIBIENDO UN NODO SUBSCRIPTOR EN PYTHON

Creamos el archivo scripts/listener.py: #!/usr/bin/env python import roslib; roslib.load_manifest('beginner_tutorials') import rospy from std_msgs.msg import String def callback(data): rospy.loginfo(rospy.get_name() + ": I heard %s" % data.data) def listener(): rospy.init_node('listener', anonymous=True) #declara el nodo. Con anonymous podemos tener varios listener rospy.Subscriber("chatter", String, callback) #se suscribe al topic chatter para recibir un string rospy.spin() #hace que el nodo permanezca a la escucha infinitamente (no funciona como en c++) if __name__ == '__main__': listener()

Para hacer ejecutable un nodo escrito en python: $ chmod +x scripts/listener.py $ make

FUNCIONAMIENTO DE PUBLICADORES Y SUBSCRIPTORES

Lo primero es ejecutar el sistema ROS:

Ejecutamos el publicador:

Page 19: TUTORIALES ROS.pdf

19

Lo que estamos viendo es el mensaje que el nodo publica en el topic chatter. Ahora vamos a ver cómo funciona el nodo subscriptor. Escribir en otro terminal:

ESCRIBIENDO UN NODO SERVICIO Y UN NODO CLIENTE EN C++

El nodo que vamos a programar recibe 2 números enteros y devuelve la suma de ellos. Para que funcione este ejemplo, debemos tener en la carpeta /srv un fichero llamado “AddTwoInst.srv” con el contenido siguiente (y haberlo compilado en el paquete mediante rosmake):

NOTA: Al compilar el archivo srv, se genera automáticamente un archivo include (.h) asociado. Nos desplazamos al directorio de nuestro paquete con roscd y creamos add_two_ints_server.cpp en la carpeta /src con el siguiente contenido: #include "ros/ros.h" #include "beginner_tutorials/AddTwoInts.h" // Esta funciona recoge los argumentos de la petición (req) y de la respuesta (res) para trabajar con ellos bool add(beginner_tutorials::AddTwoInts::Request &req, beginner_tutorials::AddTwoInts::Response &res) { res.sum = req.a + req.b; ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b); ROS_INFO("sending back response: [%ld]", (long int)res.sum); return true; } int main(int argc, char **argv) { ros::init(argc, argv, "add_two_ints_server"); //Inicializa el nodo servicio ros::NodeHandle n; //manejador del nodo

Page 20: TUTORIALES ROS.pdf

20

ros::ServiceServer service = n.advertiseService("add_two_ints", add); //anuncia el servicio a ROS y su función asociada ROS_INFO("Ready to add two ints."); ros::spin(); return 0; }

El nodo servicio está a disposición de los nodos denominados “cliente”. Para programar un nodo cliente, creamos en /src el archivo “add_two_ints_client.cpp”: #include "ros/ros.h" #include "beginner_tutorials/AddTwoInts.h" #include <cstdlib> int main(int argc, char **argv) { ros::init(argc, argv, "add_two_ints_client"); if (argc != 3) { ROS_INFO("usage: add_two_ints_client X Y"); return 1; } ros::NodeHandle n; // Lo siguiente crea un cliente add_two_ints para el servicio AddTwoInts ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints"); beginner_tutorials::AddTwoInts srv; //Instanciamos la estructura del archivo srv srv.request.a = atoll(argv[1]); srv.request.b = atoll(argv[2]); if (client.call(srv)) //llamada al nodo servicio con los argumentos definidos justo arriba { ROS_INFO("Sum: %ld", (long int)srv.response.sum); } else { ROS_ERROR("Failed to call service add_two_ints"); return 1; } return 0; }

Para construir estos nodos, editamos el archivo CMakeLists.txt de nuestro paquete y añadimos las siguientes líneas al final:

Esto creará dos ejecutables en la carpeta /bin. Finalmente ejecutar: $ make

ESCRIBIENDO UN NODO SERVICIO Y UN NODO CLIENTE EN PYTHON

De forma similar a c++ y teniendo creado y empaquetado el archivo .srv, creamos el archivo add_two_ints_server.py en la carpeta /scripts del paquete.

Page 21: TUTORIALES ROS.pdf

21

#!/usr/bin/env python import roslib; roslib.load_manifest('beginner_tutorials') from beginner_tutorials.srv import * import rospy def handle_add_two_ints(req): print "Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b)) return AddTwoIntsResponse(req.a + req.b) def add_two_ints_server(): rospy.init_node('add_two_ints_server') s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints) print "Ready to add two ints." rospy.spin() if __name__ == "__main__": add_two_ints_server()

Para hacer el nodo ejecutable: $ chmod +x scripts/add_two_ints_server.py $ make Ahora, para escribir un nodo cliente llamado scripts/add_two_ints_client.py #!/usr/bin/env python import roslib; roslib.load_manifest('beginner_tutorials') import sys import rospy from beginner_tutorials.srv import * def add_two_ints_client(x, y): rospy.wait_for_service('add_two_ints') try: add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts) resp1 = add_two_ints(x, y) return resp1.sum except rospy.ServiceException, e: print "Service call failed: %s"%e def usage(): return "%s [x y]"%sys.argv[0] if __name__ == "__main__": if len(sys.argv) == 3: x = int(sys.argv[1]) y = int(sys.argv[2]) else: print usage() sys.exit(1) print "Requesting %s+%s"%(x, y) print "%s + %s = %s"%(x, y, add_two_ints_client(x, y))

Para hacer el nodo ejecutable: $ chmod +x scripts/add_two_ints_client.py $ make

FUNCIONAMIENTO DE LOS NODOS SERVICIO Y CLIENTE

Page 22: TUTORIALES ROS.pdf

22

Primero ejecutamos el nodo servicio:

Debes ver algo similar a esto:

Ahora ejecutamos el nodo cliente en otro terminal con los argumentos en la línea de comandos:

Debes ver algo similar a esto:

USANDO ROSLAUNCH

Este tutorial explica el uso de roslaunch para iniciar varios nodos al mismo tiempo.

Lo primero es crear un archivo de lanzamiento. Se guardan en el directorio /launch del paquete. Siguiendo con nuestro paquete-tutorial, escribimos en un terminal:

Ahora creamos un archivo llamado turtlemimic.launch en el directorio /launch de nuestro paquete con el siguiente contenido: <launch> <group ns="turtlesim1"> <node pkg="turtlesim" name="sim" type="turtlesim_node"/> </group> <group ns="turtlesim2"> <node pkg="turtlesim" name="sim" type="turtlesim_node"/> </group> <node pkg="turtlesim" name="mimic" type="mimic"> <remap from="input" to="turtlesim1/turtle1"/> <remap from="output" to="turtlesim2/turtle1"/> </node>

Page 23: TUTORIALES ROS.pdf

23

</launch>

La etiqueta <launch> identifica el archivo xml como de tipo launch. Vemos también que se inician dos grupos con espacio de nombres denominado “turtlesim1” y “turtlesim2”. En realidad se están lanzando 2 nodos turtlesim, cada uno llamado de forma distinta. Con estas definiciones iniciaremos dos simuladores sin conflictos de nombres. El último bloque del archivo inicia el nodo “mimic” con los topics de entrada y salida renombrados a “turtlesim1” y “turtlesim2” Ahora lanzamos el archivo mediante roslaunch:

Veremos que se abren dos ventanas con las tortugas. En una nueva ventana en viamos el

comando rostopic siguiente: