有时候想想,国内有些资源确实比较稀缺,但是我们一样不会落后!学习python和ROS这么久了,一直很少去实战,另外看到基于python编程的ROS话题通信教程实在是稀缺了,或者对小白不是那么的友好,所以写下这篇教程与你一起进步。
       关于ROS很基本的知识,比如:怎么启动roscore;怎么创建工作空间;source环境;python脚本的执行权限等我就不讲解了。主要是代码部分讲解!

一、初始化工作空间

在这里插入图片描述

二、source环境

把下面的内容:

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

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

传送门: https://blog.csdn.net/qq_45152498/article/details/108652439

三、创建功能包

在这里插入图片描述
创建好的src:

在这里插入图片描述

四、Writing the Publisher Node

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

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

#!/usr/bin/env python
# license removed for brevity
import rospy
from std_msgs.msg import String

def talker():
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rospy.init_node('talker', anonymous=True)
    rate = rospy.Rate(10) # 10hz
    while 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 rospy
from 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 python
import rospy
from std_msgs.msg import String

def 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 stopped
    rospy.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:http://wiki.ros.org
古月——ROS入门21讲
《ROS机器人开发实践》

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

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐