ROS actionlib學習(二)


   ROS actionlib學習(一)中的例子展示了actionlib最基本的用法,下面我們看一個稍微實際一點的例子,用actionlib計算斐波那契數列,並發布反饋(feedback)和結果(result)。斐波那契數列指的是這樣一個數列:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........

  這個數列從第3項開始,每一項都等於前兩項之和。

  首先在action文件中定義goal、result、feedback,其中goal是斐波那契數列的階數,result為最終生成的數列,feedback為當前一步的中間結果(也是一個數列)。

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

  然后按照教程SimpleActionServer(ExecuteCallbackMethod)中所述的步驟,修改CMakeLists.txt以及package.xml文件,編譯成功后會生成相應的消息文件以及頭文件。

$ cd ../.. # Go back to the top level of your catkin workspace
$ 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

 

  下面編寫服務端程序,用於處理客戶端發送的請求。

#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();  // Explicitly start the action server
  }

  ~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(); // signals that the action has been preempted by user request
        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;
}

 

   客戶端程序用於發送計算請求,服務器收到請求后會生成20階的斐波那契數列。

#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/client/terminal_state.h>
#include <actionlib_tutorials/FibonacciAction.h>

int main (int argc, char **argv)
{
  ros::init(argc, argv, "test_fibonacci");
// the action client is constructed with the server name and the auto spin option set to true.
  actionlib::SimpleActionClient<actionlib_tutorials::FibonacciAction> ac("fibonacci", true); //The action client is templated on the action definition, specifying what message types to communicate to the action server with

  ROS_INFO("Waiting for action server to start.");
  // wait for the action server to start (Since the action server may not be up and running, the action client will wait for the action server to start before continuing)
  ac.waitForServer(); //will wait for infinite time

  ROS_INFO("Action server started, sending goal.");
  // send a goal to the action
  actionlib_tutorials::FibonacciGoal goal;
  goal.order = 20;
  ac.sendGoal(goal); // the goal value is set and sent to the action serve //wait for the action to return
  bool finished_before_timeout = ac.waitForResult(ros::Duration(30.0)); // The timeout on the wait is set to 30 seconds, this means after 30 seconds the function will return with false if the goal has not finished. 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;
}

   修改CMakeLists.txt后使用catkin_make編譯上面兩個程序。運行roscore開啟ROS主節點,然后在兩個新終端中分別輸入下面命令分別運行server和client:

rosrun actionlib_tutorials fibonacci_server  

rosrun actionlib_tutorials fibonacci_client

  可以通過 rostopic echo /fibonacci/feedback 和 rostopic echo /fibonacci/result 命令查看斐波那契數列計算的反饋和結果:

   最終的計算結果result如下:

---
header: 
  seq: 1
  stamp: 1250813759950015000
  frame_id: 
status: 
  goal_id: 
    stamp: 1250813739949752000
    id: 1250813739949752000
  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)

 

  在一個新終端中輸入下面命令,運行另一個客戶端節點(重命名為client2)。如果在原fibonacci_client節點的請求還沒完成時就運行client2,那么服務將會被client2搶占。

rosrun actionlib_tutorials fibonacci_client __name:=client2

  效果如下圖所示,如果前一個client發送的請求還沒計算完成,新的goal就到達,server會重新開始計算數列:

  SimpleActionServer在ActionServer類上實現了single goal policy,就是在某一時刻只能有一個goal是處於active狀態,並且新的goal可以搶占先前的goal:

  • Only one goal can have an active status at a time
  • New goals preempt previous goals based on the stamp in their GoalID field (later goals preempt earlier ones)
  • An explicit preempt goal preempts all goals with timestamps that are less than or equal to the stamp associated with the preempt
  • Accepting a new goal implies successful preemption of any old goal and the status of the old goal will be changed automatically to reflect this

 

 

 

參考:

actionlib-Tutorials

ROS actionlib學習(一)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM