ROS与C++入门教程-actionlib-编写简单的action服务器(Goal Callback)
ROS与C++入门教程-actionlib-编写简单的action服务器(Goal Callback)
说明:
- 介绍如何用C++编写使用goal回调函数的action服务器
- 介绍使用simple_action_server库创建平均数action服务器
- 介绍使用action来处理来自节点的输入数据,平均数来自节点,goal是样本数的平均值,feedback是样本数,样本数据,当前平均值和当前标准偏差,result是所要求的样本数的平均值和标准偏差。
创建action消息
goal目标,result结果,feedback反馈
编写action,定义goal, result和feedback消息
action消息自动从.action文件生成,更多详细参考actionlib
这个文件定义action的goal, result和feedback话题的类型和格式。
创建actionlib_tutorials/action/Averaging.action
cd ~/catkin_ws/src/actionlib_tutorials/action
touch Averaging.action
vim Averaging.action
- 内容如下:
#goal definition
int32 samples
---
#result definition
float32 mean
float32 std_dev
---
#feedback
int32 sample
float32 data
float32 mean
float32 std_dev
手工生成消息文件
$ roscd actionlib_tutorials
$ rosrun actionlib_msgs genaction.py -o msg/ action/Averaging.action
- 效果显示:
Generating for action Averaging
自动生成消息文件
- 更改CMakeLists.txt, 增加如下行:
find_package(catkin REQUIRED COMPONENTS actionlib std_msgs message_generation)
add_action_files(DIRECTORY action FILES Averaging.action)
generate_messages(DEPENDENCIES std_msgs actionlib_msgs)
- 编译
$ cd ~/catkin_ws
$ catkin_make
编写服务器
- 创建actionlib_tutorials/src/averaging_server.cpp文件,代码参考
cd ~/catkin_ws/src/actionlib_tutorials/src/
touch averaging_server.cpp
vim averaging_server.cpp
- 代码如下:
#include <ros/ros.h>
#include <std_msgs/Float32.h>
#include <actionlib/server/simple_action_server.h>
#include <actionlib_tutorials/AveragingAction.h>
class AveragingAction
{
public:
AveragingAction(std::string name) :
as_(nh_, name, false),
action_name_(name)
{
//register the goal and feeback callbacks
as_.registerGoalCallback(boost::bind(&AveragingAction::goalCB, this));
as_.registerPreemptCallback(boost::bind(&AveragingAction::preemptCB, this));
//subscribe to the data topic of interest
sub_ = nh_.subscribe("/random_number", 1, &AveragingAction::analysisCB, this);
as_.start();
}
~AveragingAction(void)
{
}
void goalCB()
{
// reset helper variables
data_count_ = 0;
sum_ = 0;
sum_sq_ = 0;
// accept the new goal
goal_ = as_.acceptNewGoal()->samples;
}
void preemptCB()
{
ROS_INFO("%s: Preempted", action_name_.c_str());
// set the action state to preempted
as_.setPreempted();
}
void analysisCB(const std_msgs::Float32::ConstPtr& msg)
{
// make sure that the action hasn't been canceled
if (!as_.isActive())
return;
data_count_++;
feedback_.sample = data_count_;
feedback_.data = msg->data;
//compute the std_dev and mean of the data
sum_ += msg->data;
feedback_.mean = sum_ / data_count_;
sum_sq_ += pow(msg->data, 2);
feedback_.std_dev = sqrt(fabs((sum_sq_/data_count_) - pow(feedback_.mean, 2)));
as_.publishFeedback(feedback_);
if(data_count_ > goal_)
{
result_.mean = feedback_.mean;
result_.std_dev = feedback_.std_dev;
if(result_.mean < 5.0)
{
ROS_INFO("%s: Aborted", action_name_.c_str());
//set the action state to aborted
as_.setAborted(result_);
}
else
{
ROS_INFO("%s: Succeeded", action_name_.c_str());
// set the action state to succeeded
as_.setSucceeded(result_);
}
}
}
protected:
ros::NodeHandle nh_;
actionlib::SimpleActionServer<actionlib_tutorials::AveragingAction> as_;
std::string action_name_;
int data_count_, goal_;
float sum_, sum_sq_;
actionlib_tutorials::AveragingFeedback feedback_;
actionlib_tutorials::AveragingResult result_;
ros::Subscriber sub_;
};
int main(int argc, char** argv)
{
ros::init(argc, argv, "averaging");
AveragingAction averaging(ros::this_node::getName());
ros::spin();
return 0;
}
代码解释:
- 代码:
#include <ros/ros.h>
#include <std_msgs/Float32.h>
#include <actionlib/server/simple_action_server.h>
- 解释:actionlib/server/simple_action_server.h是action库,用于实现简单的action服务器
- 代码:
#include <actionlib_tutorials/AveragingAction.h>
- 解释:这个头文件是Averaging.action文件自动生成的消息头文件
- 代码:
class AveragingAction
{
public:
AveragingAction(std::string name) :
as_(nh_, name, false),
action_name_(name)
{
//register the goal and feeback callbacks
as_.registerGoalCallback(boost::bind(&AveragingAction::goalCB, this));
as_.registerPreemptCallback(boost::bind(&AveragingAction::preemptCB, this));
- 解释:
- action构造器,创建action服务器。
- action服务器需要提供节点句柄,服务器名称,可选的executeCB参数。
- 本例不使用executeCB参数,而是在构造函数里注册goal和preempt回调函数。
- 代码:
//subscribe to the data topic of interest
sub_ = nh_.subscribe("/random_number", 1, &AveragingAction::analysisCB, this);
as_.start();
}
- 解释:订阅输入的数据,并定义数据处理的回调函数。
- 代码:
void goalCB()
{
// reset helper variables
data_count_ = 0;
sum_ = 0;
sum_sq_ = 0;
// accept the new goal
goal_ = as_.acceptNewGoal()->samples;
}
- 解释:
- 这里是在构造函数中引用的goalCB函数。
- 回调函数不返回任何内容,并且不带参数。
- 当goalCB被调用时,action需要接受goal并存储任何重要信息。
- 如果您需要在接受目标之前查看目标,请参阅SimpleActionServer(ExecuteCallbackMethod)教程。
- 代码:
void preemptCB()
{
ROS_INFO("%s: Preempted", action_name_.c_str());
// set the action state to preempted
as_.setPreempted();
}
解释:
- 此操作是事件驱动的,操作代码仅在回调发生时运行,因此创建抢占回调以确保操作快速响应取消请求。
- 回调函数不带参数,设置优先抢占处理。
代码:
void analysisCB(const std_msgs::Float32::ConstPtr& msg)
{
// make sure that the action hasn't been canceled
if (!as_.isActive())
return;
解释:这里,分析回调采用订阅数据通道的消息格式,并在继续处理数据之前检查动作是否仍处于活动状态。
代码:
data_count_++;
feedback_.sample = data_count_;
feedback_.data = msg->data;
//compute the std_dev and mean of the data
sum_ += msg->data;
feedback_.mean = sum_ / data_count_;
sum_sq_ += pow(msg->data, 2);
feedback_.std_dev = sqrt(fabs((sum_sq_/data_count_) - pow(feedback_.mean, 2)));
as_.publishFeedback(feedback_);
解释:这里,相关数据被放入feedback变量中,然后通过action服务器发布。
代码:
if(data_count_ > goal_)
{
result_.mean = feedback_.mean;
result_.std_dev = feedback_.std_dev;
if(result_.mean < 5.0)
{
ROS_INFO("%s: Aborted", action_name_.c_str());
//set the action state to aborted
as_.setAborted(result_);
}
else
{
ROS_INFO("%s: Succeeded", action_name_.c_str());
// set the action state to succeeded
as_.setSucceeded(result_);
}
}
}
解释:
- 一旦收集到足够的数据,action服务器将设置为成功或失败。
- 这将停用动作服务器,并且analysisCB函数返回。
代码:
protected:
ros::NodeHandle nh_;
actionlib::SimpleActionServer<actionlib_tutorials::AveragingAction> as_;
std::string action_name_;
int data_count_, goal_;
float sum_, sum_sq_;
actionlib_tutorials::AveragingFeedback feedback_;
actionlib_tutorials::AveragingResult result_;
ros::Subscriber sub_;
};
解释:私有变量,用于保存不同的对象,节点句柄,action对象,action名,goal,feedback等。
代码:
int main(int argc, char** argv)
{
ros::init(argc, argv, "averaging");
AveragingAction averaging(ros::this_node::getName());
ros::spin();
return 0;
}
- 解释:main函数启动action线程。会一直运行,等待接受goal。
编译
- 更改CMakeLists.txt,增加:
add_executable(averaging_server src/averaging_server.cpp)
target_link_libraries(averaging_server ${catkin_LIBRARIES})
- 编译:
$ cd ~/catkin_ws
$ catkin_make
运行:
- 新终端,执行:
$ roscore
- 新终端,执行:
$ rosrun actionlib_tutorials averaging_server
- 显示:
[ INFO] 1250790662.410962000: Started node [/averaging], pid [29267], bound on [aqy], xmlrpc port [39746], tcpros port [49573], logging to [~/ros/ros/log/averaging_29267.log], using [real] time
- 检查
$ rostopic list -v
- 显示:
Published topics:
* /averaging/status [actionlib_msgs/GoalStatusArray] 1 publisher
* /averaging/result [actionlib_tutorials/AveragingActionResult] 1 publisher
* /averaging/feedback [actionlib_tutorials/AveragingActionFeedback] 1 publisher
* /rosout [roslib/Log] 1 publisher
* /rosout_agg [roslib/Log] 1 publisher
Subscribed topics:
* /time [unknown type] 2 subscribers
* /rosout [roslib/Log] 1 subscriber
* /averaging/goal [unknown type] 1 subscriber
* /averaging/cancel [unknown type] 1 subscriber
- 或者通过图形显示
$ rosrun rqt_graph rqt_graph &
获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号