ROS专题----actionlib简明笔记

ROS专题----actionlib简明笔记

http://wiki.ros.org/actionlib/Tutorials

----

除了下面列出的教程之外,还为actionlib包创建了两套教程。您可以通过浏览这些教程roscd -ing到actionlib_tutorials或turtle_actionlib包,即

roscd actionlib_tutorials
roscd turtle_actionlib

工作区设置

如果您尚未创建用于完成教程的工作区, 请单击此处查看一些简要说明

在开始任何actionlib教程之前,需要花时间创建一个scratch包来处理和操作示例代码。创建具有以下依赖关系的沙箱包:

catkinrosbuild

$ cd%YOUR_CATKIN_WORKSPACE%/ src
$ catkin_create_pkg actionlib_tutorials actionlib message_generation roscpp rospy std_msgs actionlib_msgs

(如果你不familliar与创建catkin包,请参阅Creating_a_catkin_Package教程 (在该网页上,不要忘记选择catkin为构建系统)。

初学者教程

  1. 使用执行回调编写简单操作服务器 本教程涵盖使用simple_action_server库创建Fibonacci操作服务器。此示例操作服务器生成斐波纳契序列,目标是序列的顺序,反馈是计算的序列,结果是最终序列。
  2. 编写简单的Action Client 本教程涵盖使用simple_action_client库创建Fibonacci操作客户端。此示例程序创建操作客户端并将目标发送到操作服务器。
  3. 运行操作客户端和服务器本教程包括运行Fibonacci服务器和客户端,然后可视化通道输出和节点图。
  4. 使用执行回调(Python)编写简单操作服务器 本教程包括使用simple_action_server库在Python中创建Fibonacci操作服务器。此示例操作服务器生成斐波纳契序列,目标是序列的顺序,反馈是计算的序列,结果是最终序列。
  5. 编写简单操作客户端(Python) 本教程包括使用action_client库在Python中创建一个Fibonacci简单动作客户端。

中级教程

  1. 使用目标回调方法编写简单操作服务器 本教程涵盖使用simple_action_server库创建平均动作服务器。此示例显示如何使用操作处理或响应来自ros节点的传入数据。操作服务器对来自ros节点的数据进行平均,目标是要平均的样本数,反馈是样本数,样本数据,当前平均值和当前标准偏差,结果是平均值和标准偏差所请求的样本数。
  2. 编写一个线程化简单动作客户端 本教程涵盖使用simple_action_client库创建平均化操作客户端。此示例程序旋转线程,创建操作客户端,并将目标发送到操作服务器。
  3. 使用其他节点运行操作服务器和客户端本教程包括使用另一个数据节点运行平均化操作服务器和客户端,然后可视化通道输出和节点图。

高级教程

  1. 编写基于回调的SimpleActionClient使用比简单的线性脚本更复杂的程序流回调的示例。

----

小乌龟在空间画一个五边形:

$ rosrun turtlesim turtlesim_node [ INFO] [1489482578.049901977]: Starting turtlesim with node name /turtlesim [ INFO] [1489482578.064715851]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]

$ rosrun turtle_actionlib shape_server [ INFO] [1489482564.155776333]: /turtle_shape: Succeeded [ INFO] [1489482609.968814937]: /turtle_shape: Succeeded

$ rosrun turtle_actionlib shape_client [ INFO] [1489482590.715268543]: Waiting for action server to start. [ INFO] [1489482590.999293581]: Action server started, sending goal. [ INFO] [1489482609.969416143]: Action finished: SUCCEEDED

server代码:

#include <ros/ros.h>
#include <turtlesim/Pose.h>
#include <actionlib/server/simple_action_server.h>
#include <cmath>
#include <math.h>
#include <angles/angles.h>

#include <geometry_msgs/Twist.h>
#include <turtle_actionlib/ShapeAction.h>

// This class computes the command_velocities of the turtle to draw regular polygons 
class ShapeAction
{
public:
  ShapeAction(std::string name) : 
    as_(nh_, name, false),
    action_name_(name)
  {
    //register the goal and feeback callbacks
    as_.registerGoalCallback(boost::bind(&ShapeAction::goalCB, this));
    as_.registerPreemptCallback(boost::bind(&ShapeAction::preemptCB, this));

    //subscribe to the data topic of interest
    sub_ = nh_.subscribe("/turtle1/pose", 1, &ShapeAction::controlCB, this);
    pub_ = nh_.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 1);

    as_.start();
  }

  ~ShapeAction(void)
  {
  }

  void goalCB()
  {
    // accept the new goal
    turtle_actionlib::ShapeGoal goal = *as_.acceptNewGoal();
    //save the goal as private variables
    edges_ = goal.edges;
    radius_ = goal.radius;

    // reset helper variables
    interior_angle_ = ((edges_-2)*M_PI)/edges_;
    apothem_ = radius_*cos(M_PI/edges_);
    //compute the side length of the polygon
    side_len_ = apothem_ * 2* tan( M_PI/edges_);
    //store the result values
    result_.apothem = apothem_;
    result_.interior_angle = interior_angle_;
    edge_progress_ =0;
    start_edge_ = true;
  }

  void preemptCB()
  {
    ROS_INFO("%s: Preempted", action_name_.c_str());
    // set the action state to preempted
    as_.setPreempted();
  }

  void controlCB(const turtlesim::Pose::ConstPtr& msg)
  {
    // make sure that the action hasn't been canceled
    if (!as_.isActive())
      return;
  
    if (edge_progress_ < edges_)
    {
      // scalar values for drive the turtle faster and straighter
      double l_scale = 6.0;
      double a_scale = 6.0;
      double error_tol = 0.00001;

      if (start_edge_)
      {
        start_x_ = msg->x;
        start_y_ = msg->y;
        start_theta_ = msg->theta;
        start_edge_ = false;
      }

      // compute the distance and theta error for the shape
      dis_error_ = side_len_ - fabs(sqrt((start_x_- msg->x)*(start_x_-msg->x) + (start_y_-msg->y)*(start_y_-msg->y)));
      theta_error_ = angles::normalize_angle_positive(M_PI - interior_angle_ - (msg->theta - start_theta_));
     
      if (dis_error_ > error_tol)
      {
        command_.linear.x = l_scale*dis_error_;
        command_.angular.z = 0;
      }
      else if (dis_error_ < error_tol && fabs(theta_error_)> error_tol)
      { 
        command_.linear.x = 0;
        command_.angular.z = a_scale*theta_error_;
      }
      else if (dis_error_ < error_tol && fabs(theta_error_)< error_tol)
      {
        command_.linear.x = 0;
        command_.angular.z = 0;
        start_edge_ = true;
        edge_progress_++;
      }  
      else
      {
        command_.linear.x = l_scale*dis_error_;
        command_.angular.z = a_scale*theta_error_;
      } 
      // publish the velocity command
      pub_.publish(command_);
      
    } 
    else
    {          
      ROS_INFO("%s: Succeeded", action_name_.c_str());
      // set the action state to succeeded
      as_.setSucceeded(result_);
    }   
  }

protected:
  ros::NodeHandle nh_;
  actionlib::SimpleActionServer<turtle_actionlib::ShapeAction> as_;
  std::string action_name_;
  double radius_, apothem_, interior_angle_, side_len_;
  double start_x_, start_y_, start_theta_;
  double dis_error_, theta_error_;
  int edges_ , edge_progress_;
  bool start_edge_;
  geometry_msgs::Twist command_;
  turtle_actionlib::ShapeResult result_;
  ros::Subscriber sub_;
  ros::Publisher pub_;
};

int main(int argc, char** argv)
{
  ros::init(argc, argv, "turtle_shape");

  ShapeAction shape(ros::this_node::getName());
  ros::spin();

  return 0;
}

client代码:

#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/client/terminal_state.h>
#include <turtle_actionlib/ShapeAction.h>

int main (int argc, char **argv)
{
  ros::init(argc, argv, "test_shape"); 

  // create the action client
  // true causes the client to spin it's own thread
  actionlib::SimpleActionClient<turtle_actionlib::ShapeAction> ac("turtle_shape", true); 

  ROS_INFO("Waiting for action server to start.");
  // wait for the action server to start
  ac.waitForServer(); //will wait for infinite time
 
  ROS_INFO("Action server started, sending goal.");
  // send a goal to the action 
  turtle_actionlib::ShapeGoal goal;
  goal.edges = 5;
  goal.radius = 1.3;
  ac.sendGoal(goal);
  
  //wait for the action to return
  bool finished_before_timeout = ac.waitForResult(ros::Duration(40.0));

  if (finished_before_timeout)
    {
      actionlib::SimpleClientGoalState state = ac.getState();
      ROS_INFO("Action finished: %s",state.toString().c_str());
    }
  else  
    ROS_INFO("Action did not finish before the time out.");

  //exit
  return 0;
}

认真读懂代码后,小修改一下程序,画8边形等。

----

1 使用回调函数编写简单动作服务端和客户端

本教程涵盖使用simple_action_server库创建Fibonacci操作服务器。此示例操作服务器生成斐波纳契序列,目标是序列的顺序,反馈是计算的序列,结果是最终序列。

首先要创建动作消息,然后编写简单服务器。具体参考官网wiki。

$ rosrun actionlib_tutorials fibonacci_server [ INFO] [1489479587.031699478]: fibonacci: Executing, creating fibonacci sequence of order 20 with seeds 0, 1 [ INFO] [1489479607.031776190]: fibonacci: Succeeded

然后,编写客户端。

$ rosrun actionlib_tutorials fibonacci_client [ INFO] [1489479586.767608642]: Waiting for action server to start. [ INFO] [1489479587.030931151]: Action server started, sending goal. [ INFO] [1489479607.032538514]: Action finished: SUCCEEDED

$ rostopic echo /fibonacci/result header:   seq: 0   stamp:     secs: 1489479774     nsecs: 565314290   frame_id: '' status:   goal_id:     stamp:       secs: 1489479754       nsecs: 564505228     id: /test_fibonacci-1-1489479754.564505228   status: 3   text: '' result:   sequence: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946] ---

Python版本参考官网~

----

2 使用目标回调方式编写简单动作服务端和客户端

这包括从上面显示的Averaging.action文件生成的操作消息。这是从AveragingAction.msg文件自动生成的头。有关消息定义的更多信息,请参阅msg页面。

$ rosrun actionlib_tutorials gen_numbers.py /home/relaybot/ROS_tutorial/src/common_tutorials-kinetic-devel/actionlib_tutorials/scripts/gen_numbers.py:14: SyntaxWarning: The publisher should be created with an explicit keyword argument 'queue_size'. Please see http://wiki.ros.org/rospy/Overview/Publishers%20and%20Subscribers for more information.   pub = rospy.Publisher('random_number', Float32) [INFO] [1489480362.608077]: Generating random numbers

如果遇到.py无法运行情况,参考chmod +x xxx.py;

$ rosrun actionlib_tutorials averagingerver [ INFO] [1489480293.583761868]: /averaging: Preempted [ INFO] [1489480367.859222479]: /averaging: Succeeded [ INFO] [1489480373.959334705]: /averaging: Aborted [ INFO] [1489480388.709328974]: /averaging: Succeeded [ INFO] [1489480421.359241543]: /averaging: Aborted [ INFO] [1489480429.459318829]: /averaging: Succeeded

$ rosrun actionlib_tutorials averaging_client [ INFO] [1489480416.045880779]: Waiting for action server to start. [ INFO] [1489480416.327395926]: Action server started, sending goal. [ INFO] [1489480421.360004624]: Action finished: ABORTED relaybot@relaybot-desktop:~/ROS_tutorial$ rosrun actionlib_tutorials averaging_client [ INFO] [1489480424.163872017]: Waiting for action server to start. [ INFO] [1489480424.450870275]: Action server started, sending goal. [ INFO] [1489480429.460235297]: Action finished: SUCCEEDED

$ rostopic echo /averaging/result header:   seq: 4   stamp:     secs: 1489480421     nsecs: 359319144   frame_id: '' status:   goal_id:     stamp:       secs: 1489480416       nsecs: 327495617     id: /test_averaging-1-1489480416.327495617   status: 4   text: '' result:   mean: 4.91720485687   std_dev: 0.955443501472 --- header:   seq: 5   stamp:     secs: 1489480429     nsecs: 459373174   frame_id: '' status:   goal_id:     stamp:       secs: 1489480424       nsecs: 450959415     id: /test_averaging-1-1489480424.450959415   status: 3   text: '' result:   mean: 5.10838794708   std_dev: 0.911464750767 ---

----

3 编写一个基于SimpleActionClient回调函数

$ rosrun actionlib_tutorials fibonacci_callback_client [ INFO] [1489480914.681402037]: Waiting for action server to start. [ INFO] [1489480914.902727647]: Action server started, sending goal. [ INFO] [1489480914.903744185]: Goal just went active [ INFO] [1489480914.903939017]: Got Feedback of length 3 [ INFO] [1489480915.903842332]: Got Feedback of length 4 [ INFO] [1489480916.904067905]: Got Feedback of length 5 [ INFO] [1489480917.904147835]: Got Feedback of length 6 [ INFO] [1489480918.904100351]: Got Feedback of length 7 [ INFO] [1489480919.904197519]: Got Feedback of length 8 [ INFO] [1489480920.904099212]: Got Feedback of length 9 [ INFO] [1489480921.904155594]: Got Feedback of length 10 [ INFO] [1489480922.904136690]: Got Feedback of length 11 [ INFO] [1489480923.904118087]: Got Feedback of length 12 [ INFO] [1489480924.904240841]: Got Feedback of length 13 [ INFO] [1489480925.904160093]: Got Feedback of length 14 [ INFO] [1489480926.904194218]: Got Feedback of length 15 [ INFO] [1489480927.904136215]: Got Feedback of length 16 [ INFO] [1489480928.904171361]: Got Feedback of length 17 [ INFO] [1489480929.904130197]: Got Feedback of length 18 [ INFO] [1489480930.904089898]: Got Feedback of length 19 [ INFO] [1489480931.904149072]: Got Feedback of length 20 [ INFO] [1489480932.904232818]: Got Feedback of length 21 [ INFO] [1489480933.904166482]: Got Feedback of length 22 [ INFO] [1489480934.904440340]: Finished in state [SUCCEEDED] [ INFO] [1489480934.904546340]: Answer: 10946

$ rosrun actionlib_tutorials fibonacci_class_client [ INFO] [1489480960.866926239]: Waiting for action server to start. [ INFO] [1489480961.086980182]: Action server started, sending goal. [ INFO] [1489480971.088604073]: Finished in state [SUCCEEDED] [ INFO] [1489480971.088696472]: Answer: 89

----

----

----

----

----

----

----

机器翻译

actionlib--详细说明--

介绍

此页面不适用于刚刚开始使用actionlib的人。相反,它描述了客户端和服务器之间相互作用的底层机制。

如果只想使用SimpleActionClientSimpleActionServer,则无需了解此页面上的概念。然而,如果简单的客户端和服务器不够描述,理解这些概念提供了对调试客户端/服务器交互或实现备用客户端/服务器策略有用的洞察。

高级客户端/服务器交互

服务器描述

服务器状态机

  • 目标由ActionClient启动。ActionServer接收到目标后,ActionServer会创建一个状态机来跟踪目标的状态:

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

服务器转换

  • 这些状态转换中的大多数由服务器实现者使用一小组可能的命令触发:
    • setAccepted - 检查目标后,决定开始处理它
    • setRejected - 检查目标后,决定从不处理它,因为它是一个无效的请求(超出范围,资源不可用,无效等)
    • setSucceeded - 通知目标已成功处理
    • setAborted - 通知目标在处理过程中遇到错误,必须中止
    • setCanceled - 由于取消请求,通知该目标不再处理

    操作客户端还可以异步触发状态转换:

    • CancelRequest:客户端通知操作服务器它希望服务器停止处理目标。
服务器状态
  • 中间国
    • 待处理 - 目标尚未由操作服务器处理
    • 活动 - 目标正由操作服务器处理
    • 回忆 - 目标尚未处理,并且已收到来自操作客户端的取消请求,但操作服务器尚未确认目标已取消
    • 抢占 - 目标正在处理,并且已从操作客户端接收到取消请求,但操作服务器尚未确认目标已取消

    终端国

    • 已拒绝 - 目标被操作服务器拒绝,未经处理,没有来自操作客户端的取消请求
    • 成功 - 操作服务器成功完成了目标
    • 中止 -我们的目标是终止了行动服务器,而不从操作客户端的外部请求取消
    • 已恢复 - 在操作服务器开始处理目标之前,目标已被另一个目标或取消请求取消
    • 抢占 - 目标的处理被另一个目标取消,或取消请求发送到操作服务器
并发问题
  • setAccepted-CancelRequest vs CancelRequest-setAccepted: 这是有点不直观,一个动作服务器可以setAccepted它已经收到了即使经过一个目标CancelRequest。这是因为有一个竞争条件CancelRequest被异步处理。也就是说,由于服务器实现者的代码之外的其他东西触发转换,服务器实现者不能确定它们是处于[PENDING]还是[RECALLING]状态。

客户描述

客户机状态机

  • 在actionlib中,我们将服务器状态机视为主机,然后将客户机状态机视为辅助/耦合状态机,尝试跟踪服务器的状态:

客户端过渡

  • 服务器触发的转换
    • 报告的[状态]:由于客户端正在尝试跟踪服务器的状态,大多数转换由服务器报告其状态到ActionClient触发。
    • 接收结果消息:在这种情况下,该服务器发送一个结果消息给客户端。接收结果将始终表示跟踪结束的目标。

    客户端触发转换

    • 取消目标:请求服务器停止处理此目标

    “跳过”状态

    • 给定我们的基于ROS的传输层,客户端可能没有从服务器接收所有的状态更新。因此,我们必须允许客户端状态机“跳过”服务器触发状态。
      • 例如:如果客户端在[WAITING FOR目标ACK] ,并接收[已抢先]从服务器状态更新,客户端的状态可以跳过[活动],并过渡到直接[WAITING FOR RESULT]
    • 由于多个动作客户端可以连接到单个动作服务器,所以第二客户端可以取消由第一客户端发送的目标。因此,它是有效的为客户端过渡从[未决][回顾]如果[回顾]是从服务器接收到的状态。

动作接口和传输层

  • 操作客户端和服务器之间通过预定的通信动作协议。此操作协议依赖于指定ROS命名空间中的ROS主题以传输消息。
  • ROS消息
    • goal - 用于向服务器发送新目标
    • cancel - 用于向服务器发送取消请求
    • status - 用于通知客户端系统中每个目标的当前状态。
    • 反馈 - 用于向客户发送目标的定期辅助信息。
    • result - 用于在目标完成时向客户端发送一次性辅助信息

数据关联和目标ID

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

消息

目标主题:发送目标

  • 目标主题使用自动生成的ActionGoal消息(示例:actionlib / TestActionGoal),用于向操作服务器发送新目标。实质上,ActionGoal消息包装了目标消息,并将其与目标ID捆绑在一起。 当发送目标时,动作客户端通常生成唯一的目标ID和时间戳。然而,可能的是,天真(也称为哑)客户端可能留下这些空。如果是,操作服务器将填充它们。
    • 邮戳:在操作服务器接收时,邮票设置为now()
    • ID:收到由动作服务器,ID是自动生成的。请注意,此ID不是非常有用,因为操作客户端没有任何方式知道服务器为其目标生成的ID。

取消主题:取消目标

  • cancel主题使用actionlib_msgs / GoalID消息,并让操作客户端向操作服务器发送取消请求。每个取消消息都有一个时间戳和目标ID,以及这些消息字段的填充方式会影响哪些目标被取消:

状态主题:服务器目标状态更新

  • 状态主题使用actionlib_msgs / GoalStatusArray,并给出操作的客户服务器目标状态有关目前正在动作服务器跟踪每一个目标的信息。这由动作服务器以某个固定速率(通常为10Hz)发送,并且也在任何服务器目标状态转换上异步发送。 目标由动作服务器跟踪,直到它到达终端状态。然而,为了增加通信鲁棒性,服务器在达到终端状态后再发布该目标的状态几秒钟。

反馈主题:异步目标信息

  • 反馈主题使用自动生成的ActionFeedback消息(示例:todo),并为服务器实施者提供在目标处理期间向操作客户端发送定期更新的方法。由于ActionFeedback具有目标ID,因此操作客户端可以确定它是应该使用还是丢弃它接收到的反馈消息。发送反馈是完全可选的。

结果主题:完成后的目标信息

  • 结果主题使用自动生成的ActionResult消息(示例:actionlib / TestActionResult),并为服务器实施者提供在目标完成后向操作客户端发送信息的方法。由于ActionResult有一个目标ID,动作客户端可以确定它应该使用还是丢弃结果消息。虽然结果可以是空消息,但它是操作接口的必需部分,并且必须始终在目标完成时发送。因此,必须在转换到任何终端状态(拒绝,调用,抢占,中止,成功)时发送结果,

政策

简单操作客户端

  • 一般来说,高级应用程序和高管只关心目标是否正在处理,或者是否完成。他们很少关心所有的中间状态。在简单的动作客户端因素原始客户端状态机的三种状态:等待,活动,完成与

客户状态歧义

  • 注意,单独的客户端状态不足以确定简单的客户端状态。然而,这可以通过查看客户端状态转换来容易地解决。如果客户端状态转换不在任何简单客户端状态之间交叉,则不更新简单客户端状态示例:如果客户端从RECALLING转换WAITING FOR RESULT,则简单客户端状态将仍然为PENDING

多目标政策

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

线程模型(C ++)

  • 构造简单动作客户端时,用户决定是否旋转额外的线程。
    • 无额外螺纹(推荐)
      • 操作客户端中的所有订户都向全局回调队列注册。
      • 用户的动作回调从ros :: spin()中调用。因此,在用户的动作回调中的阻塞将阻止全局回调队列被服务。
    • 旋转一个线程
      • 操作客户端中的所有订户都注册一个回调队列,与全局回调队列分开。此队列由启动线程提供服务。
      • 用户的动作回调从启动线程调用。尽管阻塞在动作回调不会阻止其他ROS消息被服务,但它仍然是一个坏主意,因为这个操作的状态,反馈和结果消息不能被服务。
      • 一个(而且唯一?)的优点是旋转额外的线程是一个用户可能能够避免调用ros :: spin()在他们的应用程序。

简单操作服务器

许多动作服务器遵循类似的模式,其中一次只能有一个目标是活动的,每个新目标抢占前一个目标。简单动作服务器是一个动作服务器的包装器,旨在强制执行这个简单的策略来处理目标。

在从动作客户端接收到新目标时,简单动作服务器将该目标移动到其待决槽中。如果目标已经占用了挂起的插槽,则简单动作服务器将该目标设置为取消,并将其替换为通过线路传递的目标。

一旦新目标被简单动作服务器接收并被移动到待决槽中,简单动作服务器的用户被通知新目标可用。此通知以以下两种方式之一发生,如“目标通知”部分所述。在接收到通知时,用户可以接受使得待决槽中的目标移动到当前目标槽的目标,并且允许用户修改与新接受的目标相关联的状态机。

目标通知

用户可以通过两种方式接收简单动作服务器已经接收到新目标的通知:

  • 回调通知:这里,用户在构建时向简单动作服务器注册回调,当新目标移动到简单动作服务器的挂起槽时被调用。用户可以在回调中接受新目标,或者在准备好时通知另一个线程接受目标。
  • 轮询通知:这里用户要求简单动作服务器显式地确定新目标是否可用。简单动作服务器基于自从上次进行新的目标查询以来新目标是否已移动到未决插槽中来回答此查询。

线程模型(C ++)

构造简单动作服务器时,用户决定是否旋转额外的线程以允许在目标回调中采取长时间运行的动作。

  • 无额外螺纹(推荐)
    • 在为新目标接收的回调中执行的任何操作不应长时间运行。当然,用户可以发信号通知另一个线程做工作,但不应该阻塞。
    • 或者,用户可以使用轮询实现来检查新目标的可用性,并完全避免回调。
  • 旋转一个线程
    • 生成单独的线程以允许用户在新目标可用时接收到的回调中执行长时间运行或阻塞动作。在此回调中,用户还可以轮询简单操作服务器以检查新目标是否可用。
    • 简单动作服务器为用户旋转线程的优点是用户不必处理管理另一线程的开销。然而,重要的是用户应该知道这个线程存在,以便他们遵循标准的线程安全约定,如锁定。

概述

在任何大的基于ROS的系统中,有时候有人想要向节点发送请求以执行某个任务,并且还接收对该请求的答复。这可以通过目前ROS可实现的服务

然而,在某些情况下,如果服务需要很长时间来执行,则用户可能想要在执行期间取消请求的能力或获得关于请求如何进展的定期反馈。该actionlib包提供的工具来创建执行可抢占长期运行的目标服务器。它还提供了一个客户端接口,以便向服务器发送请求。

详细说明

对于actionlib如何“引擎盖下”工作的全面讨论,请参阅 详细说明

客户端 - 服务器交互

ActionClientActionServer经由通信“ROS操作协议”,这是建立在ROS消息的顶部。客户端和服务器然后为用户提供一个简单的API,以通过函数调用和回调请求目标(在客户端)或执行目标(在服务器端)。

操作规范:目标,反馈和结果

为了使客户端和服务器进行通信,我们需要定义一些他们进行通信的消息。这是一个动作规范。这定义了客户端和服务器与之通信的目标,反馈和结果消息:

目标 要使用动作完成任务,我们介绍一个可以由ActionClient发送到ActionServer的目标的概念。在移动基地的情况下,目标将是PoseStamped消息,其包含关于机器人应该在世界中移动到哪里的信息。为了控制倾斜激光扫描器,目标将包含扫描参数(最小角度,最大角度,速度等)。

反馈 反馈为服务器实现者提供了一种告诉ActionClient关于目标的增量进度的方法。对于移动基地,这可能是机器人沿着路径的当前姿势。为了控制倾斜激光扫描器,这可以是直到扫描完成的时间。

结果 完成目标后,结果将从ActionServer发送到ActionClient。这与反馈不同,因为它只发送一次。当动作的目的是提供某种信息时,这是非常有用的。对于移动基准,结果不是很重要,但它可能包含机器人的最终姿态。为了控制倾斜激光扫描器,结果可能包含从请求的扫描生成的点云。

.action文件

动作规格是使用限定.action文件。该.action文件具有目标定义,随后的结果定义,随后通过反馈定义,与由3连字号(分隔每个部分---)。

这些文件放在包的./action目录中,看起来非常类似于服务的.srv文件。做菜的操作规范可能如下所示:

./action/DoDishes.action

#定义目标
uint32 dishwasher_id#指定要使用的洗碗机
--- ---
#定义结果
uint32 total_dishes_cleaned
--- ---
#定义反馈消息
float32 percent_complete

基于此.action文件,需要生成6个消息,以便客户端和服务器进行通信。该生成可以在make过程中自动触发:

猫科

以下添加到您的CMakeLists.txt文件之前 catkin_package() 。

find_package(catkin REQUIRED genmsg actionlib_msgs actionlib)
add_action_files(DIRECTORY操作FILES DoDishes.action)
generate_messages(DEPENDENCIES actionlib_msgs)

此外,包的package.xml必须包含以下依赖项:

<build_depend> actionlib </ build_depend>
<build_depend> actionlib_msgs </ build_depend>
<run_depend> actionlib </ run_depend>
<run_depend> actionlib_msgs </ run_depend>

Rosbuild

如果您正在使用rosbuild而不是柳絮,而不是添加以下之前 rosbuild_init() 。

rosbuild_find_ros_package(actionlib_msgs)
include($ {actionlib_msgs_PACKAGE_PATH} /cmake/actionbuild.cmake)
genaction()

然后,输出路径之后,取消注释(或添加)

rosbuild_genmsg()

注: rosbuild_genmsg()的输出路径设置完成后,必须调用。

对于1.0系列(即boxturtle)使用:

rosbuild_find_ros_package(actionlib)
include($ {actionlib_PACKAGE_PATH} /cmake/actionbuild.cmake)
genaction()
rosbuild_genmsg()

此外,包的manifest.xml必须包含以下依赖关系:

<depends package =“actionlib”/>
<depends package =“actionlib_msgs”/>

结果

对于DoDishes.action,以下消息由genaction.py生成:

  • DoDishesAction.msg
  • DoDishesActionGoal.msg
  • DoDishesActionResult.msg
  • DoDishesActionFeedback.msg
  • DoDishesGoal.msg
  • DoDishesResult.msg
  • DoDishesFeedback.msg

然后,actionlib在内部使用这些消息在ActionClient和ActionServer之间进行通信。

使用ActionClient

C ++ SimpleActionClient

为全面API参考C ++ SimpleActionClient

快速入门指南: 假设你已经定义DoDishes.action在家务包。以下代码段显示如何将目标发送到名为“do_dishes”的DoDishes ActionServer。

切换行号

   1 #包括<家务/ DoDishesAction.h>
   2 #包括<actionlib / 客户/ simple_action_client.h>
   3 
   4 typedef  actionlib :: SimpleActionClient < chores :: DoDishesAction > Client ; 
   5 
   6 int  main(int  argc,char ** argv)
   7 { 
   8   ros :: init(argc,argv,“ do_dishes_client ”); 
   9   客户 端客户端(“ do_dishes ”,true); // true  - >不需要ros :: spin()
  10   客户端。waitForServer(); 
  11   chores :: DoDishesGoal  目标 ; 
  12   //在这里填写目标
  13   客户端。sendGoal(goal); 
  14   客户端。waitForResult(ros :: Duration(5.0)); 
  15   如果(客户端。的getState()== actionlib :: SimpleClientGoalState :: SUCCEEDED)
  16     printf(“ Yay!The dishes are now clean ”); 
  17   的printf(“ 当前状态:%S \ n ”,客户端。的getState()的toString()。c_str()); 
  18   return  0 ; 
  19 }}

注意:对于C ++ SimpleActionClient,waitForServer方法将仅在单独的线程服务客户端的回调队列时才起作用。这需要在传入真为spin_thread客户端的构造的选择,用多线程喷丝运行,或使用自己的线程服务的ROS回调队列。

Python SimpleActionClient

为全面API参考Python的SimpleActionClient

假设DoDishes.action存在于差事包中。以下代码段显示如何使用Python将目标发送到名为“do_dishes”的DoDishes ActionServer。

切换行号

   1 #!/ usr / bin / env python 
   2 
   3 import  roslib 
   4 roslib。load_manifest(' my_pkg_name ')
   5 import  rospy 
   6 import  actionlib 
   7 
   8 从 chores.msg  导入 DoDishesAction,DoDishesGoal 
   9 
  10 如果 __name__ == ' __main__ ':
  11     rospy。init_node(' do_dishes_client ')
  12     client = actionlib。SimpleActionClient(' do_dishes ',DoDishesAction)
  13     客户端。wait_for_server()
  14 
  15     goal = DoDishesGoal()
  16     #填写这里的目标
  17     客户端。send_goal(goal)
  18     客户端。wait_for_result(rospy。持续时间。from_sec(5.0))

实现ActionServer

C ++ SimpleActionServer

为全面API参考C ++ SimpleActionServer

快速入门指南: 假设你已经定义DoDishes.action在家务包。以下代码段显示了如何编写名为“do_dishes”的DoDishes ActionServer。

切换行号

   1 #包括<家务/ DoDishesAction.h>
   2 #包括<actionlib / 服务器/ simple_action_server.h>
   3 
   4 typedef  actionlib :: SimpleActionServer < chores :: DoDishesAction > 服务器 ; 
   5 
   6 void  execute(const  chores :: DoDishesGoalConstPtr&goal,Server * as)
   7 { 
   8   //在这里做很多真棒破解机器人的东西
   9   as - > setSucceeded(); 
  10 } 
  11 
  12 int  main(int  argc,char ** argv)
  13 { 
  14   ros :: init(argc,argv,“ do_dishes_server ”); 
  15   ros :: NodeHandle  n ; 
  16   服务器 服务器(n,“ do_dishes ”,boost :: bind(&execute,_1,&server),false); 
  17   服务器。start(); 
  18   ros :: spin(); 
  19   return  0 ; 
  20 }}

Python SimpleActionServer

为全面API参考Python的SimpleActionServer

快速入门指南: 假设你已经定义DoDishes.action在家务包。以下代码段显示了如何编写名为“do_dishes”的DoDishes ActionServer。

切换行号

   1 #!/ usr / bin / env python 
   2 
   3 import  roslib 
   4 roslib。load_manifest(' my_pkg_name ')
   5 import  rospy 
   6 import  actionlib 
   7 
   8 从 chores.msg  导入 DoDishesAction 
   9 
  10 类 DoDishesServer:
  11   def  __init__(self):
  12     自我。server = actionlib。SimpleActionServer(' do_dishes ',DoDishesAction,自我,执行,假)
  13     自我。服务器。start()
  14 
  15   def  execute(self,goal):
  16     #做很多真棒破碎机器人的东西在这里
  17     自我。服务器。set_succeeded()
  18 
  19 
  20 如果 __name__ == ' __main__ ':
  21   rospy。init_node(' do_dishes_server ')
  22   server = DoDishesServer()
  23   rospy。spin()

SimpleActionServer目标策略

该SimpleActionServer实现在顶部的单一目标政策ActionServer类。政策的规范如下:

  • 一次只能有一个目标具有活动状态
  • 新目标会根据其目标ID字段中的戳记(之后的目标优先于之前的目标)抢占之前的目标
  • 显式抢占目标利用小于或等于与抢占相关联的戳记的时间戳来抢占所有目标
  • 接受新目标意味着成功抢占任何旧目标,旧目标的状态将自动更改以反映这一点

调用acceptNewGoal在有可用时接受新目标。状态这个目标设置为接受时激活,任何以前的状态 活动目标设置为抢占。抢先接收新目标之间检查isNewGoalAvailable或调用目标回调和acceptNewGoal调用不会触发抢占回调。意即,isPreemptRequested应该在接受目标后调用基于回调的实现,以确保新的目标没有 等待抢占请求。

教程

请参阅教程页面

报告错误

使用trac 报告错误请求功能。[ 查看活动票券 ]

----

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏AI科技评论

大讲堂 | 面向实际应用的欠驱动机械手设计

机械手作为机器人的末端执行器,对机器人实际的操作和抓取性能有着决定性作用。实际上,集成在机器人上承担真实环境操作任务的机械手和实验室环境的机械手在设计和功能上的...

10130
来自专栏AI研习社

明晚20点大讲堂 | 面向实际应用的欠驱动机械手设计

机械手作为机器人的末端执行器,对机器人实际的操作和抓取性能有着决定性作用。实际上,集成在机器人上承担真实环境操作任务的机械手和实验室环境的机械手在设计和功能上的...

14040
来自专栏钱塘大数据

2018年智能机器人研究报告发布(附PDF)

近二十年来,互联网的发展带动了一系列网络延展科技的发展,给人们的生活带来了翻天覆地的变化。未来互联网将向物联网发展,而在物联网的时代,RT(Robotics T...

24710
来自专栏大数据文摘

学界 | 伯克利最新:基于视觉模型强化学习的通用机器人

有时候,只要看一眼,有些天分的人就能进行模仿。用学术一点的话说就是:只需少量的明确监督和反馈,人类就可以通过简单的交互和对世界的生理感知,来学习各种运动技能。

16720
来自专栏ATYUN订阅号

谷歌教机器人通过与环境的交互来识别物体

谷歌希望使AI系统至少在对象识别和感知方面,能像儿童那样思考。在论文“Grasp2Vec: Learning Object Representations fr...

10620
来自专栏量子位

当一盆植物在MIT成了精,不,它只是成了机器人

这些伶俐的反应,被MIT和Parsons设计学院的研究人员地利用起来,才有了机器人 x 植物这样优雅的存在。

12640
来自专栏大数据文摘

快讯 | 日本这家全是机器人的咖啡馆,给残疾人创造了新岗位

机器将大规模抢走人类工作岗位的言论在日常生活中并不少见。所以目前来说,人与机器平等的相处的想法似乎只存在于科幻小说中。毕竟,这个时代,人与自然都不能和谐的相处,...

10730
来自专栏华章科技

机器人行走背后的机械原理动画,一文看透

导读:波士顿动力机器人Atlas如今已经能跑能跳,还能躲避障碍。人形机器人一直是AI行业的研发热点,如果想让人形机器人动起来,就得先了解一下动作背后的机械原理。

23430
来自专栏大数据文摘

业界 | 大乌龙,俄罗斯最先进的机器人里头竟然是个小伙子

动动退,扭扭腰~~~你看现在的机器人多么自然,是不是边上的主持人和它比起来更像机器人。

9230
来自专栏量子位

亚马逊自动化仓库事故,机器人失手戳破有毒喷雾,24名工人被送医院

昨晚(12月5日),亚马逊自动化仓库发生机器人事故,造成24名员工直接受伤被送医院,1名员工进重症监护室,50多名员工受影响。

9830

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励