地图基础概念:占据栅格地图、概率地图、代价地图

做机器人导航这些年,我越来越觉得一个道理:地图是机器人的眼睛。没有好的地图,再牛的算法也白搭。

今天咱们就来聊聊三种最核心的地图概念。这三种地图,说白了就是机器人理解世界的三种不同方式。我刚开始接触Nav2时,也经常把它们搞混。后来踩了不少坑,才慢慢理清楚。

1. 占据栅格地图(Occupancy Grid Map)

这是最基础、最常用的地图形式。你想想看,机器人怎么知道前面有没有墙?

占据栅格地图的做法很简单:把环境切成一个个小格子。每个格子要么是空的,要么被占了。

核心思想:每个栅格独立存储一个概率值,表示该位置被障碍物占据的可能性。

我记得第一次做扫地机器人项目时,用的就是这种地图。每个格子大小设成了5cm×5cm。太小了计算量太大,太大了又看不清细节。

占据栅格地图的数学表达其实不复杂:

// 每个栅格的值范围:0-100
// 0  = 完全空闲
// 100 = 完全占据
// -1 = 未知区域

// 更新公式(对数几率形式):
l(m_i | z_{1:t}) = l(m_i | z_{1:t-1}) + l(m_i | z_t) - l(m_i)

嗯,这里要注意:不是所有格子都需要更新。只有激光雷达扫到的区域才需要处理。我见过有人傻乎乎地每帧更新全图,结果CPU直接拉满。

2. 概率地图(Probability Map)

占据栅格地图有个问题:它太了。一个格子要么是0要么是100,但现实世界哪有这么黑白分明?

概率地图就是来解决这个问题的。它用贝叶斯滤波的思想,不断更新每个格子的置信度。

我的经验:概率地图的更新频率很关键。我一般设成5Hz,太快了反而容易引入噪声。

概率地图的更新流程大概是这样的:

  1. 预测阶段:根据机器人运动模型,预测新位置
  2. 观测阶段:激光雷达扫到障碍物,更新对应栅格
  3. 融合阶段:把新旧信息加权平均

我曾经在一个仓库项目里吃过亏。仓库里全是货架,激光雷达扫过去全是反射。如果用普通的占据栅格地图,机器人会以为到处都是墙。后来换成概率地图,加上一个动态阈值,才把问题解决。

概率地图的更新公式:

P(occupied | z) = P(z | occupied) * P(occupied) / P(z)

// 实际应用中常用对数几率形式:
log_odds = log(P(occupied) / (1 - P(occupied)))

避坑指南:我曾经把概率阈值设得太低(0.3),结果机器人把地毯上的花纹当成了障碍物。建议阈值设在0.6-0.7之间。

3. 代价地图(Costmap)

代价地图是Nav2里最常用的。它不是在问「这里有没有障碍物」,而是在问「从这里走有多危险」。

代价地图把每个格子赋予一个代价(cost),范围0-255:

代价范围 含义 典型场景
0 完全自由 开阔地面
1-50 低风险 靠近墙壁但可通行
51-100 中等风险 障碍物边缘
101-253 高风险 狭窄通道
254 致命障碍 墙壁、柱子
255 未知区域 未探索区域

代价地图有个很实用的特性:膨胀层。说白了就是把障碍物「吹胖」一圈,给机器人留出安全距离。

核心参数:inflation_radius(膨胀半径)。我一般设成机器人半径的1.2倍。太小了容易撞墙,太大了又过不去门。

代价地图的生成流程:

# 伪代码示例
def update_costmap(laser_scan, robot_pose):
    # 1. 从激光数据生成障碍物层
    obstacle_layer = compute_obstacles(laser_scan)
    
    # 2. 添加膨胀层
    inflated_layer = inflate(obstacle_layer, radius=0.3)
    
    # 3. 叠加静态地图层
    static_layer = load_static_map("map.yaml")
    
    # 4. 融合所有层
    final_costmap = fuse_layers([static_layer, obstacle_layer, inflated_layer])
    
    return final_costmap

三种地图的关系

这三种地图不是互相替代的关系,而是层层递进的:

  • 占据栅格地图是基础,告诉你「有没有东西」
  • 概率地图是升级,告诉你「有多大概率有东西」
  • 代价地图是应用,告诉你「从这里走划不划算」

我个人的习惯是:建图时用概率地图,导航时用代价地图。占据栅格地图作为中间格式,用来保存和加载。

小技巧:如果你发现机器人导航时总在同一个地方犹豫不决,多半是代价地图的膨胀参数没调好。试着把inflation_radius调小0.05米,效果立竿见影。

下面这张图展示了三种地图的核心逻辑关系:

三种地图核心逻辑关系 占据栅格地图 Occupancy Grid Map 每个格子:0(空)或100(占) 概率地图 Probability Map 每个格子:0.0 ~ 1.0 概率值 代价地图 Costmap 每个格子:0 ~ 255 代价(含膨胀层) 基础 → 升级 → 应用

最后说一句:别把地图想得太玄乎。它就是个数据结构,关键是怎么用好它。我见过有人把参数调了三个月,结果发现是传感器没装稳。嗯,有时候问题没你想的那么复杂。

重要提醒:地图的坐标系一定要和机器人坐标系对齐。我曾经因为tf树配错了,机器人对着墙直直撞过去。还好速度不快,不然就出事故了。

可可精品资料库