4、贝叶斯更新基础:概率论回顾、贝叶斯公式、二值贝叶斯滤波、对数几率表示
各位同学,欢迎来到占用网格开发的核心理论课。说实话,很多做机器人感知的工程师,代码写得飞起,但一提到概率论就头疼。我当年也是这样,直到在项目中踩了坑,才老老实实回来补数学。今天这一讲,咱们就把贝叶斯更新这件事彻底聊透。
4.1 概率论快速回顾
先别急着翻书,咱们用大白话过一遍。概率论说白了就是研究「不确定性」的学问。你想想看,传感器读数准不准?障碍物到底在不在那个位置?这些都是不确定的。
几个关键概念,我建议你刻在脑子里:
- 先验概率 P(x):在拿到任何观测数据之前,你对状态 x 的初始信念。比如,一个新地图,每个格子我习惯先设成 0.5,表示「完全未知」。
- 似然概率 P(z|x):给定状态 x 下,观测到数据 z 的可能性。说白了就是「如果这里真有障碍物,传感器会看到什么」。
- 后验概率 P(x|z):拿到观测 z 之后,更新对 x 的信念。这就是我们最终想要的东西。
- 全概率公式:P(z) = P(z|x)P(x) + P(z|¬x)P(¬x),用来做归一化。
核心思想:占用网格的本质,就是不断用新观测数据去修正每个格子的「占用概率」。你每扫一帧激光,都是在做一次概率更新。
4.2 贝叶斯公式:从理论到直觉
贝叶斯公式长这样:
P(x|z) = P(z|x) * P(x) / P(z)
别被公式吓到。我换个说法你就懂了:后验 = 似然 × 先验 / 证据。
为什么会这样?你想想看:
- 先验 P(x) 是你之前的判断
- 似然 P(z|x) 是传感器模型告诉你的「如果假设成立,观测有多合理」
- 证据 P(z) 就是个归一化常数,保证概率加起来等于1
我在项目中遇到过一个问题:直接用贝叶斯公式更新,发现概率越算越极端,最后全变成0或1。后来才意识到,是传感器模型没校准好,似然概率给得太绝对了。
我的习惯:在实际工程中,我建议你先用仿真数据跑一遍贝叶斯更新,看看概率收敛曲线是否平滑。如果跳变太剧烈,八成是模型参数有问题。
4.3 二值贝叶斯滤波:占用网格的数学引擎
占用网格里每个格子只有两种状态:占用(Occupied)或空闲(Free)。这就是典型的二值状态估计问题。二值贝叶斯滤波就是专门干这个的。
它的更新公式长这样:
P(occ | z) = P(z | occ) * P(occ) / P(z)
P(free | z) = P(z | free) * P(free) / P(z)
嗯,这里要注意:我们其实只需要更新 P(occ) 就够了,因为 P(free) = 1 - P(occ)。
但直接算概率有个麻烦——数值不稳定。尤其是当概率接近0或1时,浮点数精度会出问题。我曾经在嵌入式平台上调试,发现概率更新几轮后直接变成 NaN,查了半天才发现是数值下溢了。
避坑指南:千万不要在代码里直接用浮点数累乘概率!概率值越乘越小,很快会超出浮点数精度范围。我曾经因为这个 bug 排查了整整两天。
4.4 对数几率表示:工程界的标准解法
为了解决数值问题,业界普遍采用对数几率(Log-Odds)表示。说白了就是把概率映射到整个实数轴上:
l(x) = log( P(x) / (1 - P(x)) )
反过来,从对数几率恢复概率:
P(x) = 1 / (1 + exp(-l(x)))
这样做的好处太明显了:
- 数值稳定:对数几率范围是 (-∞, +∞),不会出现0或1的极端情况
- 更新变加法:贝叶斯更新在对数几率空间里就是简单的加法
- 计算高效:不用每次都做除法,加减法比乘除法快得多
更新公式在对数几率空间里简化为:
l_new = l_prior + l_measurement
其中 l_measurement 是传感器模型的逆对数几率。你想想看,原来要算乘除法,现在就是加一下,多清爽。
实际工程中的标准做法:
- 初始化每个格子为 l=0(对应概率0.5)
- 每次收到传感器数据,计算 l_measurement
- 执行 l_new = l_old + l_measurement
- 需要输出概率时,再转回 P = 1/(1+exp(-l))
我个人习惯在代码里维护一个 float 类型的对数几率数组,只在可视化或决策时才转成概率。这样既省计算,又避免精度问题。
4.5 一个完整的更新示例
咱们用代码说话。假设一个格子当前占用概率是0.3,传感器模型给出:
// 当前概率 0.3,对应对数几率
float l_prior = log(0.3 / 0.7); // ≈ -0.847
// 传感器模型:如果检测到障碍物,似然比 = 0.9/0.1 = 9
float l_measurement = log(9); // ≈ 2.197
// 更新
float l_new = l_prior + l_measurement; // ≈ 1.350
// 转回概率
float p_new = 1.0 / (1.0 + exp(-l_new)); // ≈ 0.794
你看,一次观测就把概率从0.3提升到了0.794。如果连续多次观测到障碍物,概率会逐渐趋近于1,但永远不会真的变成1——这就是对数几率表示的好处。
工程小技巧:在实际代码中,我建议把 exp() 计算换成查表法。尤其是嵌入式平台,exp() 函数很慢。预计算一个1024大小的查找表,精度完全够用。
好了,这一讲的核心就这些。记住:贝叶斯更新是占用网格的灵魂,对数几率表示是工程实现的基石。下一讲我们会把这些理论真正落地到代码里,到时候你就知道今天学的有多重要了。