ROS actionlib 高级交互技术

作者:teddyluo
Emai: huiwu.luo@aliyun.com
文档日期(首发):2019年07月19日
(注:2019年07月22日已同步更新到ROS Wiki)
ROS Wiki: http://wiki.ros.org/cn/actionlib/DetailedDescription
欢迎留言勘误

ROS actioinlib通讯机制

actioinlib是ROS的一个功能包集,实现了基于action的通讯机制。action也是一种类似于service的问答通讯机制,不一样的地方是action还带有一个反馈机制,可以不断反馈任务的实施进度,而且可以在任务实施过程中,中止运行。action采用了服务器端/客户端(client and server)的工作模式,如下图所示。

client_server_interaction

actioinlib服务器端描述

服务器状态机:
goal 由ActionClient启动。ActionServer接收到目标后,ActionServer会创建一个状态机来跟踪目标的状态(如下图所示。

在这里插入图片描述

请注意,此状态机跟踪单个目标,而不是ActionServer本身。因此,对于系统中的每个目标都有一个状态机。

服务器转换:大多数状态转换由服务器实现的一小组命令进行触发:

  • setAccepted:检查目标后,决定开始处理它

  • setRejected: 检查目标后,决定从不处理它,因为它是一个无效的请求(超出范围,资源不可用,无效等)

  • setSucceeded:通知目标已成功处理

  • setAborted: 通知目标在处理过程中遇到错误,必须中止

  • setCanceled: 由于取消请求,通知该目标不再处理

当然,客户端也能以异步方式触发状态转换:

  • CancelRequest:客户端通知操作服务器它希望服务器停止处理的目标。

服务器状态:

  1. 中间状态

    • Pending 待处理 - 目标尚未由操作服务器处理
    • Active 活动 - 目标正由操作服务器处理
    • Recalling 已调用 - 目标尚未处理,并且已收到来自操作客户端的取消请求,但操作服务器尚未确认目标已取消
    • Preempting 抢占 - 目标正在处理,并且已从操作客户端接收到取消请求,但操作服务器尚未确认目标已取消
  2. 终端状态

    • Rejected 已拒绝 - 目标被操作服务器拒绝,未经处理,没有来自操作客户端的取消请求

    • Succeeded 成功 - 操作服务器成功完成了目标

    • Aborted 中止 -我们的目标是终止了行动服务器,而不从操作客户端的外部请求取消

    • Recalled 已调用 - 在操作服务器开始处理目标之前,目标已被另一个目标或取消请求取消

    • Preempted 抢占 - 目标的处理被另一个目标取消,或取消请求发送到操作服务器

并发问题:

setAccepted-CancelRequest vs. CancelRequest-setAccepted
即使在收到CancelRequest之后,action server也可以设置接受目标,这有点不直观。 这是因为一个异步处理CancelRequest的竞争条件。 也就是说,由于server implementer代码之外的其他内容正在触发转换,因此server implementer无法确定它们是处于[PENDING]还是[RECALLING]状态。

actioinlib客户端描述

客户端的状态机

actionlib中,我们将服务器状态机视为主状态机,同时将客户端的状态机视为辅助/耦合(secondary/coupled)状态机,以尝试跟踪服务器的状态(如下图所示)。

在这里插入图片描述

客户端状态的过渡

  • 服务器触发的转换

    • 报告的[State(状态)]:由于客户端正在尝试跟踪服务器的状态,大多数转换由服务器报告其状态到Action Client触发。

    • 接收结果消息:在这种情况下,该服务器发送一个结果消息给客户端。接收结果将始终表示跟踪结束的目标。

  • 客户端触发转换:

    • 取消目标:请求服务器停止处理此目标
  • “跳过(Skipping)”的状态

    • 给定我们的基于ROS的传输层,客户端可能没有从服务器接收所有的状态更新。因此,我们必须允许客户端状态机“跳过”服务器触发状态。\par
      例如:如果客户端在[WAITING FOR GOAL ACK] ,从服务器状态接收PREEMPTED的更新,客户端的状态可以跳过过去的[ACTIVE],并直接过渡到[WAITING FOR RESULT]

    • 由于多个action clients可以连接到单个action server,所以第二个client可以取消由第一个client发送的目标。因此,如果[RECALLING]是从server接收到的状态,则client从[PENDING]过渡至[RECALLING]应该判为有效的。

Action的接口和传输层

action的client和server之间通过actionlib定义的“action protocol”进行通讯。这种通讯协议是基于ROS的消息机制实现的,为用户提供了client和server的接口,接口如下图所示。

在这里插入图片描述

可以看到,在action的接口框图上,client向server端发布任务目标以及在必要的时候取消任务,server会向client发布当前的状态、实时的反馈和最终的任务结果。ROS Messages有:

  • goal:任务目标
  • cancel:请求取消任务
  • status:通知client当前的状态
  • feedback:周期反馈任务运行的监控数据
  • result:向client发送任务的执行结果,这个topic只会发布一次。

数据关联和Goal的ID

目标ID是一个字符串字段,用于操作界面中的所有消息。 这为动作服务器和客户端提供了一种强大的方法,可以将通过ROS传输的消息与正在处理的特定目标相关联。 目标ID通常是节点名称,计数器和时间戳的组合。 注意:目标ID的格式仍然不稳定,因此用户永远不应解析目标ID,也不应依赖其格式。

消息介绍

goal topic:发送目标点

goal topic使用自动生成的ActionGoal消息(例如:actionlib/TestActionGoal),用于将新目标发送到action server。 实质上,ActionGoal消息包装的是goal消息,并将其与Goal ID捆绑在一起。

在发送目标时,action client通常会生成唯一的Goal ID和时间戳(timestamp)。 但是,天真(或:愚蠢的)的客户端可能会将其中任何一个留空。 如果发生这种情况,action server会对它们的内容进行填充。

  • Empty stamp: 根据action server收到数据报的时间,stamp被填充为now()
  • Empty id:在action server收到后,自动生成ID。 注意,此ID不是非常有用,因为操作客户端无法知道服务器为goal生成的ID

cancel topic: 取消目标点

cancel topic使用actionlib_msgs/GoalID消息,并允许action clients将cancel请求发送到action server。 每个cancel消息都有一个timestamp(时间戳)和goal ID。这些消息字段的填充方式将影响被取消的goal(见下图)。

在这里插入图片描述

status topic: server端目标点的状态更新

状态topic使用的topic名字为actionlib_msgs/GoalStatusArray,它为action clients端提供有关action server当前正在跟踪的每个goal的server端的目标状态信息。 状态由action server以某种固定速率(通常为10 Hz)发送,并在任意服务器进行状态转换后异步发送目标服务器的状态。

action server会一直跟踪goal,直到达到terminal状态。 但是,为了提高通信稳健性,server在达到terminal状态后会将此目标的状态持续发布几秒钟。

feedback topic:异步目标信息

feedback主题使用自动生成的ActionFeedback消息,并为server implementers提供在处理goal期间向action clients发送定期更新的方法。 由于ActionFeedback具有goal的ID属性,因此action client可以确定它是否应该使用或丢弃它收到的feedback(反馈)消息。 发送feedback消息完全是一种可选的行为。

result topic:完成后的目标信息

result主题使用自动生成的ActionResult消息(例如:actionlib/TestActionResult),并为server implementers提供在完成目标后向action clients发送信息的方法。 由于ActionResult具有目标ID属性,因此action client可以自行决定是否使用还是丢弃result消息。 虽然result可以是空消息,但它是action接口的必需部分,并且必须始终在完成目标时发送。 因此,必须在转换到任何Terminal状态时发送结果(Rejected, Recalled, Preempted, Aborted, Succeeded)。

Simple Action Client

一般来说,高级应用程序和执行程序只关心目标是否正在处理,或者是否完成。它们很少关心所有的中间状态。Simple Action Client将原始客户端状态机分为三种状态:PendingActiveDone

在这里插入图片描述

客户状态的歧义

注意,仅客户端状态不足以确定简单的客户端状态。 但是,通过查看客户端状态转换可以轻松解决此问题。 如果客户端状态转换未在任何simple client states之间交叉,则不更新简单客户端状态。

示例:如果client 从RECALLING转换为WAITING FOR RESULT,则simple client state仍将保留为PENDING

多个目标点的政策

为简单起见,Simple Action Client一次只跟踪一个目标。 当用户使用简单客户端发送目标时,它会禁用与上一个目标关联的所有回调,并停止跟踪其状态。 注意,它不会取消之前的目标!

线程模型(C++)

在构造simple action client时,用户决定是否启动额外的线程。

  • 无额外的线程(推荐)
    • action client的所有subscribers都使用全局回调队列(global callback queue)进行注册。
    • 用户的action callbacks是从ros::spin()中调用的。 因此,阻止用户的action callbacks将阻止全局回调队列(global callback queue)服务。
  • 启动线程(Spins up a thread)。
    • action client中的所有subscribers都注册一个回调队列,与全局回调队列(global callback queue)分开。此队列由启动线程(spun up thread)提供服务。
    • 用户的action callbacks(动作回调)从启动线程(spun up thread)调用。尽管在action callbacks的阻塞不会阻止其他ROS服务消息,但仍然是一个不好的做法,因为无法为此操作提供状态(status),反馈(feedback)和结果(result)消息。。
    • 启动额外线程的一个(也是唯一的)优势是用户可以避免在他们的应用程序中调用ros::spin()

Simple Action Server

许多action servers遵循类似的模式:其中一次只能有一个目标是活动的,并且每个新目标都优先于前一个目标。simple action server是一个action server的包装器,旨在强制执行这个简单的策略来处理目标。

在从ction client接收到新goal后,simple action server将该goal移动到其待定槽(pending slot)中。 如果待定插槽已有目标占用,则simple action server会将该目标设置为取消,并将其替换为通过网络传入的目标。

一旦simple action server接收到新目标并将其移动到待定槽中,则通知simple action server的用户新目标可用。 此通知以下面两种方式之一,如下图的目标通知部分所述。 在接收到通知时,用户可以选择接受(accept),移动待定槽中的目标使它变成当前目标槽的目标,同时允许用户修改与新接受的目标相关联的状态机。

在这里插入图片描述
以下用一个实例详细讨论一下actionlib的Goal执行流程。首先看上图,
在Action接收到goal C时,并不立即抢断正在执行的goal,而是进入到Pending Goal待定槽里,如果待定槽里已经有一个goal,则将其清出并进入Recalled状态。到此都是actionlib底层架构自动完成的。

接下来,Simple Action Server的用户就可以收到新goal到来的通知(有两种方式,这里是通过回调函数的方式)。用户可以接收新的goal,将Current Goal槽指向新的goal,并将状态机跟新的goal关联起来,将其改为active状态,其他的就会自动变为待定或者完成状态状态。这个过程可参见下图。

simple_goal_accept

这里需要注意的是,一个goal从客户端发送过来是通过goal话题,cancel是通过cancel话题发送的,一个Simple Action Client发布的goal有 PendingActiveDown三种状态,cancel后并不是立马就消失的,还存在与内存中,需要服务器去清除。这个过程的执行可参照move_baseexecuteCb()函数进行分析。

Goals的通知方式

用户可以通过两种方式接收simple action server已收到新目标的通知:

  • 回调(Callback)通知:此处用户使用构造时的simple action server注册callback,当新目标移动到simple action server的待定槽(pending slot)时调用该callback。 用户可以接受callback中的新goal,或者在准备就绪时发信号通知另一个线程接受目标。

  • 轮询(Polling)通知:此处用户明确询问simple action server是否有新目标。 simple action server根据上次对新目标查询的情况确定新目标是否已移入待定槽来回答此查询。

线程模型(C++)

在构造simple action server时,用户可以自行决定是否启动额外线程以允许在目标回调中长时间运行actions(动作)。

  • 没有额外线程(推荐)
    • 在新目标收到的回调中,任何actions都不应该长时间运行。 当然,用户可以发信号通知另一个线程来工作,但不应进行阻止。
    • 或者,用户可以使用轮询(polling)实现检查新目标的可用性以完全避免callbacks方式。
  • 启动线程(Spins up a thread)
    • 生成单独的线程,以允许用户发现新goal可用时在收到的回调中执行long running或blocking actions。 在此callback中,用户还可以轮询simple action server以检查新目标是否可用。
    • simple action server为用户启动线程的优势在于用户不必对管理另一个线程的开销进行处理。 但是,用户必须意识到该线程的存在性,以保证它们遵循标准的线程安全约定,例如锁定。

参考来源:

[1] actionlib Wiki: http://wiki.ros.org/actionlib
[1] actionlib Detailed Description: http://wiki.ros.org/actionlib/DetailedDescription

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值