ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | Q&A answers.ros.org

# How to include generated message .h file into C++ (ROS 2)?

I am new to ROS 2, coming from ROS, currently using crystal to try and learn the basics.

I want to just simply create a custom message and use it in the minimal publisher/subscriber example from here, instead of the default string message used there. So far, I have put a message file LNumM.msg with the content:

int64 num


into the msg folder of the publisher package. Using the below CMakeLists.txt and package.xml, the package compiles with colcon build.

CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)
project(examples_rclcpp_minimal_publisher)

# 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")
endif()

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

set(msg_files
"msg/LNumM.msg"
)

rosidl_generate_interfaces(${PROJECT_NAME}${msg_files}
DEPENDENCIES std_msgs
)

ament_target_dependencies(publisher_lambda rclcpp std_msgs)

ament_target_dependencies(publisher_member_function rclcpp std_msgs)

ament_target_dependencies(publisher_not_composable rclcpp std_msgs)

install(TARGETS
publisher_lambda
publisher_member_function
publisher_not_composable
DESTINATION lib/${PROJECT_NAME} ) ament_package()  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>examples_rclcpp_minimal_subscriber</name> <version>0.6.1</version> <description>Examples of minimal subscribers</description> <maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer> <license>Apache License 2.0</license> <author>Mikael Arguedas</author> <author>Morgan Quigley</author> <buildtool_depend>ament_cmake</buildtool_depend> <buildtool_depend>rosidl_default_generators</buildtool_depend> <build_depend>rclcpp</build_depend> <build_depend>std_msgs</build_depend> <member_of_group>rosidl_interface_packages</member_of_group> <exec_depend>rosidl_default_runtime</exec_depend> <exec_depend>rclcpp</exec_depend> <exec_depend>std_msgs</exec_depend> <export> <build_type>ament_cmake</build_type> </export> </package>  However, I don't know how to now include the message into the C++ code of the publisher (or subscriber) to actually publish and receive messages of this type. In ROS, the generated C++ header is placed in the devel folder, and I can simply include it via (adapting a similar ROS 1 example): #include "beginner_tutorials/LNumM.h"  In the install folder of my colcon workspace, I couldn't find any file called LNumM.h, although there are many files with similar names such as LNumM_.h. I know that ROS 2 uses the IDL to encode messages, and there are also several generated idl files in the install folder. But I would expect to get a generated C++ header file as well by using rosidl_generate_interfaces. I have tried some random things like: #include "examples_rclcpp_minimal_subscriber/msg/dds_opensplice/LNumM_.h"  But they did not work (and I didn't actually expect them to). Maybe someone can push me in the right direction? edit retag close merge delete ## 2 Answers Sort by » oldest newest most voted The example you are referring to already uses a message header ( https://github.com/ros2/examples/blob... ) which you can use as a template to find yours: #include "std_msgs/msg/string.hpp"  That file is generated from std_msgs/msg/String.msg. In ROS 2 all C++ headers are ending in .hpp (rather than .h which is used for C). Also to conform with the Google style guide all filenames use lowercase/underscore. So your file will be named examples_rclcpp_minimal_publisher/msg/l_num_m.hpp. Update: You will also need to update the CMake code to tell the executable targets where to find the headers: rosidl_target_interfaces(publisher_lambda${PROJECT_NAME} "rosidl_typesupport_cpp")

more

You're right, that file exists. I have to go now, but I'll test it tomorrow - thanks a lot!

( 2019-01-23 16:09:28 -0500 )edit

Sorry to bother again... But I'm still not able to actually include the file. I thought now that I know which it is it should be easy, but things like #include examples_rclcpp_minimal_publisher/msg/l_num_m.hpp (and many similar things I tried) are not working (except abs. path). What am I missing?

( 2019-01-24 06:39:25 -0500 )edit

Thanks a lot for the update, that works, too! Now one last question - how can I let the subscriber look up the SAME message? (I always end up with a copy. I'm not so good in CMake, so these are no "do my homework" questions, I'm really tryin in between... :-) And ROS2 doc is sparse...)

( 2019-01-28 15:53:40 -0500 )edit

I don't know what you mean with "SAME message / always end up with a copy"?

( 2019-01-28 16:00:00 -0500 )edit

I mean I want the subscriber to use the same message type given by examples_rclcpp_minimal_publisher/msg/l_num_m.hpp. So far I only managed to let the subscriber generate its own header from the publisher's msg file, but it doesn't receive it when I run both, since it's in a different namespace.

( 2019-01-28 16:20:30 -0500 )edit

If you want both packages to use the same message definition you should move the message and it's generation into a third package which is then used by your publisher and subscriber packages. That is exactly how the demos are structured.

( 2019-01-28 16:24:28 -0500 )edit

Including examples_rclcpp_minimal_publisher/msg/l_num_m.hpp in the subscriber doesn't work.

( 2019-01-28 16:24:57 -0500 )edit
1

Ok, I'll look into it then. I thought it would be easiest to start with the minimal example. Isn't it at all possible like I tried, do you necessarily require the third package? I know it's nicer, but just for the sake of getting it running?

( 2019-01-28 16:32:26 -0500 )edit

Ok, just for ROS2 beginners like myself, here is what I ended up doing to get this minimal publisher/subscriber example with a custom message running.

First, it gets much simpler when using only one single package that contains both the publisher and the subscriber. Based on the example packages and the very helpful remarks of @Dirk Thomas, I came up with the following files which I put in a src/minimal_chatter folder:

• lambda_pub.cpp
• lambda_sub.cpp
• msg/LNumM.msg

and of course

• CMakeLists.txt
• package.xml

Here is the content of the files:

lambda_pub.cpp

#include <chrono>
#include "rclcpp/rclcpp.hpp"
#include "minimal_chatter/msg/l_num_m.hpp"

using namespace std::chrono_literals;

class MinimalPublisher : public rclcpp::Node
{
public:
MinimalPublisher()
: Node("minimal_publisher"), count_(0)
{
publisher_ = this->create_publisher<minimal_chatter::msg::LNumM>("chatter");
auto timer_callback =
[this]() -> void {
auto message = minimal_chatter::msg::LNumM();
message.num = this->count_++;
RCLCPP_INFO(this->get_logger(), "Publishing: '%d'", message.num);
this->publisher_->publish(message);
};
timer_ = this->create_wall_timer(500ms, timer_callback);
}

private:
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<minimal_chatter::msg::LNumM>::SharedPtr publisher_;
size_t count_;
};

int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}


lambda_sub.cpp

#include <iostream>
#include "rclcpp/rclcpp.hpp"
#include "minimal_chatter/msg/l_num_m.hpp"

class MinimalSubscriber : public rclcpp::Node
{
public:
MinimalSubscriber()
: Node("minimal_subscriber")
{
subscription_ = this->create_subscription<minimal_chatter::msg::LNumM>(
"chatter",
[this](minimal_chatter::msg::LNumM::UniquePtr msg) {
RCLCPP_INFO(this->get_logger(), "I heard: '%d'", msg->num);
});
}

private:
rclcpp::Subscription<minimal_chatter::msg::LNumM>::SharedPtr subscription_;
};

int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalSubscriber>());
rclcpp::shutdown();
return 0;
}


msg/LNumM.msg

int64 num


CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(minimal_chatter)

if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
endif()

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

set(msg_files
"msg/LNumM.msg"
)

rosidl_generate_interfaces(${PROJECT_NAME}${msg_files}
DEPENDENCIES std_msgs
)

ament_target_dependencies(subscriber_lambda rclcpp std_msgs)
rosidl_target_interfaces(subscriber_lambda ${PROJECT_NAME} "rosidl_typesupport_cpp") add_executable(publisher_lambda lambda_pub.cpp) ament_target_dependencies(publisher_lambda rclcpp std_msgs) rosidl_target_interfaces(publisher_lambda${PROJECT_NAME} "rosidl_typesupport_cpp")

ament_package()

install(TARGETS
subscriber_lambda
publisher_lambda
DESTINATION lib/\${PROJECT_NAME}
)


package.xml

<?xml version="1.0"?>
<package format="3">
<name>minimal_chatter</name>
<version>0.6.1</version>
<description>Examples of a minimal publisher/subscriber pair</description>
<maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer>
<author>Mikael Arguedas</author>
<author>Morgan Quigley</author>

<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>

<build_depend>rclcpp</build_depend>
<build_depend>std_msgs</build_depend>

<member_of_group>rosidl_interface_packages</member_of_group>

<exec_depend>rosidl_default_runtime</exec_depend>
<exec_depend>rclcpp</exec_depend>
<exec_depend>std_msgs</exec_depend>

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

more