参考:http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber(python)
1 编写Publisher节点
节点是连接到ROS网络的可执行文件的ROS术语。在这里,我们创建一个不断广播消息的发布者(“talker”)节点。创建一个ros包,也可以用现有的ros包,比如:
roscd beginner_tutorials
创建包的方法参考之前的文章《ROS中编写服务器和客户端的方法(C++版)》
1.1 代码
首先创建一个‘script’的路径来保存python代码
mkdir scripts
cd 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
1.2 代码解释
让我们来一行一行地看代码意义
#!/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)
rospy.init_node('talker', anonymous=True)
这部分代码定义了talker与其它ROS节点的通讯。 pub = rospy.Publisher(“chatter”, String, queue_size=10) 表示你正在使用String类型的消息来发布你的节点到chatter。String就是std_msgs.msg.String类。如果任何订阅者都没有足够快地接收到消息,queue_size将会限制队列消息的数量。在旧的ROS版本中,忽略掉了这一点。 下一行是非常重要的,因为它告诉rospy你的ros节点名字,直到rospy有了这个信息,它不能够和ROS Master开始通讯。在这个例子中,你的节点名字是talker anoymous=True 通过在你名字的后边添加一个随机数,来保证你的节点独一无二。
rate = rospy.Rate(10) # 10hz
这一行创建速率对象rate.在其方法sleep()的帮助下,它提供了一个以一定速率循环的方便的方法。参数10表示我们期望以每秒循环10次(只要我们的处理时间不超过1/10秒)
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标志位然后开始工作(‘work’)。你必须检查is_shutdown()来确定你的成熟是否应该退出(例如有Ctrl-C操作或其它)。在这个例子中,‘work’是调用pub.publish(hello_str)来发布一个字符串到chatter话题。循环调用rate.sleep(),睡眠足够的时间,以便通过循环来保持所需的速率。 (你可以运行rospy.sleep()和time.sleep()来达到相同的定时效果) 循环中海油rospy.loginfo(str),这条执行三重任务:消息打印到屏幕上,写入到节点的日志文件中,并且被写入rosout。rosout可以方便的进行调试:你可以使用rqt_console来提取消息,而不必使用节点的输出找到控制台窗口。 std_msgs.msg.String是一个非常简单的消息类型,所以你可能会想知道发布更复杂的类型是什么样子。一般的经验是构造函数args与.msg文件中的顺序相同。你也可以传递任何参数,也可以直接初始化字段。
msg = String()
msg.data = str
或者可以初始化一些值,剩余的采用默认值:
String(data=str)
你可能会好奇剩余的几行代码:
try:
talker()
except rospy.ROSInterruptException:
pass
除了标准的Python_main_检查之外,他会捕获一个rospy.ROSInterruptException异常,当Ctrl-C被按下或者Node被关闭时,它将以rospy.sleep()和rospy.Rate.sleep()的方法抛出。引发这个异常的原因是因为在sleep()之后不会再继续执行代码。 现在,让我们来写一个节点来接收这条消息。
2.写一个Subscriber节点
2.1 代码
还是在上一节文件夹中,建立一个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
# node 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()
2.2 代码解释
listener.py与talker.py文件类似,在listener中会引入一种新的基于回调机制callback来订阅消息。
rospy.init_node('listener', anonymous=True)
rospy.Subscriber("chatter", String, callback)
# spin() simply keeps python from exiting until this node is stopped
rospy.spin()
这个声明表示你的节点订阅消息类型为std_msgs.msgs.String的chatter主题。当接收到新的消息时,回调callback将作为第一个参数被调用。 我们也改变了对rospy.init_node()的调用,我们添加了anonymous=True关键字参数。ROS要求每个节点都有唯一的名称,如果有相同名称的节点出现,则会突破前一个节点。这样就可以很容易地从网络上启动故障的节点。anonymous=True标志高速rospy为节点生成唯一的名称,以便可以轻松地运行多个listener.py节点。 最后添加rospy.spin()只是为了让你的节点退出,直到节点已经关闭。与roscpp不同,rospy.spin()不影响用户回调函数,因为它们有自己的线程。
3 构建自己的节点
我们使用CMake作为我们的构建系统,是的,即使对于python节点也必须使用它。这是为了确保自动生成的消息和服务的python代码被创建。 运行到catkin工作空间,然后运行catkin_make:
cd ~/catkin_ws
catkin_make
然后分别在三个不同的终端运行下边的指令
roscore
rosrun beginner_tutorials talker.py
rosrun beginner_tutorials listener.py
运行结果如下