Create a Custom Message

In order to create a new message, you will need to do the following steps:

  1. Create a directory named msg inside your package
  2. Inside this directory, create a file named Name_of_your_message.msg.
  3. Modify CMakeLists.txt file.
  4. Modify package.xml file.
  5. Compile and source your workspace
  6. Use in code

Let's create a message that indicates age, with years, months, and days.

Create message file

Create a directory msg in your package.

cd ~/ros2_ws/src/my_package
mkdir msg

Inside the msg folder, create a new file named Age.msg, which contains the following:

float32 years
float32 months
float32 days

Modify CMakeLists.txt

You will have to edit two functions inside CMakeLists.txt:

find_package()

This is where all the packages required to COMPILE the messages of the topics, services, and actions go. In package.xml, you have to state them as build_depend and exec_depend.

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(builtin_interfaces REQUIRED)
find_package(rosidl_default_generators REQUIRED)
II. rosidl_generate_interfaces()

This function includes all of the messages of this package (in the msg folder) to be compiled. The file should look like this.

rosidl_generate_interfaces(my_package
"msg/Age.msg"
DEPENDENCIES builtin_interfaces
)

Summarizing, this is the minimum expression of what is needed for the CMakaelist.txt to work:

cmake_minimum_required(VERSION 3.5)
project(my_package)
​
​# Default to C99
​if(NOT CMAKE_C_STANDARD)
​set(CMAKE_C_STANDARD 99)
​endif()
​
​# Default to C++14
​if(NOT CMAKE_CXX_STANDARD)
​set(CMAKE_CXX_STANDARD 14)
​endif()
​
​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(builtin_interfaces 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
​# remove the line when a copyright and license is present in all source files
​set(ament_cmake_copyright_FOUND TRUE)
​# the following line skips cpplint (only works in a git repo)
​# remove the line when this package is a git repo
​set(ament_cmake_cpplint_FOUND TRUE)
​ament_lint_auto_find_test_dependencies()
​endif()
​
​rosidl_generate_interfaces(my_package
​"msg/Age.msg"
​DEPENDENCIES builtin_interfaces
​)
​
​ament_package()
​

Modify package.xml

First, you will need to set the package format to 3. Note that, by default, this will be set to 2, so you will need to manually modify it.

<package format="3">
​

This has to be done because the member_of_group command requires format 3. ​Now, just add the following lines to the package.xml file.

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

This is the minimum expression of the package.xml

<?xml version="1.0"?>
​<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
​<package format="3">
​<name>my_package</name>
​<version>0.0.0</version>
​<description>TODO: Package description</description>
​<maintainer email="ubuntu@todo.todo">ubuntu</maintainer>
​<license>TODO: License declaration</license>
​
​<buildtool_depend>ament_cmake</buildtool_depend>
​
​<depend>rclcpp</depend>
​<depend>std_msgs</depend>
​
​<build_depend>builtin_interfaces</build_depend>
​<build_depend>rosidl_default_generators</build_depend>
​<exec_depend>builtin_interfaces</exec_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>
​

Build the new message

Now, you have to compile the new message.

cd ~/ros2_ws
​colcon build --symlink-install --packages-select new_msg
​source install/setup.bash
​

To verify that your message has been created successfully, type the below command. If the structure of the Age message appears, it will mean that your message has been created successfully and it's ready to be used in your ROS programs.

ros2 msg show new_msg/Age

You should get an output like this one:

float32 years
​float32 months
​float32 days
​

Use the new Messages in a program

You will have to add to your CMakeLists.txt the following extra lines to compile and link your executable (in this example, it's called publish_age.cpp):

find_package(my_package REQUIRED)
​
​add_executable(age_publisher_node src/publish_age.cpp)
​ament_target_dependencies(age_publisher_node rclcpp std_msgs my_package)
​
​install(TARGETS
​age_publisher_node
​DESTINATION lib/${PROJECT_NAME}
​)

Practice Online

Also, you can test this tutorial in ROSDS, using a ROSject which already contains all the code described in it. You can get the ROSject by clicking on the button below:

Above: Get ROSDS ROSject

The link above will take yo a page like the below one:

Now, you just need to Sign In (or Sign Up if you don't have an account yet) to ROSDS in order to launch the ROSject.