【ROS2概念】系列(十五)——ROS 1 和 ROS 2 之间的自定义类型的桥接通信

目录

  • 〇、概述
  • 一、自定义消息(msg)类型
    • 1.1 ROS 1 和ROS 2 消息(msg)的相互关联步骤
    • 1.2 为消息(msg)指定自定义映射规则
    • 1.3 示例映射规则文件
    • 1.4 安装映射规则文件
  • 二、自定义服务(srv)类型
    • 2.1 ROS 1 和ROS 2 服务(service)的相互关联步骤
    • 2.2 为服务(srv)指定自定义映射规则
    • 2.3 示例映射规则文件
    • 2.4 安装映射规则文件
  • 三、ros1_bridge桥接自定义接口
    • 3.1 工作区文件目录结构
    • 3.2 编译
    • 3.3 运行
  • 四、从外部包定义外部包的映射规则
  • 五、完整的自定义消息功能包
    • 5.1 TestFoxy.msg
    • 5.2 mapping_rules.yaml
    • 5.3 CMakeLists.txt
    • 5.4 package.xml
    • 5.5 编译ros2下的custom_msgs功能包

〇、概述

使用自定义消息(msg)和服务(srv)类型作为通讯接口时,需要分别在ROS1和ROS2消息包内定义消息类型,然后在ROS2的消息包内定义yaml映射规则文件,最后将ROS1_bridge必须用源代码重新编译,

一、自定义消息(msg)类型

1.1 ROS 1 和ROS 2 消息(msg)的相互关联步骤

ROS 1 和ROS 2 消息(msg)之间的自动映射是根据名称执行的。

  • 第一步:_msgs名称结尾的 ROS 1 包与_msgs_msgs_interfaces名称结尾的 ROS 2 包相关联。

  • 第二步:在ROS1和ROS2的消息(msg)包相关联后,包内具有相同名称的消息(msg)进一步相互关联。

  • 第三步:在ROS1和ROS2的相同名称的消息(msg)相互关联后,则对相同名称消息(msg)内的相同名称字段进一步相互关联。

注意:

  • 如果两条关联消息(msg)之一的字段不是另一条消息(msg)的一部分,则它们将被忽略;
  • 如果两个消息(msg)中均包含彼此不存在的字段,则会判断映射不完整并且无法建立关联。

1.2 为消息(msg)指定自定义映射规则

ROS 2 包使用yaml文件提供映射规则,映射规则具三种类型,分别是包映射规则、消息映射规则、字段映射规则。

  • 包映射规则:
ros1_package_name
ros2_package_name

ROS1的包名称和ROS2包名称对应,表示彼此相互映射

默认情况下,ros2_package_name必须与定义此映射规则的 ROS 2 包相同。

  • 消息(msg)映射规则:
ros1_message_name.msg
ros2_message_name.msg

ROS1包中的消息(msg)名称和ROS2包中的消息(msg)名称对应,表示彼此相互映射

  • 字段映射规则:
  fields_1_to_2: 
    foo: 'foo' 
    ros1_bar: 'ros2_bar'

fields_1_to_2:将 ROS 1 消息(msg)内的字段映射到 ROS 2 消息(msg)内的字段。

消息(msg)要映射的字段什么呢?

消息(msg)要映射的字段是.分隔的消息类型字段名称,如消息类型header.stampheaderstamp均是字段。在映射时,会递归映射该字段的所有子数据成员,即字段指定从.开始一直到最底层的所有子字段。例如,映射header时,则stamp也会自动被映射,但映射stamp时,header的其它子字段不会被映射

在映射时,所有字段都必须明确列出,未列出的字段在名称匹配时不会隐式映射。

1.3 示例映射规则文件

ROS2提供包、消息、字段三种不同层次的映射规则。

  • 映射ROS1和ROS2包内名称和字段相同的所有消息(msg)

my_mapping_rules.yaml

- 
  ros1_package_name: 'ros1_pkg_name' 
  ros2_package_name: 'ros2_pkg_name'
  • 映射ROS1和ROS2包内字段相同的特定消息(msg)

my_mapping_rules.yaml

- 
  ros1_package_name: 'ros1_pkg_name' 
  ros1_message_name: 'ros1_msg_name' 
  ros2_package_name: 'ros2_pkg_name' 
  ros2_message_name: 'ros2_msg_name'
  • 映射ROS1和ROS2包内特定消息(msg)内的相同字段
-
  ros1_package_name: 'ros1_pkg_name'
  ros1_message_name: 'ros1_msg_name'
  ros2_package_name: 'ros2_pkg_name'
  ros2_message_name: 'ros2_msg_name'
  fields_1_to_2:
    foo: 'foo'
    ros1_bar: 'ros2_bar'

注意:ROS1和ROS2的消息包的文件名和内部变量名不一样,所以需要定义映射文件my_mapping_rules.yaml;若文件名和内部变量名一样,不需要定义映射文件my_mapping_rules

1.4 安装映射规则文件

如果存在my_mapping_rules.yaml文件,则yaml文件还必须在以_msgs_msgs_interfaces名称结尾的 ROS 2消息包CMakeLists.txt中进行install

install( FILES my_mapping_rules.yaml 
               DESTINATION share/${PROJECT_NAME})

映射规则文件必须在以_msgs_msgs_interfaces名称结尾的 ROS 2消息包中的package.xml进行export,才能由该包处理:

 
   

二、自定义服务(srv)类型

ROS 1 和 2 服务(service)之间的映射类似于消息(msg),但服务不支持名称不同的字段进行映射,即ROS1和ROS2的服务包内.srv文件中定义的服务名称必须一样

2.1 ROS 1 和ROS 2 服务(service)的相互关联步骤

ROS 1 和ROS 2 服务(service)之间的自动映射是根据名称执行的。

  • 第一步:_srvs名称结尾的 ROS 1 包与_srvs_srvs_interfaces名称结尾的 ROS 2 包相关联。

  • 第二步:在ROS1和ROS2的服务(service)包相关联后,包内具有相同名称的服务(srv)进一步相互关联。

  • 第三步:在ROS1和ROS2的相同名称的服务(service)相互关联后,则对相同名称服务(srv)内的相同名称字段进一步相互关联。

注意:ROS1和ROS2的服务包内.srv文件中定义的服务名称必须一样

2.2 为服务(srv)指定自定义映射规则

ROS 2 包使用yaml文件提供映射规则,映射规则具两种类型,分别是包映射规则、服务映射规则。

  • 包映射规则:
ros1_package_name
ros2_package_name

ROS1的包名称和ROS2包名称对应,表示彼此相互映射

默认情况下,ros2_package_name必须与定义此映射规则的 ROS 2 包相同。

  • 服务(srv)映射规则:
ros1_service_name.srv
ros1_service_name.srv
  • 服务目前不支持自定义字段映射

即不支持字段名称不同的服务进行相互映射,但字段名称相同的服务仍然可以相互映射。

2.3 示例映射规则文件

ROS2提供包、服务两种不同层次的映射规则。

  • 映射ROS1和ROS2包内名称和字段相同的所有服务(srv)

my_mapping_rules.yaml

- 
  ros1_package_name: 'ros1_pkg_name' 
  ros2_package_name: 'ros2_pkg_name'
  • 映射ROS1和ROS2包内字段相同的特定服务(srv)

my_mapping_rules.yaml

- 
  ros1_package_name: 'ros1_pkg_name' 
  ros1_service_name: 'ros1_srv_name' 
  ros2_package_name: 'ros2_pkg_name' 
  ros2_service_name: 'ros2_srv_name'
  • 映射ROS1和ROS2包内特定服务(srv)内的相同字段
-
  ros1_package_name: 'ros1_pkg_name'
  ros1_service_name: 'ros1_srv_name'
  ros2_package_name: 'ros2_pkg_name'
  ros2_service_name: 'ros2_srv_name'
  request_fields_1_to_2:
    foo: 'foo'
    ros_bar: 'ros_bar'
  response_fields_1_to_2:
    foo: 'foo'
    ros_bar: 'ros_bar'

2.4 安装映射规则文件

如果存在my_mapping_rules.yaml文件,则yaml文件还必须在以_srvs__srvs_interfaces名称结尾的 ROS 2服务包CMakeLists.txt中进行install

install( FILES my_mapping_rules.yaml 
               DESTINATION share/${PROJECT_NAME})

映射规则文件必须在以_srvs_srvs_interfaces名称结尾的 ROS 2服务包中的package.xml进行export,才能由该包处理:

 
   

三、ros1_bridge桥接自定义接口

ROS 1 和 ROS 2 消息(msg)和服务(srv)包需要位于单独的工作区中,以便每个工作区都可以获取其相应的 ROS1/ROS2环境变量,ros1_bridge应该在它自己的工作区中,因为它需要同时获取 ROS 1 和 ROS 2 版本。

3.1 工作区文件目录结构

测试自定义的消息类型需要三个工作区,分别是

  • ROS 1 工作区:ros1_msgs_ws
  • ROS 2 工作区:ros2_msgs_ws
  • ros1_bridge工作区:bridge_ws

ROS 1 和 ROS 2 自定义接口的包、消息和字段使用相同的名称。

目录布局如下所示:

.
├─ ros1_msgs_ws/
│  └─ src/
│     └─ bridge_msgs/
│        └─ msg/
│           └─ JointCommand.msg
├─ ros2_msgs_ws/
│  └─ src/
│     └─ bridge_msgs/
│        ├─ msg/
│        │  └─ JointCommand.msg
│        └─ my_mapping_rules.yaml
└─ bridge_ws/
   └─ src/
      └─ ros1_bridge

其中JointCommand.msg文件内容是:

float64 position

注意:ros2的msg文件命名有正则表达式的规定,要以大写字母开头,文件内容中的每个变量不可以存在大写字母,更详细的规则请参考官方文档。

3.2 编译

  • 编译ROS 1消息
source /opt/ros/melodic/setup.bash
cd /ros1_msgs_ws
catkin_make_isolated --install
  • 编译ROS 2消息
source /opt/ros/crystal/setup.bash
cd /ros2_msgs_ws
colcon build --packages-select bridge_msgs
  • 编译ros1_bridge
source /opt/ros/melodic/setup.bash
source /opt/ros/crystal/setup.bash
source /ros1_msgs_ws/install_isolated/setup.bash
source /ros2_msgs_ws/install/local_setup.bash
cd /bridge_ws
colcon build --packages-select ros1_bridge --cmake-force-configure
  • 查看ROS1/2消息是否配对成功

通过打印所有桥接类型对来验证自定义类型是否被ros1_bridge识别:

ros2 run ros1_bridge dynamic_bridge --print-pairs

3.3 运行

打开四个终端,分别运行下列命令:

Terminal 1 Terminal 2 Terminal 3 Terminal 4
ROS环境变量 source ros1 source ros2;
source /ros2_msgs_ws/install/local_setup.bash
source ros1;
source ros2;
source /bridge_ws/install/local_setup.bash
source ros1;
source /ros1_msgs_ws/install_isolated/setup.bash
命令 roscore ros2 topic pub /joint_command bridge_msgs/JointCommand "{position: 0.123}" ros2 run ros1_bridge dynamic_bridge --bridge-all-topics rostopic list;
rostopic echo /joint_command

四、从外部包定义外部包的映射规则

在前面的部分中,已经指出 ros2_package_name 映射规则必须与定义映射规则的 ROS 2 包相同。虽然建议这样做以防止冲突和/或重复规则,但可以覆盖检查 使用 enable_foreign_mappings字段强制执行此操作。

这意味着,对于 yaml 文件中定义的每个包映射规则,将跳过对 ROS 2 包名称相等性的检查。 再次,请注意,这是一种应负责任地使用的黑暗艺术,请务必小心! 如果存在冲突的映射规则,则使用按排序顺序解析的最后一个! 这通常很难预测,所以要非常小心!

enable_foreign_mappings设置为true后,可以为 ROS 2 包指定与映射规则文件所在的包不同的映射规则:

-
  enable_foreign_mappings: true
  ros1_package_name: 'ros1_pkg_name'
  ros1_service_name: 'ros1_srv_name'
  ros2_package_name: 'ros2_FOREIGN_pkg_name'  # the package with the service definition
  ros2_service_name: 'ros2_srv_name'

同时还必须使ros1_bridge_foreign_mapping资源可用于映射包的 CMakeLists.txt 中的索引。 放置该行的好地方是在映射规则的安装规则之前执行此操作,如下所示:

-
  ament_index_register_resource("ros1_bridge_foreign_mapping")
  install(
    FILES YOUR_MAPPING_RULE_FILE.yaml
    DESTINATION share/${PROJECT_NAME})

注意:在这种情况下,应该在 `ros2_package_name 中输入的包名称是带有消息定义的包的名称。 实际上,它是一个外部包,相对于在其中定义映射规则的映射包。

一个示例目录布局如下所示:

.
├─ ros1_msgs_ws
│  └─ src
│     └─ ros1_bridge_msgs
│        └─ msg
│           └─ ros1_msg_name.msg
├─ ros2_msgs_ws
│  └─ src
│     └─ ros2_bridge_msgs
│     │  ├─ msg
│     │  │  └─ ros2_msg_name.msg
│     └─ ros2_bridge_mappings
│        └─ my_mapping_rules.yaml
└─ bridge_ws
   └─ src
      └─ ros1_bridge

在上面的示例中,映射规则如下所示:

-
  enable_foreign_mappings: true
  ros1_package_name: 'ros1_bridge_msgs'
  ros1_message_name: 'ros1_msg_name'
  ros2_package_name: 'ros2_bridge_msgs'  # this is a foreign package, relative to ros2_bridge_mappings!
  ros2_message_name: 'ros2_msg_name'

五、完整的自定义消息功能包

5.1 TestFoxy.msg

string test_str1
int32 test_int1

5.2 mapping_rules.yaml

-
  ros1_package_name: 'custom_msgs'
  ros1_message_name: 'TestNoetic'
  ros2_package_name: 'custom_msgs'
  ros2_message_name: 'TestFoxy'
  fields_1_to_2:
    test_str1: 'test_str2'
    test_int1: 'test_int2'

5.3 CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(custom_msgs)

# 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(builtin_interfaces REQUIRED)
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(custom_msgs
    msg/TestFoxy.msg
  DEPENDENCIES
    builtin_interfaces
)

install(FILES mapping_rules.yaml
  DESTINATION share/${PROJECT_NAME}
)

ament_export_dependencies(rosidl_default_runtime)

ament_package()

5.4 package.xml




  custom_msgs
  0.0.0
  The custom_msgs package

  weibw

  TODO

  rclpy
  builtin_interfaces
  rosidl_default_generators

  rosidl_interface_packages

  
    ament_cmake
    
  

5.5 编译ros2下的custom_msgs功能包

source /opt/ros/foxy/setup.bash
cd colcon_ws
colcon build --packages-select custom_msgs

参考:
在ros2下使用ros1_bridge与ros1自定义消息桥接:https://blog.csdn.net/weixin_38274206/article/details/123062134
ros1_bridge官方文档:https://github.com/ros2/ros1_bridge/blob/master/doc/index.rst

你可能感兴趣的:(【ROS2概念】系列(十五)——ROS 1 和 ROS 2 之间的自定义类型的桥接通信)