ROS launch文档解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lqzdreamer/article/details/82813174


ROS提供了一个同时启动节点管理器(master)和多个节点的途径,即使用启动文件(launch file)。事实上,在ROS功能包中,启动文件的使用是非常普遍的。任何包含两个或两个以上节点的系统都可以利用启动文件来指定和配置需要使用的节点。通常的命名方案是以.launch作为启动文件的后缀,启动文件是XML文件。一般把启动文件存储在取名为launch的目录中。
lannch机制不保证节点的启动顺序,虽然launch文件是顺序分析,但节点初始化的时间长度不一,启动时间不一。

1. 宣告launch 档<launch> … </launch>

在launch文件一开头和结尾都必须用这个宣告框出来,像这样:

<launch>
…
</launch>

2.引数

引数通常用来作为执行各节点或launch 档所需要的输入参数,换句话说,设定区域变数,通常需要使用者输入所需的数值,但也可以事先写好预设的数值。另外一种用法,是用引数作为一个逻辑判断,决定那些节点要执行,哪些不用。
引数的语法会像这样:

<arg name="…" value="…">

其中name是参数的名称。Value 是参数的值。有时候也用default=”…”来设定预设值。以下举几个例子:

<arg name=”max_value” value=”0.5”>
<arg name=”height_above_ground” default=”1.6”>
<arg name=”camera_input” value=”/camera”>
<arg name=”sensors_on” value=”true”> <!—下面章节会再提及这个指令的用法–>

而在Indigo新版本中arg参数还存在选项doc=“description for this arg” (optional),这个选项是用来描述参数的说明文字。

3.注释代码<!– –>

举几个例子:

<!—Turn on laser–>
<!—Fire up Rviz–>
<!—Just want to comment out this line–>
<!–<node name=”foo” pkg=”foo_pkg” type=”foo”>–>

4.节点<node />

呼叫节点会包含以下几个参数:

<node pkg="…" type="…" name="…" respawn=true ns="…" args=”….” output="screen"/> 

<!—记得后面要写成/>要不然执行的时候会出错!–>
里面的参数及其公用:

参数 功用
pkg 表示要启动的节点所在的package
type 表示自己写的节点.cpp程序通过编译生产的可执行文件的名字,你最初编译.cpp程序的时候要在CMakeLists.txt添加cpp程序编译的设置,这个可执行文件的名字在CMakeLists.txt中就可以找到。
如果是Python文件时,则是filename.py文件,如:type=“inplace_pick_place_demo.py”
name 指该节点的名称,不过可以再另外帮这个节点取名字,那么该节点便会把原名给覆盖掉,以这个名称表示。你可以在执行时,用rqt或者rosnode list, rosnode info等指令查看到。
respawn/required 是当该节点由于不明原因停止执行的时候,会自动重新启动。而required比较霸道一点,当该节点停止执行的时候,会让整个launch 档都停止执行、关闭。
ns 指明在哪一个工作区间(workspace)的时候执行该节点,当必须在多个子类别的实体(instance)中执行同一个节点的时候会很用。
output 某个单独的节点在控制台中输出信息,只需在节点元素中配置:output=”screen”配置了该属性的节点会将标准输出显示在屏幕上而不是记录到日志文档。如果记录到log日志文档,即为output=“log”。

若要设定该节点的引用参数,可以在节点内下以下指令:

<args name=”” value=””>

基本上跟上述的引数用法差不多,但是当要引用使用者在上面小节给的数值的话,可以这样写:

<arg name=”camera” value=”/camera/rgb/image_raw”> <!—这是文件一开头时的引数–>….
<node pkg=”foo_pkg” type=”foo” name=”foo”>
<args name=”camera_namespace” value=”$(arg camera)”>
</node>
<!—记得要加入这个做结尾–>

其中,$(arg ….) 会自动去前面的<arg>找数值读进去。
除了<args>以外,还有其他选项,如以下:

参数 功用
<remap> 用法是<remap from=”…” to=”…”>。声明一个名称的映射,允许你通过名称映射参数到ROS 节点(通过更结构化的方式而不是直接设置节点参数属性来启动的节点)。
<env> 让该节点读入环境变数, 制定启动节点的环境变量
<rosparam> 让该节点读进参数设定档,使用rosparam 文件设置启动要用的ROS 参数
<param> 设定该节点所需的参数
<node> 启动一个节点.
<machine> 声明启动要使用的机器.
<include> 包含roslaunch 文件.
<test> 启动一个测试节点see rostest).
<arg> 声明参数
<group> 共享一个命名空间或映射的封闭的元素组。

这边只是列举几个比较常见参数。当然,还有更多参数选项,可以参考ROS Wiki文件。

5.添加其他launch 档

它的语法其实就是让ROS去找目标launch档的路径,一个很有用的写法,是用$(find <pkg>)这种语法来直接找包裹下的路径,所以不管这个包裹的路径被更改,程式照样能找得到目标。请看下面范例:

<include file="$(find openni2_launch)/launch/openni2.launch">
<arg name="camera" value="rgbd_front_top"/>
<arg name="device_id" value="#1″/>
<arg name="depth_registration" default="true"/>
</include>

以上是一个启动openni2.launch这个launch 档的语法,包含在<include>里面的则是其引数。那又要怎么知道设定那些引数呢?最简单的方法就是去看看目标launch档一开头的<arg> 标签,看看有那些设定可以更改。

6.逻辑判断式if & unless

讲到这边,可能你会有一个疑问。那这样的脚本语言有没有判断式,在某个情况下执行特定节点,另外一个特定情况不要执行呢?有的,但是并不像是你看过的任何高阶语言那样:

If (foo=true){
Return yes ;
}
Else
{
Return no ;
}

那怎么办?其实只要转念一想,我们可以拿作为逻辑判断的方式,但是必须搭配标签使用,写法如下:

<arg name="load_driver" default="true"/>
<group if="$(arg load_driver)">
<include file="$(find openni2_launch)/launch/openni2.launch"/>
</group>

同样的,也可以把标签中的if 换成unless,整个设定就变得像是"直到收到值为真或1时,执行该节点或launch档"。
到时候在终端机执行这个launch 档的时候,如果要关闭或执行某节点或launch档,请输入:

$ roslaunch pkg node load_driver:=false

或者

$ roslaunch pkg node load_driver:=true

这样就能决定是否执行或跳过某部分不执行。还有,要打”:=”,否则launch档要不就不理你继续执行,或者是跳出语法错误的讯息。

7.可替代参数substitution

args
Roslaunch标签属性可以使用可替代参数args,这将在roslaunch启动节点之前解析。 目前支持的替换参数是:

$(env ENVIRONMENT_VARIABLE)

替换当前环境中的变量值。 如果未设置环境变量,则启动将失败。 标记无法覆盖此值。

$(optenv ENVIRONMENT_VARIABLE)
$(optenv ENVIRONMENT_VARIABLE default_value)

如果已设置,则替换环境变量的值。 如果提供了default_value,则在未设置环境变量时将使用它。 如果未提供default_value,则将使用空字符串。 default_value可以是由空格分隔的多个单词。

Examples:
     <param name="foo" value="$(optenv NUM_CPUS 1)" />
     <param name="foo" value="$(optenv CONFIG_PATH /home/marvin/ros_workspace)" 		    />
     <param name="foo" value="$(optenv VARIABLE ros rocks)" />
$(find pkg)

例如
$(findrospy)/manifest.xml.指定包相对路径。
包目录的文件系统路径将被内联替换。
由于硬编码路径(绝对路径)会抑制启动配置的可移植性,因此强烈建议使用与程序包相关的路径。正向和反向斜杠将被解析为本地文件系统约定。

$(anon name)
e.g. $(anon rviz-1).

根据名称生成匿名ID。name本身是一个唯一标识符:$(anon foo)的多次使用将创建相同的“匿名”名称。
这用于name属性以创建具有匿名名称的节点,因为ROS要求节点具有唯一名称。
例如:

<node name="$(anon foo)"
pkg="rospy_tutorials" type="talker.py" />
<node name="$(anon
foo)" pkg="rospy_tutorials" type="talker.py"
/>
$(arg foo)
$(arg foo)计算为<arg>标记指定的值。

在同一个启动文件中必须有一个相应的标签来声明arg。
例如:

<param name="foo" value="$(arg
my_foo)" />
将my_foo参数分配给foo参数。
另一个例子:
<node name="add_two_ints_server"
pkg="beginner_tutorials" type="add_two_ints_server"
/>
<node
name="add_two_ints_client" pkg="beginner_tutorials"
type="add_two_ints_client" args="$(arg a) $(arg b)"
/>

将从<add_two_ints>示例启动服务器和客户端,将值a和b作为参数传递。 生成的启动项目可以按如下方式调用:

roslaunch beginner_tutorials launch_file.launch
a:=1 b:=5
$(eval <expression>) New in Kinetic
$(eval <expression>)允许评估任意复杂的python表达式。

例如:

<param
name="circumference" value="$(eval 2.* 3.1415 *arg('radius'))"/>

将从radius参数计算周长并将结果分配给适当的参数。
注意:作为限制,$(eval)表达式需要跨越整个属性字符串。 不可能在单个字符串中混合使用eval的其他替换args:

<param name="foo" value="$(arg foo)$(eval 6*7)bar"/>

要解决此限制,所有替换命令也可作为eval中的函数使用:

"$(eval arg('foo') + env('PATH') + 'bar' +find('pkg')"

为方便起见,还隐式解析了参数,即以下两个表达式是相同的:

"$(eval arg('foo'))"
"$(eval foo)"

替换args目前已在本地计算机上解决。 换句话说,环境变量和ROS包路径将在当前环境中设置为它们的值,即使对于远程启动的进程也是如此。

8.if和unless属性

所有标记都支持if和unless属性,这些属性包含或排除基于标记的评估值。 “1”和“真”被认为是真值。 “0”和“假”被认为是假值。 其他值将出错。

if = value(可选)
如果value的计算结果为true,则包含标记及其内容。
unless=value(可选)
除非value计算为true(这意味着如果value的计算结果为false),则包括标记及其内容。

例子:

<group
if="$(arg foo)">
<!-- stuff that will
only be evaluated if foo is true -->
</group>
<param name="foo" value="bar" unless="$(arg
foo)" /> <!-- This param won't be set when "unless"
condition is met

9.示例——启动XML配置文件

按照惯例,roslaunch XML文件以扩展名.launch命名,例如example.launch。
一个较为复杂的例子:

<launch>
  <! - 本地机器默认有一个定义。此标记用于覆盖默认定义特定的ROS_ROOT和ROS_PACKAGE_PATH值 - >
  <machine name =“local_alt”address =“localhost”default =“true”ros-root =“/ u / user / ros / ros /”ros-package-path =“/ u / user / ros / ros-pkg” />
  <! - 一个基本的listener 节点 - >
  <node name =“listener-1”pkg =“rospy_tutorials”type =“listener”/>
  <! - 将args传递给listener 节点 - >
  <node name =“listener-2”pkg =“rospy_tutorials”type =“listener”args =“ - foo arg2”/>
  <! - 一个可重新生成的listener 节点 - >
  <node name =“listener-3”pkg =“rospy_tutorials”type =“listener”respawn =“true”/>
  <! - 在'wg1'命名空间中启动listener 节点 - >
  <node ns =“wg1”name =“listener-wg1”pkg =“rospy_tutorials”type =“listener”respawn =“true”/>
  <! - 在'wg2'命名空间中启动一组节点 - >
  <group ns =“wg2”>
    <! - remap适用于此范围内的所有未来语句。 - >
    <remap from =“chatter”to =“hello”/>
    <node pkg =“rospy_tutorials”type =“listener”name =“listener”args =“ - test”respawn =“true”/>
    <node pkg =“rospy_tutorials”type =“talker”name =“talker”>
      <! - 为节点设置私有参数 - >
      <param name =“talker_1_param”value =“a value”/>
      <! - 节点可以有自己的重映射args - >
      <remap from =“chatter”to =“hello-1”/>
      <! - 您可以为节点设置环境变量 - >
      <env name =“ENV_EXAMPLE”value =“some value”/>
</node>
</group>
</launch>

10.开发一个大型专案的Launch 写法

最重要的,就是专案由于功能众多,有许多节点互相连结,所以会被隔成一层层的,所以,一个rule of thumb就是最上层的节点尽量解结的呼叫下一层的launch 档,然后下一层的launch档在呼叫下一层的launch档。而参数的设定尽量不要越级,该层级的参数设定就直接写在该层的launch档内,而不要上面好几层的launch档直接介入。这样的方法在除错和阅读上会清晰不少。
另外一个我之前开发碰到的问题就是,直接将他人的包裹直接加进自己的专案内部。站在版本控制的观念而言,每个包裹都是一个档案库(repository),除了在本地端维护外,更新的版本也会随时上传到云端。问题就发上在,一旦你将别人的档案库加进自己的专案,然后推上远端自己的档案库后,这些档案库变成你专案的一部分,再也不是他人的档案库,因此也无法更新成最新的版本。当我要把我的这个拥肿的包裹下载到另外一台电脑编译时,又与我之前安装的他人的同样的包裹名称起冲突。在把他人的包裹去掉,安装自己的包裹后,发现编译出错,但是密密麻麻的讯息,已经让我很难知道错误的源头。因此,后来也就决定把他人的智慧结晶从我的专案中移除,往后有需要使用到他人包裹中的某些功能时,直接用launch档呼叫即可。
从这个错误中我学到的教训是,不要把别人的档案库直接加进自己的档案库内,而是各别克隆(git clone)和编译,然后自己的档案库只负责自己写的程式和launch档。

11.怎么在终端机输入指令

档可以在自己的包裹内呼叫其他包裹的launch档或节点,在实用上更方便。那么在终端机时,只要用roslaunch指令即可,语法是:

$ roslaunch <pkg name> <launch file> <arg1>:=… <arg2>:=… <arg3>:=…

先宣告launch档所在的包裹名称,再来是launch档名称,后面的引数arg则是前面小节已经提到过的标签,其值可以被终端机上的指令覆盖掉。实际的例子:

$ roslaunch rtabmap_ros rgbd_mapping.launch rviz:=true rtabmapviz:=false

让我们来细看上面这行指令。

Pkg name rtabmap_ros
Launch file rgbd_mapping.launch
<arg 1> rviz:=true
<arg 2> rtabmapviz:=false

为了加快并简化launch的指令,其实可以直接把自打到一半,按Tab键,会自动补齐,按两下Tab键则会跳出更多选项让使用者输入正确的launch档,但是注意,有时候电脑不会帮你写后面的.launch,需要自己写完或在按Tab补齐。如果你按Tab老半天,电脑都没有反应,有两个选项,一个就是把名字自己打完执行看看,要不然就是直接source,让ROS连结到正在使用的工作空间上,如下 然后再试试看roslaunch一次。

$ souce ~/your_ws/devel/setup.bash

文章有参考:
ROS Launch文档是什么?

猜你喜欢

转载自blog.csdn.net/lqzdreamer/article/details/82813174