概念
我们先来看一张动图,下文再围绕这张图对动作作简单阐释和说明。
如上图所示,动作的复杂度比之前提到的几种通信方式(主题、服务)要大一点,但是几者之间也有着千丝万缕的关系,动作糅合了主题和服务的机制,可以看作是主题和服务孕育出的结晶。首先,动作遵循的也是C/S架构,有动作客户端节点(action client node),对应的有动作服务端节点(action server node)。其次,每个动作节点内一般由三部分组成:【目标服务客户端/服务端】 + 【反馈订阅者/发布者】 + 【结果服务客户端/服务端】,动作过程由三要素组成:【目标 + (不断地)反馈+ 结果】,【目标】是客户端通过目标服务发向服务端的(比如让小海龟移动到某个位置),【反馈】是由动作服务端不断通过反馈主题发布给客户端的,【结果】是由服务端通过结果服务回复给客户端的。最后,动作有个特点,可以随时取消之前设定的目标值。一个完整的流程是:
1.动作客户端(之目标服务客户端)通过目标服务向动作服务端(之目标服务服务端)发送目标值;
2.动作服务端(之目标服务服务端)收到目标请求后通过目标服务向动作客户端(之目标服务客户端)回复确认;
3.动作客户端收到动作服务端(之目标服务服务端)的回复后又通过结果服务向动作服务端(之结果服务服务端)请求执行的结果;
4.动作服务端收到结果请求后,通过反馈主题,不断发布反馈消息给动作客户端(之反馈订阅者);
5.最后,当达到目标值时,反馈消息不再发布,取而代之的是动作服务端(之结果服务服务端)通过结果服务回复给动作客户端(之结果服务客户端)一个最终的结果值。
大概是这么个流程,不够简练(大家还是自己看图领悟吧),没关系,下面我们可以通过具体的例子来加深理解。
动动手
启动小海龟
$ros2 run turtlesim turtlesim_node
$ros2 run turtlesim turtle_teleop_key
体验动作(action)
在启动控制节点的时候,可以看见有提示一句话“Use G|B|V|C|D|E|R|T keys to rotate to absolute orientations. 'F' to cancel a rotation.”,这里面就涉及到了动作相关内容,我们通过围绕F周围的一圈按键来给动作服务端节点发送目标值,后者会返回“Rotation goal completed successfully”信息来回复目标完成的结果。
我们再来体验下临时取消之前目标的功能(考验你们手速的时刻到了),先随便在控制节点终端让小海龟沿某个方向游动(目标值),然后马上摁下F键,看看turtlesim节点返回什么。
最后一行打印出了“Rotation goal canceled”的字样,代表动作目标取消成功。
不仅我们的动作客户端节点(teleop_key)可以取消动作目标,我们的动作服务端节点(turtlesim)也能主动取消动作目标,但是返回的内容会不一样,我们来试试。我们在控制终端先摁下d键,然后立即摁下g键,返回了aborting,意思是在前一个目标还未完成的情况下又收到了一个目标,我们停止上一个目标值(但是会执行最后的那个目标,所以后面才有目标完成的信息)。
节点信息
在前面的章节里面我们通过ros2 node info <node_name>命令查看了各节点的详细信息,我们再来回顾下:
$ros2 node info /turtlesim
先看/turtlesim节点,
最后几行可以看到,/turtlesim节点是充当动作服务端(该节点的部分)的,其关联的动作是/turtle1/rotate_absolute,其动作数据类型是turtlesim/action/RotateAbsolute,它会提供反馈和回复给动作/turtle1/rotate_absolute(再由该动作将消息传给动作客户端)。
再来看看另外一个节点/teleop_turtle,
$ros2 node info /teleop_turtle
从最后几行可以看出,该节点是充当动作客户端(该节点的部分)的,其关联了同样的动作/turtle1/rotate_absolute(动作数据类型保持一致)。
动作列表
$ros2 action list
小海龟例子当前只有一种动作/turtle1/rotate_absolute,其中/turtlesim节点的部分充当该动作的服务端,/teleop_turtle节点的部分充当该动作的客户端。
动作数据类型列表
既然是一种通信方式,那么肯定有其对应的通信数据类型咯(其实上面查看节点信息的时候就已经罗列出来了),
$ros2 action list -t
[turtlesim/action/RotateAbsolute]就是动作/turtle1/rotate_absolute的数据类型,当我们需要从命令行或代码里面控制某些目标时是得了解对应的数据类型的。
数据类型详情
既然数据类型都列出来了,我们得继续深入了解这个数据类型长啥样啊,继续我们的interface show命令:
$ros2 interface show turtlesim/action/RotateAbsolute
一共三段内容(---分开),首先是float32 theta目标值,中间的是float32 delta,与开始角度之间的差值,也即结果值,最后是过程中的反馈值。
动作信息
根据查询到的动作名称反查下关联该动作的客户端及服务端情况,此处不再过多赘述。
$ros2 action info /turtle1/rotate_absolute
发送动作目标
既然动作过程、动作名称及动作数据类型我们都能查到了解了,那我们是不是可以通过命令行的形式发送一个动作目标给动作服务端节点呢(之前通过键盘是另一种动作发送目标的方式)?答案是毋庸置疑的。
$ros2 action send_goal <action_name> <action_type> <values>
上面这个命令即可办到,国际惯例,<values>这个得符合YAML语法规则。
$ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 1.57}"
再试试上面的命令控制下小海龟 ,我们只用负责发送一个目标值即可(theta,单位为弧度),同时观察下小海龟的游动情况,
可以看到小海龟在收到动作目标值之后旋转了方向,目标成功完成,返回结果里面有出现一个目标接收端的ID号以及结果delta值。theta和delta都出场了,还有个反馈值remaining不知道猫在哪,我们让它现身,通过追加--feedback(目标值theta我们逆时针再转回去,方便观察),
$ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: -1.57}" --feedback
...
不断有Feedback刷新在终端信息里(精度还挺高),直到到达目标值了,Feedback才停止发布,由动作服务端回复给动作客户端结果信息。
根据上面的体验,我们可以看出,动作方式适用于那种需要灵活处理实时情况的场景,比如导航和机械臂,从A点到B点,中间如遇到障碍物,那么要随时取消上一个目标,并发送一个新目标让其执行,此过程不断反复直到抵达目标。
本篇完。