-
Notifications
You must be signed in to change notification settings - Fork 11
管理大型项目的roslaunch技巧
ericwen edited this page Apr 11, 2019
·
1 revision
- 描述:本教程描述了为大型项目编写roslaunch文件的一些技巧。 重点是如何构建launch文件,以便在不同情况下尽可能多地重用它们。 我们将使用2dnav_pr2包作为案例研究。
- 教程级别:中级 原文链接
- 机器人上的大型应用程序通常涉及多个互连节点,每个节点具有许多参数。 2d导航就是一个很好的例子。 2dnav_pr2应用程序由move_base节点本身,本地化,地平面过滤,基本控制器和地图服务器组成。 总的来说,还有几百个ROS参数会影响这些节点的行为。 最后,存在一些限制,例如地平面滤波应该与倾斜激光器在同一台机器上运行以提高效率。
- 一个roslaunch文件允许我们说出这一切。 给定一个正在运行的机器人,在2dnav_pr2包中启动文件2dnav_pr2.launch将显示机器人导航所需的所有内容。 在本教程中,我们将介绍此启动文件以及所使用的各种功能。
- 我们也希望roslaunch文件尽可能重用。 在这种情况下,可以在不改变启动文件的情况下在物理上相同的机器人之间移动。 即使是从机器人移动到模拟器的变化也只需要进行一些改动即可完成。 我们将详细介绍如何构建启动文件以使其成为可能。
- 这是顶级启动文件(在“rospack find 2dnav_pr2 / move_base / 2dnav_pr2.launch”中(译者注:需要事先把pr2的包克隆下来,可以去github搜2dnav_pr2)
<launch>
<group name="wg">
<include file="$(find pr2_alpha)/$(env ROBOT).machine" />
<include file="$(find 2dnav_pr2)/config/new_amcl_node.xml" />
<include file="$(find 2dnav_pr2)/config/base_odom_teleop.xml" />
<include file="$(find 2dnav_pr2)/config/lasers_and_filters.xml" />
<include file="$(find 2dnav_pr2)/config/map_server.xml" />
<include file="$(find 2dnav_pr2)/config/ground_plane.xml" />
<!-- The navigation stack and associated parameters -->
<include file="$(find 2dnav_pr2)/move_base/move_base.xml" />
</group>
</launch>
- 该文件包含一组其他文件。 这些包含文件中的每一个都包含与系统的一部分有关的节点和参数(以及可能嵌套的包含),例如本地化,传感器处理和路径规划。
- 设计提示:顶级启动文件应该很短,并且包含与应用程序子组件对应的其他文件的包含,以及通常更改的ROS参数。
- 这样可以轻松更换系统的一部分,我们稍后会看到。
- 要在PR2机器人上运行它,需要启动一个核心,然后在pr2_alpha包中调出一个特定于机器人的启动文件,例如pre.launch,然后启动2dnav_pr2.launch。 我们可以在这里只包含一个机器人启动文件,而不是要求它单独启动。 这将带来以下权衡:
- PRO:我们不得不少做一些“开放式新终端,roslaunch”步骤。
- CON:启动机器人启动文件启动持续约一分钟的校准阶段。 如果2dnav_pr2启动文件包含机器人启动文件,每次我们杀死roslaunch(使用control-c)并将其重新启动时,校准将再次发生。
- CON:某些2d导航节点要求校准在开始之前已经完成。 Roslaunch故意不对节点启动的顺序或时间提供任何控制。 理想的解决方案是通过等待校准完成来使节点正常工作,但在此之前,将事物放入两个启动文件允许我们启动机器人,等到校准完成,然后启动2dnav。
- 因此,对于是否将事物分成多个启动文件没有通用的答案。 在这里,已经决定使用两个不同的启动文件。
- 设计提示:在决定应用程序需要多少顶级启动文件时,请注意权衡。
- 我们希望控制哪些节点在哪些机器上运行,以实现负载平衡和带宽管理。 例如,我们希望amcl节点在与基本激光器相同的机器上运行。 同时,为了可重用,我们不希望将机器名称硬编码到roslaunch文件中。 Roslaunch使用机器标签来处理这个问题。
- The first include is
<include file="$(find pr2_alpha)/$(env ROBOT).machine" />
- 关于这个文件要注意的第一件事是使用env替换参数来使用环境变量ROBOT的值。 例如,这样做
export ROBOT=pre
- 在roslaunch之前会导致文件pre.machine被包括在内。
- 设计提示:使用env substitution参数允许启动文件的某些部分依赖于环境变量。
- 接下来,让我们看一下示例机器文件:pr2_alpha包中的pre.machine。
<launch>
<machine name="c1" address="pre1" ros-root="$(env ROS_ROOT)" ros-package-path="$(env ROS_PACKAGE_PATH)" default="true" />
<machine name="c2" address="pre2" ros-root="$(env ROS_ROOT)" ros-package-path="$(env ROS_PACKAGE_PATH)" />
</launch>
- 此文件在逻辑机器名称(本例中为“c1”和“c2”)和实际主机名(如“pre2”)之间设置映射。 它甚至允许控制您登录的用户(假设您有适当的ssh凭据)。
- 一旦定义了映射,就可以在启动节点时使用它。 例如,2dnav_pr2包中包含的文件config / new_amcl_node.xml包含该行
<node pkg="amcl" type="amcl" name="amcl" machine="c1">
- 这会导致amcl节点在逻辑名为c1的计算机上运行(查看其他启动文件,您将看到大多数激光传感器处理已放在此计算机上)。
- 当在新机器人上运行时,比如一个称为prf的机器人,我们只需要更改ROBOT环境变量。 然后将加载相应的机器文件(pr2_alpha包中的prf.machine)。 我们甚至可以通过将ROBOT设置为sim来使用它在模拟器上运行。 查看pr2_alpha包中的文件sim.machine,我们看到它只是将所有逻辑机器名称映射到localhost。
- 设计提示:使用机器标签来平衡负载并控制在同一台机器上运行的节点,并考虑使机器文件名依赖于环境变量以获得可重用性。
- 我们来看看包含的文件move_base.xml。 以下是此文件的一部分:
<node pkg="move_base" type="move_base" name="move_base" machine="c2">
<remap from="odom" to="pr2_base_odometry/odom" />
<param name="controller_frequency" value="10.0" />
<param name="footprint_padding" value="0.015" />
<param name="controller_patience" value="15.0" />
<param name="clearing_radius" value="0.59" />
<rosparam file="$(find 2dnav_pr2)/config/costmap_common_params.yaml" command="load" ns="global_costmap" />
<rosparam file="$(find 2dnav_pr2)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find 2dnav_pr2)/move_base/local_costmap_params.yaml" command="load" />
<rosparam file="$(find 2dnav_pr2)/move_base/global_costmap_params.yaml" command="load" />
<rosparam file="$(find 2dnav_pr2)/move_base/navfn_params.yaml" command="load" />
<rosparam file="$(find 2dnav_pr2)/move_base/base_local_planner_params.yaml" command="load" />
</node>
- 此片段启动move_base节点。 第一个包含的元素是重新映射。 Move_base旨在接收关于“odom”主题的里程计。 在pr2的情况下,odometry发布在pr2_base_odometry主题上,因此我们重新映射它。
- 设计提示:在不同情况下针对不同主题发布给定类型的信息时,使用主题重新映射。
- 接下来是一堆标签。 请注意,这些参数位于节点元素内(因为它们位于结尾的</ node>之前),因此它们将是私有参数。 例如,第一个将move_base / controller_frequency设置为10.0。
- 在元素之后,有一些元素。 这些以yaml读取参数数据,这是一种人类可读的格式,允许复杂的数据结构。 以下是第一个元素加载的costmap_common_params.yaml文件的一部分:
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
# 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}
- 我们看到yaml允许像vector这样的东西(用于足迹参数)。 它还允许将一些参数放入嵌套的命名空间中。 例如,base_scan_marking / sensor_frame设置为base_laser。 请注意,这些名称空间与yaml文件自己的名称空间相关,该名称空间由包含rosparam元素的ns属性声明为global_costmap。 反过来,由于该rosparam包含在node元素中,因此参数的完全限定名称为/ move_base / global_costmap / base_scan_marking / sensor_frame。
- move_base.xml中的下一行是:
<rosparam file="$(find 2dnav_pr2)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />
- 这实际上包含与之前的行完全相同的yaml文件。 它只是在不同的命名空间中(local_costmap命名空间用于轨迹控制器,而global_costmap命名空间影响全局导航规划器)。 这比重新键入所有值要好得多。
- 下一行是:
<rosparam file="$(find 2dnav_pr2)/move_base/local_costmap_params.yaml" command="load"/>
- 与以前的元素不同,此元素没有ns属性。 因此,yaml文件的命名空间是父命名空间/ move_base。 但是看一下yaml文件本身的前几行:
local_costmap:
#Independent settings for the local costmap
publish_voxel_map: true
global_frame: odom_combined
robot_base_frame: base_link
- 因此,我们看到参数毕竟位于/ move_base / local_costmap命名空间中。
- 设计提示:Yaml文件允许具有复杂类型的参数,参数的嵌套命名空间以及在多个位置重用相同的参数值
- 上述许多提示的动机是让在不同情况下重用启动文件更容易。 我们已经看到了一个例子,其中使用env替换arg可以允许在不改变任何启动文件的情况下修改行为。 但是,在某些情况下,这是不方便或不可能的。 我们来看看pr2_2dnav_gazebo包。 这包含2d导航应用程序的一个版本,但用于Gazebo模拟器。 对于导航,唯一改变的是实际上我们使用的Gazebo环境基于不同的静态映射,因此必须使用不同的参数加载map_server节点。 我们可以在这里使用另一个env替换。 但这需要用户设置一堆环境变量才能发布。 相反,2dnav gazebo包含自己的顶级启动文件,名为'2dnav-stack-amcl.launch',此处显示(为清楚起见略微修改):
<launch>
<include file="$(find pr2_alpha)/sim.machine" />
<include file="$(find 2dnav_pr2)/config/new_amcl_node.xml" />
<include file="$(find 2dnav_pr2)/config/base_odom_teleop.xml" />
<include file="$(find 2dnav_pr2)/config/lasers_and_filters.xml" />
<node name="map_server" pkg="map_server" type="map_server" args="$(find gazebo_worlds)/Media/materials/textures/map3.png 0.1" respawn="true" machine="c1" />
<include file="$(find 2dnav_pr2)/config/ground_plane.xml" />
<!-- The naviagtion stack and associated parameters -->
<include file="$(find 2dnav_pr2)/move_base/move_base.xml" />
</launch>
- 第一个区别是,因为我们知道我们在模拟器上,所以我们只使用sim.machine文件而不是使用替换参数。 接下来,这一行
<include file="$(find 2dnav_pr2)/config/map_server.xml" />
- 已被下面这些取代
<node name="map_server" pkg="map_server" type="map_server" args="$(find gazebo_worlds)/Media/materials/textures/map3.png 0.1" respawn="true" machine="c1" />
- 第一种情况下包含的文件只包含一个节点声明,就像第二种情况,但使用不同的映射文件。
- 设计提示:要修改应用程序的“顶级”方面,请复制顶级启动文件并更改所需的部分。
- 上述技术有时会变得不方便。 假设我们要使用2dnav_pr2,只需将本地costmap的resolution参数更改为0.5即可。 我们可以在本地更改local_costmap_params.yaml。 这对于临时修改来说是最简单的,但这意味着我们无法再检查修改后的文件。我们可以复制local_costmap_params.yaml并对其进行修改。 然后我们必须更改move_base.xml以包含修改后的yaml文件。 然后我们必须更改2dnav_pr2.launch以包含修改后的move_base.xml。 这可能非常耗时,如果使用版本控制,我们将不再看到原始文件的更改。 另一种方法是重新构建启动文件,以便在顶级文件2dnav_pr2.launch中定义move_base / local_costmap / resolution参数,并生成该文件的修改版本。 如果我们事先知道哪些参数可能会改变,这是一个很好的选择。
- 另一个选择是使用roslaunch的覆盖行为:参数按顺序设置(处理包含后)。 因此,我们可以创建一个覆盖原始解决方案的更高级别文件:
<launch>
<include file="$(find 2dnav_pr2)/move_base/2dnav_pr2.launch" />
<param name="move_base/local_costmap/resolution" value="0.5"/>
</launch>
- 主要缺点是这种方法可能使事情变得更难理解:知道roslaunch为参数设置的实际值需要跟踪包含roslaunch文件。 但它确实避免了必须复制多个文件。
设计提示:要修改无法更改的启动文件树中的深层嵌套参数,请使用roslaunch的参数覆盖语义。
- 从CTurtle开始,roslaunch具有参数替换功能以及允许根据参数值调整启动文件部分的标签。 这可以是比上面的参数覆盖机制或启动文件重用技术更加通用和清晰的结构化方法,代价是必须修改原始启动文件以指定可更改的参数是什么。 请参阅roslaunch XML文档。
- 设计提示:如果您可以修改原始启动文件,通常最好使用roslaunch参数而不是参数覆盖或复制roslaunch文件。
- 原文链接 翻译:谷歌翻译、文岩
Stay hungry,stay foolish.