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

Written by Ernest Cheong



  • In this tutorial I’ll answer this question from the Robotics Stack Exchange and show you how to create custom interfaces using a field type that is a msg from a package, and not just a primitive built-in-type.
  • Ideally you should already have an 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 not, The Robotics Back-End has a great tutorial.
  • I’ll be using The Construct’s ROS development studio (rosject) and doing it in a ROS2 Humble workspace called ros2_ws. You can access this rosject here.

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)


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


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

  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:


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.

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


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