3. 环境建模方法:从栅格地图到高程图,再到3D体素地图
做机器人导航这些年,我踩过最大的坑是什么?
就是地图建得不对,后面路径规划再漂亮也白搭。
你想想看,算法再牛,给它的地图是错的,它怎么走?
所以今天咱们聊聊环境建模。
说白了,就是把真实世界“翻译”成机器人能懂的语言。
从最简单的栅格地图,到带高度信息的高程图,再到真正的3D体素地图。
这三种方法,我都在项目里用过,各有各的脾气。
3.1 栅格地图:最朴素的起点
栅格地图,我习惯叫它“格子地图”。
把环境切成一个个小方格,每个格子要么是0(空闲),要么是1(占用)。
简单粗暴,但非常有效。
构建原理
- 将连续空间离散化为固定大小的网格
- 每个格子存储一个占用概率值(0~1)
- 通过激光雷达或声纳数据更新格子状态
核心公式(贝叶斯更新):
P(occ | z) = P(z | occ) * P(occ) / P(z)
// 实际工程中常用对数概率形式:
l_i = l_i + log(P(occ | z) / (1 - P(occ | z))) - l_0
适用场景
- 室内扫地机器人、仓储AGV
- 地面平坦、障碍物垂直的环境
- 计算资源有限的嵌入式平台
我的经验:
我曾经在一个仓库项目里用栅格地图,分辨率设了5cm。
结果内存爆了——100m×100m的仓库,格子数400万。
后来我改成10cm分辨率,内存降了75%,精度也没差多少。
嗯,选分辨率是个平衡活。
注意:
栅格地图无法表达高度信息。
如果有个悬空的障碍物,它在地面的投影会挡住所有路径。
这就是为什么户外机器人不能用纯栅格地图。
3.2 高程图:给地图加上高度
户外项目让我意识到,光有平面地图不够。
机器人要爬坡、要绕坑,得知道地形起伏。
高程图就是栅格地图的“升级版”——每个格子存一个高度值。
构建原理
- 在栅格地图基础上,每个格子存储地形高度
- 通过多线激光雷达或立体视觉获取点云数据
- 对点云进行网格化处理,取每个网格内的平均高度
高程图数据结构示例:
struct ElevationCell {
float height; // 地形高度
float variance; // 高度方差(表示平坦度)
uint8_t confidence; // 置信度
bool is_obstacle; // 是否障碍物
};
适用场景
- 户外巡检机器人、农业机器人
- 有坡度、有坑洼的地形
- 需要判断可通行性的场景
避坑指南:
我曾经在矿山项目里用高程图,遇到一个问题:
陡坡边缘的高度突变,导致机器人误判为障碍物。
后来我加了“坡度滤波器”——计算相邻格子的高度差,
如果坡度小于30度,就认为是可通行的斜坡。
高程图的局限性
- 本质还是2.5D——一个(x,y)只能对应一个高度
- 无法处理悬空障碍物(比如头顶的树枝)
- 对于洞穴、桥梁下方等场景无能为力
3.3 3D体素地图:真正的三维世界
说到3D体素地图,我第一个想到的是无人机项目。
无人机要穿越树林,头顶有树枝,脚下有灌木。
这时候,高程图就不够用了。
你需要知道每个三维空间点是否被占用。
构建原理
- 将三维空间划分为立方体体素(voxel)
- 每个体素存储占据概率
- 通过深度相机、激光雷达或RGB-D传感器更新
- 常用数据结构:八叉树(OctoMap)
八叉树体素地图核心逻辑:
class OctoMapNode {
bool is_leaf; // 是否为叶子节点
float occupancy; // 占据概率
OctoMapNode* children[8]; // 八个子节点
float log_odds; // 对数概率值
};
// 更新公式(与栅格地图类似,但扩展到3D):
L(n) = L(n) + log(P(occ | z) / (1 - P(occ | z)))
适用场景
- 无人机避障、空中作业
- 洞穴勘探、地下空间
- 复杂3D环境(树林、建筑内部)
- 需要精确3D建模的场景
我的经验:
做无人机项目时,我一开始用0.1m分辨率的体素地图。
结果飞了5分钟,内存就爆了。
后来我改用八叉树——只在有障碍物的区域细分体素,
空旷区域用大块体素表示。
内存占用直接降了80%。
你想想看,这就是数据结构选型的重要性。
注意:
3D体素地图的计算量是栅格地图的立方倍。
如果分辨率是0.1m,一个10m×10m×5m的空间,
体素数量是50万个。
实时更新需要GPU加速或高效的数据结构。
3.4 三种地图的对比与选择
| 特性 | 栅格地图 | 高程图 | 3D体素地图 |
|---|---|---|---|
| 维度 | 2D | 2.5D | 3D |
| 内存占用 | 低 | 中 | 高 |
| 计算复杂度 | O(n²) | O(n²) | O(n³) |
| 高度信息 | 无 | 有(单值) | 有(多值) |
| 悬空障碍物 | 无法处理 | 无法处理 | 可以处理 |
| 典型传感器 | 单线激光 | 多线激光 | 深度相机/3D激光 |
| 推荐场景 | 室内平地 | 户外起伏 | 复杂3D环境 |
我的选择原则:
- 如果机器人只在平地上跑 → 栅格地图就够了
- 如果要去户外爬坡 → 高程图是性价比之选
- 如果要在树林里飞 → 老老实实用3D体素地图
说白了,地图的复杂度要和任务匹配。
别为了炫技用3D体素,结果算力跟不上。
也别为了省事用栅格地图,结果机器人掉坑里。
3.5 实际工程中的混合策略
我在实际项目中,很少只用一种地图。
更多时候是混合使用。
举个例子:
- 全局规划用栅格地图(快速、轻量)
- 局部规划用高程图(感知地形起伏)
- 遇到悬空障碍物时,临时启用3D体素局部地图
我的做法:
我习惯在系统中维护三层地图:
1. 全局栅格地图(分辨率0.5m,用于远距离规划)
2. 局部高程图(分辨率0.1m,用于中距离避障)
3. 实时3D体素窗口(分辨率0.05m,用于紧急避障)
三层地图同步更新,但计算量可控。
嗯,环境建模就聊到这里。
记住一句话:地图是机器人的“眼睛”,
选对了地图,路径规划就成功了一半。
公众号:蓝海资料掘金营,微信deep3321