Crea una Interfaz de Servicio Personalizada – Spanish ROS Tutorial

Crea una Interfaz de Servicio Personalizada – Spanish ROS Tutorial

This tutorial is created by Robotics Ambassador 017 Jose

Rosbotics Ambassador Program (https://www.theconstruct.ai/robotics-ambassador/

Lo que vamos a aprender

  1. Crear una interfaz de servicio personalizada.
  2. Emplear la interfaz creada para comandar el Turtlebot3.

Lista de recursos usados en esta publicación

  1. Usa este rosject: https://app.theconstruct.ai/l/603ea5b0/
  2. The Construct: https://app.theconstructsim.com/
  3. Cursos ROS: ROS2 Basics in 5 Days (Python): https://app.theconstructsim.com/courses/132

Resumen

ROS (Robot Operating System) se está convirtiendo en el “framework” estándar para programar robots. En este tutorial emplearemos los conceptos de servicios para comandar el robot “Turtlebot3” haciendo uso de una interfaz de servicio personalizada que crearemos.Todo esto es simulado en un mundo de Gazebo. Ten en cuenta que se usará ROS2 Humble para este tutorial.

PASO 1: Abriendo el rosject

Para seguir este tutorial, necesitamos tener instalado ROS2 HUMBLE en nuestro sistema, y lo ideal sería tener un ros2_ws (Espacio de Trabajo ROS2). Para facilitarte la vida, ya hemos preparado un rosject para eso: https://app.theconstruct.ai/l/603ea5b0/.

Simplemente copiando el rosject (haciendo clic en el enlace de arriba), tendrás una configuración ya preparada para ti.

Después de haber copiado el rosject a tu propia área de trabajo, deberías ver el botón RUN. Haz clic en ese botón para lanzar el rosject (abajo tienes un ejemplo de rosject).

Rosject

Tras pulsar el botón RUN, deberías tener cargado el rosject. Ahora, pasemos a la siguiente sección para ponernos manos a la obra.

PASO 2: Creación del paquete

El robot a usar es el Turtlebot3.

Rosject

Primero crearemos el paquete con las dependencias necesarias. Tener en cuenta que, para crear una interfaz de servicio personalizada solo puede hacer con un paquete CMake. Para ello ejecutamos en un terminal lo siguiente:

cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake tutorial_interface_srv --dependencies rclcpp std_msgs

PASO 3: Creación de la interfaz

Luego crearemos dentro del paquete la carpeta srv que contendrá la interfaz de servicio. Dentro de este creamos el archivo Mover.srv

cd ~/ros2_ws/src/tutorial_interface_srv
mkdir srv
cd srv
touch Mover.srv

Dentro de este archivo colocamos el contenido de la interfaz que necesitamos, el cual se compone de la parte de petición (request) y respuesta (response), que se separan mediante 3 guiones (—). Toda esta estructura permite la comunicación entre cliente y servidor.

Dentro de el archivo recién creado colocar lo siguiente: Mover.srv

float64 angulo                  # Angulo a girar
float64 velocidad_lineal        # Velocidad Lineal (en m/s)
int32 tiempo                    # Duración del movimiento (en segundos)
---
bool completado                 # Se logró?

PASO 4: Modificando CMakeLists.txt y package.xml

Una vez creado el archivo del servicio personalizado, necesitamos modificar los siguientes archivos para que nuestro paquete compile correctamente:

CMakeLists.txt: Para convertir las interfaces definidas en código específico de un lenguaje (como C++ y Python) para que puedan utilizarse en esos lenguajes.
package.xml: Añadimos las dependencias adicionales necesarias.

CMakeLists.txt

En CMakeLists.txt debemos tener lo siguiente:

find_package()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces()

Aquí se colocará la interfaz de servicio que creamos dentro de la carpeta srv.

rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/Mover.srv"
)

Con lo cual CMakeList.txt deberia quedarnos mínimante con lo siguiente:

cmake_minimum_required(VERSION 3.8)
project(tutorial_interface_srv)
​
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()
​
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)
​
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()
​
rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/Mover.srv"
)
​
ament_package()

package.xml

En package.xml debemos añadir:

<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

Y deberia quearnos mínimamente con lo siguiente:

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>tutorial_interface_srv</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="user@todo.todo">user</maintainer>
  <license>TODO: License declaration</license>
​
  <buildtool_depend>ament_cmake</buildtool_depend>
​
  <depend>rclcpp</depend>
  <depend>std_msgs</depend>
​
  <build_depend>rosidl_default_generators</build_depend>
  <exec_depend>rosidl_default_runtime</exec_depend>
  <member_of_group>rosidl_interface_packages</member_of_group>
​
  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>
​
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

PASO 5: Compilación del paquete

Por último, compilamos el paquete para poder usar la interfaz de servicio creada.

cd ~/ros2_ws
colcon build --packages-select tutorial_interface_srv
source install/setup.bash

Es importante realizar el último paso, de lo contrario no se encontrará la interfaz de servicio que se generó. Para asegurarnos de que interfaz se generó correctamente usamos los siguiente:

ros2 interface show tutorial_interface_srv/srv/Mover

Esto nos debería mostrar el contenido de Mover.srv en la terminal:

Simulación en Gazebo del Turtlebot3 moviendose

PASO 6: Usando la interfaz

Para hacer uso de la interfaz, se creo un paquete (tutorial_servicios) que contiene un servidor y un cliente que se comunican mediante el mensaje Mover.srv que acabamos de generar.

Simulación en Gazebo del Turtlebot3 moviendose

El cliente hace una petición al servidor enviándole el ángulo que necesita girar, la velocidad lineal y el tiempo que debe moverse con esa velocidad. Una vez realizado el movimiento el servidor enviará un mensaje de confirmación al cliente como respuesta. Para ello, ejecutamos la simulación en Gazebo (la cual se puede obtener desde este link).

export TURTLEBOT3_MODEL=waffle
ros2 launch turtlebot3_gazebo empty_world.launch.py

Simulación en Gazebo del Turtlebot3 moviendose

Luego en otra terminal ejecutamos el servidor:

ros2 launch tutorial_servicios mover_server_launch_file.launch.py

Y en otra terminal ejecutamos el cliente:

ros2 launch tutorial_servicios mover_client_launch_file.launch.py

Y listo!! ya estamos haciendo uso de la interfaz de servicio recién generada.

Simulación en Gazebo del Turtlebot3 moviendose


Video Tutorial

ROS2 Message Interfaces: Ano at Paano Gamitin ang mga Ito – ROS Filipino Tutorial

ROS2 Message Interfaces: Ano at Paano Gamitin ang mga Ito – ROS Filipino Tutorial

This tutorial is created by Robotics Ambassador Christian



Unit 1 Introduksyon sa Kurso

– Buod –

Tinatayang Oras na Kakailanganin: 30 minuto

Mabuhay!

Sa maikling gabay na ito, ating dadaluban kung ano nga ba ang mga ROS2 Messages at paano natin magagamit ang mga ito sa ating paggawa ng mga robots.

Bilang gabay sa mga tatalakayin ng proyektong ito, sila ay nakalista bilang mga sumusunod:

1. Introduksyon sa Kurso
1.1 Mga Pangangailangan sa Kurso at mga Sanggunian
2. Pagpapahayag ng Konsepto ng ROS2 Messages
3. Praktikal na Halimbawa: Paggawa ng Sariling Message at Paggamit nito sa uganayang publisher-subscriber

– Dulo ng Buod –

Unit 1.1 Mga Pangangailangan sa Kurso at mga Sanggunian

Para sa kursong ito, tinataya ng may-akda na may kaalaman ka na sa pangunahing operasyong pang-terminal ng mga sistemang gumagana sa Ubuntu 22.04 LTS na OS at maging sa pangunahing nabigasyon and commands na ginagamit sa mga Linux terminals. Kung nais mo pang matuto ng mga programming fundamentals na kailangan para sa pag-aral ng ROS, mangyaring tumungo at tignan ang kursong Code Foundation for ROS Learning Path ng The Construct! Tiyak na matututunan mo ang lahat ng kailangan mong kaalaman upang masimulan mo na ang paggamit ng ROS!

Mga Akdang Kasama sa Code Foundation for ROS Learning Path:
* Linux for Robotics
* Python 3 for Robotics
*Examination

Iba pang mga Sanggunian:
* The Construct Blog
* Course Support of The Construct and Robot Ignite Academy
* The Official Rosbotics Ambassadors Channel

Kung nais mong matuto hinggil sa kung paano gumawa at mag-ayos ng workspace directories sa ROS1 at ROs2, maaari mong tignan ang aking nakarrang gabay na ito: How to Create and Organize Workspaces in ROS1 and ROS2

At kung interesado ka na rin sa kung paano gumawa ng mga packages at nodes sa ROS2, mangyaring tignan mo na rin ang gabay na ito: How to Create Packages and Nodes in ROS2

Sa tutorial na ito, Gagamitin natin ang ROSject platform na handog ng TheConstruct! Subalit, kung nais niyong gumamit ng sariling virtual machine o computer, mangyaring gawing gabay ang OS-to-Distribution compatibilities na siyang ibinahagi ng opisyal na dokumentasyon ng ros.org para sa ROS2 Humble Hawksbill

* ROS2 Humble Hawksbill: PC o Virtual Machine na may Ubuntu 22.04 LTS

Unit 2: ROS2 Messages – Ano nga ba sila?

Sa kasalukuyan ay dapat pamilyar na tayo sa konsepto ng mga topics sa ROS2. Kung ating babalikan ang naging halimbawa noong nakaraan, ang mga ROS2 topics ay siyang mga nagsisilbing lagusan ng impormasyon kung saan maaari itong gamitin ng mga subscriber para mapatakbo ang isang feature ng robot. Ngayon naman’y ating dadaluban ang isa sa mga mahalagang aspeto ng mga ito at ito ang mga messages!

Sa ROS2, may tatlong pangunahing uri ng interface — ang messages, services, at actions. Sa gabay na ito, ating tatalakayin ang mga messages. Sa madaling salita, ang mga dokumentong ito ang siyang naglalaman ng mga variables na gagamitin ng ating robot o program hinggil sa pagpapagana ng naturang feature. Isang magandang halimbawa na nito ay ang Twist() message na gamit natin upang bigyan ng velocities and ating turtlesim.

Kung ating sisiyasatin ang laman ng Twist() message type, ito ang ating masisilayan:
Vector3  linear
Vector3  angular

At sa loob ng bawat Vector, ito naman ang laman nila:

linear: 
x: 0.0 
y: 0.0 
z: 0.0
angular: 
x: 0.0 
y: 0.0 
z: 0.0

Ibigsabihin nito ay mayroong tatlong posibleng velocities na pwedeng ipublish gamit ang message na ito. Ang mga linear velocities ay siyang tumutkoy sa paharap at palikod, pagilid, at pataas na galaw ng robot; habang ang mga angular naman ay tinatawag na pitch, yaw, at roll. Para sa mga groundbot, ang madalas gamitin na angular velocity ay ang yaw; habang para sa mga robot na panghimpapawid ay ginagamit ang anim na yan dahil mas marami ang kanilang Degrees of Freedom (DoF).

Ngayon, kung ating papansinin ang pagsulat ng mga messages sa ROS2, masasabi nating napakasimple ng kanilang istruktura. Ang tanging kailangan lamang na ilagay ay yung klase ng variable at yung pangalan nito — at hinggil naman sa pagpapangalan, dapat ay masimulan lamang ito sa kapital ng letra at ang file extension ay ‘.msg’.

<variable type> <variable name>   

Ngayon at alam na natin kung ano sila, halina’t ating simulan ang paggawa at paggamit ng ating mga customized na ROS2 messages!

Unit 3: Paggawa ng Sariling Message at Paggamit nito sa Uganayang Publisher-Subscriber

Bilang panimula, tayo muna ay gumawa ng ating Workspaces folder na siyang maglalaman ng lahat ng ating workspace para sa ROS2.

# Sa Terminal 1
user:~$ mkdir Workspaces

Matapos niyan ay gumawa tayo ng workspace na ating papangalanang sample_ws. Mangyaring patakbuhin na rin ang ‘colcon build’ pagkatapos sa loob nito.

# Sa Terminal 1
user:~/Workspaces$ mkdir sample_ws
user:~/Workspaces$ colcon build

Para sa gabay na ito, gagawa tayo ng dalawang packages — una ay ating tatawaging ‘my_interface_pkg’ na siyang maglalaman ng ating custom message at ang isa naman ay ating tatawagin ‘use_custom_interface_pkg’ kung saan natin gagawin ang mga publisher at subscriber na gagamit ng ating nagawang interface.

Ngayon ay ating unahin muna ang ‘my_interface_pkg’

# Sa Terminal 1

user:~/Workspaces$ cd sample_ws
user:~/Workspaces/sample_ws$ mkdir src
user:~/Workspaces/sample_ws$ cd src
user:~/Workspaces/sample_ws/src$ ros2 pkg create my_interface_pkg --build-type ament_cmake --dependencies std_msgs rclcpp

Isang mahalagang konseptong alalahanin ay sa tuwing gagawa ng package para sa mga interfaces ay dapat na ament_cmake ang build type ng mga ito at nakadepende sa rclcpp. Subalit para sa paggawa ng nodes ay malaya tayong pumili kung nais nating gumamit ng python o cpp na environment.

Kasunod ng paggawa ng package ay gumawa tayo ng folder na ating papangalanang ‘msg’ sa loob ng my_interface_pkg. Mula rito ay gumawa na rin tayo ng ating message file na ating papangalanang ‘Numbers.msg’

# Sa Terminal 1

user:~/Workspaces/sample_ws/src$ cd my_interface_pkg
user:~/Workspaces/sample_ws/src/my_interface_pkg$ mkdir msg
user:~/Workspaces/sample_ws/src/my_interface_pkg$ cd msg
user:~/Workspaces/sample_ws/src/my_interface_pkg/msg$ touch Numbers.msg

Mangyaring kopyahin ang naturang code sa ilalim na siyang magiging laman ng Numbers.msg

int32 a
int32 b

Tumungo sa CMakeList.txt na file at idagdag ang sumusunod na mga linya:

find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/num.msg"
)

Ganito ang kalalabasan ng iyong CMakeList.txt

cmake_minimum_required(VERSION 3.8)
project(my_interface_pkg)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rosidl_default_generators REQUIRED)

if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/Numbers.msg"
)

ament_package()

Ngayon naman ay tumungo sa package.xml at idagdag ang mga sumusunod na sipi ng code:

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>my_interface_pkg</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="user@todo.todo">user</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <depend>std_msgs</depend>
  <depend>rclcpp</depend>

  <build_depend>rosidl_default_generators</build_depend>
  <exec_depend>rosidl_default_runtime</exec_depend>
  <member_of_group>rosidl_interface_packages</member_of_group>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

Siguraduhing na-save ang lahat ng mga file at i-compile ang naturang package

# Terminal 1

user:~/Workspaces/sample_ws/src/my_interface_pkg/msg$ cd ../../../
user:~/Workspaces/sample_ws$ colcon build

Ngayon naman ay tumungo tayo sa paggawa ng publisher at subscriber na gagamit ng ating custom message.

Una, gumawa ng python package na ating papangalanang ‘use_custom_interface_pkg’

# Sa Terminal 1

user:~/Workspaces/sample_ws$ cd src
user:~/Workspaces/sample_ws/src$ ros2 pkg create use_custom_interface_pkg --build-type ament_python --dependencies rclpy std_msgs geometry_msgs my_interface_pkg

Para sa ating mga codes, atin itong ilalagay sa loob ng /use_custom_interface_pkg/use_custom_interface_pkg na directory at papangalanan natin ang publisher at subscriber bilang sumusunod:

Publisher: custom_publisher.py
Subscriber: custom_subscriber.py

# Sa Terminal 1

user:~/Workspaces/sample_ws/src$ cd use_custom_interface_pkg/cd use_custom_interface_pkg/
user:~/Workspaces/sample_ws/src/use_custom_interface_pkg/cd use_custom_interface_pkg$ touch custom_subscriber.py custom_publisher.py

Siguraduhing executable na rin ang mga ito.

# Sa Terminal 1

user:~/Workspaces/sample_ws/src/use_custom_interface_pkg/cd use_custom_interface_pkg$ chmod +x custom_subscriber.py custom_publisher.py

Ngayon ay simulan na natin silang i-program!

Code para sa custom_publisher.py

import rclpy
from rclpy.node import Node
from my_interface_pkg.msg import Numbers
import random
import time

class CustomPublisher(Node):
    def __init__(self):
        super().__init__('custom_publisher')
        self.custom_publisher_ = self.create_publisher(Numbers, 'custom_topic_sample', 10)
        self.msg = Numbers()
        self.timer = self.create_timer(1, self.timer_callback)
    
    def timer_callback(self):
        
        self.msg.a = random.randint(1,10)
        self.msg.b = random.randint(1,10)
        self.custom_publisher_.publish(self.msg)
        self.get_logger().info(f"Publishing numbers: a={self.msg.a}, b={self.msg.b}")

def main(args=None):
    rclpy.init(args=args)
    custom_publisher = CustomPublisher()
    rclpy.spin(custom_publisher)
    custom_publisher.destroy_node()
    rclpy.shutdown()

if __name__=='__main__':
    main()

Code para sa custom_subscriber.py

import rclpy
from rclpy.node import Node
from my_interface_pkg.msg import Numbers

class CustomSubscriber(Node):
    def __init__(self):
        super().__init__('custom_subscriber')
        self.subscription = self.create_subscription(
            Numbers,
            'custom_topic_sample',
            self.listener_callback,
            10)

    def listener_callback(self, msg):
        sum_numbers = msg.a + msg.b
        self.get_logger().info(f"Received numbers: a={msg.a}, b={msg.b}. Sum: {sum_numbers}")

def main(args=None):
    rclpy.init(args=args)
    custom_subscriber_node = CustomSubscriber()
    rclpy.spin(custom_subscriber_node)
    custom_subscriber_node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Matapos nating maprogram ang mga ito, tumungo tayo sa ‘setup.py’ at idagdag ang mga sumusunod na linya:

from glob import glob
import os


(os.path.join('share', package_name), glob('launch/*.launch.py'))

Sa loob naman ng ‘console_scripts’ ay gawin natin ang ating mga node:

'custom_publisher = use_custom_interface_pkg.custom_publisher:main',
'custom_subscriber = use_custom_interface_pkg.custom_subscriber:main'

Sa kalaunan ay ganito dapat ang ating setup.py:

from setuptools import setup
from glob import glob
import os

package_name = 'use_custom_interface_pkg'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name), glob('launch/*.launch.py'))
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='user',
    maintainer_email='user@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'custom_publisher = use_custom_interface_pkg.custom_publisher:main',
            'custom_subscriber = use_custom_interface_pkg.custom_subscriber:main'
        ],
    },
)

Matapos i-code ang mga ito, mangyaring muling i-compile ang kabuuan ng ‘sample_ws’. At ngayon, atin nang patakbuhin ang ating mga nagawang node na gumagamit ng ating custom message!

# Sa Terminal 1

user:~/Workspaces/sample_ws/src/use_custom_interface_pkg/cd use_custom_interface_pkg$ cd ../../../
user:~/Workspaces/sample_ws$ colcon build

Para sa bahaging ito, i-source ang terminal 2 and 3 para sa sample_ws

# Sa Terminal 2 at 3

user:~$ cd Workspaces/sample_ws
user:~/Workspaces/sample_ws$ source install/setup.bash

Patakbuhin ang publisher at subscriber sa Terminal 2 at 3

# Sa Terminal 2
user:~/Workspaces/sample_ws$ ros2 run use_custom_interface_pkg custom_publisher
# Sa Terminal 3
user:~/Workspaces/sample_ws$ ros2 run use_custom_interface_pkg custom_subscriber

Ganito dapat ang lalabas sa iyong pagpapatakbo:

[INFO] [1711264604.204593355] [custom_subscriber]: Received Numbers: a=3, b=8, sum=11
[INFO] [1711264604.204593546] [custom_subscriber]: Received Numbers: a=3, b=5, sum=8
[INFO] [1711264604.187348953] [custom_subscriber]: Received Numbers: a=2, b=3, sum=5
[INFO] [1711264604.237378829] [custom_subscriber]: Received Numbers: a=4, b=4, sum=8
[INFO] [1711264604.142367827] [custom_subscriber]: Received Numbers: a=1, b=6, sum=7
...
[INFO] [1711264604.204593355] [custom_subscriber]: Received Numbers: a=7, b=9, sum=16
[INFO] [1711264604.204593355] [custom_subscriber]: Received Numbers: a=3, b=1, sum=4

At ayan!

Matagumpay nating nagawa ang ating custom message sa ROS2 at magamit ang mga ito sa publisher at subscriber! Kapag tapos ka nang magsiyasat, maaarin mong pindutin ang “ctrl + c” upang patayin na ang naturang node at maging ang simulation. At diyan nagtatapos ang ating maikling gabay hinggil sa paggawa ng custom messages! Nawa’y may natutunan kayong bago na makatutulong sa inyong pag-aaral ng ROS!

Para sa iba pang mga ROSject na tulad nito, mangyaring bisitahin ang The Construct. Nag-aalok sila ng napakaraming praktikal na mga gabay sa ROS mula sa mga payak hanggang sa mga konseptong pangbihasa na!

Hanggang sa muli! Ito si Christian C. Anabeza, ang inyong Filipino ROSbotics Ambassador!

Video Tutorial

ROS2 Concepts in Practice #4 – Interfaces

ROS2 Concepts in Practice #4 – Interfaces

What we are going to learn

In this video, you’ll understand what is a ROS2 interface, the common language behind ROS2 messages, services, and actions.

You will also learn:

  • How to create and compile your own ROS2 Message Interfaces
  • How to create and compile your own ROS2 Service Interfaces

List of resources used in this post

  1. Use the rosject: https://app.theconstructsim.com/#/l/4a5c5215/
  2. ROS Development Studio (ROSDS) —▸ http://rosds.online
  3. ROS2 Courses –▸
    1. ROS2 Basics in 5 Days (Python): https://app.theconstructsim.com/#/Course/73
    2. ROS2 Basics in 5 Days (C++): https://app.theconstructsim.com/#/Course/61

Opening the rosject

In order to better understand ROS2 Interfaces, we need to have ROS2 installed in our system, and sometimes it is also useful to have some simulations. To make your life easier, we already prepared a rosject that you can use, with ROS2 already installed: https://app.theconstructsim.com/#/l/4a5c5215/.

You can download the rosject on your own computer if you want to work locally, but just by copying the rosject (clicking the link), you will have a setup already prepared for you.

After the rosject has been successfully copied to your own area, you should see a Run button. Just click that button to launch the rosject (below you have a rosject example).

Learn ROS2 Parameters - Run rosject

Learn ROS2 – Run rosject (example of the RUN button)

 

After pressing the Run button, you should have the rosject loaded. Let’s now head to the next section to really get some real practice.

ROS2 Interfaces overview

ROS2 Interfaces is a common language behind ROS2 messages, services, and actions.

Let’s start listing the interfaces. For that, let’s first open a terminal:

Open a new Terminal

Open a new Terminal

After the terminal is open, you can list the interfaces with the command ros2 interface list. The output would be something similar to the following:

ros2 interface list

Messages:
    action_msgs/msg/GoalInfo
    action_msgs/msg/GoalStatus
    action_msgs/msg/GoalStatusArray
    actionlib_msgs/msg/GoalID
    actionlib_msgs/msg/GoalStatus
    actionlib_msgs/msg/GoalStatusArray
    bond/msg/Constants
    bond/msg/Status
    builtin_interfaces/msg/Duration
    builtin_interfaces/msg/Time
    cartographer_ros_msgs/msg/LandmarkEntry
    cartographer_ros_msgs/msg/LandmarkList
    cartographer_ros_msgs/msg/SensorTopics
    cartographer_ros_msgs/msg/StatusCode
    cartographer_ros_msgs/msg/StatusResponse
    cartographer_ros_msgs/msg/SubmapEntry
    cartographer_ros_msgs/msg/SubmapList
    cartographer_ros_msgs/msg/SubmapTexture
    cartographer_ros_msgs/msg/TrajectoryOptions
    checking_interfaces/msg/NewMsg
    control_msgs/msg/DynamicJointState
    control_msgs/msg/GripperCommand
    control_msgs/msg/InterfaceValue
    control_msgs/msg/JointComponentTolerance
    control_msgs/msg/JointControllerState
    control_msgs/msg/JointJog
    control_msgs/msg/JointTolerance
    control_msgs/msg/JointTrajectoryControllerState
    control_msgs/msg/PidState
    controller_manager_msgs/msg/ControllerState
    controller_manager_msgs/msg/HardwareInterface
    diagnostic_msgs/msg/DiagnosticArray
    diagnostic_msgs/msg/DiagnosticStatus
    diagnostic_msgs/msg/KeyValue
    dwb_msgs/msg/CriticScore
    dwb_msgs/msg/LocalPlanEvaluation
    dwb_msgs/msg/Trajectory2D
    dwb_msgs/msg/TrajectoryScore
    example_interfaces/msg/Bool
    example_interfaces/msg/Byte
    example_interfaces/msg/ByteMultiArray
    example_interfaces/msg/Char
    example_interfaces/msg/Empty
    example_interfaces/msg/Float32
    example_interfaces/msg/Float32MultiArray
    example_interfaces/msg/Float64
    example_interfaces/msg/Float64MultiArray
    example_interfaces/msg/Int16
    example_interfaces/msg/Int16MultiArray
    example_interfaces/msg/Int32
    example_interfaces/msg/Int32MultiArray
    example_interfaces/msg/Int64
    example_interfaces/msg/Int64MultiArray
    example_interfaces/msg/Int8
    example_interfaces/msg/Int8MultiArray
    example_interfaces/msg/MultiArrayDimension
    example_interfaces/msg/MultiArrayLayout
    example_interfaces/msg/String
    example_interfaces/msg/UInt16
    example_interfaces/msg/UInt16MultiArray
    example_interfaces/msg/UInt32
    example_interfaces/msg/UInt32MultiArray
    example_interfaces/msg/UInt64
    example_interfaces/msg/UInt64MultiArray
    example_interfaces/msg/UInt8
    example_interfaces/msg/UInt8MultiArray
    example_interfaces/msg/WString
    gazebo_msgs/msg/ContactState
    gazebo_msgs/msg/ContactsState
    gazebo_msgs/msg/EntityState
    gazebo_msgs/msg/LinkState
    gazebo_msgs/msg/LinkStates
    gazebo_msgs/msg/ModelState
    gazebo_msgs/msg/ModelStates
    gazebo_msgs/msg/ODEJointProperties
    gazebo_msgs/msg/ODEPhysics
    gazebo_msgs/msg/PerformanceMetrics
    gazebo_msgs/msg/SensorPerformanceMetric
    gazebo_msgs/msg/WorldState
    geometry_msgs/msg/Accel
    geometry_msgs/msg/AccelStamped
    geometry_msgs/msg/AccelWithCovariance
    geometry_msgs/msg/AccelWithCovarianceStamped
    geometry_msgs/msg/Inertia
    geometry_msgs/msg/InertiaStamped
    geometry_msgs/msg/Point
    geometry_msgs/msg/Point32
    geometry_msgs/msg/PointStamped
    geometry_msgs/msg/Polygon
    geometry_msgs/msg/PolygonStamped
    geometry_msgs/msg/Pose
    geometry_msgs/msg/Pose2D
    geometry_msgs/msg/PoseArray
    geometry_msgs/msg/PoseStamped
    geometry_msgs/msg/PoseWithCovariance
    geometry_msgs/msg/PoseWithCovarianceStamped
    geometry_msgs/msg/Quaternion
    geometry_msgs/msg/QuaternionStamped
    geometry_msgs/msg/Transform
    geometry_msgs/msg/TransformStamped
    geometry_msgs/msg/Twist
    geometry_msgs/msg/TwistStamped
    geometry_msgs/msg/TwistWithCovariance
    geometry_msgs/msg/TwistWithCovarianceStamped
    geometry_msgs/msg/Vector3
    geometry_msgs/msg/Vector3Stamped
    geometry_msgs/msg/Wrench
    geometry_msgs/msg/WrenchStamped
    libstatistics_collector/msg/DummyMessage
    lifecycle_msgs/msg/State
    lifecycle_msgs/msg/Transition
    lifecycle_msgs/msg/TransitionDescription
    lifecycle_msgs/msg/TransitionEvent
    map_msgs/msg/OccupancyGridUpdate
    map_msgs/msg/PointCloud2Update
    map_msgs/msg/ProjectedMap
    map_msgs/msg/ProjectedMapInfo
    nav2_msgs/msg/BehaviorTreeLog
    nav2_msgs/msg/BehaviorTreeStatusChange
    nav2_msgs/msg/Costmap
    nav2_msgs/msg/CostmapFilterInfo
    nav2_msgs/msg/CostmapMetaData
    nav2_msgs/msg/Particle
    nav2_msgs/msg/ParticleCloud
    nav2_msgs/msg/SpeedLimit
    nav2_msgs/msg/VoxelGrid
    nav_2d_msgs/msg/Path2D
    nav_2d_msgs/msg/Pose2D32
    nav_2d_msgs/msg/Pose2DStamped
    nav_2d_msgs/msg/Twist2D
    nav_2d_msgs/msg/Twist2D32
    nav_2d_msgs/msg/Twist2DStamped
    nav_msgs/msg/GridCells
    nav_msgs/msg/MapMetaData
    nav_msgs/msg/OccupancyGrid
    nav_msgs/msg/Odometry
    nav_msgs/msg/Path
    pcl_msgs/msg/ModelCoefficients
    pcl_msgs/msg/PointIndices
    pcl_msgs/msg/PolygonMesh
    pcl_msgs/msg/Vertices
    pendulum_msgs/msg/JointCommand
    pendulum_msgs/msg/JointState
    pendulum_msgs/msg/RttestResults
    rcl_interfaces/msg/FloatingPointRange
    rcl_interfaces/msg/IntegerRange
    rcl_interfaces/msg/ListParametersResult
    rcl_interfaces/msg/Log
    rcl_interfaces/msg/Parameter
    rcl_interfaces/msg/ParameterDescriptor
    rcl_interfaces/msg/ParameterEvent
    rcl_interfaces/msg/ParameterEventDescriptors
    rcl_interfaces/msg/ParameterType
    rcl_interfaces/msg/ParameterValue
    rcl_interfaces/msg/SetParametersResult
    rmw_dds_common/msg/Gid
    rmw_dds_common/msg/NodeEntitiesInfo
    rmw_dds_common/msg/ParticipantEntitiesInfo
    rosgraph_msgs/msg/Clock
    sensor_msgs/msg/BatteryState
    sensor_msgs/msg/CameraInfo
    sensor_msgs/msg/ChannelFloat32
    sensor_msgs/msg/CompressedImage
    sensor_msgs/msg/FluidPressure
    sensor_msgs/msg/Illuminance
    sensor_msgs/msg/Image
    sensor_msgs/msg/Imu
    sensor_msgs/msg/JointState
    sensor_msgs/msg/Joy
    sensor_msgs/msg/JoyFeedback
    sensor_msgs/msg/JoyFeedbackArray
    sensor_msgs/msg/LaserEcho
    sensor_msgs/msg/LaserScan
    sensor_msgs/msg/MagneticField
    sensor_msgs/msg/MultiDOFJointState
    sensor_msgs/msg/MultiEchoLaserScan
    sensor_msgs/msg/NavSatFix
    sensor_msgs/msg/NavSatStatus
    sensor_msgs/msg/PointCloud
    sensor_msgs/msg/PointCloud2
    sensor_msgs/msg/PointField
    sensor_msgs/msg/Range
    sensor_msgs/msg/RegionOfInterest
    sensor_msgs/msg/RelativeHumidity
    sensor_msgs/msg/Temperature
    sensor_msgs/msg/TimeReference
    shape_msgs/msg/Mesh
    shape_msgs/msg/MeshTriangle
    shape_msgs/msg/Plane
    shape_msgs/msg/SolidPrimitive
    statistics_msgs/msg/MetricsMessage
    statistics_msgs/msg/StatisticDataPoint
    statistics_msgs/msg/StatisticDataType
    std_msgs/msg/Bool
    std_msgs/msg/Byte
    std_msgs/msg/ByteMultiArray
    std_msgs/msg/Char
    std_msgs/msg/ColorRGBA
    std_msgs/msg/Empty
    std_msgs/msg/Float32
    std_msgs/msg/Float32MultiArray
    std_msgs/msg/Float64
    std_msgs/msg/Float64MultiArray
    std_msgs/msg/Header
    std_msgs/msg/Int16
    std_msgs/msg/Int16MultiArray
    std_msgs/msg/Int32
    std_msgs/msg/Int32MultiArray
    std_msgs/msg/Int64
    std_msgs/msg/Int64MultiArray
    std_msgs/msg/Int8
    std_msgs/msg/Int8MultiArray
    std_msgs/msg/MultiArrayDimension
    std_msgs/msg/MultiArrayLayout
    std_msgs/msg/String
    std_msgs/msg/UInt16
    std_msgs/msg/UInt16MultiArray
    std_msgs/msg/UInt32
    std_msgs/msg/UInt32MultiArray
    std_msgs/msg/UInt64
    std_msgs/msg/UInt64MultiArray
    std_msgs/msg/UInt8
    std_msgs/msg/UInt8MultiArray
    stereo_msgs/msg/DisparityImage
    test_msgs/msg/Arrays
    test_msgs/msg/BasicTypes
    test_msgs/msg/BoundedSequences
    test_msgs/msg/Builtins
    test_msgs/msg/Constants
    test_msgs/msg/Defaults
    test_msgs/msg/Empty
    test_msgs/msg/MultiNested
    test_msgs/msg/Nested
    test_msgs/msg/Strings
    test_msgs/msg/UnboundedSequences
    test_msgs/msg/WStrings
    tf2_msgs/msg/TF2Error
    tf2_msgs/msg/TFMessage
    trajectory_msgs/msg/JointTrajectory
    trajectory_msgs/msg/JointTrajectoryPoint
    trajectory_msgs/msg/MultiDOFJointTrajectory
    trajectory_msgs/msg/MultiDOFJointTrajectoryPoint
    turtlesim/msg/Color
    turtlesim/msg/Pose
    unique_identifier_msgs/msg/UUID
    visualization_msgs/msg/ImageMarker
    visualization_msgs/msg/InteractiveMarker
    visualization_msgs/msg/InteractiveMarkerControl
    visualization_msgs/msg/InteractiveMarkerFeedback
    visualization_msgs/msg/InteractiveMarkerInit
    visualization_msgs/msg/InteractiveMarkerPose
    visualization_msgs/msg/InteractiveMarkerUpdate
    visualization_msgs/msg/Marker
    visualization_msgs/msg/MarkerArray
    visualization_msgs/msg/MenuEntry
Services:
    action_msgs/srv/CancelGoal
    cartographer_ros_msgs/srv/FinishTrajectory
    cartographer_ros_msgs/srv/StartTrajectory
    cartographer_ros_msgs/srv/SubmapQuery
    cartographer_ros_msgs/srv/WriteState
    checking_interfaces/srv/NewServiceMessage
    composition_interfaces/srv/ListNodes
    composition_interfaces/srv/LoadNode
    composition_interfaces/srv/UnloadNode
    control_msgs/srv/QueryCalibrationState
    control_msgs/srv/QueryTrajectoryState
    controller_manager_msgs/srv/ConfigureController
    controller_manager_msgs/srv/ConfigureStartController
    controller_manager_msgs/srv/ListControllerTypes
    controller_manager_msgs/srv/ListControllers
    controller_manager_msgs/srv/ListHardwareInterfaces
    controller_manager_msgs/srv/LoadConfigureController
    controller_manager_msgs/srv/LoadController
    controller_manager_msgs/srv/LoadStartController
    controller_manager_msgs/srv/ReloadControllerLibraries
    controller_manager_msgs/srv/SwitchController
    controller_manager_msgs/srv/UnloadController
    diagnostic_msgs/srv/AddDiagnostics
    diagnostic_msgs/srv/SelfTest
    dwb_msgs/srv/DebugLocalPlan
    dwb_msgs/srv/GenerateTrajectory
    dwb_msgs/srv/GenerateTwists
    dwb_msgs/srv/GetCriticScore
    dwb_msgs/srv/ScoreTrajectory
    example_interfaces/srv/AddTwoInts
    example_interfaces/srv/SetBool
    example_interfaces/srv/Trigger
    gazebo_msgs/srv/ApplyBodyWrench
    gazebo_msgs/srv/ApplyJointEffort
    gazebo_msgs/srv/ApplyLinkWrench
    gazebo_msgs/srv/BodyRequest
    gazebo_msgs/srv/DeleteEntity
    gazebo_msgs/srv/DeleteLight
    gazebo_msgs/srv/DeleteModel
    gazebo_msgs/srv/GetEntityState
    gazebo_msgs/srv/GetJointProperties
    gazebo_msgs/srv/GetLightProperties
    gazebo_msgs/srv/GetLinkProperties
    gazebo_msgs/srv/GetLinkState
    gazebo_msgs/srv/GetModelList
    gazebo_msgs/srv/GetModelProperties
    gazebo_msgs/srv/GetModelState
    gazebo_msgs/srv/GetPhysicsProperties
    gazebo_msgs/srv/GetWorldProperties
    gazebo_msgs/srv/JointRequest
    gazebo_msgs/srv/LinkRequest
    gazebo_msgs/srv/SetEntityState
    gazebo_msgs/srv/SetJointProperties
    gazebo_msgs/srv/SetJointTrajectory
    gazebo_msgs/srv/SetLightProperties
    gazebo_msgs/srv/SetLinkProperties
    gazebo_msgs/srv/SetLinkState
    gazebo_msgs/srv/SetModelConfiguration
    gazebo_msgs/srv/SetModelState
    gazebo_msgs/srv/SetPhysicsProperties
    gazebo_msgs/srv/SpawnEntity
    gazebo_msgs/srv/SpawnModel
    lifecycle_msgs/srv/ChangeState
    lifecycle_msgs/srv/GetAvailableStates
    lifecycle_msgs/srv/GetAvailableTransitions
    lifecycle_msgs/srv/GetState
    logging_demo/srv/ConfigLogger
    map_msgs/srv/GetMapROI
    map_msgs/srv/GetPointMap
    map_msgs/srv/GetPointMapROI
    map_msgs/srv/ProjectedMapsInfo
    map_msgs/srv/SaveMap
    map_msgs/srv/SetMapProjections
    nav2_msgs/srv/ClearCostmapAroundRobot
    nav2_msgs/srv/ClearCostmapExceptRegion
    nav2_msgs/srv/ClearEntireCostmap
    nav2_msgs/srv/GetCostmap
    nav2_msgs/srv/LoadMap
    nav2_msgs/srv/ManageLifecycleNodes
    nav2_msgs/srv/SaveMap
    nav_msgs/srv/GetMap
    nav_msgs/srv/GetPlan
    nav_msgs/srv/LoadMap
    nav_msgs/srv/SetMap
    pcl_msgs/srv/UpdateFilename
    rcl_interfaces/srv/DescribeParameters
    rcl_interfaces/srv/GetParameterTypes
    rcl_interfaces/srv/GetParameters
    rcl_interfaces/srv/ListParameters
    rcl_interfaces/srv/SetParameters
    rcl_interfaces/srv/SetParametersAtomically
    rosbag2_interfaces/srv/GetRate
    rosbag2_interfaces/srv/IsPaused
    rosbag2_interfaces/srv/Pause
    rosbag2_interfaces/srv/PlayNext
    rosbag2_interfaces/srv/Resume
    rosbag2_interfaces/srv/Seek
    rosbag2_interfaces/srv/SetRate
    rosbag2_interfaces/srv/TogglePaused
    sensor_msgs/srv/SetCameraInfo
    slam_toolbox/srv/AddSubmap
    slam_toolbox/srv/Clear
    slam_toolbox/srv/ClearQueue
    slam_toolbox/srv/DeserializePoseGraph
    slam_toolbox/srv/LoopClosure
    slam_toolbox/srv/MergeMaps
    slam_toolbox/srv/Pause
    slam_toolbox/srv/SaveMap
    slam_toolbox/srv/SerializePoseGraph
    slam_toolbox/srv/ToggleInteractive
    std_srvs/srv/Empty
    std_srvs/srv/SetBool
    std_srvs/srv/Trigger
    test_bond/srv/TestBond
    test_msgs/srv/Arrays
    test_msgs/srv/BasicTypes
    test_msgs/srv/Empty
    tf2_msgs/srv/FrameGraph
    turtlesim/srv/Kill
    turtlesim/srv/SetPen
    turtlesim/srv/Spawn
    turtlesim/srv/TeleportAbsolute
    turtlesim/srv/TeleportRelative
    visualization_msgs/srv/GetInteractiveMarkers
Actions:
    action_tutorials_interfaces/action/Fibonacci
    control_msgs/action/FollowJointTrajectory
    control_msgs/action/GripperCommand
    control_msgs/action/JointTrajectory
    control_msgs/action/PointHead
    control_msgs/action/SingleJointPosition
    example_interfaces/action/Fibonacci
    nav2_msgs/action/BackUp
    nav2_msgs/action/ComputePathThroughPoses
    nav2_msgs/action/ComputePathToPose
    nav2_msgs/action/DummyRecovery
    nav2_msgs/action/FollowPath
    nav2_msgs/action/FollowWaypoints
    nav2_msgs/action/NavigateThroughPoses
    nav2_msgs/action/NavigateToPose
    nav2_msgs/action/Spin
    nav2_msgs/action/Wait
    test_msgs/action/Fibonacci
    test_msgs/action/NestedMessage
    tf2_msgs/action/LookupTransform
    turtlesim/action/RotateAbsolute

 

As you can see in the output above, the same command returned Messages, Actions, and Services.

Creating our first ROS2 Interface (a message)

If you are using the rosject provided at the beginning of this post, there is already a ROS2 Package called checking_interfaces on the /home/user/ros2_ws/src/checking_interfaces path. The package also already contains a message on the ~/ros2_ws/src/checking_interfaces/msg/NewMsg.msg path.

If you are not using the provided rosject, you can create a package with:

mkdir -p /home/user/ros2_ws/src

cd /home/user/ros2_ws/src

ros2 pkg create --build-type ament_cmake checking_interfaces

The output would be similar to the following:

going to create a new package
package name: checking_interfaces
destination directory: /home/user/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['user <user@todo.todo>']
licenses: ['TODO: License declaration']
build type: ament_cmake
dependencies: []
creating folder ./checking_interfaces
creating ./checking_interfaces/package.xml
creating source and include folder
creating folder ./checking_interfaces/src
creating folder ./checking_interfaces/include/checking_interfaces
creating ./checking_interfaces/CMakeLists.txt

 

If you are using the rosject you do not need to create the message msg/NewMsg.msg, but for learning purposes, let’s see how to create it.

~/ros2_ws/src/checking_interfaces$ ls ~/ros2_ws/src/checking_interfaces$ ls msg/NewMsg.msg

cd ~/ros2_ws/src/checking_interfaces

mkdir msg

cd msg

touch NewMsg.msg

Then, paste the following content on the NewMsg.msg file:

bool check
int16 number
string text

If you are wondering how to know which types are accepted for the properties of the new message, the types available are:

ROS2 build-in-types

Type name C++ Python DDS type
bool bool builtins.bool boolean
byte uint8_t builtins.bytes* octet
char char builtins.str* char
float32 float builtins.float* float
float64 double builtins.float* double
int8 int8_t builtins.int* octet
uint8 uint8_t builtins.int* octet
int16 int16_t builtins.int* short
uint16 uint16_t builtins.int* unsigned short
int32 int32_t builtins.int* long
uint32 uint32_t builtins.int* unsigned long
int64 int64_t builtins.int* long long
uint64 uint64_t builtins.int* unsigned long long
string std::string builtins.str string
wstring std::u16string builtins.str string

Compiling our ROS2 Interface

In order to compile our ROS2 Interface, we have to touch the ~/ros2_ws/src/checking_interfaces/CMakeLists.txt file. Again, if you are using the rosject we provided, everything is already prepared for you. For learning purposes, let’s open the file to modify it. Let’s start by opening the Code Editor:

Open the IDE - Code Editor

Open the IDE – Code Editor

 

After the Code Editor is open, feel free to open the checking_interfaces/CMakeListst.txt file.

Around line 13 of the CMakeListst.txt file, we have to add the following lines:

find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
	"msg/NewMsg.msg"
)

The first line mentioned above includes the rosidl_default_generators package, which will be used to “compile” our message to make it available for Python and C++, so that our nodes can use the interface.

In the rosidl_generate_interfaces we add the name of the interface (message, service, or action) that we want to compile, which in this case is “msg/NewMsg.msg”. 

After setting up the CMakeLists.txt file, you also have to set up the checking_interfaces/package.xml file. We have to add rosidl_default_generators as a build dependency, we also need to add the rosidl_default_runtime execution dependency, and we also need the rosidl_interface_packages “member of group”. In the end, the lines you would need to add are:

<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

In the end, the final package.xml file would be as follows:

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
  <package format="3">
  <name>checking_interfaces</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintaineremail="user@todo.todo">user</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <build_depend>rosidl_default_generators</build_depend>
  <exec_depend>rosidl_default_runtime</exec_depend>
  <member_of_group>rosidl_interface_packages</member_of_group>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>
After saving the files, you can compile the interface with:
cd ~/ros2_ws/

colcon build --packages-select checking_interfaces

 

The package should compile with no errors:

...
This may be promoted to an error in a future release of colcon-core.
Starting >>> checking_interfaces
Finished <<< checking_interfaces [5.27s]

Summary: 1 package finished [5.87s]

 

After the package is compiled, we can now source the installation folder so that ROS can find what we have compiled so far:

source install/setup.bash

 

If we now list the interfaces again and search for our NewMsg, we should be able to find it:

ros2 interface list | grep New

# ...
checking_interfaces/msg/NewMsg
# ...

As we can see in the output above, we have a message called NewMsg.

Using the interface we just created

If we have just created the checking_interfaces/msg/NewMsg, we should be able to use it. We can check its definition with:

ros2 interface show checking_interfaces/msg/NewMsg

which shows the exact thing we defined in the NewMsg.msg file:

bool check
int16 number
string text

 

Let’s now create a publisher in a topic called /testing using this message:

ros2 topic pub /testing checking_interfaces/msg/NewMsg

After pressing ENTER, we should see the message being published with default values:

 

publisher: beginning loop
publishing #1: checking_interfaces.msg.NewMsg(check=False, number=0, text='')
publishing #2: checking_interfaces.msg.NewMsg(check=False, number=0, text='')
publishing #3: checking_interfaces.msg.NewMsg(check=False, number=0, text='')
publishing #4: checking_interfaces.msg.NewMsg(check=False, number=0, text='')
publishing #5: checking_interfaces.msg.NewMsg(check=False, number=0, text='')
publishing #6: checking_interfaces.msg.NewMsg(check=False, number=0, text='')
publishing #7: checking_interfaces.msg.NewMsg(check=False, number=0, text='')
...

You can of course set the values of the message:

ros2 topic pub /testing checking_interfaces/msg/NewMsg "{check: true, number: 7, text: 'Be perfect' }"

# WHICH OUTPUTS

publisher: beginning loop
publishing #1: checking_interfaces.msg.NewMsg(check=True, number=7, text='Be perfect')
publishing #2: checking_interfaces.msg.NewMsg(check=True, number=7, text='Be perfect')
publishing #3: checking_interfaces.msg.NewMsg(check=True, number=7, text='Be perfect')
...

 

Creating our second ROS2 Interface (a service)

Similar to when defining our message, we already have a service defined on ~/ros2_ws/src/checking_interfaces/srv/NewServiceMessage.srv, but we will be following the process for learning purposes in case you are not using the rosject we provided, or wants to learn with hands-on.

cd ~/ros2_ws/src/checking_interfaces

mkdir srv

cd srv

touch NewServiceMessage.srv

You can now open the NewServiceMessage.srv file with the Code Editor and paste the following content on it:

bool check
---
int16 number
string text

In order to compile the service interface, you have to open the CMakeLists.txt file again and add “srv/NewServiceMessage.srv” right after “msg/NewMsg.msg” that we added earlier around line 15 of our CMakeLists.txt file. The rosidl_generate_interfaces section of the file would be like:

find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
    "msg/NewMsg.msg"
    "srv/NewServiceMessage.srv"
)
As you can see so far, the same rosidl_generate_interfaces is used to compile Messages and Services.
You may have noticed that we have a line containing “—“ in the NewServiceMessage.srv file. These three “—” basically separate the Service Request from the Service Response.
The request will be “bool check“, and the fields used in a response will be “int16 number” and “string text“.
After saving the file changes, we can just compile the service interface in the same way that we compiled the message interface:
cd ~/ros2_ws/

colcon build --packages-select checking_interfaces

 source install/setup.bash

If you now list the interfaces, you should be able to easily find our NewServiceMessage.

ros2 interface list | grep New


checking_interfaces/msg/NewMsg
checking_interfaces/srv/NewServiceMessage

 

Congratulations. You now know how to easily create ROS2 Interfaces. Please go ahead and try to create ROS2 Action Interfaces yourself, or you can also check the courses that we have listed at the end of this post.

Youtube video

So this is the post for today. Remember that we have the live version of this post on YouTube. If you liked the content, please consider subscribing to our youtube channel. We are publishing new content ~every day.

Keep pushing your ROS Learning.

Related Courses & Training

If you want to learn more about ROS and ROS2, we recommend the following courses:

Pin It on Pinterest