3. 环境建模方法:从栅格地图到高程图,再到3D体素地图

做机器人导航这些年,我踩过最大的坑是什么?
就是地图建得不对,后面路径规划再漂亮也白搭。
你想想看,算法再牛,给它的地图是错的,它怎么走?

所以今天咱们聊聊环境建模。
说白了,就是把真实世界“翻译”成机器人能懂的语言。
从最简单的栅格地图,到带高度信息的高程图,再到真正的3D体素地图。
这三种方法,我都在项目里用过,各有各的脾气。

环境建模方法演进 栅格地图 2D 平面 / 0-1 占用 高程图 2.5D / 高度值 3D 体素地图 3D 体素 / 占据概率 室内 / 简单地面 户外 / 起伏地形 复杂3D / 空中 / 洞穴 维度: 2D 维度: 2.5D 维度: 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