尽管在ROS中已经提供了service机制来满足请求—响应式的使用场景, 但是假如某个请求执行时间很长,在此期间用户想查看执行的进度或者取消这个请求的话,service机制就不能满足了,但是action通信可满足用户这种需求。例如,控制机器人运动到地图中某一目标位置,这个过程可能复杂而漫长,执行过程中还可能强制中断或反馈信息,这时需要的就是 actionlib
机制
service是阻塞的,action是非阻塞的
通信机制
ROS的Action机制本质上仍然是一种Topic机制,只是通过actionlib进行了封装。ActionClient
和 ActionServer
通过ROS Action Protocol
进行通信,本质上还是消息的方式,action protocol就是预定义的一组 ROS message。
在服务器和客户端之间通过goal, cancel, status, feedback, result五个主题实现Action机制。
goal: 代表一个任务,可以被ActionClient发送到ActionServer。比如在MoveBase中,它的类型是PoseStamped,包含了机器人运动目标位置的信息。
cancel: client发送取消请求到server
status: server通知client每个goal的当前状态
feedback: 向client发送周期性的goall执行过程中的辅助信息。在Move Base中,它表示机器人当前姿态。
result: 等goal执行结束,才向client发送一次性辅助信息
类似msg文件,在程序的action文件(文件名后缀为.action)中定义上述goal、result、feedback等消息,依次用---
分隔
action通信时,如果双方约定的action名称为do_dishes
,那么客户端会发布话题 /do_dishes/goal
和/do_dishes/cancel
;服务端发布话题/do_dishes/feedback
, /do_dishes/result
, /do_dishes/status
client要么发送goal,要么cancel; result和feedback的取值由用户决定,status的取值是固定的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19uint8 PENDING = 0 # The goal has yet to be processed by the action server
uint8 ACTIVE = 1 # The goal is currently being processed by the action server
uint8 PREEMPTED = 2 # The goal received a cancel request after it started executing
# and has since completed its execution (Terminal State)
uint8 SUCCEEDED = 3 # The goal was achieved successfully by the action server (Terminal State)
uint8 ABORTED = 4 # The goal was aborted during execution by the action server due
# to some failure (Terminal State)
uint8 REJECTED = 5 # The goal was rejected by the action server without being processed,
# because the goal was unattainable or invalid (Terminal State)
uint8 PREEMPTING = 6 # The goal received a cancel request after it started executing
# and has not yet completed execution
uint8 RECALLING = 7 # The goal received a cancel request before it started executing,
# but the action server has not yet confirmed that the goal is canceled
uint8 RECALLED = 8 # The goal received a cancel request before it started executing
# and was successfully cancelled (Terminal State)
uint8 LOST = 9 # An action client can determine that a goal is LOST. This should not be
# sent over the wire by an action server
server状态机
Goals请求由ActionClient发出,ActionServer接收后会创建一个有限状态机来跟踪goal的状态,而不是server的状态,每个goal都有一个状态机。
server状态机的命令(actionlib::SimpleActionServer的函数)
- setAccepted: 收到goal后,准备处理
- setRejected: 收到goal后,拒绝处理
- setSucceeded: 标志goal已经成功处理
- setAborted: 处理goal的过程中发现错误,终止处理
- setCanceled: server取消对goal的处理,与setRejected不同之处在于发出取消请求的是client
从客户端也可以异步地触发状态变换,比如cancelGoal
函数
client状态机
server状态机用于追踪goal的状态,client状态机作为补充,追踪server的状态。一般根据goal的情况分成 PENDING,ACTIVE,DONE三部分。
由于可以有多个client连接到一个Action server,有可能第二个client会取消第一个client发的goal
Goal的状态转变
两张图片表示得太复杂了。一般这样记忆:对于MoveBase的client,最开始的状态是LOST
。 当前sendGoal
后,状态为ACTIVE
. 调用cancelGoal
后,变为 PREEMPTED
,而不是Aborted
。客户端实际的操作goal的函数只有send
和cancel
。
车成功到达目标后,状态为SUCCEEDED
。彻底失败,为Aborted
对于多个goal的处理策略
高层的client和server只考虑goal的处理情况,所以叫SimpleActionServer
和SimpleActionClient
Simple Action Client
一次只追踪一个goal,它发出goal之后,会disables之前的goal的所有回调函数,也不再追踪之前goal的状态,但是并不cancel之前的 goal
SimpleActionServer
在ActionServer类上实现了单一目标策略,就是在某一时刻只能有一个goal是处于active状态,并且新的goal可以抢占先前的goal:
- 每次只能有一个goal处于active状态
- 新的goal抢占以前的goal是根据GoalID中的时间戳
- 新的goal抢占成功后,旧的goal状态会改变
如图,goal A正在被处理,后面等待的是B,此时client又发了C,那么SimpleActionServer
会把C放到队列中,B进入RECALLING状态,然后server又调用setCanceled使B进入RECALLED状态,C成了PENDING
actionlib 为每个 goal 设置了一个状态跟踪器,每个目标有一个独有的id,这是状态发布与取消goal的基础。
actionlib 是通过多个话题的形式实现控制,由于它支持多个客户端同时对服务器发指令,内部必须维护一个list来控制多个目标点的实施。
工具
actionlib
包自带了一个客户端的工具,可以用来测试与server端的通信:1
rosrun actionlib axclient.py /do_dishes // do_dishes是action名称
参考: