1. VINS-Fusion关键帧选择策略解析
在视觉惯性里程计(VIO)系统中,关键帧的选择直接影响着系统的精度和效率。VINS-Fusion作为业界广泛使用的开源VIO方案,其关键帧选择策略经过精心设计,在保证精度的同时兼顾计算效率。
1.1 关键帧的核心作用
关键帧在VIO系统中承担着多重重要角色:
- 地图点维护:作为视觉特征的载体,保留高质量的特征观测
- 位姿图优化:构成位姿图中的节点,参与全局优化
- 滑动窗口管理:决定边缘化策略,影响系统内存和计算负载
- 冗余信息过滤:避免相似视角的帧过度累积
提示:好的关键帧策略应该在场景变化显著时及时插入新帧,同时在静态场景中避免冗余帧积累。
1.2 策略设计的基本原则
VINS-Fusion的关键帧选择基于以下几个核心考量:
- 系统初始化需求:前两帧必须作为关键帧,为系统提供初始位姿和地图
- 特征跟踪质量:当跟踪特征数量不足时强制插入关键帧
- 运动充分性:通过视差判断相机运动是否足够显著
- 场景变化适应:当新特征比例过高时响应场景变化
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 特征管理与统计实现
2.1 特征数据结构解析
VINS-Fusion使用两级结构管理特征点:
cpp复制struct FeaturePerFrame {
Vector3d point; // 归一化平面坐标
double td; // 时间偏移
// 右目观测相关方法
};
struct FeaturePerId {
int feature_id; // 特征点ID
int start_frame; // 首次观测帧
vector<FeaturePerFrame> feature_per_frame; // 多帧观测序列
};
这种设计实现了:
- 生命周期管理:通过start_frame和feature_per_frame记录特征的首次出现和持续跟踪情况
- 多传感器支持:FeaturePerFrame可存储双目或多相机的观测数据
- 运动分析基础:连续的观测序列为视差计算提供数据支持
2.2 特征统计指标详解
代码中维护了三个关键统计量:
-
new_feature_num:当前帧中新出现的特征数量
- 反映场景变化程度
- 计算方式:在特征管理器中找不到对应ID的特征点
-
last_track_num:从上一帧持续跟踪到的特征数量
- 衡量特征跟踪的稳定性
- 递增条件:特征ID已存在且匹配成功
-
long_track_num:跟踪长度≥4帧的特征数量
- 标识高质量的长寿命特征
- 用于判断系统是否积累了足够的可靠特征
3. 关键帧快速判断逻辑
3.1 强制关键帧条件
当满足以下任一条件时,直接判定为关键帧:
cpp复制if (frame_count < 2 || last_track_num < 20 || long_track_num < 40 || new_feature_num > 0.5 * last_track_num)
return true;
各条件的实际含义:
-
frame_count < 2:系统初始化阶段,前两帧必须作为关键帧
- 为三角化和BA提供足够的观测约束
-
last_track_num < 20:跟踪特征过少
- 可能导致后续位姿估计不可靠
- 20是经验阈值,平衡精度和鲁棒性
-
long_track_num < 40:长轨迹特征不足
- 影响地图点质量和系统稳定性
- 40的阈值适用于大多数室内外场景
-
new_feature_num > 0.5*last_track_num:新增特征占比过高
- 表明场景发生显著变化(如转弯、遮挡)
- 0.5的比例因子避免频繁关键帧插入
3.2 阈值选择的工程考量
这些阈值需要根据实际应用场景调整:
- 高动态场景:可适当降低last_track_num和long_track_num阈值
- 计算资源受限:可提高比例阈值减少关键帧数量
- 特殊传感器配置:广角相机可支持更高特征数阈值
4. 视差计算与关键帧判定
4.1 视差计算的核心逻辑
对于未触发快速判断的帧,进行精细化的视差分析:
cpp复制for (auto &it_per_id : feature) {
if (it_per_id.start_frame <= frame_count - 2 &&
it_per_id.start_frame + int(it_per_id.feature_per_frame.size()) - 1 >= frame_count - 1) {
parallax_sum += compensatedParallax2(it_per_id, frame_count);
parallax_num++;
}
}
关键条件解析:
-
start_frame <= frame_count - 2:特征至少在两帧前出现
- 确保有足够基线进行视差计算
-
持续到当前帧:特征未被丢失
- 保证视差计算的连续性
4.2 视差计算方法剖析
compensatedParallax2函数实现细节:
- 获取特征在倒数第二帧(frame_count-2)和倒数第一帧(frame_count-1)的观测
- 将特征点投影到归一化平面(去除相机内参影响)
- 计算两点间的欧氏距离作为视差度量
- 考虑时间偏移补偿(如使用IMU数据)
注意:归一化处理使得视差阈值可以跨相机通用,不受具体焦距影响
4.3 基于视差的最终决策
视差判断逻辑:
cpp复制if (parallax_num == 0) {
return true; // 无有效视差计算,强制关键帧
} else {
double avg_parallax = parallax_sum / parallax_num * FOCAL_LENGTH;
return avg_parallax >= MIN_PARALLAX;
}
典型参数设置:
- FOCAL_LENGTH:相机焦距(像素单位),用于将归一化视差转换回像素单位
- MIN_PARALLAX:经验值(如10像素),表示相机运动足够显著
5. 对滑动窗口的影响机制
5.1 边缘化策略选择
关键帧决策直接影响边缘化方式:
cpp复制if (f_manager.addFeatureCheckParallax(frame_count, image, td)) {
marginalization_flag = MARGIN_OLD; // 边缘化最旧帧
} else {
marginalization_flag = MARGIN_SECOND_NEW; // 边缘化次新帧
}
两种边缘化模式的对比:
| 模式 | 触发条件 | 移除帧 | 优点 | 缺点 |
|---|---|---|---|---|
| MARGIN_OLD | 关键帧 | 最旧帧 | 保持窗口时间跨度 | 可能丢失重要历史信息 |
| MARGIN_SECOND_NEW | 非关键帧 | 次新帧 | 保留最新观测 | 窗口时间跨度收缩 |
5.2 工程实现注意事项
在实际部署时需要注意:
- 边缘化先验维护:正确构造和更新边缘化产生的先验信息
- 舒尔补计算效率:优化矩阵操作,避免成为性能瓶颈
- 数值稳定性:处理边缘化可能引入的病态矩阵问题
- 一致性保持:确保边缘化后的线性化点与窗口内其他帧一致
6. 实际应用中的调参经验
6.1 场景自适应参数调整
根据场景特点调整关键参数:
-
动态场景:
- 提高new_feature_num比例阈值(如0.7)
- 降低MIN_PARALLAX(如8像素)
-
高纹理场景:
- 增加last_track_num和long_track_num阈值
- 可支持更严格的关键帧筛选
-
低帧率运行:
- 降低所有数量阈值
- 调整视差计算的时间窗口
6.2 常见问题排查
-
关键帧过于密集:
- 检查MIN_PARALLAX是否设置过小
- 确认特征检测是否稳定(避免大量特征突然消失)
-
关键帧过于稀疏:
- 验证last_track_num阈值是否过高
- 检查视差计算是否正确(可能相机运动但视差小)
-
系统漂移加剧:
- 确认边缘化先验是否正确应用
- 检查长轨迹特征是否足够参与优化
7. 与其他模块的协同工作
7.1 与前端特征跟踪的配合
关键帧策略需要与特征跟踪协同设计:
-
特征匹配策略:
- 光流跟踪应保持一定数量的长轨迹特征
- 对低质量特征及时剔除,避免干扰统计
-
关键帧特征提取:
- 关键帧应提取更多新特征(如增加特征点数)
- 非关键帧可适当减少计算量
7.2 与后端优化的交互
关键帧影响后端优化的多个方面:
-
滑动窗口优化:
- 关键帧提供更强的位姿约束
- 非关键帧主要提供局部优化信息
-
全局位姿图:
- 只有关键帧加入位姿图节点
- 非关键帧的位姿通过插值获得
-
回环检测:
- 仅对关键帧进行回环检测
- 关键帧选择影响回环识别率
在实际项目中,我们通常需要根据具体硬件平台和应用场景对关键帧策略进行调优。例如在无人机平台上,由于计算资源有限,可以适当提高关键帧的选取标准;而在车载场景中,面对快速运动则需要更敏感的关键帧响应。理解算法原理后,这些调整才能有的放矢。
