文章目录
- ROS机器人入门第四课:话题通信
- 一、话题通信概述
- (一)概念
- (二)作用
- 二、话题通信基本操作
- 需求:
- 分析:
- 流程:
- (一)发布方
- 解释一些关键的ROS函数和概念:
- (二)订阅方
- 关键ROS函数和概念的解释:
- (三)添加可执行权限
- (四)配置 CMakeLists.txt
- (五)执行
- (六)ROS解耦合
ROS机器人入门第四课:话题通信
一、话题通信概述
话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:一个节点发布消息,另一个节点订阅该消息。话题通信的应用场景也极其广泛,比如下面一个常见场景:
机器人在执行导航功能,使用的传感器是激光雷达,机器人会采集激光雷达感知到的信息并计算,然后生成运动控制信息驱动机器人底盘运动。
在上述场景中,就不止一次使用到了话题通信。
- 以激光雷达信息的采集处理为例,在 ROS 中有一个节点需要时时的发布当前雷达采集到的数据,导航模块中也有节点会订阅并解析雷达数据。
- 再以运动消息的发布为例,导航模块会根据传感器采集的数据时时的计算出运动控制信息并发布给底盘,底盘也可以有一个节点订阅运动信息并最终转换成控制电机的脉冲信号。
以此类推,像雷达、摄像头、GPS… 等等一些传感器数据的采集,也都是使用了话题通信,换言之,话题通信适用于不断更新的数据传输相关的应用场景。
(一)概念
以发布订阅的方式实现不同节点之间数据交互的通信模式。
(二)作用
用于不断更新的、少逻辑处理的数据传输场景。
二、话题通信基本操作
需求:
编写发布订阅实现,要求发布方以10HZ(每秒10次)的频率发布文本消息,订阅方订阅消息并将消息内容打印输出。
分析:
在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:
- 发布方
- 接收方
- 数据(此处为普通文本)
流程:
- 编写发布方实现;
- 编写订阅方实现;
- 为python文件添加可执行权限;
- 编辑配置文件;
- 编译并执行。
(一)发布方
这段代码是一个使用ROS(Robot Operating System,机器人操作系统)的Python脚本示例,旨在创建一个名为talker_p
的节点,该节点周期性地向名为chatter
的话题发送包含字符串的消息。它是一个简单的发布者(Publisher)节点,演示了ROS中发布消息的基本模式。下面对代码进行详细注释,并解释其中使用的关键ROS函数。
#! /usr/bin/env python
# - coding: utf-8 -*-
# 导入 rospy 库,rospy 是 ROS 在 Python 中的客户端库,用于使Python代码能与ROS通信。
import rospy
# 从 std_msgs 包中导入 String 消息类型,std_msgs 包含了许多标准的消息类型,String 是其中用于传输文本信息的一个类型。
from std_msgs.msg import String
# 判断此脚本是直接被执行而不是被导入到其他文件中时,下面的代码块将被执行。
if __name__ == "__main__":
# 初始化ROS节点,命名为 "talker_p"。
# 每个ROS节点都必须有一个唯一的名称,这样其他节点就可以与之通信。
rospy.init_node("talker_p")
# 实例化发布者对象。
# 这一步创建了一个发布者对象,它能够向名为 "chatter" 的话题发布 String 类型的消息。
# queue_size 参数是发布队列的大小,用于限制未处理消息的数量,防止内存消耗过大。
pub = rospy.Publisher("chatter", String, queue_size=10)
# 创建一个 String 类型的消息对象。
msg = String()
# 准备消息的前缀文本。
msg_front = "hello 你好"
# 初始化计数器,用于生成消息序列。
count = 0
# 设置消息发布的频率,这里是每秒1次。
rate = rospy.Rate(1)
# 在 ROS 节点未被关闭的情况下循环。
while not rospy.is_shutdown():
# 拼接字符串,将前缀和计数器的值合成为最终的消息内容。
msg.data = msg_front + str(count)
# 发布消息到 "chatter" 话题。
pub.publish(msg)
# 根据之前设置的频率暂停,确保按照设定的频率发布消息。
rate.sleep()
# 将发布的消息内容记录到 ROS 日志信息中,便于调试和记录。
rospy.loginfo("写出的数据:%s", msg.data)
# 更新计数器,为下一条消息准备。
count += 1
解释一些关键的ROS函数和概念:
-
rospy.init_node("talker_p")
:该函数用于初始化一个ROS节点,这是启动任何ROS节点的第一步。这里的"talker_p"是节点的名称,它必须在ROS系统中唯一。 -
rospy.Publisher("chatter", String, queue_size=10)
:这个函数创建一个发布者对象,用于向特定的话题(这里是chatter
)发布消息。String
是消息类型,而queue_size
参数用于控制发布者消息队列的大小,有助于处理网络延迟或处理速度慢时消息的积压问题。 -
rospy.Rate(1)
:这个函数创建一个Rate对象,用于控制循环的速率。这里设置为1Hz,意味着循环每秒运行一次。 -
rospy.is_shutdown()
:这是一个检查ROS节点是否收到了终止信号(如Ctrl+C)的函数。如果是,则返回True,循环将停止。 -
pub.publish(msg)
:通过之前创建的发布者对象pub
调用publish
方法来发布消息。msg
是要发布的消息对象。 -
rate.sleep()
:这个方法使当前循环休眠足够的时间,以保持循环运行在设定的频率上。 -
rospy.loginfo()
:这个函数用于将信息记录到ROS日志里,类似于Python的print
函数,但是它提供了更丰富的日志管理功能。
如若不写订阅方的代码想直接查看发布方的消息,可以使用如下命令:
rostopic echo 话题
(二)订阅方
这段代码展示了如何在ROS(Robot Operating System,机器人操作系统)中创建一个订阅者节点,监听并处理来自特定话题(chatter
)的消息。下面对代码进行详细注释,并解释其中使用的关键ROS函数。
# 导入 rospy 库,rospy 是 ROS 在 Python 中的客户端库,允许Python代码与ROS通信。
import rospy
# 从 std_msgs 包中导入 String 消息类型,这是一个用于传输文本信息的标准消息类型。
from std_msgs.msg import String
# 定义回调函数,这个函数将在节点收到新消息时被调用。
# msg 参数是收到的消息对象。
def doMsg(msg):
# 在 ROS 日志中记录信息,这里记录的是收到的消息内容。
rospy.loginfo("I heard:%s", msg.data)
# 检查这个脚本是否是主程序,而不是被其他文件导入。
if __name__ == "__main__":
# 初始化 ROS 节点,节点名为 "listener_p"。
# 每个节点必须有一个唯一的名称,以便在 ROS 网络中被识别。
rospy.init_node("listener_p")
# 实例化订阅者对象。
# 这一步创建了一个订阅者对象,它将监听名为 "chatter" 的话题,
# 并且每当有新消息时,就会调用 doMsg 函数。
# String 指定了话题消息的类型,queue_size 用于限制消息队列的大小。
sub = rospy.Subscriber("chatter", String, doMsg, queue_size=10)
# 进入循环,等待消息到来。
# rospy.spin() 使得Python脚本保持运行状态,并在收到新消息时调用回调函数。
# 它是一个阻塞调用,直到节点被明确地关闭或接收到终止信号(例如Ctrl+C)。
rospy.spin()
关键ROS函数和概念的解释:
-
rospy.init_node("listener_p")
:该函数用于初始化一个ROS节点。这是启动任何ROS节点的必要步骤。这里的"listener_p"
是节点的名称,它在ROS网络中必须是唯一的。 -
rospy.Subscriber("chatter", String, doMsg, queue_size=10)
:这个函数创建了一个订阅者对象,用于订阅特定的话题(这里是chatter
)并指定当收到新消息时所调用的回调函数(这里是doMsg
)。String
参数指定了订阅的消息类型,而queue_size
参数用于控制消息队列的大小,有助于处理网络延迟或处理速度慢时消息的积压问题。 -
doMsg(msg)
:这是一个用户定义的回调函数,每当订阅的话题有新的消息时,这个函数就会被调用。msg
参数是收到的消息对象。在这个函数内部,使用rospy.loginfo
来记录消息内容。 -
rospy.spin()
:这个函数会让节点进入等待循环,等待并处理回调函数。它是一个阻塞(blocking)调用,意味着除非节点被关闭,否则程序将停在这里。这是订阅者节点保持激活并响应话题消息的常用方法。 -
rospy.loginfo()
:这个函数用于在ROS日志中记录信息,对于调试和跟踪节点行为非常有用。
注意: 二者需要设置相同的话题
比如上面的发布方和订阅方都订阅了相同的话题:chatter
(三)添加可执行权限
终端下进入 scripts 执行:chmod +x *.py
(四)配置 CMakeLists.txt
catkin_install_python(PROGRAMS
scripts/talker_p.py
scripts/listener_p.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
(五)执行
-
启动 roscore;
-
启动发布节点;
- 先启动命令行
source ./devel/setup.bash
- 再启动命令行
rosrun 包名 自定义文件名.py
- 启动订阅节点。
- 先启动命令行
source ./devel/setup.bash
- 再启动命令行
rosrun 包名 自定义文件名.py
运行结果如图所示:
因为发布方是软起动,需要先在master里注册,注册过程中可能就已经把消息发出去了,即使先打开订阅方,再打开发布方也一样会丢失数据,所以我们可以先确保发布方注册完,再发送信息
即在发布方的代码中设置一个休眠函数rospy.sleep(3)
,我这里是休眠3秒再发送数据
注意:可以使用 rqt_graph 查看节点关系。
(六)ROS解耦合
即便你使用不同的语言编写的节点,那么他们之间也是可以实现数据交换
在话题通信中,只要话题一致,C++和python写的代码就可以相互通信