失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > ROS发布者(Publisher)和订阅者(Subscriber)的python编程实现(讲解超级详细)

ROS发布者(Publisher)和订阅者(Subscriber)的python编程实现(讲解超级详细)

时间:2024-05-17 11:00:23

相关推荐

ROS发布者(Publisher)和订阅者(Subscriber)的python编程实现(讲解超级详细)

有时候想想,国内有些资源确实比较稀缺,但是我们一样不会落后!学习python和ROS这么久了,一直很少去实战,另外看到基于python编程的ROS话题通信教程实在是稀缺了,或者对小白不是那么的友好,所以写下这篇教程与你一起进步。

关于ROS很基本的知识,比如:怎么启动roscore;怎么创建工作空间;source环境;python脚本的执行权限等我就不讲解了。主要是代码部分讲解!

一、初始化工作空间

二、source环境

把下面的内容:

source /home/xiaolong/ros_practice/devel/setup.bash

放在.bashrc末尾,这个不懂得看我之前的博客

传送门:/qq_45152498/article/details/108652439

三、创建功能包

创建好的src:

四、Writing the Publisher Node

在scripts下创建talker.py并确保它有可执行权限

让我先把整体代码给你,让你有一个整体的印象:

#!/usr/bin/env python# license removed for brevityimport rospyfrom std_msgs.msg import Stringdef talker():pub = rospy.Publisher('chatter', String, queue_size=10)rospy.init_node('talker', anonymous=True)rate = rospy.Rate(10) # 10hzwhile not rospy.is_shutdown():hello_str = "hello world %s" % rospy.get_time()rospy.loginfo(hello_str)pub.publish(hello_str)rate.sleep()if __name__ == '__main__':try:talker()except rospy.ROSInterruptException:pass

然后开始一点点分析:

#!/usr/bin/env python

每一个python ROS节点在顶部都有一个声明,它使你的程序被作为python脚本来执行。

import rospyfrom std_msgs.msg import String

如果要编写ROS节点,则需要导入rospy。std_msgs.msg的导入是为了使我们可以重用std_msgs/String消息类型进行发布。

pub = rospy.Publisher('chatter', String, queue_size=10)

声明你的节点正在使用消息类型String发布到chatter话题上。String实际上是std_msgs.msg.String类。queue_size=10表示队列长度是10,如果任何订阅者没有足够快地接收它们,则限制已排队消息的数量。

rospy.init_node('talker', anonymous=True)

这个非常重要,因为它告诉rospy你的节点名称。直到rospy获得此信息之前,它才能开始与ROS Master进行通信。节点将使用名称talker。注意:名称不能包含任何斜杠“/”。anonymous = True通过在名称的末尾添加随机数来确保你的节点具有唯一名称。

rate = rospy.Rate(10)

rate对象可以允许你指定自循环的频率。它会追踪记录自上一次调用rate.sleep()后时间的流逝,并休眠直到一个频率周期的时间。参数为10时,我们期望每秒执行10次循环。简单理解:以每秒 10 次的频率在 chatter 上发布消息。

while not rospy.is_shutdown():hello_str = "hello world %s" % rospy.get_time()rospy.loginfo(hello_str)pub.publish(hello_str)rate.sleep()

此循环是相当标准的rospy构造:检查rospy.is_shutdown()标志,然后进行工作。必须通过is_shutdown()来检查程序是否应该退出(例如Ctrl-C)。hello_str = “hello world %s” % rospy.get_time()定义了发布消息的内容是"hello world 时间"。至于这个%的用法,你可以查查python语法。

rospy.loginfo(hello_str)

此循环调用rospy.loginf(),它执行三项任务:将消息打印到屏幕上,将消息写入node的日志文件,并将消息写入rosout。rosout是一个方便的调试工具:你可以使用rqt_console提取消息,而不必查找带有node输出的控制台窗口。

pub.publish(hello_str)

将字符串发布到chatter话题上。

rate.sleep()

该循环调用rate.sleep(),该睡眠刚好足够长是为了在整个循环中保持所需的速度,和rate = rospy.Rate(10)对应。

try:talker()except rospy.ROSInterruptException:pass

它会捕获rospy.ROSInterruptException异常,当按Ctrl-C或关闭node时,rospy.sleep()和rospy.rate.sleep()可能会引发该异常。

总结:如何实现一个发布者?

• 初始化ROS节点;

• 向ROS Master注册节点信息,包括发布的话题名和话题中的消息类型;

• 创建消息数据;

• 按照一定频率循环发布消息。

五、Writing the Subscriber Node

在scripts下创建listener.py并确保它有可执行权限

#!/usr/bin/env pythonimport rospyfrom std_msgs.msg import Stringdef callback(data):rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)def listener():# In ROS, nodes are uniquely named. If two nodes with the same# name are launched, the previous one is kicked off. The# anonymous=True flag means that rospy will choose a unique# name for our 'listener' node so that multiple listeners can# run simultaneously.rospy.init_node('listener', anonymous=True)rospy.Subscriber("chatter", String, callback)# spin() simply keeps python from exiting until this node is stoppedrospy.spin()if __name__ == '__main__':listener()

这个和talker.py类似,除了我们已经引入了一种新的基于回调的机制来订阅消息。

rospy.Subscriber("chatter", String, callback)rospy.spin()

这声明你的节点订阅了消息类型为std_msgs.msg.String的chatter话题。收到新消息时,将以消息(String)作为第一个参数来调用回调函数。rospy.spin()只是使节点无法退出,直到该节点已关闭。与roscpp不同,rospy.spin()不会影响订阅者回调函数机制,因为它们具有自己的线程。

总结:如何实现一个订阅者?

• 初始化ROS节点;

• 订阅需要的话题;

• 循环等待话题消息,接收到消息后进入回调函数;

• 在回调函数中完成消息处理。

六、测试

1.让我们看看src:

2.启动ROS Master:

roscore

3.启动发布者和订阅者:

4.rqt可视化:

七、探索

1.在运行节点后可以关闭roscore,此时也可以正常发布和接收。但是不启动ROS Master直接运行程序却不行。

2.python脚本可以不用编译直接执行,相当于你不用修改CMakeLists.txt文件。但是编译运行需要添加东西。官方Wiki是说:

但是在工作空间catkin_make却不能通过,而下面这样可以:

3.scripts文件夹可以不要,直接放在topic功能包下,这是因为我们的程序以及功能简单,但是我还是希望你放在scripts下,因为以后你的功能包下基本不会只有python脚本,而没有其它文件。

4.可以用下面两行代替上面一行,这样执行效果一样:

5.关于回调函数:

def callback(data):rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)

这说明传递过来的是String对象,而消息内容的话当然是String.data(即data.data)。

注意:String是不能直接更改的,因为这是ROS内部封装好的,你只能引用,要是直接更改岂不是乱套了嘛。就像上面的hello_str = String(),然后hello_str = “hello world %s” % rospy.get_time()

而使用String.data替代hello_str.data是错误的。

注:以上完全是自己摸索出来的,小白看不懂没有关系,还有大家摸索出来其他东西或者和我的类似,或者知道这其中原理的,欢迎交流哟!

八、话题通信机制

1.基本概念:

2.话题通信机制:

本文内容参考:

ROS官方wiki:

古月——ROS入门21讲

《ROS机器人开发实践》

如有错误或者不足之处,欢迎大家留言指正!

如果觉得《ROS发布者(Publisher)和订阅者(Subscriber)的python编程实现(讲解超级详细)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。