Fundamentos de ROS 1 Noetic

Este capítulo presenta el flujo de trabajo de desarrollo de ROS 1 Noetic en reComputer Jetson, incluyendo espacios de trabajo, paquetes, herramientas comunes, comunicación por temas/servicios, mensajes personalizados y TF.

Los ejemplos largos y ejecutables se guardan en ./code/, y las figuras relacionadas se guardan en ./images/.

Contenido

7.2.1.1 Introduction to ROS 1

ROS 1 (Robot Operating System 1) es un framework de software robótico de código abierto, mantenido por Open Robotics. No es un sistema operativo en el sentido tradicional, sino que proporciona a las aplicaciones robóticas mecanismos de comunicación, cadenas de herramientas y una biblioteca de funcionalidades comunes, reduciendo notablemente la dificultad del desarrollo de software robótico. Ofrece los servicios que normalmente requiere un sistema operativo, incluyendo la abstracción de hardware, el control de dispositivos de bajo nivel, la implementación de funciones de uso común, la mensajería entre procesos y la gestión de paquetes. También proporciona las herramientas y funciones de biblioteca necesarias para obtener, compilar, preparar y ejecutar código entre distintos equipos.

Publicación de ROS 1

Las ediciones comunes de ROS 1 son las siguientes:

Nombre de la versiónUbuntuEstado de mantenimiento
Kinetic16.04Detenido
Melodic18.04Detenido
Noetic20.04Última versión de ROS 1 (LTS)

Los ejemplos posteriores de este capítulo se basarán en la versión noetic de ROS 1.

El objetivo principal de ROS es ofrecer soporte de reutilización de código para la investigación y el desarrollo robótico. ROS es un framework distribuido de procesos (es decir, "nodos") empaquetados en paquetes fáciles de compartir y publicar. ROS también admite un sistema colaborativo similar a un repositorio de código, que igualmente permite la colaboración y la difusión en ingeniería. Este diseño permite el desarrollo de un proyecto de ingeniería con una toma de decisiones completamente independiente (sin restricciones de ROS), desde el sistema de archivos hasta la interfaz de usuario. Al mismo tiempo, todos los trabajos pueden integrarse en las herramientas básicas de ROS.

Principales características de ROS 1

(1) Una estructura distribuida (cada proceso de trabajo se considera un nodo, gestionado mediante un gestor de nodos),

(2) Soporte multilingüe (por ejemplo, C++, Python, etc.),

(3) Buena elasticidad (se puede escribir un único nodo, o bien organizar muchos nodos en un proyecto más grande mediante roslaunch),

(4) Código abierto (ROS sigue la licencia BSD y es completamente gratuito para aplicaciones y modificaciones tanto personales como comerciales).

Arquitectura general de ROS 1

Nivel de comunidad de código abierto: incluye, entre otras cosas, el intercambio de conocimiento entre desarrolladores, códigos y algoritmos.

Nivel del sistema de archivos: descripción del código y los ejecutables que se pueden encontrar en el disco.

Nivel de cómputo (grafo de cómputo): refleja la comunicación entre proceso y proceso, y entre proceso y sistema.

Iniciar el entorno de desarrollo de ROS 1

El SeeedStudio Jetson Orin Nano Super DevKit, con una versión local de sistema Ubuntu 22.04, no admite el uso de ROS 1 de forma nativa, aunque sí fue evaluado previamente en JetPack 6; si está utilizando el BSP JetPack 6 que proporcionamos, puede usar el siguiente comando para iniciar en la ventana de terminal del dispositivo Jetson un contenedor Docker que incluye ROS 1, sobre Ubuntu 22.04:

bash
xhost +
sudo docker run -it \
  --net=host \
  --privileged \
  -v /dev:/dev \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  -e DISPLAY=$DISPLAY \
  -e QT_X11_NO_MITSHM=1 \
  ros:noetic

Si adquirió un dispositivo Jetson sin el entorno de desarrollo de ROS 1 preevaluado, consulte aquí para la instalación.

Perfil del grafo de cómputo

Nodos

El nodo es el módulo de implementación de cómputo más básico en ROS 1 y, por lo general, corresponde a un proceso que se ejecuta de forma independiente. Un sistema ROS no es un único programa, sino un sistema distribuido de múltiples nodos que trabajan de forma conjunta.

En ROS 1, cada nodo suele tener una función relativamente única y específica, por ejemplo:

Adquisición de datos de sensores (cámara, radar, IMU)

Procesamiento de algoritmos (localización, mapeo, planificación de rutas)

Salida de control (control de velocidad, control eléctrico)

Reenvío de datos y depuración (logs, visualización)

Características de los nodos de ROS 1

Proceso independiente Cada nodo suele ser un proceso de Linux independiente, y la interacción entre nodos se realiza mediante los mecanismos de comunicación de ROS.

Diseño desacoplado Las funciones no se llaman directamente entre nodos, sino que se comunican mediante Topic, Service, Action, Parameter, etc., para facilitar la extensión y el mantenimiento del sistema.

Nombre único Cada nodo debe tener un nombre único en el grafo de cómputo de ROS, por ejemplo: /turtle_velocity_publisher

Ejecución distribuible Los nodos pueden ejecutarse en distintos hosts, siempre que estén conectados al mismo ROS Master.

Ciclo de vida gestionado por el ROS Master Al iniciarse, el nodo registra su propia información (nombre, publicaciones/suscripciones, etc.) ante el ROS Master, y este gestiona la ejecución de cada nodo.

Métodos de comunicación habituales entre nodos de ROS 1

Topic (tema). Los nodos lo usan para comunicaciones listas para usar mediante el modelo de publicación/suscripción para flujos de datos de alta frecuencia.

Service (servicio) Comunicación síncrona basada en petición-respuesta.

Action (acción) Apta para tareas de larga duración, con soporte de retroalimentación y cancelación.

Parameter Server (servidor de parámetros) Se utiliza para almacenar los parámetros de ejecución del sistema.

El nodo es el proceso principal de implementación de cómputo. ROS está compuesto por muchos nodos.

Al introducir una parte del comando, basta con completarla con [Tab].

A continuación se muestra un ejemplo de grafo de nodos:

Cuando escribimos [rosnode] en la línea de comandos y pulsamos dos veces Tab, encontramos estas palabras clave debajo de la línea de comandos.

Herramienta de línea de comandos de ROS rosnode:

El desarrollo y la depuración requieren con frecuencia información sobre el nodo actual y otros nodos, por lo que conviene recordar estos comandos habituales. Si no lo consigue, también puede consultar el uso de rosnode mediante rosnode help.

Mensajes

Los enlaces lógicos y el intercambio de datos entre nodos se implementan mediante mensajes.

Cuando escribimos [rosmsg] en la línea de comandos y pulsamos dos veces la tecla Tab, encontramos estas palabras clave debajo de la línea de comandos.

Herramienta de línea de comandos de ROS rosmsg:

Topic (tema)

El topic es una forma de transmitir información (publicación/suscripción). Cada mensaje se publica en el topic correspondiente, y cada topic tiene un tipo estrictamente definido.

Los mensajes de topic de ROS se pueden transmitir mediante TCP/IP o UDP; ROS usa TCP/IP de forma predeterminada. La transmisión basada en TCP, como TCPROS, es una conexión de larga duración; la basada en UDP, UDPROS, es un modo de transmisión de baja latencia y eficiente, pero propenso a la pérdida de datos, adecuado para operación remota.

Cuando escribimos [rostopic] en la línea de comandos y pulsamos dos veces la tecla Tab, encontramos estas palabras clave debajo de la línea de comandos.

Herramienta de línea de comandos de ROS rostopic:

Servicios

El servicio también debe tener un nombre único para el modelo de petición-respuesta. Cuando un nodo ofrece un servicio, cualquier nodo puede comunicarse con él mediante código desarrollado con el cliente de ROS.

Cuando escribimos [rosservice] en la línea de comandos y pulsamos dos veces la tecla Tab, encontramos estas palabras clave debajo de la línea de comandos.

Herramienta de línea de comandos de ROS rosservice:

Paquete de registro de mensajes

El paquete de registro de mensajes es un formato de archivo para guardar y reproducir datos de mensajes de ROS, almacenado en archivos .bag. Es un mecanismo importante para el almacenamiento de datos.

Cuando escribimos [rosbag] en la línea de comandos y pulsamos dos veces Tab, encontramos estas palabras clave debajo de la línea de comandos.

Herramienta de línea de comandos de ROS rosbag:

Servidor de parámetros

El servidor de parámetros es un diccionario compartido de múltiples variables, accesible en línea y almacenado por clave en el gestor de nodos.

Cuando escribimos [rosparam] en la línea de comandos y pulsamos dos veces la tecla Tab, encontramos estas palabras clave debajo de la línea de comandos.

Herramienta de línea de comandos de ROS rosparam:

Gestor de nodos (Master)

El gestor de nodos se utiliza para el registro y la búsqueda de nombres de topics, servicios, etc. Sin el gestor de nodos, los nodos de todo el sistema ROS no podrían comunicarse entre sí.

Nivel del sistema de archivos

Se pueden configurar relaciones de dependencia entre paquetes. Si el paquete A depende del paquete B, B debe ser anterior a A en el sistema de compilación de ROS, y A puede usar los archivos de cabecera y de biblioteca de B.

Los conceptos del nivel del sistema de archivos son los siguientes:

Manifiesto del paquete:

Esta lista indica las dependencias del paquete, la documentación del código fuente, etc. El archivo package.xml del paquete es dicho manifiesto.

Paquete:

El paquete es la forma básica de organización de software en el sistema ROS y contiene nodos ejecutables, archivos de configuración, etc.

Comandos relacionados con paquetes de ROS

Metapaquete

Es posible formar una combinación de varios paquetes.

Tipo de mensaje

Para enviar mensajes entre nodos de ROS se necesita definir previamente su formato. ROS proporciona tipos de mensaje estándar, y también se pueden definir tipos propios. La descripción del tipo de mensaje se almacena en archivos msg dentro del paquete.

Tipo de servicio

Define la estructura de datos de la petición y la respuesta de cada servicio proporcionado por los procesos en ROS.

Nivel de comunidad de código abierto

Distribución: una distribución de ROS es un conjunto de paquetes integrados que se pueden instalar de forma independiente mediante un número de versión. Las distribuciones de ROS cumplen un papel similar al de las distribuciones de Linux. Esto facilita la instalación del software de ROS y permite mantener versiones consistentes mediante un repositorio de software.

Repositorio: ROS depende de sitios web o servicios de alojamiento de repositorios de código y software abiertos y compartidos, donde distintas organizaciones pueden publicar y compartir su propio software y programas robóticos.

ROS Wiki: la ROS Wiki es el foro principal para documentar información sobre los sistemas ROS. Cualquiera puede registrar una cuenta, aportar sus propios documentos, ofrecer correcciones o actualizaciones, preparar tutoriales y otras acciones.

Sistema de tickets de errores: si encuentra un problema o desea proponer una nueva función, ROS ofrece los recursos para hacerlo.

Lista de correo (Mailing list): la lista de correo de usuarios de ROS es el principal canal de comunicación de ROS, que permite intercambiar preguntas o información sobre actualizaciones y uso del software de ROS; lo mismo ocurre con el foro.

ROS Answers: los usuarios pueden usar este recurso para hacer preguntas.

Resumen del mecanismo de comunicación

Topic

El modo de comunicación publicación-suscripción se usa ampliamente en ROS. El Topic se utiliza generalmente para comunicación en flujo unidireccional. Los topics suelen tener una definición de tipo estricta: un topic de un tipo determinado solo puede aceptar/enviar mensajes de un tipo de dato específico. El publicador no exige coincidencia de tipos, pero el suscriptor comprueba el md5 del tipo al recibir, y en caso contrario se produce un error.

Service

El Service se usa para gestionar la comunicación síncrona en ROS, mediante la semántica servidor/cliente. Cada tipo de servicio consta de dos partes: petición y respuesta. Para el servidor de servicio, ROS no comprueba alias; solo el último servidor registrado es válido y queda conectado al cliente.

Action

Action usa varios topics para definir una tarea, incluyendo el objetivo (Goal), la retroalimentación (feedback) y el resultado (result). La compilación de una action genera automáticamente siete estructuras: Action, ActionGoal, ActionFeedback, ActionResult, Goal, Feedback y Result.

Características de action:

Mecanismo de comunicación de tipo pregunta-respuesta

Retroalimentación continua

Puede cancelarse durante la tarea.

Implementado sobre el mecanismo de mensajería de ROS

Interfaz de Action:

Goal: publica el objetivo de la tarea

Cancel: solicita la cancelación

Status: notifica al cliente el estado actual

Feedback: datos de control retroalimentados periódicamente durante la ejecución de la tarea

Result: envía el resultado del trabajo al cliente, solo una vez.

Comparación de los modos de comunicación

Componentes comunes

Archivos launch; conversión de coordenadas TF; Rviz; Gazebo; caja de herramientas QT; navegación; MoveIt!

Launch: el archivo Launch es una forma de activar varios nodos simultáneamente en ROS. También activa automáticamente el gestor de nodos (ROS Master) y permite habilitar la configuración de cada nodo, lo que facilita enormemente la operación de múltiples nodos.

TF (conversión de coordenadas): en el entorno de trabajo de un robot suele haber una gran cantidad de componentes, y la posición y orientación de los distintos componentes intervienen en el diseño y las aplicaciones robóticas. TF es un paquete que permite a los usuarios rastrear múltiples sistemas de coordenadas a lo largo del tiempo, usando una estructura de datos en árbol, almacenando en búfer el tiempo y manteniendo las relaciones entre múltiples coordenadas; puede ayudar a los desarrolladores a transformar coordenadas en cualquier momento, y a completar puntos entre coordenadas, vectores, etc.

Caja de herramientas QT: para facilitar la depuración y presentación visual, ROS ofrece un conjunto de herramientas gráficas de backend basado en la arquitectura Qt: rqt_common_plugins, que incluye numerosas herramientas prácticas: herramienta de salida de logs (rqt_console), herramienta de visualización del grafo de cómputo (rqt_graph), herramienta de mapeo de datos (rqt_plot) y herramienta de configuración dinámica de parámetros (rqt_reconfigure).

Rviz: rviz es una herramienta de visualización 3D basada en el framework de software ROS, con buena compatibilidad con diversas plataformas robóticas. En rviz, se puede usar XML para describir el tamaño, la masa, la posición, el material, las articulaciones, etc., del robot y los objetos circundantes, y presentarlos en la interfaz. Al mismo tiempo, rviz puede mostrar gráficamente en tiempo real la información de los sensores del robot, el estado de su movimiento y los cambios en el entorno circundante.

Gazebo: Gazebo es una potente plataforma de simulación física 3D, con un motor físico robusto, renderizado gráfico de alta calidad, programación e interfaz gráfica convenientes y, sobre todo, es de código abierto y gratuito. Aunque el modelo robótico en Gazebo es el mismo que se usa en rviz, es necesario añadir al modelo propiedades físicas del robot y del entorno circundante, como la masa, el coeficiente de fricción, el coeficiente de elasticidad, etc. El entorno de simulación se añade mediante plugins, y la información de los sensores del robot también puede presentarse en forma visual.

Navigation: navigation es el paquete de navegación 2D de ROS que, en términos simples, calcula comandos de control de velocidad seguros y fiables para el robot a partir del flujo de información y la posición global del robot (por ejemplo, la odometría de entrada).

MoveIt: ¡el paquete de funciones MoveIt! es el conjunto de herramientas más utilizado y se emplea principalmente para la planificación de trayectorias. MoveIt! depende de un asistente de configuración de documentos que resulta esencial para algunas tareas de planificación.

Todas las distribuciones de ROS 1

Enlace de referencia: http://wiki.ros.org/Distributions

La distribución de ROS (ROS release) se refiere a un paquete de software ROS, similar a una distribución de Linux (por ejemplo, Ubuntu). El lanzamiento de las distribuciones de ROS tiene como objetivo permitir a los desarrolladores usar un repositorio de código relativamente estable hasta que estén listos para actualizar todo el contenido. Por ello, los desarrolladores de ROS normalmente solo corrigen errores de una versión después de su lanzamiento, además de aportar mejoras menores en los paquetes principales. Hasta octubre de 2019, los nombres de las principales versiones de distribución de ROS, sus fechas de lanzamiento y su ciclo de vida se muestran en la siguiente tabla:

Enlaces de referencia

Wiki oficial de ROS:

Guía oficial de ROS: http://wiki.ros.org/ROS/Tutorials

Instalación de ROS: https://wiki.ros.org/noetic/Installation/Ubuntu (omita este paso si ROS ya está preinstalado)

Figuras

7.2.1.1 Introduction to ROS 1 figure 1

7.2.1.1 Introduction to ROS 1 figure 2

7.2.1.1 Introduction to ROS 1 figure 3

7.2.1.1 Introduction to ROS 1 figure 4

7.2.1.1 Introduction to ROS 1 figure 5

7.2.1.1 Introduction to ROS 1 figure 6

7.2.1.1 Introduction to ROS 1 figure 7

7.2.1.1 Introduction to ROS 1 figure 8

7.2.1.1 Introduction to ROS 1 figure 9

7.2.1.1 Introduction to ROS 1 figure 10

7.2.1.1 Introduction to ROS 1 figure 11

7.2.1.1 Introduction to ROS 1 figure 12

7.2.1.2 Preparing the Workspace

Directorio del espacio de trabajo

La estructura de documentos de ROS no es obligatoria en cada carpeta; se diseña según las necesidades del proyecto.

Acerca del espacio de trabajo

El espacio de trabajo es el lugar donde se gestionan y organizan los documentos de un proyecto ROS. Se puede describir visualmente como un repositorio que contiene los distintos elementos de un proyecto ROS, lo que facilita la gestión del sistema. Es una carpeta dentro de una interfaz gráfica visual. Nuestro propio código de ROS suele residir en el espacio de trabajo. Existen principalmente los siguientes cuatro directorios de primer nivel:

src: espacio fuente; paquetes Catkin de ROS (paquetes de código fuente)

build: espacio de compilación; información de caché e archivos intermedios de Catkin (CMake)

devel: espacio de desarrollo; salida de los archivos objetivo (incluyendo archivos de cabecera, bibliotecas de enlace dinámico, bibliotecas de enlace estático, documentos ejecutables, etc.) y variables de entorno

install: espacio de instalación

El espacio de trabajo de nivel superior (se puede nombrar libremente) y la carpeta src (debe llamarse src) deben crearse manualmente;

Las carpetas build y devel se crean automáticamente con el comando catkin_make;

La carpeta install se crea automáticamente con el comando catkin_make install, que casi no se usa y normalmente no se crea.

Nota: antes de usar catkin_make, hay que volver al nivel superior del espacio de trabajo. No se permite que existan paquetes duplicados dentro del mismo espacio de trabajo; sí se permite que existan paquetes con el mismo nombre en distintos espacios de trabajo.

bash
mkdir -p ~/catkin_ws/src  # create
cd catkin_ws/             # enter the workspace
catkin_make               # build
source devel/setup.bash   # source the workspace environment

Paquetes

Un paquete es una combinación específica de estructura de archivos y carpetas. El código de programa que implementa la misma función suele colocarse en un paquete. Solo CMakeLists.txt y package.xml son [obligatorios]; el resto de rutas dependen de si el paquete las necesita.

Crear un paquete de funciones

bash
cd ~/catkin_ws/src
catkin_create_pkg my_pkg rospy rosmsg roscpp

[rospy], [rosmsg], [roscpp] son bibliotecas de dependencia que se pueden añadir según las necesidades del proyecto, o bien añadir otras; al crearlas no es necesario reconfigurar nada, pero si se olvidan al crear el paquete, deberán configurarse después.

Estructura de archivos

bash
|-- CMakeLists.txt  # (required) build rules for the current package.
|—— package.xml     # (required) package metadata and ROS dependencies.
|—— include directory    # stores C++ header files
|—— config directory     # stores parameter files
|—— launch directory     # stores launch files (.launch or .xml)
|—— meshes directory     # stores robot or simulation 3D models (.sda, .stl, .dae, etc.)
|—— urdf directory       # stores robot model descriptions (.urdf or .xacro)
|—— rviz directory       # rviz files
|—— src directory        # C++ source code
|—— scripts directory    # executable scripts, such as shell scripts (.sh) and Python scripts (.py)
|—— srv directory        # custom services
|—— msg directory        # custom topics
|—— action directory     # custom actions

Introducción a CMakeLists.txt

General

El CMakeLists.txt es originalmente un documento de reglas para el sistema de compilación CMake, y las compilaciones de Catkin siguen en gran medida el estilo de compilación de CMake, aunque añaden algunas definiciones de macros específicas para el proyecto ROS. Por lo tanto, al escribirlo, el CMakeLists.txt de Catkin es básicamente igual al de CMake.

Este documento define directamente el proceso del que depende el paquete, qué objetivos compila, cómo los compila, etc. Por eso CMakeLists.txt es muy importante, ya que especifica las reglas desde el código fuente hasta el archivo objetivo, y las compilaciones de catkin primero localizan el CMakeLists.txt de cada paquete y luego lo compilan y construyen según esas reglas.

Formato

La sintaxis básica de CMakeLists.txt es la misma que la de CMake, en la que Catkin añade un pequeño número de macros; la estructura general es la siguiente:

Un CMakeLists.txt típico de catkin incluye estas partes:

cmake
cmake_minimum_required(VERSION 3.0.2)
project(package_name)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs)
catkin_package()
include_directories(${catkin_INCLUDE_DIRS})
add_executable(node_name src/node_name.cpp)
target_link_libraries(node_name ${catkin_LIBRARIES})

Los paquetes que definen mensajes, servicios o acciones personalizados también usan add_message_files(), add_service_files(), add_action_files() y generate_messages().

Habilitar Boost

Si utiliza C++ y Boost, debe llamar a find_package() sobre Boost e indicar qué componentes de Boost se usan. Por ejemplo, si desea usar Boost thread, escribiría:

find_package

catkin_package()

catkin_package() es una macro de CMake proporcionada por catkin. Es necesaria para asignar información específica de catkin a la compilación del sistema, y se usa para generar archivos pkg-config y de CMake.

Esta función debe llamarse antes de declarar cualquier objeto mediante add_library() o add_executable(). Esta función tiene cinco parámetros opcionales:

INCLUDE_DIRS: exporta las rutas de include

LIBRARIES: biblioteca exportada del proyecto

CATKIN_DEPENDS: otros proyectos de catkin de los que depende el proyecto

DEPENDS: proyecto de CMake que no es de catkin y del que depende el proyecto. Para una mejor comprensión, consulte esta explicación.

CFG_EXTRAS: otras opciones de configuración

El documento completo sobre esta macro se puede encontrar aquí.

Por ejemplo:

bash
catkin_package(
   INCLUDE_DIRS include
   LIBRARIES ${PROJECT_NAME}
   CATKIN_DEPENDS roscpp nodelet
   DEPENDS eigen opencv)

Esto indica que la carpeta "include" dentro de la carpeta del paquete es el punto desde el que se exportan los archivos de cabecera. La variable de entorno de CMake ${PROJECT_NAME} toma el valor previamente pasado a la función project(), que en este caso sería "robot_brain". "roscpp" + "nodelet" es un paquete de software que debe existir para compilar/ejecutar este paquete, y "eigen" + "opencv" es una dependencia a nivel de sistema que debe existir para compilar/ejecutar este paquete.

Rutas de inclusión y bibliotecas

Antes de especificar un objetivo, es necesario indicar la ubicación en la que se pueden encontrar los recursos para dicho objetivo, en particular los archivos de cabecera y las bibliotecas:

Include Paths: dónde se puede encontrar el archivo de cabecera (habitualmente C/C++)

Library Path: dónde se ubican las bibliotecas con las que se enlaza el objetivo

include_directories

link_directories

include_directories()

Los parámetros de include_directories deben ser la llamada al paquete de figuras y cualquier otro directorio que deba incluirse. Si usa catkin y Boost, su include_directories() debería ser el siguiente:

include_directories

El primer parámetro "include" significa que el directorio include/ del paquete también forma parte de la ruta.

link_directories()

Ejemplo:

link_directories(~)

La función link_directories() de CMake se puede usar para añadir rutas de bibliotecas adicionales, pero no se recomienda. Todos los paquetes de catkin y CMake añaden automáticamente la información de enlace a la biblioteca en target_link_libraries() cuando encuentran el paquete.

Consulte la documentación de CMake para ver un ejemplo detallado del uso de target_link_libraries() frente a link_directories().

Objetivos ejecutables

Para especificar el ejecutable que debe compilarse, debemos usar la función de CMake add_executable().

bash
edd executeable

Esto compilará un objetivo ejecutable llamado MyProgram, construido a partir de tres archivos fuente: src/main.cpp, src/some_file.cpp y src/other_file.cpp.

Objetivos de biblioteca

Use add_library() cuando el paquete necesite compilar un objetivo de biblioteca reutilizable. Muchos paquetes de tutorial sencillos solo necesitan ejecutables.

cmake
add_library(${PROJECT_NAME} src/library_file.cpp)

target_link_libraries

Use target_link_libraries() después de add_executable() o add_library() para enlazar el objetivo con catkin y otras bibliotecas necesarias.

cmake
target_link_libraries(node_name ${catkin_LIBRARIES})

Ejemplo:

bash
(foo src/foo.cpp)
Add library (moo src/moo.cpp)
This links fly against libmoo.so

Tenga en cuenta que, en la mayoría de los casos, no es necesario usar link_directories(), ya que la información se introduce automáticamente mediante find_package().

Mensajes, servicios y acciones

Los archivos de mensaje (.msg), de servicio (.srv) y de acción (.action) requieren un preprocesamiento especial antes de que se compilen y usen los paquetes de ROS. El elemento clave de estas macros es la generación de documentos específicos para cada lenguaje de programación, de modo que se puedan usar mensajes, servicios y acciones en el lenguaje de programación elegido. El sistema de compilación se vinculará usando todos los generadores disponibles (por ejemplo, gencpp, genpy, genlisp, etc.).

Se proporcionan tres macros para gestionar mensajes, servicios y acciones por separado:

add_message_files()

add_service_files()

add_action_files()

Estas macros deben ir seguidas de la macro resultante:

generate_messages()

Lea CMake Practice: https://github.com/Akagi201/learning-cmake/blob/master/docs/cmake-practice.pdf si nunca ha tenido contacto con la sintaxis de CMake. Dominar CMake es de gran ayuda para comprender el proyecto ROS.

Introducción a Package.xml

Resumen

El manifiesto del paquete es un archivo XML raíz llamado package.xml que debe incluirse en cualquier paquete compatible. package.xml también es un archivo obligatorio para los paquetes de catkin: es una descripción del paquete que, en una versión anterior de ROS (el sistema de compilación rosbuild), se llamaba "manifest.xml" para describir la información básica del paquete. Si ve algunos proyectos de ROS en internet que contienen manifest.xml, probablemente sean anteriores a la versión Hydro. El package.xml contiene información sobre el nombre, el número de versión, la descripción del contenido, el personal de mantenimiento, las licencias de software, las herramientas de compilación, las dependencias de compilación y las dependencias de ejecución del paquete.

Los archivos package.xml deben incluir la generación de mensajes; la dependencia de ejecución (run_depend) debe incluir el runtime de mensajes.

Formato

Un package.xml típico contiene los metadatos del paquete y las declaraciones de dependencias:

xml
<package format="2">
  <name>package_name</name>
  <version>0.0.0</version>
  <description>Package description</description>
  <maintainer email="user@example.com">Maintainer Name</maintainer>
  <license>BSD</license>
  <buildtool_depend>catkin</buildtool_depend>
  <depend>roscpp</depend>
  <depend>rospy</depend>
  <depend>std_msgs</depend>
</package>

Relaciones de dependencia

La lista de paquetes con las etiquetas mínimas no especifica ninguna dependencia con otros paquetes. El paquete tiene seis tipos de dependencias:

build_depend especifica el paquete necesario para compilar este paquete. Es el caso cuando se requieren archivos de esos paquetes para la compilación. Esto puede incluir el archivo de cabecera en tiempo de compilación, un enlace al archivo de biblioteca de esos paquetes o cualquier otro recurso necesario para la compilación (especialmente cuando los paquetes se localizan mediante find_package() en CMake). En un escenario de compilación cruzada, la relación de dependencia se establece hacia el sistema objetivo.

build_export_depend especifica el paquete necesario para compilar una biblioteca sobre este paquete. Es el caso cuando se incluye esta cabecera en el archivo de cabecera público de este paquete (especialmente cuando se declara en CATKIN_DEPENDS de catkin_package() en CMake).

exec_depend especifica el paquete de software necesario para ejecutar el código de este paquete. Es el caso cuando se depende de la biblioteca compartida de este paquete (especialmente cuando se declara en catkin_package() en CMake).

test_depend especifica únicamente dependencias adicionales para pruebas unitarias. No deben duplicar ninguna dependencia ya mencionada como dependencia de compilación o de ejecución.

buildtool_depend especifica la herramienta del sistema de compilación que este paquete necesita para compilarse. Normalmente el único compilador es catkin. En el escenario de compilación cruzada, las dependencias de herramientas de compilación implementan la arquitectura de compilación.

doc_depend especifica la herramienta de documentación que el paquete necesita para generar la documentación.

Otras etiquetas

: URL con información sobre el paquete, normalmente la página wiki en ros.org.

: autor del paquete

Por ejemplo:

"Website"

Seed.

Preparar el espacio de trabajo con los ejemplos oficiales

Tenga en cuenta que las operaciones de ROS 1 deben realizarse dentro de los contenedores Docker ya existentes.

Después de entrar al contenedor Docker, descargue el ejemplo oficial de ROS (si está disponible):

bash
git clone https://github.com/ros/ros_tutorials.git -b noetic-devel

Este es un caso de ROS 1 basado en C++:

bash
cd ros_tutorials/
mkdir  src
cp roscpp_tutorials/ src/ -r
catkin_make # start building

La compilación se completa de la siguiente manera:

Una vez completada la compilación, es necesario inicializar el espacio de trabajo antes de poder continuar con él:

bash
source devel/setup.bash

Figuras

7.2.1.2 Preparing the Workspace figure 1

7.2.1.2 Preparing the Workspace figure 2

7.2.1.3 Common Commands and Tools

Formas de iniciar un nodo

Archivos launch

Existen al menos dos formas de iniciar un archivo launch con el comando roslaunch:

  1. Iniciar mediante la ruta del paquete de ROS

El formato es el siguiente:

bash
roslaunch package_name launch_file_name
roslaunch pkg_name launchfile_name.launch
  1. Ruta absoluta al archivo launch

El formato es el siguiente:

bash
roslaunch path_to_launchfile

Sea cual sea la forma en que inicie el archivo launch, puede añadir parámetros al final, siendo los siguientes los más comunes.

--screen: facilita la depuración al mostrar la información del nodo de ROS (si existe) directamente en pantalla en lugar de guardarla en un archivo de log

arg:=value: si en el archivo launch se debe proporcionar la variable indicada, el valor se puede asignar de esta manera, por ejemplo:

bash
roslaunch pkg_name launchfile_name model:=urdf/myfile.urdf # the launch file has a `model` argument that must be set

o bien

bash
roslaunch pkg_name launchfile_name model:='$(find urdf_pkg)/urdf/myfile.urdf' # use `find` to provide the path

El comando roslaunch, al ejecutarse, primero detecta si el rosmaster del sistema ya está en ejecución y, en ese caso, usa el rosmaster existente; si no se ha iniciado, primero inicia el rosmaster y luego ejecuta la configuración del archivo launch, permitiendo iniciar varios nodos según la preconfiguración.

Cabe señalar que el archivo launch no necesita compilarse y puede ejecutarse directamente como se describió anteriormente.

rosrun

El gestor de nodos (master) debe estar activo, ya que master se usa para gestionar muchos procesos del sistema, y cada nodo, al iniciarse, se registra ante él y gestiona la comunicación entre nodo y nodo. Una vez iniciado el master, cada nodo se registra a través de él. Escriba el comando en la terminal de Ubuntu:

bash
roscore

Para iniciar un nodo: rosrun + nombre_del_paquete + nombre_del_nodo; el método rosrun solo ejecuta un nodo a la vez.

bash
rosrun [--prefix cmd] [--debug] pkg_name node_name [ARGS]

rosrun buscará un programa ejecutable dentro del paquete, al que se le pueden pasar ARGS opcionales.

Python

Si el código está en Python, puede iniciarlo directamente desde el directorio donde se encuentra el archivo .py, prestando atención a la diferencia entre Python 2 y Python 3.

Iniciar una pequeña tortuga

bash
roscore    # start roscore in the first terminal
rosrun turtlesim turtlesim_node     # start the turtlesim node in the second terminal
rosrun turtlesim turtle_teleop_key  # start keyboard teleoperation in the third terminal

Una vez completado el inicio, puede controlar el movimiento de la pequeña tortuga mediante la entrada del teclado; el cursor debe controlar el movimiento de la tortuga haciendo clic en las teclas [arriba], [abajo], [izquierda], [derecha] bajo el comando [rosrun turtlesim turtle_teleop_key].

Y la terminal de rosrun turtlesim turtlesim_node imprimirá algunos registros de la pequeña tortuga.

bash
[ INFO] [1607648666.226328691]: Starting turtlesim with node name /turtlesim
[ INFO] [1607648666.229275030]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]

Inicio de la segunda tortuga

En la primera terminal, inicie el nodo mediante el archivo launch:

bash
roslaunch turtle_tf turtle_tf_demo.launch

Mantenga activo el nodo de control por teclado anterior.

En este punto, al pulsar las teclas [arriba], [abajo], [izquierda], [derecha] se controla el movimiento de la pequeña tortuga; se puede observar que una tortuga sigue el movimiento de la otra.

Archivos launch

General

Un programa de nodo en ROS suele realizar solo una única función, pero un robot ROS completo normalmente opera de forma simultánea con muchos programas de nodo que colaboran entre sí para realizar tareas complejas, lo que requiere iniciar muchos programas de nodo cuando se activa un robot; esto resulta más engorroso si cada nodo se inicia por separado. El archivo launch y el comando roslaunch permiten activar varios nodos de una sola vez, facilitando la operación "de un solo botón" y la configuración de parámetros enriquecidos.

Formato del documento

El archivo launch es esencialmente un archivo XML, que se puede resaltar en algunos editores; es legible, se le puede añadir o no una cabecera.

¿"0"? >

Al igual que otros archivos en formato XML, los archivos launch se escriben mediante etiquetas (tag); las etiquetas principales son las siguientes:

Archivo de código: 7-2-1-3-common-commands-and-tools-example-01.xml

xml
<launch>                <!-- root tag -->
<node>                  <!-- node and parameters to start -->
<include>               <!-- include another launch file -->
<machine>               <!-- target machine -->
<env-loader>            <!-- set environment variables -->
<param>                 <!-- define a parameter on the parameter server -->
<rosparam>              <!-- load YAML parameters into the parameter server -->
<arg>                   <!-- define an argument -->
<remap>                 <!-- set topic remapping -->
<group>                 <!-- set a group -->
</launch>               <!-- root tag -->
  1. Etiqueta [node]

La etiqueta [node] es la parte central del archivo launch.

Archivo de código: 7-2-1-3-common-commands-and-tools-example-02.xml

xml
<launch>
    <node pkg="package_name" type="executable_file" name="node_name"/>
    <node pkg="another_package" type="another_executable" name="another_node"></node>...
</launch>

donde

pkg es el nombre del paquete del nodo

type es el archivo ejecutable dentro del paquete, que, si está escrito en Python, puede ser .py o, si está escrito en C++, es el nombre del archivo ejecutable resultante tras compilar el archivo fuente.

name es el nombre con el que se inicia el nodo, y cada nodo debe tener su propio nombre único.

Nota: roslaunch no puede garantizar el orden de inicio de los nodos, por lo que todos los nodos del archivo launch deberían ser tan independientes del orden de inicio como sea posible.

Se pueden configurar más parámetros, como se muestra a continuación:

Archivo de código: 7-2-1-3-common-commands-and-tools-example-03.xml

xml
<launch>
    <node
        pkg=""
        type=""
        name=""
        respawn="true"
        required="true"
        launch-prefix="xterm -e"
        output="screen"
        ns="namespace"
    />
</launch>

En el orden anterior,

respawn: si este nodo se cierra, ¿se reinicia automáticamente?

required: si este nodo se cierra, ¿se cierran también todos los demás nodos?

launch-prefix: si se debe abrir una nueva ventana para la ejecución. Por ejemplo, conviene abrir una nueva ventana para el control del nodo cuando se requiere controlar el movimiento del robot mediante ventanas; o cuando el nodo tiene cierta información de salida que no se desea mezclar con la de otros nodos.

output: de forma predeterminada, launch redirige la información del nodo a un archivo de log; se puede mostrar en pantalla configurando este parámetro.

ns: integra el nodo en un espacio de nombres distinto, es decir, añade un prefijo ns antes del nombre del nodo. Para lograr este tipo de operación, el nombre del nodo y el nombre del topic se definen en el archivo fuente del nodo mediante nombres relativos, es decir, sin el símbolo /.

El nombre de un recurso de cómputo se divide en:

  1. Nombre base, por ejemplo: topic

  2. Nombre global, por ejemplo: /A/topic

  3. Nombre relativo, por ejemplo: A/topic

  4. Nombre privado, por ejemplo: ~topic

Existe esta línea de código al publicar o suscribirse.

bash
 ros::init(argc, argv, "publish_node");
 ros::NodeHandle nh;
 ros::Publisher pub = nh.advertise<std_msgs::string>("topic",1000);
  1. Etiqueta [remap]

Suele aparecer como subetiqueta de la etiqueta node para modificar el topic. En muchos archivos de nodo de ROS, es posible que no se haya especificado el topic de recepción o de envío, sino que simplemente se sustituye por input_topic y output_topic, de modo que se usan nombres de topic abstractos en lugar de nombres de topic específicos de la escena.

En resumen, la función de remap es facilitar la aplicación del mismo archivo de nodo en un entorno distinto, remapeando el topic desde el exterior sin necesidad de modificar el archivo fuente.

Los formatos de uso comunes de remap son los siguientes:

Archivo de código: 7-2-1-3-common-commands-and-tools-example-04.xml

xml
<node pkg="some" type="some" name="some">
    <remap from="origin" to="new" />
</node>
  1. Include

Esta etiqueta se usa para añadir otro archivo launch dentro de este archivo launch, de forma similar a un anidamiento de archivos launch. Formato básico:

"Path-to-launch-file"

La ruta de nivel superior del archivo se puede indicar con una ruta específica, pero, en general, para la portabilidad del programa, es preferible dar la ruta del archivo con un comando find:

<include file=$(find package-name)"/>

En el comando anterior, el valor de $(find package-name) equivale a la ruta del paquete correspondiente en esta máquina. Esto permite encontrar la ruta correspondiente incluso si se sustituye el mismo paquete en otra máquina.

A veces, otro nodo introducido por launch puede necesitar un nombre uniforme o un nombre de nodo con características similares, como /my/gps, /my/lidar, /my/imu, o el nodo necesita un prefijo uniforme que facilite la búsqueda. Esto se puede lograr configurando la propiedad ns (namespace), con el siguiente comando:

<include file=$(find package-name) "ns= "my"/>

  1. Etiqueta [arg]

La repetición de parámetros mediante [arg] es posible y se puede modificar fácilmente en varios lugares. Tres métodos comunes:

: declara [arg] pero sin valor. Posteriormente, se puede asignar el valor mediante la línea de comandos o mediante la etiqueta [include].

: valor predeterminado, es decir, un valor fijo.

Asignar el valor mediante la línea de comandos

roslaunch pack_name file_name.launch arg1:=value1 arg2:=value2

  1. Sustitución de variables

Existen dos sustituciones de variables comúnmente usadas en los archivos launch

$(find pkg): por ejemplo, $(find rospy)/manifest.xml. Se recomienda encarecidamente usar rutas basadas en el paquete siempre que sea posible.

$(arg arg_name): establece un valor predeterminado; se usa cuando no se proporciona ningún valor de sustitución

Por ejemplo:

Archivo de código: 7-2-1-3-common-commands-and-tools-manifest.xml

xml
<arg name="gui" default="true" />
<!-- set a default value; use it when no override is provided -->
<param name="use_gui" value="$(arg gui)"/>

Otro ejemplo:

cpp
<node pkg="package_name" type="executable_file" name="node_name" args="$(arg a) $(arg b)" />

Tras configurar este valor, se puede asignar a los parámetros args al iniciar roslaunch

bash
roslaunch package_name file_name.launch a:=1 b:=5
  1. Etiqueta [param]

A diferencia de [arg], [param] es compartido, y su valor no se limita a un simple valor: puede ser un archivo, o incluso una línea de comando.

Formato

Archivo de código: 7-2-1-3-common-commands-and-tools-example-06.xml

bash
<param name="param_name" type="type1" value="val"/>                         # type can be omitted; ROS infers it
<param name="param_name" textfile="$(find pkg)/path/file"/>                 # read file content as a string
<param name="param_name" command="$(find pkg)/exe '$(find pkg)/arg.txt'"/>
Example:
<param name="param" type="yaml" command="cat '$(find pkg)/*.yaml'"/>        # store command output in the parameter

[param] puede definirse en el contexto global, en cuyo caso su nombre es el nombre original, o en un ámbito más reducido, como dentro de un node, en cuyo caso su nombre completo es node/param.

Por ejemplo, en el contexto global, se define de la siguiente manera:

bash
<param name="publish_frequency" type="double" value="10.0" />

Se define de la siguiente manera dentro del ámbito de un node

Archivo de código: 7-2-1-3-common-commands-and-tools-example-07.xml

xml
 <node name="node1" pkg="pkg1" type="exe1">
    <param name="param1" value="False"/>
 </node>

Si se listan los [param] mediante rosparam list, se obtiene

bash
/publish_frequency
/node1/param1   # namespace prefix is added automatically

Nota: aunque se añadió el espacio de nombres al nombre de [param], sigue siendo global.

[rosparam]

[param] solo puede operar sobre un único [param], y solo en tres formas: value, textfile, command, devolviendo el contenido de un [param] individual. [rosparam] permite operaciones por lotes e incluye comandos para configurar parámetros, por ejemplo dump, delete, etc.

load: carga un lote de parámetros desde un archivo YAML con el siguiente formato:

bash
<rosparam command="load" file="$(find rosparam)/example.yaml" />

delete: elimina algún parámetro

bash
<rosparam command="delete" param="my_param" />

Operación de asignación de tipo [param]

bash
<rosparam param="my_param">[1,2,3,4]</rosparam>

O bien...

Archivo de código: 7-2-1-3-common-commands-and-tools-example-08.xml

bash
<rosparam>
a: 1
b: 2
</rosparam>

[rosparam] también se puede colocar dentro de [node], en cuyo caso se aplica al espacio de nombres del nodo.

  1. Group

Si desea la misma configuración para varios nodos, por ejemplo, en el mismo espacio de nombres, remapeando el mismo topic, puede usar [group]. Todas las etiquetas comunes se pueden usar dentro de [group], por ejemplo

Archivo de código: 7-2-1-3-common-commands-and-tools-example-09.xml

xml
<group ns="rosbot">
    <remap from="chatter" to="talker"/>       # applies to following nodes in this group
    <node... />
    <node... >
        <remap from="chatter" to="talker1"/>  # each node can override the remap
    </node>
</group>

Conversión de coordenadas TF

tf es un paquete que permite a los usuarios rastrear múltiples sistemas de coordenadas en cualquier momento. tf mantiene la relación entre las coordenadas mediante una estructura de búfer en tiempo real y permite a los usuarios convertir puntos, vectores, etc., entre dos sistemas de referencia en cualquier instante.

El paquete tf es el que convierte las coordenadas de un punto en un sistema de coordenadas a las coordenadas de otro. El sensor puede ver un sistema de coordenadas, la máquina puede ver un sistema de coordenadas y el obstáculo puede verse como un punto.

Tras activar las dos pequeñas tortugas, realice las siguientes operaciones.

Herramientas comunes de tf

  1. Herramienta view_frames

Es capaz de escuchar todas las transformaciones tf publicadas a través de ROS en el momento actual y dibujar un árbol que indica la conexión entre las coordenadas, generando un archivo llamado frame.pdf y guardándolo en su ubicación local actual.

bash
rosrun tf view_frames
  1. Herramienta rqt_tf_tree

Aunque view_frames puede guardar la relación de coordenadas actual en un archivo sin conexión, no refleja la relación de coordenadas en tiempo real, por lo que es posible actualizar dicha relación en tiempo real con rqt_tf_tree

bash
rosrun rqt_tf_tree rqt_tf_tree
  1. Herramienta tf_echo

Con la herramienta tf_echo, puede ver la relación entre dos sistemas de referencia.

bash
rosrun tf tf_echo <source_frame> <target_frame>

Imprime la transformación de rotación de source_frame a target_frame; por ejemplo:

bash
rosrun tf tf_echo turtle1 turtle2
  1. Static transform publisher

Publica coordenadas estáticas entre dos sistemas de coordenadas, que no cambian de posición relativa. Formato del comando:

bash
static_transform_publisher x y z yaw pitch roll frame_id child_frame_id period_in_ms
static_transform_publisher x y z qx qy qz qw frame_id child_frame_id period_in_ms

Uso en launch:

Archivo de código: 7-2-1-3-common-commands-and-tools-example-10.xml

xml
<launch>
<node pkg="tf" type="static_transform_publisher" name="link1_broadcaster" args="1 0 0 0 0 0 1 link1_parent link1 100" />
</launch>
  1. Plugin roswtf

Un plugin para analizar su configuración actual de tf e intentar identificar problemas comunes.

bash
roswtf

Sistema de coordenadas comunes

Las coordenadas habituales son el frame_id, con map, odom, base_link, base_footprint, base_laser, etc.

Coordenadas del mundo (map)

Las coordenadas Map son un sistema de coordenadas fijo del mundo, con el eje Z apuntando hacia arriba. La postura de la plataforma móvil respecto al sistema Map no debería moverse de forma significativa con el tiempo. Las coordenadas Map no son continuas, lo que significa que la postura de la plataforma móvil en el sistema Map puede presentar discontinuidades en cualquier momento. Normalmente, los módulos de localización, basados en la monitorización de sensores, recalculan constantemente la posición del robot en las coordenadas del mundo, eliminando así las desviaciones, pero pueden producirse saltos cuando llega nueva información de los sensores. Las coordenadas Map resultan útiles como referencia global a largo plazo, pero los saltos las convierten en una mala referencia para sensores locales.

odom

odom es un sistema de coordenadas global que registra la postura de movimiento actual del robot a través de la odometría. La posición de la plataforma móvil en las coordenadas odom es libre de moverse sin ningún límite, lo que impide que las coordenadas odom sirvan como referencia global a largo plazo. Esto sirve para distinguir entre los conceptos de coordenadas y la odometría calculada a partir de encoders (o de visión, etc.). Pero también existe una relación, y la transformación de odom es la relación tf de odom->base_link. Las coordenadas odom y map coinciden al comienzo del movimiento del robot. Sin embargo, con el tiempo, dejan de coincidir, y la desviación es el error acumulado de la odometría. Algunos paquetes de corrección de posición, como amcl, ofrecen una estimación de posición (localization), que se puede obtener mediante la tf de map->base_link, de modo que la diferencia entre esta posición y la posición de la odometría es la diferencia entre las coordenadas odom y map. Si el cálculo de su odometría no tiene errores, la tf map->odom será cero. El sistema de coordenadas odom es útil como referencia local a corto plazo, pero la desviación impide que sea una referencia a largo plazo.

Coordenadas base (base_link)

El sistema de coordenadas base del robot coincide con el centro del robot, que normalmente es el centro de rotación del robot.

base_footprint: el origen es la proyección del origen de base_link sobre el suelo, con cierta diferencia (en los valores de z).

Relación entre coordenadas

En los sistemas robóticos, usamos un árbol para conectar todas las coordenadas, de modo que cada una tiene un padre y coordenadas hijas aleatorias, de la siguiente manera: map -> odom -> base_link; el sistema de coordenadas del mundo es el padre de odom, y odom es el padre de base_link. Aunque, de forma intuitiva, map y odom deberían conectarse ambos a base_link, esto no está permitido, ya que cada sistema solo puede tener un padre.

Permisos del sistema de coordenadas

La conversión de odom a base_link se calcula y publica mediante la fuente de odometría. Sin embargo, el módulo de localización no publica la transformación (transform) de map a base_link. En su lugar, el módulo de localización recibe la transformación de odom a base_link y usa esta información para publicar la transformación de map a odom.

rqt (herramienta QT)

Abra la ventana de la línea de comandos, escriba rosrun rqt y pulse dos veces la tecla Tab para ver qué contiene la herramienta QT en ROS, tal como se muestra en la siguiente figura:

Tomemos como ejemplo a las pequeñas tortugas y hagamos una breve introducción a algunas de las herramientas QT utilizadas:

  1. Visualización con rqt_graph

Abra la ventana de la línea de comandos, escriba el siguiente comando y aparecerá una ventana de diálogo.

rosrun rqt_graph rqt_graph

En las imágenes se aprecia claramente que el nodo /teleop_turtle transmite datos a través del topic /turtle1/cmd_vel hacia el nodo /turtlesim.

/teleop_turtle es el nodo con función de publicador.

/turtlesim es el nodo con función de suscriptor.

Este artículo forma parte de nuestra cobertura especial de Zambia.

  1. rqt_topic: ver topics

rosrun rqt_topic rqt_topic

A través de esta herramienta, podemos ver claramente información en tiempo real sobre los cambios en la pequeña tortuga.

  1. rqt_publisher

rqt_publisher proporciona un plugin de GUI para publicar cualquier mensaje con valores de campo fijos o calculados. Abra la ventana de la línea de comandos, escriba el siguiente comando y aparecerá una ventana de diálogo.

bash
rosrun rqt_publisher rqt_publisher

Haga clic en el cuadro de selección a la derecha de Topic para encontrar el topic /turtle1/cmd_vel que necesitamos, y haga clic a la derecha para añadir el número, tal como se muestra a continuación:

  1. rqt_plot: mapeo de datos

Las instrucciones de referencia son las siguientes:

bash
rosrun rqt_plot rqt_plot
  1. rqt_console: salida de logs

El sistema de logs de ROS tiene la función de generar mensajes de log que se muestran en pantalla, se envían a un topic específico o se almacenan en un archivo de log determinado para facilitar la depuración, el registro, las alertas, etc.

El mensaje de log en ROS se puede dividir en 5 niveles según su gravedad: DEBUG, INFO, WARN, ERROR, FATAL. Mientras el programa pueda ejecutarse, no es necesario prestarle atención, pero la presencia de ERROR y FATAL indica que existen problemas graves en el programa que impiden su ejecución.

bash
rosrun rqt_console rqt_console

La herramienta de salida de logs forma parte del framework de logging de ROS, que muestra información de salida de los nodos; en el mapa podemos ver que la tortuga ha chocado contra la pared.

API común

  1. rqt_reconfigure: configuración dinámica de parámetros

Las instrucciones de referencia son las siguientes:

bash
rosrun rqt_reconfigure rqt_reconfigure

Fuente de la imagen: ROS wiki:

Rviz

rviz es una herramienta gráfica que permite ejecutar de forma visual y sencilla los programas de ROS. También es sencilla de usar.

[Set initial pose], [Set target pose]

La interfaz de rviz se compone principalmente de:

1: área de visualización 3D para mostrar datos de forma visual; actualmente no hay datos disponibles, por lo que aparece en negro.

2: barra de herramientas, que ofrece herramientas como control de perspectiva, configuración de objetivos, ubicación de distribución, etc.

3: muestra una lista de elementos para visualizar el plugin de visualización seleccionado actualmente, donde se pueden configurar las propiedades de cada plugin.

4: configuración de perspectiva, con múltiples vistas de observación disponibles.

5: área de visualización del tiempo, que muestra la hora actual del sistema y el tiempo de ROS.

Añadir visualización

Paso 1: haga clic en el botón [Add]. Aparecerá un cuadro.

Paso 2: añada por tipo de visualización [By display type]; aunque las coordenadas solo se pueden mostrar si se modifica el topic correspondiente; también puede añadir directamente seleccionando el topic [by topic] para que se muestre correctamente.

Paso 3: haga clic en [OK].

Comandos comunes de ROS

Figuras

7.2.1.3 Common Commands and Tools figure 1

7.2.1.3 Common Commands and Tools figure 2

7.2.1.3 Common Commands and Tools figure 3

7.2.1.3 Common Commands and Tools figure 4

7.2.1.3 Common Commands and Tools figure 5

7.2.1.3 Common Commands and Tools figure 6

7.2.1.3 Common Commands and Tools figure 7

7.2.1.3 Common Commands and Tools figure 8

7.2.1.3 Common Commands and Tools figure 9

7.2.1.3 Common Commands and Tools figure 10

7.2.1.3 Common Commands and Tools figure 11

7.2.1.3 Common Commands and Tools figure 12

7.2.1.3 Common Commands and Tools figure 13

7.2.1.4 Publisher

Publicador

El publicador, por definición, actúa como emisor. Este mensaje, que podría ser enviado por la máquina de nivel inferior con información de sensores de la máquina, se empaqueta y se envía al suscriptor del topic; también es posible calcular los datos en la aeronave, empaquetarlos y enviarlos al suscriptor que se suscribe al topic.

Creación del espacio de trabajo y del paquete de topics

Creación del espacio de trabajo

bash
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace

Compilación del espacio de trabajo

bash
cd ~/catkin_ws/
catkin_make

Actualizar las variables de entorno

bash
source devel/setup.bash

Comprobación de las variables de entorno

bash
echo $ROS_PACKAGE_PATH

Crear el paquete

bash
cd ~/catkin_ws/src
catkin_create_pkg learning_topic std_msgs rospy roscpp geometry_msgs turtlesim

Nota aclaratoria: learning_topic es el nombre del paquete de funciones

Compilar el paquete

bash
cd ~/catkin_ws
catkin_make
source ~/catkin_ws/devel/setup.bash

Crear un publicador

Pasos de creación

  1. Inicialización del nodo de ROS

  2. Crear el handle (manejador)

  1. Registrar la información del nodo ante el ROS Master, incluyendo el nombre y el tipo del mensaje que se publica y la longitud de la cola
  1. Crear e inicializar los datos del mensaje

  2. Publicar el mensaje de forma cíclica a una frecuencia determinada

Implementación en C++

  1. Cree un archivo C++ (archivo con sufijo .cpp) en la carpeta src del paquete, llamado turtle_velocity_publisher.cpp (recordatorio básico de uso de vim: 14 con el editor Vim)
bash
touch turtle_velocity_publisher.cpp # create the file
vim turtle_velocity_publisher.cpp # edit the file
  1. Copie el siguiente código de programa en el archivo turtle_velocity_publisher.cpp

Archivo de código: 7-2-1-4-publisher-turtle_velocity_publisher.cpp

cpp
/*Create a turtlesim velocity publisher.*/
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
int main(int argc, char **argv){

    ros::init(argc, argv, "turtle_velocity_publisher");//Initialize the ROS node.

    ros::NodeHandle n;//Create a node handle.

    //Create a publisher for /turtle1/cmd_vel with geometry_msgs::Twist messages and queue size 10.
    ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);

    ros::Rate loop_rate(10);//Set the loop rate.

    while (ros::ok()){
            //Initialize a message with the same type as the publisher.
        geometry_msgs::Twist turtle_vel_msg;
        turtle_vel_msg.linear.x = 0.8;
        turtle_vel_msg.angular.z = 0.6;

        turtle_vel_pub.publish(turtle_vel_msg);// Publish the velocity message.

        //Print the published velocity.
        ROS_INFO("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]", turtle_vel_msg.linear.x, turtle_vel_msg.angular.z);

        loop_rate.sleep();//Sleep according to the loop rate.
    }
    return 0;
}

El directorio del proyecto editado queda estructurado de la siguiente manera:

bash
catkin_ws/
├── CMakeLists.txt
└── src/
    ├── CMakeLists.txt
    └── learning_topic/
        ├── CMakeLists.txt
        ├── package.xml
        └── src/
            └── turtle_velocity_publisher.cpp
  1. Diagrama de flujo del programa, correspondiente al contenido de 1.3.1

  2. En catkin_ws/src/learning_topic/CMakeLists.txt, dentro del área de compilación, añada lo siguiente:

(recordatorio básico de uso de vim: 14 con el editor Vim)

Archivo de código: 7-2-1-4-publisher-example-02.cmake

cmake
add_executable(turtle_velocity_publisher src/turtle_velocity_publisher.cpp)
target_link_libraries(turtle_velocity_publisher ${catkin_LIBRARIES})

Nota: modifique el CMakeLists.txt en la ruta correcta

  1. Recompile el código en el directorio del espacio de trabajo
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program

Procedimiento operativo

Abra la primera terminal y ejecute roscore:

bash
roscore

Ejecute el nodo de la pequeña tortuga

bash
rosrun turtlesim turtlesim_node

Ejecute el lanzador, que sigue enviando velocidad a la tortuga.

bash
rosrun learning_topic turtle_velocity_publisher
  1. Resultado esperado

  2. Descripción del funcionamiento del procedimiento

Al listar los topics en la terminal, encontrará el topic /turtle1/cmd_vel.

Lo comprobaremos con rostopic info /turtle1/cmd_vel

Esto significa que la tortuga es suscriptora del topic de velocidad /turtle1/cmd_vel, de modo que el publicador sigue enviando datos de velocidad, y cuando la tortuga los recibe, comienza a moverse a esa velocidad.

Implementación en Python

  1. En el directorio del paquete, cree una nueva carpeta scripts, y luego un nuevo archivo Python (con sufijo .py) dentro de la carpeta scripts, llamado turtle_velocity_publisher.py

  2. Copie el siguiente código de programa en el archivo turtle_velocity_publisher.py

Archivo de código: 7-2-1-4-publisher-suffix.py

python
#!/usr/bin/env python3

import rospy
from geometry_msgs.msg import Twist

def turtle_velocity_publisher():

    rospy.init_node('turtle_velocity_publisher', anonymous=True) # Initialize the ROS node.

    # Create a turtlesim velocity publisher on /turtle1/cmd_vel. The message type is geometry_msgs/Twist and the queue size is 8.
    turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=8)


    rate = rospy.Rate(10) # Set the loop rate.

    while not rospy.is_shutdown():
        # Initialize a geometry_msgs::Twist message.
        turtle_vel_msg = Twist()
        turtle_vel_msg.linear.x = 0.8
        turtle_vel_msg.angular.z = 0.6

        # Publish the message.
        turtle_vel_pub.publish(turtle_vel_msg)
        rospy.loginfo("linear is:%0.2f m/s, angular is:%0.2f rad/s",
                turtle_vel_msg.linear.x, turtle_vel_msg.angular.z)


        rate.sleep()# Sleep according to the loop rate.

if __name__ == '__main__':
    try:
        turtle_velocity_publisher()
    except rospy.ROSInterruptException:
        pass
  1. Diagrama de flujo del proyecto

  2. Procedimiento operativo

Abra la primera terminal y ejecute roscore

bash
roscore

Ejecute el nodo de la pequeña tortuga

bash
rosrun turtlesim turtlesim_node

Ejecute el lanzador, que sigue enviando velocidad a la tortuga.

bash
rosrun learning_topic turtle_velocity_publisher.py

Nota: antes de ejecutarlo, es necesario añadir permisos de ejecución a turtle_velocity_publisher.py, abriendo la terminal en la carpeta de turtle_velocity_publisher.py.

bash
sudo chmod a+x turtle_velocity_publisher.py

Todos los archivos de Python necesitan que se les añadan permisos de ejecución; de lo contrario, se producirá un error.

Figuras

7.2.1.4 Publisher figure 1

7.2.1.4 Publisher figure 2

7.2.1.4 Publisher figure 3

7.2.1.4 Publisher figure 4

7.2.1.4 Publisher figure 5

7.2.1.4 Publisher figure 6

7.2.1.5 Subscriber

Suscriptores

El suscriptor recibe los datos publicados por el publicador y luego entra en su función de callback, donde se procesan los datos recibidos. El contenido central es una función de callback, y cada suscriptor se suscribe a un topic.

Crear un suscriptor

Pasos de creación

  1. Inicialización del nodo de ROS

  2. Crear el handle (manejador)

  3. Suscripción a topics

  4. Recorrer en bucle los mensajes del topic y devolverlos a la función de callback

  1. Finalizar el procesamiento del mensaje dentro de una función de callback

El espacio de trabajo de este capítulo continúa a partir del espacio de trabajo creado en la sección IV.

Implementación en C++

  1. Cree un nuevo archivo C++ en el directorio del tutorial de "publisher", dentro de la carpeta src del paquete creado, llamado turtle_pose_subscriber.cpp

  2. Copie el siguiente código de programa en el archivo turtle_pose_subscriber.cpp

Archivo de código: 7-2-1-5-subscriber-subscriber.cpp

cpp
/*Create a subscriber for the current turtlesim pose.*/
#include <ros/ros.h>
#include "turtlesim/Pose.h"
// The callback runs when a subscribed message is received.
void turtle_poseCallback(const turtlesim::Pose::ConstPtr& msg){
    // Print the received message.
    ROS_INFO("Turtle pose: x:%0.3f, y:%0.3f", msg->x, msg->y);
}

int main(int argc, char **argv){

    ros::init(argc, argv, "turtle_pose_subscriber");// Initialize the ROS node.

    ros::NodeHandle n;//Create a node handle.

    // Create a subscriber for /turtle1/pose and register poseCallback.
    ros::Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, turtle_poseCallback);

    ros::spin(); // Wait for callbacks.

    return 0;
}
bash
catkin_ws/
├── CMakeLists.txt
└── src/
    ├── CMakeLists.txt
    └── learning_topic/
        ├── CMakeLists.txt
        ├── package.xml
        └── src/
            └── turtle_velocity_publisher.cpp
            └── turtle_pose_subscriber.cpp
  1. Diagrama de flujo del programa, correspondiente al contenido de 5.2.1

  2. En catkin_ws/src/learning_topic/CMakeLists.txt, dentro del área de compilación, añada lo siguiente:

(recordatorio básico de uso de vim: 14 con el editor Vim)

bash
Add executeable (turtle pose subscriber src/turtle_pose_subscriber.cpp)
{\cHFFFFFF}{\cH00FFFF}
  1. Código compilado en el directorio del espacio de trabajo
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program

Procedimiento operativo

Abra la primera terminal y ejecute roscore

bash
roscore

En la segunda terminal, ejecute el nodo de la tortuga.

bash
rosrun turtlesim turtlesim_node

La tercera terminal ejecuta el nodo suscriptor y sigue recibiendo los datos de la posición de la tortuga

bash
rosrun learning_topic turtle_pose_subscriber
  1. Descripción del funcionamiento del procedimiento

Después de ejecutar el nodo de la pequeña tortuga, esta sigue enviando sus mensajes de posición, y el topic es,

/turtle1/pose

Y al ejecutarlo, recibe los mensajes de datos enviados por la tortuga, y luego los imprime en la función de callback.

Implementación en Python

  1. En el directorio del paquete, cree una nueva carpeta scripts y luego un nuevo archivo Python (con sufijo .py) dentro de la carpeta scripts, llamado turtle_pose_subscriber.py

  2. Copie el siguiente código de programa en turtle_pose_subscriber.py

Archivo de código: 7-2-1-5-subscriber-suffix.py

python
#!/usr/bin/env python3

import rospy
from turtlesim.msg import Pose

def poseCallback(msg):
    rospy.loginfo("Turtle pose: x:%0.3f, y:%0.3f", msg.x, msg.y)

def turtle_pose_subscriber():

    rospy.init_node('turtle_pose_subscriber', anonymous=True)# Initialize the ROS node.

    # Create a subscriber for /turtle1/pose and register poseCallback.
    rospy.Subscriber("/turtle1/pose", Pose, poseCallback)


    rospy.spin()# Wait for callbacks.

if __name__ == '__main__':
    turtle_pose_subscriber()
  1. Diagrama de flujo del proyecto

  2. Procedimiento operativo

Ejecute roscore

bash
roscore

Ejecute el nodo de la pequeña tortuga

bash
rosrun turtlesim turtlesim_node

Ejecute el suscriptor y siga recibiendo los datos de la posición de la tortuga

bash
rosrun learning_topic turtle_pose_subscriber.py

Figuras

7.2.1.5 Subscriber figure 1

7.2.1.5 Subscriber figure 2

7.2.1.5 Subscriber figure 3

7.2.1.5 Subscriber figure 4

7.2.1.5 Subscriber figure 5

7.2.1.6 Custom Topic Messages and Usage

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

Esta sección crea y usa un mensaje de topic personalizado llamado Information.msg. El ejemplo continúa con el paquete learning_topic creado anteriormente.

Crear el archivo del mensaje

Cree el directorio msg y defina el mensaje personalizado:

bash
cd ~/catkin_ws/src/learning_topic
mkdir -p msg
vim msg/Information.msg

Archivo de código: 7-2-1-6-custom-topic-messages-and-usage-Information.msg

msg
string company
string city

Actualizar package.xml

Añada las dependencias de generación/runtime de mensajes a package.xml:

xml
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

Actualizar CMakeLists.txt

Añada la generación de mensajes a CMakeLists.txt:

cmake
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

add_message_files(
  FILES
  Information.msg
)

generate_messages(
  DEPENDENCIES
  std_msgs
)

catkin_package(
  CATKIN_DEPENDS message_runtime
)

Compile el espacio de trabajo:

bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash

Publicador y suscriptor en C++

Cree los siguientes archivos dentro de ~/catkin_ws/src/learning_topic/src.

Archivo de código: 7-2-1-6-custom-topic-messages-and-usage-Information_publisher.cpp

cpp
/**
 * Publish /company_info with the custom learning_topic::Information message type.
 */
#include <ros/ros.h>
#include "learning_topic/Information.h"

int main(int argc, char **argv)
{
    ros::init(argc, argv, "company_information_publisher");
    ros::NodeHandle nh;

    ros::Publisher info_pub = nh.advertise<learning_topic::Information>("/company_info", 10);
    ros::Rate loop_rate(1);

    while (ros::ok())
    {
        learning_topic::Information info_msg;
        info_msg.company = "Seeed";
        info_msg.city = "Shenzhen";

        info_pub.publish(info_msg);
        ROS_INFO("Information: company:%s city:%s", info_msg.company.c_str(), info_msg.city.c_str());
        loop_rate.sleep();
    }
    return 0;
}

Archivo de código: 7-2-1-6-custom-topic-messages-and-usage-Information_subscriber.cpp

cpp
/**
 * Subscribe to /company_info with the custom learning_topic::Information message type.
 */
#include <ros/ros.h>
#include "learning_topic/Information.h"

void companyInfoCallback(const learning_topic::Information::ConstPtr& msg)
{
    ROS_INFO("Company: %s, city: %s", msg->company.c_str(), msg->city.c_str());
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "company_information_subscriber");
    ros::NodeHandle nh;
    ros::Subscriber sub = nh.subscribe("/company_info", 10, companyInfoCallback);
    ros::spin();
    return 0;
}

Añada los ejecutables a CMakeLists.txt:

cmake
add_executable(Information_publisher src/Information_publisher.cpp)
target_link_libraries(Information_publisher ${catkin_LIBRARIES})
add_dependencies(Information_publisher ${PROJECT_NAME}_generate_messages_cpp)

add_executable(Information_subscriber src/Information_subscriber.cpp)
target_link_libraries(Information_subscriber ${catkin_LIBRARIES})
add_dependencies(Information_subscriber ${PROJECT_NAME}_generate_messages_cpp)

Compile y ejecute:

bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash
roscore
rosrun learning_topic Information_publisher
rosrun learning_topic Information_subscriber

Publicador y suscriptor en Python

Cree los siguientes archivos dentro de ~/catkin_ws/src/learning_topic/scripts, y luego hágalos ejecutables.

Archivo de código: 7-2-1-6-custom-topic-messages-and-usage-Information_publisher.py

python
#!/usr/bin/env python3
import rospy
from learning_topic.msg import Information

def information_publisher():
    rospy.init_node('information_publisher', anonymous=True)
    info_pub = rospy.Publisher('/company_info', Information, queue_size=10)
    rate = rospy.Rate(1)

    while not rospy.is_shutdown():
        info_msg = Information()
        info_msg.company = 'Seeed'
        info_msg.city = 'Shenzhen'
        info_pub.publish(info_msg)
        rospy.loginfo('Information: company:%s city:%s', info_msg.company, info_msg.city)
        rate.sleep()

if __name__ == '__main__':
    information_publisher()

Archivo de código: 7-2-1-6-custom-topic-messages-and-usage-Information_subscriber.py

python
#!/usr/bin/env python3
import rospy
from learning_topic.msg import Information

def company_info_callback(msg):
    rospy.loginfo('Company: %s, city: %s', msg.company, msg.city)

def information_subscriber():
    rospy.init_node('information_subscriber', anonymous=True)
    rospy.Subscriber('/company_info', Information, company_info_callback)
    rospy.spin()

if __name__ == '__main__':
    information_subscriber()
bash
chmod +x scripts/Information_publisher.py scripts/Information_subscriber.py
roscore
rosrun learning_topic Information_publisher.py
rosrun learning_topic Information_subscriber.py

Figuras

7.2.1.6 Custom Topic Messages and Usage figure 1

7.2.1.6 Custom Topic Messages and Usage figure 2

7.2.1.6 Custom Topic Messages and Usage figure 3

7.2.1.6 Custom Topic Messages and Usage figure 4

7.2.1.6 Custom Topic Messages and Usage figure 5

7.2.1.6 Custom Topic Messages and Usage figure 6

7.2.1.6 Custom Topic Messages and Usage figure 7

7.2.1.6 Custom Topic Messages and Usage figure 8

7.2.1.7 Client

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

Además de la comunicación por topic, existe la comunicación por servicio. Un cliente envía una petición, y un servidor devuelve una respuesta. Esta sección se centra en el cliente, mostrando cómo implementar uno en C++ y en Python.

Trabajo preparatorio

Continúe usando el paquete learning_server creado en esta sección.

Creación del paquete

  1. Cambie a ~/catkin_ws/src y ejecute en la terminal:
bash
catkin_create_pkg learning_server std_msgs rospy roscpp geometry_msgs turtlesim

Cambie al directorio ~ para ejecutar la compilación:

bash
catkin_make

Implementación en C++

Pasos de implementación

  1. Inicialización del nodo de ROS

  2. Crear el handle (manejador)

  1. Crear una instancia de cliente
  1. Inicializar y publicar los datos de la petición del servicio

  2. Respuesta recibida del servidor

Cree a_new_turtle.cpp dentro de ~/catkin_ws/src/learning_server/src y pegue el siguiente código.

a_new_turtle.cpp

Archivo de código: 7-2-1-7-client-new.cpp

cpp
/**
This example calls the turtlesim /spawn service to create a new turtle at the specified position.
*/

#include <ros/ros.h>
#include <turtlesim/Spawn.h>

int main(int argc, char** argv)
{

    ros::init(argc, argv, "a_new_turtle");// Initialize the ROS node.

    ros::NodeHandle node;

    ros::service::waitForService("/spawn"); // Wait for the /spawn service.

    ros::ServiceClient new_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");//Create a service client for /spawn.

    // Initialize the turtlesim::Spawn request.
    turtlesim::Spawn new_turtle_srv;
    new_turtle_srv.request.x = 6.0;
    new_turtle_srv.request.y = 8.0;
    new_turtle_srv.request.name = "turtle2";

    // Call the service with x/y position and name parameters.
    ROS_INFO("Call service to create a new turtle name is %s,at the x:%.1f,y:%.1f", new_turtle_srv.request.name.c_str(),
        new_turtle_srv.request.x,
        new_turtle_srv.request.y);

    new_turtle.call(new_turtle_srv);


    ROS_INFO("Spawn turtle successfully [name:%s]", new_turtle_srv.response.name.c_str());// Display the service call result.

    return 0;
};
  1. Diagrama de flujo de procedimientos
  1. En la configuración de CMakeLists.txt, dentro del área de compilación, añada lo siguiente:

(recordatorio básico de uso de vim: 14 con el editor Vim)

Archivo de código: 7-2-1-7-client-example-02.cmake

cmake
add_executable(a_new_turtle src/a_new_turtle.cpp)
target_link_libraries(a_new_turtle ${catkin_LIBRARIES})
  1. Recompile el código en el directorio del espacio de trabajo
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program
  1. Abra tres terminales y ejecute los programas
bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_server a_new_turtle
  1. Resultado esperado

  2. Proceso

Una vez activado el nodo de la pequeña tortuga, al volver a ejecutar a_new_turtle aparece otra tortuga en la imagen, porque el nodo de la pequeña tortuga proporciona el servicio /spawn, que crea otra tortuga llamada turtle2, algo que se puede comprobar mediante el comando rosservice list, tal como se muestra a continuación.

Los parámetros que requiere este servicio se pueden consultar mediante rosservice info /spawn, tal como se muestra en la siguiente figura.

Se puede observar que se necesitan cuatro parámetros: x, y, theta, name, que se inicializan en a_new_turtle.cpp

Archivo de código: 7-2-1-7-client-turtle.cpp

cpp
srv.request.x = 6.0;
srv.request.y = 8.0;
srv.request.name = "turtle2";

Nota: theta no se asigna, por lo que su valor predeterminado es 0

Implementación en Python

Cree scripts/a_new_turtle.py dentro de ~/catkin_ws/src/learning_server y pegue el siguiente código.

a_new_turtle.py

Archivo de código: 7-2-1-7-client-a_new_turtle.py

python
#!/usr/bin/env python3

import rospy
from turtlesim.srv import Spawn


def turtle_spawn():
    rospy.init_node('new_turtle')
    rospy.wait_for_service('/spawn')

    try:
        spawn_client = rospy.ServiceProxy('/spawn', Spawn)
        response = spawn_client(2.0, 2.0, 0.0, 'turtle2')
        return response.name
    except rospy.ServiceException as exc:
        rospy.logerr('Failed to call /spawn: %s', exc)
        return None


if __name__ == '__main__':
    name = turtle_spawn()
    if name:
        rospy.loginfo('Created a new turtle named %s.', name)
  1. Diagrama de flujo de procedimientos

  2. Abra tres terminales y ejecute los programas

bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_server a_new_turtle.py
  1. Los efectos del funcionamiento y la descripción del procedimiento son consistentes con los resultados obtenidos en C++; aquí se muestran los parámetros de cómo Python proporciona el servicio,

response = spawn_client(2.0, 2.0, 0.0, "turtle2")

Los parámetros correspondientes son x, y, theta, name.

Figuras

7.2.1.7 Client figure 1

7.2.1.7 Client figure 2

7.2.1.7 Client figure 3

7.2.1.7 Client figure 4

7.2.1.7 Client figure 5

7.2.1.7 Client figure 6

7.2.1.7 Client figure 7

7.2.1.8 Server

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

Cuando hablamos de que el cliente hace una petición y luego el servicio responde, hablamos de la prestación del servicio.

Continúe usando el paquete learning_server creado en esta sección.

Implementación en C++

Pasos de implementación

  1. Inicialización del nodo de ROS

  2. Ejemplos de creación del servidor

  1. Espera en bucle de las peticiones del servicio, entrando en una función de callback

  2. Completa el procesamiento funcional del servicio dentro de la función de callback y proporciona la retroalimentación de los datos de respuesta

Cree turtle_vel_command_server.cpp dentro de ~/catkin_ws/src/learning_server/src y pegue el siguiente código.

Archivo de código: 7-2-1-8-server-new.cpp

cpp
/**
This example provides /turtle_vel_command with the std_srvs/Trigger service type.
*/
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <std_srvs/Trigger.h>

ros::Publisher turtle_vel_pub;
bool pubvel = false;

// Service callback: req is the request and res is the response.
bool pubvelCallback(std_srvs::Trigger::Request  &req,
                    std_srvs::Trigger::Response &res)
{
    pubvel = !pubvel;

        ROS_INFO("Do you want to publish the vel?: [%s]", pubvel==true?"Yes":"No");// Print the client request.

    // Set response data.
    res.success = true;
    res.message = "The status is changed!";

    return true;
}

int main(int argc, char **argv)
{

    ros::init(argc, argv, "turtle_vel_command_server");


    ros::NodeHandle n;

    // Create the /turtle_vel_command server and register pubvelCallback.
    ros::ServiceServer command_service = n.advertiseService("/turtle_vel_command", pubvelCallback);

    // Create a publisher for /turtle1/cmd_vel. The message type is geometry_msgs::Twist and the queue size is 8.
    turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 8);

    ros::Rate loop_rate(10);// Set the loop rate.

    while(ros::ok())
    {

        ros::spinOnce();// Process callbacks once.

        // Publish turtle velocity commands when pubvel is true.
        if(pubvel)
        {
            geometry_msgs::Twist vel_msg;
            vel_msg.linear.x = 0.6;
            vel_msg.angular.z = 0.8;
            turtle_vel_pub.publish(vel_msg);
        }

        loop_rate.sleep();//Sleep according to the loop rate.
    }

    return 0;
}
  1. Diagrama de flujo de procedimientos
  1. Configure en CMakeLists.txt, dentro del área de compilación, añada lo siguiente:

Archivo de código: 7-2-1-8-server-example-02.cmake

cmake
add_executable(turtle_vel_command_server src/turtle_vel_command_server.cpp)
target_link_libraries(turtle_vel_command_server ${catkin_LIBRARIES})
  1. Código compilado en el directorio del espacio de trabajo
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program
  1. Inicio de cuatro programas en terminal
bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_server turtle_vel_command_server
rosservice call /turtle_vel_command
  1. Resultado esperado

  2. Proceso

Primero, al ejecutar el nodo de la pequeña tortuga, puede escribir rosservice list en la terminal para ver cuál es el servicio actual, como se muestra a continuación:

Y luego ejecutamos el programa turtle_vel_command_server, y al escribir rosservice list, encontramos un turtle_vel_command_server adicional, tal como se muestra en la siguiente figura.

Y luego llamamos a este servicio escribiéndolo en la terminal, y vemos a la pequeña tortuga moviéndose en círculos continuamente; si se llama de nuevo, se detiene. Esto se debe a que, en el callback del servicio, invertimos el valor de pubvel y luego lo devolvemos como retroalimentación; la función principal evalúa el valor de pubvel, y si es True, envía comandos de velocidad, y si es False, no lo hace.

Implementación en Python

Cree scripts/turtle_vel_command_server.py dentro de ~/catkin_ws/src/learning_server y pegue el siguiente código.

turtle_vel_command_server.py

Archivo de código: 7-2-1-8-server-turtle_vel_command_server.py

python
#!/usr/bin/env python3

import threading

import rospy
from geometry_msgs.msg import Twist
from std_srvs.srv import Trigger, TriggerResponse

pubvel = False
turtle_vel_pub = None


def publish_velocity_loop():
    rate = rospy.Rate(10)
    while not rospy.is_shutdown():
        if pubvel:
            vel_msg = Twist()
            vel_msg.linear.x = 0.6
            vel_msg.angular.z = 0.8
            turtle_vel_pub.publish(vel_msg)
        rate.sleep()


def pubvel_callback(req):
    global pubvel
    pubvel = not pubvel
    rospy.loginfo('Publish turtle velocity: %s', pubvel)
    return TriggerResponse(success=True, message='Velocity publishing toggled.')


def turtle_pubvel_command_server():
    global turtle_vel_pub
    rospy.init_node('turtle_vel_command_server')
    turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=8)
    rospy.Service('/turtle_vel_command', Trigger, pubvel_callback)
    threading.Thread(target=publish_velocity_loop, daemon=True).start()
    rospy.loginfo('Ready to receive /turtle_vel_command requests.')
    rospy.spin()


if __name__ == '__main__':
    turtle_pubvel_command_server()
  1. Diagrama de flujo de procedimientos
  1. Abra tres terminales y ejecute los programas:
bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_server turtle_vel_command_server.py
  1. Los efectos del funcionamiento del procedimiento y la descripción del mismo son consistentes con los obtenidos en C++.

Figuras

7.2.1.8 Server figure 1

7.2.1.8 Server figure 2

7.2.1.8 Server figure 3

7.2.1.8 Server figure 4

7.2.1.8 Server figure 5

7.2.1.8 Server figure 6

7.2.1.8 Server figure 7

7.2.1.9 Custom Service Messages and Usage

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

Esta sección define un servicio personalizado llamado IntPlus.srv e implementa un par servidor/cliente en C++ y Python.

Crear el archivo del servicio

bash
cd ~/catkin_ws/src/learning_server
mkdir -p srv
vim srv/IntPlus.srv

Archivo de código: 7-2-1-9-custom-service-messages-and-usage-IntPlus.srv

srv
int64 a
int64 b
---
int64 result

Actualizar package.xml y CMakeLists.txt

Añada estas dependencias a package.xml:

xml
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

Añada la generación de servicios a CMakeLists.txt:

cmake
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

add_service_files(
  FILES
  IntPlus.srv
)

generate_messages(
  DEPENDENCIES
  std_msgs
)

catkin_package(
  CATKIN_DEPENDS message_runtime
)

Compile el espacio de trabajo:

bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash

Servidor y cliente en C++

Archivo de código: 7-2-1-9-custom-service-messages-and-usage-IntPlus_server.cpp

cpp
#include <ros/ros.h>
#include "learning_server/IntPlus.h"

bool intPlusCallback(learning_server::IntPlus::Request &req,
                     learning_server::IntPlus::Response &res)
{
    ROS_INFO("number 1 is:%ld, number 2 is:%ld", req.a, req.b);
    res.result = req.a + req.b;
    return true;
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "IntPlus_server");
    ros::NodeHandle nh;
    ros::ServiceServer service = nh.advertiseService("/Two_Int_Plus", intPlusCallback);
    ROS_INFO("Ready to calculate two integers.");
    ros::spin();
    return 0;
}

Archivo de código: 7-2-1-9-custom-service-messages-and-usage-IntPlus_client.cpp

cpp
#include <ros/ros.h>
#include "learning_server/IntPlus.h"

int main(int argc, char **argv)
{
    ros::init(argc, argv, "IntPlus_client");
    ros::NodeHandle nh;
    ros::service::waitForService("/Two_Int_Plus");
    ros::ServiceClient client = nh.serviceClient<learning_server::IntPlus>("/Two_Int_Plus");

    learning_server::IntPlus srv;
    srv.request.a = 8;
    srv.request.b = 6;

    if (client.call(srv)) {
        ROS_INFO("Result: %ld", srv.response.result);
    } else {
        ROS_ERROR("Failed to call /Two_Int_Plus");
    }
    return 0;
}

Añada los ejecutables a CMakeLists.txt:

cmake
add_executable(IntPlus_server src/IntPlus_server.cpp)
target_link_libraries(IntPlus_server ${catkin_LIBRARIES})
add_dependencies(IntPlus_server ${PROJECT_NAME}_generate_messages_cpp)

add_executable(IntPlus_client src/IntPlus_client.cpp)
target_link_libraries(IntPlus_client ${catkin_LIBRARIES})
add_dependencies(IntPlus_client ${PROJECT_NAME}_generate_messages_cpp)

Ejecute el ejemplo:

bash
roscore
rosrun learning_server IntPlus_server
rosrun learning_server IntPlus_client

También puede llamar directamente al servicio:

bash
rosservice call /Two_Int_Plus 5 6

Servidor y cliente en Python

Archivo de código: 7-2-1-9-custom-service-messages-and-usage-IntPlus_server.py

python
#!/usr/bin/env python3
import rospy
from learning_server.srv import IntPlus, IntPlusResponse

def int_plus_callback(req):
    rospy.loginfo('Ints: a:%d b:%d', req.a, req.b)
    return IntPlusResponse(req.a + req.b)

if __name__ == '__main__':
    rospy.init_node('IntPlus_server')
    rospy.Service('/Two_Int_Plus', IntPlus, int_plus_callback)
    rospy.loginfo('Ready to calculate two integers.')
    rospy.spin()

Archivo de código: 7-2-1-9-custom-service-messages-and-usage-IntPlus_client.py

python
#!/usr/bin/env python3
import rospy
from learning_server.srv import IntPlus

if __name__ == '__main__':
    rospy.init_node('IntPlus_client')
    rospy.wait_for_service('/Two_Int_Plus')
    plus_client = rospy.ServiceProxy('/Two_Int_Plus', IntPlus)
    response = plus_client(22, 20)
    rospy.loginfo('Result: %d', response.result)
bash
chmod +x scripts/IntPlus_server.py scripts/IntPlus_client.py
roscore
rosrun learning_server IntPlus_server.py
rosrun learning_server IntPlus_client.py

Figuras

7.2.1.9 Custom Service Messages and Usage figure 1

7.2.1.9 Custom Service Messages and Usage figure 2

7.2.1.9 Custom Service Messages and Usage figure 3

7.2.1.9 Custom Service Messages and Usage figure 4

7.2.1.9 Custom Service Messages and Usage figure 5

7.2.1.9 Custom Service Messages and Usage figure 6

7.2.1.9 Custom Service Messages and Usage figure 7

7.2.1.10 Publishing and Listening with TF

Run the commands in the ROS 1 Noetic Docker container described in 7.2.1.1 Introduction to ROS 1.

Paquete tf

tf es un paquete que permite a los usuarios rastrear múltiples sistemas de coordenadas a lo largo del tiempo, usando estructuras de datos en árbol que ayudan a los desarrolladores a transformar coordenadas en cualquier momento, completar puntos entre coordenadas, vectores, etc., usando búferes de tiempo y manteniendo las relaciones de alternancia entre múltiples coordenadas.

Pasos de uso

  1. Interceptar la transformación tf

Recibe todas las coordenadas publicadas en el sistema de búfer, transforma los datos y, a partir de ahí, busca las coordenadas necesarias.

  1. Difundir la transformación tf

(b) Difunde la alternancia de coordenadas entre los sistemas de coordenadas. Puede haber difusiones modificadas de tf en varias partes del sistema. Cada difusión se puede insertar directamente en el árbol de tf sin necesidad de sincronización adicional.

Implementación de la difusión y escucha de coordenadas tf mediante programación

Crear y compilar el paquete

bash
cd ~/catkin_ws/src
catkin_create_pkg learning_tf rospy roscpp turtlesim tf
cd..
catkin_make

Cómo implementar un tf broadcaster

  1. Definición del TF broadcaster (Transform Broadcaster);

  2. Inicialización de los datos de tf y creación de las coordenadas;

  1. Publicación de la transformación de coordenadas (sendTransform);

Cómo implementar un tf listener

  1. Definición del TF listener (TransformListener);
  1. Búsqueda de coordenadas (waitForTransform, lookupTransform)

Implementación en C++ del tf broadcaster

  1. Cree un archivo C++ (archivo con sufijo .cpp) en la carpeta src del paquete

  2. Copie el siguiente código de programa en el archivo turtle_tf_broadcaster.cpp

Archivo de código: 7-2-1-10-publishing-and-listening-with-tf-with.cpp

cpp
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>

std::string turtle_name;

void poseCallback(const turtlesim::PoseConstPtr& msg)
{

    static tf::TransformBroadcaster br;// Create a TF broadcaster.

    // Initialize TF data.
    tf::Transform transform;
    transform.setOrigin( tf::Vector3(msg->x, msg->y, 0.0) );//Set xyz coordinates.
    tf::Quaternion q;
    q.setRPY(0, 0, msg->theta);//Set Euler angles for x, y, and z rotation.
    transform.setRotation(q);

    br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));// Broadcast TF data between world and the turtle frame.
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "turtle_world_tf_broadcaster");// Initialize the ROS node.

    if (argc != 2)
    {
        ROS_ERROR("Missing a parameter as the name of the turtle!");
        return -1;
    }

    turtle_name = argv[1];// Use the input argument as the turtle name.

    // Subscribe to the turtle pose topic.
    ros::NodeHandle node;
    ros::Subscriber sub = node.subscribe(turtle_name+"/pose", 10, &poseCallback);

        // Wait for callbacks.
    ros::spin();

    return 0;
};
  1. Diagrama de flujo del proyecto

  2. Análisis del código

En primer lugar, se suscribe a la posición /pose de la tortuga, y cuando se publica el topic, entra en la función de callback. Y luego, en el broadcaster de tf, se inicializan los datos de tf, cuyo valor proviene de la suscripción al topic /pose. Finalmente, la transformación de las coordenadas del mundo respecto a la pequeña tortuga se publica mediante br.sendTransform, la función sendTransform. Tiene cuatro parámetros: el primero representa tf (es decir, los datos de tf inicializados previamente) del tipo Transform, el segundo parámetro es una marca de tiempo, y el tercero y el cuarto son las coordenadas de origen y de destino, respectivamente.

Implementación en C++ del tf listener

  1. Cree un archivo C++ (archivo con sufijo .cpp) en la carpeta src del paquete

  2. Copie el siguiente código de programa en el archivo turtle_tf_listener.cpp

Archivo de código: 7-2-1-10-publishing-and-listening-with-tf-with.cpp

cpp
/**
This example listens to TF data, computes velocity commands, and publishes them to turtle2.
turtle2->turtle1 = world->turtle*world->turtle2
*/

#include <ros/ros.h>
#include <tf/transform_listener.h>
#include <geometry_msgs/Twist.h>
#include <turtlesim/Spawn.h>

int main(int argc, char** argv)
{

    ros::init(argc, argv, "turtle1_turtle2_listener");// Initialize the ROS node.


    ros::NodeHandle node; // Create a node handle.

    // Call the service to spawn turtle2.
    ros::service::waitForService("/spawn");
    ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
    turtlesim::Spawn srv;
    add_turtle.call(srv);

    // Create a publisher for turtle2 velocity commands.
    ros::Publisher vel = node.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 10);

    tf::TransformListener listener;// Create a TF listener.

    ros::Rate rate(10.0);

    while (node.ok())
    {
        // Get TF data between turtle1 and turtle2.
        tf::StampedTransform transform;
        try
        {
            listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));
            listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);
        }
        catch (tf::TransformException &ex)
        {
            ROS_ERROR("%s",ex.what());
            ros::Duration(1.0).sleep();
            continue;
        }

        // Compute angular and linear velocity from the relative pose between turtle1 and turtle2, then publish commands for turtle2.
        geometry_msgs::Twist turtle2_vel_msg;

        turtle2_vel_msg.angular.z = 6.0 * atan2(transform.getOrigin().y(),
                                        transform.getOrigin().x());
        turtle2_vel_msg.linear.x = 0.8 * sqrt(pow(transform.getOrigin().x(), 2) +
                                      pow(transform.getOrigin().y(), 2));
        vel.publish(turtle2_vel_msg);

        rate.sleep();
    }
    return 0;
};
  1. Diagrama de flujo del proyecto

  2. Análisis del código

Primero, la llamada al servicio crea otra pequeña tortuga, turtle2, y luego se crea un controlador de velocidad para turtle2; después se crea un listener, que escucha y busca la relación entre turtle1 y turtle2, lo cual involucra dos funciones:

Archivo de código: 7-2-1-10-publishing-and-listening-with-tf-example-03.cpp

cpp
waitForTransform and lookupTransform
waitForTransform(target_frame,source_frame,time,timeout)

Los dos frames representan las coordenadas de destino y las coordenadas de origen, respectivamente, y el tiempo indica cuánto esperar el cambio entre las dos coordenadas, ya que el cambio de coordenadas es un procedimiento bloqueante y, por lo tanto, es necesario establecer un límite de tiempo.

lookupTransform(target_frame, source_frame, transform): dado el source_frame y las coordenadas de destino (target_frame), obtiene la transformación (transform) entre ambas coordenadas en el tiempo indicado.

Obtuvimos el resultado de la transformación de coordenadas mediante lookupTransform, y luego x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x

Modificaciones a CMakeLists.txt y compilación

  1. Modificar CMakeLists.txt

Modifique src/learning_tf/CMakeLists.txt dentro del paquete añadiendo lo siguiente:

(recordatorio básico de uso de vim: 14 con el editor Vim)

Archivo de código: 7-2-1-10-publishing-and-listening-with-tf-example-04.cpp

cpp
add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})

add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})
  1. Compilar los archivos de implementación
bash
cd ~/catkin_ws
catkin_make
source devel/setup.bash     # source the workspace so ROS can find the program

Demostración de inicio y efectos operativos

  1. Abra seis terminales y ejecute los siguientes comandos:
bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_tf turtle_tf_broadcaster __name:=turtle1_tf_broadcaster /turtle1
rosrun learning_tf turtle_tf_broadcaster __name:=turtle2_tf_broadcaster /turtle2
rosrun learning_tf turtle_tf_listener
rosrun turtlesim turtle_teleop_key  # start keyboard teleoperation for the turtle
  1. Efecto demostrado

III. DESCRIPCIÓN DEL PROCEDIMIENTO

Al activarse roscore, se activa el nodo de la pequeña tortuga, y aparecerá una pequeña tortuga al final; luego publicamos dos transformaciones tf, turtle1->world y turtle2->world, porque si queremos conocer el cambio entre turtle2 y turtle1, necesitamos conocer el cambio de cada una respecto a world; después se abre el programa de escucha de tf, momento en el cual la terminal genera otra tortuga, y turtle2 se moverá hacia turtle1; luego activamos el control por teclado, y al pulsar las flechas controlamos el movimiento de turtle1, y turtle2 seguirá el movimiento de turtle1.

tf broadcaster en Python

  1. Cree una carpeta scripts dentro del paquete tf, cambie a este directorio y cree un nuevo archivo .py llamado turtle_tf_broadcaster.py

  2. Copie el siguiente código de programa en el archivo turtle_tf_broadcaster.py

Archivo de código: 7-2-1-10-publishing-and-listening-with-tf-new.py

python
#!/usr/bin/env python3

import roslib
roslib.load_manifest('learning_tf')
import rospy

import tf
import turtlesim.msg

def handle_turtle_pose(msg, turtlename):
    br = tf.TransformBroadcaster()# Create a TF broadcaster.
    # Broadcast the TF transform between world and the named turtle.
    br.sendTransform((msg.x, msg.y, 0),
                     tf.transformations.quaternion_from_euler(0, 0, msg.theta),
                     rospy.Time.now(),
                     turtlename,
                     "world")

if __name__ == '__main__':

    rospy.init_node('turtle1_turtle2_tf_broadcaster')# Initialize the ROS node.

    turtlename = rospy.get_param('~turtle') # Get the turtle name from the parameter server.
    # Subscribe to the turtle pose topic.
    rospy.Subscriber('/%s/pose' % turtlename,
                     turtlesim.msg.Pose,
                     handle_turtle_pose,
                     turtlename)
    rospy.spin()
  1. Diagrama de flujo del proyecto

tf listener en Python

  1. Cree un archivo Python (con sufijo .py) en la carpeta scripts del paquete learning_tf, llamado turtle_tf_listener.py

  2. Copie el siguiente código de programa en el archivo turtle_tf_listener.py

Archivo de código: 7-2-1-10-publishing-and-listening-with-tf-with.py

python
#!/usr/bin/env python3
import rospy
import math
import tf
import geometry_msgs.msg
import turtlesim.srv

if __name__ == '__main__':
    rospy.init_node('turtle_tf_listener')# Initialize the ROS node.

    listener = tf.TransformListener()# Initialize a TF listener.

    rospy.wait_for_service('spawn')
    # Call the service to create another turtle named turtle2.
    spawner = rospy.ServiceProxy('spawn', turtlesim.srv.Spawn)
    spawner(8, 6, 0, 'turtle2')
    # Declare a publisher for turtle2 velocity.
    turtle_vel = rospy.Publisher('turtle2/cmd_vel', geometry_msgs.msg.Twist,queue_size=1)

    rate = rospy.Rate(10.0)
    while not rospy.is_shutdown():
        try:
            # Look up the TF transform between turtle2 and turtle1.
            (trans,rot) = listener.lookupTransform('/turtle2', '/turtle1', rospy.Time(0))
        except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
            continue
        # Compute linear and angular velocity, then publish them.
        angular = 6.0 * math.atan2(trans[1], trans[0])
        linear = 0.8 * math.sqrt(trans[0] ** 2 + trans[1] ** 2)
        cmd = geometry_msgs.msg.Twist()
        cmd.linear.x = linear
        cmd.angular.z = angular
        turtle_vel.publish(cmd)
        rate.sleep()
  1. Diagrama de flujo del proyecto

Demostración de inicio y efectos operativos

  1. Preparación de un archivo launch

En el directorio del paquete, cree una nueva carpeta launch, cambie a launch, cree un nuevo archivo launch llamado start_tf_demo_py.launch, y copie lo siguiente dentro de él:

Archivo de código: 7-2-1-10-publishing-and-listening-with-tf-py.xml

xml
<launch>

    <!-- turtlesim node-->
    <node pkg="turtlesim" type="turtlesim_node" name="sim"/>
    <!-- broadcast turtle1 -> world -->
    <node name="turtle1_tf_broadcaster" pkg="learning_tf" type="turtle_tf_broadcaster.py" respawn="false" output="screen" >
      <param name="turtle" type="string" value="turtle1" />
    </node>
    <!-- broadcast turtle2 -> world -->
    <node name="turtle2_tf_broadcaster" pkg="learning_tf" type="turtle_tf_broadcaster.py" respawn="false" output="screen" >
      <param name="turtle" type="string" value="turtle2" />
    </node>
    <!--listener-->
    <node pkg="learning_tf" type="turtle_tf_listener.py" name="listener" />
    <!--turtle keyboard control node-->
    <node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen"/>
</launch>
  1. Inicio
bash
roslaunch learning_tf start_tf_demo_py.launch

Mientras la aplicación se ejecuta, haga clic con el ratón en la ventana donde se ejecuta launch, pulse las teclas de flecha, y turtle2 se moverá junto con turtle1.

  1. Los efectos operativos son, en general, consistentes con los de C++

Figuras

7.2.1.10 Publishing and Listening with TF figure 1

7.2.1.10 Publishing and Listening with TF figure 2

7.2.1.10 Publishing and Listening with TF figure 3

7.2.1.10 Publishing and Listening with TF figure 4

7.2.1.10 Publishing and Listening with TF figure 5

7.2.1.10 Publishing and Listening with TF figure 6

7.2.1.10 Publishing and Listening with TF figure 7