目錄
〇、概述
使用自定義消息(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.stamp,header和stamp均是字段。在映射時,會遞歸映射該字段的所有子數(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