ROS-ActionServerClient

actionlib tutorial 参考连接:http://wiki.ros.org/actionlib_tutorials/Tutorials

功能说明

创建一个计算fibonacci数列的action Server

  1. goal 是输入一个整数,为需要计算的第几个fibonacci数
  2. result是一个整型数组:数组存放所有的fibonacci数;
  3. feedback是一个整型数组:数组依次存放在计算过程中的fibonacci数;

00. 如何构建ActionServer

0.1 创建action文件

在工程目录中,创建action目录,然后创建action文件,内容如下
wiki_actionlib_tutorials/action/Fibonacci.action

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

0.2 修改CMakeLists.txt文件

find_package(catkin REQUIRED COMPONENTS roscpp actionlib actionlib_msgs)
## 需要包含actionlib_msgs actionlib
add_action_files( 
  DIRECTORY action   
  FILES Fibonacci.action )
## 关联action目录中的action文件,前面创建好的
generate_messages(
  DEPENDENCIES
  actionlib_msgs std_msgs  )
catkin_package(
    CATKIN_DEPENDS actionlib_msgs )

0.3 package.xml

依赖actionlib包:

  <build_depend>actionlib</build_depend>
  <build_depend>actionlib_msgs</build_depend>
  <build_export_depend>actionlib</build_export_depend>
  <build_export_depend>actionlib_msgs</build_export_depend>
  <exec_depend>actionlib</exec_depend>
  <exec_depend>actionlib_msgs</exec_depend>

catkin_make

编译成功后,
~/catkin_ws/devel/share/wiki_actionlib_tutorials/msg 目录中
msgfile
~/catkin_ws/devel/include/wiki_actionlib_tutorials 目录中:
includefile

01.Action 类方式实现

1.1 Server

使用类方式的形式,实现Action Server,并启动Server

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

class FibonacciAction
{
protected:
    ros::NodeHandle nh_;
    actionlib::SimpleActionServer<wiki_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
    wiki_actionlib_tutorials::FibonacciFeedback feedback_;
    wiki_actionlib_tutorials::FibonacciResult result_;

public:
    FibonacciAction(std::string name) : as_(nh_, name, boost::bind(&FibonacciAction::executeCB, this, _1), false),action_name_(name)
    {
        as_.start();
        ROS_INFO_STREAM(action_name_ << ": server start!");
    }

    ~FibonacciAction(void)
    {
    }

    void executeCB(const wiki_actionlib_tutorials::FibonacciGoalConstPtr &goal)
    {
        // helper variables
        ros::Rate r(0.5);
        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
            ROS_INFO_STREAM(action_name_ << ": current index " << i);
            for (int j = 0; j != feedback_.sequence.size(); j++)
            {
                std::cout << feedback_.sequence[j] << ".";
            }
            std::cout << std::endl;
            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;
}
  1. as_(nh_, name, boost::bind(&FibonacciAction::executeCB, this, _1), false)对于actionServer的创建,第三个参数要绑定到类中的成员函数,区别于普通函数,需要有几个注意事项;
  2. boost::bind()绑定类成员函数的时候,需要在成员函数前使用“&”
  3. 由于类成员函数的调用,需要知道是在调用哪一个类实例的成员函数,所以需要传入类实例,所以此处boost::bind()的第二个参数是类实例,由于这是在类中进行绑定的,所以第二个参数可以写成是this;
  4. boost::bind()的第三个参数:_1,是留给后续ActionGoal消息传进来的时候,占位符,由于实际的参数值是未知的,所以使用占位符;

  • 说明
rosrun wiki_actionlib_tutorials fibonacci_server

启动server,此时将server初始化并start了,但是由于没有收到goal的消息,所以处于等待状态

rostopic list -v

Published topics:
 * /rosout_agg [rosgraph_msgs/Log] 1 publisher
 * /rosout [rosgraph_msgs/Log] 1 publisher
 * /fibonacci/result [wiki_actionlib_tutorials/FibonacciActionResult] 1 publisher
 * /fibonacci/feedback [wiki_actionlib_tutorials/FibonacciActionFeedback] 1 publisher
 * /fibonacci/status [actionlib_msgs/GoalStatusArray] 1 publisher

Subscribed topics:
 * /rosout [rosgraph_msgs/Log] 1 subscriber
 * /fibonacci/goal [wiki_actionlib_tutorials/FibonacciActionGoal] 1 subscriber
 * /fibonacci/cancel [actionlib_msgs/GoalID] 1 subscriber
  1. 通过/fibonacci/goal 向ActionServer发送执行目标;
  2. 通过/fibonacci/cancel 向server请求取消任务,此时server会检测到as_.isPreemptRequested(),从而结束
  3. /fibonacci/result 只有在server完成全部工作的时候,才会向这个topic发送一次FibonacciActionResult消息
  4. /fibonacci/feedback 会按照程序中指定的频率,反馈 FibonacciActionFeedback消息
  5. /fibonacci/status 会按照5hz的频率,固定发布当前server的状态数据
## 向server发送FibonacciActionGoal消息
rostopic  pub  -1 /fibonacci/goal wiki_actionlib_tutorials/FibonacciActionGoal "header:
  seq: 0
  stamp:
    secs: 0
    nsecs: 0
  frame_id: ''
goal_id:
  stamp:
    secs: 0
    nsecs: 0
  id: ''
goal:
  order: 20" 

1.2 Client

如下,当client执行的时候,会向Action Server发送一个请求ac.sendGoal(goal),server收到请求后,开始运行;

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

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

    actionlib::SimpleActionClient<wiki_actionlib_tutorials::FibonacciAction> ac("fibonacci", true);
    ROS_INFO("Waiting for action server to start.");
    ac.waitForServer(); // will wait for infinite time
    ROS_INFO("Action server started, sending goal.");

    wiki_actionlib_tutorials::FibonacciGoal goal;
    goal.order = 10;
    ac.sendGoal(goal);

    bool finished_before_timeout = ac.waitForResult(ros::Duration(30.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.");

    return 0;
}

02.Action 普通的函数方式实现

2.1 Server

#include <chores/DoDishesAction.h>  // Note: "Action" is appended
#include <actionlib/server/simple_action_server.h>

typedef actionlib::SimpleActionServer<chores::DoDishesAction> Server;

void execute(const chores::DoDishesGoalConstPtr& goal, Server* as)  // Note: "Action" is not appended to DoDishes here
{
  // Do lots of awesome groundbreaking robot stuff here
  as->setSucceeded();
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "do_dishes_server");
  ros::NodeHandle n;
  Server server(n, "do_dishes", boost::bind(execute, _1, &server), false);//boost::bind()绑定函数需要此处需要注意,需要理解下
  server.start();
  ros::spin();
  return 0;
}

说明:

  1. 首先创建一个Server,且在server中绑定会到函数,在server启动之后,在回调函数中对收到的goal执行响应的操作
  2. 在示例代码中,没有写详细的处理过程,应该在这里有按照一定的频率反馈feekback的操作
  3. 在回调函数中,还应该检测server的状态,检测是否被强占了,这是应该及时处理,结束server;
  4. Server server(n, "do_dishes", boost::bind(execute, _1, &server), false);此处ActionServer的创建,首先需要注意其标准规范;其次,server()的第三个参数是actionServer在收到goal消息之后,进行消息处理的回调函数execute(),
  5. 回调函数:execute()含有两个参数,分别是ActionGoal消息和一个自定义的参数server,第一个参数是必须的,用来接收actionGoal,第二个是用户自定的,非必需;
  6. 绑定回调函数:boost::bind(execute, _1,&server),对于要绑定的普通函数,没有必要一定要使用&,_1表示当接受到ActionGoal消息的时候,会传给占位符_1这个位置,&server表示要传给execute的第二个参数,用于在函数中对该server进行一些状态操作,此处server就是本声明语句要创建的对象,这样使用,证明是可以的。

2.2 Client

#include <chores/DoDishesAction.h> // Note: "Action" is appended
#include <actionlib/client/simple_action_client.h>

typedef actionlib::SimpleActionClient<chores::DoDishesAction> Client;

int main(int argc, char** argv)
{
  ros::init(argc, argv, "do_dishes_client");
  Client client("do_dishes", true); // true -> don't need ros::spin()
  client.waitForServer();
  chores::DoDishesGoal goal;
  // Fill in goal here
  client.sendGoal(goal);
  client.waitForResult(ros::Duration(5.0));
  if (client.getState() == actionlib::SimpleClientGoalState::SUCCEEDED)
    printf("Yay! The dishes are now clean");
  printf("Current State: %s\n", client.getState().toString().c_str());
  return 0;
}
posted @ 2022-07-14 10:07  DNGG  阅读(242)  评论(0)    收藏  举报