【ROS總結】教程Actionlib——使用Execute Callback編寫一個簡單的行爲服務器

前言


  所有的Actionlib教程已經在中文ROS頁面服務器上翻譯,有興趣的可以查看總目錄: http://wiki.ros.org/cn/actionlib_tutorials/Tutorials


描述


   這個教程涵蓋了使用simple_action_server庫來創建一個Fibonacci行爲服務器。這個行爲服務器教程會生成一個Fibonacci序列、目標的順序序列、自身計算的反饋序列和最後序列的結果。


創建行爲消息


開始編寫行爲之前,非常重要的事是定義目標,結果和反饋消息。行爲消息會自動從.action'文件生成,對於更多行爲文件信息查看actionlib文檔。這個文件定義目標、結果和行爲的反饋話題的類型和文本格式。使用你最喜歡的文本編輯器創建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  # 或其他包含消息的包
    )

    • catkin_package也指定了CATKIN_DEPENDactionlib_msgsmessage_runtime自動傳遞依賴。


現在遵循以下,會自動生成你的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

爲了從這個文件手動生成消息文件,從actionlib_msgs包使用genaction.py腳本。


編寫一個簡單的服務器


代碼

首先,使用你最喜歡的編輯器創建actionlib_tutorials/src/fibonacci_server.cpp,隨後用以下內容填充:

#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <actionlib_tutorials/FibonacciAction.h>

class FibonacciAction
{
protected:

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

public:

  FibonacciAction(std::string name) :
    as_(nh_, name, boost::bind(&FibonacciAction::executeCB, this, _1), false),
    action_name_(name)
  {
    as_.start();
  }

  ~FibonacciAction(void)
  {
  }

  void executeCB(const actionlib_tutorials::FibonacciGoalConstPtr &goal)
  {
    // helper variables
    ros::Rate r(1);
    bool success = true;

    // push_back the seeds for the fibonacci sequence
    feedback_.sequence.clear();
    feedback_.sequence.push_back(0);
    feedback_.sequence.push_back(1);

    // publish info to the console for the user
    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]);

    // start executing the action
    for(int i=1; i<=goal->order; i++)
    {
      // check that preempt has not been requested by the client
      if (as_.isPreemptRequested() || !ros::ok())
      {
        ROS_INFO("%s: Preempted", action_name_.c_str());
        // set the action state to preempted
        as_.setPreempted();
        success = false;
        break;
      }
      feedback_.sequence.push_back(feedback_.sequence[i] + feedback_.sequence[i-1]);
      // publish the feedback
      as_.publishFeedback(feedback_);
      // this sleep is not necessary, the sequence is computed at 1 Hz for demonstration purposes
      r.sleep();
    }

    if(success)
    {
      result_.sequence = feedback_.sequence;
      ROS_INFO("%s: Succeeded", action_name_.c_str());
      // set the action state to succeeded
      as_.setSucceeded(result_);
    }
  }


};


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

  FibonacciAction fibonacci("fibonacci");
  ros::spin();

  return 0;
}


代碼解釋

現在,讓我們分塊解釋代碼。

#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>

從實現簡單行爲中使用的行爲庫actionlib/server/simple_action_server.h。

#include <actionlib_tutorials/FibonacciAction.h>

這個包含了從以上Fibonacci.action文件中生成的行爲消息。這是從FibonacciAction.msg文件中自動生成的。對於更多關於消息定義的細節,查看msg界面.

protected:

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

這些是行爲類中受保護的變量。在創建行爲的過程中,構造node handle並傳遞到行爲服務器中。在行爲的構造函數中構造行爲服務器,並且關於行爲服務器將會在以下講述。創建反饋和結果消息用於在行爲中發佈。

  FibonacciAction(std::string name) :
    as_(nh_, name, boost::bind(&FibonacciAction::executeCB, this, _1), false),
    action_name_(name)
  {
    as_.start();
  }

在行爲構造函數中,行爲服務器會被創建。行爲服務器會得到一個節點句柄(node handle)、行爲名稱和選擇一個運行回調函數(executeCB)參數。在這個例子中,創建的行爲服務器將回調函數(executeCB)作爲參數。

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

現在調用的executeCB函數以及在構造函數中創建。回調函數會傳遞一個指向目標消息的指針。注意: 這是一個boost共享指針, 在目標消息類型最後附加一個給定的"ConstPtr".

  ros::Rate r(1);
    bool success = true;

    // push_back the seeds for the fibonacci sequence
    feedback_.sequence.clear();
    feedback_.sequence.push_back(0);
    feedback_.sequence.push_back(1);

    // publish info to the console for the user
    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來讓用戶指定行爲正在運行。

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

一個行爲服務器的一個重要組成部分是允許行爲客戶端請求取消當前目標執行。當一個客戶端請求搶佔當前目標是,行爲服務器應該取消目標,隨後執行重要的清理,然後調用函數setPreempted(),該函數會發出該行爲已經被用戶請求搶佔信號。設置檢查搶佔請求服務器的等級到服務器系統。

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

這裏,Fibonacci序列賦值給feedback變量,然後通過行爲服務器提供的反饋頻道發佈出去。隨後行爲繼續循環和發佈反饋。

    if(success)
    {
      result_.sequence = feedback_.sequence;
      ROS_INFO("%s: Succeeded", action_name_.c_str());
      // set the action state to succeeded
      as_.setSucceeded(result_);
    }
  }

一旦行爲完成計算Fibonacci序列,會通知行爲客戶端操作設置成功。

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

  FibonacciAction fibonacci("fibonacci");
  ros::spin();

  return 0;
}

最後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}
)

運行行爲服務器

在一個新終端中開啓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

這個顯示你的行爲服務器正在發佈反饋、狀態和期望的通道結果,和目標訂閱以及期望的目標取消通道。行爲服務器啓動並運行正常。


發送一個目標到行爲服務器

對於使用你的行爲的下一步,你需要使用Ctrl-C關閉行爲服務器,然後編寫一個簡單的行爲客戶端.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章