1. 栅格地图基础
做路径规划这些年,我接触过不少地图表示方法。但要说最直观、最容易上手的,还得是栅格地图。说白了,就是把真实世界切成一个个小格子,每个格子要么能走,要么不能走。就这么简单。
什么是栅格地图
栅格地图,也叫网格地图或 occupancy grid map。它把环境离散化成一个个大小相同的单元格。每个单元格代表真实世界中的一小块区域。
我刚开始接触这个时,觉得它像极了像素画。你想想看,一张图片由像素组成,每个像素有自己的颜色。栅格地图也一样,每个格子有自己的状态——要么是障碍物,要么是自由空间。
核心思想:用离散的格子来表示连续的世界。这是所有基于栅格的路径规划算法的基础。
栅格地图的表示方法
实际项目中,栅格地图通常用二维数组或矩阵来表示。我个人习惯用 NumPy 的 ndarray,操作起来特别方便。
import numpy as np
# 创建一个 5x5 的栅格地图
# 0 表示空闲,1 表示障碍物
grid_map = np.array([
[0, 0, 0, 1, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 0]
])
print("栅格地图:")
print(grid_map)
print(f"地图尺寸:{grid_map.shape}")
这里要注意,不同的库或框架对值的定义可能不一样。有的用 0 表示障碍物,1 表示空闲。我建议你在项目开始时先统一约定,不然容易出 bug。
占据栅格与空闲栅格
每个栅格只有两种状态吗?其实不是。实际应用中,我们通常用概率来表示一个格子是否被占据。这就是占据栅格的概念。
| 栅格状态 | 数值范围 | 含义 |
|---|---|---|
| 空闲 (Free) | 0.0 - 0.3 | 该区域无障碍物,机器人可以通行 |
| 未知 (Unknown) | 0.3 - 0.7 | 传感器未覆盖,状态不确定 |
| 占据 (Occupied) | 0.7 - 1.0 | 该区域有障碍物,机器人不可通行 |
嗯,这里要注意。用概率而不是二值状态,好处是能处理传感器噪声。我在做扫地机器人项目时遇到过这种情况:激光雷达扫到一面玻璃墙,有时候能穿透,有时候反射回来。如果用二值状态,地图会频繁跳变。用概率表示就好多了,多次测量后概率会收敛到正确值。
小技巧:实际编码时,可以用 -1 表示未知区域,0 表示空闲,100 表示占据。这样既节省内存,又方便做阈值判断。
地图分辨率与精度
分辨率,就是每个格子代表的实际物理尺寸。比如 0.05m/格,意味着每个格子边长 5 厘米。
分辨率怎么选?这问题我当年纠结了很久。给你几个参考值:
- 室内机器人:0.02m - 0.05m/格。够精细,能识别门框、桌腿
- 室外无人车:0.1m - 0.5m/格。范围大,不需要太细
- 仓储 AGV:0.05m - 0.1m/格。兼顾精度和效率
分辨率越高,地图越精细,但内存占用和计算量也越大。我曾经做过一个项目,把分辨率从 0.05m 改成 0.02m,结果地图数据量暴增 6 倍多,算法跑一圈要等半天。
避坑指南:分辨率不是越高越好。我见过有人把室内地图设成 0.01m/格,结果一个 100 平米的房间,地图矩阵就有 10000x10000 那么大。路径规划算法跑一次要好几秒,根本没法实时用。
精度嘛,和分辨率有关,但不是一回事。精度说的是地图和真实世界的吻合程度。即使分辨率很高,如果传感器数据不准,地图照样不准。我建议你:
- 先根据机器人尺寸和任务需求确定分辨率
- 再根据传感器精度评估地图精度
- 最后在实际环境中验证和调优
说白了,栅格地图就是个 trade-off。你要在精度、内存、计算速度之间找到平衡点。没有完美的参数,只有适合你项目的参数。
一句话总结:栅格地图就是把世界切成格子,每个格子用概率表示是否被占据。分辨率决定格子大小,精度决定地图可信度。选参数时,别贪心,够用就好。
下一章,我们会聊聊怎么用代码生成栅格地图,以及如何把传感器数据转换成栅格状态。到时候我会分享一些我在实际项目中踩过的坑,保证有用。