ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

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

asked 2019-01-23 11:27:38 -0600

coenig gravatar image

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")
  add_compile_options(-Wall -Wextra -Wpedantic)
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
)

add_executable(publisher_lambda lambda.cpp)
ament_target_dependencies(publisher_lambda rclcpp std_msgs)

add_executable(publisher_member_function member_function.cpp)
ament_target_dependencies(publisher_member_function rclcpp std_msgs)

add_executable(publisher_not_composable not_composable.cpp)
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 flag offensive close merge delete

2 Answers

Sort by ยป oldest newest most voted
2

answered 2019-01-23 13:20:38 -0600

Dirk Thomas gravatar image

updated 2019-01-25 14:36:51 -0600

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")
edit flag offensive delete link more

Comments

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

coenig gravatar image coenig  ( 2019-01-23 16:09:28 -0600 )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?

coenig gravatar image coenig  ( 2019-01-24 06:39:25 -0600 )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...)

coenig gravatar image coenig  ( 2019-01-28 15:53:40 -0600 )edit

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

Dirk Thomas gravatar image Dirk Thomas  ( 2019-01-28 16:00:00 -0600 )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.

coenig gravatar image coenig  ( 2019-01-28 16:20:30 -0600 )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.

Dirk Thomas gravatar image Dirk Thomas  ( 2019-01-28 16:24:28 -0600 )edit

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

coenig gravatar image coenig  ( 2019-01-28 16:24:57 -0600 )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?

coenig gravatar image coenig  ( 2019-01-28 16:32:26 -0600 )edit
1

answered 2019-01-29 02:28:06 -0600

coenig gravatar image

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")
   add_compile_options(-Wall -Wextra -Wpedantic)
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
)

add_executable(subscriber_lambda lambda_sub.cpp)
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"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<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>
  <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>
edit flag offensive delete link more

Question Tools

2 followers

Stats

Asked: 2019-01-23 11:27:38 -0600

Seen: 8,758 times

Last updated: Jan 29 '19