ROS actionlib
action作为ROS中三种基础通讯方式之一,在service的基础上,增加了反馈与中断,是很好的任务控制机制实现基础。actionlib是基于action通讯方式的ROS包集,包含了便捷丰富的功能接口,用来实现对任务服务器,客户端的高级设计。
下图为action通讯机制:
基础使用
如果暂时只想简单使用,不想关心深入的任务机制问题,wiki actionlib 基础教程给出了最基础的server与client实现,教程中使用SimpleActionServer提供的接口,这也是我们使用最多最便捷工具,提供的清晰简易的接口,例如导航move_base,机械臂move_group规划都是使用这种方式实现的,设计更复杂的任务机制时,会配合ActionServer一起使用,注意少了Simple。
在这一部分,需要了解部分实现方式与消息接口。
消息接口:
目标(Goal)
目标由ActionClient发送到ActionServer。消息类型就是你使用的actionGoal类型,其实就是action消息里的Goal部分。
反馈(Feedback)
反馈为服务器实现者提供了一种方法,可以通知ActionClient目标的增量进度,比如执行到了第几步。
结果(Result)
完成目标后,结果将从ActionServer发送到ActionClient。与反馈不同,结果是一次发送,只有比较重要的步骤完成或者失败后会返回一个整个任务的状态。
实现方式
-
确定action消息类型,可以自定义或者使用基础消息类型,基础消息需要在头文件引入,而自定义消息需要在cmakelist文件中添加,同时两种方式都需要将actionlib添加进acmkelist与package两个文件的依赖,参考基础教程。
-
代码实现:
举例C++的server实现:
#include <chores/DoDishesAction.h> // 使用的自定义消息类型 #include <actionlib/server/simple_action_server.h> // 使用SimpleActionServer的接口实现功能 typedef actionlib::SimpleActionServer<chores::DoDishesAction> Server; // 定义SimpleActionServer类 //当server接受Goal之后,调用execute函数,完成任务主体功能 void execute(const chores::DoDishesGoalConstPtr& goal, Server* as) // Note: "Action" is not appended to DoDishes here { // 函数实现任务功能 as->setSucceeded(); // 完成任务后将server状态设置为成功 } int main(int argc, char** argv) { ros::init(argc, argv, "do_dishes_server"); ros::NodeHandle n; // 初始化类对象,并定义action名称为“do_dishes”,绑定execute为任务回调函数,并传入参数server,方便在回调函数中使用对象接口,false为不在初始化时,自动启动 Server server(n, "do_dishes", boost::bind(&execute, _1, &server), false); server.start(); // 开始server ros::spin(); // 进入回调,以固定频率轮循并完成回调队列中的任务 return 0; }
-
action消息会自动转换为msg消息,并发出五个接口,action_name + /goal /status /feedback /result /cancel,这几个接口同样可以直接使用,使用topic发送消息的形式,而不是直接使用actionlib接口
例如生成了NameActionGoal.msg与NameGoal.msg,前者主要是用于生成topic消息的类型发出,而后者是在程序中actionlib对象发送的消息内容,注意区分
高级任务交互
本部分主要记录SimpleAcitionServer与ActionServer的复杂任务代码实现,状态机制等原理参考给出的相关教程。
原理
可以参考actionlib高级交互技术,非常好的总结了状态转换的过程。
功能实现
wiki actionlib Tutorials给出了一些深入使用的相关教程,可以阅读了解并测试。
actionlib的所有接口都在 here.
simple_action_server与action_server区别:
- 简单与复杂(画重点)
- 阅读过原理之后可以了解到,simple就是做了简化,包括任务的状态机制,以及新任务的处理机制
- 更多的需要关注的是在使用过程中如何选择,很简单,看下边的接口文档,了解接口都有什么,当你需要的任务功能simple不能满足时,选后者,如果都不能实现,那就选简单的呗
- 一般情况下simple可以满足大多数任务需求,但是当任务复杂,需要关注更多更细致的状态时,就要更换action_server,最明显的差异是两者对于新任务的处理,simple会直接放弃旧的开始新的,没有改变的方法,而后者可以灵活的处理新任务,无论是拒绝或者执行或者实现更复杂的任务队列
- 在功能,代码复杂度上相差不多,根据需求灵活选用即可
SimpleActionServer
包含所有的能用接口,其实就这几种:
- 对象初始化
- 回调函数注册绑定
- 三种状态改变,注意包含参数,可以设置result的消息内容
- 状态判断,注意setPreempted 和 isPreemptRequested的区别
- 反馈发布
例子:
#include <ros/ros.h>
#include <ros/time.h>
#include "pan_tilt_msgs/PanTiltCmdAction.h"
#include "pan_tilt_driver.h"
#include <actionlib/server/simple_action_server.h>
#include <string>
typedef actionlib::SimpleActionServer<pan_tilt_msgs::PanTiltCmdAction> Server;
class PanTiltDriverAction
{
public:
PanTiltDriverAction();
~PanTiltDriverAction();
void ExecuteCb(const pan_tilt_msgs::PanTiltCmdGoalConstPtr& goal);
void preemptCB();
voi