☰
SLAM本质拆解:一句话、一个公式看懂同步定位与建图
2026/10/17 5:13:54 网站建设 项目流程

准备给新同事讲SLAM的时候,我发现自己在这个话题上最吃亏的点不是算法细节,而是"到底怎么用最短的话把这件事讲明白"。我们一出场就聊特征提取、聊优化求解、聊回环检测,听的人往往撑到中间就开始走神。后来我换了个讲法:先压成一句话、一个公式,把框架钉死,再往里面填代码和细节。效果立刻不一样了。

这篇文章就把我一直在用的那套"一句话 + 一个公式"完整摊开。核心内容就是SLAM的本质拆解:它到底在估计什么、为什么轨迹和地图必须放在一起算、那个最著名的概率公式每个符号代表什么、以及实际工程里它又是怎么变成非线性最小二乘问题的。不管你是刚接触视觉SLAM、激光SLAM,还是做机器人导航、AR定位相关的开发,这套框架都能直接往自己的项目上套。

1. 一句话:把"我在哪"和"周围长什么样"捆在一起

1.1 这句话拆开到底说了几层意思

我给SLAM本质写过很多种"一句话"版本,最常拿出来用的是这句:机器人一边猜测自己走过的轨迹,一边拼凑周围的地图,而轨迹和地图互为前提,必须捆在一起同时估计。

这里面其实有四层信息。第一,"边走边猜"强调的是过程,SLAM不是先建好地图再定位,地图和定位是在运动过程中同步生成的。第二,"轨迹"不是一个点,而是一串位姿,从开始运动到当前时刻所有时刻的位置姿态合在一起,这才是我们要估计的。第三,"地图"也不是一张静态jpg,它是环境中所有路标点、墙面、障碍物位置关系的整体描述。第四,也是最重要的一点——轨迹和地图不是两个独立任务,它们是同一个变量的两个侧面。

很多人刚接触SLAM时会本能地把它拆成两个模块:先做定位,再做建图。实际不行。没有地图,你没法把当前观测转化成一个"绝对位置";没有位置,你又没法把新观测正确地拼接进地图。任何一个单独拿出来都缺一条腿。

还有一种更直白的说法:**SLAM问的不是"我在哪"或"周围是什么",而是"我在哪且周围是什么,这两者加在一起的最可信组合是什么"。**这就是"估计量"和"估计结果"的差别,也是后面那个公式存在的根本原因。

1.2 那个"互为前提"的诅咒

为什么这句看似普通的话,背后藏着一个绕不开的难点?

想象一个最简单的情况。机器人从原点出发,用激光雷达扫了一圈。第一帧它看到走廊尽头有一面墙,它觉得自己在原点,于是把这面墙记在地图里。走了十米之后,它需要靠这面墙反推自己现在的位置。问题来了:如果第一次记墙时的位姿是错的,那现在用这面墙推导出来的位姿天然带误差;而带误差的位姿又会产生一张更歪的地图。误差就这样在轨迹和地图之间来回传递,越传越大。

SLAM的全称是Simultaneous Localization and Mapping,同步定位与建图。那个"同步"不是修饰语,而是这个问题的核心诅咒:**你必须在不知道答案A的情况下去求解答案B,同时又在不知道答案B的情况下去求答案A。**数学上这叫做联合估计,工程上它造成了误差累积、回环漂移、地图不闭合这一系列经典问题。

你先记住这句话:轨迹和地图,不是一个先一个后,也不是一左一右,它们是同一个概率分布里的两个角色。理解不了这一点,后面看再多代码都是散的。

2. 一个公式:p(x1:t, m | z1:t, u1:t)到底在描述什么

2.1 四个符号先对齐:x、m、z、u

公式长这样:

p(x_{1:t}, m | z_{1:t}, u_{1:t})

这大概是SLAM领域出现频率最高的一个式子,各种论文里换个写法,但内核都是它。先把符号一个个翻译成人话:

符号含义通俗解释
x_{1:t}从时刻1到时刻t的机器人轨迹每个时刻位姿串起来的一条"行走路线"
m环境地图所有路标、特征点、栅格位置的集合
z_{1:t}一路上获得的所有观测相机图像里的特征点、激光雷达的扫描帧、深度图的点云
u_{1:t}一路上得到的控制或运动输入轮式里程计、IMU积分、底盘下发速度指令

竖线左边是要估计的量,右边是已知的输入。所以这个式子读出来就是:已知我从头到尾的所有运动输入和传感器观测,让我估计我走过的全部轨迹和整个环境地图的联合概率分布。

注意不是"估计轨迹的概率"再"估计地图的概率",而是把轨迹和地图放在同一个条件概率里面。这是整个公式最重要的姿态:因为它们互相牵制,所以不能拆开。

2.2 条件概率里的"同时估计"才是精髓

贝叶斯公式告诉我们另一件事:p(x, m | z, u) 正比于 p(z | x, m) 乘以 p(x | u) 再乘以先验。这个形式看着绕,实际每个因子都有明确物理意义。

p(z | x, m) 是观测模型:在给定位置和给定地图的情况下,我"预期"看到什么。比如激光雷达在某个位姿扫描一面墙,理论上应该打出什么样的距离数值。p(x | u) 是运动模型:上一刻我在哪、我发出了什么运动指令,我"预计"现在到哪。这本质上是把物理规律翻译成了概率。最后一个先验,通常来自你的初始假设或全局约束。

这三项叠在一起,恰好把SLAM的每一部分知识都收纳了:传感器怎么工作、机器人怎么运动、世界大概长什么样。公式的最高明之处,不在于它算起来简单,而在于它把你所有能利用的信息来源都列清楚了。你后来加的IMU预积分、相机重投影约束、激光帧间匹配,全都要通过修改这三项里的某一个来完成。

2.3 运动模型和观测模型,公式里的两个"齿轮"

实际写代码时,p(x | u) 会被具体化成带噪声的运动方程:

x_t = f(x_{t-1}, u_t) + w_t

p(z | x, m) 会被具体化成带噪声的观测方程:

z_t = h(x_t, m) + v_t

f表示运动递推,比如根据轮速和转向角推算下一个位置;h表示观测生成,比如相机看到某个3D路标点投影到图像上的哪个像素。w_t和v_t是噪声,通常假设为高斯。

这两个方程就是SLAM系统的"齿轮":运动模型负责把时间轴往前推,观测模型负责把地图和轨迹咬合在一起。前面那句话里的"捆在一起",在数学上就是靠观测模型h实现的——因为z同时出现在x和m的联系里,所以轨迹和地图没法独立求解。

我见过不少人把SLAM理解成"为特征点匹配一个刚性变换",那只是前端——如果你只看两帧图像,算出来的只是帧间运动,不是完整SLAM。真正的SLAM求的是整个历史轨迹加地图的后验,不是某一个位移向量。哪两帧之间的变换,只是完成这个公式的中间材料。

3. 公式是怎么变成你熟悉的优化问题的

3.1 最大后验估计:把概率最高的一档选出来

概率分布本身不能直接写成代码,你需要一个"解"。SLAM用的解,叫最大后验估计,简写MAP。意思很直白:在满足观测和运动约束的所有可能轨迹与地图组合里,挑一组概率最高的。

把后验概率最大化:

X*, M* = argmax_{X,M} p(X, M | Z, U)

这在贝叶斯世界观下很自然。但在工程上,概率连乘不方便,更不方便的是大量小数相乘容易下溢。于是取负对数,把乘变成加,最大变成最小。这一步几乎是所有概率算法转向实际代码的必经之路。

3.2 高斯噪声假设一进来,目标就变成了平方和

再往前走一步需要噪声假设。绝大多数SLAM系统假设噪声w_t和v_t服从零均值高斯分布。高斯分布取负对数之后,结果出奇地漂亮:一堆复杂的概率表达式变成了二次型。

于是最大化后验就等价于最小化这么一堆平方和:

X*, M* = argmin_{X,M} ( Σ_i || z_i - h(x_i, m_i) ||^2_{Σ_i} + Σ_j || x_j - f(x_{j-1}, u_j) ||^2_{Q_j} )

第一项是观测残差的平方和,第二项是运动残差的平方和。每一项都用自己的噪声协方差(Σ_i和Q_j)做了加权,噪声越不确定,权重越小。这个式子几乎是现代SLAM系统的"源代码"——无论你用G2O、Ceres、GTSAM还是自己手写优化器,底层都在解这个最小二乘问题。

从公式到这段转换的意义是什么?它把概率论问题变成了一个你熟悉的数值优化问题。而最小二乘的解法非常成熟,可以用信赖域、高斯牛顿、列文伯格-马夸尔特等一整套工具箱。这也是从业者真正打交道的地方:我们在代码里写的残差块、信息矩阵、Hessian增量方程,全部来自这里。

3.3 因子图:拆散整张大公式,问题好解很多

不过上面的求和式子还不是最终实现形态。整个SLAM问题规模很大,所有状态一起进出会让矩阵爆炸。于是大家用因子图来组织计算。

因子图的基本思想很朴素:把每一项残差看成一个"因子",因子只连接它涉及的变量。比如一个观测残差只连接某一个位姿x_i和地图中的某几个路标m_i,它不会连接所有状态。把因子画出来,就是一张有节点和边的图。变量是节点,因子是边。

这样做有两个巨大好处。第一,图结构天然稀疏,因为传感器每次观测覆盖的范围有限。第二,求解时可以利用稀疏线性代数加速,不需要每次都处理满矩阵。这就是为什么一亿个路标点的地图也能实时求优,不是计算器变聪明了,是问题被拆成了许多局部小问题,再用稀疏结构整合全局。

因子图还有一个副产品:它让SLAM领域各个模块的分工变得特别清楚。前端做特征匹配、帧间估计,本质是在构造观测因子;后端做全局图优化,本质是调整变量让所有因子的总残差最小。你的脑海里完全可以这样建模:SLAM就是维护一张不断增长的图,新因子代表新的观测,变量随着机器人的轨迹不断增删。

4. 公式之外的真实系统:稀疏性、边缘化和回环这三件事

4.1 稀疏性:地图能撑大的关键,也是你代码跑不快的分水岭

公式里求和项很多,每个因子只和局部变量相连,这带来一个天然的稀疏模式。但这个稀疏模式不是白给的,它需要你从数据结构和编码方式上去维护。

在我调试一个室内巡检Demo时遇到过这样的情况:给优化器塞了上千个关键帧位姿和数万路标点,理论上规模不大,但求解一次耗时超过一秒。查到最后发现,某个自定义残差残差实现里不小心把观测残差写成了"和所有地图点相关",把稀疏结构破坏了。去掉这一条错误逻辑之后,耗时直接降了一个数量级。

这个教训说明:公式告诉你每个残差该连接哪些变量,你就必须严格遵守。SLAM为什么会用滑动窗口、子地图、甚至体素地图?本质都是为了保持稀疏性,把计算量控制在可控范围内。反过来说,你接手一个卡顿系统时,第一个应该怀疑的就是有没有人无意中把某个因子连成了全连接。

4.2 边缘化:窗口滑走以后,旧信息不能真扔掉

机器人在环境中走很久,轨迹长度会一直增长。如果后验公式里的x_{1:t}无限变长,哪怕优化器再稀疏,迟早也会承受不住。工程上的通用解法是引入滑动窗口:只保留最近一段时间内的关键帧和路标点,更早的删掉。

但"删掉"不是简单移除变量。删掉一个变量,和它相连的因子也会消失,这等于丢掉了历史信息。信息虽然越老越不重要,但完全不要也会影响当前估计的一致性。所以正规做法叫边缘化:把被删变量从联合分布中积分掉,余下的信息折叠到剩余变量的先验约束里。

这个概念很像是团队里退休的老员工,虽然不能再全职干活,但把积累的经验写进了一份交接文档,后面工作时还要参考。边缘化的产物就是那些额外的先验因子,它们不会让你的求解变复杂太多,但对状态估计的效果有实际改善。你在不少VIO系统的代码里看到"marginalization factor"一类名词,做的事就是这个。

4.3 回环为什么重要:它给累积误差一次"对表"机会

公式里的误差是逐帧累积的。哪怕每一帧的匹配误差都很小,走一百米之后也可能漂出几十厘米。靠什么把这种累积误差压回来?答案是回环检测。

回环的本质是:当机器人回到曾经来过的一片区域时,当前观测能跟历史地图中的旧观测对上。这时你会多出一个强有力的因子——它直接把当前位姿和历史位姿连在一起。这个约束跨越了很长的时间轴,相当于在优化问题里加了一条"你现在的观测应该和五秒前的地点对齐"的硬核证据。

我特别想强调:很多入门者在建好前端匹配之后,就把回环当成可选模块。但你看一眼那个后验公式,回环带来的额外p(z|x,m)因子,往往是全局一致性最大的信息来源。没有回环的SLAM顶多算局部里程计累加。所以真正做SLAM系统时,我会先把回环检测与优化打通,再回头打磨前端细节,顺序反过来很容易做出一个"局部漂亮、整体漂移"的假完整系统。

5. 我按这个框架搭最小SLAM闭环的思路

5.1 一个最精简的视觉SLAM骨架

如果要我带着一个刚入门的人用几天时间跑通一个最简SLAM系统,我会让它长这样:第一步,对图像提取ORB特征点,并在连续帧之间做特征匹配;第二步,用对极几何或者本质矩阵估计两帧之间位姿变化,这部分本质是在构造运动因子;第三步,对匹配上的特征点做三角化,得到路标点的3D位置,这部分是在构造地图;第四步,把新帧的观测和已有路标点做重投影,计算重投影残差,交给后端做优化;第五步,检测当前帧是否和某个历史关键帧构成闭环,是就多插入一个回环因子,再触发一次全局优化。

请注意,这五个步骤里,第四和第五步做得事情,完全就是最小二乘公式在代码里的展开。重投影残差对应 || z_i - h(x_i, m_i) ||^2,回环因子也是同一类观测因子。形式上看,整条流水线就是在往因子图里持续加节点和边。

这个骨架示意简洁,颗粒度很细,但它是理解一切现代SLAM系统的地基。你去读ORB-SLAM也好、读LIO-SAM也好,看到他们费劲做的局部地图维护、共视关系管理、关键帧筛选,本质上都是在为这个最小骨架做加速和鲁棒性增强。

5.2 我的落地顺序:先写后端公式,再定前端方案

这里说一个我的个人习惯。很多人做SLAM Demo的时候,第一步先挑特征提取方法,第二步做匹配,然后才想后端怎么优化。我自己吃过这个顺序的亏。前端做的花里胡哨,最后发现后端优化框架承载不了那些残差,被迫返工。

后来我尝试倒过来:先把后端的最小二乘框架搭好,确认残差、信息矩阵、优化器的接口都稳定,再去选前端方案。后端框架一旦写清楚,前端只是"如何生成残差项"的问题。选ORB、选SuperPoint、选直接法,都不影响后端结构,只要曲线插进一个共同接口就行。这种可替换性让整个实验逻辑干净得多。

这一条经验本质上也是从公式里长出来的:**那个求和式的核心只关心"残差是什么、信息权重是多少",不关心特征长什么样。**所以你应该先锁住代价函数结构,再决定特征提取细节。

5.3 最容易抄错的地方:协方差矩阵与信息矩阵

最后给一个非常具体的坑:写残差的时候,一定要搞清楚权重矩阵到底是协方差还是信息矩阵。

马氏距离的用法是残差转置 × 信息矩阵 × 残差,而信息矩阵是协方差矩阵的逆。协方差越大代表信息越少,信息矩阵越小代表越不可信。我曾经在一个扫描匹配的残差实现里,直接拿协方差矩阵当权重用,结果发现大噪声的地方残差被放得很大,优化器拼命去迁就那些坏观测,整个轨迹被拉得不自然。改成信息矩阵之后,系统立刻稳定了。

还有个容易踩的暗坑是信息矩阵的稀疏性。如果你构造的信息矩阵是个稠密矩阵,解算器的性能会断崖式下降。SLAM的每一个约束都应该是局部的,信息矩阵呈带状或块状稀疏,求解才能快。你看到某些工业级DEMO跑到很稳,除了算法好,通常还因为它连矩阵填充顺序都对。这一块新手容易忽略,但直接决定系统能不能实时。

我一直觉得,SLAM的知识体系看着庞杂,核心却真的很集中。把联合后验公式想明白了,后面的采样、滑窗、回环、全局优化,全是给这个公式找更高效的近似解法。真把公式踩通之后,你会发现自己看其他变体——语义SLAM、动态环境SLAM、多机器人协同SLAM——都不再是一头雾水,因为它们有的只是往公式里加因子,有的只是把变量含义换一下。

最后分享一个我用了很久的思维习惯。每回在项目里卡住,比如地图变形了、回环闭合失败、或者轨迹漂移严重,我不会急着翻代码,而是先回到那句话、那个公式,问自己三个问题:我现在估计的变量是什么?我加入了什么信息让位姿和地图彼此约束?我是不是把某条旧约束错误地丢掉了?大部分问题,答案会自然浮出来。这个"回到本质"的习惯,比任何库和工具都更能救你。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询