【ROS2概念】系列(十四)——ROS 1 和 ROS 2 之間的橋接通信

目錄

一、ROS1和RO2之間的橋接概述

ROS2提供了一個(gè)ROS2 package,這個(gè)包提供了一個(gè)橋(bridge),可以在 ROS 1 和 ROS 2 之間交換消息。

該橋目前在 C++ 中實(shí)現(xiàn),因?yàn)楫?dāng)時(shí) ROS 2 的 Python API 尚未開發(fā)。因此,它的支持僅限于橋編譯時(shí)可用的消息/服務(wù)類型。

目前ros1_bridge這個(gè)包的功能有限,還不提供python實(shí)現(xiàn),預(yù)構(gòu)建 ROS 2 二進(jìn)制文件提供的橋包括對(duì)通用 ROS 接口(消息/服務(wù))的支持,僅支持ros2_common_interfaces中列出的通訊類型和tf2_msgs。

如果想使用自定義類型的ros1_bridge,則必須在單獨(dú)的 ROS 1 和 ROS 2 工作區(qū)中構(gòu)建自定義類型后,從源代碼構(gòu)建ros1_bridge。

出于效率原因,只有當(dāng)匹配的發(fā)布者-訂閱者對(duì)在ros1_bridge的任一側(cè)都處于活動(dòng)狀態(tài)時(shí),才會(huì)橋接topic。因此,如果沒(méi)有其他訂閱者存在,使用命令行工具ros2 topic echo <topic-name>不起作用,但會(huì)失敗并顯示錯(cuò)誤消息Could not determine the type for the passed topic,因?yàn)?code>dynamic_bridge尚未橋接topic。

作為解決方法,可以明確指定topic類型ros2 topic echo <topic-name> <topic-type>,這會(huì)觸發(fā)topic的橋接,因?yàn)樵?code>echo命令表示必要的訂閱者。

但在 ROS 1 端rostopic echo沒(méi)有明確指定topic類型的選項(xiàng)。因此,如果沒(méi)有其他訂閱者,它不能與ROS2的dynamic_bridge一起使用。作為替代方案,可以使用ros2 run ros1_bridge dynamic_bridge --bridge-all-2to1-topics選項(xiàng)把所有 ROS 2的topic橋接到 ROS 1,以便即使沒(méi)有匹配的 ROS 1 訂閱者,rostopic echorostopic listrqt這些工具也會(huì)看到topic。運(yùn)行ros2 run ros1_bridge dynamic_bridge -- --help以獲得更多選項(xiàng)。

注意,ROS2官方提示目前ros1_bridge已經(jīng)存在的一些問(wèn)題:

  • 可能需要 ros2 run ros1_bridge dynamic_bridge --bridge-all-topics 才能正確橋接。 經(jīng)過(guò)測(cè)試并報(bào)告說(shuō),當(dāng)至少一種自定義消息類型在 ROS 2 中發(fā)布并在 ROS 1 中訂閱時(shí),--bridge-all-topics是必需的。還有非正式地報(bào)告說(shuō),在相同條件下,內(nèi)置消息類型需要--bridge-all-topics選項(xiàng)。
  • 最后ros1_bridge一旦與 ROS 1 主服務(wù)器建立了映射,就可以在沒(méi)有 --bridge-all-topics的情況下重新運(yùn)行ros1_bridge,以便有選擇地橋接主題。 但是,這不能保證。

二、橋接流程

橋接過(guò)程至少需要開啟四個(gè)終端:

Terminal 1 Terminal 2 Terminal 3 Terminal 4
ROS環(huán)境變量 source ros1 source ros1 source ros1, source ros2 source ros2
命令 roscore 運(yùn)行你需要使用的ros1的包 export ROS_MASTER_URI=http://localhost_IP:11311
ros2 run ros1_bridge dynamic_bridge
運(yùn)行你需要使用的ros2的包

注意:當(dāng)運(yùn)行這些demo時(shí),確保僅提供指定ROS版本的環(huán)境變量。如果在某些節(jié)點(diǎn)中有兩個(gè)ROS工作區(qū),將收到錯(cuò)誤消息。

三、ROS 1 talker -> ROS 2 listener

3.1 運(yùn)行命令

Terminal 1 Terminal 2 Terminal 3 Terminal 4
ROS環(huán)境變量 source ros1 source ros1 source ros1, source ros2 source ros2
命令 roscore rosrun rospy_tutorials talker export ROS_MASTER_URI=http://localhost_IP:11311
ros2 run ros1_bridge dynamic_bridge
ros2 run demo_nodes_cpp listener

首先啟動(dòng)roscore(左上角),接著運(yùn)行自己的ROS1節(jié)點(diǎn)(左下角),ROS 1 節(jié)點(diǎn)開始將發(fā)布的消息打印到控制臺(tái)。

然后啟動(dòng)ros1_bridge(右上角),它將監(jiān)視可用的 ROS 1 和 ROS 2 主題,一旦檢測(cè)到匹配的主題,它就會(huì)開始橋接關(guān)于該主題的消息。ros1_bridge開始定期輸出 ROS 1 和 ROS 2 中當(dāng)前可用的主題。

最后運(yùn)行自己的ROS2節(jié)點(diǎn)(右下角),ROS 2 節(jié)點(diǎn)開始將收到的消息打印到控制臺(tái)。

3.2 輸出結(jié)果

在橋接的過(guò)程中,Terminal 3(右上角)有一行說(shuō)明該topic的bridge已創(chuàng)建:

created 1to2 bridge for topic '/chatter' with ROS 1 type 'std_msgs/String' and ROS 2 type 'std_msgs/String'

在停止了 talker 或 listener 中的任何一個(gè),就會(huì)有一行表明橋已被拆除:

removed 1to2 bridge for topic '/chatter'

四、ROS 2 talker -> ROS 1 listener

4.1 運(yùn)行命令

Terminal 1 Terminal 2 Terminal 3 Terminal 4
ROS環(huán)境變量 source ros1 source ros1 source ros1, source ros2 source ros2
命令 roscore rosrun roscpp_tutorials listener export ROS_MASTER_URI=http://localhost_IP:11311
ros2 run ros1_bridge dynamic_bridge
ros2 run demo_nodes_py talker

首先啟動(dòng)roscore(左上角),接著運(yùn)行自己的ROS2節(jié)點(diǎn)(左下角),ROS 2 節(jié)點(diǎn)開始將發(fā)布的消息打印到控制臺(tái)。

然后啟動(dòng)ros1_bridge(右上角),它將監(jiān)視可用的 ROS 1 和 ROS 2 主題,一旦檢測(cè)到匹配的主題,它就會(huì)開始橋接關(guān)于該主題的消息。ros1_bridge開始定期輸出 ROS 1 和 ROS 2 中當(dāng)前可用的主題。

最后運(yùn)行自己的ROS1節(jié)點(diǎn)(右下角),ROS 1 節(jié)點(diǎn)開始將收到的消息打印到控制臺(tái)。

4.2 輸出結(jié)果

在橋接的過(guò)程中,Terminal 3(右上角)有一行說(shuō)明該topic的bridge已創(chuàng)建:

created 2to1 bridge for topic '/chatter' with ROS 2 type 'std_msgs/String' and ROS 1 type 'std_msgs/String'

在停止了 talker 或 listener 中的任何一個(gè),就會(huì)有一行表明橋已被拆除:

removed 2to1 bridge for topic '/chatter'

五、ROS 2 拍攝圖像 -> ROS 1 顯示圖像

ros1_bridge也可以傳輸大數(shù)據(jù)消息,如圖像數(shù)據(jù)。

ROS 2 節(jié)點(diǎn)發(fā)布相機(jī)圖像,ROS 1 使用rqt_image_view在 GUI 中渲染圖像。

5.1 運(yùn)行命令

Terminal 1 Terminal 2 Terminal 3 Terminal 4
ROS環(huán)境變量 source ros1 source ros1 source ros1, source ros2 source ros2
命令 roscore rqt_image_view /image export ROS_MASTER_URI=http://localhost_IP:11311
ros2 run ros1_bridge dynamic_bridge
ros2 run image_tools cam2image

首先啟動(dòng)roscore(左上角),接著運(yùn)行自己的ROS1節(jié)點(diǎn)(左下角)。

然后啟動(dòng)ros1_bridge(右上角),它將監(jiān)視可用的 ROS 1 和 ROS 2 主題,一旦檢測(cè)到匹配的主題,它就會(huì)開始橋接關(guān)于該主題的消息。ros1_bridge開始定期輸出 ROS 1 和 ROS 2 中當(dāng)前可用的主題。

最后運(yùn)行自己的ROS2節(jié)點(diǎn)(右下角)。

5.2 輸出結(jié)果

六、傳輸Service

在這個(gè)例子中,我們將橋接來(lái)自 ros/roscpp_tutorials的服務(wù) TwoInts和來(lái)自 ros2/roscpp_examples 的 AddTwoInts。

在構(gòu)建時(shí),ros1_bridge 會(huì)查找所有已安裝的 ROS 和 ROS2 服務(wù)。通過(guò)比較請(qǐng)求和響應(yīng)中的包名稱、服務(wù)名稱和字段來(lái)匹配找到的服務(wù)。如果 ROS 和 ROS2 服務(wù)中的所有名稱都相同,則將創(chuàng)建網(wǎng)橋。也可以通過(guò)創(chuàng)建包含相應(yīng)服務(wù)名稱的 yaml 文件來(lái)手動(dòng)配對(duì)服務(wù)。

為了使這個(gè)示例正常運(yùn)行,請(qǐng)確保在構(gòu)建 ros1_bridge 時(shí)系統(tǒng)上安裝了 roscpp_tutorials 包并且環(huán)境設(shè)置正確。

6.1 運(yùn)行命令

Terminal 1 Terminal 2 Terminal 3 Terminal 4
ROS環(huán)境變量 source ros1 source ros1 source ros1, source ros2 source ros2
命令 roscore rosrun roscpp_tutorials add_two_ints_server export ROS_MASTER_URI=http://localhost_IP:11311
ros2 run ros1_bridge dynamic_bridge
ros2 run demo_nodes_cpp add_two_ints_client

首先啟動(dòng)roscore(左上角),接著運(yùn)行自己的ROS1節(jié)點(diǎn)(左下角)。

然后啟動(dòng)ros1_bridge(右上角),它將監(jiān)視可用的 ROS 1 和 ROS 2 服務(wù),一旦檢測(cè)到匹配的服務(wù),它就會(huì)開始橋接關(guān)于該服務(wù)的消息。ros1_bridge開始定期輸出 ROS 1 和 ROS 2 中當(dāng)前可用的服務(wù)。

最后運(yùn)行自己的ROS2節(jié)點(diǎn)(右下角)。

6.2 輸出結(jié)果

七、僅橋接選定的主題和服務(wù)

ros1_bridge通過(guò)配置文件可以選擇性橋接自己需要的主題和服務(wù)。

ros1_bridge包使用dynamic_bridge節(jié)點(diǎn)來(lái)橋接所有主題和服務(wù),使用parameter_bridge節(jié)點(diǎn)通過(guò)ROS1的參數(shù)服務(wù)器來(lái)選擇橋接哪些主題和服務(wù)。

橋接所有主題和服務(wù)
ros2 run ros1_bridge dynamic_bridge

通過(guò)ROS1的參數(shù)服務(wù)器來(lái)選擇橋接指定的主題和服務(wù)
ros2 run ros1_bridge parameter_bridge

注意:service的bridge是單向的,必須相應(yīng)地使用services_2_to_1橋接 ROS 2 -> ROS 1 和services_1_to_2來(lái)橋接 ROS 1 -> ROS 2 的service。

例如,使得主題/chatter時(shí)雙向橋接的,而服務(wù)/add_two_ints service僅從 ROS 2 -> ROS 1,可以創(chuàng)建如下配置文件bridge.yaml

topics:
  -
    topic: /chatter  # Topic name on both ROS 1 and ROS 2
    type: std_msgs/msg/String  # Type of topic to bridge
    queue_size: 1  # Queue size
services_2_to_1:
  -
    service: /add_two_ints  # ROS 1 service name
    type: roscpp_tutorials/TwoInts  # The ROS 1 service type name

7.1 運(yùn)行命令

Terminal 1 Terminal 2 Terminal 3 Terminal 4 Terminal 5 Terminal 6
ROS環(huán)境變量 source ros1 source ros1 source ros1 source ros2 source ros2 source ros2
命令 roscore rosparam load bridge.yaml;
rosrun rospy_tutorials talker
rosrun roscpp_tutorials add_two_ints_server ros2 run ros1_bridge parameter_bridge ros2 run demo_nodes_cpp listener ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 1, b: 2}"

首先啟動(dòng)roscore,接著加載 bridge.yaml 配置文件,然后啟動(dòng) talker和server。

啟動(dòng)ros1_bridge中的parameter_bridge,日志顯示它正在為主題和服務(wù)創(chuàng)建橋梁,如果一切順利,就能夠調(diào)用該服務(wù)并從 ROS 2 收聽 ROS 1 talker。

對(duì)于ROS2的listener,這時(shí)應(yīng)該輸出I heard: [hello world ...]帶有時(shí)間戳的文本。

對(duì)于ROS2的client,這時(shí)應(yīng)該輸出example_interfaces.srv.AddTwoInts_Response(sum=3)。

八、參數(shù)化服務(wù)質(zhì)量

ROS 2 優(yōu)于 ROS 1 的一個(gè)優(yōu)勢(shì)是可以為每個(gè)主題定義不同的服務(wù)質(zhì)量設(shè)置。

parameter_bridge 也可以通過(guò)ROS 1的參數(shù)配置文件修改ROS2相應(yīng)的話題的服務(wù)質(zhì)量(Qos)。

ROS 1 中的/tf_static話題用來(lái)描述不同靜態(tài)坐標(biāo)系的變換關(guān)系,在ROS1中只會(huì)發(fā)布一次,若ROS2節(jié)點(diǎn)需要該數(shù)據(jù),可以通過(guò)ROS 2 中parameter_bridge設(shè)置Qos中的history參數(shù)來(lái)獲取數(shù)據(jù),該/tf_static的主題配置為:

topics:
  -
    topic: /tf_static
    type: tf2_msgs/msg/TFMessage
    queue_size: 1
    qos:
      history: keep_all
      durability: transient_local

所有其他 QoS 選項(xiàng)(如https://docs.ros.org/en/foxy/Concepts/About-Quality-of-Service-Settings.html中所述)均可用:

topics:
  -
    topic: /some_ros1_topic
    type: std_msgs/msg/String
    queue_size: 1
    qos:
      history: keep_last  # OR keep_all, then you can omit `depth` parameter below
      depth: 10  # Only required when history == keep_last
      reliability: reliable  # OR best_effort
      durability: transient_local  # OR volatile
      deadline:
          secs: 10
          nsecs: 2345
      lifespan:
          secs: 20
          nsecs: 3456
      liveliness: liveliness_system_default  # Values from https://design.ros2.org/articles/qos_deadline_liveliness_lifespan.html, eg. LIVELINESS_AUTOMATIC
      liveliness_lease_duration:
          secs: 40
          nsecs: 5678

請(qǐng)注意,該qos部分可以完全省略,未設(shè)置的選項(xiàng)保留為默認(rèn)值。

參考:
ros1_bridge官方:https://github.com/ros2/ros1_bridge
ros1_bridge無(wú)法建立通訊解決辦法:https://blog.csdn.net/weixin_37669024/article/details/122348311
ros1_bridge運(yùn)行結(jié)果不成功,報(bào)錯(cuò)No executable found:https://blog.csdn.net/wsc820508/article/details/81408251

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

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

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