機器人是一種高度復雜的系統性實現,在機器人上可能集成各種傳感器(雷達、攝像頭、GPS…)以及運動控制實現,為了解耦合,在 ROS 中每一個功能點都是一個單獨的進程,每一個進程都是獨立運行的。
更確切的講,ROS 是進程(也稱為Nodes)的分布式框架。 因為這些進程甚至還可分布於不同主機,不同主機協同工作,從而分散計算壓力。不過隨之也有一個問題: 不同的進程是如何通信的?也即不同進程間如何實現數據交換的?在此我們就需要介紹一下 ROS 中的通信機制了。
ROS 中的基本通信機制主要有如下三種實現策略:
話題通信是 ROS 中使用頻率最高的一種通信模式,話題通信是基於發布訂閱模式的,也即:一個節點發布消息,另一個節點訂閱該消息。話題通信的應用場景也極其廣泛,比如下面一個常見場景:
機器人在執行導航功能,使用的傳感器是激光雷達,機器人會采集激光雷達感知到的信息並計算,然後生成運動控制信息驅動機器人底盤運動。
在上述場景中,就不止一次使用到了話題通信。
ROS 中有一個節點需要時時的發布當前雷達采集到的數據,導航模塊中也有節點會訂閱並解析雷達數據。以此類推,像雷達、攝像頭、GPS… 等等一些傳感器數據的采集,也都是使用了話題通信,換言之,話題通信適用於不斷更新的數據傳輸相關的應用場景。
首先我們要實現下圖所示的話題模型,該模型如下圖所示,該模型中涉及到三個角色:
ROS Master (管理者)Talker (發布者)Listener (訂閱者)ROS Master 負責保管 Talker 和 Listener 注冊的信息,並匹配話題相同的 Talker 與 Listener,幫助 Talker 與 Listener 建立連接,連接建立後,Talker 可以發布消息,且發布的消息會被 Listener 訂閱。
其中,主題名稱節點為 /turtle1/cmd_vel ,發布者和訂閱者的消息類型為 geometry_msgs::Twist 。
在 src 目錄下創建功能包,包名為 topic_demo,依賴分別為 rospy 、 std_msgs 、 geometry_msgs。
[email protected]:~/project/ros/ros_demo/src$ catkin_create_pkg topic_demo rospy std_msgs geometry_msgs
輸出如下:
turtlesim Created file topic_demo/package.xml
Created file topic_demo/CMakeLists.txt
Created folder topic_demo/src
Successfully created files in /home/wohu/project/ros/ros_demo/src/topic_demo. Please adjust the values in package.xml.
在功能包下面建立一個 scripts 文件夾,在 scripts 文件裡面建立一個 .py 文件。

publisher.py 代碼內容:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 該例程將發布turtle1/cmd_vel話題,消息類型geometry_msgs::Twist
import rospy
from geometry_msgs.msg import Twist
def velocity_publisher():
# ROS節點初始化
rospy.init_node('velocity_publisher', anonymous=True)
# 創建一個Publisher,發布名為/turtle1/cmd_vel的topic,消息類型為geometry_msgs::Twist,隊列長度10
turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)
#設置循環的頻率
rate = rospy.Rate(10)
while not rospy.is_shutdown():
# 初始化geometry_msgs::Twist類型的消息
vel_msg = Twist()
vel_msg.linear.x = 0.5
vel_msg.angular.z = 0.2
# 發布消息
turtle_vel_pub.publish(vel_msg)
rospy.loginfo("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]",
vel_msg.linear.x, vel_msg.angular.z)
# 按照循環頻率延時
rate.sleep()
if __name__ == '__main__':
try:
velocity_publisher()
except rospy.ROSInterruptException:
pass
$ chmod +x publisher.py
roscore 啟動 ROS 服務$ roscore
$ rosrun turtlesim turtlesim_node
$ rosrun topic_demo publisher.py
rosrun 後面跟著創建的包名 topic_demo 和發布者的文件 publisher.py
演示結果如下:
如果運行後報錯:
$ rosrun topic_demo publisher.py
[rosrun] Couldn't find executable named publisher.py below /home/wohu/project/ros/ros_demo/src/topic_demo [rosrun] Found the following, but they're either not files,
[rosrun] or not executable:
[rosrun] /home/wohu/project/ros/ros_demo/src/topic_demo/scripts/publisher.py
[email protected]:~/project/ros/ros_demo/src$
說明 publisher.py 文件權限沒有修改。
和前面一樣,在功能包 topic_demo 目錄的 src 目錄下存放訂閱者文件

訂閱者代碼實現
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 該例程將訂閱/turtle1/pose話題,消息類型turtlesim::Pose
import rospy
from turtlesim.msg import Pose
def poseCallback(msg):
rospy.loginfo("Turtle pose: x:%0.6f, y:%0.6f", msg.x, msg.y)
def pose_subscriber():
# ROS節點初始化
rospy.init_node('pose_subscriber', anonymous=True)
# 創建一個Subscriber,訂閱名為/turtle1/pose的topic,注冊回調函數poseCallback
rospy.Subscriber("/turtle1/pose", Pose, poseCallback)
# 循環等待回調函數
rospy.spin()
if __name__ == '__main__':
pose_subscriber()
依次分別打開 3 個終端執行下面命令:
$ roscore
$ rosrun turtlesim turtlesim_node
$ rosrun turtlesim turtle_teleop_key
通過下面命令啟動訂閱者
$ rosrun topic_demo pose_subscriber.py
通過上、下、左、右 控制海龜移動,然後就可以看到 pose_subscriber.py 腳本輸出海龜的位置信息了。
經過測試驗證,publisher.py 代碼也可以放在 src 目錄下

更改腳本名稱後運行下面命令,也是可以運行起來的。
$ rosrun topic_demo publisher_demo.py