机器人是一种高度复杂的系统性实现,在机器人上可能集成各种传感器(雷达、摄像头、GPS…)以及运动控制实现,为了解耦合,在 ROS
中每一个功能点都是一个单独的进程,每一个进程都是独立运行的。
更确切的讲,ROS
是进程(也称为Nodes)的分布式框架。 因为这些进程甚至还可分布于不同主机,不同主机协同工作,从而分散计算压力。不过随之也有一个问题: 不同的进程是如何通信的?也即不同进程间如何实现数据交换的?在此我们就需要介绍一下 ROS
中的通信机制了。
ROS
中的基本通信机制主要有如下三种实现策略:
- 话题通信(发布订阅模式)
- 服务通信(请求响应模式)
- 参数服务器(参数共享模式)
话题通信是 ROS
中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:一个节点发布消息,另一个节点订阅该消息。话题通信的应用场景也极其广泛,比如下面一个常见场景:
机器人在执行导航功能,使用的传感器是激光雷达,机器人会采集激光雷达感知到的信息并计算,然后生成运动控制信息驱动机器人底盘运动。
在上述场景中,就不止一次使用到了话题通信。
- 以激光雷达信息的采集处理为例,在
ROS
中有一个节点需要时时的发布当前雷达采集到的数据,导航模块中也有节点会订阅并解析雷达数据。 - 再以运动消息的发布为例,导航模块会根据传感器采集的数据时时的计算出运动控制信息并发布给底盘,底盘也可以有一个节点订阅运动信息并最终转换成控制电机的脉冲信号。
以此类推,像雷达、摄像头、GPS… 等等一些传感器数据的采集,也都是使用了话题通信,换言之,话题通信适用于不断更新的数据传输相关的应用场景。
1. 发布者 Publisher
1.1 话题模型
首先我们要实现下图所示的话题模型,该模型如下图所示,该模型中涉及到三个角色:
ROS Master
(管理者)Talker
(发布者)Listener
(订阅者)
ROS Master
负责保管 Talker
和 Listener
注册的信息,并匹配话题相同的 Talker
与 Listener
,帮助 Talker
与 Listener
建立连接,连接建立后,Talker
可以发布消息,且发布的消息会被 Listener
订阅。
其中,主题名称节点为
/turtle1/cmd_vel
,发布者和订阅者的消息类型为 geometry_msgs::Twist
。
1.2 创建功能包
在 src
目录下创建功能包,包名为 topic_demo
,依赖分别为 rospy
、 std_msgs
、 geometry_msgs
。
wohu@wohu-pc:~/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.
1.3 创建发布者代码
在功能包下面建立一个 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
1.4 修改 Python 代码可执行权限
$ chmod +x publisher.py
1.5 运行发布者代码
- 执行
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
wohu@wohu-pc:~/project/ros/ros_demo/src$
说明 publisher.py
文件权限没有修改。
2. 订阅者subscriber
和前面一样,在功能包
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
脚本输出海龟的位置信息了。
3. 其它
经过测试验证,publisher.py
代码也可以放在 src
目录下
更改脚本名称后运行下面命令,也是可以运行起来的。
$ rosrun topic_demo publisher_demo.py