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

内容分享2天前发布
0 0 0

目录

  • 〇、概述
  • 一、自定义消息(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,才能由该包处理:

<export> 
  <ros1_bridge mapping_rules="my_mapping_rules.yaml"/> 
</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,才能由该包处理:

<export> 
  <ros1_bridge mapping_rules="my_mapping_rules.yaml"/> 
</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 <workspace-parent-path>/ros1_msgs_ws
catkin_make_isolated --install

  • 编译ROS 2消息

source /opt/ros/crystal/setup.bash
cd <workspace-parent-path>/ros2_msgs_ws
colcon build --packages-select bridge_msgs

  • 编译ros1_bridge

source /opt/ros/melodic/setup.bash
source /opt/ros/crystal/setup.bash
source <workspace-parent-path>/ros1_msgs_ws/install_isolated/setup.bash
source <workspace-parent-path>/ros2_msgs_ws/install/local_setup.bash
cd <workspace-parent-path>/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 <workspace-parent-path>/ros2_msgs_ws/install/local_setup.bash
source ros1;
source ros2;
source <workspace-parent-path>/bridge_ws/install/local_setup.bash
source ros1;
source <workspace-parent-path>/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

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>custom_msgs</name>
  <version>0.0.0</version>
  <description>The custom_msgs package</description>

  <maintainer email="weibw@todo.todo">weibw</maintainer>

  <license>TODO</license>

  <depend>rclpy</depend>
  <depend>builtin_interfaces</depend>
  <depend>rosidl_default_generators</depend>

  <member_of_group>rosidl_interface_packages</member_of_group>

  <export>
    <build_type>ament_cmake</build_type>
    <ros1_bridge mapping_rules="mapping_rules.yaml"/>
  </export>
</package>

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

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...