ROS launch文档介绍

本文章转自:https://charlyhuangrostutorial.wordpress.com/2015/08/12/20/

前面已经提过关于launch 档的角色,很类似bash 档,基本上就是把所有为了执行某个特定功能所需要的指令都写在一张纸上,交给ROS 一次执行开来。举例来说,今天我想执行SLAM (即时建图及定位)的功能,但是这个演算法并不简单,我必须喂好几个输入资料进去,所以,在不知道launch 档好处的情况下,我就必须在终端机上个别开分页这样操作:

  • 把雷射打开
  • 把里程计(odometry)打开
  • 把tf 程式打开
  • 把gmapping 节点打开
  • 把马达驱动器打开
  • 把teleoperation 节点打开,因为我想直接操作机器人
  • 把图形介面打开,因为我像直接看结果

这如果是在同一台电脑上操作还好,但是如果今天如果是远端怎么办?尤其是碰到频宽有限的问题,那么远端频频断线,工作起来真的会要人命。所以,睿智且聪明的设计者们便设计了launch 档。只要执行它一次,便会call所有需要的launch 档和节点,并且可以自订输入的参数,可以说是相当方便。但是怎么个方便法?让我们继续看下去。

内容一览

撰写第一份Launch 档

如果你有机会下载一个别人写好的包裹,并且打开资料夹进里面看,可能会找到一个Launch资料夹,这里就是存放所有Launch档的地方,这种存放方法我把它视为某种约定俗成的习惯,因为方便管理嘛,哈!
我们将以Spencer People Tracking为范例,将这个专案中一部份的Launch file拿出来参考。但是在开始看之前,让我们先了解launch的语法。

Launch 档实际上使用YAML 格式,说穿了,Launch 档就是一种脚本语言(Duh!)所以如果你有碰过XML, XAML, HTML等语言过,便可驾轻就熟。有几种关键语法:

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

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

<launch>

</launch>

引数<arg>

引数通常用来作为执行各节点或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”> <!—下面章节会再提及这个指令的用法–>

注解<!– –>

举几个例子:

 <!—Turn on laser–>

<!—Fire up Rviz–>

<!—Just want to comment out this line–>

<!–<node name=”foo” pkg=”foo_pkg” type=”foo”>–>

呼叫节点<node />

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

<node pkg="…" type="…" name="…" respawn=true ns="…" args=”….”/> 
<!—记得后面要写成/>要不然执行的时候会出错!–>

里面的参数及其公用:

参数 功用
pkg 表示该节点所在的包裹
type 表示这个节点实际的名称,也就是开发的时候取的名字
name 虽然也是指该节点的名称,不过你可以再另外帮这个节点取名字,那么该节点便会把原名给覆盖掉,以这个名称表示。你可以在执行时,用rqt或者
rosnode list, rosnode info等指令查看到。
respawn/required 是当该节点由于不明原因停止执行的时候,会自动重新启动。而required比较霸道一点,当该节点停止执行的时候,会让整个launch 
档都停止执行、关闭。
ns 指明在哪一个工作区间(workspace)的时候执行该节点,当你必须在多个子类别的实体(instance)中执行同一个节点的时候会很用。

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

<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=”…”>。将原本节点的输入管道,接"到"新的topics 
上面。我都这样想像,把节点想像成大象,把他那条长长的鼻子(输入的topics,from=”…”)拉到我要他吃进去的topics 
上面去(to=”target topic”) 。
<env> 让该节点读入环境变数
<rosparam> 让该节点读进参数设定档
<param> 设定该节点所需的参数

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

呼叫其他launch 档<include>

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

<include file="$(find openni2_launch)/launch/openni2.launch">

<arg name="camera" value="rgbd_front_top"/>

<arg name="d​​evice_id" value="#1″/>

<arg name="d​​epth_registration" default="true"/>

</include>

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

逻辑判断式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>

同样的,也可以把<group>标签中的if 换成unless,整个设定就变得像是"直到收到值为真或1时,执行该节点或launch档"。

到时候在终端机执行这个launch 档的时候,如果要关闭或执行某节点或launch档,请输入:


$ roslaunch pkg node load_driver:=false


或者


$ roslaunch pkg node load_driver:=true


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

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

其实我尚未真的开发过一个大型专案,不过我们可以结合前人的智慧结晶以及自己的开发经验。

最重要的,就是专案由于功能众多,有许多节点互相连结,所以会被隔成一层层的,所以,一个rule of thumb就是最上层的节点尽量解结的呼叫下一层的
launch 
档,然后下一层的launch档在呼叫下一层的launch档。而参数的设定尽量不要越级,该层级的参数设定就直接写在该层的launch档内,而不要上面好几层的launch档直接介入。这样的方法在除错和阅读上会清晰不少。

另外一个我之前开发碰到的问题就是,直接将他人的包裹直接加进自己的专案内部。站在版本控制的观念而言,每个包裹都是一个档案库(repository),除了在本地端维护外,更新的版本也会随时上传到云端。问题就发上在,一旦你将别人的档案库加进自己的专案,然后推上远端自己的档案库后,这些档案库变成你专案的一部分,再也不是他人的档案库,因此也无法更新成最新的版本。当我要把我的这个拥肿的包裹下载到另外一台电脑编译时,又与我之前安装的他人的同样的包裹名称起冲突。在把他人的包裹去掉,安装自己的包裹后,发现编译出错,但是密密麻麻的讯息,已经让我很难知道错误的源头。因此,后来也就决定把他人的智慧结晶从我的专案中移除,往后有需要使用到他人包裹中的某些功能时,直接用launch档呼叫即可。

从这个错误中我学到的教训是,不要把别人的档案库直接加进自己的档案库内,而是各别克隆(git 
clone)和编译,然后自己的档案库只负责自己写的程式和launch档。这样做的好处有四:

  1. 可以独立测试各包裹,方便除错
  2. 由于每个包裹仍然是个别的档案库,因此仍然可以更新成最新版本,也方便维护与同步
  3. 在不同的平台编译的时候,方便除错与维护。
  4. 我们不应该很大喇喇地窃取人家的智慧结晶,占为己有却不说声谢谢。(啊!这算是我个人观感,就别算在原因内了吧!)

怎么在终端机下指令

像上面的错误反省有提到的,Launch档可以在自己的包裹内呼叫其他包裹的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

实际范例

好了,大概讲完了,我们来看launch档实际范例,这是spencer_people_tracking中的tracking_single_rgbd_senosr.launch 
在终端机执行时,请打: 以下是Launch档:
$ roslaunch spencer_people_tracking tracking_single_rgbd_sensor.launch


<launch>

<!– Launch file arguments –>

<arg name="height_above_ground" default="1.6″/> <!– in meters, assumes a 
horizontally oriented RGB-D sensor; important for accurate detection –>

<arg name="load_driver" default="true"/> <!– set to false if you are already 
running OpenNi from elsewhere –>

<arg name="visualization" default="true"/>

<arg name="d​​ummy_transforms" default="true"/>

<!– Run OpenNi2 driver –>

<group ns="spencer/sensors" if="$(arg load_driver)">

<include file="$(find openni2_launch)/launch/openni2.launch">

<arg name="camera" value="rgbd_front_top"/>

<arg name="d​​evice_id" value="#1″/>

<arg name="d​​epth_registration" default="true"/>

</include>

</group>

<!– Set ground plane distance –>

<rosparam 
param="/spencer/perception_internal/people_detection/ground_plane/distance" 
subst_value="true">$(arg height_above_ground)</rosparam>

<!– Set up dummy transforms into an imaginary robot and odom frame –>

<group if="$(arg dummy_transforms)">

<node name="tf_base_footprint" pkg="tf" type="static_transform_publisher" 
args="0 0 $(arg height_above_ground) 0 0 0 base_footprint rgbd_front_top_link 
10″/>

<node name="tf_odom" pkg="tf" type="static_transform_publisher" args="0 0 0 0 0 
0 odom base_footprint 10″/>

</group> 
<!– Detectors –>

<include file="$(find 
spencer_people_tracking_launch)/launch/detectors/front_rgbd_detectors.launch"/>

<!– People tracking –>

<include file="$(find 
spencer_people_tracking_launch)/launch/tracking/people_tracking.launch">

<arg name="rgbd" default="true"/>

<arg name="laser_low_confidence_detections" default="false"/>

</include>

<!– As there is not yet any high-recall/low-confidence detector for RGB-D, and 
we are not using laser, tracks may get deleted too quickly in case of missed 
detections.

To deal with this, for the moment, we increase the maximum number of occluded 
frames to be a bit more tolerant towards missed detections.

This works fine in uncrowded environments which are not very dynamic. –>

<rosparam 
param="/spencer/perception_internal/people_tracking/srl_nearest_neighbor_tracker/max_occlusions_before_deletion">50</rosparam>

<rosparam 
param="/spencer/perception_internal/people_tracking/srl_nearest_neighbor_tracker/max_occlusions_before_deletion_of_mature_track">200</rosparam>

<!– Group tracking –>

<include file="$(find 
spencer_people_tracking_launch)/launch/tracking/group_tracking.launch"/>

<!– RViz visualization –>

<node name="tracking_visualization_rviz" pkg="rviz" type="rviz" args="-d $(find 
spencer_people_tracking_launch)/rviz/ tracking-single-rgbd-sensor.rviz " if="$(arg 
visualization)"/ >

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值