http://wiki.ros.org/actionlib/Tutorials
----
除了下面列出的教程之外,还为actionlib包创建了两套教程。您可以通过浏览这些教程roscd -ing到actionlib_tutorials或turtle_actionlib包,即
roscd actionlib_tutorials
roscd turtle_actionlib
如果您尚未创建用于完成教程的工作区, 请单击此处查看一些简要说明 。
在开始任何actionlib教程之前,需要花时间创建一个scratch包来处理和操作示例代码。创建具有以下依赖关系的沙箱包:
catkinrosbuild
$ cd%YOUR_CATKIN_WORKSPACE%/ src
$ catkin_create_pkg actionlib_tutorials actionlib message_generation roscpp rospy std_msgs actionlib_msgs
(如果你不familliar与创建catkin包,请参阅Creating_a_catkin_Package教程 (在该网页上,不要忘记选择catkin为构建系统)。
----
小乌龟在空间画一个五边形:
$ rosrun turtlesim turtlesim_node [ INFO] [1489482578.049901977]: Starting turtlesim with node name /turtlesim [ INFO] [1489482578.064715851]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
$ rosrun turtle_actionlib shape_server [ INFO] [1489482564.155776333]: /turtle_shape: Succeeded [ INFO] [1489482609.968814937]: /turtle_shape: Succeeded
$ rosrun turtle_actionlib shape_client [ INFO] [1489482590.715268543]: Waiting for action server to start. [ INFO] [1489482590.999293581]: Action server started, sending goal. [ INFO] [1489482609.969416143]: Action finished: SUCCEEDED
server代码:
#include <ros/ros.h>
#include <turtlesim/Pose.h>
#include <actionlib/server/simple_action_server.h>
#include <cmath>
#include <math.h>
#include <angles/angles.h>
#include <geometry_msgs/Twist.h>
#include <turtle_actionlib/ShapeAction.h>
// This class computes the command_velocities of the turtle to draw regular polygons
class ShapeAction
{
public:
ShapeAction(std::string name) :
as_(nh_, name, false),
action_name_(name)
{
//register the goal and feeback callbacks
as_.registerGoalCallback(boost::bind(&ShapeAction::goalCB, this));
as_.registerPreemptCallback(boost::bind(&ShapeAction::preemptCB, this));
//subscribe to the data topic of interest
sub_ = nh_.subscribe("/turtle1/pose", 1, &ShapeAction::controlCB, this);
pub_ = nh_.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 1);
as_.start();
}
~ShapeAction(void)
{
}
void goalCB()
{
// accept the new goal
turtle_actionlib::ShapeGoal goal = *as_.acceptNewGoal();
//save the goal as private variables
edges_ = goal.edges;
radius_ = goal.radius;
// reset helper variables
interior_angle_ = ((edges_-2)*M_PI)/edges_;
apothem_ = radius_*cos(M_PI/edges_);
//compute the side length of the polygon
side_len_ = apothem_ * 2* tan( M_PI/edges_);
//store the result values
result_.apothem = apothem_;
result_.interior_angle = interior_angle_;
edge_progress_ =0;
start_edge_ = true;
}
void preemptCB()
{
ROS_INFO("%s: Preempted", action_name_.c_str());
// set the action state to preempted
as_.setPreempted();
}
void controlCB(const turtlesim::Pose::ConstPtr& msg)
{
// make sure that the action hasn't been canceled
if (!as_.isActive())
return;
if (edge_progress_ < edges_)
{
// scalar values for drive the turtle faster and straighter
double l_scale = 6.0;
double a_scale = 6.0;
double error_tol = 0.00001;
if (start_edge_)
{
start_x_ = msg->x;
start_y_ = msg->y;
start_theta_ = msg->theta;
start_edge_ = false;
}
// compute the distance and theta error for the shape
dis_error_ = side_len_ - fabs(sqrt((start_x_- msg->x)*(start_x_-msg->x) + (start_y_-msg->y)*(start_y_-msg->y)));
theta_error_ = angles::normalize_angle_positive(M_PI - interior_angle_ - (msg->theta - start_theta_));
if (dis_error_ > error_tol)
{
command_.linear.x = l_scale*dis_error_;
command_.angular.z = 0;
}
else if (dis_error_ < error_tol && fabs(theta_error_)> error_tol)
{
command_.linear.x = 0;
command_.angular.z = a_scale*theta_error_;
}
else if (dis_error_ < error_tol && fabs(theta_error_)< error_tol)
{
command_.linear.x = 0;
command_.angular.z = 0;
start_edge_ = true;
edge_progress_++;
}
else
{
command_.linear.x = l_scale*dis_error_;
command_.angular.z = a_scale*theta_error_;
}
// publish the velocity command
pub_.publish(command_);
}
else
{
ROS_INFO("%s: Succeeded", action_name_.c_str());
// set the action state to succeeded
as_.setSucceeded(result_);
}
}
protected:
ros::NodeHandle nh_;
actionlib::SimpleActionServer<turtle_actionlib::ShapeAction> as_;
std::string action_name_;
double radius_, apothem_, interior_angle_, side_len_;
double start_x_, start_y_, start_theta_;
double dis_error_, theta_error_;
int edges_ , edge_progress_;
bool start_edge_;
geometry_msgs::Twist command_;
turtle_actionlib::ShapeResult result_;
ros::Subscriber sub_;
ros::Publisher pub_;
};
int main(int argc, char** argv)
{
ros::init(argc, argv, "turtle_shape");
ShapeAction shape(ros::this_node::getName());
ros::spin();
return 0;
}
client代码:
#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/client/terminal_state.h>
#include <turtle_actionlib/ShapeAction.h>
int main (int argc, char **argv)
{
ros::init(argc, argv, "test_shape");
// create the action client
// true causes the client to spin it's own thread
actionlib::SimpleActionClient<turtle_actionlib::ShapeAction> ac("turtle_shape", true);
ROS_INFO("Waiting for action server to start.");
// wait for the action server to start
ac.waitForServer(); //will wait for infinite time
ROS_INFO("Action server started, sending goal.");
// send a goal to the action
turtle_actionlib::ShapeGoal goal;
goal.edges = 5;
goal.radius = 1.3;
ac.sendGoal(goal);
//wait for the action to return
bool finished_before_timeout = ac.waitForResult(ros::Duration(40.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.");
//exit
return 0;
}
认真读懂代码后,小修改一下程序,画8边形等。
----
本教程涵盖使用simple_action_server库创建Fibonacci操作服务器。此示例操作服务器生成斐波纳契序列,目标是序列的顺序,反馈是计算的序列,结果是最终序列。
首先要创建动作消息,然后编写简单服务器。具体参考官网wiki。
$ rosrun actionlib_tutorials fibonacci_server [ INFO] [1489479587.031699478]: fibonacci: Executing, creating fibonacci sequence of order 20 with seeds 0, 1 [ INFO] [1489479607.031776190]: fibonacci: Succeeded
然后,编写客户端。
$ rosrun actionlib_tutorials fibonacci_client [ INFO] [1489479586.767608642]: Waiting for action server to start. [ INFO] [1489479587.030931151]: Action server started, sending goal. [ INFO] [1489479607.032538514]: Action finished: SUCCEEDED
$ rostopic echo /fibonacci/result header: seq: 0 stamp: secs: 1489479774 nsecs: 565314290 frame_id: '' status: goal_id: stamp: secs: 1489479754 nsecs: 564505228 id: /test_fibonacci-1-1489479754.564505228 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] ---
Python版本参考官网~
----
这包括从上面显示的Averaging.action文件生成的操作消息。这是从AveragingAction.msg文件自动生成的头。有关消息定义的更多信息,请参阅msg页面。
$ rosrun actionlib_tutorials gen_numbers.py /home/relaybot/ROS_tutorial/src/common_tutorials-kinetic-devel/actionlib_tutorials/scripts/gen_numbers.py:14: SyntaxWarning: The publisher should be created with an explicit keyword argument 'queue_size'. Please see http://wiki.ros.org/rospy/Overview/Publishers%20and%20Subscribers for more information. pub = rospy.Publisher('random_number', Float32) [INFO] [1489480362.608077]: Generating random numbers
如果遇到.py无法运行情况,参考chmod +x xxx.py;
$ rosrun actionlib_tutorials averagingerver [ INFO] [1489480293.583761868]: /averaging: Preempted [ INFO] [1489480367.859222479]: /averaging: Succeeded [ INFO] [1489480373.959334705]: /averaging: Aborted [ INFO] [1489480388.709328974]: /averaging: Succeeded [ INFO] [1489480421.359241543]: /averaging: Aborted [ INFO] [1489480429.459318829]: /averaging: Succeeded
$ rosrun actionlib_tutorials averaging_client [ INFO] [1489480416.045880779]: Waiting for action server to start. [ INFO] [1489480416.327395926]: Action server started, sending goal. [ INFO] [1489480421.360004624]: Action finished: ABORTED relaybot@relaybot-desktop:~/ROS_tutorial$ rosrun actionlib_tutorials averaging_client [ INFO] [1489480424.163872017]: Waiting for action server to start. [ INFO] [1489480424.450870275]: Action server started, sending goal. [ INFO] [1489480429.460235297]: Action finished: SUCCEEDED
$ rostopic echo /averaging/result header: seq: 4 stamp: secs: 1489480421 nsecs: 359319144 frame_id: '' status: goal_id: stamp: secs: 1489480416 nsecs: 327495617 id: /test_averaging-1-1489480416.327495617 status: 4 text: '' result: mean: 4.91720485687 std_dev: 0.955443501472 --- header: seq: 5 stamp: secs: 1489480429 nsecs: 459373174 frame_id: '' status: goal_id: stamp: secs: 1489480424 nsecs: 450959415 id: /test_averaging-1-1489480424.450959415 status: 3 text: '' result: mean: 5.10838794708 std_dev: 0.911464750767 ---
----
$ rosrun actionlib_tutorials fibonacci_callback_client [ INFO] [1489480914.681402037]: Waiting for action server to start. [ INFO] [1489480914.902727647]: Action server started, sending goal. [ INFO] [1489480914.903744185]: Goal just went active [ INFO] [1489480914.903939017]: Got Feedback of length 3 [ INFO] [1489480915.903842332]: Got Feedback of length 4 [ INFO] [1489480916.904067905]: Got Feedback of length 5 [ INFO] [1489480917.904147835]: Got Feedback of length 6 [ INFO] [1489480918.904100351]: Got Feedback of length 7 [ INFO] [1489480919.904197519]: Got Feedback of length 8 [ INFO] [1489480920.904099212]: Got Feedback of length 9 [ INFO] [1489480921.904155594]: Got Feedback of length 10 [ INFO] [1489480922.904136690]: Got Feedback of length 11 [ INFO] [1489480923.904118087]: Got Feedback of length 12 [ INFO] [1489480924.904240841]: Got Feedback of length 13 [ INFO] [1489480925.904160093]: Got Feedback of length 14 [ INFO] [1489480926.904194218]: Got Feedback of length 15 [ INFO] [1489480927.904136215]: Got Feedback of length 16 [ INFO] [1489480928.904171361]: Got Feedback of length 17 [ INFO] [1489480929.904130197]: Got Feedback of length 18 [ INFO] [1489480930.904089898]: Got Feedback of length 19 [ INFO] [1489480931.904149072]: Got Feedback of length 20 [ INFO] [1489480932.904232818]: Got Feedback of length 21 [ INFO] [1489480933.904166482]: Got Feedback of length 22 [ INFO] [1489480934.904440340]: Finished in state [SUCCEEDED] [ INFO] [1489480934.904546340]: Answer: 10946
$ rosrun actionlib_tutorials fibonacci_class_client [ INFO] [1489480960.866926239]: Waiting for action server to start. [ INFO] [1489480961.086980182]: Action server started, sending goal. [ INFO] [1489480971.088604073]: Finished in state [SUCCEEDED] [ INFO] [1489480971.088696472]: Answer: 89
----
----
----
----
----
----
----
机器翻译
actionlib--详细说明--
此页面不适用于刚刚开始使用actionlib的人。相反,它描述了客户端和服务器之间相互作用的底层机制。
如果只想使用SimpleActionClient或SimpleActionServer,则无需了解此页面上的概念。然而,如果简单的客户端和服务器不够描述,理解这些概念提供了对调试客户端/服务器交互或实现备用客户端/服务器策略有用的洞察。
请注意,此状态机跟踪单个目标,而不是ActionServer本身。因此,对于系统中的每个目标都有一个状态机。
操作客户端还可以异步触发状态转换:
终端国
客户端触发转换
“跳过”状态
许多动作服务器遵循类似的模式,其中一次只能有一个目标是活动的,每个新目标抢占前一个目标。简单动作服务器是一个动作服务器的包装器,旨在强制执行这个简单的策略来处理目标。
在从动作客户端接收到新目标时,简单动作服务器将该目标移动到其待决槽中。如果目标已经占用了挂起的插槽,则简单动作服务器将该目标设置为取消,并将其替换为通过线路传递的目标。
一旦新目标被简单动作服务器接收并被移动到待决槽中,简单动作服务器的用户被通知新目标可用。此通知以以下两种方式之一发生,如“目标通知”部分所述。在接收到通知时,用户可以接受使得待决槽中的目标移动到当前目标槽的目标,并且允许用户修改与新接受的目标相关联的状态机。
用户可以通过两种方式接收简单动作服务器已经接收到新目标的通知:
在构造简单动作服务器时,用户决定是否旋转额外的线程以允许在目标回调中采取长时间运行的动作。
在任何大的基于ROS的系统中,有时候有人想要向节点发送请求以执行某个任务,并且还接收对该请求的答复。这可以通过目前ROS可实现的服务。
然而,在某些情况下,如果服务需要很长时间来执行,则用户可能想要在执行期间取消请求的能力或获得关于请求如何进展的定期反馈。该actionlib包提供的工具来创建执行可抢占长期运行的目标服务器。它还提供了一个客户端接口,以便向服务器发送请求。
对于actionlib如何“引擎盖下”工作的全面讨论,请参阅 详细说明。
的ActionClient和ActionServer经由通信“ROS操作协议”,这是建立在ROS消息的顶部。客户端和服务器然后为用户提供一个简单的API,以通过函数调用和回调请求目标(在客户端)或执行目标(在服务器端)。
为了使客户端和服务器进行通信,我们需要定义一些他们进行通信的消息。这是一个动作规范。这定义了客户端和服务器与之通信的目标,反馈和结果消息:
目标 要使用动作完成任务,我们介绍一个可以由ActionClient发送到ActionServer的目标的概念。在移动基地的情况下,目标将是PoseStamped消息,其包含关于机器人应该在世界中移动到哪里的信息。为了控制倾斜激光扫描器,目标将包含扫描参数(最小角度,最大角度,速度等)。
反馈 反馈为服务器实现者提供了一种告诉ActionClient关于目标的增量进度的方法。对于移动基地,这可能是机器人沿着路径的当前姿势。为了控制倾斜激光扫描器,这可以是直到扫描完成的时间。
结果 完成目标后,结果将从ActionServer发送到ActionClient。这与反馈不同,因为它只发送一次。当动作的目的是提供某种信息时,这是非常有用的。对于移动基准,结果不是很重要,但它可能包含机器人的最终姿态。为了控制倾斜激光扫描器,结果可能包含从请求的扫描生成的点云。
动作规格是使用限定.action文件。该.action文件具有目标定义,随后的结果定义,随后通过反馈定义,与由3连字号(分隔每个部分---)。
这些文件放在包的./action目录中,看起来非常类似于服务的.srv文件。做菜的操作规范可能如下所示:
./action/DoDishes.action
#定义目标
uint32 dishwasher_id#指定要使用的洗碗机
--- ---
#定义结果
uint32 total_dishes_cleaned
--- ---
#定义反馈消息
float32 percent_complete
基于此.action文件,需要生成6个消息,以便客户端和服务器进行通信。该生成可以在make过程中自动触发:
以下添加到您的CMakeLists.txt文件之前 catkin_package() 。
find_package(catkin REQUIRED genmsg actionlib_msgs actionlib)
add_action_files(DIRECTORY操作FILES DoDishes.action)
generate_messages(DEPENDENCIES actionlib_msgs)
此外,包的package.xml必须包含以下依赖项:
<build_depend> actionlib </ build_depend>
<build_depend> actionlib_msgs </ build_depend>
<run_depend> actionlib </ run_depend>
<run_depend> actionlib_msgs </ run_depend>
如果您正在使用rosbuild而不是柳絮,而不是添加以下之前 rosbuild_init() 。
rosbuild_find_ros_package(actionlib_msgs)
include($ {actionlib_msgs_PACKAGE_PATH} /cmake/actionbuild.cmake)
genaction()
然后,在输出路径之后,取消注释(或添加)
rosbuild_genmsg()
注: rosbuild_genmsg()的输出路径设置完成后,必须调用。
对于1.0系列(即boxturtle)使用:
rosbuild_find_ros_package(actionlib)
include($ {actionlib_PACKAGE_PATH} /cmake/actionbuild.cmake)
genaction()
rosbuild_genmsg()
此外,包的manifest.xml必须包含以下依赖关系:
<depends package =“actionlib”/>
<depends package =“actionlib_msgs”/>
对于DoDishes.action,以下消息由genaction.py生成:
然后,actionlib在内部使用这些消息在ActionClient和ActionServer之间进行通信。
为全面API参考C ++ SimpleActionClient
快速入门指南: 假设你已经定义DoDishes.action在家务包。以下代码段显示如何将目标发送到名为“do_dishes”的DoDishes ActionServer。
1 #包括<家务/ DoDishesAction.h>
2 #包括<actionlib / 客户/ simple_action_client.h>
3
4 typedef actionlib :: SimpleActionClient < chores :: DoDishesAction > Client ;
5
6 int main(int argc,char ** argv)
7 {
8 ros :: init(argc,argv,“ do_dishes_client ”);
9 客户 端客户端(“ do_dishes ”,true); // true - >不需要ros :: spin()
10 客户端。waitForServer();
11 chores :: DoDishesGoal 目标 ;
12 //在这里填写目标
13 客户端。sendGoal(goal);
14 客户端。waitForResult(ros :: Duration(5.0));
15 如果(客户端。的getState()== actionlib :: SimpleClientGoalState :: SUCCEEDED)
16 printf(“ Yay!The dishes are now clean ”);
17 的printf(“ 当前状态:%S \ n ”,客户端。的getState()的toString()。c_str());
18 return 0 ;
19 }}
注意:对于C ++ SimpleActionClient,waitForServer方法将仅在单独的线程服务客户端的回调队列时才起作用。这需要在传入真为spin_thread客户端的构造的选择,用多线程喷丝运行,或使用自己的线程服务的ROS回调队列。
为全面API参考Python的SimpleActionClient
假设DoDishes.action存在于差事包中。以下代码段显示如何使用Python将目标发送到名为“do_dishes”的DoDishes ActionServer。
1 #!/ usr / bin / env python
2
3 import roslib
4 roslib。load_manifest(' my_pkg_name ')
5 import rospy
6 import actionlib
7
8 从 chores.msg 导入 DoDishesAction,DoDishesGoal
9
10 如果 __name__ == ' __main__ ':
11 rospy。init_node(' do_dishes_client ')
12 client = actionlib。SimpleActionClient(' do_dishes ',DoDishesAction)
13 客户端。wait_for_server()
14
15 goal = DoDishesGoal()
16 #填写这里的目标
17 客户端。send_goal(goal)
18 客户端。wait_for_result(rospy。持续时间。from_sec(5.0))
为全面API参考C ++ SimpleActionServer
快速入门指南: 假设你已经定义DoDishes.action在家务包。以下代码段显示了如何编写名为“do_dishes”的DoDishes ActionServer。
1 #包括<家务/ DoDishesAction.h>
2 #包括<actionlib / 服务器/ simple_action_server.h>
3
4 typedef actionlib :: SimpleActionServer < chores :: DoDishesAction > 服务器 ;
5
6 void execute(const chores :: DoDishesGoalConstPtr&goal,Server * as)
7 {
8 //在这里做很多真棒破解机器人的东西
9 as - > setSucceeded();
10 }
11
12 int main(int argc,char ** argv)
13 {
14 ros :: init(argc,argv,“ do_dishes_server ”);
15 ros :: NodeHandle n ;
16 服务器 服务器(n,“ do_dishes ”,boost :: bind(&execute,_1,&server),false);
17 服务器。start();
18 ros :: spin();
19 return 0 ;
20 }}
为全面API参考Python的SimpleActionServer
快速入门指南: 假设你已经定义DoDishes.action在家务包。以下代码段显示了如何编写名为“do_dishes”的DoDishes ActionServer。
1 #!/ usr / bin / env python
2
3 import roslib
4 roslib。load_manifest(' my_pkg_name ')
5 import rospy
6 import actionlib
7
8 从 chores.msg 导入 DoDishesAction
9
10 类 DoDishesServer:
11 def __init__(self):
12 自我。server = actionlib。SimpleActionServer(' do_dishes ',DoDishesAction,自我,执行,假)
13 自我。服务器。start()
14
15 def execute(self,goal):
16 #做很多真棒破碎机器人的东西在这里
17 自我。服务器。set_succeeded()
18
19
20 如果 __name__ == ' __main__ ':
21 rospy。init_node(' do_dishes_server ')
22 server = DoDishesServer()
23 rospy。spin()
该SimpleActionServer实现在顶部的单一目标政策ActionServer类。政策的规范如下:
调用acceptNewGoal在有可用时接受新目标。状态这个目标设置为接受时激活,任何以前的状态 活动目标设置为抢占。抢先接收新目标之间检查isNewGoalAvailable或调用目标回调和acceptNewGoal调用不会触发抢占回调。意即,isPreemptRequested应该在接受目标后调用基于回调的实现,以确保新的目标没有 等待抢占请求。
请参阅教程页面
----