过滤噪声引起的障碍物

传感器测量噪声可能导致体素层(Voxel Layer)或障碍物层(Obstacle Layer)出错,在代价地图上产生椒盐噪声。这些噪声会形成虚假障碍物,阻碍机器人找到最优路径。虽然上图同时展示了椒盐噪声和定位偏差(mislocalization)引起的问题,但本层仅移除传感器噪声,不处理与静态地图错位的定位偏差伪影。
本教程介绍如何配置对噪声引起的虚假障碍物的过滤功能。该功能由 DenoiseLayer 代价地图层插件提供,本文将讲解如何启用并使用该插件。
假设 ROS 2、Gazebo 和 TurtleBot3 软件包已安装或在本地构建完成,同时请确保 Nav2 项目也已在本地构建,具体方法参见构建说明(build-instructions)。
1. 启用 Denoise 层
Section titled “1. 启用 Denoise 层”Denoise Layer 是一个 Costmap2D 插件。在 nav2_params.yaml 的 plugins 参数中添加 denoise_layer,即可在 Costmap2D 中启用 DenoiseLayer 插件。可将其放入 global_costmap 和(或)local_costmap 中,分别过滤全局或局部代价地图上的噪声。DenoiseLayer 插件需要定义以下参数:
plugin:插件类型,本例中为nav2_costmap_2d::DenoiseLayer。
DenoiseLayer 支持的完整参数列表请参见 denoise 页面。
需要注意的是,DenoiseLayer 通常应位于膨胀层(inflation layer)之前,以避免对噪声引起的障碍物进行膨胀。此外,DenoiseLayer 只处理代价地图中的障碍物信息:INSCRIBED_INFLATED_OBSTACLE、LETHAL_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"] ... denoise_layer: plugin: "nav2_costmap_2d::DenoiseLayer" enabled: True注意:成功过滤噪声的关键在于理解噪声类型并选择合适的
DenoiseLayer参数。默认参数侧重于快速移除孤立的障碍物。严格来说,如果某个障碍物的相邻八个栅格中没有任何障碍物,该障碍物就会被丢弃。在典型场景下这已经足够。如果某些传感器会产生相互关联的噪声障碍物,且环境中不太可能存在小障碍物,则可以移除小规模的障碍物群。要为这类场景配置
DenoiseLayer并理解其工作原理,请参阅工作原理一节。
警告:请谨慎使用此插件过滤全局代价地图,因为它可能带来性能问题。例如,对于典型远距离激光雷达(20 米以上),更新窗口可能非常大,导致处理时间长得无法接受。作为应用设计者,应将此因素纳入考虑。
2. 运行 Nav2 技术栈
Section titled “2. 运行 Nav2 技术栈”在为全局/局部代价地图启用 Denoise Layer 后,按照快速入门中的说明运行 Nav2 程序栈:
ros2 launch nav2_bringup tb3_simulation_launch.py headless:=False然后检查过滤器是否正常工作:在默认参数下,代价地图上不应残留孤立障碍物。可在 RViz 主窗口中查看——移除多余噪点后,局部和全局代价地图应如本教程顶部所示。
该插件基于两种算法。
当参数 minimal_group_size = 2 时,启用第一种算法。该算法将腐蚀(erosion)函数应用于代价地图,使用的核如下图所示(group_connectivity_type = 4 时用左侧,group_connectivity_type = 8 时用右侧)。核中白色像素表示使用该值,黑色像素表示忽略。

腐蚀函数会生成一张邻居图像(neighbors image)。核在代价地图上的每个可能位置对应邻居图像中的一个像素。该像素的值等于掩码白色像素所对应的 4 或 8 个代价地图像素中的最大值。换言之,如果附近有障碍物,邻居图像的像素值等于障碍物值,否则等于自由空间值。随后,邻居图像中对应自由空间值的障碍物将被移除。
该过程如下图所示。左侧是代价地图,右侧是邻居图像。白色像素为自由空间,黑色像素为障碍物,group_connectivity_type = 4。动画结束时标记的障碍物将被移除。

当参数 minimal_group_size > 2 时,执行第二种算法。这是一种通用方案,允许在相邻障碍物群的大小小于 minimal_group_size 时将其移除。为选取相邻的障碍物群,算法会对其进行分割。一个分割段内栅格的连通类型由参数 group_connectivity_type 决定。然后计算每个分割段的大小,大小小于 minimal_group_size 的障碍物分割段将被替换为空闲栅格。
该算法比第一种慢约 10 倍,请谨慎使用,仅在必要时启用。其执行时间取决于所处理地图片段的大小(与 minimal_group_size 的值无关)。
下面的动画演示了该算法的运行过程(group_connectivity_type = 8)。动画结束时标记的障碍物将被移除(大小小于 3 的分割段)。
