ROS-写一个简单的消息发布器与订阅器(Python)(roswiki初级教程rospy部分翻译以及程序注释)

Topic通信机制---编写简单的Publisher & Subscriber

http://wiki.ros.org/cn/ROS/Tutorials/WritingPublisherSubscriber%28python%29

(可参考创客智造中的教程https://www.ncnynl.com/category/ros-python/

 

"talker"节点发布信息在主题"chatter","listener"节点接受和打印信息。

 

1.编写发布器节点(Publisher)

节点是可以连接到ROS网络的可执行文件。此处将创建一个发布者节点(Talker),该节点将持续发布消息到主题chatter上。

先创建一个package,包的名字为beginner_tutorials。将目录切换到该包的路径下 。

$cd ~/wiki_ws/src/beginner_tutorials

代码:

$mkdir scripts #先创建一个脚本文件scripts用来存储python脚本。
$cd scripts    #进入脚本路径。
$vi talker.py  #进入vi编辑器编写python脚本,命名为talker.py。
$chmod +x talker.py   #授予talker.py这个文件的可执行权限

talker.py

   1 #!/usr/bin/env python   #指定通过python解释代码(必须放在首行,不可少)
     #-*- coding: utf-8 -*-  #加上这句才可以在程序文件中加中文注释
   2 # license removed for brevity  
   3 import rospy    #导入rospy包,rospy是ROS的python客户端
   4 from std_msgs.msg import String   #导入python的标准字符处理库
   
     #定义talker函数
   6 def talker():
         #定义发布的主题名称chatter, 消息类型String(实质是std_msgs.msg.String),设置队列条目个数.
   7     pub = rospy.Publisher('chatter', String, queue_size=10)

         #初始化节点,节点名称为talker
         #anonymous=True,要求每个节点都有唯一的名称,避免冲突。这样可以运行多个talker.py
   8     rospy.init_node('talker', anonymous=True)
    
         #设置发布的频率,单位是每秒次数,这是每秒10次的频率发布主题
   9     rate = rospy.Rate(10) # 10hz

  10     while not rospy.is_shutdown():  #rospy.is_shutdown()检查节点是否关闭
  11         hello_str = "hello world %s" % rospy.get_time()
  12         rospy.loginfo(hello_str)  #在屏幕输出日志信息,写入到rosout节点
  13         pub.publish(hello_str)    #发布信息到主题
  14         rate.sleep() #睡眠一定持续时间,如果参数为负数,睡眠会立即返回
  15 
     #talker节点的主程序
  16 if __name__ == '__main__':
  17     try:
  18         talker()
  19     except rospy.ROSInterruptException:
  20         pass
pub = rospy.Publisher('chatter', String, queue_size=10)

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

pub.publish(hello_str)  

以上三句是该节点的程序中最主要的三句话:定义发布主题;初始化节点;发布信息到主题。

from std_msgs.msg import String

在编译完.msg文件之后,在python中会生成一个module:std_msgs.msg,在编写Publisher时要使用String消息类型发布消息,那么就需要导入std_mass.msg这个module中的String消息类型。

   6 def talker():
   7     pub = rospy.Publisher('chatter', String, queue_size=10)
   8     rospy.init_node('talker', anonymous=True)

这部分代码定义了talker与其它ROS节点的通讯

7表明正在使用String类型的消息来将节点talker发布到主题chatter上,String就是std_msgs.msg.String类。定义队列长度为10(通常设置为一个较小的数,没有严格要求),如果所有订阅者都没有足够快的接受到消息,那么queue_size将会限制队列消息的数量,旧的消息会被踢出队列。

8注册和初始化节点(告诉rospy节点的名字),只有在rospy接收到这个消息后节点才会开始与ROS Master通讯。anonymous=True通过在节点的名称后面添加一个随机数来保证节点名称的唯一性。在ROS中节点的名称要唯一,如果有一个同名的节点出现,那么前一个节点就会被踢出网络中。保证节点名称唯一性可以确保多个节点能够同时启动。

rate = rospy.Rate(10) # 10hz

设置发布的频率,单位是每秒的次数,这是每秒10次的频率发布的主题

创建速率对象rate。在其方法sleep()的帮助下,它提供了一个可以以一定速率循环的简便方法。参数10表示我们期望以每秒循环10次(只要我们的处理时间不超过0.1秒)。rate是让程序休眠,休眠要在循环之中,所以后面必须有一个while循环

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

这个while循环是标准的rospy结构:检查rospy.is_shutdown标志位然后开始工作。必须检查is_shutdown()来确定程序是否应该退出(例如有Ctrl+c或者其他操作)。

在此例中,工作是指调用pub.publish(hello_str)来发布一个字符串到chatter话题。循环最后调用rate.sleep(),执行挂起,睡眠足够的时间,以便通过循环来保持所需的速率(循环sleep,每发布一个消息就sleep一会)。循环中的hello_str是定义一个字符串,rospy.get_time()是返回当前时间     

rospy.loginfo(hello_str)执行三重任务:将消息打印到屏幕上;写入到节点的日志文件中;写入到rosout中。rosout可以较便利的进行调试,可以使用rqt_console来提取消息,而不必使用节点的输出找到控制台窗口。

  16 if __name__ == '__main__':
  17     try:
  18         talker()
  19     except rospy.ROSInterruptException:
  20         pass

__name__==’__main__’是标准的python __mian__检查。

try是正常执行程序。except是用于发生异常时执行的程序。

此例中,会捕获一个rospy.ROSInterruptException异常,当Ctrl+c被按下或者节点被关闭时,他将以rospy.sleep()和rospy.Rate.sleep()的方法抛出。而引发这个异常的原因是在sleep()之后不会再继续执行代码。

 

2.编写订阅器节点(Subscriber)

在编辑完talker.py文件后,退出,在同一个脚本文件中用vim继续编写Subscriber节点,建立listener.py文件。

$vi listener.py
$chmod +x listener.py  #授予可执行权限

listener.py

talker.py文件类似,listener.py会引入一种新的基于回调机制callback来订阅消息。

1 #!/usr/bin/env python
2 #-*- coding: utf-8 -*-
3 import rospy
4 from std_msgs.msg import String
5 
6 def callback(data):   #定义一个回调函数,这个回调函数将接收到的消息作为参数进行处理
7     rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
8
9 def listener():
10	#定义listener函数(订阅者)
11	#在ROS中节点要有唯一的名称。
12	#如果有一个同名节点出现,它会碰上前一个节点,这样就很容易的将前一个节点从网络上踢开
13	#初始化节点
14	rospy.init_node('listener', anonymous=True)
15	
16	#订阅函数,订阅chatter主题,内容类型是std_msgs.msgs.String,
17	#当有新内容,调用callback函数处理。接受到的主题内容作为参数传递给callback
18	rospy.Subscriber("chatter", String, callback)
19	rospy.spin() #保持节点运行,直到节点关闭。不影响订阅的回调函数,因为回调函数有自己的线程
20
    #主程序
21 if __name__ == '__main__':
22	listener()
rospy.Subscriber("chatter", String, callback)

构造一个订阅函数,节点listener订阅主题的名称是chatter,消息类型是std_msgs.msg.String。定义一个处理函数callback(回调函数)来处理subscriber接收到的消息,当接收到新的消息时,将消息作为第一个参数传给回调函数callback。

rospy.spin()

触发topic的处理,会阻塞直到节点关闭。rospy.spin()不会影响订阅服务器的回调函数,它们具有自己的线程。

 

3.测试消息发布器和订阅器

$roscore        #打开一个终端,启动ROS
$cd ~/wiki_ws   #新开一个终端,进入包所在的工作空间
$source ./devel/setup.bash   #source一下工作空间,添加路径
$rosrun beginner_tutorials talker.py  #运行发布器talker,运行之后会不停地发布消息再新开一个终端,运行订阅器listener
$cd ~/wiki_ws   #切换路径到工作空间
$source ./devel/setup.bash   #新开了一个工作终端,必须要source一下工作空间的环境
$rosrun beginner_tutorials listener.py #运行订阅器listener,只要talker不断地发布消息,listener就会不停地接收并处理消息。

简化程序以及使用操作:

打开一个新的终端,输入命令:

echo “source ~/wiki_ws/devel/setup.bash”>>~/.bashrc

这样的话重新打开一个新的终端就不需要再source工作空间环境。

$roscore
$cd ~/wiki_ws
$rosrun beginner_tutorials talker.py
$cd ~/wiki_ws    # 再新开一个终端,运行订阅器listener
$rosrun beginner_tutorials listener.py

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值