Note: This tutorial assumes that you have completed the previous tutorials: ROS教程.
(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

使用Execute Callback编写一个简单的行为服务器

Description: 这个教程涵盖了使用simple_action_server库来创建一个Fibonacci行为服务器。这个行为服务器教程会生成一个Fibonacci序列、目标的顺序序列、自身计算的反馈序列和最后序列的结果。

Tutorial Level: BEGINNER

Next Tutorial: 编写一个简单的行为客户端

创建行为消息

开始编写行为之前,非常重要的事是定义目标,结果和反馈消息。行为消息会自动从.action文件生成,对于更多行为文件信息查看actionlib文档。这个文件定义目标、结果和行为的反馈话题的类型和文本格式。使用你最喜欢的文本编辑器创建actionlib_tutorials/action/Fibonacci.action文件,并且用以下内容在该文件中添加: https://raw.githubusercontent.com/ros/common_tutorials/hydro-devel/actionlib_tutorials/action/Fibonacci.action

#goal definition
int32 order
---
#result definition
int32[] sequence
---
#feedback
int32[] sequence

为了在make进程中自动生成消息文件,需要在CMakeLists.txt中添加一些小的内容。

  • 添加actionlib_msgs包作为参数到find_package这个宏中,就像这样(如果你使用catkin_create_package生成的CMakeLists.txt,这一行可能已经添加):

    find_package(catkin REQUIRED COMPONENTS actionlib_msgs)
    • 注意CMake需要find_package actionlib_msgs (message_generation不需要明确的列出,因为以及被actionlib_msgs隐式的参考了).

  • 使用add_action_files 宏来声明你想要生成的行为:

    add_action_files(
      DIRECTORY action
      FILES Fibonacci.action
    )
  • 调用generate_messages宏,不要忘记依赖actionlib_msgs和其他消息包,例如std_msgs

    generate_messages(
      DEPENDENCIES actionlib_msgs std_msgs  # 或其他包含消息的包
    )
  • 添加actionlib_msgscatkin_package宏,例如这样:

    catkin_package(
      CATKIN_DEPENDS actionlib_msgs
    )
    • catkin_package也指定了CATKIN_DEPENDactionlib_msgsmessage_runtime自动传递依赖。

注意:有时你需要设置你的package.xml,一旦完成在清单文件里面声明的,同时将生成消息。你可以插入以下行。

<exec_depend>message_generation</exec_depend>

现在遵循以下,会自动生成你的action文件的msg文件,并且可以查看结果。

$ cd ../.. # 返回到你的catkin工作空间的最顶层
$ catkin_make
$ ls devel/share/actionlib_tutorials/msg/
FibonacciActionFeedback.msg  FibonacciAction.msg        FibonacciFeedback.msg
FibonacciResult.msg          FibonacciActionGoal.msg    FibonacciActionResult.msg  FibonacciGoal.msg
$ ls devel/include/actionlib_tutorials/
FibonacciActionFeedback.h  FibonacciAction.h        FibonacciFeedback.h  FibonacciResult.h
FibonacciActionGoal.h      FibonacciActionResult.h  FibonacciGoal.h

为了在make进程中自动生成消息文件,在CMakeLists.txt添加以下内容 (在调用rosbuild_init之前)

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

你也需要确保通过genaction.py脚本生成的消息文件会生成头文件。为了完成这个,取消你的CMakeLists.txt中的以下行,如果你还没有做的话。

rosbuild_genmsg()

为了从这个文件手动生成消息文件,从actionlib_msgs包使用genaction.py脚本。

编写一个简单的服务器

代码

首先,使用你最喜欢的编辑器创建actionlib_tutorials/src/fibonacci_server.cpp,随后用以下内容填充:

   1 #include <ros/ros.h>
   2 #include <actionlib/server/simple_action_server.h>
   3 #include <actionlib_tutorials/FibonacciAction.h>
   4 
   5 class FibonacciAction
   6 {
   7 protected:
   8 
   9   ros::NodeHandle nh_;
  10   actionlib::SimpleActionServer<actionlib_tutorials::FibonacciAction> as_; // NodeHandle instance must be created before this line. Otherwise strange error occurs.
  11   std::string action_name_;
  12   // create messages that are used to published feedback/result
  13   actionlib_tutorials::FibonacciFeedback feedback_;
  14   actionlib_tutorials::FibonacciResult result_;
  15 
  16 public:
  17 
  18   FibonacciAction(std::string name) :
  19     as_(nh_, name, boost::bind(&FibonacciAction::executeCB, this, _1), false),
  20     action_name_(name)
  21   {
  22     as_.start();
  23   }
  24 
  25   ~FibonacciAction(void)
  26   {
  27   }
  28 
  29   void executeCB(const actionlib_tutorials::FibonacciGoalConstPtr &goal)
  30   {
  31     // helper variables
  32     ros::Rate r(1);
  33     bool success = true;
  34 
  35     // push_back the seeds for the fibonacci sequence
  36     feedback_.sequence.clear();
  37     feedback_.sequence.push_back(0);
  38     feedback_.sequence.push_back(1);
  39 
  40     // publish info to the console for the user
  41     ROS_INFO("%s: Executing, creating fibonacci sequence of order %i with seeds %i, %i", action_name_.c_str(), goal->order, feedback_.sequence[0], feedback_.sequence[1]);
  42 
  43     // start executing the action
  44     for(int i=1; i<=goal->order; i++)
  45     {
  46       // check that preempt has not been requested by the client
  47       if (as_.isPreemptRequested() || !ros::ok())
  48       {
  49         ROS_INFO("%s: Preempted", action_name_.c_str());
  50         // set the action state to preempted
  51         as_.setPreempted();
  52         success = false;
  53         break;
  54       }
  55       feedback_.sequence.push_back(feedback_.sequence[i] + feedback_.sequence[i-1]);
  56       // publish the feedback
  57       as_.publishFeedback(feedback_);
  58       // this sleep is not necessary, the sequence is computed at 1 Hz for demonstration purposes
  59       r.sleep();
  60     }
  61 
  62     if(success)
  63     {
  64       result_.sequence = feedback_.sequence;
  65       ROS_INFO("%s: Succeeded", action_name_.c_str());
  66       // set the action state to succeeded
  67       as_.setSucceeded(result_);
  68     }
  69   }
  70 
  71 
  72 };
  73 
  74 
  75 int main(int argc, char** argv)
  76 {
  77   ros::init(argc, argv, "fibonacci");
  78 
  79   FibonacciAction fibonacci("fibonacci");
  80   ros::spin();
  81 
  82   return 0;
  83 }

代码解释

现在,让我们分块解释代码。

   1 #include <ros/ros.h>
   2 #include <actionlib/server/simple_action_server.h>
   3 

从实现简单行为中使用的行为库actionlib/server/simple_action_server.h。

   3 #include <actionlib_tutorials/FibonacciAction.h>
   4 

这个包含了从以上Fibonacci.action文件中生成的行为消息。这是从FibonacciAction.msg文件中自动生成的。对于更多关于消息定义的细节,查看msg界面.

   7 protected:
   8 
   9   ros::NodeHandle nh_;
  10   actionlib::SimpleActionServer<actionlib_tutorials::FibonacciAction> as_; // NodeHandle instance must be created before this line. Otherwise strange error occurs.
  11   std::string action_name_;
  12   // create messages that are used to published feedback/result
  13   actionlib_tutorials::FibonacciFeedback feedback_;
  14   actionlib_tutorials::FibonacciResult result_;

这些是行为类中受保护的变量。在创建行为的过程中,构造node handle并传递到行为服务器中。在行为的构造函数中构造行为服务器,并且关于行为服务器将会在以下讲述。创建反馈和结果消息用于在行为中发布。

  18   FibonacciAction(std::string name) :
  19     as_(nh_, name, boost::bind(&FibonacciAction::executeCB, this, _1), false),
  20     action_name_(name)
  21   {
  22     as_.start();
  23   }

在行为构造函数中,行为服务器会被创建。行为服务器会得到一个节点句柄(node handle)、行为名称和选择一个运行回调函数(executeCB)参数。在这个例子中,创建的行为服务器将回调函数(executeCB)作为参数。

  29   void executeCB(const actionlib_tutorials::FibonacciGoalConstPtr &goal)
  30   {

现在调用的executeCB函数以及在构造函数中创建。回调函数会传递一个指向目标消息的指针。注意: 这是一个boost共享指针, 在目标消息类型最后附加一个给定的"ConstPtr".

  32     ros::Rate r(1);
  33     bool success = true;
  34 
  35     // push_back the seeds for the fibonacci sequence
  36     feedback_.sequence.clear();
  37     feedback_.sequence.push_back(0);
  38     feedback_.sequence.push_back(1);
  39 
  40     // publish info to the console for the user
  41     ROS_INFO("%s: Executing, creating fibonacci sequence of order %i with seeds %i, %i", action_name_.c_str(), goal->order, feedback_.sequence[0], feedback_.sequence[1]);

在行为中创建内部参数。在这个例程中,发布ROS_INFO来让用户指定行为正在运行。

  43     // start executing the action
  44     for(int i=1; i<=goal->order; i++)
  45     {
  46       // check that preempt has not been requested by the client
  47       if (as_.isPreemptRequested() || !ros::ok())
  48       {
  49         ROS_INFO("%s: Preempted", action_name_.c_str());
  50         // set the action state to preempted
  51         as_.setPreempted();
  52         success = false;
  53         break;
  54       }
  55       feedback_.sequence.push_back(feedback_.sequence[i] + feedback_.sequence[i-1]);

一个行为服务器的一个重要组成部分是允许行为客户端请求取消当前目标执行。当一个客户端请求抢占当前目标是,行为服务器应该取消目标,随后执行重要的清理,然后调用函数setPreempted(),该函数会发出该行为已经被用户请求抢占信号。设置检查抢占请求服务器的等级到服务器系统。

  56       // publish the feedback
  57       as_.publishFeedback(feedback_);
  58       // this sleep is not necessary, the sequence is computed at 1 Hz for demonstration purposes
  59       r.sleep();
  60     }

这里,Fibonacci序列赋值给feedback变量,然后通过行为服务器提供的反馈频道发布出去。随后行为继续循环和发布反馈。

  62     if(success)
  63     {
  64       result_.sequence = feedback_.sequence;
  65       ROS_INFO("%s: Succeeded", action_name_.c_str());
  66       // set the action state to succeeded
  67       as_.setSucceeded(result_);
  68     }
  69   }

一旦行为完成计算Fibonacci序列,会通知行为客户端操作设置成功。

  75 int main(int argc, char** argv)
  76 {
  77   ros::init(argc, argv, "fibonacci");
  78 
  79   FibonacciAction fibonacci("fibonacci");
  80   ros::spin();
  81 
  82   return 0;
  83 }

最后main函数,创建行为然后spins节点。行为会运行并等待接收目标。

编译

在你的CMakeLists.txt添加如下行:

add_executable(fibonacci_server src/fibonacci_server.cpp)

target_link_libraries(
  fibonacci_server
  ${catkin_LIBRARIES}
)

add_dependencies(
  fibonacci_server
  ${actionlib_tutorials_EXPORTED_TARGETS}
)

完整的CMakeLists.txt如下:

cmake_minimum_required(VERSION 2.8.3)
project(actionlib_tutorials)

find_package(catkin REQUIRED COMPONENTS roscpp actionlib actionlib_msgs)
find_package(Boost REQUIRED COMPONENTS system)

add_action_files(
  DIRECTORY action
  FILES Fibonacci.action
)

generate_messages(
  DEPENDENCIES actionlib_msgs std_msgs
)

catkin_package(
  CATKIN_DEPENDS actionlib_msgs
)

include_directories(include ${catkin_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})

add_executable(fibonacci_server src/fibonacci_server.cpp)

target_link_libraries(
  fibonacci_server
  ${catkin_LIBRARIES}
)

add_dependencies(
  fibonacci_server
  ${actionlib_tutorials_EXPORTED_TARGETS}
)

rosbuild_add_executable(fibonacci_server src/fibonacci_server.cpp)

在ROS fuerte版本, 使用boost链接rosbuild_add_executable宏:

rosbuild_add_boost_directories()
rosbuild_add_executable(fibonacci_server src/fibonacci_server.cpp)
rosbuild_link_boost(fibonacci_server thread)

然后编译

$ rosmake actionlib_tutorials

运行行为服务器

在一个新终端中开启roscore

$ roscore

然后运行行为服务器:

$ rosrun actionlib_tutorials fibonacci_server

你可以看到类似如下输出:

  • [ INFO] 1250790662.410962000: Started node [/fibonacci], pid [29267], bound on [aqy], xmlrpc port [39746], tcpros port [49573], logging to [~/ros/ros/log/fibonacci_29267.log], using [real] time

想要检查你的行为运行是否正常的话,查看发布的话题列表:

$ rostopic list -v

你可以看到类似如下输出:

  • Published topics:
     * /fibonacci/feedback [actionlib_tutorials/FibonacciActionFeedback] 1 publisher
     * /fibonacci/status [actionlib_msgs/GoalStatusArray] 1 publisher
     * /rosout [rosgraph_msgs/Log] 1 publisher
     * /fibonacci/result [actionlib_tutorials/FibonacciActionResult] 1 publisher
     * /rosout_agg [rosgraph_msgs/Log] 1 publisher
    
    Subscribed topics:
     * /fibonacci/goal [actionlib_tutorials/FibonacciActionGoal] 1 subscriber
     * /fibonacci/cancel [actionlib_msgs/GoalID] 1 subscriber
     * /rosout [rosgraph_msgs/Log] 1 subscriber

另外你可以查看节点:

$ rqt_graph

fibonacci_server.png

这个显示你的行为服务器正在发布反馈、状态和期望的通道结果,和目标订阅以及期望的目标取消通道。行为服务器启动并运行正常。

发送一个目标到行为服务器

对于使用你的行为的下一步,你需要使用Ctrl-C关闭行为服务器,然后编写一个简单的行为客户端.


Wiki: cn/actionlib_tutorials/Tutorials/SimpleActionServer(ExecuteCallbackMethod) (last edited 2018-05-22 01:11:25 by Playfish)