ROS系列实践1.4-ROS的通信机制(下)
ROS系列实践
第一部分:机器人环境感知与路径规划( Turtlebot
机器人)
(四)ROS的通信机制(下)
上一节中,我们了解了ROS的核心通信中枢Master,ROS工作的基本单元Node以及启动多个Node的launch文件,以及了解了ROS最常用的通信机制Topic和它传递的消息Message。本节我们继续介绍ROS通信机制中的Service、Parameter Server和Action。
1.4.1 Service与Srv
上一节我们讲到了Topic的通信机制,它的特点是由发布者向平台不停地发布消息,接收者一旦启动便不停地接收,发布和接收的过程类似轮询,在时刻不停地处理消息,但这种通信机制是异步的,发布者和接收者只关心平台和消息本身,而不用在意消息来源,因此不会出现阻塞的现象,可以各自正常运转。
但是在很多情况下,Topic机制因为其类似轮询的特点而显得笨拙。如果我们想要传递一种需要大量计算资源,但又不是实时需要的数据,比如我们想让机器人的摄像头检测人脸后,写一个节点用作人脸识别确认身份。人脸识别用到机器学习算法,是很耗费计算资源的,但我们用于识别的节点又不是时时刻刻都需要处理图像,它只需要在检测到人脸的时候再进行识别。
在上述的情况下,我们需要一种不需要实时发送消息,只在特定时候工作并发送消息的通信机制,这就是Service。我们通过Service的名称,服务,就可以看出来它的特点。形象地理解,Service就像我们身边的服务业,Service的提供方Server就像是家庭医生,接受Service的接收方Client就像是接受服务的家庭。这个家庭的成员在身体有恙的时候才会联系医生,医生向其提供问诊开药的服务。医生可以签约其他家庭,为其他家庭提供服务,同样这个家庭还可以签约多个医生,但平时大多数时候没事,家庭和医生都是空闲的,不过一旦开始看病,一个医生在同一时间只能看一个病人。
如图,Service的通信模式是:客户需要某项服务的时候,向Master发送一个请求,而Service所对应的服务者此时收到请求,开始处理请求,处理结束后将结果返回给客户。在Server处理请求的过程中,Client一直处于阻塞等待的状态,因此Service是一种同步通信。
事实上,Service采用的通信模型是RPC远程过程调用,将服务端的服务看成是一个函数,由客户远程调用这个函数来完成计算或信息处理。
与Topic对应有msg一样,Service模型对应的消息类型就是srv了。同样的,srv的书写方式也十分简洁,只需要用三个短横隔开两部分,每部分与msg相同,定义基本类型或嵌套msg即可,短横上方是request信息,短横下方是reply信息。我们可以通过以下的命令来了解正在运行的service与srv的信息:
同样的,我们通过实践来观察srv的运行。还是打开我们turtlebot的gazebo仿真界面,然后观察当前活跃的service有哪些:
roslaunch turtlebot_gazebo turtlebot_world.launch
rosservice list
这样我们可以看到,命令行出现了一系列非常多的service,上图仅仅截取一部分。我们会发现这些service的命名也大多是动词,体现出了服务被调用的特性。我们随意选取一个service来查看一下它的信息:
rosservice info /gazebo/delete_light
可以看到系统反馈了这一Service信息,包括提供服务的Node:gazebo,它的通信URI,它的Type,也就是它传递的srv的类型,是在gazebo_msgs包中定义的srv文件:DeleteLight,以及它传递的参数:light_name。那么我们现在就可以进一步查看这个srv文件是如何定义的:
rossrv show gazebo_msgs/DeleteLight
可以清晰地看到,DeleteLight这个srv就是这样定义的,用户向服务者提供light的名字,然后服务者将对应的光线删掉,返回是否成功删除,以及一段描述的字符串。如果感兴趣可以使用roscd指令进入对应的包,查看srv文件的定义。也可以使用rosservice call指令实际地运行这一服务,可以删掉仿真界面的灯光,此处不再演示。
1.4.2 Parameter Server
严格来讲,Parameter Server参数服务器并不算是ROS节点间通信的方式,它是一种更静态的传递消息方式。稍微有一点编程经验的同学都知道,我们在写不管是c++还是python程序的时候,更愿意在程序中定义一些静态变量或者常量,来表示程序中用到的一些常数,这样会提高代码的可读性,也有利于我们修改这些量来做一些调试工作,机器学习中神经网络的调参就是一个典型的例子。
ROS为我们这方面的需求做了考虑,参数服务器就是专门用于维护一批较为静态的参数的。ROS程序中用到的一些参数可以提前定义在独立的文件中,在ROS运行的时候从这些文件中加载这些参数,这样就省去了源码调整静态参数-重新编译-重新运行的繁琐操作。
参数配置所定义的文件是yaml格式,它的书写也是非常简单,参数,冒号,值即可。在定义好参数之后,之所以说参数服务器是“比较静态”,而非“完全静态”,就是因为我们还可以通过命令行等方式在运行过程中直接修改参数。下图就说明了参数的定义和操作方式:
我们同样可以在仿真环境中观察这些参数的情况。使用以下指令打开turtlebot的gazebo仿真,并列出所有参数:
roslaunch turtlebot_gazebo turtlebot_world.launch
rosparam list
可以看到,有非常多的参数在系统中配置好。此时我们可以通过rosparam get <参数名>来查看这些参数的值,如下:
事实上,参数配置的主要方式是通过yaml文件配置好,再通过launch文件直接加载这些参数。我们知道launch文件是xml格式,其中加载yaml参数是通过param和rosparam标签来实现的,前者直接制定某一个参数的值,后者将整个yaml文件中的所有参数加载进来。下面的图就是分别使用了param和rosparam标签的launch文件,其实初学者不需要深究launch的写法,通过已有的launch文件模仿修改,很容易实现自己的功能。
1.4.3 Action
Action是ROS中最复杂的通信方式,这种方式和Service有相似之处,都是客户请求服务,只不过Action在Service的基础上增加了反馈和取消的功能。实际的例子就是,我们要让机器人去到某个位置,就会给机器人一个Action命令,机器人执行这一命令可能需要较长的时间,这时候我们希望它实时向我们反馈数据,让我们能够知道他在正常运行,而不是找不到路或者其他问题卡死,而且有可能我们希望它中途改道,去往别的地点,这时候就需要cancel掉当前的任务。
具体的action文件写法与srv文件非常类似,只不过action需要由两组短线隔成三个部分,第一个部分是客户向服务端发送目标,比如导航要去到的位置;第二部分是服务执行完之后发回的结果信息;第三部分是action运行过程中的实时反馈信息。在如下的文件中我们可以找到action文件的实例:
至此,我们基本了解了ROS所有的通信机制的架构和原理,在后面的实践中,我们会在具体任务中反复运用和体会这些通信机制的原理。很多同学,包括刚开始的我自己,在上手工程的时候由于没有这些原理的概念,百度一下照猫画虎,多走了很多弯路,在这里也建议大家先耐下性子,把原理搞清楚,在上手会事半功倍。
上一篇: Qt学习7——模态和非模态对话框
下一篇: Qt中的模态、非模态、半模态对话框