【ROS2概念】系列(十五)——ROS 1 和 ROS 2 之間的自定義類型的橋接通信

目錄

〇、概述

使用自定義消息(msg)和服務(wù)(srv)類型作為通訊接口時,需要分別在ROS1和ROS2消息包內(nèi)定義消息類型,然后在ROS2的消息包內(nèi)定義yaml映射規(guī)則文件,最后將ROS1_bridge必須用源代碼重新編譯,

一、自定義消息(msg)類型

1.1 ROS 1 和ROS 2 消息(msg)的相互關(guān)聯(lián)步驟

ROS 1 和ROS 2 消息(msg)之間的自動映射是根據(jù)名稱執(zhí)行的。

  • 第一步:_msgs名稱結(jié)尾的 ROS 1 包與_msgs_msgs_interfaces名稱結(jié)尾的 ROS 2 包相關(guān)聯(lián)。

  • 第二步:在ROS1和ROS2的消息(msg)包相關(guān)聯(lián)后,包內(nèi)具有相同名稱的消息(msg)進一步相互關(guān)聯(lián)。

  • 第三步:在ROS1和ROS2的相同名稱的消息(msg)相互關(guān)聯(lián)后,則對相同名稱消息(msg)內(nèi)的相同名稱字段進一步相互關(guān)聯(lián)。

注意:

  • 如果兩條關(guān)聯(lián)消息(msg)之一的字段不是另一條消息(msg)的一部分,則它們將被忽略;
  • 如果兩個消息(msg)中均包含彼此不存在的字段,則會判斷映射不完整并且無法建立關(guān)聯(lián)。

1.2 為消息(msg)指定自定義映射規(guī)則

ROS 2 包使用yaml文件提供映射規(guī)則,映射規(guī)則具三種類型,分別是包映射規(guī)則、消息映射規(guī)則、字段映射規(guī)則。

  • 包映射規(guī)則:
ros1_package_name
ros2_package_name

ROS1的包名稱和ROS2包名稱對應(yīng),表示彼此相互映射。

默認情況下,ros2_package_name必須與定義此映射規(guī)則的 ROS 2 包相同。

  • 消息(msg)映射規(guī)則:
ros1_message_name.msg
ros2_message_name.msg

ROS1包中的消息(msg)名稱和ROS2包中的消息(msg)名稱對應(yīng),表示彼此相互映射。

  • 字段映射規(guī)則:
  fields_1_to_2: 
    foo: 'foo' 
    ros1_bar: 'ros2_bar'

fields_1_to_2:將 ROS 1 消息(msg)內(nèi)的字段映射到 ROS 2 消息(msg)內(nèi)的字段。

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

消息(msg)要映射的字段是.分隔的消息類型字段名稱,如消息類型header.stampheaderstamp均是字段。在映射時,會遞歸映射該字段的所有子數(shù)據(jù)成員,即字段指定從.開始一直到最底層的所有子字段。例如,映射header時,則stamp也會自動被映射,但映射stamp時,header的其它子字段不會被映射

在映射時,所有字段都必須明確列出,未列出的字段在名稱匹配時不會隱式映射。

1.3 示例映射規(guī)則文件

ROS2提供包、消息、字段三種不同層次的映射規(guī)則。

  • 映射ROS1和ROS2包內(nèi)名稱和字段相同的所有消息(msg)

my_mapping_rules.yaml

- 
  ros1_package_name: 'ros1_pkg_name' 
  ros2_package_name: 'ros2_pkg_name'
  • 映射ROS1和ROS2包內(nèi)字段相同的特定消息(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包內(nèi)特定消息(msg)內(nèi)的相同字段
-
  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的消息包的文件名和內(nèi)部變量名不一樣,所以需要定義映射文件my_mapping_rules.yaml;若文件名和內(nèi)部變量名一樣,不需要定義映射文件my_mapping_rules。

1.4 安裝映射規(guī)則文件

如果存在my_mapping_rules.yaml文件,則yaml文件還必須在以_msgs_msgs_interfaces名稱結(jié)尾的 ROS 2消息包CMakeLists.txt中進行install

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

映射規(guī)則文件必須在以_msgs_msgs_interfaces名稱結(jié)尾的 ROS 2消息包中的package.xml進行export,才能由該包處理:

<export> 
  <ros1_bridge mapping_rules="my_mapping_rules.yaml"/> 
</export>

二、自定義服務(wù)(srv)類型

ROS 1 和 2 服務(wù)(service)之間的映射類似于消息(msg),但服務(wù)不支持名稱不同的字段進行映射,即ROS1和ROS2的服務(wù)包內(nèi).srv文件中定義的服務(wù)名稱必須一樣。

2.1 ROS 1 和ROS 2 服務(wù)(service)的相互關(guān)聯(lián)步驟

ROS 1 和ROS 2 服務(wù)(service)之間的自動映射是根據(jù)名稱執(zhí)行的。

  • 第一步:_srvs名稱結(jié)尾的 ROS 1 包與_srvs_srvs_interfaces名稱結(jié)尾的 ROS 2 包相關(guān)聯(lián)。

  • 第二步:在ROS1和ROS2的服務(wù)(service)包相關(guān)聯(lián)后,包內(nèi)具有相同名稱的服務(wù)(srv)進一步相互關(guān)聯(lián)。

  • 第三步:在ROS1和ROS2的相同名稱的服務(wù)(service)相互關(guān)聯(lián)后,則對相同名稱服務(wù)(srv)內(nèi)的相同名稱字段進一步相互關(guān)聯(lián)。

注意:ROS1和ROS2的服務(wù)包內(nèi).srv文件中定義的服務(wù)名稱必須一樣。

2.2 為服務(wù)(srv)指定自定義映射規(guī)則

ROS 2 包使用yaml文件提供映射規(guī)則,映射規(guī)則具兩種類型,分別是包映射規(guī)則、服務(wù)映射規(guī)則。

  • 包映射規(guī)則:
ros1_package_name
ros2_package_name

ROS1的包名稱和ROS2包名稱對應(yīng),表示彼此相互映射。

默認情況下,ros2_package_name必須與定義此映射規(guī)則的 ROS 2 包相同。

  • 服務(wù)(srv)映射規(guī)則:
ros1_service_name.srv
ros1_service_name.srv
  • 服務(wù)目前不支持自定義字段映射

即不支持字段名稱不同的服務(wù)進行相互映射,但字段名稱相同的服務(wù)仍然可以相互映射。

2.3 示例映射規(guī)則文件

ROS2提供包、服務(wù)兩種不同層次的映射規(guī)則。

  • 映射ROS1和ROS2包內(nèi)名稱和字段相同的所有服務(wù)(srv)

my_mapping_rules.yaml

- 
  ros1_package_name: 'ros1_pkg_name' 
  ros2_package_name: 'ros2_pkg_name'
  • 映射ROS1和ROS2包內(nèi)字段相同的特定服務(wù)(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包內(nèi)特定服務(wù)(srv)內(nèi)的相同字段
-
  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 安裝映射規(guī)則文件

如果存在my_mapping_rules.yaml文件,則yaml文件還必須在以_srvs__srvs_interfaces名稱結(jié)尾的 ROS 2服務(wù)包CMakeLists.txt中進行install

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

映射規(guī)則文件必須在以_srvs_srvs_interfaces名稱結(jié)尾的 ROS 2服務(wù)包中的package.xml進行export,才能由該包處理:

<export> 
  <ros1_bridge mapping_rules="my_mapping_rules.yaml"/> 
</export>

三、ros1_bridge橋接自定義接口

ROS 1 和 ROS 2 消息(msg)和服務(wù)(srv)包需要位于單獨的工作區(qū)中,以便每個工作區(qū)都可以獲取其相應(yīng)的 ROS1/ROS2環(huán)境變量,ros1_bridge應(yīng)該在它自己的工作區(qū)中,因為它需要同時獲取 ROS 1 和 ROS 2 版本。

3.1 工作區(qū)文件目錄結(jié)構(gòu)

測試自定義的消息類型需要三個工作區(qū),分別是

  • ROS 1 工作區(qū):ros1_msgs_ws
  • ROS 2 工作區(qū):ros2_msgs_ws
  • ros1_bridge工作區(qū):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文件內(nèi)容是:

float64 position

注意:ros2的msg文件命名有正則表達式的規(guī)定,要以大寫字母開頭,文件內(nèi)容中的每個變量不可以存在大寫字母,更詳細的規(guī)則請參考官方文檔。

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環(huán)境變量 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

四、從外部包定義外部包的映射規(guī)則

在前面的部分中,已經(jīng)指出 ros2_package_name 映射規(guī)則必須與定義映射規(guī)則的 ROS 2 包相同。雖然建議這樣做以防止沖突和/或重復(fù)規(guī)則,但可以覆蓋檢查 使用 enable_foreign_mappings字段強制執(zhí)行此操作。

這意味著,對于 yaml 文件中定義的每個包映射規(guī)則,將跳過對 ROS 2 包名稱相等性的檢查。 再次,請注意,這是一種應(yīng)負責(zé)任地使用的黑暗藝術(shù),請務(wù)必小心! 如果存在沖突的映射規(guī)則,則使用按排序順序解析的最后一個! 這通常很難預(yù)測,所以要非常小心!

enable_foreign_mappings設(shè)置為true后,可以為 ROS 2 包指定與映射規(guī)則文件所在的包不同的映射規(guī)則:

-
  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 中的索引。 放置該行的好地方是在映射規(guī)則的安裝規(guī)則之前執(zhí)行此操作,如下所示:

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

注意:在這種情況下,應(yīng)該在 `ros2_package_name 中輸入的包名稱是帶有消息定義的包的名稱。 實際上,它是一個外部包,相對于在其中定義映射規(guī)則的映射包。

一個示例目錄布局如下所示:

.
├─ 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

在上面的示例中,映射規(guī)則如下所示:

-
  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  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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容