VINS-Fusion与VINS-Mono核心技术差异解析:从单目到多传感器的演进
1. 从单目到多传感器:VINS的进化之路
如果你玩过无人机或者研究过自动驾驶,肯定对“定位”和“建图”这两个词不陌生。简单来说,就是让机器知道自己在哪里,周围环境是什么样。在GPS信号弱或者没有GPS的室内、地下车库、城市峡谷,视觉惯性里程计(VIO)就成了机器的“眼睛”和“内耳前庭”。而VINS-Mono和它的升级版VINS-Fusion,就是VIO领域里两座绕不开的里程碑。
我最早接触VINS-Mono是在2018年,当时为了给一个室内机器人项目做定位,试了好几个开源方案,VINS-Mono以其在单目+IMU配置下的稳定性和开源代码的清晰度脱颖而出。但用久了就会发现,纯单目的方案有个天生的“硬伤”:尺度不确定。虽然IMU能帮忙估计出尺度,但这个初始化过程又慢又容易翻车,在纹理单一或者快速旋转的场景下,十有八九会初始化失败。更别提纯视觉在快速运动或遮挡时容易跟丢,那种看着轨迹突然飘走或者地图突然扭曲的感觉,真是让人头大。
所以当2019年初看到港科大开源VINS-Fusion时,我第一时间就跑去试了。它的口号很直接:一个支持多种传感器组合的、基于优化的状态估计器。说白了,就是从VINS-Mono的“单目+IMU”这一条路,拓展成了“单目+IMU”、“纯双目”、“双目+IMU”甚至“双目+IMU+GPS”多条路。这种从单一传感器到多传感器融合的演进,不仅仅是功能的堆砌,其背后是一整套技术思路的升级,尤其是在特征点三角化、系统初始化和重投影误差模型这几个核心环节上,改动是颠覆性的。
这篇文章,我就结合自己这几年在无人机和机器人项目上的实战经验,掰开揉碎了讲讲VINS-Mono和VINS-Fusion到底有哪些不同。我不会堆砌复杂的公式吓跑你,而是用最直白的话和实际的代码例子,告诉你这些技术差异在实际应用中意味着什么,以及你应该如何根据项目需求去选择。无论你是刚入门SLAM的新手,还是正在为项目选型纠结的工程师,相信都能找到有用的信息。
2. 核心差异一:特征点三角化——从时序到空间的跨越
三角化,是视觉里程计的基石。它的目标很简单:通过多视角观测,把一个图像上的2D像素点,还原成它在3D空间中的位置。但“如何三角化”,VINS-Mono和VINS-Fusion走了两条完全不同的路。
2.1 VINS-Mono:在时间序列中“猜”深度
VINS-Mono是单目系统,它只有一只“眼睛”。单目相机无法直接感知深度,就像一个独眼的人,很难判断物体的远近。那VINS-Mono怎么解决呢?它的策略是利用时间序列。
具体来说,VINS-Mono的三角化发生在一帧图像和它的前一帧之间。它假设场景是静态的,然后通过特征点跟踪(光流),找到同一个点在连续两帧图像上的位置。有了这两个2D点,以及通过IMU预积分估计出的这两帧相机之间的相对运动(旋转和平移),理论上就能解算出这个点的3D位置。
这个过程在代码里,主要体现在 feature_tracker 的光流跟踪和 estimator.cpp 中对特征点的处理上。但这里有个大问题:依赖连续的、成功的跟踪。如果跟踪断了(比如因为运动模糊、遮挡),或者两帧之间的平移量太小(俗称“纯旋转”),三角化就会失败或者产生极大的误差。更麻烦的是,这个三角化出来的3D点,其尺度是依赖于IMU预积分估计的位移尺度的。如果IMU的偏差估计不准,或者初始化没做好,整个地图的尺度都会错。
我在实际项目中就踩过这个坑。让机器人做原地旋转,单目VIO的轨迹在高度方向(z轴)上就开始“放飞自我”,飘得没边了。因为纯旋转时三角化条件恶劣,深度估计极不可靠,误差会不断累积。
2.2 VINS-Fusion:用双目的“双眼”直接测量
VINS-Fusion在支持双目模式后,三角化的逻辑发生了根本变化。它不再需要依赖时间上连续的两帧,而是利用同一时刻、空间上分开的左右目相机。
在同一时刻,左目相机和右目相机同时看到了一个特征点。由于两个相机之间的相对位置(旋转和平移)在出厂时就已经通过标定精确知道了(这个平移就是基线长度,是一个已知的、固定的尺度!),那么根据这两个2D观测,就能直接、唯一地确定这个点的3D位置,而且这个位置是具有真实尺度的。
这个改变的好处是巨大的:
- 尺度确定:基线长度是物理已知的,因此三角化出的点云和轨迹从一开始就是真实尺度,彻底解决了单目的尺度模糊问题。
- 更鲁棒:不依赖于时间上的连续跟踪。即使某一帧跟踪丢失,只要在下一帧左目和右目能同时看到这个点,就能重新三角化。这对于快速运动或存在遮挡的场景友好得多。
- 初始化快:因为有了直接的深度测量,系统初始化不再需要像VINS-Mono那样进行复杂的SFM和视觉-IMU对齐来估计尺度,过程大大简化。
我们来看看代码层面的体现。在VINS-Fusion的 estimator.cpp 中,对于双目模式,三角化的核心函数通常直接对左右目的观测进行操作。它不再需要像Mono那样维护一个复杂的滑动窗口和等待足够的视差,而是在特征点被左右目同时观测到的第一时间,就能计算出其3D坐标。这种从“时序推算”到“空间测量”的转变,是多传感器融合带来的最直观的优势。
3. 核心差异二:系统初始化——从“复杂仪式”到“快速启动”
初始化是VIO系统最脆弱也最关键的环节。一个糟糕的初始化会导致后续所有估计都建立在错误的基础上。VINS-Mono的初始化过程以其复杂和耗时“闻名”,而VINS-Fusion则针对多传感器(尤其是双目)进行了大幅简化。
3.1 VINS-Mono:松耦合对齐的“交响乐”
VINS-Mono的初始化,我称之为一个“复杂的仪式”。它大致分为两个阶段:
- 纯视觉SfM(Structure from Motion):系统先只用图像,在滑动窗口内进行纯视觉的 Structure from Motion。这个过程要恢复出窗口内所有相机帧的姿态和特征点的3D位置,但注意,这个重建是没有尺度的,是一个“缩放版”的地图和轨迹。
- 视觉-IMU对齐:然后,将视觉SfM恢复出的轨迹与IMU预积分得到的轨迹进行对齐。这个对齐过程需要估计出尺度因子
s、重力向量g、以及IMU的零偏ba,bg和速度v。这个过程通过一个松耦合的优化来完成,非常依赖于视觉SfM的质量和IMU数据的激励(需要有足够的加速度变化)。
这个过程在代码里对应 initialStructure() 和 visualInitialAlign() 等一系列函数,逻辑复杂,判断条件多。在实际跑数据时,经常能看到系统卡在“初始化中...”的状态,尤其是在缓慢运动或者纹理较差的场景下,失败率不低。
3.2 VINS-Fusion:双目加持下的“一键启动”
VINS-Fusion的初始化,特别是双目+IMU模式下,过程被极大地简化了。核心原因就是我们上一节提到的:双目直接提供了带有真实尺度的观测。
它的初始化流程可以概括为:
- 静态初始化:系统启动后,首先利用短暂静止时刻的IMU数据平均值,与已知的重力向量对比,粗略估计出初始时刻载体坐标系到世界坐标系(通常z轴对齐重力)的旋转
Rs[0]。这个在processMeasurements函数开头就能看到。 - 直接三角化:由于左右目相机的相对位姿已知,系统可以立即对观测到的特征点进行三角化,得到它们在世界坐标系下的3D坐标。注意,这里的坐标已经是真实尺度了。
- PnP优化:利用这些带有真实尺度的3D点,通过Perspective-n-Point(PnP)方法,可以进一步优化滑动窗口中各帧的位姿(位置
Ps和旋转Rs)。 - 紧耦合优化:最后,将所有视觉观测(重投影误差)和IMU预积分约束一起,放入一个完整的紧耦合非线性优化框架中,进行一次优化,从而得到更精确的初始状态(位姿、速度、零偏等)。
对比之下,VINS-Fusion完全跳过了VINS-Mono中那个繁琐的SfM和视觉-IMU松耦合对齐的步骤。因为尺度问题被双目硬件解决了,所以初始化从一个需要“精心准备”的仪式,变成了一个可以“快速启动”的过程。在实际使用中,VINS-Fusion双目的初始化成功率和速度明显高于VINS-Mono。
下面这个表格可以更清晰地对比两者的初始化逻辑:
| 特性 | VINS-Mono (单目+IMU) | VINS-Fusion (双目+IMU) |
|---|---|---|
| 尺度来源 | 通过视觉-IMU对齐估计,不确定 | 由双目基线物理确定,真实尺度 |
| 核心步骤 | 1. 纯视觉SfM (无尺度) 2. 与IMU预积分轨迹对齐 | 1. IMU重力对齐 2. 双目直接三角化(有尺度) 3. PnP + 紧耦合优化 |
| 速度 | 较慢,需等待足够视差和运动 | 较快,几乎可立即启动 |
| 鲁棒性 | 对运动激励和纹理敏感,容易失败 | 对纹理要求相对较低,更稳定 |
| 依赖 | 严重依赖时间上的连续跟踪 | 依赖同一时刻的左右目观测 |
4. 核心差异三:重投影误差模型——误差定义的革新
重投影误差是视觉SLAM后端优化的核心约束项。它衡量的是:一个3D地图点,投影到当前相机图像上,其投影位置与实际观测到的像素位置之间的差异。VINS-Mono和VINS-Fusion在这个误差的定义上,也因其传感器配置的不同而有本质区别。
4.1 VINS-Mono:单目序列的共视约束
在VINS-Mono中,一个特征点通常被多个不同时刻的单目相机帧观测到。因此,它的重投影误差是同一个3D点,投影到不同时刻的左目相机图像上,与对应时刻的像素观测进行比较。
用公式和通俗的话来解释:假设一个点在第 i 帧被首次观测到,并估计出其逆深度 λ。当滑动窗口滑到第 j 帧时,我们需要把这个点根据第 i 帧到第 j 帧的位姿变换(由IMU预积分和状态估计得到),投影到第 j 帧的图像平面上,然后与第 j 帧实际检测到的该特征点像素坐标求差。
这个误差模型紧密耦合了视觉和IMU,因为 i 帧到 j 帧的位姿变换里包含了IMU的估计信息。但它有一个潜在问题:整个误差链严重依赖于首次观测帧的深度估计(逆深度 λ)的准确性。如果这个初始深度估计有误,误差会传递到所有后续观测帧。
4.2 VINS-Fusion:双目与跨相机的共视约束
VINS-Fusion的重投影误差模型变得更加丰富,尤其是在双目模式下。它不仅仅有时序上的共视(同一个相机在不同时刻看同一个点),更重要的是引入了空间上的共视(左右目相机在同一时刻看同一个点)。
对于双目模式,一个特征点的误差项通常包括:
- 左目时序重投影误差:和Mono类似,点被左目在第
i帧观测,重投影到左目第j帧。 - 右目时序重投影误差:同一个点被右目在第
i帧观测,重投影到右目第j帧。 - 左右目间重投影误差(关键!):这是Fusion特有的。一个点被左目在第
i帧观测到,我们可以利用已知的左右目外参,直接预测它在同一时刻右目第i帧中应该出现的位置,并与右目实际的观测进行比较。反之亦然。
这第三种误差形式,是同一时刻、不同相机之间的约束。它不依赖于系统的运动估计,只依赖于精确标定的双目外参。因此,它是一个非常强、非常直接的约束,能够极大地帮助估计器校正旋转和尺度,尤其是在系统快速旋转或平移量很小、时序三角化困难的时候。
在代码 estimator.cpp 的优化问题构建部分,你可以看到Fusion会为特征点添加更多种类的残差块。对于双目观测,一个路标点可能同时存在于左目和右目的多个帧中,因此它会构建一个更复杂的因子图,同时约束了时间序列和空间基线,使得整体的优化结果更加稳定和精确。
5. 工程实现与代码层面的关键改动
光讲理论可能还有点抽象,我们直接深入到代码里,看看这些核心差异在 estimator.cpp 和 feature_tracker.cpp 中是怎么体现的。这对于想要自己修改、调试代码的开发者来说至关重要。
5.1 特征跟踪策略的优化
在 feature_tracker 节点中,VINS-Fusion针对多传感器做了针对性优化。
对于单目+IMU模式,它基本继承了Mono的策略:使用LK光流进行前向跟踪,并且为了剔除误匹配,增加了反向光流验证。也就是用当前帧跟踪到的特征点位置,再反向跟踪回上一帧,看看是否回到原位置附近。这个操作虽然增加了一点计算量,但能有效提升特征匹配的鲁棒性。
对于双目模式,策略变了:右目的特征点不是独立提取的,而是基于左目进行光流跟踪。具体流程是:
- 在左目图像上提取FAST角点。
- 利用已知的双目外参(旋转和平移)和一个假设的初始深度(比如最小深度值),将左目的特征点“投影”到右目图像上,得到一个预测的起始位置。
- 在这个预测位置的小邻域内,进行光流搜索,找到右目对应的匹配点。
这种方法的好处是效率高,并且极大地缩小了搜索范围,提高了匹配的正确率。因为左右目是同步的,理论上对应点只会在极线附近出现,这种基于运动先验的跟踪比漫无目的的全图搜索靠谱得多。
5.2 数据处理与状态估计流程
在 estimator.cpp 的 processMeasurements 函数中,差异也很明显。
首先是状态量的初始化。在Mono里,旋转 Rs[0] 通常初始化为单位矩阵。而在Fusion里,会利用初始时刻一段IMU加速度计数据的平均值 gave,与重力向量 [0,0,G] 进行对齐,通过一个 g2R 函数计算出初始旋转。这给了系统一个更合理的重力方向先验。
// 类似VINS-Fusion中的初始化思想
Eigen::Vector3d g_avg = acc_buf.average(); // 加速度计均值
Eigen::Matrix3d R0 = Utility::g2R(g_avg); // 对齐到重力
Rs[0] = R0;
最大的区别还是在初始化函数里。Mono有一整套 initialStructure() 和 visualInitialAlign() 的复杂流程。而在Fusion的双目初始化中,这些都被简化了。正如前文所述,它直接利用双目三角化得到有尺度的3D点,然后通过PnP和一次紧耦合优化就完成了初始化。代码结构上,Fusion的初始化部分看起来更简洁、更集中。
5.3 优化问题与因子图构建
在后端非线性优化部分(通常是基于Ceres或g2o),两者构建的优化问题维度不同。Mono优化的是单目相机的轨迹和地图点的逆深度。而Fusion在双目模式下,优化的是双目相机的轨迹(虽然通常以左目为主)和地图点的3D坐标(x,y,z)。
更重要的是残差因子的类型。Mono的视觉残差只有一种:单目重投影误差。而Fusion的视觉残差可能包含多种:左目重投影误差、右目重投影误差,以及左右目之间的立体约束误差。这些额外的约束使得Fusion的优化问题具有更好的可观性,尤其是在某些退化场景(如匀速直线运动)下,系统更不容易发散。
我在一个无人机编队项目中深有体会。当无人机进行编队飞行,前方主机可能遮挡后机的大部分视野,导致后机的单目VINS特征跟踪数量急剧下降。切换到VINS-Fusion双目模式后,即使前方视野被挡,利用机身自带的左右目相机,依然能通过左右目之间的空间约束维持稳定的定位,鲁棒性提升了一个数量级。
6. 实战指南:如何选择与配置
分析了这么多技术细节,最后落到实际应用上:我到底该用VINS-Mono还是VINS-Fusion?我的项目该怎么配置?
6.1 场景选择与传感器配置建议
选择VINS-Mono的情况:
- 对成本、体积、功耗极度敏感:你的设备只能搭载一个摄像头和一个IMU。这是单目的唯一理由。
- 主要进行前端研究:你想深入理解单目VIO的原理、初始化、尺度估计等经典问题。
- 运行平台算力非常有限:双目图像处理和数据传输的负担是单目的两倍。
选择VINS-Fusion的情况(推荐大多数应用):
- 追求稳定性和精度:你的无人机、机器人或AR/VR设备需要可靠的定位。双目提供的绝对尺度是“定心丸”。
- 面临挑战性环境:经常有快速旋转、剧烈运动、纹理稀疏或重复纹理的场景。双目的空间约束能有效应对。
- 需要快速启动:设备要求上电后立刻工作,不能等待漫长的初始化过程。
- 融合GPS等绝对传感器:VINS-Fusion框架原生支持GPS融合,对于室外大范围导航是刚需。
关于配置的真心建议:
- 首选“双目+IMU”:这是VINS-Fusion的黄金配置,在精度、鲁棒性和复杂度之间取得了最佳平衡。两个便宜的全局快门相机加一个IMU,成本增加不多,但系统可靠性提升巨大。
- 谨慎使用“纯双目”:如果没有IMU,在快速运动时图像容易模糊,导致特征跟踪失败,而且无法估计重力方向。除非你的应用场景运动非常缓慢平稳。
- “单目+IMU”作为备选:当实在无法安装双目时再用。务必保证相机和IMU的硬件同步尽可能精确,并做好面对初始化问题和尺度漂移的心理准备。
- “双目+IMU+GPS”用于室外:如果你做的是无人机物流、自动驾驶等室外应用,一定要加上GPS。VINS-Fusion的GPS融合模块可以在GPS信号良好时提供绝对位置纠正,防止里程计累积误差;在信号丢失时(如隧道),平滑地切换回纯视觉惯性里程计。
6.2 参数调试与性能优化经验
无论选择哪个,参数调试都绕不开。这里分享几个关键参数的经验:
- 图像分辨率与频率:不是越高越好。对于VIO,高帧率(30Hz以上)比高分辨率更重要。通常640x480或752x480的分辨率足以提供丰富的特征,同时保证处理速度。将图像裁剪到ROI(感兴趣区域)也能加速。
- 特征点数量:在
config.yaml文件中调整max_cnt。太少会导致约束不足,太多会增加计算量。室内丰富纹理场景150-200个点足够,室外天空地面占比较大的场景可以适当增加到250-300个。 - IMU参数:
acc_n(加速度计噪声)和gyr_n(陀螺仪噪声)以及对应的零偏随机游走噪声acc_w,gyr_w至关重要。一定要用Allan方差工具标定你的IMU,并填入真实参数! 使用默认值或随意填写是精度下降的主要原因。 - 外参标定:相机和IMU之间的时空外参
T_b_c(旋转和平移)必须精确标定。特别是时间戳同步,硬件同步最佳,如果做不到,也要在软件上精确估计时间偏移。对于双目,左右目之间的外参T_c0_c1同样需要高精度标定。 - 优化器设置:可以尝试调整滑动窗口大小
window_size。更大的窗口包含更多历史信息,可能提高精度,但也增加计算量。在资源受限的嵌入式平台(如Jetson Nano)上,可能需要减小窗口大小以维持实时性。
调试时,多使用ROS的Rviz工具观察实时轨迹和特征点,并录制数据包进行离线回放测试。关注初始化成功率、轨迹的平滑度、以及在转弯、上下楼梯等场景下的表现。记住,没有一套参数能通吃所有场景,根据你的实际传感器和运行环境进行微调是必不可少的步骤。
从我自己的经验来看,从VINS-Mono迁移到VINS-Fusion,最大的感受就是“省心”。以前需要花大量时间处理初始化失败、尺度漂移、旋转发散等问题,现在用双目版本,很多问题自然就消失了。当然,系统的复杂度和计算需求有所上升,但考虑到如今嵌入式处理器性能的飞速发展,这点开销对于获得一个稳定可靠的定位系统来说,是完全值得的。多传感器融合这条路,无疑是机器人感知定位领域最坚实的方向。
更多推荐


所有评论(0)