ros2/ros1_bridge

How to bridge builtin_interfaces/Time to ROS1

minghaohsu410168 opened this issue · 5 comments

Hello,
I have a custom msg in ROS2 needs to be bridge to ROS1. The msg file contains builtin_interfaces/Time but in ROS1 it does not have same message type.

How should i do? Write own map yaml?
Thank you for your help!

ros1_bridge already has builtin conversions for ROS 2 builtin_interfaces/Time, which converts to and from a std_msgs/Time in ROS 1. So this should just work out-of-the-box. Can you give more details on what problem you are having?

I'm having this exact issue, so I'll outline my own problems.

I have a largish ROS 2 project with many message types within it, all using builtin_interfaces. I need to bridge these messages over to ROS 1. The fastest way for me to do this was to create a ROS 1 workspace where I copied all of the ROS 2 message packages over to the ROS 1 workspace, then convert the CMakeLists.txt and package.xml to their ROS 1 equivalents. However, since ROS 1 doesn't have the builtin_interfaces package, I had two choices:

  • I could try to figure out what the Duration and Time types map to in ROS 1 (maybe share/std_msgs/msg/(Duration.msg|Time.msg)?), and replace all uses of the types in my ROS 1 workspace with those types
  • I could copy the builtin_interfaces package into my ROS 1 workspace, and rebuild everything there.

The second choice allowed me to build all of my messages in both the ROS 1 and ROS 2 workspaces, but when I build the bridge I get linking errors. I tried to create a custom mapping using a YAML file, but I don't know how to correctly do that. If you can give us a step-by-step guide on what we need to do, it would be much appreciated.

So, an update. After looking into the source code for the bridge, I decided to try removing my copy of builtin_interfaces, and replace all ROS 1 workspace uses of builtin_interfaces/Duration with std_msgs/Duration, and likewise for Time. Unfortunately, that didn't work. Here are the errors I'm getting:

--- stderr: ros1_bridge                                            
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_2_to_1<std_msgs::Time_<std::allocator<void> >, builtin_interfaces::msg::Time_<std::allocator<void> > >(builtin_interfaces::msg::Time_<std::allocator<void> > const&, std_msgs::Time_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_1_to_2<std_msgs::Time_<std::allocator<void> >, builtin_interfaces::msg::Time_<std::allocator<void> > >(std_msgs::Time_<std::allocator<void> > const&, builtin_interfaces::msg::Time_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `ros1_bridge::Factory<arl_unity_ros::JointDescription_<std::allocator<void> >, arl_unity_ros::msg::JointDescription_<std::allocator<void> > >::convert_1_to_2(arl_unity_ros::JointDescription_<std::allocator<void> > const&, arl_unity_ros::msg::JointDescription_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_1_to_2<std_msgs::Duration_<std::allocator<void> >, builtin_interfaces::msg::Duration_<std::allocator<void> > >(std_msgs::Duration_<std::allocator<void> > const&, builtin_interfaces::msg::Duration_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `ros1_bridge::Factory<arl_unity_ros::JointDescription_<std::allocator<void> >, arl_unity_ros::msg::JointDescription_<std::allocator<void> > >::convert_2_to_1(arl_unity_ros::msg::JointDescription_<std::allocator<void> > const&, arl_unity_ros::JointDescription_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_2_to_1<std_msgs::Duration_<std::allocator<void> >, builtin_interfaces::msg::Duration_<std::allocator<void> > >(builtin_interfaces::msg::Duration_<std::allocator<void> > const&, std_msgs::Duration_<std::allocator<void> >&)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/parameter_bridge.dir/build.make:597: parameter_bridge] Error 1
make[1]: *** [CMakeFiles/Makefile2:317: CMakeFiles/parameter_bridge.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_2_to_1<std_msgs::Time_<std::allocator<void> >, builtin_interfaces::msg::Time_<std::allocator<void> > >(builtin_interfaces::msg::Time_<std::allocator<void> > const&, std_msgs::Time_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_1_to_2<std_msgs::Time_<std::allocator<void> >, builtin_interfaces::msg::Time_<std::allocator<void> > >(std_msgs::Time_<std::allocator<void> > const&, builtin_interfaces::msg::Time_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `ros1_bridge::Factory<arl_unity_ros::JointDescription_<std::allocator<void> >, arl_unity_ros::msg::JointDescription_<std::allocator<void> > >::convert_1_to_2(arl_unity_ros::JointDescription_<std::allocator<void> > const&, arl_unity_ros::msg::JointDescription_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_1_to_2<std_msgs::Duration_<std::allocator<void> >, builtin_interfaces::msg::Duration_<std::allocator<void> > >(std_msgs::Duration_<std::allocator<void> > const&, builtin_interfaces::msg::Duration_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `ros1_bridge::Factory<arl_unity_ros::JointDescription_<std::allocator<void> >, arl_unity_ros::msg::JointDescription_<std::allocator<void> > >::convert_2_to_1(arl_unity_ros::msg::JointDescription_<std::allocator<void> > const&, arl_unity_ros::JointDescription_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_2_to_1<std_msgs::Duration_<std::allocator<void> >, builtin_interfaces::msg::Duration_<std::allocator<void> > >(builtin_interfaces::msg::Duration_<std::allocator<void> > const&, std_msgs::Duration_<std::allocator<void> >&)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/static_bridge.dir/build.make:597: static_bridge] Error 1
make[1]: *** [CMakeFiles/Makefile2:182: CMakeFiles/static_bridge.dir/all] Error 2
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_2_to_1<std_msgs::Time_<std::allocator<void> >, builtin_interfaces::msg::Time_<std::allocator<void> > >(builtin_interfaces::msg::Time_<std::allocator<void> > const&, std_msgs::Time_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_1_to_2<std_msgs::Time_<std::allocator<void> >, builtin_interfaces::msg::Time_<std::allocator<void> > >(std_msgs::Time_<std::allocator<void> > const&, builtin_interfaces::msg::Time_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `ros1_bridge::Factory<arl_unity_ros::JointDescription_<std::allocator<void> >, arl_unity_ros::msg::JointDescription_<std::allocator<void> > >::convert_1_to_2(arl_unity_ros::JointDescription_<std::allocator<void> > const&, arl_unity_ros::msg::JointDescription_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_1_to_2<std_msgs::Duration_<std::allocator<void> >, builtin_interfaces::msg::Duration_<std::allocator<void> > >(std_msgs::Duration_<std::allocator<void> > const&, builtin_interfaces::msg::Duration_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `ros1_bridge::Factory<arl_unity_ros::JointDescription_<std::allocator<void> >, arl_unity_ros::msg::JointDescription_<std::allocator<void> > >::convert_2_to_1(arl_unity_ros::msg::JointDescription_<std::allocator<void> > const&, arl_unity_ros::JointDescription_<std::allocator<void> >&)'
/usr/bin/ld: libros1_bridge.so: undefined reference to `void ros1_bridge::convert_2_to_1<std_msgs::Duration_<std::allocator<void> >, builtin_interfaces::msg::Duration_<std::allocator<void> > >(builtin_interfaces::msg::Duration_<std::allocator<void> > const&, std_msgs::Duration_<std::allocator<void> >&)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/dynamic_bridge.dir/build.make:597: dynamic_bridge] Error 1
make[1]: *** [CMakeFiles/Makefile2:398: CMakeFiles/dynamic_bridge.dir/all] Error 2
make: *** [Makefile:141: all] Error 2
---
Failed   <<< ros1_bridge [38min 12s, exited with code 2]

Any suggestions would be much appreciated.

@minghaohsu410168 it appears that the ROS 1 equivalent of builtin_interfaces/Time is just time, and builtin_interfaces/Duration is just duration. I just got it working in my project and haven't had a chance to properly test it, so it might not work for you.

Thanks @ckaran, with your comment I managed to get it working as well.

In case you want don't want to have separate ros1 and ros2 message packages, I came up with a solution that allows building the same package with colcon and catkin.

I use cmake to define the correct duration type in the configure phase.

If there's a simpler solution, let me know but the following works just fine:

package.xml
<?xml version='1.0' encoding='utf-8'?>
<package format="3">
  <name>custom_msgs</name>

  <!-- Other meta data here -->

  <!-- ROS1 -->
  <buildtool_depend condition="$ROS_VERSION == 1">catkin</buildtool_depend>
  <build_depend condition="$ROS_VERSION == 1">message_generation</build_depend>
  <exec_depend condition="$ROS_VERSION == 1">message_runtime</exec_depend>

  <!-- ROS2 -->
  <buildtool_depend condition="$ROS_VERSION == 2">ament_cmake</buildtool_depend>
  <build_depend condition="$ROS_VERSION == 2">rosidl_default_generators</build_depend>
  <exec_depend condition="$ROS_VERSION == 2">rosidl_default_runtime</exec_depend>
  <member_of_group condition="$ROS_VERSION == 2">rosidl_interface_packages</member_of_group>

  <!-- other dependencies here, using geometry_msgs throughout this example to show where else we need it -->
  <depend>geometry_msgs</depend>

  <export>
    <build_type condition="$ROS_VERSION == 1">catkin</build_type>
    <build_type condition="$ROS_VERSION == 2">ament_cmake</build_type>
  </export>

</package>

CMakeLists.txt
cmake_minimum_required(VERSION 3.5.1)
project(custom_msgs)

find_package(ros_environment REQUIRED QUIET)
set(ROS_VERSION $ENV{ROS_VERSION})

if(${ROS_VERSION} EQUAL 2)

    find_package(ament_cmake REQUIRED)
    find_package(rosidl_default_generators REQUIRED)

    find_package(geometry_msgs REQUIRED)

    ament_export_dependencies(
        geometry_msgs
    )

    # Use cmake to set the correct duration type
    set(DURATION_TYPE "builtin_interfaces/Duration")
    configure_file(msg/Custom.msg.in msg/Custom.msg @ONLY)

    # The rosidl_generate_interfaces macro allows absolute paths if there a passed with a colon separating the relative path
    # No need to manually install anything for ROS2, the macro handles it just fine
    set(msg_files
        "${CMAKE_BINARY_DIR}:msg/Custom.msg"
        "msg/SomeOtherMessage.msg
    )

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

    ament_package()

elseif(${ROS_VERSION} EQUAL 1)

    find_package(catkin REQUIRED COMPONENTS
        geometry_msgs
        message_generation
    )

    # Configure the file for the ROS1 duration type
    set(DURATION_TYPE "duration")
    configure_file(msg/Custom.msg.in msg/Custom.msg @ONLY)

    # Unfortunately, this macro doesn't handle the installation very well, when an absolute path is given
    # Therefore, we'll just not install the message at this point and do it manually later
    add_message_files(
        DIRECTORY "${CMAKE_BINARY_DIR}/msg"
        FILES "Custom.msg"
        NOINSTALL
    )

    # Add other message files like you normally would. Only the ones with "duration" or "time" need special handling
    add_message_files(
        DIRECTORY msg
        FILES
            "SomeOtherMessage.msg"
    )

    generate_messages(DEPENDENCIES
        geometry_msgs
    )

    catkin_package(CATKIN_DEPENDS
        geometry_msgs
    )

    # Here we manually install the generated message file
    install(
	  FILES "${CMAKE_BINARY_DIR}/msg/Custom.msg"
   	  DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/msg
    )

else()
    message(FATAL_ERROR "Unsupported ROS version detected: ROS_VERSION ${ROS_VERSION}. Only ROS_VERSION 1 and 2 are supported.")
endif()
msg/Custom.msg.in
# Use this cmake syntax to replace the type during the configure phase
@DURATION_TYPE@ duration

# Other types can be defined like they normally would
std_msgs/Header header