How to create custom interfaces using a field type that is a msg from a package | English ROS2 Tutorial

Written by Ernest Cheong

08/03/2024

What you will learn:

How to create custom interfaces using a field type that is a msg from a package, and not just a primitive built-in-type.

Prerequisite:

Basic understanding of the basics of creating custom interfaces with the built-in field types (including best practices such as creating a package dedicated to your interface definitions).

If you want to learn ROS 2 Python in a practical, hands-on way, check out the course ROS 2 Basics in 5 Days: https://app.theconstruct.ai/courses/132

In this course, you’ll cover the foundational concepts needed to start working with ROS 2, as well as more advanced topics, all while engaging in hands-on practice.

 

Opening the rosject

In order to follow this tutorial, we need to have ROS2 installed in our system, and ideally a ros2_ws (ROS2 Workspace). To make your life easier, we have already prepared a rosject for that: /https://app.theconstruct.ai/l/5f773d8c/

Just by copying the rosject (clicking the link above), 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 ( see this example).

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

In order to interact with ROS2, we need a terminal.

Let’s open a terminal by clicking the Open a new terminal button.

Open a new Terminal

Recreating the error: Creating the custom srv

Create a new package named custom_interfaces. This package, however, has to be a CMake package (default when using ros2 pkg create). Currently, there is no way to generate custom interfaces in a pure Python package. However, you can create a custom interface in a CMake package and then use it in a Python node.

cd ~/ros2_ws/src/
ros2 pkg create custom_interfaces

Next, remove the include and src directories and create a directory named srv inside your package. Inside this directory, create the srv file TurnCamera.srv:

cd ~/ros2_ws/src/custom_interfaces
rm -rf include
rm -rf src
mkdir srv
touch srv/TurnCamera.srv

Paste this inside TurnCamera.srv:

float32 deg_turn
---
sensor_msgs/Image camera_image

Then add the following lines to the CMakeLists.txt file:

find_package(rosidl_default_generators REQUIRED)
find_package(sensor_msgs REQUIRED)

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

And also the following lines to the package.xml file within the <package> element:

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

Finally compile the package by running this in the terminal:

cd ~/ros2_ws
colcon build --packages-select custom_interfaces

You should get this error:

Starting >>> custom_interfaces
--- stderr: custom_interfaces
In file included from /home/user/ros2_ws/build/custom_interfaces/rosidl_typesupport_cpp/custom_interfaces/srv/turn_camera__type_support.cpp:7:
/home/user/ros2_ws/build/custom_interfaces/rosidl_generator_cpp/custom_interfaces/srv/detail/turn_camera__struct.hpp:134:10: fatal error: sensor_msgs/msg/detail/image__struct.hpp: No suchfile or directory
  134 | #include "sensor_msgs/msg/detail/image__struct.hpp"
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
gmake[2]: *** [CMakeFiles/custom_interfaces__rosidl_typesupport_cpp.dir/build.make:86: CMakeFiles/custom_interfaces__rosidl_typesupport_cpp.dir/rosidl_typesupport_cpp/custom_interfaces/srv/turn_camera__type_support.cpp.o] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:414: CMakeFiles/custom_interfaces__rosidl_typesupport_cpp.dir/all] Error 2
gmake: *** [Makefile:146: all] Error 2
---
Failed   <<< custom_interfaces [20.3s, exited with code 2]

Summary: 0 packages finished [20.8s]
  1 package failed: custom_interfaces
  1 package had stderr output: custom_interfaces

Fixing the error: Adding a dependency

So what was the issue?

Well the OP did not pass the dependencies to rosidl_generate_interfaces by specifying other packages that their custom interface used. In this case, they did not specify the dependency on sensor_msgs which their custom srv TurnCamera.srv used.

They added the find_package line (in green) but did not add the DEPENDENCIES line (in red) in the CMakeLists.txt file. Both lines are necessary when using a msg from another package as a field type:

find_package(rosidl_default_generators REQUIRED)
find_package(sensor_msgs REQUIRED) # Add packages that the custom interfaces depend on

rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/TurnCamera.srv"
  DEPENDENCIES sensor_msgs # Add packages that the custom interfaces depend on
)

After adding this line, recompile and source the workspace:

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

It should work now!

Starting >>> custom_interfaces
Finished <<< custom_interfaces [21.1s]

Summary: 1 package finished [21.5s]

You can verify that your custom srv has been created successfully by running these two commands:

ros2 interface list | grep custom

ros2 interface show custom_interfaces/srv/TurnCamera

You should get these results respectively:

custom_interfaces/srv/TurnCamera

float32 deg_turn
---
sensor_msgs/Image camera_image
        std_msgs/Header header #
                builtin_interfaces/Time stamp
                        int32 sec
                        uint32 nanosec
                string frame_id
                                     # Header frame_id should be optical frame of camera
                                     # origin of frame should be optical center of cameara
                                     # +x should point to the right in the image
                                     # +y should point down in the image
                                     # +z should point into to plane of the image
                                     # If the frame_id here and the frame_id of the CameraInfo
                                     # message associated with the image conflict
                                     # the behavior is undefined
        uint32 height                #
        uint32 width                 #
        string encoding       #
                              # taken from the list of strings in include/sensor_msgs/image_encodings.hpp
        uint8 is_bigendian    #
        uint32 step           #
        uint8[] data          #

The same principles should apply for custom msgs and actions with field types that are msgs from packages rather than simply the primitive built-in-types.

Congratulations. You now know how to create ROS 2 Custom Interface.

To learn more advanced topics about ROS 2, have a look at the course below:

We hope this post was really helpful to you.

This tutorial is created by Robotics Ambassador Ernest.

Video Tutorial

Topics: ros2
Masterclass 2023 batch2 blog banner

Check Out These Related Posts

129. ros2ai

129. ros2ai

I would like to dedicate this episode to all the ROS Developers who believe that ChatGPT or...

read more

0 Comments

Submit a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Pin It on Pinterest

Share This