机器人应用

机器人操作系统ROS(21):launch文件

0 – Summary

Roslaunch is a tool for easily launching multiple ROS nodes locally or remotely via SSH, as well as setting parameters on the Parameter Server.
It includes options to automatically respawn processes that have already died.
Roslaunch takes in one or more XML configuration files with the .launch extension that specify the parameters to set and nodes to launch, as well as the machines that they should be run on.

1 – Roslaunch Architecture

roslaunch was designed to fit the ROS architecture of complexity via composition: write a simple system first, then combine it with other simple systems to make more complex systems.

In roslaunch, this is expressed through several mechanisms:
1. include : you can easily include other .launch files and also assign them a namespace so that their names do not confict with yours.
2. group : you can group together a collection of nodes to give them the same name remappings. All resource names can be remapped. You can also provide assignment for private node parameters. Remapping arguments can be passed to any node and use the syntax name:=new_name. For example, to configure the talker node to publish to /wg/chatter instead of chatter:
$ rosrun rospy_tutorials talker chatter:=/wg/chatter. You can assign private parameters for a node directly from the command-line using a single underscore _ as a prefix. For example, $rosrun rospy_tutorials talker _param:=1.0, this will sets ~param to 1.0. ROS uses YAML syntax to determine the parameter typing. Note there are some Special keys(double _): __name, __ip and __hostname, __master, __ns.
3. aliased : you can separate machine definitions and node definitions into separate .launch files and use aliases to define which machines get used at runtime. This allows you to reuse the same node definitions for multiple robots. For example, instead of saying that a laser_assembler runs on ‘foo.willowgarage.com’, you can say that it runs on the ’tilt-laser’ machine. The machine definitions then take care of which host is the ’tilt-laser’ machine.

You can use the tag to specify environment variables that need to be set for a particular machine or node. The $(find pkg) syntax let you specify file paths relative to a ROS package, instead of specifying their location on a particular machine. You can also use the $(env ENVIRONMENT_VARIABLE) syntax within include tags to load in .launch files based on environment variables (e.g. MACHINE_NAME).

Roslaunch launches local processes using popen and kills them using POSIX signals. NOTE roslaunch does not guarantee any particular order to the startup of nodes — although this is a frequently requested feature, it is not one that has any particular meaning in the ROS architecture as there is no way to tell when a node is initialized.

2 – Commandline Tools

roslaunch is an important tool that manages the start and stop of ROS nodes. It takes one or more .launch files as arguments.
Most roslaunch command require the name of a launch file. You can either specify the file path of the launch file, or you can specify a package name and launch file in that package:
$ roslaunch [args]
Launch located in , e.g.:
$ roslaunch rospy_tutorials talker_listener.launch
or,
$ roscd package_name
$ roslaunch example.launch
or,Launch the file(s) specified by relative or absolute paths
$ roslaunch pr2_robot/pr2_bringup/pr2.launch

And [args] New in Indigo:
-p port, If you launched roscore on a different port using the -p option.
–wait, Delay the launch until a roscore is detected.
–local, Launch of the local nodes only. Nodes on remote machines will not be run.
–screen, Force all node output to screen. Useful for node debugging.
–dump-params, Print parameters in launch file in YAML format.

If the file you are launching specifies args that require setting, you can do so using an identical syntax to ROS remapping arguments, e.g.:
$ roslaunch my_file.launch arg:=value

2 – roslaunch XML

ROS 的 launch 文档中经常被使用到的三个与参数设置有关的标签 <arg>、<param>、rosparam。arg和param的区别是,param是在node或py等执行文件里面定义的,而arg则是在launch或xml等配置文件里定义的 。

变量标签 arg

<arg>标签给launch文件内部设置参数,通过<arg>标签设置的参数仅在launch文件内有效。

<arg>标签有三种设置参数的方式:

1、直接用此标签设置

2、用此标签声明,用roslaunch命令确定其值

3、用此标签声明,在include 这个launch文件的 父launch文件中确定其值

0、如何声明一个 argument :
<arg name=”arg-name” …/>

1、 如何在声明 argument 时赋值
<arg name=”arg-name” default=”arg-value” />
<arg name=”arg-name” value=”arg-value” />
default 和 value 的区别在于,在命令行中赋值的参数可以覆盖 default,但是不能重写 value 的值。

获取变量值的方式是:
$(arg arg-name)
我们可以通过 $arg$ 来使用该变量,roslaunch 会用给定的参数 $arg-name$ 的值替换整个表达式的值。

2、如何在命令行赋值指定 argument 的值
$ roslaunch package-name launch-file-name arg-name:=arg-value

3、将 argument 值传递给 included launch 文档
<include file=”path-to-file”>
<arg name=”arg-name”  value=”arg-value” />
</include>

注意:若 launch 文档及其 include 的 launch 文档出现相同的 argument,通过如下方式传递给 include launch 文档 :
<arg name=”arg-name” value=”$(arg arg-name)” />
第一个”arg-name”表示 include的launch 文档中的变量;第二个”arg-name”则表示当前 launch 文档中的变量 “arg-name” 指定的变量在当前 launch 文档以及 included launch 文档中都有相同的值。

参数标签 rosparam

尽管 argument(变量)和 parameter(参数)优势可互换,但二者在 ROS 中的意义完全不同:parameters 是 ROS 系统使用的数值,存在 parameter server 上,nodes 可通过ros::param::get函数编程得到,用户可通过rosparam命令获取;与之不同,arguments 仅在 launch 文档内部有意义,nodes 不能直接获取它们的值

前面介绍的<arg>用于设置变量;接下来介绍的两个<param>和<rosparam>标签用于设置参数。

参数访问有三种方式
1、命令行:
rosparam set、rosparam get

2、launch 文档:
<param>、<rosparam>

3、API方式
roscpp: ros::param::set、ros::param::get
rospy: set_param、set_param

<rosparam>  通过配置文档*.yaml文档加载参数
<rosparam file=”$(find navigation_launch)/config/costmap_common_params.yaml” command=”load” ns=”global_costmap” />

<rosparam> 其他常见方式
<rosparam command=”delete” param=”my/param” />
<rosparam param=”a_list”>[1, 2, 3, 4]</rosparam>
<rosparam>
a: 1
b: 2
</rosparam>

参数标签 param

<param>更像是 define 宏,必须在本地 launch 文档中赋值,无法用于在 launch 文档中获取命令中的参数(但可以通过下面介绍 rosrun 方式接收和传递)。定义的参数均会保留在 ROS 参数服务器(PARAMETERS)中,该参数会被节点(NODES)使用进行节点配置。
<launch>
<param name=”param-name-1″ value=”false” />
<node ….>
<param name=”param-name-2″ value=”…”>
</node>
</launch>
注意区别:第一个参数 param-name-1 ,是整个 launch 全局(global)有效;第二个参数 param-name-2 ,只相对(relative)节点局部(private)有效。

rosrun 本身的设计是可以让使用者直接修改节点内的参数,通过以下的方式:
$ rosrun package-name node-name _param-name:=param-value

rosparam可以使用command属性 :

<arg name=”urdf_file” default=”$(find xacro)/xacro.py ‘$(find turtlebot_description)/robots/$(arg base)_$(arg stacks)_$(arg 3d_sensor).urdf.xacro'” />

<param name=”robot_description” command=”$(arg urdf_file)” />

The output of the command will be read and stored as a string. It is strongly recommended that you use the package-relative $(find)/file.txt syntax to specify file arguments. You should also quote file arguments using single quotes due to XML escaping requirements.

env和optenv 标签

These two are using to modify the environment variables that one or more nodes are executed with.

Using env will force the user to have this variable set (roslaunch would fail if the variable is missing) . If you used the optenv expansion arg to set the value of the argument, then, you’d be able to decide the value of the argument simply by setting an environment variable.

The roslaunch evaluates the XML file in a single pass. Includes are processed in depth-first traversal order. Tags are evaluated serially and the last setting wins. Thus, if there are multiple settings of a parameter, the last value specified for the parameter will be used.
Relying on the override behavior can be brittle. There is no guarantee that an override is specified correctly (e.g. if a parameter name is changed in an included file). Instead, it is recommended that override behavior be done using $(arg)/ settings.

substitution args
Roslaunch tag attributes can make use of substitution args, which roslaunch will resolve prior to launching nodes. The currently supported substitution args are:

1. $(env ENVIRONMENT_VARIABLE)
Substitute the value of a variable from the current environment. The launch will fail if environment variable is not set. This value cannot be overridden by tags.

2. $(optenv ENVIRONMENT_VARIABLE)  $(optenv ENVIRONMENT_VARIABLE default_value)
Substitute the value of an environment variable if it is set. If default_value is provided, it will be used if the environment variable is not set. If default_value is not provided, an empty string will be used. default_value can be multiple words separated by spaces.

Examples:3. $(find pkg)
e.g. $(find rospy)/manifest.xml. Specifies a package-relative path. The filesystem path to the package directory will be substituted inline. Use of package-relative paths is highly encouraged as hard-coded paths inhibit the portability of the launch configuration. Forward and backwards slashes will be resolved to the local filesystem convention.

4. $(anon name)
e.g. $(anon rviz-1). Generates an anonymous id based on name. name itself is a unique identifier: multiple uses of $(anon foo) will create the same “anonymized” name. This is used for name attributes in order to create nodes with anonymous names, as ROS requires nodes to have unique names. For example:

5. $(arg foo)
$(arg foo) evaluates to the value specified by an tag. There must be a corresponding tag in the same launch file that declares the arg.
For example:
Will assign the my_foo argument to the foo parameter.
Another example:Will assign the my_foo argument to the foo parameter.
Another example:

Will launch both the server and client from the example, passing as parameters the value a and b. The resulting launch project can be called as follows:
The resulting launch project can be called as follows:
$ roslaunch beginner_tutorials launch_file.launch a:=1 b:=5

All tags support if and unless attributes, which include or exclude a tag based on the evaluation of a value. “1” and “true” are considered true values. “0” and “false” are considered false values. Other values will error.

TAGs:
1. tag
The tag is the root element of any roslaunch file. Its sole purpose is to act as a container for the other elements.
2. tag
The tag specifies a ROS node that you wish to have launched.
Examples

Launches the “listener1″ node using the listener.py executable from the rospy_tutorials package with the command-line argument –test. If the node dies, it will automatically be respawned.

Launches the bar node from the foo_pkg package. This example uses substitution arguments to pass in a portable reference to baz_pkg/resources/map.pgm.
Attributes includes: pkg=”mypackage”, Package of node. type=”nodetype”, Node type. There must be a corresponding executable with the same name. name=”nodename”, Node name. NOTE: name cannot contain a namespace. Use the ns attribute instead. machine=”machine-name”(optional, see ), Launch node on designated machine. ns=”foo”(optional) ,Start the node in the ‘foo’ namespace. output=”log|screen”(optional), If ‘screen’, stdout/stderr from the node will be sent to the screen. If ‘log’, the stdout/stderr output will be sent to a log file in $ROS_HOME/log, and stderr will continue to be sent to screen. The default is ‘log’.
3. tag
The tag declares a machine that you can run ROS nodes on. You do not need this tag if you are launching all the nodes locally. It is mainly used to declare SSH and ROS environment variable settings for remote machines, though you can also use it to declare information about the local machine.
4. tag
The tag allows you to pass in name remapping arguments to the ROS node that you are launching in a more structured manner than setting the args attribute of a directly.
Example, For example, you are given a node that says it subscribes to the “chatter” topic, but you only have a node that publishes the “hello” topic. They are the same type, and you want to pipe your “hello” topic into the new node which wants “chatter” by simply:
5.tag
Thetag defines a parameter to be set on the Parameter Server.
Examples,
6. tag
The tag enables the use of rosparam YAML files for loading and dumping parameters from the ROS Parameter Server. It can also be used to remove parameters. The tag can be put inside of a tag, in which case the parameter is treated like a private name. The delete and dump commands run before the load command as well as before any other parameters are uploaded to the Parameter Server. delete and dump commands run in the declared order.
Examples
7. The tag
The tag makes it easier to apply settings to a group of nodes. I
8. tag
The tag allows you to create more re-usable and configurable launch files by specifying values that are passed via the command-line, passing in via an , or declared for higher-level files. Args are not global. An arg declaration is specific to a single launch file, much like a local parameter in a method. You must explicitly pass arg values to an included file, much like you would in a method call.
can be used in one of three ways:
, Declares the existence of foo. foo must be passed in either as a command-line argument (if top-level) or via passing (if included).
, Declares foo with a default value. foo can be overriden by command-line argument (if top-level) or via passing (if included).
, Declares foo with constant value. The value for foo cannot be overridden. This usage enables internal parameterization of a launch file without exposing that parameterization at higher levels.
Examples:
my.launch: ## declare var hoge, and assign value, productor

included.launch: ## get value , store t paramserver, consumer

Passing an argument via the command-line:

$ $ roslaunch %YOUR_ROS_PKG% my.launch hoge:=my_new_value

Minimal Example
The following example shows a minimal launch configuration script. It launches a single ‘talker’ node, which is part of the ‘rospy_tutorials’ package. This node will launch on the local machine using the currently configured ROS environment (i.e. ROS_ROOT, etc…).

A Complicated Example

Setting parameters
You can also set parameters on the Parameter Server. These parameters will be stored on the Parameter Server before any nodes are launched.

5 – TIPS FOR LARGE PROJECTS

1. Introduction
Large applications on a robot typically involve several interconnected nodes, each of which have many parameters.
The 2d navigation is a good example. The 2dnav_pr2 application consists of the move_base node itself, localization, ground plane filtering, the base controller, and the map server. Collectively, there are also a few hundred ROS parameters that affect the behavior of these nodes. Finally, there are constraints such as the fact that ground plane filtering should run on the same machine as the tilt laser for efficiency.
A roslaunch file allows us to say all this. Given a running robot, launching the file 2dnav_pr2.launch in the 2dnav_pr2 package will bring up everything required for the robot to navigate. In this tutorial, we’ll go over this launch file and the various features used.
We’d also like roslaunch files to be as reusable as possible. In this case, moving between physically identical robots can be done without changing the launch files at all. Even a change such as moving from the robot to a simulator can be done with only a few changes. We’ll go over how the launch file is structured to make this possible.

2. Top-level organization
Here is the top-level launch file (in “rospack find 2dnav_pr2/move_base/2dnav_pr2.launch”).

This file includes a set of other files. Each of these included files contains nodes and parameters (and possibly nested includes) pertaining to one part of the system, such as localization, sensor processing, and path planning.
Design tip: Top-level launch files should be short, and consist of include’s to other files corresponding to subcomponents of the application, and commonly changed ROS parameters.
This makes it easy to swap out one piece of the system, as we’ll see later.

To run this on the PR2 robot requires bringing up a core, then bringing up a robot-specific launch file such as pre.launch in the pr2_alpha package, and then launching 2dnav_pr2.launch. We could have included a robot launch file here rather than requiring it to be launched separately. That would bring the following tradeoffs:
PRO: We’d have to do one fewer “open new terminal, roslaunch” step.
CON: Launching the robot launch file initiates a calibration phase lasting about a minute long. If the 2dnav_pr2 launch file included the robot launch file, every time we killed the roslaunch (with control-c) and brought it back up, the calibration would happen again.
CON: Some of the 2d navigation nodes require that the calibration already have finished before they start. Roslaunch intentionally does not provide any control on the order or timing of node start up. The ideal solution would be to make nodes work gracefully by waiting till calibration is done, but pending that, putting things in two launch files allows us to launch the robot, wait until calibration is complete, then launch 2dnav.

There is therefore no universal answer on whether or not to split things into multiple launch files. Here, it has been decided to use two different launch files.
Design tip: Be aware of the tradeoffs when deciding how many top-level launch files your application requires.

3. Machine tags and Environment Variables
We would like control over which nodes run on which machines, for load-balancing and bandwidth management. For example, we’d like the amcl node to run on the same machine as the base laser. At the same time, for reusability, we don’t want to hardcode machine names into $ roslaunch files. Roslaunch handles this with ****machine*** tags.

The first include is

The first thing to note about this file is the use of the env substitution argument to use the value of the environment variable ROBOT. For example, doing “$ export ROBOT=pre”
prior to the roslaunch would cause the file pre.machine to be included.
*** Design tip: Use the env substitution argument to allow parts of a launch file to depend on environment variables.

Next, let’s look at an example machine file: pre.machine in the pr2_alpha package.

This file sets up a mapping between logical machine names, “c1” and “c2” in this case, and actual host names, such as “pre2”. It even allows controlling the user you log in as (assuming you have the appropriate ssh credentials).
Once the mapping has been defined, it can be used when launching nodes. For example, the included file config/new_amcl_node.xml in the 2dnav_pr2 package contains the line:

This causes the amcl node to run on machine with logical name c1 (looking at the other launch files, you’ll see that most of the laser sensor processing has been put on this machine).
When running on a new robot, say one known as prf, we just have to change the ROBOT environment variable. The corresponding machine file (prf.machine in the pr2_alpha package) will then be loaded. We can even use this for running on a simulator, by setting ROBOT to sim. Looking at the file sim.machine in the pr2_alpha package, we see that it just maps all logical machine names to localhost.
*** Design tip: Use machine tags to balance load and control which nodes run on the same machine, and consider having the machine file name depend on an environment variable for reusability.

4. Parameters, namespaces, and yaml files
Let’s look at the included file move_base.xml. Here is a portion of this file:

The first included element is a remapping. Move_base is designed to receive odometry on the topic “odom”. In the case of the pr2, odometry is published on the pr2_base_odometry topic, so we remap it.
*** Design tip: Use topic remapping when a given type of information is published on different topics in different situations.
Then is followed by a bunch oftags. Note that these parameters are inside the node element (since they’re before the at the end), so they will be private parameters. For example, the first one sets move_base/controller_frequency to 10.0.
After theelements, there are some elements. These read parameter data in yaml, a format which is human readable and allows complex data structures.
Here’s a portion of the costmap_common_params.yaml file loaded by the first element:
raytrace_range: 3.0
footprint: [[-0.325, -0.325], [-0.325, 0.325], [0.325, 0.325], [0.46, 0.0], [0.325, -0.325]]
inflation_radius: 0.55
We see that yaml allows things like vectors (for the footprint parameter). It also allows putting some parameters into a nested namespace. For example, base_scan_marking/sensor_frame is set to base_laser. Note that these namespaces are relative to the yaml file’s own namespace, which was declared as global_costmap by the ns attribute of the including rosparam element. In turn, since that rosparam was included by the node element, the fully qualified name of the parameter is /move_base/global_costmap/base_scan_marking/sensor_frame.
# BEGIN VOXEL STUFF
observation_sources: base_scan_marking base_scan tilt_scan ground_object_cloud
base_scan_marking: {sensor_frame: base_laser, topic: /base_scan_marking, data_type: PointCloud, expected_update_rate: 0.2,
observation_persistence: 0.0, marking: true, clearing: false, min_obstacle_height: 0.08, max_obstacle_height: 2.0}
We see that yaml allows things like vectors (for the footprint parameter). It also allows putting some parameters into a nested namespace. For example, base_scan_marking/sensor_frame is set to base_laser. Note that these namespaces are relative to the yaml file’s own namespace, which was declared as global_costmap by the ns attribute of the including rosparam element. In turn, since that rosparam was included by the node element, the fully qualified name of the parameter is /move_base/global_costmap/base_scan_marking/sensor_frame.

The next line in move_base.xml is:

This actually includes the exact same yaml file as the line before it. It’s just in a different namespace (the local_costmap namespace is for the trajectory controller, while the global_costmap namespace affects the global navigation planner). This is much nicer than having to retype all the values.

The next line is:

Unlike the previous ones, this element doesn’t have an ns attribute. Thus the yaml file’s namespace is the parent namespace, /move_base.
But take a look at the first few lines of the yaml file itself:
local_costmap:
#Independent settings for the local costmap
publish_voxel_map: true
global_frame: odom_combined
robot_base_frame: base_link
Thus we see that the parameters are in the /move_base/local_costmap namespace after all.
*** Design tip: Yaml files allow parameters with complex types, nested namespaces of parameters, and reusing the same parameter values in multiple places.

5. Reusing launch files
The motivation for many of the tips above was to make reusing launch files in different situations easier. We’ve already seen one example, where the use of the env substitution arg can allow modifying behavior without changing any launch files. There are some situations, though, where that’s inconvenient or impossible. Let’s take a look at the pr2_2dnav_gazebo package. This contains a version of the 2d navigation app, but for use in the Gazebo simulator. For navigation, the only thing that changes is actually that the Gazebo environment we use is based on a different static map, so the map_server node must be loaded with a different argument. We could have used another env substitution here. But that would require the user to set a bunch of environment variables just to be able to roslaunch. Instead, 2dnav gazebo contains its own top level launch file called ‘2dnav-stack-amcl.launch’, shown here (modified slightly for clarity):

The first difference is that, since we know we’re on the simulator, we just use the sim.machine file rather than using a substitution argument.
Second, the line

has been replaced by

The included file in the first case just contained a node declaration as in the second case, but with a different map file.
*** Design tip: To modify a “top-level” aspect of an application, copy the top level launch file and change the portions you need.

6. Parameter overrides
The technique above sometimes becomes inconvenient. Suppose we want to use 2dnav_pr2, but just change the resolution parameter of the local costmap to 0.5. We could just locally change local_costmap_params.yaml. This is the simplest for temporary modifications, but it means we can’t check the modified file back in. We could instead make a copy of local_costmap_params.yaml and modify it. We would then have to change move_base.xml to include the modified yaml file. And then we would have to change 2dnav_pr2.launch to include the modified move_base.xml. This can be time-consuming, and if using version control, we would no longer see changes to the original files. An alternative is to restructure the launch files so that the move_base/local_costmap/resolution parameter is defined in the top-level file 2dnav_pr2.launch, and make a modified version of just that file. This is a good option if we know in advance which parameters are likely to be changed.
Another option is to use roslaunch’s overriding behavior: parameters are set in order (after includes are processed). Thus, we could make a further top-level file that overrides the original resolution:

The main drawback is that this method can make things harder to understand: knowing the actual value that roslaunch sets for a parameter requires tracing through the including roslaunch files. But it does avoid having to make copies of multiple files.
*** Design tip: To modify a deeply nested parameter in a tree of launch files which you cannot change, use roslaunch’s parameter overriding semantics.

7. Roslaunch arguments
As of CTurtle, roslaunch has an argument substitution feature together with tags that allow conditioning parts of the launch file on the value of arguments. This can be a more general and clear way to structure things than the parameter override mechanism or launch file reuse techniques above, at the cost of having to modify the original launch file to specify what the changeable arguments are. See the roslaunch XML documentation.

Design tip: If you can modify the original launch file, it’s often preferable to use roslaunch arguments rather than parameter overriding or copying roslaunch files.

机器人操作系统ROS(17):actionlib进度查询

ROS 里除了基本的msg publisher/Subscriber和Server/Client之外,还有ActionLib 值得注意。
ROS中的服务service是一问一答的形式,你来查询了,我就返给你要的信息。
Actionlib也有服务的概念,但是它不一样的地方是:不是一问一答,而多了一个反馈,它会不断反馈项目进度。

actionlib提供动作(action ROS中表示一些长时间连续性的行为)的编程接口,提供动作执行时的查看状态,终止, 抢占等一系列功能。
如navigation下的move_base, 你设定了目标点,反馈信息可能是机器人在规划路径上的即时位姿,直到机器人到达目标点,返回SUCCEEDED消息。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.

因为move_base会遇到很多有关actionlib的实现,需要先例解actionlib才可以清楚navigation的其他包。
move_base作为navigation的逻辑核心,实现了一个供上层节点调用的action 接口(通过actionlib 实现),即给定坐标信息与相应坐标位置,执行相应的安全导航动作。对于下层控制器,输出为cmd_vel 速度。 它规定了这个navigation的行为。
ROS中的自主移动规划层向上的编程接口一般都是通过这个actionlib。

1 ActionClient, ActionServer
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.

ActionClient 和 ActionServer 通过 ROS Action Protocol (ROS Action 协议,建立在ROS messages的基础上)来通信。
Client&Server 为用户提供了简单的API,通过函数的调用和回调,用来在client端request一个目标,或者,在server端来执行达成一个目标。下图说明这个机制如何运行:


这个是Client主动,ActionServer被动.

=== a Simple Action Server using the Execute Callback

2 Goal, Feedback, & Result
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:

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).
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.
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.
.action File
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 (—).
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
熟悉Message机制就知道,这个类似,就是定义 action specification的消息数据类型.
Based on this .action file, 6 messages need to be generated in order for the client and server to communicate. This generation can be automatically triggered during the make process.
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

3. Catkin, Rosbuild
3.1 Catkin
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)
actionlib
actionlib_msgs

3.2 Rosbuild

What following lists some tutorials examples.
4. create a test stack
$ cd ~/catkin_ws/src
$ catkin_create_pkg learning_actionlib actionlib message_generation roscpp rospy std_msgs actionlib_msgs

=== CH 5 – a Simple Action Server using the Execute Callback

5 C++ Example 1: Simple Action Server using the Execute Callback
This tutorial covers using the simple_action_server library to create a Fibonacci action server. This example action server generates a Fibonacci sequence,
the goal is the order of the sequence,
the feedback is the sequence as it is computed,
and the result is the final sequence.

5.1 Creating the Action Messages
Before writing an action it is important to define the goal, result, and feedback messages.
The action messages are generated automatically from the .action file.
This file defines the type and format of the goal, result, and feedback topics for the action.

mkdir ./action
vi ./action/fib.action

#goal definition
int32 order

#result definition
int32[] sequence

#feedback
int32[] sequence

5.2 Change CMakeLists.txt
a few things need to be added to CMakeLists.txt, add the actionlib_msgs package to the find_package macro’s argument like this

vi CMakeLists.txt

**1** find_package macro:
find_package(catkin REQUIRED COMPONENTS actionlib_msgs)

**2**: add_action_files macro
add_action_files(
DIRECTORY action
FILES fib.action
)

**3** generate_messages macro:
generate_messages(
DEPENDENCIES actionlib_msgs std_msgs # Or other packages containing msgs
)

**4** catkin_package macro:
catkin_package(
CATKIN_DEPENDS actionlib_msgs
)

Note: Sometimes you have to setup your package.xml, since we are generating messages you have to declare on the manifest file that at run time you have to generate messages.

**5** You could just insert the follow line:
vi package.xml
actionlib_msgs
actionlib_msgs

**6**
Now, we can automatically generate msg files of your action files, and also see the result:
$ cd ~/catkin_ws
$ catkin_make
$ ls devel/share/mimebot/msg/

fibActionFeedback.msg fibActionGoal.msg fibAction.msg fibActionResult.msg fibFeedback.msg fibGoal.msg fibResult.msg

**7**
Also, we can To manually generate the message files from this file,

$ roscd mimebot
$ rosrun mimebot genaction.py -o msg/ action/fib.action

Generating for action fib

5.3 Writing a Simple Server

$ vi ./src/m_fib_server.cpp

#include
#include
// Include the action library used from implementing simple actions.
#include
// The file generated automatically from the /devel/share/mimebot/msg/fibAction.msg file .
// this action message generated from the fib.action file .

class fibAction
{
protected:
ros::NodeHandle nh_;
//The node handle is constructed and passed into the action server during construction of the action.

actionlib::SimpleActionServer as_;
//The action server is constructed in the constructor of the action
//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

mimebot::fibFeedback feedback_;
mimebot::fibResult result_;
//The feedback and result messages are created for publishing in the action.

public:
fibAction(std::string name) :
as_(nh_, name, boost::bind(&fibAction::executeCB, this, _1), false),
action_name_(name) { as_.start(); }
//In the action constructor, an action server is created.
//The action server takes arguments of a node handle, name of the action, and optionally an executeCB.

~fibAction(void)
{
}

//The callback function is passed a pointer to the goal message.
// Note: This is a boost shared pointer, given by appending “ConstPtr” to the end of the goal message type.
void executeCB(const mimebot::fibGoalConstPtr &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 is executing, creating 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 is 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
r.sleep();
}

if(success)
{
result_.sequence = feedback_.sequence;
ROS_INFO(“%s is Succeeded”, action_name_.c_str());
// set the action state to succeeded
as_.setSucceeded(result_);
}
}

};

int main(int argc, char** argv)
{
ros::init(argc, argv, “m_fib”);

fibAction fibonacci(“action_fib”);
ros::spin();

return 0;
}

5.4 change CMakeLists.txt

Add the following lines to your CMakeLists.txt file:

add_executable(m_fib_server src/m_fib_server.cpp)
target_link_libraries(m_fib_server ${catkin_LIBRARIES} )
add_dependencies(m_fib_server ${mimebot_EXPORTED_TARGETS})

$ catkin_make

5.5
test
$ rosrun mimebot m_fib_server
to cheak:
$ rostopic list -v

/fib/cancel
/fib/feedback
/fib/goal
/fib/result
/fib/status
or,
$ rqt_graph

=== CH 6 clinet

5.6 Writing Action Client

$ vi mimebot/src/m_fib_client.cpp

#include
#include
//the action library used from implementing simple action clients.
#include
//defines the possible goal states.
#include

int main (int argc, char **argv)
{
ros::init(argc, argv, “m_fib_client”);

// create the action client
// true causes the client to spin its own thread
actionlib::SimpleActionClient ac(“action_fib”, true);
//The action client constructor also takes two arguments, the server name to connect to and a boolean option to automatically spin a thread.
//Here the action client is constructed with the server name and the auto spin option set to 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
mimebot::fibGoal goal;
goal.order = 20;
ac.sendGoal(goal);

//wait for the action to return
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.”);

//exit
return 0;
}

5.7 change CMakeLists.txt

add_executable(m_fib_client src/m_fib_client.cpp)

target_link_libraries( m_fib_client ${catkin_LIBRARIES} )
add_dependencies( m_fib_client ${mimebot_EXPORTED_TARGETS})

$catkin_make

5.8 to ckeak
$ rostopic echo /fib/feedback
$ rostopic echo /fib/result
$ rosrun learning_actionlib fibonacci_server
$ rosrun learning_actionlib fibonacci_client

6. Python Example2: Simple Action Server using the Execute Callback
6.1 Writing a Simple Action Server
#! /usr/bin/env python
import roslib; roslib.load_manifest(‘actionlib_tutorials’)
import rospy
import actionlib
import actionlib_tutorials.msg

class FibonacciAction(object):
# create messages that are used to publish feedback/result
_feedback = actionlib_tutorials.msg.FibonacciFeedback()
_result = actionlib_tutorials.msg.FibonacciResult()

def __init__(self, name):
self._action_name = name
self._as = actionlib.SimpleActionServer(self._action_name, actionlib_tutorials.msg.FibonacciAction, execute_cb=self.execute_cb, auto_start = False)
#Here, the SimpleActionServer is created, we pass it a name, an action type, and optionally an execute callback
#Since we’ve specified an execute callback in this example, a thread will be spun for us which allows us to take long running actions in a callback received when a new goal comes in.
self._as.start()

def execute_cb(self, goal):
# helper variables
r = rospy.Rate(1)
success = True

# append the seeds for the fibonacci sequence
self._feedback.sequence = []
self._feedback.sequence.append(0)
self._feedback.sequence.append(1)

# publish info to the console for the user
rospy.loginfo(‘%s: Executing, creating fibonacci sequence of order %i with seeds %i, %i’ % (self._action_name, goal.order, self._feedback.sequence[0], self._feedback.sequence[1]))

# start executing the action
for i in xrange(1, goal.order):
# check that preempt has not been requested by the client
if self._as.is_preempt_requested():
rospy.loginfo(‘%s: Preempted’ % self._action_name)
self._as.set_preempted()
success = False
break
#An important component of an action server is the ability to allow an action client to request that the goal under execution be canceled. When a client requests that the current goal be preempted, the action server should cancel the goal, perform any necessary cleanup, and call the set_preempted function, which signals that the action has been preempted by user request. Here, we’ll check if we’ve been preempted every second. We could, alternatively, receive a callback when a preempt request is received.

self._feedback.sequence.append(self._feedback.sequence[i] + self._feedback.sequence[i-1])
# publish the feedback
self._as.publish_feedback(self._feedback)
# this step is not necessary, the sequence is computed at 1 Hz for demonstration purposes
r.sleep()
#Here, the Fibonacci sequence is put into the feedback variable and then published on the feedback channel provided by the action server. Then, the action continues looping and publishing feedback.

if success:
self._result.sequence = self._feedback.sequence
rospy.loginfo(‘%s: Succeeded’ % self._action_name)
self._as.set_succeeded(self._result)
#Once the action has finished computing the Fibonacci sequence, the action server notifies the action client that the goal is complete by calling set_succeeded.

if __name__ == ‘__main__’:
rospy.init_node(‘fibonacci’)
FibonacciAction(rospy.get_name())
rospy.spin()

6.2 Writing a Simple Action Client
#! /usr/bin/env python
import roslib; roslib.load_manifest(‘actionlib_tutorials’)
import rospy
# Brings in the SimpleActionClient
import actionlib
# Brings in the messages used by the fibonacci action, including the goal message and the result message.
import actionlib_tutorials.msg

def fibonacci_client():
# Creates the SimpleActionClient, passing the type of the action (FibonacciAction) to the constructor.
client = actionlib.SimpleActionClient(‘fibonacci’, actionlib_tutorials.msg.FibonacciAction)
#The action client and server communicate over a set of topics, described in the actionlib protocol. The action name describes the namespace containing these topics, and the action specification message describes what messages should be passed along these topics

# Waits until the action server has started up and started
# listening for goals.
client.wait_for_server()
#Sending goals before the action server comes up would be useless. This line waits until we are connected to the action server.

# Creates a goal to send to the action server.
goal = actionlib_tutorials.msg.FibonacciGoal(order=20)

# Sends the goal to the action server.
client.send_goal(goal)

# Waits for the server to finish performing the action.
client.wait_for_result()

# Prints out the result of executing the action
return client.get_result() # A FibonacciResult

if __name__ == ‘__main__’:
try:
# Initializes a rospy node so that the SimpleActionClient can
# publish and subscribe over ROS.
rospy.init_node(‘fibonacci_client_py’)
result = fibonacci_client()
print “Result:”, ‘, ‘.join([str(n) for n in result.sequence])
except rospy.ROSInterruptException:
print “program interrupted before completion”

6.3 to check
$ rosrun actionlib_tutorials fibonacci_server
$ rosrun actionlib_tutorials fibonacci_client.py
$ rostopic echo fibonacci/feedback

7. C++ Example3: Writing a Simple Action Server using the Goal Callback Method
This tutorial covers using the simple_action_server library to create an averaging action server. This example shows how to use an action to process or react to incoming data from ros nodes. The action server averages data from a ros node, the goal is the number of samples to average, the feedback is the sample number, the sample data, the current average, and current standard deviation, and the result is the average and standard deviation of the requested number of samples.

7.1 Create learning_actionlib/action/Averaging.action
#goal definition
int32 samples

#result definition
float32 mean
float32 std_dev

#feedback
int32 sample
float32 data
float32 mean
float32 std_dev

7.2 manually generate the message files from this file:
$ roscd learning_actionlib
$ rosrun actionlib_msgs genaction.py -o msg/ action/Averaging.action<—-???
or,
automatically generate the message files during the make process, need to add the following to 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)
and
$ catkin_make.

7.3 nano learning_actionlib/src/averaging_server.cpp

7.4 Add the following line to CMakeLists.txt file:
add_executable(averaging_server src/averaging_server.cpp)
target_link_libraries(averaging_server ${catkin_LIBRARIES})

7.5 catkin_make
7.6 to cheak
$ rosrun learning_actionlib averaging_server

7.7 Writing a Threaded Simple Action Client
$ nano learning_actionlib/src/averaging_client.cpp

7.8 Add the following line to CMakeLists.txt file:
add_executable(averaging_client src/averaging_client.cpp)
target_link_libraries(averaging_client ${catkin_LIBRARIES})
catkin_make

7.9 to check
$ rosrun learning_actionlib averaging_server
$ rosrun learning_actionlib averaging_client

7.10 from other node

4. C++ samples
4.1 C++ ActionClient
4.2 C++ ActionServer
5. Python samples
5.1 Python ActionClient
5.2 Python ActionServer

http://www.cnblogs.com/feixiao5566/p/4757916.html

机器人操作系统ROS(15):nodelet

ROS的数据通信是以XML-RPC的方式,在graph结构中以topic,service和param的方式传输数据,天生的数据交互存在一定的延时和阻塞。

Nodelet 包就是改善这一状况设计的, 使得多个算法运行在同一个过程中,并且算法间数据传输无需拷贝就可实现。 简单的讲就是可以将以前启动的多个node捆绑在一起manager,使得同一个manager里面的topic的数据传输更快,数据通讯中roscpp采用boost shared pointer方式进行publish调用,实现zero copy。  The nodelet package is designed to provide a way to run multiple algorithms in the same process with zero copy transport between algorithms. This package provides both the nodelet base class needed for implementing a nodelet, as well as the NodeletLoader class used for instantiating nodelets.

Regular nodes use TCP and this works fine for a lot of things. But if you have multiple processes that need to use messages that contain large amounts of data (like images or point clouds) packaging the message, sending it, then unpacking it can take a bit of time.
So if the two processes are on the same computer, it is quicker to just send a pointer to that data rather than sending the data itself over TCP. This only works for processes (aka nodelets) on the same computer though since a pointer for one computer doesn’t make sense for another computer.
So nodelets don’t make the processes quicker, but it is a quicker way to get information from one process to another.

TCPROS style

BUT, I recommend you don’t use nodelet. If you’re thinking of using it, you might want to first consider just launching two threads in a single node, instead of two nodes. If you create two threads in a single node, it will be more memory efficient than launching two “nodelets” in ROS.
e.g. REF: kobuki Tutorials Write your own controller for Kobuki

特点
1 nodelets间数据传输zero copy,有效避免数据copy和网络传输代价
2 支持pulgin的方式动态加载
3  使用C++ ROS的接口方式

nodelet 基类
1 定义基类nodelet::Nodelet, 任何nodelet继承自它可以使用plugin的方式动态加载。
2 提供命名空间,参数可remap
3  nodelet的manager的过程可以加载更多的nodelets. 同一个manager过程的nodelet数据传输zero copy .

Basic Usage
nodelet usage:
nodelet load pkg/Type manager – Launch a nodelet of type pkg/Type on manager manager
nodelet standalone pkg/Type   – Launch a nodelet of type pkg/Type in a standalone nod
nodelet unload name manager   – Unload a nodelet a nodelet by name from manager
nodelet manager               – Launch a nodelet manager node

Bring up a manager
A nodelet will be run inside a NodeletManager. A nodelet manager is a c++ program which is setup to listen to ROS services and will be the executable in which the nodelet is dynamically loaded. In this case we will run a standalone manager, but in many cases these managers will be embedded within running nodes.
$ rosrun nodelet nodelet manager __name:=nodelet_manager

Launching the nodelet
The nodelet executable, called here, will contact the nodelet_manager and ask it to instantiate an instance of the nodelet_tutorial_math/Plus nodelet. It will pass through the name, nodelet1, and also any remappings if applied to the code inside the nodelet. And parameters appear in the right namespace too.
$ rosrun nodelet nodelet load nodelet_tutorial_math/Plus nodelet_manager __name:=nodelet1 nodelet1/in:=foo _value:=1.1