过滤噪声引起的障碍物

../../_images/title.png

概述

嘈杂的传感器测量可能导致``Voxel Layer``或``Obstacle Layer``中的错误。结果是,在代价地图上可能出现椒盐噪声。这种噪声会产生虚假障碍物,阻止机器人在地图上找到最佳路径。尽管上面的图像显示了盐和胡椒噪声以及由于错误定位引起的错误,但此图层仅会去除传感器噪声,而不会去除与静态地图不对齐的错误定位伪影。本教程展示了如何配置用于过滤由噪声引起的虚假障碍物。这个功能由``DenoiseLayer``代价地图图层插件提供,将在本文档中启用和使用。

要求

假设已安装或本地构建了ROS 2、Gazebo和TurtleBot3软件包。请确保Nav2项目也在本地构建,就像在:ref:`build-instructions`中所述一样。

教程步骤

1. 启用去噪图层

Denoise Layer是Costmap2D插件。您可以通过在``nav2_params.yaml``的``plugins``参数中添加``denoise_layer``来启用``DenoiseLayer``插件。您可以将其放置在``global_costmap``和/或``local_costmap``中以在全局或局部地图上过滤噪声。DenoiseLayer插件应定义以下参数:

  • plugin: 插件类型。在我们的情况下是 nav2_costmap_2d::DenoiseLayer

支持``DenoiseLayer``的完整参数列表列在 降噪层参数 页面上。

值得注意的是,通常应将 DenoiseLayer 放在膨胀图层之前。这是为了防止噪声引起的障碍物膨胀。此外,DenoiseLayer 仅处理代价地图中的障碍物信息。INSCRIBED_INFLATED_OBSTACLELETHAL_OBSTACLE``和可选的``NO_INFORMATION``将被解释为障碍物单元。在处理时,具有其他任何值的单元将被解释为``FREE_SPACE``(在代价地图中不会失真)。如果将障碍物单元识别为噪声,则在处理后将其替换为``FREE_SPACE

要为全局和局部代价地图同时启用``DenoiseLayer``,请使用以下配置:

global_costmap:
  global_costmap:
    ros__parameters:
      ...
      plugins: ["static_layer", "obstacle_layer", "denoise_layer", "inflation_layer"]
      ...
      denoise_layer:
        plugin: "nav2_costmap_2d::DenoiseLayer"
        enabled: True
...
local_costmap:
  local_costmap:
    ros__parameters:
      ...
      plugins: ["voxel_layer", "denoise_layer", inflation_layer"]
      ...
      keepout_filter:
        plugin: "nav2_costmap_2d::DenoiseLayer"
        enabled: True

注解

在过滤噪声方面成功的关键是了解噪声的类型并选择正确的“DenoiseLayer”参数。默认参数专注于快速移除独立的障碍物。更正式地说,如果相邻的八个单元中没有障碍物,则会丢弃一个障碍物。这在典型情况下应该足够了。

如果某个传感器生成了相互关联的噪声引起的障碍物,并且世界上的小障碍物不太可能存在,可以删除小组障碍物。要配置``DenoiseLayer``以处理此类情况并了解其工作原理,请参阅``How it works``部分。

警告

谨慎使用此插件来过滤全局costmap,因为它可能引入潜在的性能问题。例如,在通常具有较高范围的激光雷达(20米以上)的情况下,更新窗口可能非常大,导致处理时间不可接受地长。作为应用程序设计者,考虑到这一点是值得的。

2. 运行Nav2 stack

在为全局/局部costmap启用Denoise Layer后,按照:ref:`getting_started`中的指示运行Nav2 stack:

ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False

并检查过滤器是否正常工作:使用默认参数,在代价地图上不应保留任何独立的障碍物。例如,可以在移除不必要的粒子后,在RViz主窗口中显示局部和全局代价地图来进行检查(如本教程顶部所示)。

工作原理

该插件基于两种算法。

当参数“minimal_group_size”= 2时,第一个算法启动。它将`erosion <https://docs.opencv.org/3.4/db/df6/tutorial_erosion_dilatation.html>`_ 函数应用于下方图片中的内核(如果“group_connectivity_type”= 4,则使用左侧内核;如果“group_connectivity_type”= 8,则使用右侧内核),作用于costmap上。内核像素的白色表示使用该值,黑色表示忽略该值。

../../_images/3x3_kernels.png

通过腐蚀函数生成了邻居图像。内核在代价图上的每个可能位置对应于邻居图像的一个像素。该图像的像素值等于与掩码的白色像素对应的4/8个代价图像素的最大值。换句话说,如果附近有障碍物,则邻居图像的像素等于障碍物代码;否则为自由空间代码。然后,将邻居图像上对应自由空间代码的障碍物移除。

该过程如下图所示。图像的左侧是costmap,右侧是邻居图像。白色像素表示自由空间,黑色像素表示障碍物,“group_connectivity_type”= 4。动画结束时标记的障碍物将被移除。

../../_images/dilate.gif

当参数``minimal_group_size`` > 2时,执行第二种算法。这是一种通用解决方案,可以删除邻近障碍物组,如果它们的总数小于``minimal_group_size``。为了选择相邻障碍物组,算法执行它们的分割。一个段中的细胞连接类型由参数``group_connectivity_type``确定。然后计算每个段的大小。大小小于``minimal_group_size``的障碍物段将被替换为空细胞。该算法比第一种算法慢大约10倍,因此请谨慎使用,仅在必要时使用。它的执行时间取决于处理地图片段的大小(而不取决于``minimal_group_size``的值)。

该算法如下动画所示(“group_connectivity_type”= 8)。动画结束时标记的障碍物将被移除(大小小于3的组)。

../../_images/connected_components.gif