ROS 里除了基本的msg publisher/Subscriber 以外,还有以下东东需要理解:
ActionLib是ROS里一个重要的部分(link:http://wiki.ros.org/APIs),既然我在学习move_base时遇到很多有关actionlib的实现,还是先把actionlib理解之后再回去搞navigation的其他包比较合适。打好基础。
ROS中的服务service是一问一答的形式,你来查询了,我就返给你要的信息。
action也有服务的概念,但是它不一样的地方是:不是一问一答,而多了一个反馈,它会不断反馈项目进度。如navigation下的move_base package,你设定了目标点,反馈信息可能是机器人在规划路径上的即时位姿,直到机器人到达目标点,返回SUCCEEDED消息。有关actionlib的具体学习笔记sonictl见下:
wikipage: http://wiki.ros.org/actionlib
The actionlib stack provides a standardized interface for interfacing with preemptable tasks. Examples of this include moving the base to a target location, performing a laser scan and returning the resulting point cloud, detecting the handle of a door, etc.
actionlib是个stack,意味着与navigation stack平级?为和pre emptable tasks 可预先中止性任务进行交互而提供标准的接口。相关的例子有:moving base to a target location,performing a laser scan and returning the resulting point cloud, detecting the handle of a door, etc.
(sonictl@csdn:看起来还是很必须的一块内容,如果希望往高级一点的ROS应用发力的话。)
In any large ROS based system, there are cases when someone would like to send a request to a node to perform some task, and also receive a reply to the request. This can currently be achieved via ROS services.
In some cases, however, if the service takes a long time to execute, the user might want the ability to cancel the request during execution or get periodic feedback about how the request is progressing. The actionlib package provides tools to create servers that execute long-running goals that can be preempted. It also provides a client interface in order to send requests to the server.
有的大点的ROS系统项目,有些时候需要向一个节点发送一个request然后等它处理完了发回一个response.这个已经可以通过ROS里的Service的概念达到了。但有时候呢,如果这个service需要稍长一些的时间来运算之后才能反馈所需的结果,但在运行过程中,用户sonictl也许要根据运行的不同情况来决定是否取消request或者是需要周期性地获知运行状态。The actionlib 包就是提供这些工具,使得可以创建一些servers, 这些servers是用来执行需要长时间的任务的,并且是可以被preempted(预先制止)的。同时,actionlib包还提供了一个client接口,用来给server发请求。(sonictl@csdn_blog:这使我想到了move_base包可以被 cancle_goal 的功能)
For a full discussion of how actionlib operates "under the hood", please see theDetailed Description.
这里居然不就actionlib的实现机制进一步展开,而是让读者自己去看。我进去看了一下,感觉要ROS上上一台阶,应该好好理解它里面的机制。
*由于我有更重要的事情需要解决,暂时actionlib笔记就到这里等我回头来继续 --- sonictl@csdn_blog
The ActionClient and ActionServer communicate via a "ROS Action Protocol", which is built on top of ROS messages. The client and server then provide a simple API for users to request goals (on the client side) or to execute goals (on the server side) via function calls and callbacks.
上面所述的 ActionClient 和 ActionServer 通过 ROS Action Protocol (ROS Action 协议,建立在ROS messages的基础上)来通信。Client&Server 为用户提供了简单的API,通过函数的调用和回调,用来在client端request一个目标,或者,在server端来执行达成一个目标。下图说明这个机制如何运行:
这个其实是Client占主动,ActionServer占被动那意思~--- sonictl, csdn_blog
In order for the client and server to communicate, we need to define a few messages on which they communicate. This is with anaction specification. This defines the Goal, Feedback, and Result messages with which clients and servers communicate:
首先是如何定义messages来让Client&Server之间通信,这就是所谓的 action specification (Action说明书-的概念) 。它定义了Goal,Feedback,and Result messages来让Client和Server通信:
4.1 Goal
To accomplish tasks using actions, we introduce the notion of a goal that can be sent to an ActionServer by an ActionClient. In the case of moving the base, the goal would be a PoseStamped message that contains information about where the robot should move to in the world. For controlling the tilting laser scanner, the goal would contain the scan parameters (min angle, max angle, speed, etc).
为了用actions机制完成一个任务,我们引入了the notion of a goal, 这个goal可以被ActionClient发送到ActionServer. 比如在move base这个案例中,它的类型是PoseStamped,包含了机器人应该到达的哪里的信息。在激光扫描云台控制案例中,the goal会包含扫描的参数(min angle, max angle, speed 等)
Feedback
Feedback provides server implementers a way to tell an ActionClient about the incremental progress of a goal. For moving the base, this might be the robot's current pose along the path. For controlling the tilting laser scanner, this might be the time left until the scan completes.
Feedback是server用来告诉ActionClient goal执行过程中的各种情况。在moving_base案例中,它是机器人现在的位姿;在controlling the tilting laser scanner案例中, this might be the time left until the scan completes(扫描剩余时间).
Result
A result is sent from the ActionServer to the ActionClient upon completion of the goal. This is different than feedback, since it is sent exactly once. This is extremely useful when the purpose of the action is to provide some sort of information. For move base, the result isn't very important, but it might contain the final pose of the robot. For controlling the tilting laser scanner, the result might contain a point cloud generated from the requested scan.
执行结果,比如在move_base中的结果和机器人pose;在云台激光扫描中的一个请求的点云数据。等
The action specification is defined using a .action file. The .action file has the goal definition, followed by the result definition, followed by the feedback definition, with each section separated by 3 hyphens (---).
熟悉Message机制的朋友就会知道,这个跟那个类似,就是定义 action specification的消息数据类型滴~ -- sonictl_at_blog_csdn_net
These files are placed in a package's ./action directory, and look extremely similar to a service's.srv file. An action specification for doing the dishes might look like the following:
./action/DoDishes.action
# Define the goal uint32 dishwasher_id # Specify which dishwasher we want to use --- # Define the result uint32 total_dishes_cleaned --- # Define a feedback message float32 percent_complete
Based on this .action file, 6 messages(y? 可能是Client&Server各产生3个?) need to be generated in order for the client and server to communicate. This generation can be automatically triggered during the make process:
Add the following to your CMakeLists.txt file before catkin_package(). 注意你要用 actionlib 的话,记得修改CMakeLists.txt 文件!
find_package(catkin REQUIRED genmsg actionlib_msgs actionlib) add_action_files(DIRECTORY action FILES DoDishes.action) generate_messages(DEPENDENCIES actionlib_msgs)
Additionally, the package's package.xml must include the following dependencies:
actionlib actionlib_msgs actionlib actionlib_msgs
If you are using rosbuild instead of catkin, instead add the following before rosbuild_init().
rosbuild_find_ros_package(actionlib_msgs) include(${actionlib_msgs_PACKAGE_PATH}/cmake/actionbuild.cmake) genaction()
Then, after the output paths, uncomment (or add)
rosbuild_genmsg()
Note: rosbuild_genmsg() must be called after the output paths have been set.
For 1.0 series (i.e. boxturtle) use:
rosbuild_find_ros_package(actionlib) include(${actionlib_PACKAGE_PATH}/cmake/actionbuild.cmake) genaction() rosbuild_genmsg()
Additionally, the package's manifest.xml must include the following dependencies:
For the DoDishes.action, the following messages are generated by genaction.py:
DoDishesAction.msg
DoDishesActionGoal.msg
DoDishesActionResult.msg
DoDishesActionFeedback.msg
DoDishesGoal.msg
DoDishesResult.msg
DoDishesFeedback.msg
These messages are then used internally by actionlib to communicate between the ActionClient and ActionServer.
Full API Reference for the C++ SimpleActionClient
Quickstart Guide:
Suppose you have defined DoDishes.action in the chores package. The following snippet shows how to send a goal to a DoDishes ActionServer called "do_dishes".
1 #include /DoDishesAction.h>
2 #include /client/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 Client client("do_dishes", true); // true -> don't need ros::spin()
10 client.waitForServer();
11 chores::DoDishesGoal goal;
12 // Fill in goal here
13 client.sendGoal(goal);
14 client.waitForResult(ros::Duration(5.0));
15 if (client.getState() == actionlib::SimpleClientGoalState::SUCCEEDED)
16 printf("Yay! The dishes are now clean");
17 printf("Current State: %s\n", client.getState().toString().c_str());
18 return 0;
19 }
Note: For the C++ SimpleActionClient, thewaitForServer method will only work if a separate thread is servicing the client's callback queue. This requires passing intrue for the spin_thread option of the client's constructor, running with a multi-threaded spinner, or using your own thread to service ROS callback queues.
Full API reference for the Python SimpleActionClient
Suppose the DoDishes.action exists in thechores package. The following snippet shows how to send a goal to a DoDishes ActionServer called "do_dishes" using Python.
1 #! /usr/bin/env python 2 3 import roslib 4 roslib.load_manifest('my_pkg_name') 5 import rospy 6 import actionlib 7 8 from chores.msg import DoDishesAction, DoDishesGoal 9 10 if __name__ == '__main__': 11 rospy.init_node('do_dishes_client') 12 client = actionlib.SimpleActionClient('do_dishes', DoDishesAction) 13 client.wait_for_server() 14 15 goal = DoDishesGoal() 16 # Fill in the goal here 17 client.send_goal(goal) 18 client.wait_for_result(rospy.Duration.from_sec(5.0))
Full API Reference for the C++ SimpleActionServer
Quickstart Guide:
Suppose you have defined DoDishes.action in the chores package. The following snippet shows how to write a DoDishes ActionServer called "do_dishes".
1 #include /DoDishesAction.h>
2 #include /server/simple_action_server.h>
3
4 typedef actionlib::SimpleActionServer<chores::DoDishesAction> Server;
5
6 void execute(const chores::DoDishesGoalConstPtr& goal, Server* as)
7 {
8 // Do lots of awesome groundbreaking robot stuff here
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 Server server(n, "do_dishes", boost::bind(&execute, _1, &server), false);
17 server.start();
18 ros::spin();
19 return 0;
20 }
Full API Reference for the Python SimpleActionServer
Quickstart Guide:
Suppose you have defined DoDishes.action in the chores package. The following snippet shows how to write a DoDishes ActionServer called "do_dishes".
1 #! /usr/bin/env python 2 3 import roslib 4 roslib.load_manifest('my_pkg_name') 5 import rospy 6 import actionlib 7 8 from chores.msg import DoDishesAction, DoDishesServer 9 10 class DoDishesServer: 11 def __init__(self): 12 self.server = actionlib.SimpleActionServer('do_dishes', DoDishesAction, self.execute, False) 13 self.server.start() 14 15 def execute(self, goal): 16 # Do lots of awesome groundbreaking robot stuff here 17 self.server.set_succeeded() 18 19 20 if __name__ == '__main__': 21 rospy.init_node('do_dishes_server') 22 server = DoDishesServer() 23 rospy.spin()
The SimpleActionServer implements a single goal policy on top of theActionServer class. The specification of the policy is as follows:
Calling acceptNewGoal accepts a new goal when one is available. The status of this goal is set to active upon acceptance, and the status of any previouslyactive goal is set to preempted. Preempts received for the new goal betweenchecking if isNewGoalAvailable or invocation of a goal callback and theacceptNewGoal call will not trigger a preempt callback. This means,isPreemptRequested should be called after accepting the goal even forcallback-based implementations to make sure the new goal does not have apending preempt request.
Please refer to the Tutorials page
Use trac to report bugs or request features. [View active tickets]
Type |
# of Files |
# of Code Lines |
# of Comment Lines |
C++ |
14 |
769 |
512 |
C/C++ Header |
33 |
3212 |
1941 |
CMake |
3 |
115 |
8 |
Python |
24 |
1985 |
1163 |
XML |
1 |
30 |
0 |
SUM |
75 |
6111 |
3624 |
This metric is defined to be the number of visible characters in comments divided by the number of visible characters outside comments. Comment delimiters are ignored. Whitespace characters in strings are treated as visible characters. A large metric value may indicate that there are too many comments - an attribute that can make a module difficult to read. A small value may indicate that there are not enough.
Info
Distro average:
0.86
Stack average:
1.17
Thresholds:
0.2 < x < 2.0
In range:
100.0 %
This metric is a measure of the maximum control flow nesting in your source code. You can reduce the value of this metric by turning your nesting into separate functions. This will improve the readability of the code by reducing both the nesting and the average cyclomatic complexity per function.
Info
Distro average:
1.01
Stack average:
2.21
Thresholds:
0 < x < 5
In range:
100.0 %
The count of lines of a function body (starting at the first non-comment token that follows the exception specification, through to the last brace) that contain tokens, where lines that only contain any of the following are not counted: {, }, comment and all tokens of declarations.
Info
Distro average:
10.63
Stack average:
8.21
Thresholds:
1 < x < 70
In range:
28.6 %
Cyclomatic complexity is calculated as the number of decisions plus 1. High cyclomatic complexity indicates inadequate modularization or too much logic in one function. Software metric research has indicated that functions with a cyclomatic complexity greater than 10 tend to have problems related to their complexity.
Info
Distro average:
4.63
Stack average:
12.14
Thresholds:
1 < x < 15
In range:
64.3 %
The number of function calls within a function. Functions with a large number of function calls are more difficult to understand because their functionality is spread across several components. Note that the calculation of STSUB is based on the number of function calls and not the number of distinct functions that are called.
Info
Distro average:
17.05
Stack average:
32.64
Thresholds:
1 < x < 10
In range:
21.4 %
This gives an upper bound on the number of possible paths in the control flow of the function. It is the number of non-cyclic execution paths in a function.
Info
Distro average:
10265104.76
Stack average:
35714665.71
Thresholds:
1 < x < 250
In range:
64.3 %
The number of methods declared within a class. This does not include methods declared in base classes. Classes with a large number of methods will be difficult to understand.
Info
Distro average:
6.58
Stack average:
7.0
Thresholds:
1 < x < 20
In range:
100.0 %
This represents the number of derivations from the furthest base class down to this class. A high figure may indicate that the class depends on accumulated functionality, which makes understanding the class potentially difficult. This is one of the metrics defined by Chidamber & Kemerer.