ros学习笔记.4 Path Planning Part 2 (避障)

  • 避障是如何工作的
  • 什么是局部规划器?
  • 什么是局部成本图?
  • 路径规划回顾
  • 如何使用动态重新配置和其他 Rviz 工具

局部规划器

一旦全局规划器计算出要遵循的路径,该路径就会发送给局部规划器。然后,局部规划器将执行全局规划的每个部分(让我们将局部规划想象为全局规划的一小部分)。因此,给定要遵循的规划(由全局规划器提供)和地图,局部规划器将提供速度命令以移动机器人。

与全局规划器不同,局部规划器监视里程表和激光数据,并为机器人选择无碰撞的局部规划(让我们将局部规划想象为全局规划的一小部分)。因此,局部规划器可以动态重新计算机器人的路径,以防止机器人撞到物体,但仍允许它到达目的地。

一旦计算出局部规划,它将发布到名为 /local_plan 的主题中。局部规划器还会将其尝试遵循的全局规划部分发布到主题 /global_plan 中。让我们做一个练习,以便你更好地理解这一点。

练习

a) 打开 Rviz 并添加显示,以便能够可视化局部规划器的 /global_plan 和 /local_plan 主题。

b) 向机器人发送目标姿势并可视化两个主题。

和全局规划器一样,还有不同类型的局部规划器。根据您的设置(您使用的机器人、它导航的环境等)和您想要的性能类型,您将使用其中一种或另一种。让我们来看看最重要的几种。

base_local_planner

base_local_planner提供了轨迹展开(Trajectory Rollout)和动态窗口方法(Dynamic Window Approach, DWA)算法的实现,用于计算和执行机器人的全局路径规划。

总结一下,这些算法的基本工作原理如下:

  1. 从机器人的控制空间中离散地采样。
  2. 对于每个采样的速度,从机器人的当前状态开始执行前向模拟,以预测应用该速度后的情况。
  3. 评估每条前向模拟得到的轨迹。
  4. 丢弃不合法的轨迹。
  5. 选择得分最高的轨迹,并将相关的速度发送到移动底盘。
  6. 重复以上步骤。

DWA 轨迹展开的不同之处在于机器人的空间采样方式。轨迹展开从整个前向模拟期间内可实现的速度集合中进行采样,考虑了机器人的加速度限制;而 DWA 则仅在一个模拟步骤中从可实现的速度集合中进行采样,考虑了机器人的加速度限制。

由于 DWA 采样的空间较小,因此它是一种更高效的算法,但对于加速度限制较低的机器人,DWA 可能不如轨迹展开表现得好,因为 DWA 不会对恒定加速度进行前向模拟。在实际应用中,DWA 和轨迹展开的性能类似,因此推荐使用 DWA 以获得效率上的优势。

base_local_planner的 DWA 算法在一个新的局部规划器中得到了改进,这就是我们接下来要介绍的 DWA local planner。

dwa_local_planner

DWA local planner 提供了动态窗口方法(Dynamic Window Approach, DWA)算法的实现。它基本上是对基础局部规划器的 DWA 选项的重写,但代码更简洁、更易于理解,特别是在轨迹模拟方面。

因此,对于使用 DWA 方法进行局部规划的应用,dwa_local_planner 可能是最佳选择。这是最常用的选项。

eband_local_planner

eband_local_planner实现了弹性带(Elastic Band)方法,以计算要跟随的局部路径。

teb_local_planner

teb 局部规划器实现了有时间弹性带(Timed Elastic Band)方法,以计算要跟随的局部路径。

修改Local Planner

move_base 节点使用的局部规划器在 base_local_planner 参数中指定。可以在参数文件中设置,如下例所示:

base_local_planner: "base_local_planner/TrajectoryPlannerROS" # Sets the Trajectory Rollout algorithm from base local                                                                 planner

base_local_planner: "dwa_local_planner/DWAPlannerROS" # Sets the dwa local planner

base_local_planner: "eband_local_planner/EBandPlannerROS" # Sets the eband local planner

base_local_planner: "teb_local_planner/TebLocalPlannerROS" # Sets the teb local planner

或者可以直接在启动文件中设置,就像我们的例子一样:

<arg name="base_local_planner" default="dwa_local_planner/DWAPlannerROS"/>

正如您所期望的,每个局部规划器也有自己的参数。这些参数将根据您使用的局部规划器而有所不同。在本课程中,我们将重点介绍 DWA 局部规划器参数,因为它是最常见的选择。无论如何,如果您想检查其他局部规划器的具体参数,您可以在此处查看:

base_local_planner: base_local_planner - ROS Wiki

eband_local_planner: eband_local_planner - ROS Wiki

teb_local_planner: teb_local_planner - ROS Wiki

DWAPlannerROS 参数

如果您检查包 my_move_base_launcher 中的文件 my_move_base_params.yaml,您将看到 DWAPlannerROS 规划器的参数:

DWAPlannerROS:
  # Robot configuration parameters  
  acc_lim_x: 2.5
  acc_lim_y: 0
  acc_lim_th: 3.2

  max_vel_x: 0.5
  min_vel_x: 0.0
  max_vel_y: 0
  min_vel_y: 0

  max_vel_trans: 0.5
  min_vel_trans: 0.1
  max_vel_theta: 1.0
  min_vel_theta: 0.2

  # Goal Tolerance Parameters
  yaw_goal_tolerance: 0.1
  xy_goal_tolerance: 0.2
  latch_xy_goal_tolerance: false

DWA 局部规划器最重要的参数如下:

机器人配置参数

  • /acc_lim_x(默认值:2.5):机器人在 x 方向上的加速度限制,单位是米/秒²。
  • /acc_lim_th(默认值:3.2):机器人在旋转方向上的加速度限制,单位是弧度/秒²。
  • /max_vel_trans(默认值:0.55):机器人最大平移速度的绝对值,单位是米/秒。
  • /min_vel_trans(默认值:0.1):机器人最小平移速度的绝对值,单位是米/秒。
  • /max_vel_x(默认值:0.55):机器人最大 x 方向速度,单位是米/秒。
  • /min_vel_x(默认值:0.0):机器人最小 x 方向速度,单位是米/秒,负值表示向后运动。
  • /max_vel_theta(默认值:1.0):机器人最大旋转速度的绝对值,单位是弧度/秒。
  • /min_vel_theta(默认值:0.4):机器人最小旋转速度的绝对值,单位是弧度/秒。

目标容差参数

  • /yaw_goal_tolerance(双精度,默认值:0.05):控制器在达到目标时在偏航/旋转方向上的容差,单位是弧度。
  • /xy_goal_tolerance(双精度,默认值:0.10):控制器在达到目标时在 x 和 y 方向上的容差,单位是米。
  • /latch_xy_goal_tolerance(布尔值,默认值:false):如果目标容差被锁定,当机器人达到目标 xy 位置时,它会原地旋转,即使在旋转过程中最终位置超出了目标容差范围。

练习

a) 打开上一章中创建的 my_move_base_params.yaml 文件进行编辑。

b) 修改 DWAPlannerROS 的 xy_goal_tolerance 参数并将其设置为更高的值。

c) 检查是否注意到性能有任何差异。

High XY tolerance:

 Low XY tolerance:

正如您在练习中看到的,您在参数文件中设置的目标容差越高,机器人设定的目标就越不准确。

正如您在参数文件中看到的,还有其他参数被注释(因此规划器采用它们的默认值):

# 前向模拟参数
# sim_time: 2.0  # 模拟时间(秒)
# sim_granularity: 0.02  # 模拟粒度(秒)
# vx_samples: 6  # x方向速度样本数
# vy_samples: 0  # y方向速度样本数
# vtheta_samples: 20  # 旋转速度样本数
# penalize_negative_x: true  # 是否惩罚负x方向速度

# 轨迹评分参数
# path_distance_bias: 32.0  # 控制器保持接近给定路径的权重
# goal_distance_bias: 24.0  # 控制器试图到达局部目标的权重,也控制速度
# occdist_scale: 0.01  # 控制器尝试避免障碍物的权重
# forward_point_distance: 0.325  # 从机器人中心点到额外评分点的距离(米)
# stop_time_buffer: 0.2  # 为了使轨迹被认为有效,机器人在碰撞前必须停止的时间(秒)
# scaling_speed: 0.25  # 机器人足迹开始缩放的速度绝对值(米/秒)
# max_scaling_factor: 0.2  # 机器人足迹的最大缩放因子

# 振荡防止参数
# oscillation_reset_dist: 0.25  # 机器人在振荡标志重置之前必须行驶的距离(米)(默认值: 0.05)

练习:

a) 修改局部规划器参数文件中的 sim_time 参数并将其设置为 4.0。

b) 检查是否注意到局部规划器的性能或可视化方面有任何差异。

Regular sim_time (local plan in blue):

 High sim_time (local plan in blue):

从上面的练习中可以看出,sim_time 参数越高,计算出的局部规划就越长。但是,请记住,这也会增加使用的计算资源。

轨迹评分参数

  • /path_distance_bias(默认值:32.0):控制器应保持接近给定路径的权重
  • /goal_distance_bias(默认值:24.0):控制器应尝试达到其局部目标的权重;还控制速度
  • /occdist_scale(默认值:0.01):控制器应尝试避开障碍物的权重

这里有一个 dwa_local_planner_params.yaml 的示例:

# 轨迹评分参数
# path_distance_bias: 32.0  # 控制器保持接近给定路径的权重
# goal_distance_bias: 24.0  # 控制器试图到达局部目标的权重,也控制速度
# occdist_scale: 0.01  # 控制器尝试避免障碍物的权重
# forward_point_distance: 0.325  # 从机器人中心点到额外评分点的距离(米)
# stop_time_buffer: 0.2  # 机器人在碰撞前必须停止的时间,以使轨迹被认为有效(秒)
# scaling_speed: 0.25  # 机器人足迹开始缩放的速度绝对值(米/秒)
# max_scaling_factor: 0.2  # 机器人足迹的最大缩放因子

尝试自己修改这些参数,看看它们如何影响规划过程。例如,您可以尝试更改局部规划器参数文件中的 path_distance_bias 参数。

path_distance_bias 参数用于控制机器人在轨迹追踪时保持接近给定路径的权重。修改这个参数会对机器人行为产生以下影响:

  1. 增加 path_distance_bias 的值:

    • 机器人会更紧密地跟随路径。 增大的权重使得控制器更加重视保持机器人在给定路径上的位置,导致机器人更努力地纠正偏离路径的情况。
    • 可能导致路径跟随过度。 如果 path_distance_bias 设得过高,机器人可能会变得过于固执,紧紧依赖路径,可能会导致在复杂或狭窄环境中的表现变差,甚至导致在障碍物附近出现困难。
  2. 减少 path_distance_bias 的值:

    • 机器人会更灵活。 较低的权重使得控制器对路径的依赖性降低,机器人可能会有更多的自由度来进行调整和优化其他目标,例如到达目标点或避开障碍物。
    • 可能导致路径偏离。 如果这个权重设置得过低,机器人可能不会很好地遵循给定路径,从而导致偏离预定路径,尤其是在路径弯曲或变化较大的情况下。

在全局规划器部分,我们已经向您介绍了代价地图,重点介绍了全局代价地图。现在是时候谈谈局部代价地图了。

Local Costmap

您需要知道的第一件事是,局部规划器使用局部代价地图来计算局部规划。

与全局代价地图不同,局部代价地图直接根据机器人的传感器读数创建。给定代价地图的宽度和高度(由用户定义),它会在机器人在整个环境中移动时将机器人保持在代价地图的中心,并在机器人移动时从地图中删除障碍物信息。

让我们做一个练习,以便您更好地了解局部代价地图的外观,以及如何区分局部代价地图和全局代价地图。

练习

a) 打开 Rviz 并添加适当的显示,以便可视化全局和局部成本地图。

b) 执行以下命令以在房间中生成障碍物。

检查您的工作区中是否已经有 object.urdf 文件。如果您还没有,您需要执行以下命令将其移动到您的工作区。

cp /home/simulations/public_sim_ws/src/all/turtlebot/turtlebot_navigation_gazebo/urdf/object.urdf /home/user/catkin_ws/src

 创建对象

rosrun gazebo_ros spawn_model -file /home/user/catkin_ws/src/object.urdf -urdf -x 0 -y 0 -z 1 -model my_object

c) 启动键盘操作并靠近生成的物体。

roslaunch husky_launch keyboard_teleop.launch

d) 检查全局和局部Costmaps之间的差异。

Husky 面对生成的障碍物:

 全局成本地图(未出现障碍物):

 局部成本地图(障碍物出现):

因此,正如您在上一个练习中看到的,局部代价地图确实会检测模拟中出现的新对象,而全局代价地图则不会。

您可能已经推断出这种情况,因为全局代价地图是从静态地图文件创建的。这意味着即使环境发生变化,代价地图也不会改变。相反,局部代价地图是根据机器人的传感器读数创建的,因此它将始终使用来自传感器的新读数进行更新。

由于全局代价地图和局部代价地图的行为不同,因此参数文件也必须不同。让我们来看看我们需要为局部代价地图设置的最重要的参数。

Local Costmap 参数

你需要了解的参数如下:

  • global_frame: 成本地图操作的全局坐标系。在局部成本地图中,此参数必须设置为 "/odom"。
  • robot_base_frame: 机器人基座链接的坐标系名称。
  • rolling_window: 是否使用滚动窗口版本的成本地图。如果 static_map 参数设置为 true,则此参数必须设置为 false。在局部成本地图中,此参数必须设置为 true
  • update_frequency(默认值: 5.0): 更新地图的频率,以赫兹(Hz)为单位。
  • width(默认值: 10): 成本地图的宽度。
  • height(默认值: 10): 成本地图的高度。
  • plugins: 插件规范的序列,每个层一个。每个规范是一个包含 nametype 字段的字典。name 用于定义插件的参数命名空间。

正如你所看到的,这些参数与全局成本地图的参数相同。然而,我们增加了一些在处理局部成本地图时很有用的参数:widthheight。我们还增加了 update_frequency,稍后将在本单元中讨论。

现在,让我们做一个简单的练习来测试 widthheight 参数。

练习

a) 将一个名为 my_local_costmap_params.yaml 的文件添加到您在练习my_move_base_launcher中创建的包的 params目录中。

b) 将 husky_navigation 包的 costmap_local.yaml 文件的内容复制到此文件中。

global_frame: odom
rolling_window: true

plugins:
  - {name: obstacles_laser,           type: "costmap_2d::ObstacleLayer"}
  - {name: inflation,                 type: "costmap_2d::InflationLayer"}

c) 修改您在练习my_move_base_launcher中创建的 my_move_base.launch 文件,以便它加载您刚刚创建的局部 costmap 参数文件。

<?xml version="1.0"?>
<launch>

  <!-- Run the map server -->
  <arg name="map_file" default="$(find husky_navigation)/maps/my_map.yaml"/>
  <node name="map_server" pkg="map_server" type="map_server" args="$(arg map_file)" />

  <!--- Run AMCL -->
  <include file="$(find husky_navigation)/launch/amcl.launch" />

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

  <arg name="base_global_planner" default="navfn/NavfnROS"/>
  <arg name="base_local_planner" default="dwa_local_planner/DWAPlannerROS"/>
  <!-- <arg name="base_local_planner" default="base_local_planner/TrajectoryPlannerROS"/> -->

  <node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">

    <param name="base_global_planner" value="$(arg base_global_planner)"/>
    <param name="base_local_planner" value="$(arg base_local_planner)"/>  
    <rosparam file="$(find my_move_base_launcher)/params/my_move_base_params.yaml" command="load"/>

    <!-- observation sources located in costmap_common.yaml -->
    <rosparam file="$(find husky_navigation)/config/costmap_common.yaml" command="load" ns="global_costmap" />
    <rosparam file="$(find husky_navigation)/config/costmap_common.yaml" command="load" ns="local_costmap" />

    <!-- local costmap, needs size -->
    <rosparam file="$(find my_move_base_launcher)/params/my_local_costmap_params.yaml" command="load" ns="local_costmap" />
    <param name="local_costmap/width" value="10.0"/> # Change to 5 for the Exercise
    <param name="local_costmap/height" value="10.0"/> # Change to 5 for the Exercise

    <!-- static global costmap, static map provides size -->
    <rosparam file="$(find my_move_base_launcher)/params/my_global_costmap_params.yaml" command="load" ns="global_costmap" unless="$(arg no_static_map)"/>
    
    <!-- global costmap with laser, for odom_navigation_demo -->
    <rosparam file="$(find husky_navigation)/config/costmap_global_laser.yaml" command="load" ns="global_costmap" if="$(arg no_static_map)"/>
    <param name="global_costmap/width" value="100.0" if="$(arg no_static_map)"/>
    <param name="global_costmap/height" value="100.0" if="$(arg no_static_map)"/>
  </node>

</launch>

d) 启动 Rviz 并再次可视化局部costmap。可视化地图和 costmap 模式。

e) 修改宽度和高度参数并将其设置为 5。再次可视化 costmap。

10x10 costmap (map view):

 10x10 costmap (costmap view):

5x5 costmap (map view):

5x5 costmap (costmap view):

正如您在上一个练习中所看到的,为代价地图设置正确的宽度和高度非常重要。根据您想要导航的环境,您将设置一个或另一个值以正确显示障碍物。

让我们继续使用局部代价地图参数。尽管参数与全局代价地图相同,但它们的设置值并不相同。

例如,对于局部代价地图,rolling_window 参数将设置为 true。这样,我们表明我们不希望代价地图从静态地图初始化(就像我们对全局代价地图所做的那样),而是从机器人的传感器读数构建。

此外,由于我们没有任何静态地图,因此需要将 global_frame 参数设置为 odom。

您可以在上一个练习中创建的 my_local_costmap_params.yaml 文件中看到这一点:

global_frame: odom
rolling_window: true

正如我们在全局成本地图中看到的,局部成本地图也可以添加层。在局部成本地图的情况下,通常会添加以下两层:

  • costmap_2d::ObstacleLayer: 用于避障。
  • costmap_2d::InflationLayer: 用于对障碍物进行膨胀处理。

因此,你最终会得到如下结果:

plugins:
    - {name: obstacle_layer,      type: "costmap_2d::ObstacleLayer"}
    - {name: inflation_layer,     type: "costmap_2d::InflationLayer"}

**障碍层**对**局部代价地图**和**全局代价地图**使用不同的插件。对于局部代价地图,它使用**costmap_2d::ObstacleLayer**,而对于全局代价地图,它使用**costmap_2d::VoxelLayer**。这非常重要,因为在导航中,对障碍层使用错误的插件是一个常见错误。

让我们添加最后一个参数!正如您通过练习已经看到的,局部成本地图会不断更新自身。这些更新周期以 update_frequency 参数指定的速率进行。每个周期的工作方式如下:

  1. 传感器数据进入。
  2. 执行标记和清除操作。
  3. 为每个单元格分配适当的成本值。
  4. 对每个有障碍物的单元格执行障碍物膨胀。这包括将成本值从每个占用的单元格向外传播到指定的膨胀半径。

练习

a) 在 local costmap  参数文件中,将地图的 update_frequency 参数改得更慢。

b) 再次重复上面练习,看看现在会发生什么。

代价地图中的对象的生成稍有延迟。

现在,您可能想知道……上面提到的标记和清除操作是什么?

正如您已经知道的,代价地图会自动订阅传感器主题,并根据从传感器收到的数据进行自我更新。每个传感器都用于标记(将障碍物信息插入代价地图)、清除(从代价地图中删除障碍物信息)或两者兼而有之。

标记操作只是数组中的索引,用于更改单元格的成本。

但是,清除操作包括从传感器原点向外通过网格进行光线追踪,以报告每个观察结果。

标记和清除操作可以在障碍层中定义。

此时,我们几乎可以说您已经知道如何配置全局和本地代价地图。但如果您还记得的话,还有一个我们尚未讨论的参数文件。那就是通用代价地图参数文件。这些参数将影响全局和本地代价地图。

Common Costmap Parameters

基本上,您需要在这个文件中设置的最重要参数如下:

  • footprint: 足迹是移动底盘的轮廓。在 ROS 中,它表示为一个二维数组,例如 [x0, y0], [x1, y1], [x2, y2], ...。这个足迹将用于计算内接圆和外接圆的半径,这些圆用于以适合该机器人的方式膨胀障碍物。通常,为了安全起见,我们希望足迹略大于机器人的实际轮廓。
  • robot_radius: 如果机器人是圆形的,我们将指定此参数而不是足迹。
  • layers parameters: 在这里我们将定义每一层的参数。每一层都有其自己的参数。

障碍层

障碍层负责标记和清除操作。

如您所知,成本地图会自动订阅传感器主题,并根据接收到的数据进行更新。每个传感器用于标记(将障碍物信息插入成本地图)、清除(从成本地图中移除障碍物信息),或两者兼具。

  • 标记操作:只是通过数组索引来改变一个单元格的成本。
  • 清除操作:则是通过从传感器原点向外进行网格射线追踪,处理每个报告的观察数据。

标记和清除操作可以在障碍层中定义。

为了配置障碍层,我们首先需要为层设置名称,然后设置 observation_sources 参数。

  • observation_sourcesdefault: ""):由空格分隔的观察源名称列表。这定义了以下各个 source_name 命名空间。

您可以在 husky_navigation 包中的 costmap_common.yaml 文件中查看其操作方式:

obstacles_laser: # Name of the layer
    observation_sources: laser # We define 1 observation_source named laser

现在我们可以为这个 observation_source 定义具体的参数。observation_sources 中的每个 source_name 定义了一个命名空间,其中可以设置参数:

  • /source_name/topic(默认值: source_name):传感器数据接收的主题。默认为源名称。
  • /source_name/data_type(默认值: "PointCloud"):与主题相关联的数据类型,目前只支持 "PointCloud"、"PointCloud2" 和 "LaserScan"。
  • /source_name/clearing(默认值: false):是否使用该观察源来清除空闲空间。
  • /source_name/marking(默认值: true):是否使用该观察源来标记障碍物。
  • /source_name/inf_is_valid(默认值: false):允许在 "LaserScan" 观察消息中存在 Inf 值。Inf 值将被转换为激光的最大范围。
  • /source_name/max_obstacle_height(默认值: 2.0):要插入到成本地图中的障碍物的最大高度(米)。该参数应设置为比机器人的实际高度稍高。
  • /source_name/obstacle_range(默认值: 2.5):障碍物将在成本地图中插入的默认最大距离(米)。此设置可以按传感器进行覆盖。
  • /source_name/raytrace_range(默认值: 3.0):使用传感器数据从地图中射线追踪障碍物的默认范围(米)。此设置可以按传感器进行覆盖。

您可以在 husky_navigation 包中的 costmap_common.yaml 文件中查看其完成方式:

laser: {data_type: LaserScan, clearing: true, marking: true, topic: scan, inf_is_valid: true, obstacle_range: 5.5}

正如您在示例中看到的,我们仅声明 1 个 observer_source,用于激光。这意味着我们的 Husky 机器人将根据从其激光接收到的数据构建其本地成本地图。

练习:

a) 将名为 my_common_costmap_params.yaml 的文件添加到您在练习my_move_base_launcher中创建的包的 params 目录中。

b) 将 husky_navigation 包的 costmap_common.yaml 文件的内容复制到这个新文件中。

# 机器人足迹定义,表示机器人在代价图上的占用区域
footprint: [[-0.5, -0.33], [-0.5, 0.33], [0.5, 0.33], [0.5, -0.33]]

# 足迹填充值,用于确保代价图上机器人的边缘有一定的缓冲
footprint_padding: 0.01

# 机器人基础坐标系
robot_base_frame: base_link

# 更新代价图的频率(Hz)
update_frequency: 4.0

# 发布代价图的频率(Hz)
publish_frequency: 3.0

# 坐标变换的容忍度(秒),用于处理传感器数据和代价图之间的坐标变换
transform_tolerance: 0.5

# 代价图的分辨率(米/像素),定义每个像素在实际世界中的大小
resolution: 0.05

# 层定义部分
# 静态地图层
static:
    # 静态地图的主题名称
    map_topic: /map
    # 是否订阅地图更新
    subscribe_to_updates: true

# 障碍物激光层
obstacles_laser:
    # 观测源,表示激光数据的来源
    observation_sources: laser
    # 激光数据的配置
    laser: {
        data_type: LaserScan,          # 数据类型
        clearing: true,                # 是否清除已探测到的障碍物
        marking: true,                 # 是否标记障碍物
        topic: scan,                   # 激光数据的主题
        inf_is_valid: true,            # 是否将无穷大距离视为有效
        obstacle_range: 5.5            # 识别障碍物的最大范围(米)
    }

# 膨胀层
inflation:
    # 膨胀半径(米),用于扩展障碍物区域以考虑机器人周围的安全距离
    inflation_radius: 1.0

a) 现在,修改 obstacle_range 参数并将其设置为 1。

b) 将机器人移近障碍物并观察会发生什么。

Inflation Layer

Inflation Layer负责对每个包含障碍物的单元格进行膨胀处理。

  • inflation_radius(默认值: 0.55):用于膨胀障碍物成本值的半径(米)。
  • cost_scaling_factor(默认值: 10.0):在膨胀过程中应用于成本值的缩放因子。

练习

a) 现在,修改 costmap 的 inflation_radius 参数,使其速度更慢。

b) 靠近某个物体,检查差异。

inflation_radius 参数用于控制障碍物周围的膨胀区域的大小,这有助于为机器人提供一个更加保守的避障区域。

Low inflation:

High inflation:

Static Layer

静态层负责向需要它的代价地图(全局代价地图)提供静态地图。

map_topic(string,默认值:“map”):代价地图为静态地图订阅的主题。

Rotate Recovery

基本上,旋转恢复行为是一种简单的恢复行为,通过将机器人旋转 360 度来尝试清理空间。这样,机器人可能能够找到一条没有障碍物的路径继续导航。

它有一些可以自定义的参数,以便改变或改善其行为:

旋转恢复参数
  • /sim_granularity (默认值: 0.017): 在检查是否可以安全地进行原地旋转时,检查障碍物的距离,单位是弧度。默认为 1 度。
  • /frequency (默认值: 20.0): 向移动底盘发送速度命令的频率,单位是赫兹 (HZ)。
其他参数
  • /yaw_goal_tolerance (double, 默认值: 0.05): 控制器在实现目标时在偏航/旋转方向上的容差,单位是弧度。
  • /acc_lim_th (double, 默认值: 3.2): 机器人的旋转加速度限制,单位是弧度/秒²。
  • /max_rotational_vel (double, 默认值: 1.0): 底盘允许的最大旋转速度,单位是弧度/秒。
  • /min_in_place_rotational_vel (double, 默认值: 0.4): 执行原地旋转时底盘允许的最小旋转速度,单位是弧度/秒。

这些参数也可以在 move_base 参数文件中设置。

Clear Costmap

清除成本图恢复是一种简单的恢复行为,通过清除机器人地图中指定区域之外的障碍物来清理空间。基本上,局部成本图会恢复到与全局成本图相同的状态。

让我们来进行一个简短的练习,以测试这些恢复行为。

练习

向无法到达的机器人发送导航目标,并检查是否触发恢复行为。

例如,您可以在模拟世界的厨房内设置一个目标。查看下图:

move base节点的输出:

旋转恢复行为:

move_base 节点还提供了一项服务,用于清除 costmap 中的障碍物。这项服务称为 /move_base/clear_cotmaps。

请记住,通过清除 costmap 中的障碍物,您将使这些障碍物对机器人不可见。因此,调用此服务时要小心,因为它可能会导致机器人开始撞到障碍物。

练习

a) 如果还没有,则将一个未出现在全局代价地图中的对象添加到场景中。

b) 移动机器人,使其在局部代价地图中检测到这个新障碍物。

c) 转动机器人,使其不再看到障碍物(激光束检测不到它)。

d) 通过 WebShell 调用 /clear_costmaps 服务,并检查发生了什么。

rosservice call /move_base/clear_costmaps "{}"

被激光检测到的物体,放置到局部Ccostmap中:

Husky转身,激光不再检测到物体,但它仍然出现在本地代价地图中。

调用/move_base/clear_costmaps服务后,对象从局部代价地图中清除:

Recap

恭喜你!到目前为止,你已经了解了本章涵盖的几乎所有重要内容。由于这是课程的最后一章,这意味着你距离完全掌握 ROS 导航系统已经非常接近了!

不过,你可能会对有关路径规划的大量信息感到不知所措。因此,我认为这是一个总结本章内容的好时机。让我们开始吧!

move_base 节点

move_base 节点基本上是协调所有路径规划系统的节点。它接收目标位姿作为输入,并输出必要的速度指令,以便将机器人从初始位姿移动到指定的目标位姿。为实现这一目标,move_base 节点管理一个完整的内部流程,其中包括以下几个部分:

  • 全局规划器
  • 局部规划器
  • 成本图
  • 恢复行为

全局规划器

move_base 节点收到新的目标时,它会立即将其发送给全局规划器。全局规划器随后将计算出一条安全的路径,供机器人到达指定的目标。全局规划器使用全局成本图数据来计算这条路径。

有不同类型的全局规划器。根据你的设置,你将使用不同的全局规划器。

局部规划器

一旦全局规划器为机器人计算出路径,它会将路径发送给局部规划器。局部规划器将执行这条路径,将其拆分为更小的(局部)部分。因此,给定一个要遵循的计划和地图,局部规划器将提供速度指令以移动机器人。局部规划器在局部成本图上操作。

有不同类型的局部规划器。根据你对性能的需求,你将选择不同的局部规划器。

成本图

成本图基本上是表示地图上哪些点对机器人是安全的,哪些点是不安全的地图。有 2 种类型的成本图:

  • 全局成本图
  • 局部成本图

基本上,它们之间的区别在于,全局成本图是使用先前构建的静态地图数据构建的,而局部成本图则是从机器人的传感器读取数据构建的。

恢复行为

恢复行为提供了在机器人卡住时的处理方法。导航栈提供了 2 种不同的恢复行为:

  • 旋转恢复
  • 清除成本图

配置

由于有许多不同的节点协同工作,因此可配置的参数数量也非常高。我认为总结一下需要设置的路径规划相关参数文件是一个好主意。你需要的参数文件如下:

<launch>
    <!-- Load move_base parameters -->
    <param file="$(find your_package)/config/move_base_params.yaml" command="load" />
    
    <!-- Load global planner parameters -->
    <param file="$(find your_package)/config/global_planner_params.yaml" command="load" />
    
    <!-- Load local planner parameters -->
    <param file="$(find your_package)/config/local_planner_params.yaml" command="load" />
    
    <!-- Load common costmap parameters -->
    <param file="$(find your_package)/config/common_costmap_params.yaml" command="load" />
    
    <!-- Load global costmap parameters -->
    <param file="$(find your_package)/config/global_costmap_params.yaml" command="load" />
    
    <!-- Load local costmap parameters -->
    <param file="$(find your_package)/config/local_costmap_params.yaml" command="load" />

    <!-- Start the move_base node -->
    <node pkg="move_base" type="move_base" name="move_base" />
</launch>

除了上述参数文件外,我们还需要一个启动文件,以便启动整个系统并加载不同的参数。

总结

总体而言,路径规划的流程如下:

在获取机器人的当前位置后,我们可以将目标位置发送到 move_base 节点。该节点将目标位置发送到全局规划器,全局规划器将计划出一条从当前位置到目标位置的路径。这个计划是基于全局成本图的,而全局成本图由地图服务器提供数据。

全局规划器将此路径发送给本地规划器,本地规划器执行全局计划的每个部分。本地规划器获取里程计和激光数据值,并找到一个没有碰撞的局部计划。局部规划器与本地成本图相关联,本地成本图可以监视机器人周围的障碍物。局部规划器生成速度指令并将其发送到底盘控制器。底盘控制器将这些指令转换为实际的机器人运动。

如果机器人卡住了,恢复行为节点(如清除成本图恢复或旋转恢复)将被调用。

现在一切都更有意义了,对吧?

所以,凭借你在本课程中获得的所有知识,你可以再次查看下面的图示,尝试理解其中涉及的所有不同元素。

动态重新配置

到目前为止,我们已经了解了如何通过在参数文件中修改参数来更改参数。但是,猜猜看……这不是更改参数的唯一方法!您还可以使用 rqt_reconfigure 工具更改动态参数。请按照以下步骤操作:

a) 运行下一个命令以打开 rqt_reconfigure 工具。

rosrun rqt_reconfigure rqt_reconfigure
  • 打开 move_base group。
  • 选择 DWAPlannerROS 节点。
  • 稍微调整一下以下 3 个参数:
    • path_distance_bias
    • goal_distance_bias
    • occdist_scale
  • 上述参数用于计算成本函数,用于对每条轨迹进行评分。更详细地说,它们定义了以下内容:
    • path_distance_bias:控制器应保持接近给定路径的权重。
    • goal_distance_bias:控制器应尝试达到其局部目标的权重,也控制速度。
    • occdist_scale:控制器应尝试避开障碍物的权重。
  • 打开 Rviz 并可视化全局和局部计划如何根据设置的值发生变化。

Rviz 中的其他有用可视化

到目前为止,我们已经了解了一些通过 Rviz 可视化 move_base 节点过程不同部分的方法。但是,还有一些可能值得了解:

Robot Footprint

它显示了机器人的足迹。

Current Goal

要显示导航堆栈试图实现的目标姿势,请添加姿势显示并将其主题设置为 /move_base_simple/goal。现在您将能够看到红色箭头所示的目标姿势。它可用于了解机器人的最终位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CZDXWX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值