Robotics StackExchange | Archived questions

Creating custom message for ros1_bridge, #include <my_msgs/MyCustom.h> fails

ROS: Kinetic, ROS2: Ardent, OS: Ubuntu 16.04

Hello! This question acts as somewhat of a continuation of my previous question. I'm trying to build a custom message to use with ros1bridge, and in the generated file `<pathtofolder>/ros2ws/build/ros1bridge/generated/mymsgsfactories.hpp, the line#include <mymsgs/MyCustom.h>is failing while trying to buildros1_bridge`. The error message is as follows:

In file included from <path_to_folder>/ros2_ws/build/ros1_bridge/generated/get_factory.cpp:25:0:
<path_to_folder>/ros2_ws/build/ros1_bridge/generated/my_msgs_factories.hpp:6:31: fatal error: my_msgs/MyCustom.h: No such file or directory
compilation terminated.
CMakeFiles/ros1_bridge.dir/build.make:156: recipe for target 'CMakeFiles/ros1_bridge.dir/generated/get_factory.cpp.o' failed

As a sanity check, in /opt/ros/kinetic/include, I created a folder called my_msgs and placed MyCustom.h in it, and then it successfully built. Only at this point is my_msgs seen in the output of ros2 run ros1_bridge dynamic_bridge --print-pairs. However, when I run dynamic_bridge after this (using MyCustom), I get the following output:

Failed to find library 'my_msgs__rosidl_typesupport_introspection_cpp'
failed to create 1to2 bridge for topic '/test_chatter' with ROS 1 type 'my_msgs/MyCustom' and ROS 2 type 'my_msgs/MyCustom': could not create publisher: type support not from this implementation, at <path_to_folder>/ros2_ws/src/ros2/rmw_fastrtps/rmw_fastrtps_cpp/src/rmw_publisher.cpp:75, at <path_to_folder>/ros2_ws/src/ros2/rcl/rcl/src/rcl/publisher.c:151

The contents of <path_to_folder>/ros2_ws/build/ros1_bridge/generated/my_msgs_factories.hpp and .cpp are as follows:

//CONTENTS OF my_msgs_factories.hpp
// generated from ros1_bridge/resource/pkg_factories.hpp.em

#include <ros1_bridge/factory.hpp>

// include ROS 1 messages
#include <my_msgs/MyCustom.h>

// include ROS 2 messages
#include <my_msgs/msg/my_custom.hpp>

namespace ros1_bridge
{

std::shared_ptr<FactoryInterface>
get_factory_my_msgs(const std::string & ros1_type_name, const std::string & ros2_type_name);
std::unique_ptr<ServiceFactoryInterface>
get_service_factory_my_msgs(const std::string & ros_id, const std::string & package_name, const std::string & service_name);
// conversion functions for available interfaces

template<>
void
Factory<
  my_msgs::MyCustom,
  my_msgs::msg::MyCustom
>::convert_1_to_2(
  const my_msgs::MyCustom & ros1_msg,
  my_msgs::msg::MyCustom & ros2_msg);

template<>
void
Factory<
  my_msgs::MyCustom,
  my_msgs::msg::MyCustom
>::convert_2_to_1(
  const my_msgs::msg::MyCustom & ros2_msg,
  my_msgs::MyCustom & ros1_msg);

}  // namespace ros1_bridge

And .cpp:

// CONTENTS OF my_msgs_factories.cpp
// generated from ros1_bridge/resource/pkg_factories.cpp.em
#include "rclcpp/rclcpp.hpp"
#include "my_msgs_factories.hpp"

// include builtin interfaces
#include <ros1_bridge/convert_builtin_interfaces.hpp>
// include ROS 1 services

// include ROS 2 services

namespace ros1_bridge
{

std::shared_ptr<FactoryInterface>
get_factory_my_msgs(const std::string & ros1_type_name, const std::string & ros2_type_name)
{
  // mapping from string to specialized template
  if (
    (ros1_type_name == my_"msgs/MyCustom" ||
     ros1_type_name == "") &&
    ros2_type_name == "my_msgs/MyCustom")
  {
    return std::make_shared<
      Factory<
        my_msgs::MyCustom,
        my_msgs::msg::MyCustom
      >
    >("my_msgs/MyCustom", ros2_type_name);
  }
  return std::shared_ptr<FactoryInterface>();
}
// conversion functions for available interfaces

template<>
void
Factory<
  my_msgs::MyCustom,
  my_msgs::msg::MyCustom
>::convert_1_to_2(
  const my_msgs::MyCustom & ros1_msg,
  my_msgs::msg::MyCustom & ros2_msg)
{
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros2_msg.positions.resize(ros1_msg.positions.size());
  // convert primitive array elements
  std::copy(
    ros1_msg.positions.begin(),
    ros1_msg.positions.end(),
    ros2_msg.positions.begin());
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros2_msg.velocities.resize(ros1_msg.velocities.size());
  // convert primitive array elements
  std::copy(
    ros1_msg.velocities.begin(),
    ros1_msg.velocities.end(),
    ros2_msg.velocities.begin());
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros2_msg.currents.resize(ros1_msg.currents.size());
  // convert primitive array elements
  std::copy(
    ros1_msg.currents.begin(),
    ros1_msg.currents.end(),
    ros2_msg.currents.begin());
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros2_msg.temperatures.resize(ros1_msg.temperatures.size());
  // convert primitive array elements
  std::copy(
    ros1_msg.temperatures.begin(),
    ros1_msg.temperatures.end(),
    ros2_msg.temperatures.begin());
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros2_msg.position_error.resize(ros1_msg.position_error.size());
  // convert primitive array elements
  std::copy(
    ros1_msg.position_error.begin(),
    ros1_msg.position_error.end(),
    ros2_msg.position_error.begin());
}
template<>
void
Factory<
  my_msgs::MyCustom,
  my_msgs::msg::MyCustom
>::convert_2_to_1(
  const my_msgs::msg::MyCustom & ros2_msg,
  my_msgs::MyCustom & ros1_msg)
{
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros1_msg.positions.resize(ros2_msg.positions.size());
 // convert primitive array elements
  std::copy(
    ros2_msg.positions.begin(),
    ros2_msg.positions.end(),
    ros1_msg.positions.begin());
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros1_msg.velocities.resize(ros2_msg.velocities.size());
  // convert primitive array elements
  std::copy(
    ros2_msg.velocities.begin(),
    ros2_msg.velocities.end(),
    ros1_msg.velocities.begin());
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros1_msg.currents.resize(ros2_msg.currents.size());
  // convert primitive array elements
  std::copy(
    ros2_msg.currents.begin(),
    ros2_msg.currents.end(),
    ros1_msg.currents.begin());
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros1_msg.temperatures.resize(ros2_msg.temperatures.size());
  // convert primitive array elements
  std::copy(
    ros2_msg.temperatures.begin(),
    ros2_msg.temperatures.end(),
    ros1_msg.temperatures.begin());
  // convert array field
  // ensure array size
  // dynamic arrays must be resized
  ros1_msg.position_error.resize(ros2_msg.position_error.size());
  // convert primitive array elements
  std::copy(
    ros2_msg.position_error.begin(),
    ros2_msg.position_error.end(),
    ros1_msg.position_error.begin());
}
std::unique_ptr<ServiceFactoryInterface>
get_service_factory_my_msgs(const std::string & ros_id, const std::string & package_name, const std::string & service_name)
{
  (void)ros_id;
  (void)package_name;
  (void)service_name;
  return nullptr;
}
}  // namespace ros1_bridge

Furthermore, the contents of <path_to_folder>/ros1_bridge_overlay/catkin_ws_overlay/src/my_msgs/CMakeLists.txt is:

cmake_minimum_required(VERSION 2.8)
project(my_msgs)
find_package(catkin REQUIRED COMPONENTS
  message_generation
  roscpp
)

add_message_files(
  FILES
  MyCustom.msg
)

generate_messages()

catkin_package(
  INCLUDE_DIRS include
  CATKIN_DEPENDS message_runtime roscpp
)
include_directories(${catkin_INCLUDE_DIRS})

add_executable(pub src/pub.cpp)
target_link_libraries(pub ${catkin_LIBRARIES})

install(
  DIRECTORY include/${PROJECT_NAME}/
  DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
)

I think my problem is that I'm not installing ROS1's version of my_msgs correctly, hence my inclusion of the CMakeLists file. Any suggestions would be much appreciated!

EDIT #1: my build process and starting up the nodes is very similar to Example #1 in ros1_bridge's README, the only differences being that I source the overlay workspaces when necessary.

I delete the build folder in ros1_bridge, and rebuild with the following process:

$ source /opt/ros/kinetic/setup.bash
$ source <path_to_folder>/ros2_ws/install/local_setup.bash //My core ros2 workspace
$ source <path_to_folder>/ros1_bridge_overlay/catkin_ws_overlay/devel/setup.bash //ROS1 ws
$ source <path_to_folder>/ros1_bridge_overlay/ros2_ws_overlay/install/local_setup.bash //ROS2 ws
$ src/ament/ament_tools/scripts/ament.py build --build-tests --symlink-install --only ros1_bridge --force-cmake-configure

EDIT #2: the above command is where the original build fails and throws the initial error.

Then, in separate terminal windows:

Starting roscore:

$ source /opt/ros/kinetic/setup.bash
$ roscore

Starting the bridge:

$ source /opt/ros/kinetic/setup.bash
$ source <path_to_folder>/ros2_ws/install/local_setup.bash //Contains ros1_bridge
$ source <path_to_folder>/ros1_bridge_overlay/catkin_ws_overlay/devel/setup.bash
$ source <path_to_folder>/ros1_bridge_overlay/ros2_ws_overlay/install/local_setup.bash
$ export ROS_MASTER_URI=http://localhost:11311
$ ros2 run ros1_bridge dynamic_bridge

Starting the ROS1 publisher:

$ source /opt/ros/kinetic/setup.bash
$ source <path_to_folder>/ros1_bridge_overlay/catkin_ws_overlay/devel/setup.bash
$ rosrun my_msgs pub

Starting the ROS2 subscriber:

$ source /opt/ros/kinetic/setup.bash
$ source <path_to_folder>/ros2_ws/install/local_setup.bash
$ source <path_to_folder>/ros1_bridge_overlay/ros2_ws_overlay/install/local_setup.bash
$ ros2 run my_msgs sub

Asked by Rob V on 2018-07-27 15:52:15 UTC

Comments

Did you source your ROS 1 workspace after building it? Please try to include the exact steps you did with which you can reproduce the problem.

Asked by Dirk Thomas on 2018-07-27 16:20:20 UTC

See my above edit... Thanks for helping out again!!

Asked by Rob V on 2018-07-27 17:00:13 UTC

Your original post mentioned that the include failed. So you shouldn't be able to build successfully. You might want to revert your manual changes to /opt/ros/kinetic to reproduce the original problem.

Asked by Dirk Thomas on 2018-07-27 17:03:58 UTC

You should only need to source a single ws per ROS version. In ROS 1 your overlay workspace will automatically source the underlay which was used to build that ws. In ROS 2 that works the same when you source the setup.* instead of the local_setup.*.

Asked by Dirk Thomas on 2018-07-27 17:05:15 UTC

Good point... However, when I revert it back, that's the same procedure I follow too. See the above minor edit at EDIT #2. Also, didn't know about that setup functionality. Good to know.

Asked by Rob V on 2018-07-27 17:08:45 UTC

Answers