PythonRobotics 高斯网格地图(Gaussian Grid Map)算法解析:原理、源码实现与运行指南
【免费下载链接】PythonRoboticsPython sample codes and textbook for robotics algorithms.项目地址: https://gitcode.com/GitHub_Trending/py/PythonRobotics
导读:本文以 PythonRobotics 仓库的 Mapping 模块中
gaussian_grid_map示例为核心,讲解"2D 高斯网格建图"(2D Gaussian grid mapping)的完整算法流程。你将掌握:如何用障碍点云与高斯概率密度构建占据网格地图、EXTEND_AREA/xyreso/std三个关键参数的作用,以及该示例的运行与测试方法,并深入理解 generate_gaussian_grid_map 的实现细节。
一、示例定位:Mapping 模块中的高斯网格建图
PythonRobotics 是一个以"机器人算法教科书 + Python 示例代码"形式组织的开源仓库。在 Mapping 模块总览 中,Mapping 被定义为机器人借助 LIDAR、相机等外部传感器理解周围环境的能力——机器人必须识别障碍物的位置与形状才能避障,而网格建图(grid mapping)是其中被广泛使用的核心手段之一。
本文讲解的高斯网格建图示例位于 Mapping/gaussian_grid_map/gaussian_grid_map.py,对应文档 gaussian_grid_map_main.rst 与 README 中的 Gaussian grid map 条目。它的定位是:将一组离散障碍物坐标转换为一张连续的二维占据概率网格地图——用高斯分布刻画"距离障碍物越近、占据概率越高"的空间关系。
该模块与同目录下的 ray_casting_grid_map(射线投射网格地图)、distance_map(距离地图)等共同构成 Mapping 模块的网格建图家族,但高斯网格地图的独特之处在于:它不是基于传感器射线的几何模拟,而是基于障碍点最近距离的解析概率建模,算法更简洁、无方向性假设。
二、算法原理:最近距离 + 高斯累积分布
2.1 核心思想
对于地图中的每一个网格单元 ((x, y)),算法执行两个步骤:
- 计算该网格到所有障碍物的最近欧氏距离
mindis; - 将该距离映射为占据概率,映射函数为高斯累积分布函数的补集:
pdf = 1.0 - norm.cdf(mindis, 0.0, std)其中norm.cdf来自scipy.stats。当mindis = 0(网格恰好落在障碍点上)时,pdf趋近于 1.0,表示该区域几乎必然被占据;当距离远大于std时,pdf趋近于 0,表示该区域几乎必然空闲。std即高斯分布的标准差,决定了概率随距离衰减的"柔软程度"。
2.2 与经典占据栅格(Occupancy Grid)的差异
经典的占据栅格地图通常使用贝叶斯更新(如 ekf_slam、FastSLAM 中的建图部分)累加传感器观测概率。而本示例采用非增量式的单次解析计算:给定一组障碍物坐标,直接一次性生成整张概率图。因此它更适合作为"障碍概率场"的静态构建示例,用于路径规划中的势场构造或代价地图生成等场景,而不是在线 SLAM 的实时建图组件。
三、源码级实现解析
完整实现位于 Mapping/gaussian_grid_map/gaussian_grid_map.py,共包含四个核心函数。下面逐段拆解。
3.1 网格地图配置计算:calc_grid_map_config
EXTEND_AREA = 10.0 # [m] grid map extention length def calc_grid_map_config(ox, oy, xyreso): minx = round(min(ox) - EXTEND_AREA / 2.0) miny = round(min(oy) - EXTEND_AREA / 2.0) maxx = round(max(ox) + EXTEND_AREA / 2.0) maxy = round(max(oy) + EXTEND_AREA / 2.0) xw = int(round((maxx - minx) / xyreso)) yw = int(round((maxy - miny) / xyreso)) return minx, miny, maxx, maxy, xw, yw该函数根据障碍点集的最小/最大坐标,向四周外扩EXTEND_AREA / 2.0(默认各方向外扩 5 m)确定地图边界,再按分辨率xyreso换算成网格数量xw × yw。返回的边界值同时供后续绘制热力图使用。
3.2 主算法:generate_gaussian_grid_map
def generate_gaussian_grid_map(ox, oy, xyreso, std): minx, miny, maxx, maxy, xw, yw = calc_grid_map_config(ox, oy, xyreso) gmap = [[0.0 for i in range(yw)] for i in range(xw)] for ix in range(xw): for iy in range(yw): x = ix * xyreso + minx y = iy * xyreso + miny # Search minimum distance mindis = float("inf") for (iox, ioy) in zip(ox, oy): d = math.hypot(iox - x, ioy - y) if mindis >= d: mindis = d pdf = (1.0 - norm.cdf(mindis, 0.0, std)) gmap[ix][iy] = pdf return gmap, minx, maxx, miny, maxy关键实现细节:
- 三重循环结构:外层遍历网格的
ix/iy索引,内层遍历全部障碍点求最近距离。该实现为朴素 O(网格数 × 障碍点数) 的暴力搜索,代码直观、便于教学,但网格较大时计算量可观——实际工程中可替换为 k-d 树或距离变换(distance transform)加速,仓库中的 distance_map 即演示了另一种距离场构建思路。 - 坐标换算:网格索引通过
x = ix * xyreso + minx反算回真实世界坐标,保证每个网格中心点与地图边界严格对齐。 - 概率赋值:
norm.cdf(mindis, 0.0, std)表示"距离小于等于 mindis"的累积概率;取补集后,越靠近障碍物概率越高,正好表达占据概率的语义。 - 返回值:除
gmap概率矩阵外,还返回minx, maxx, miny, maxy四个边界值,供可视化函数绘制坐标轴。
3.3 可视化:draw_heatmap
def draw_heatmap(data, minx, maxx, miny, maxy, xyreso): x, y = np.mgrid[slice(minx - xyreso / 2.0, maxx + xyreso / 2.0, xyreso), slice(miny - xyreso / 2.0, maxy + xyreso / 2.0, xyreso)] plt.pcolor(x, y, data, vmax=1.0, cmap=plt.cm.Blues) plt.axis("equal")使用np.mgrid构造网格坐标网格,plt.pcolor以蓝色系色阶(cmap=plt.cm.Blues)渲染概率场,vmax=1.0固定色阶上限,axis("equal")保证横纵轴等比例,避免地图变形。
3.4 主流程:main
def main(): print(__file__ + " start!!") xyreso = 0.5 # xy grid resolution STD = 5.0 # standard diviation for gaussian distribution for i in range(5): ox = (np.random.rand(4) - 0.5) * 10.0 oy = (np.random.rand(4) - 0.5) * 10.0 gmap, minx, maxx, miny, maxy = generate_gaussian_grid_map( ox, oy, xyreso, STD) if show_animation: # pragma: no cover plt.cla() ... draw_heatmap(gmap, minx, maxx, miny, maxy, xyreso) plt.plot(ox, oy, "xr") plt.plot(0.0, 0.0, "ob") plt.pause(1.0)主流程连续生成5 组随机障碍场景(每组 4 个障碍点,坐标均匀分布在 ([-5, 5]\times[-5, 5]) m 内),每组渲染 1 秒,形成逐帧动画效果。图中红色x标记障碍点、蓝色圆点标记原点 (0, 0)。按下Esc键可随时退出动画(通过mpl_connect('key_release_event', ...)注册的按键回调实现)。
四、关键参数速查表
| 参数 | 定义位置 | 默认值 | 单位 | 作用与影响 |
|---|---|---|---|---|
EXTEND_AREA | 模块级常量 | 10.0 | m | 地图在障碍点集基础上各方向外扩的长度(实际单侧外扩EXTEND_AREA/2= 5 m),决定地图的物理范围 |
xyreso | main()中传入 | 0.5 | m/格 | 网格分辨率,越小地图越精细,但网格数xw × yw以平方速度增长,计算耗时显著上升 |
std(STD) | main()中传入 | 5.0 | m | 高斯分布标准差,控制占据概率随距离衰减的速度;std越大,概率场扩散越广、越平滑 |
障碍点集ox/oy | main()中随机生成 | 每组 4 点 | m | 障碍物坐标序列,实际使用中可由 LIDAR 观测点云替代 |
从 requirements/requirements.txt 可以看到,本示例仅依赖numpy、scipy(提供norm.cdf)与matplotlib(提供可视化),无需额外机器人专用库,非常适合作为算法入门与二次开发的基础。
五、运行与测试验证
5.1 直接运行示例
在仓库根目录执行:
python Mapping/gaussian_grid_map/gaussian_grid_map.py运行后会打印...gaussian_grid_map.py start!!,随后弹出 5 帧随机障碍场景的高斯概率热力图动画,按Esc可提前结束。
5.2 单元测试
仓库为每个示例配备了 pytest 测试。本模块的测试位于 tests/test_gaussian_grid_map.py:
def test1(): m.show_animation = False m.main()测试先将show_animation置为False以关闭 GUI 动画(对应源码中的# pragma: no cover分支),再调用main()走完整算法流程,从而在无显示环境下验证算法可正常执行。运行方式:
# 方式一:运行单个测试 python -m pytest tests/test_gaussian_grid_map.py # 方式二:运行 Mapping 模块全部测试 python -m pytest tests/test_gaussian_grid_map.py tests/test_distance_map.py tests/test_ray_casting_grid_map.py测试基础设施由 tests/conftest.py 提供(将仓库根目录注入sys.path以支持from Mapping.gaussian_grid_map import gaussian_grid_map的导入方式);runtests.sh 展示了仓库 CI 中执行全量测试的标准命令:pytest tests -l -Werror --durations=0(将警告视为错误并输出测试耗时排名)。
六、适用场景与扩展方向
从实现与测试可以确认:本示例是离线的、给定障碍点集的静态概率场构建。它适合以下用途:
- 路径规划前的代价地图构建:将高斯概率场作为势场法(参考 potential_field_planning)或网格搜索算法的启发式输入;
- 教学演示:直观展示"网格分辨率—概率分布标准差—地图平滑度"三者之间的关系;
- 传感器模型抽象:将激光点云降采样后的障碍点(参考 point_cloud_sampling)直接喂入
generate_gaussian_grid_map,即可得到连续占据概率场。
需要注意的局限:暴力三重循环在分辨率提高或地图范围扩大时计算开销呈平方级增长;同时该实现未考虑传感器观测方向与不确定性模型,若需要更贴近真实传感器特性的建图,可进一步研读仓库中的 ray_casting_grid_map 与 SLAM 模块(如 ekf_slam)中的概率栅格更新方式。
七、小结
高斯网格建图示例以极简的代码(约 50 行核心逻辑)演示了机器人建图中的一个基础而实用的思想:用最近障碍距离的高斯累积分布构造连续占据概率场。通过 gaussian_grid_map.py 中calc_grid_map_config、generate_gaussian_grid_map、draw_heatmap三个函数的配合,它完整覆盖了"地图范围确定 → 概率计算 → 热力图可视化"的全流程。无论你是希望快速理解网格概率地图的数学本质,还是需要为路径规划构建一个可复用的代价场,这个示例都是一个理想的起点。
【免费下载链接】PythonRoboticsPython sample codes and textbook for robotics algorithms.项目地址: https://gitcode.com/GitHub_Trending/py/PythonRobotics
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考