最近面临一个将ROS与机器人结合起来以扩展现有机器人的功能的任务,譬如将ABB的2400机器人接上ROS的moveit,然后通过ROS去控制机器人的运动,对于IRB2400这样的机器人实现还是蛮简单的,但是这种情况下,只知其然而不知其所以然,感觉就不好了,所以我得将这一套东西彻底的理解清楚,整理明白。在这里码下来记录一下我的学习与经历的过程,在面对这个问题时,首先想弄明白的就是roslaunch使用的.launch文件中到底写了些什么鬼!
对了,roslaunch文件的扩展名为.launch,是按照XML格式编写的,所以XML格式的文件也一样能解析。
1. 文件解析顺序
roslaunch 以单程的方式解析XML格式文件,include是以深度优先的方式解析内容,而标签Tag以串行的方式处理,所以一个参数的最后一次设置被认定为有效值。
也就是说在.launch文件中同样的设置可能存在多处,比如在开头定义了一个变量,遵循着尽量节省空间的想法,在后面的定义中又对这个变量进行了重新赋值,以便重新利用,在这种情况下,在整个launch文件解析完成后,最终进行的那次设置是有效的,但也并不保证一定有效,不排除在其它的文件中对变量进行了重命名,所以推荐使用$(arg)/<arg>
的方式进行覆写。
2. 置换参数
在roslaunch文件中,置换参数也称做置换符,是最常用的,可以提高变量的重用性。下面可用的置换参数如下所示:
2.1 $(env ENVIRONMENT_VARIABLE)
其中的“$”是一个置换处理标志(我是这么理解的),而“env”则是说明后面的变量被当做环境变量处理,毕竟“env”是“environment”(环境)的前面仨字母是吧。这行代码的意思就是用当前环境变量ENVIRONMENT_VARIABLE
的值置换目标变量值,不能被<env>
覆写,为什么不能覆写呢,后面有说明,如果想起来就把说明放这里,想不起来的话就到后面<env>
里找找吧。所以环境变量是比较严肃的,如果用于置换的环境变量未设置,当你启动launch文件时则会显示失败。
对着例子再来啰嗦一下(感觉比较笨怕以后看到理解不了…想像一下这里配了一张悲伤脸的表情)
<param name="variable_name" value="$(env NUM_CPUS)" />
上面例子中param
是launch文件中进行参数定义的关键字,name
属性指定了要定义的变量的名称,value
属性是对你前面指定的变量进行赋值或进行值替代,$(env NUM_CPUS)
就是你指定要用于进行目标变量值替代的选用值,这个时候问题就来了,如果你的电脑环境变量中有名叫NUM_CPUS
的环境变量,那没事,这行代码可以轻松通过,但是如果你的电脑里没有这个环境变量,那你运行这个launch文件的时候就是报错,所以相对来说,optenv
更具有鲁棒性。
2.2 $(optenv ENVIRONMENT_VARIABLE)
optenv
这个标识符就没有env
那么严肃,毕竟它在env
的前面加了opt
,那就变成了optional environment
了是吧,这一行代码就是说如果ENVIRONMENT_VARIABLE
已设置,则用其替代目标变量,若没有设置,则用空字符串表示。但我觉得直接理解下面一条更全面。
2.3 $(optenv ENVIRONMENT_VARIABLE default_value)
此行代码可以这样理解,先看ENVIRONMENT_VARIABLE
环境变量有没有设置, 若ENVIRONMENT_VARIABLE
有设置,就用ENVIRONMENT_VARIABLE
的值替代目标变量值,如果ENVIRONMENT_VARIABLE
没有设置,那就看default_value
是否有设置,如果default_value
设置过了,那就用default_value
的值去替代目标变量的值,如果default_value
未给定则用空字符串去初始化目标变量值。
给以下三个例子:
<param name="variable_name" value="$(optenv NUM_CPUS 1)" />
上面这一行代码中,在我的计算机中NUM_CPUS
这个环境变量不存在,所以最后variable
的值就是1这个缺省值。如果这个缺省值也没有设置,那最后将用空字符串替代目标变量值。
<param name="pkg_path_name" value="$(optenv PATH /home/catkin_ws/src)" />
这一行代码中,PATH
这个环境变量是存在的,所以名为pkg_path
的变量值便为PATH
的值。
<param name="variable_name" value="$(optenv VARIABLE ros rocks)" />
这一行是想说缺省设置的值可以是一个中间有空格隔开的字符串。
2.4 $(find pkg)
这行代码是用来进行相对路径的提取。
例如:
$(find rospy)/manifest.xml
便是找到包rospy的路径,并且会被内联替换。
2.5 $(anon name)
产生基于名称的匿名ID,主要用于节点名称属性中创建匿名节点,并检查是否有相同的匿名名字,因为ROS中名字作为标识符要求具有唯一性。其中的anon是anonymous的简写。
例如:
<node name="$(anon talk_01)" pkg="rospy_tutorials" type="talker.py" />
<node name="$(anon talk_01)" pkg="rospy_tutorials" type="talker.py" />
则会产生错误,因为系统检测到了两个节点具有相同的匿名名字。
2.6 $(arg varible_name)
解析由<arg>
标签指定的变量值。
例如:
<param name="varible_name" value="$(arg my_varible)" />
要求在同一文件中my_varible
已经由<arg>
标签所指定。即在launch文件中存在以下定义:
<arg name="my_varible_name" value="defined_value">
这样在解析my_varible
的值的时候才能正常进行。就如同你在表达式中用一个变量进行计算,你必须在计算之前就已经定义过这个变量才可以。
再如:
<node name="add_client" pkg="beginner" type="add_client" args="$(arg a) $(arg b)" />
其中的$(arg a)
和$(arg b)
则是对节点所需要的变量进行声明,这样你在对节点进行参数传递的时候就可以用以下代码进行:
roslaunch beginner launch_file.launch a:=1 b:=5
假设以上代码是launch_file.launch中的片段,则在调用文件时要按所声明的变量名称要求进行参数的赋值。
2.7 $(eval <expression>)
允许计算任意复杂的表达式,而标识符(Tag)或说是标签eval其实是evaluation的前几个字母,表示计算评估的意思,而后面则可以使用表达式。
例如:
<param name="circumference" value="$(eval 2.* 3.1415 * arg('radius'))"/>
会根据给定的'radius'
进行计算后对circumference
赋值。作为限制,$(eval)
的作用范围要跨越表示整个表达式的字符串,如果在其中插入其它属性是不可行的。
例如:
<param name="a_name" value="$(arg r_dis) $(eval 6*7) bar"/>
是不可行的。但可以:
"$(eval arg('sth') + env('PATH') + 'bar' + find('pkg'))"
在内部进行字符串相加操作。
3. IF 和 UNLESS 属性
所有的标签都支持if和unless属性,此类属性是基于计算的值包含或是排除一个标签的内容。
if = value (optional)
如果value的值为真则包含标签及内容。
unless = value (optional)
如果值为假,则包括此内容。
<group if="$(arg value)">
<!-- 如果值为真,则包含写在group里的内容 -->
</group>
<