4、环境感知与建图:占据栅格地图、概率栅格地图、OctoMap八叉树地图
做机器人导航这么多年,我越来越觉得——建图这件事,本质上就是回答一个问题:“这个地方到底能不能走?”
你想想看,机器人要导航,首先得知道自己周围长什么样。激光雷达扫一圈,回来一堆点云。但点云本身不是地图,它只是原始数据。我们需要把这些数据转化成机器人能理解的结构化表示。
这一章,我们就来聊聊三种最常用的地图表示方式:占据栅格地图、概率栅格地图,以及OctoMap八叉树地图。
4.1 占据栅格地图:最朴素的想法
先说最简单的。把空间划分成一个个小格子,每个格子要么是“有障碍物”,要么是“没有障碍物”。这就是占据栅格地图的核心思想。
我在项目中第一次接触这个,是在一个扫地机器人上。当时觉得,这不就是二维数组嘛,有啥难的?后来发现,事情没那么简单。
关键问题:传感器是有噪声的。你测到某个格子有障碍物,它就一定有吗?不一定。可能是噪声,可能是动态物体。
所以,纯粹的0/1二值化表示,在实际中几乎不可用。我们需要更优雅的方式——概率。
4.2 概率栅格地图:用概率说话
概率栅格地图,说白了就是给每个格子一个概率值。0表示绝对空闲,1表示绝对占据,0.5表示完全未知。
每次激光雷达打过来一束射线,我们就更新沿途格子的概率。射线穿过的格子,概率降低(更可能是空闲)。射线终点附近的格子,概率升高(更可能是障碍物)。
嗯,这里要注意:概率更新不是简单的加减法,而是用对数几率(log-odds)来做的。为什么?
因为概率乘法容易溢出,而且不方便累加。对数几率就没有这个问题。
# 一个简单的概率栅格更新示例
import numpy as np
# 初始化:所有格子概率0.5(未知)
grid = np.full((100, 100), 0.5)
# 对数几率转换
def log_odds(p):
return np.log(p / (1 - p + 1e-10))
def inverse_log_odds(l):
return 1.0 / (1.0 + np.exp(-l))
# 更新函数
def update_cell(grid, x, y, occupied, l_occ=0.9, l_free=0.4):
# 当前对数几率
l = log_odds(grid[y, x])
# 更新
if occupied:
l += l_occ
else:
l -= l_free
# 转回概率
grid[y, x] = inverse_log_odds(l)
个人经验:我习惯把l_occ和l_free设成不对称的值。比如l_occ=0.9,l_free=0.4。这样即使偶尔有误检测,地图也不会被瞬间污染。说白了,就是让“占据”的证据权重更大一些。
4.3 二维 vs 三维:什么时候用哪种?
二维概率栅格地图,适合地面机器人。比如扫地机、AGV小车。计算量小,实时性好。
但如果你做的是无人机、机械臂,或者需要感知立体空间的机器人,二维就不够用了。这时候需要三维地图。
三维地图怎么做?最直接的想法:把二维栅格扩展到三维,变成体素(voxel)网格。但问题来了——三维空间太大了。
举个例子:一个10m×10m×3m的房间,用5cm分辨率,需要多少体素?
| 维度 | 数量 |
|---|---|
| X方向 | 200 |
| Y方向 | 200 |
| Z方向 | 60 |
| 总计 | 2,400,000 |
240万个格子!而且大部分是空的。这就是三维栅格地图的致命伤——内存爆炸。
4.4 OctoMap八叉树地图:优雅的解决方案
OctoMap的出现,就是为了解决三维地图的内存问题。它的核心思想是:只在需要的地方细化。
八叉树的结构很简单:
- 根节点代表整个空间
- 每个节点有8个子节点,代表8个卦限
- 如果某个子区域内部是均匀的(全是空闲或全是占据),就不再细分
- 如果区域内部有变化,就继续递归细分
这样做的好处很明显:大片的空旷区域,只需要一个节点表示。只有障碍物边界附近,才会产生大量细节点。
对比一下:同样是10m×10m×3m的空间,5cm分辨率。
- 三维栅格:240万个格子,约2MB(如果每个格子1字节)
- 八叉树地图:通常只有几万到几十万个节点,内存节省10~100倍
4.5 OctoMap的概率更新
OctoMap不是简单的占据/空闲二值树。每个节点也存储概率值,更新方式和概率栅格地图类似。
我曾在一次无人机避障项目中用过OctoMap。当时遇到一个坑:无人机飞得很快,激光雷达数据更新频率高,八叉树频繁更新,导致CPU占用飙升。
避坑指南:我曾经在OctoMap更新时,每来一帧激光数据就全量更新整棵树。结果CPU直接跑满。后来改成局部更新+定时全局合并的策略,才把性能降下来。
具体做法:只更新机器人周围5m范围内的节点,其他区域每隔10帧再统一更新一次。
4.6 三种地图的选型建议
说了这么多,到底该用哪种?我个人的经验是这样的:
- 二维平面导航(扫地机、AGV):用概率栅格地图。简单、高效、成熟。
- 三维空间感知(无人机、机械臂):用OctoMap。内存友好,支持多分辨率查询。
- 纯障碍物检测(不建图,只避障):用占据栅格地图就够了。甚至可以用更简单的代价地图。
4.7 核心知识体系
下面这张图,是我自己总结的三种地图的核心逻辑对比。你看一眼就能明白它们之间的关系。
4.8 实践中的一点感悟
说实话,这三种地图没有绝对的好坏。我见过有人用二维概率栅格做无人机避障,也见过有人用OctoMap做扫地机导航。关键看你的场景和资源。
如果你刚开始做机器人导航,我建议从二维概率栅格地图入手。它足够简单,也足够强大。等遇到三维需求了,再切换到OctoMap也不迟。
嗯,这一章就到这里。地图建好了,下一步就是怎么用它来导航和避障了。
实用工具推荐:
- ROS中的
map_server包:加载和保存概率栅格地图 octomap_server包:实时构建八叉树地图- 可视化工具:RViz +
map和octomap显示插件
公众号:蓝海资料掘金营,微信deep3321