ROS之topic和service通信比较

实验速度

1. via topic

同一台机器上通过topic传输数据用时(单位/s)

上图是以前ROS课上做的一个实验,内容是测试一个publisher和一个subscriber之间通讯所用的时间。两个node都很简单,publisher发送一个字符串,字符串带有标号;subscriber回显该字符串,字符串长度不超过20个char。

怎么去标定发送时间是接收时间呢?目前使用的方法就是在publisher发送前使用ROS_INFO输出一个消息,消息会带有ROS的时间戳;subscriber的callback函数里边也使用ROS_INFO输出一个带时间戳的消息。虽然说这种方法并不是非常精准,但目前也没有想到更好的办法了,哪怕是使用header里边的time stamp也有一个获得当前时间和赋值的过程,难以百分百精确,所以目前只能这样粗略测量。

根据实验数据可以发现在同一台机器上,两个node通过topic通讯,大致产生0.7ms的延迟。(本人机子i5-3210M/4G)

2. via service

同一台机器上client call server后server接收到数据用时(单位/s)

这张图是今天自己做的测试,可以看到平均时间大约在3ms左右。

测量的方法也是跟上面的类似。Client在call之前先输出一个带时间戳的消息,server在接收到请求执行操作前也会输出一个带时间戳的消息。这次client与server之间传递的消息更短,是两个int。

从上图我们不难发现,通过service通讯居然比topic延迟要高?实在是不愿意相信,因为这跟我一开始的理解是相违背的。为了控制实验环境平台的一致性(我从groovy换到了indigo),于是重做了以前的那个实验,粗测通过topic传输的延迟时间,发现依然是0.7ms左右。


理论速度

为何前面的实验结果让我如此惊讶?

官方文档中写明,client与server之间维持着一个持久连接。对于这种RPC请求/回复机制,官方给出的评价是“面对低鲁棒性的服务程序变更有着更好的性能表现”(higher performance at the cost of less robustness to service provider changes)1

说到持久连接,不禁让人想起ROSTCP。照这么说,如果在ROS架构下node之间通讯的底层实现都是通过ROSTCP/ROSUDP的话,那理应via service应该跟via topic的速度相当,如果将“持久性”纳入考虑范围的话,service甚至应该比topic更快一些才对。但实际情况是via service的时延是via topic的4倍左右。如此大的差距,实在让人百思不得其解。


实现机制——同步与异步

其实在开始写这篇博文的时候,我都依然没有想明白这个问题。边写边查资料,看到ros answer上一位大大在描述这两种机制时,用到了asynchronous和synchronous这两个字眼2。回到宿舍一边洗着冷水澡一边在想,忽然灵光一现!是的,实验的结果是没有问题的,有问题的是我的实验方法!

- Asynchronous Topic

Topic是异步的。简简单单的一句话,里边却隐含了千言万语。让我关注起这一点的原因除了ros answer上面的那篇问答,还有一个就是自己做的另一个实验:创建一个publisher,pub的速度是100Hz,发送缓冲区的大小只有1;创建一个subscriber,调用callback函数的的速度是1Hz(用spinOnce),但接收缓冲区的大小是1000。这样的效果就是,publisher近乎匀速地发送着数据,每秒100次;而subscriber每一秒调用一次callback函数(spinOnce),每次调用都一次性地处理接收缓冲区内的所有数据。由于接收缓冲区大小远大于发送频率,所以接收缓冲区不存在满的情况,所以就能看到subscriber每秒都会很快地处理完100条消息,然后进入休眠,再被唤醒执行callback,再休眠……

而之前用于测试的subscriber,我使用的是spin。这也就意味着,回调函数一直是处于类似于忙等待的状态的,一旦发现接收缓冲区内有数据,即刻取出作运算(前提当然是获得CPU分片时间)。这也就说明了为什么实验得出的结果via topic会更快!但要注意了,忙等待是会占用硬件资源的。在这种简单的实验条件下,via topic固然是很快,但当一个大项目跑起来时,它之前的高性能表现可能要大打折扣。

- Synchronous Service

至于server,在基于RPC请求/回复机制的前提下,它在接收到调用请求前都是处于休眠状态的。Client的call请求将server唤醒,然后server执行请求,再返回结果给client。所以之前在计时的时候,大部分的时间都是消耗在将server从休眠状态唤醒上了!所以才会看起来比via topic慢。

分析到这里,我就在想,那真正的client与server之间通讯的时延,是不是可以利用server返回结果给client这段时间算出呢?这才是真正抛开了将server从休眠唤醒的时间影响,是client与server通讯的真正时延!于是又有了下图: 
同一台机器上server返回结果后client接收到数据用时(单位/s)

果不其然!表中数据的中位数应该在0.3ms左右,也就是说,从server返回结果到client接收到结果,只需要0.3ms的时间!尽管使用spin通过topic转发数据类似于忙等待(也许ROS的内部机制会对spin的频率作最高限制),但也需要大约0.7ms的时间!而service机制中,client发出call请求之后就会进入阻塞状态,直至server返回结果,期间的时延是via topic的一半而已!

而从这个数据我们还可以估计出将休眠状态的server唤醒,所需要的时间大约是2.7ms。如果计算一来一回,client从发出call请求到接收返回结果,除去请求被执行的时间,在通讯上耗费的时间约是3.3ms,其中将server唤醒的时间约占82%!

- Conclusion

Topic与Service各有优劣。在设计项目的时候,要周全考虑各个方面的因素。Service比较适合用于执行复杂的、调度次数较低的任务。而topic则适合在通讯频率高的情况下使用。再者,使用ros::Rate和sleep合理设置程序的执行速率,能使程序弹性更大,增强可维护性。使用topic时,也别忘了关注一下缓冲区的大小设置。


优劣分析

Service

- 优点:
  • Client只需要关注发出命令请求、接收反馈,而无须关注底层实现,系统可维护性高
  • Client维护着一个与server的持久连接(persistent connection),在单纯的数据传输上可以做到更快,尤其是在分布式作业时
- 缺点:
  • 当server单方改变执行模式或者存在bug的时候,client无法得知具体情况,因为server程序对client而言是透明的。在这种情况下,client只能得到一个错误的数据,或者被告知执行失败,甚至是等待超时
  • Server唤醒耗时太大,不建议用于通讯频率高的情况

Topic

- 优点:
  • 能非常方便地通过监听topic的方法查看各个node的运行状态等信息,可维护性高
  • 能根据实际情况调整发送速率、发送/接收缓冲区大小,使之适应项目需求
  • Topic是多对多的,可操作性强;而service只能一对多(一个server应对多个client)
- 缺点:
  • 因为是异步架构,回调函数的执行频率设计对系统整体性能影响很大
  • 对于使用频率不高的程序,若使用topic进行信息交换,想要减少额外系统开销的办法就是降低回调的速率。但一旦降低回调的速率,同时受到负面影响的还有系统的实时性

  1. Wiki of ROS services: http://wiki.ros.org/Services 
  2. ROS Answer - design question: topic vs service?http://answers.ros.org/question/188354/design-question-topic-vs-service/ 
好的,以下是使用ROS实现服务器和客户端通信程序的实验总结: 1. 实验目的 通过本次实验,学生应该掌握ROS的基本概念和使用方法,了解ROS中的节点、话题、服务等概念,能够使用ROS编写简单的服务器和客户端通信程序。 2. 实验环境 本实验使用的ROS版本为ROS Kinetic,操作系统为Ubuntu 16.04。 3. 实验步骤 (1)创建ROS工作空间 在终端中执行以下命令: ``` mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src catkin_init_workspace cd .. catkin_make source devel/setup.bash ``` (2)创建ROS包和节点 在终端中执行以下命令: ``` cd ~/catkin_ws/src catkin_create_pkg my_ros_pkg rospy cd my_ros_pkg mkdir scripts cd scripts touch my_ros_node.py chmod +x my_ros_node.py ``` 在my_ros_node.py文件中编写节点代码,代码如下: ``` #!/usr/bin/env python import rospy from std_msgs.msg import String def callback(data): rospy.loginfo("Received message: %s", data.data) def my_ros_node(): rospy.init_node('my_ros_node', anonymous=True) rospy.Subscriber("my_topic", String, callback) rospy.spin() if __name__ == '__main__': try: my_ros_node() except rospy.ROSInterruptException: pass ``` (3)创建ROS服务 在my_ros_pkg包中创建srv文件夹,并在其中创建一个名为MyService.srv的服务文件,文件内容如下: ``` string request --- string response ``` (4)编写服务端代码 在scripts文件夹中编写my_ros_server.py文件,文件内容如下: ``` #!/usr/bin/env python import rospy from my_ros_pkg.srv import MyService def handle_request(req): rospy.loginfo("Received request: %s", req.request) response = "Hello, " + req.request return response def my_ros_server(): rospy.init_node('my_ros_server', anonymous=True) rospy.Service('my_service', MyService, handle_request) rospy.loginfo("Ready to handle requests.") rospy.spin() if __name__ == '__main__': my_ros_server() ``` (5)编写客户端代码 在scripts文件夹中编写my_ros_client.py文件,文件内容如下: ``` #!/usr/bin/env python import rospy from my_ros_pkg.srv import MyService def my_ros_client(): rospy.wait_for_service('my_service') try: my_service = rospy.ServiceProxy('my_service', MyService) request = raw_input("Enter your name: ") response = my_service(request) rospy.loginfo("Received response: %s", response.response) except rospy.ServiceException as e: rospy.logerr("Service call failed: %s", e) if __name__ == '__main__': rospy.init_node('my_ros_client', anonymous=True) my_ros_client() ``` (6)运行程序 在终端中执行以下命令: ``` cd ~/catkin_ws catkin_make source devel/setup.bash roscore ``` 在新的终端中执行以下命令: ``` cd ~/catkin_ws source devel/setup.bash rosrun my_ros_pkg my_ros_node.py ``` 在新的终端中执行以下命令: ``` cd ~/catkin_ws source devel/setup.bash rosrun my_ros_pkg my_ros_server.py ``` 在新的终端中执行以下命令: ``` cd ~/catkin_ws source devel/setup.bash rosrun my_ros_pkg my_ros_client.py ``` 在客户端终端中输入您的名字,服务端会返回“Hello, 您的名字”。 4. 实验心得 本次实验通过使用ROS编写简单的服务器和客户端通信程序,让我更深入了解了ROS的基本概念和使用方法,掌握了ROS中的节点、话题、服务等概念,并且学会了如何使用ROS编写服务器和客户端通信程序。同时,在实验过程中,我也学会了如何使用ROS的一些常用命令和工具,如catkin_make、roscd、rosrun等,这些命令和工具对于ROS开发非常重要。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值