1. 视觉SLAM到底在解决什么问题:先把需求想清楚再谈算法
聊视觉SLAM之前,我更愿意先说清楚它到底在干什么。一句话概括:让一台只有摄像头的设备,在完全陌生的环境里,一边走一边算出自己在哪儿、朝向哪,同时把周围环境的三维结构画出来。"在哪儿"叫定位,"周围什么样"叫建图,两件事耦合在一起同时求解,就是SLAM(Simultaneous Localization and Mapping,同步定位与建图)。加上"视觉"两个字,意味着主要传感器是相机——单目、双目、RGB-D,或者相机加IMU的组合。
它解决的问题很实在。GPS在室内、地下车库、隧道、高楼夹缝里基本废掉;轮式里程计打滑就漂;激光雷达贵且重。相机便宜、功耗低、信息量大,一个几百块的模组就能提供百万级的像素观测。所以从扫地机器人、仓储AGV,到无人机、AR眼镜,再到车载定位盒子,视觉SLAM都是绕不开的一块拼图。
这篇文章适合谁看?如果你刚翻完《视觉SLAM十四讲》的目录,知道李群李代数但不知道工程上怎么落地,那这篇是给你写的;如果你已经在RK3588或者i.MX95这类板子上跑过VINS,被时间戳和标定折磨过,那可以重点看第3、5、7章;如果你只是想评估"我的项目要不要上视觉SLAM",那第1章和第6章能帮你少走半年弯路。下面我不按教科书的顺序讲,而是按一个真实的项目从需求到落地的顺序拆。
1.1 把一帧画面拆开:从像素到六自由度位姿
假设相机在走廊里往前挪了半步。摄像头拍到的新画面和上一帧相比,墙角的边缘往画面外侧移动了若干像素,地砖的纹理被拉长了,远处的东西几乎没动。这个"近处动得多、远处动得少、沿着运动方向的点向外扩散"的模式,就是极几何要描述的东西。
工程上的做法是:先在两帧里找出一批能稳定复现的特征点(角点、边缘交点、斑点),把它们配对,然后解一个本质矩阵或者基础矩阵,恢复出两帧之间的旋转和平移。这一步只给出相对位姿和方向,单目还有个致命的坑——尺度不可观。也就是说,你算出"往前走了1个单位",但这个单位是米还是厘米,单目自己不知道。传统解法是靠初始化时的平移量人为定标,或者引入IMU、轮速、已知尺寸的物体来锚定尺度。
从像素回到三维,中间要穿过一串坐标变换:像素坐标 → 归一化平面 → 相机坐标系 → 机体坐标系 → 世界坐标系。每一层变换都有对应的参数和误差来源,这也是为什么第2章我要专门把相机模型讲透——标定不准,后面所有优化都是在优化一个错误的模型。
提示:很多人一上来就调ORB-SLAM的参数,结果跑出来尺度乱跳、轨迹歪斜,八成不是算法问题,是标定和时间戳的问题。先把这两件事做对,再谈调参。
1.2 为什么非要视觉:和激光、轮速、GPS的互补关系
我常被问"有激光雷达了还要视觉干嘛"。答案不是替代,是互补。
激光雷达给的是精确的几何深度,点云稠密、尺度天然可观测、不受光照剧烈影响;缺点是贵、重、在长走廊和空旷场地容易退化(几何特征单一,ICP配不准),而且它拿不到纹理和语义。视觉恰恰相反:便宜、轻、纹理信息丰富,能认出"这是门、这是货架",但在弱纹理白墙、强逆光、快速运动模糊的场景下会失效,单目还有尺度问题。
轮式里程计在平整地面上精度很高,但打滑、悬空、原地转圈就崩。IMU高频、短时精度好,能扛住快速旋转和短暂遮挡,但零偏会随时间积分漂移。GPS/RTK在室外空旷处能给绝对位置,进隧道就断。
所以现实中的方案基本都是多传感器融合:视觉提供环境约束和回环能力,IMU提供高频姿态和尺度先验,轮速或激光提供尺度与几何约束,GPS提供全局锚点。理解这一点,你才不会纠结"我的方案是不是纯视觉才正宗"——工程上没人这么较劲。
1.3 能力边界与三个常见误区
误区一:以为SLAM能给出绝对精度。SLAM本质是相对约束下的最大后验估计,它优化的是内部一致性。你把轨迹整体旋转5度,重投影误差照样很小。要绝对精度,必须有外部锚点。
误区二:以为建出来的地图可以直接导航用。稀疏特征点地图是给优化器用的,人眼根本看不懂;要让机器人避障规划,得额外做稠密重建或者栅格化、ESDF、语义地图,这是另一套工程。
误区三:以为换个更贵的相机就能解决问题。我见过团队花大价钱换全局快门相机,结果发现主要误差来自相机和IMU之间那几毫秒的硬件时间戳偏差。方向错了,钱白花。
注意:动手前先写一页纸,明确三件事——精度要求(厘米还是分米)、环境特征(纹理多不多、光照稳不稳)、算力预算(板子能跑多少帧、多少特征点)。这三条决定了你后面所有的技术选型。
2. 视觉SLAM的数学模型:相机模型、坐标系与状态估计
这一章是整篇文章里最"硬"的部分,但它决定了你在调试时能不能定位到根因。我尽量不用公式堆砌,而是讲清楚每个量在工程上的物理含义,以及你会怎么用到它。
2.1 针孔相机模型与内参矩阵:从三维点到像素
最基础的成像模型是针孔模型。空间点 P 在相机坐标系下的坐标是 (X, Y, Z),投影到归一化平面得到 (X/Z, Y/Z),再乘上内参矩阵 K 变成像素坐标 (u, v):
u = fx * X/Z + cx v = fy * Y/Z + cy四个参数的含义必须记牢:fx、fy 是焦距在像素单位下的表示(物理焦距除以像元尺寸),cx、cy 是光心在图像上的像素坐标。理想情况下 cx、cy 接近图像宽高的一半,但实际模组装配有偏差。
这里有个容易忽略的点:Z 做分母。Z 越小(物体越近),同样的三维误差在像素上的放大倍数越大。这就是为什么近处的噪声点会严重污染优化结果,也是为什么前端做特征匹配时要设一个深度上限,把太近的点剔掉。
内参从哪来?标定。棋盘格或者圆点板,采集十几到几十张不同姿态的图像,用张正友标定法解。OpenCV 里calibrateCamera一把梭,但前提是采集质量要好:棋盘格要铺满画面的各个区域(四角尤其重要,畸变在边缘最严重),姿态要覆盖俯仰、偏航、滚转多个角度,不能全是一个平面平移。
实测经验:至少采集 20 张,覆盖画面 80% 以上区域,重投影误差控制在 0.3 像素以内算合格,超过 0.5 像素就重拍。别嫌麻烦,这一步花两小时,后面能省两天。
2.2 畸变模型与标定实操:径向畸变和切向畸变
真实镜头不是理想针孔,光线穿过镜片会产生弯曲。主要两类:
- 径向畸变:由透镜形状引起,画面边缘的直线会向外鼓(桶形)或向内收(枕形)。用 k1、k2、k3 三个系数描述,通常 k1 起主导作用。
- 切向畸变:由镜头和成像平面不平行引起,用 p1、p2 描述。现在模组工艺好了,切向畸变一般很小,但广角镜头上仍明显。
标定的时候有个取舍:用几个畸变系数。用太多(k1 k2 k3 p1 p2)容易过拟合,用太少边缘残差大。我的经验是:普通视角镜头(FOV 60 度以内)用 k1 k2 p1 p2 就够;鱼眼或广角(FOV 120 度以上)得用鱼眼模型(equidistant)或者加 k3,否则去畸变后边缘还是弯的。
另外一个高频坑:标定和运行时的分辨率必须一致。你用 1920×1080 标定,运行时改成 640×360 跑,内参和畸变系数全都不对了。必须按比例缩放 fx、fy、cx、cy,畸变系数不变。
# 分辨率缩放时内参的换算(1920x1080 -> 640x360,缩放因子 1/3) scale = 640.0 / 1920.0 fx_new = fx * scale cx_new = cx * scale # fy、cy 同理 # 畸变系数 k1 k2 p1 p2 k3 保持不变2.3 位姿怎么表示:旋转矩阵、四元数与李群李代数
位姿 = 旋转 + 平移。旋转有三种常见表示:
| 表示方式 | 参数个数 | 优点 | 缺点 |
|---|---|---|---|
| 旋转矩阵 R | 9 | 直接可作用于向量,无奇异 | 有正交约束,优化时需额外处理 |
| 欧拉角 | 3 | 直观 | 万向节死锁,插值不自然 |
| 四元数 | 4 | 无奇异,插值平滑,存储紧凑 | 有单位模长约束,符号二义 |
| 旋转向量(轴角) | 3 | 无约束,适合做优化变量 | 大角度时数值敏感 |
优化的核心矛盾在这里:旋转矩阵和四元数都有约束(正交、单位模长),带约束优化很难做;旋转向量没有约束,但它对旋转的表示不唯一、在 ±π 附近有奇异。李群李代数就是来解决这个矛盾的。
李群 SO(3) 是合法的旋转集合,李代数 so(3) 是它对应的切空间(一个三维向量空间)。核心结论:在单位元附近,旋转矩阵和旋转向量是一一对应的(指数映射),你可以对李代数做加法、求导、优化,然后指数映射回李群,自然满足约束。SE(3) 同理,处理旋转加平移。
工程上你不需要手推雅可比,但要知道:Sophus、g2o、Ceres都封装好了,你只需要理解"优化变量在流形上更新"这件事——更新量是李代数上的一个小增量 δ,而不是直接加到旋转矩阵的九个元素上。
为什么这件事重要?因为它直接决定了你的优化能不能收敛。早期我试过直接把四元数四个分量当自由变量丢给优化器,不加约束,结果迭代几十次后四元数模长飘到 1.3,轨迹直接乱飞。
2.4 重投影误差与最小二乘:整个优化框架的心脏
视觉SLAM几乎所有后端优化都围绕重投影误差展开。定义很朴素:一个三维地图点被观测到,通过当前估计的位姿投影到图像上,和实际检测到的像素位置之间的差,就是重投影误差。
e = z_observed - π(T, P)其中 π 是投影函数(含内参和畸变),T 是待估位姿,P 是世界坐标下的地图点。
整个问题变成:找一组位姿和地图点,让所有观测的重投影误差平方和最小。这就是非线性最小二乘,用高斯牛顿或者列文伯格-马夸尔特(LM)迭代求解。
这里要讲清楚BA(Bundle Adjustment,光束法平差):把位姿和地图点一起优化。数学上它对应一个巨大的稀疏矩阵,因为每个观测只关联一个位姿和一个点,所以雅可比矩阵极其稀疏。工程上用 Schur 补消元,先解位姿,再回代求点,复杂度从 O(n³) 降到可控。
一个实操心得:别一上来就全局 BA。地图点几万个、关键帧几百个的时候,一次全局 BA 在某些板子上要跑十几秒。正确做法是滑动窗口局部 BA(只优化最近若干关键帧和它们看到的点),加边缘化(把老状态消掉但保留其信息),全局 BA 只在回环后触发一次。ORB-SLAM 和 VINS 都是这个套路。
3. 算法框架拆解:前端、后端、回环与建图
现在把整套系统拆成四大模块。这个划分方式不是学术分类,而是工程上真实解耦的结果——每一块可以单独替换、单独调参、单独定位问题。
3.1 前端视觉里程计:特征点法、直接法、半直接法怎么选
前端负责从图像里提取约束,输出相邻帧之间的相对运动估计和一部分地图点。三条技术路线各有各的脾气。
特征点法:提取角点(ORB、SIFT、SURF、SuperPoint),做描述子匹配,然后解极几何或者 PnP。优点是成熟、鲁棒、对光照变化有一定容忍度、能直接接回环检测的词袋模型。缺点是特征提取本身耗时(ORB 在 ARM 上提 1000 个点大概几毫秒到十几毫秒),且在弱纹理场景下提不出足够的点。ORB-SLAM 系列是这条路线的代表。
直接法:不提取特征,直接假设"同一个空间点投影到两帧上的灰度应该一样"(灰度不变假设),最小化光度误差。优点是能用上所有像素信息,弱纹理场景表现好,速度快。缺点是对光照变化极其敏感,要求相机曝光必须锁死、自动增益关掉,且需要较好的初始值,否则很容易掉进局部极小。DSO、LSD-SLAM 是代表。
半直接法:提取特征点但不算描述子,只用来做光流跟踪和位姿初估,然后用直接法优化光度误差。SVO 是典型代表,速度极快,在无人机上很受欢迎。缺点是回环和重定位能力相对弱,早期版本甚至没有后端。
我的选型建议很直接:
- 板子算力一般(如 RK3588 单核跑)、场景纹理丰富、需要长期稳定建图 →特征点法
- 场景纹理弱(白墙、金属地面)、相机曝光可控、追求高帧率 →直接法
- 无人机、对延迟极敏感、不需要大范围重定位 →半直接法
实操禁忌:用直接法千万别开相机的自动曝光和自动白平衡。这两个功能会让同一场景的灰度随环境变化,直接破坏灰度不变假设,算法直接崩。上机第一件事就是锁死曝光时间和增益。
3.2 后端:滤波还是非线性优化
后端负责把前端零散的约束整合成一致的轨迹。两大流派:
滤波方法:以 EKF-SLAM、MSCKF 为代表。核心思想是递归地维护状态和协方差,来一帧更新一次。优点是计算量恒定,内存可控,适合嵌入式。MSCKF 把地图点边缘化掉,只保留滑动窗口内的相机位姿,是 VIO 里的经典。
非线性优化方法:以 BA 为代表。每次优化一个窗口内的所有状态,可以多次迭代,精度更高。缺点是计算量随窗口增长,需要控制规模。
现在的主流是优化方法,原因有二:一是关键帧 + 滑动窗口 + 边缘化这套组合拳把计算量压下来了;二是优化能多轮迭代、能处理非高斯噪声、能做鲁棒核函数抗外点。滤波方法受限于一阶线性化,在大不确定度场景下精度明显吃亏。
但这不代表滤波没用。在算力极其受限的平台上,MSCKF 依然是很好的选择,它比完整 BA 轻得多。选型时要看你的板子到底是几个核、有没有 NPU 帮忙。
3.3 回环检测:让轨迹不"漂成一个圈"
没有回环检测的 SLAM 只能叫里程计。你绕着一栋楼走一圈回到起点,纯里程计的估计位置会离起点几米远,因为累积误差从不修正。回环检测就是"认出这个地方我来过",然后加一条全局约束,把整条轨迹拉回来。
主流做法是词袋模型(BoW)。先把大量图像特征聚类成"视觉单词"(视觉词典),生成一个大的树形结构。每帧图像的特征点查词典,得到一串单词,构成一个向量。两帧的相似度就是这两个向量的距离。DBoW2/DBoW3 是最常用的实现,ORB-SLAM 就靠它。
但纯词袋会误检——走廊、重复的货架、相似的房间,都会给出高相似度。所以必须加几何一致性校验:候选回环帧和当前帧之间,做一次特征匹配 + 估计相对位姿,如果内点数够多、位姿合理,才认为回环成立。
深度学习方法这几年也进来了:NetVLAD、用 CNN 提取全局描述子。优点是能抗光照和视角变化,缺点是模型大、推理慢,在 RK3588 这种板子上要跑 NPU 才划算。我的建议是,如果场景是固定的、光照可控的,传统词袋足够;如果是室外、跨季节、光照剧变,再考虑学习类方法。
回环成立后加什么约束?两种:位姿图优化(Pose Graph Optimization)或全局 BA。位姿图只优化关键帧位姿,把地图点边缘化掉,速度快、适合在线;全局 BA 精度更高但更重,一般在建图结束后离线跑一次。
3.4 建图:你要的到底是什么地图
"建图"这个词被用得太泛了,实际项目里它至少分四种,需求完全不同:
| 地图类型 | 内容 | 用途 | 典型方案 |
|---|---|---|---|
| 稀疏点云 | 几百到几千个特征点 | 给优化器提供约束 | ORB-SLAM 自带 |
| 稠密点云/网格 | 百万级点、三角网格 | 三维重建、可视化 | DSO、MVS、TSDF |
| 栅格地图 | 二维占据栅格 | 机器人导航避障 | 点云投影 + 占据栅格 |
| 语义/拓扑地图 | 带标签的对象 + 节点连接 | 高层任务理解、路径规划 | 检测网络 + 地图融合 |
很多团队踩的坑是:用稀疏点云地图去做导航规划,结果机器人不知道怎么绕障碍。稀疏点云只是优化副产品,不是给人用的地图。要导航,必须做稠密化或者栅格化。
做稠密地图在嵌入式上代价不小。RGB-D 相机可以直接拿到深度,相对轻松;单目靠三角化只能得到稀疏深度,要做深度估计网络或滤波才能稠密化,算力压力大。如果板子只有 RK3588 级别的算力,我一般建议:二维栅格化 + 少量语义标签,性价比最高。
4. 主流开源方案怎么选:从 ORB-SLAM 到 VINS 的实战对照
纸上谈完,落到"我到底用哪个开源库"。这一章把我实际跑过、帮人调过的方案做个横向对比,包括各自的坑。
4.1 单目/双目/RGB-D 的经典方案盘点
ORB-SLAM2 / ORB-SLAM3:目前最成熟的视觉 SLAM 开源框架,支持单目、双目、RGB-D,还有视觉惯性和多地图合并(ORB-SLAM3)。优点是模块清晰、回环重定位完整、社区活跃、资料多。缺点是代码风格偏老、编译依赖重(要求 OpenCV、Eigen、Pangolin、DBoW2 版本匹配),在嵌入式上跑要裁剪。我的经验是:功能验证用 ORB-SLAM3,落地部署用裁剪版或者 VINS。
VINS-Mono / VINS-Fusion:视觉惯性紧耦合方案,单目+IMU 或双目+IMU。香港科技大学沈劭劼团队出品,在无人机和移动机器人上应用极广。核心是滑动窗口 + 边缘化 + 紧耦合优化 + 在线外参标定 + 回环。优点是精度高、能在线估计 IMU 零偏和外参,对时间戳同步要求相对宽容。缺点是初始化需要一段足够的激励运动(要动起来才收敛),静止启动会失败。
DSO:直接法代表,单目。速度极快、弱纹理表现好。缺点是没有回环、尺度不可观、对曝光敏感、代码可读性一般。
OpenVINS:MSCKF 的现代实现,滤波路线,适合算力受限的平台。精度不如优化方法,但资源占用低。
Basalt:基于非线性优化的 VIO,用了一堆先进技巧(如非参数化光度标定),性能不错,但生态和资料相对少。
4.2 视觉惯性紧耦合:为什么 IMU 是视觉的好搭档
先说清楚"松耦合"和"紧耦合"。松耦合是把视觉估计的位姿和 IMU 积分结果用滤波做加权融合,当成两个独立测量;紧耦合是把 IMU 预积分残差和视觉重投影残差放进同一个优化问题里一起解。
紧耦合的好处是信息互相修正:视觉帮 IMU 估零偏,IMU 帮视觉提供尺度、姿态先验、抗短暂遮挡。代价是系统更复杂、调参更多、初始化更难。
IMU 预积分是个关键技巧。原始 IMU 数据是 200Hz 甚至 1000Hz,如果每次优化都把上千个测量重新积分一遍,计算量爆炸。预积分就是把两个关键帧之间的 IMU 测量预先积成一个相对运动约束,优化时直接用它,不用重新积分。这个约束还给出了协方差,用作优化的信息矩阵。
启动流程一般是:先等 IMU 静止几秒估计零偏(视觉静止判据),然后要求设备做一段有平移和旋转的激励运动,用视觉的结构和 IMU 的积分结果做对齐,解出尺度、重力方向、初始速度。这段初始化如果不成功,后面全是白费。
常见坑:很多人拿手机或者小设备做测试,初始化时只是拿在手里轻微晃,平移激励不足,尺度估不准。要么放在小车上走一段,要么手持做明显的"画八字"动作。
4.3 和激光/轮速融合:LIO 路线与多源组合
纯视觉在城市道路、隧道、空旷场地都有失效风险,所以现在车载和机器人上流行LIO(LiDAR-Inertial Odometry)+ 视觉回环的组合。典型是 LIO-SAM、FAST-LIO2 加视觉做回环检测。激光保证几何精度,视觉保证长时间不漂。
车载场景还有 TBox 加组合导航的方案:TBox 拿到车辆总线上的轮速、方向盘转角,加上惯导和视觉,做组合定位。这种方案的好处是能拿到"绝对位置"锚点(RTK 可用时),在隧道里靠视觉和 IMU 短时递推顶过去。
选型对照表:
| 方案 | 传感器 | 精度 | 算力需求 | 回环 | 适合场景 |
|---|---|---|---|---|---|
| ORB-SLAM3 | 单/双/RGB-D + IMU | 高 | 中高 | 有 | 通用、建图、研究 |
| VINS-Fusion | 单/双目 + IMU | 高 | 中 | 有 | 无人机、移动机器人 |
| DSO | 单目 | 中 | 低 | 无 | 弱纹理、快速场景 |
| OpenVINS | 单/双目 + IMU | 中 | 低 | 无 | 嵌入式、算力受限 |
| FAST-LIO2 + 视觉回环 | 激光 + IMU + 相机 | 很高 | 高 | 有 | 车载、大场景 |
4.4 选型时的三个自问
第一问:我的场景有 IMU 吗,能同步吗?有 IMU 且时间戳能对齐,优先 VIO;没有 IMU,只能纯视觉,那就要接受尺度问题和更大的漂移。
第二问:我要不要回环?如果只在局部区域来回运动(比如机械臂抓取、桌面级 AR),可以不要回环,简单很多。如果要大范围长时间运行,回环是刚需。
第三问:我的板子能跑多少?这个最容易被低估。ORB-SLAM3 在 x86 台式机上跑 30fps 很轻松,但在 ARM 板子上,特征提取 + 描述子匹配 + 局部 BA 加起来很容易超过 33ms。要么降分辨率、降特征点数,要么换更轻的方案。
5. 嵌入式部署实战:RK3588 与 i.MX95 上跑视觉 SLAM 的注意点
这一章讲落地。论坛上讨论最多的板子就是 RK3588 和 i.MX95 这类,前者有 6TOPS 级别的 NPU 和强劲的多核 CPU,后者偏向车规和工业场景。两台我都碰过,坑也踩了不少。
5.1 算力与接口盘点:为什么这些平台能跑视觉 SLAM
RK3588 是 4 核 A76 + 4 核 A55,带 NPU 和较强的 GPU。对视觉 SLAM 来说,几个关键点:
- CPU 单核性能决定前端特征提取和匹配的速度。ORB 提取是单线程为主,所以高主频比多核更有用。把前端绑到 A76 大核上,实测能比绑小核快 40% 以上。
- NPU可以拿去跑深度特征提取、深度估计、语义分割,把 CPU 腾出来做优化。
- MIPI CSI 接口数量决定你能接几个相机。做双目或者多目全景,必须提前数清楚有几路 CSI 和能不能同时用。
- 内存带宽在做稠密建图时是瓶颈,图像拷贝、去畸变、金字塔构建都吃带宽。
i.MX95 这类偏车规的芯片,特点是接口丰富、温度范围宽、功能安全相关设计更完整,适合车载 TBox 和工业设备。视觉 SLAM 上它的绝对算力可能不如 RK3588,但胜在稳定性和长期供货。
5.2 相机接入与时间同步:IIC/SPI 用在哪里
这一块很多人搞混。先分清楚:
IIC(I2C):两根线(时钟+数据),多设备共享总线,靠地址区分。速度一般几百 kHz 到 1MHz。在相机系统里,IIC 主要用来配置摄像头模组的寄存器——设置分辨率、帧率、曝光、增益、输出格式。它传的是控制命令,不是图像数据。
SPI:四根线(时钟、主出从入、主入从出、片选),速度可以到几十 MHz。在相机系统里,SPI 有时用来传配置,但更多用在外围器件:IMU(很多 IMU 支持 SPI 和 IIC 两种接口)、闪存、显示屏。SPI 速率高但引脚多、不适合挂很多设备;IIC 引脚少但慢、总线负载有限。
实际选型经验:IMU 优先用 SPI。因为 IMU 输出频率高(200Hz 到 1kHz),IIC 在某些实现下会因为总线仲裁和中断延迟导致采样抖动,时间戳不稳。SPI 全双工、速率高、时序更确定,采样抖动小得多。相机配置寄存器用 IIC 就够,因为配置是一次性的,不要求实时。
时间同步是 VIO 的命门。硬件上理想做法是:用一个外部信号同时触发相机曝光和给 IMU 打时间戳(比如 MCU 输出同步脉冲),这样两路数据的时间基准天然一致。软件上退而求其次,是用相机驱动的时间戳(一般来自 VSYNC 中断)和 IMU 的时间戳做对齐,但要注意:
- 相机时间戳可能是"帧开始曝光"也可能是"帧传输完成",差一帧的曝光时间,几十毫秒的误差足够毁掉 VIO。
- IMU 时间戳如果是驱动收到数据才打的,会带上总线传输和系统调度的抖动。
- 两个时钟源如果来自不同晶振,还会有频率偏差,长时间运行会漂。
血泪提醒:如果你的 VIO 跑起来姿态总是有点歪、快速旋转时误差大,先去查时间戳。把相机和 IMU 的数据录下来,用同一段运动做离线对齐,看看最优时间偏移是多少。我遇到过一次,硬件上差了 25ms 没发现,调了两周算法都没用。
5.3 性能优化:从分辨率到算子加速
板子上跑不动的时候,按下面顺序优化,从便宜到贵:
- 降分辨率。从 1280×720 降到 640×480,前端耗时直接降一半以上。代价是远距离特征点变少,精度略降。但对室内机器人和多数场景,640×480 完全够用。
- 限制特征点数量和分布。ORB-SLAM 默认每帧提 1000 到 2000 个点,实际室内场景 500 个就够。做网格化均匀提取,避免所有点挤在纹理丰富的角落。
- 调低后端频率。局部 BA 不必每帧都跑,可以隔一两帧跑一次,或者在关键帧到来时才跑。位姿递推用前端的速度就够了。
- 图像金字塔层数减少。ORB 默认 8 层金字塔,构建金字塔很耗时。降到 4 层,对精度影响有限。
- 把去畸变和金字塔构建放进 GPU/NPU,或者在相机 ISP 里直接输出已校正的图像。
- 多线程注意线程局部存储。前端跟踪和后端优化通常跑在不同线程,临界区加锁会影响实时性。一些不共享的中间缓冲区(比如每次迭代的临时向量、每个线程的特征容器)可以用
thread_local声明,避免反复分配和锁竞争。这个小技巧在多线程 SLAM 里挺实用。
// 每个跟踪线程独立的临时缓冲区,避免跨线程加锁 thread_local std::vector<cv::KeyPoint> local_keypoints; thread_local std::vector<cv::Mat> local_descriptors; void trackFrame(const cv::Mat& img) { local_keypoints.clear(); local_descriptors.clear(); extractor->detectAndCompute(img, cv::noArray(), local_keypoints, local_descriptors); // ... 后续处理,全程不涉及共享状态 }5.4 实测记录与调参心得
拿一台 RK3588 板子,单目 640×480 30fps,跑裁剪过的 ORB-SLAM 风格前端 + 轻量后端,实测数据大概是:特征提取与匹配 8 到 12ms,局部 BA(窗口 10 帧)15 到 25ms 且只在关键帧触发,回环检测用独立的低频线程(1Hz),主线程平均负载 40% 到 60%。这个余量足够再加一路语义分割。
几个调参心得:
- 特征点阈值别一次调到位。先跑默认值,看轨迹和重投影误差,再逐步调整。一次性改三个参数,出了问题根本不知道是哪个。
- 关键帧插入策略影响巨大。插太密,后端吃不消;插太稀,跟踪容易丢。常用判据是:当前帧和上一关键帧的共视点少于阈值,或者平移超过一定比例,就插一帧。
- 回环检测独立线程 + 降频。1Hz 足够,因为它只是偶尔修正全局,没必要每帧都查。
- 看 CPU 占用要分核看。
top看到的平均占用可能只有 50%,但某个大核已经跑满,实时性照样崩。用htop或者mpstat -P ALL看每个核。
6. 应用场景全景:从扫地机到车载定位盒子
技术最终要落到场景里。这一章把主要的应用形态过一遍,每类场景我会说清楚它的核心技术诉求,因为不同场景对 SLAM 的要求差异极大。
6.1 移动机器人与 AGV/AMR:稳定大于精度
扫地机器人、仓储 AGV、服务机器人是视觉 SLAM 最大的落地市场。这类场景的特点是:运动慢、地形平、环境相对结构化、成本敏感、要求长时间无故障运行。
核心技术诉求是稳定,不是极限精度。厘米级到分米级的定位精度,配合轮速里程计做融合,就足够导航了。多数产品用的是视觉 + IMU + 轮速 + 激光(低端用结构光或者 ToF)的组合,纯视觉单打独斗的少。
一个关键设计是重定位能力。机器人在充电桩待机后重新启动,必须能立刻知道自己在地图的哪个位置,不能每次都重新建图。这要求地图持久化存储 + 全局重定位(词袋检索 + PnP)。ORB-SLAM3 的多地图和重定位功能在这里很有价值。
另一个是动态环境。仓库里有人走动、有叉车经过,这些动态物体如果被当成静态特征,会严重污染位姿估计。常见做法是加一个动态点剔除模块:用语义分割识别出人、车,把落在这些区域的点排除;或者用几何方法(如对极约束下残差过大的点)剔除。
6.2 无人机与手持设备:快、轻、抗抖动
无人机上的视觉 SLAM 要满足三个字:轻、快、稳。重量上每克都有代价,功耗直接影响续航,所以算法要极简。SVO 类的半直接法在这里有优势,速度快、算力需求低。
无人机场景的特殊难点是运动剧烈:快速旋转、大机动、图像模糊。这要求 IMU 融合必须紧耦合,且 IMU 的性能要好(消费级 IMU 的零偏噪声大,得做在线估计)。另外无人机的振动环境对相机和 IMU 的机械固定要求很高,松动了外参就变了。
手持设备和穿戴设备(AR 眼镜)的特点是运动不确定、场景变化快、要求低延迟。AR 场景对延迟极其敏感,超过 20ms 的定位延迟就会让人觉得虚拟物体"飘"。这类设备往往用视觉惯性里程计做前端,配合预先构建或者实时构建的稀疏地图做重定位。
6.3 AR/VR 与三维重建:地图质量的战场
AR/VR 对 SLAM 的要求和机器人不一样:它不需要导航,需要的是高精度的位姿 + 可用的空间地图(用于遮挡、碰撞、放置虚拟物体、平面识别)。
位姿精度上,AR 追求的是视觉上的"稳",即虚拟物体牢牢贴在真实表面上,不能有抖动和漂移。为此要做的是局部地图的高频优化 + 位姿的平滑滤波。地图上,需要平面检测、语义理解(这是桌子、这是地板、这是墙),才能让交互合理。
三维重建是另一个方向:用 SLAM 提供的位姿把多视角图像融合成稠密点云或网格,用于数字孪生、文物数字化、房产建模。这类应用通常可以离线处理,所以可以用更重的算法(如 MVS、NeRF 类方法),在线只需要采集图像和位姿。
这里的工程重点是全局一致性。重建一栋楼,如果轨迹有 1% 的漂移,首尾接不上,模型就会扭曲。所以必须做回环 + 全局 BA + 位姿图优化,必要时还要人工加约束(比如已知某些点共线、某个面是平面)。
6.4 车载 TBox 与组合导航:视觉作为冗余
车载场景这两年讨论比较多的是 TBox 加组合导航的定位方案。TBox 是车上的通信与计算盒子,能拿到车辆总线的信息(车速、轮速、方向盘角度、档位),加上 IMU、GNSS,有时还接一路相机做视觉辅助。
为什么车载要视觉?主要是GNSS 失效时的补位:地下车库、隧道、城市峡谷。这时候靠 IMU 递推会很快漂移,轮速在打滑时不可靠,视觉能提供额外的环境约束,把漂移拉慢。另外,视觉还能帮忙检测车道线、识别地面标志,做地图匹配。
车载场景的视觉 SLAM 有几个特殊要求:一是大场景、长时间,需要频繁回环和地图管理;二是光照剧变,从隧道出来瞬间曝光从暗到亮,直接法会崩,特征点法相对抗打;三是功能安全,不能出现定位跳变导致下游控制误动作,所以通常要做多源一致性校验,视觉结果和 IMU/GNSS 差异过大时,降权或者丢弃。
视觉在车载定位里通常扮演冗余和辅助的角色,而不是主定位源。这个大方向要认清,别把视觉当成能替代 RTK 的东西。
7. 常见问题与排查技巧实录
最后这一章是最实用的部分,都是我在实际项目里踩过的坑和总结的排查路径。
7.1 初始化失败:VIO 启动不收敛怎么办
症状:启动后一直卡在初始化阶段,或者初始化成功了但尺度明显不对,走几步就发散。
排查顺序:
- IMU 静止判据是否满足。多数 VIO 需要静止几秒估零偏。如果设备一直在动,或者振动大(比如放在运行中的电机旁边),判据过不去。实际做法是启动后先放稳,看日志里零偏估计是否收敛。
- 激励是否足够。初始化需要足够的平移和旋转。拿着设备原地转圈不算,因为平移几乎为零,尺度不可观。要拿着走几步,或者放在小车上走一段直线加转弯。
- 时间戳偏移是否过大。前面说过,超过十几毫秒的偏移会让初始化失败。用离线工具(很多 VIO 项目自带)估计时间偏移。
- 外参是否合理。相机和 IMU 的相对位姿如果给得太离谱(比如旋转差 90 度),初始化根本对不上。有在线外参估计的方案能自适应,但初值也不能太离谱。
- 图像质量。曝光过度、运动模糊、图像太暗,特征提不出来,初始化自然失败。
7.2 跟踪丢失与尺度漂移:运行中的典型故障
跟踪丢失是最常见的故障。原因可能是:画面里特征太少(对着白墙)、快速运动导致模糊、遮挡(有人走过)、光照突变。
工程上的应对是多级降级策略:跟踪正常时用局部地图;跟踪变差时降低对地图点的依赖,切换到纯帧间跟踪;彻底丢失时进入重定位模式(词袋检索 + PnP);重定位失败则重置。这个状态机必须提前设计好,不能等出问题再补。
尺度漂移是另一类问题。VIO 的尺度靠 IMU,如果 IMU 标定不准、零偏估计不准,尺度会慢慢变化。表现是走一段直线后,地图整体缩小或者放大。排查方向:检查 IMU 的噪声参数(零偏随机游走、白噪声)是否和实际器件匹配。这些参数通常来自 Allan 方差标定,用错参数会导致滤波器"过度自信"或者"过度怀疑"。
实操技巧:用 Allan 方差工具(很多 IMU 厂商提供)标定 IMU,得到零偏不稳定性、随机游走等参数,写进配置文件。凭经验拍脑袋填参数,是很多 VIO 调不好的根源。
重投影误差突然变大:一般是出现了外点或者位姿估计错误。检查是否开启了鲁棒核函数(如 Huber、Cauchy),核函数能有效压制外点影响。另外检查动态物体剔除逻辑是否生效。
7.3 问题速查表
| 现象 | 可能原因 | 排查动作 |
|---|---|---|
| 初始化一直不成功 | 激励不足、时间戳偏移、IMU 零偏未收敛 | 走一段明显运动、离线估时间偏移、静止等待 |
| 轨迹整体偏斜 | 外参不准、时间戳偏差 | 重标定外参、估计时间偏移 |
| 尺度忽大忽小 | IMU 参数不匹配、尺度可观性差 | Allan 方差标定、增加平移激励 |
| 跟踪频繁丢失 | 纹理少、运动快、曝光不当 | 检查图像质量、降运动速度、锁曝光 |
| 回环误检 | 场景重复、几何校验不足 | 提高相似度阈值、加内点校验 |
| 地图重影 | 回环未触发、位姿图未优化 | 检查回环模块、跑全局 BA |
| 板子上卡顿 | 特征点太多、分辨率过高、后端频率高 | 降分辨率、减特征点、降后端频率 |
| 位姿跳变 | 外点污染、协方差估计过小 | 开鲁棒核、检查信息矩阵 |
我个人在实际项目中的体会是,视觉 SLAM 的调试有 70% 的时间花在"数据没对齐"和"标定不准"这两件事上,真正算法层面的问题反而少。所以我的习惯是,接手一个新项目先不看算法,先把相机标定、IMU 标定、时间同步这三件事做干净,把录制的数据写成TUM或者EuRoC格式,用第三方工具(如evo)算轨迹误差,把基线建立起来。基线不清楚,任何"优化"都只是碰运气。
另外分享一个小技巧:录制数据的时候,尽量设计几种典型运动——静止、慢速平移、原地旋转、快速转弯、经过弱纹理区域、经过强光区域。每种录两三分钟。这样当算法出问题时,你能快速定位到"是哪类运动导致的",而不是在一大段杂乱数据里大海捞针。这套自建数据集,比任何公开数据集都更贴合你的场景,也更有诊断价值。