简介这套PPT基于VSLAM与VINS-Mono框架介绍整理面向计算机视觉初学者、SLAM方向研究生或需要做技术分享的开发者帮助快速理解视觉同时定位与建图的核心概念及VINS-Mono的模块化实现。内容从VSLAM的前后端划分入手覆盖传感器数据预处理、视觉约束、IMU约束与滑窗优化针对VINS-Mono更细化到光流跟踪、光流金字塔、readImage、IMU预积分等前端步骤以及简化VINS和后端优化流程并配合初始化、闭环检测等关键知识点。资源为1个可编辑pptx文件压缩包大小4.05MB页面结构清晰适合直接用于课程汇报、组会分享或个人学习梳理。目前已有287人浏览学习对于想系统了解VSLAM框架和VINS-Mono实现细节的读者是一份可直接上手修改的参考素材。1. VINS-Mono 是 VSLAM 绕不开的框架先理解它怎么把单目和 IMU 绑在一起单目 VSLAM 最让新手头疼的两个问题一是尺度不确定二是纯旋转运动直接导致三角化退化。VINS-Mono 用紧耦合的 IMU 预积分把这两个问题变成了可以量化的参数问题这也是它被广泛用来做框架讲解、做技术分享 PPT 的核心理由。它不只是港科大开源的一个项目更是后面一大批视觉惯性方案的对标基线很多论文的消融实验、很多产品的轻量级改造第一件事都是先把 VINS-Mono 在自家硬件上跑通。下面按「框架拆解 → vins 参数标定 → 代码跑通 → 结果验证」这条线把 VINS-Mono 从原理到落地需要知道的细节串一遍。这套内容适合准备 VSLAM 技术分享的工程师也适合刚入门想真正把「标定、yaml、bag 播放」这几个词对应起来的人。如果你的目标就是产出一份能直接编辑使用的技术讲解材料顺着这四段主线走下来幻灯片的结构不太用额外想。2. VINS-Mono 的框架拆解从 VSLAM 前端到滑窗优化的每一环2.1 为什么 VSLAM 要引入 IMU尺度、重力方向与快速运动下的兜底纯单目 VSLAM 在初始化之后能给出一个相对轨迹但这个轨迹和真实世界差一个尺度因子单靠图像无法确定物体到底是一米还是十米。IMU 的加速度计能感知重力方向从而约束了 roll 和 pitch加速度积分能提供一个绝对物理尺度。与此同时相机在快速旋转或短暂遮挡时容易丢失特征IMU 可以在一小段时间内维持状态预测等视觉重新跟上。VINS-Mono 的紧耦合体现在状态向量上它把位置 p、速度 v、姿态 q、陀螺仪零偏 bg、加速度计零偏 ba 放在同一个优化问题里而不是像松耦合方案那样把视觉位姿和 IMU 积分结果各自算完再做融合。紧耦合的好处是标定残差、视觉重投影残差和 IMU 预积分残差共享一套协方差模型精度上限更高代价是计算量集中在后端优化移动端要控制滑窗大小和特征数量。2.2 前端FAST 特征提取与 KLT 光流跟踪的配置项VINS-Mono 的前端没有用 ORB 特征描述子做匹配而是用 FAST 角点加金字塔 KLT 光流跟踪。光流假设相邻帧之间运动足够小因此在帧率稳定一般是 20Hz 到 30Hz 的相机时跟踪比描述子匹配快一个量级而且不需要显式计算描述子。代价是长时间跟踪容易漂移所以 VINS-Mono 每帧会检测新角点并做网格均匀化把特征点数量控制在固定范围。max_cnt: 150 # 单帧最大特征点数量太少会导致初始化视差不足 min_dist: 30 # 特征点之间的最小像素距离保证分布均匀 freq: 10 # 发布给后端的最频繁帧率单位 Hz f_threshold: 1.0 # 关键帧选择时用到的视差阈值单位像素上面是euroc_config.yaml里最常见的一组取值。max_cnt决定后端每次优化拿到的约束数量太少了初始化时可能没有足够轨迹支持三角化太多了后端非线性优化的耗时线性上涨在 Jetson 这类设备上帧率会掉到 10Hz 以下。min_dist做的是均匀化它规定新检测的角点不能离已有特征太近避免特征扎堆在纹理丰富的一块区域导致相机旋转时大部分点同时出视野。freq不是相机发布频率而是 VINS-Mono 前端向后端丢数据时主动降的频一般设置为相机实际帧率的一半左右。调试前端时最容易发现的问题是特征点全部在某一块区域。这时先看是不是镜头畸变太大再回来查min_dist是否设得过大比如设成 80 以上会把整张图的特征点数量压得很低初始化迟迟不成功。2.3 初始化视觉 SfM 与 IMU 对齐卡住的人大多在这里初始化是 VINS-Mono 实际运行中最容易失败的阶段也是 PPT 讲解时需要重点展开的一页。它的目标不是输出一个稳定的位姿而是估计五个量陀螺仪零偏、重力方向、速度、尺度因子以及每一帧的位姿初值。VINS-Mono 分两步完成先用运动恢复结构SfM纯靠图像恢复一组帧的位姿和地图点再把 IMU 预积分的结果和视觉位姿对齐。对齐过程用到了「视觉惯性对齐」的经典公式推导把相邻两关键帧之间的 IMU 预积分量表示成关于尺度 s、速度 v 和重力 g 的线性方程通过最小二乘解出这些初值。这里有一个点容易踩坑如果初始化时相机近似做匀速直线运动加速度几乎为零那么尺度这个自由度没有被激励即使 SfM 成功对齐结果也会飘。这也是为什么 VINS-Mono 的文档里反复强调启动后要把设备做加减速明显的运动比如先左右偏航甩两下再上下点头最后走一个「之」字形。EuRoC 数据集里的V1_02_medium序列之所以被当成默认测试集就是因为它的前几秒运动正好覆盖了初始化所需的激励。初始化失败时终端会反复打印waiting for imu data或init failed。前者是 IMU 话题没对上后者是视觉或 IMU 激励不足。先别调后端参数按照上面说的运动方式重新录一段数据成功率会明显提高。2.4 后端滑动窗口、边缘化与关键帧策略VINS-Mono 的后端是一个基于滑动窗口的非线性优化问题窗口大小默认是 10也就是同时优化最近 10 个关键帧的状态。关键帧选择看的是视差当前帧和窗口内最后一帧的平均视差超过阈值时才成为关键帧。这个策略比固定时间间隔选帧更合理因为相机静止不动时时间再久也不会带来新的几何约束。滑出窗口的帧不会直接被丢弃而是做边缘化marginalization把被移除帧的状态信息转换成对剩余状态的先验约束保留在信息矩阵里。边缘化的实现是 VINS-Mono 代码里最绕的部分它维护了一个MarginalizationInfo结构内部保存了所有相关残差的 Jacobian 和 Hessian在滑窗移除时用 Schur 补计算先验。理解边缘化时可以把它当成「把已经算过的老信息压缩成一个高维的二次型惩罚项」而不是删掉数据。下面这张表列出了后端最常调整的几个参数它们都出现在 vins 的 yaml 配置中参数名作用常见值window_size滑动窗口最大关键帧数10移动端可降到 8estimate_extrinsic是否在初始化阶段在线估计相机-IMU 外参0 表示关闭1 表示开启estimate_td是否在线估计相机与 IMU 的时间偏移1 表示开启max_solver_time后端每轮优化最大耗时上限0.04 秒约 25Hzmax_num_iterations每轮优化最大迭代次数10这里特别想说明estimate_extrinsic如果你的外参是经过联合标定得到的直接设为 0不要在线估计因为在线估计依赖运动激励而且和初值强相关。只有当你手头传感器是临时搭建、来不及标定时才打开这个开关让它作为初始化的一部分参与估计。max_solver_time是工程落地时最值得关注的一个参数它直接决定了后端是否能在目标帧率下跑完一轮优化如果经常超时优先降低window_size而不是提高迭代次数上限。2.5 回环检测与四自由度位姿图优化VINS-Mono 回环检测用的是 DBoW2 词袋特征描述子用的是 BRIEF。检测到候选回环后先做几何验证剔除外点再通过一个四自由度的位姿图优化把累积漂移修正掉。之所以是四自由度而不是六自由度是因为重力方向已经在初始化时被 IMU 锁定roll 和 pitch 的漂移很小剩下的主要漂移集中在 yaw 以及 x、y、z 三个方向。把 roll 和 pitch 固定住能减少优化变量数量让整个回环优化更快更稳。回环这一块在 PPT 里适合放一张「全局地图」的效果对比图左半边是纯前端加后端跑出来的轨迹右半边是加上回环后的轨迹两张图在绕场一周后的位置误差差异非常直观。这个对比也常被用来解释 VINS-Mono 为什么被认为是一个完整的 VSLAM 方案而不只是一个视觉惯性里程计。3. vins 参数标定相机内参、IMU 噪声密度与外参联合标定的完整套路3.1 标定顺序与各环节产出的对应关系VINS-Mono 实际运行前需要你给它准备四样东西相机内参与畸变模型、IMU 的噪声密度与随机游走、相机到 IMU 的外参旋转和平移、时间偏移。这一节说的「vins 参数标定」就是指把这四样东西用工具链标出来的过程最常见的组合是 kalibr 加 imu_utils。顺序不能乱先单独标相机内参再单独标 IMU 噪声最后做视觉-IMU 联合标定输出外参和时间偏移。原因是 kalibr 的联合标定需要相机内参作为投影模型的初值也需要 IMU 噪声参数作为优化权重跳过任何一步都会得到不稳定的结果。标定对象主要工具关键产出常见坑相机内参 畸变kalibr_calibrate_cameras投影矩阵、畸变系数标定板曝光过强、角点提取不完整IMU 噪声密度与随机游走imu_utilsgyr_n、gyr_w、acc_n、acc_w静止数据时间太短Allan 方差曲线不稳定相机-IMU 外参与时间偏移kalibr_calibrate_imu_cameraT_ci 变换矩阵、td图像与 IMU 时间戳未对齐标定用的数据采集规则也很重要相机标定板要在画面里缓慢平移、旋转封面相机的各个区域同时避免运动模糊IMU 标定则需要设备完全静止在水平桌面上远离震动源。实际情况里最容易忽略的是标定板不平整比如打印的棋盘格贴在不平的纸箱上这会给内参引入一个固定的弯曲误差后期怎么调后端参数都救不回来。3.2 相机内参标定kalibr 命令及参数选择这里用 kalibr 来做单目相机内参标定。命令执行前你已经录好了一个包含标定板图像的 bag并准备了一个描述标定板规格的 yaml 文件里面包含标定板的类型aprilgrid 或 checkerboard、尺寸、间距等信息。kalibr_calibrate_cameras \ --target /path/to/april_6x6_80x80cm.yaml \ --models pinhole-radtan \ --topics /cam0/image_raw \ --bag /path/to/camera_calib.bag \ --show-extraction--models参数指定相机模型。VINS-Mono 的euroc_config.yaml里常用PINHOLE模型加radtan畸变对应的就是这里的pinhole-radtan它包含四个畸变系数 k1、k2、p1、p2。如果是广角或鱼眼镜头一般用pinhole-equi等距投影模型。--topics必须与 bag 里的图像话题名完全一致--show-extraction会在屏幕上逐帧显示角点检测结果这一步一定要人眼确认不能跳过如果大量标定板的角点在遮挡或运动模糊中被错误提取后续优化出来的内参会明显偏离真实值。标定完成后 kalibr 会生成一个 camchain 文件里面是相机内参矩阵和畸变系数这些值稍后会出现在 VINS-Mono 的 yaml 的projection_parameters和distortion_parameters字段下。3.3 IMU 噪声标定imu_utils 与 Allan 方差读取IMU 标定在 VINS-Mono 中是个很容易被忽略、但影响长期精度的环节。需要标定的是四类噪声陀螺仪噪声密度gyr_n、陀螺仪随机游走gyr_w、加速度计噪声密度acc_n、加速度计随机游走acc_w。imu_utils 通过 Allan 方差分析从一段静止数据中提取这些值。rosrun imu_utils imu_an_calib imu.bag imu_raw -i 30 -t 600-i 30表示忽略数据开始的前 30 秒目的是等 IMU 温度稳定-t 600表示用于分析的数据总时长为 600 秒。实际使用中静止数据应该至少录一小时以上时间太短时 Allan 方差曲线的长时区间无法收敛随机游走值会变得不可信。imu_utils 的输出是一个 imu.yaml 文件里面包含四个关键数值。把这些值填进 VINS-Mono 的 yaml 时要注意单位噪声密度是连续时间谱密度随机游走则是角速度/加速度对时间的积分漂移两者差一个时间量纲不能填反。提示如果 IMU 的采样频率低于 100HzAllan 方差分析的高频段信息不够标出来的噪声密度会偏小后端优化时更信任 IMU导致视觉-IMU 权重失衡。采集时先确认 IMU 话题的真实发布频率。3.4 相机与 IMU 外参联合标定这一步把前面两个标定结果合并得到相机和 IMU 坐标系之间的旋转矩阵与平移向量以及时间偏移 td。kalibr 的联合标定命令如下kalibr_calibrate_imu_camera \ --target /path/to/april_6x6_80x80cm.yaml \ --cam /path/to/camchain.yaml \ --imu /path/to/imu.yaml \ --bag /path/to/imu_cam_calib.bag \ --timeoffset-padding 0.02--cam是第一步生成的相机内参链文件--imu是第二步生成的 IMU 噪声参数文件--timeoffset-padding 0.02表示允许 kalibr 在正负 20 毫秒范围内搜索时间偏移。联合标定结果会给出相机到 IMU 的变换矩阵记为 T_ci 或 T_ic不同版本输出命名略有差异填写到 VINS-Mono 的body_T_cam字段时要注意旋转方向。一个常见的低级错误是把 T_ci 当成 T_ic 直接抄进去结果运行后 rviz 里的特征点轨迹和 IMU 姿态方向对不上表现为点云整体扭曲。联合标定的数据采集建议是手持设备在标定板前做慢速的六自由度运动包含明显的旋转和平移避免只朝一个方向平移。录 60 秒到 90 秒即可太长会增加 kalibr 优化的时间太短的片段无法充分激励外参的三个旋转自由度。3.5 把标定结果写进 VINS-Mono 的 yaml 配置文件标定完成后所有数值需要手动填入config/euroc/euroc_config.yaml。下面是一份关键字段的示例实际数字必须用你自己标定的结果替换这里的值是演示用imu_topic: /imu/data image_topic: /cam0/image_raw model_type: PINHOLE image_width: 752 image_height: 480 distortion_parameters: k1: -0.2834 k2: 0.0735 p1: 0.0002 p2: -0.0001 projection_parameters: fx: 458.6 fy: 457.3 cx: 367.2 cy: 248.7 acc_n: 0.01 acc_w: 0.0001 gyr_n: 0.001 gyr_w: 0.0001 body_T_cam: - [0.0149, -0.9996, 0.0253, 0.0111] - [0.9993, 0.0147, -0.0329, -0.0002] - [0.0325, -0.0257, -0.9991, 0.0288] - [0.0, 0.0, 0.0, 1.0] td: 0.018 estimate_extrinsic: 0 estimate_td: 1estimate_extrinsic设为 0因为外参已经是联合标定过的值estimate_td: 1则保留在线估计让系统在前端运行的一小段内微调时间偏移。这里有一个值得记住的工程技巧如果td在线估计的结果与 kalibr 给出的值相差超过 10 毫秒说明数据采集时硬件时间戳存在同步问题比如 IMU 驱动里丢包补偿没做好这时应该回头查传感器驱动而不是在 yaml 里强行写一个补偿值。4. VINS-Mono 源码结构以及跑通 EuRoC 的最小步骤4.1 源码目录先对一遍三个子项目的边界VINS-Mono 的代码组织得很规整主要分为feature_tracker、vins_estimator、pose_graph三个子项目。很多人第一次编译后对着 VTK 报错发懵就是因为没分清楚模块边界在pose_graph里找前端的参数当然找不到。目录对应模块关键任务典型输出话题feature_tracker前端FAST 提取、KLT 光流、均匀化/feature_tracker/featurevins_estimator后端初始化、滑窗优化、边缘化/vins_estimator/odometrypose_graph回环检测与优化词袋检测、4-DOF 位姿图/pose_graph/match_imagefeature_tracker与纯视觉 SLAM 的前端没有本质区别核心输出是一组归一化平面坐标和像素坐标按帧打包发布。vins_estimator接收这些特征完成视觉惯性对齐和滑窗优化输出的是带姿态协方差的里程计话题。pose_graph是异步运行的它接收关键帧数据在后台做回环检测检测成功后会发布一个全局位姿修正修正结果再反馈给vins_estimator用于重新发布轨迹。如果只是想跑一个不带回环的 VIO可以只关闭 pose_graph但实际使用中建议保留因为滑窗本身的漂移在长走廊里会非常明显。4.2 依赖安装与编译Ceres 和 OpenCV 版本是最大的坑编译 VINS-Mono 之前先确认三个核心依赖OpenCV、Ceres Solver、Eigen。大多数编译报错都出在 OpenCV 版本与 cv_bridge 的链接不一致上尤其是 ROS Melodic 默认带的 OpenCV 3.2 和系统里手动安装的 OpenCV 4.x 同时存在时CMake 很容易找到错误的版本。sudo apt-get install -y \ ros-$ROS_DISTRO-cv-bridge \ ros-$ROS_DISTRO-image-transport \ ros-$ROS_DISTRO-tf \ libeigen3-dev \ libsuitesparse-dev \ libceres-dev cd ~/catkin_ws/src git clone https://github.com/HKUST-Aerial-Robotics/VINS-Mono.git cd ~/catkin_ws catkin_make source devel/setup.bashlibceres-dev在 Ubuntu 18.04 上对应的版本是 Ceres 1.14用来编译 VINS-Mono 没有兼容性问题。如果系统里已经装了更高版本的 Ceres比如 2.1可能会在vins_estimator的构建阶段报出和Solver::Options相关的编译错误因为高版本 Ceres 调整了部分头文件的组织方式。这种情况下最简单的方式是卸载或使用 Docker 镜像拉一个 ROS Melodic 环境。catkin_make编译过程如果报找不到dbow、g2o、vins等目标通常是第三方依赖没有正确拉取确认src/VINS-Mono目录下的依赖子模块是否完整必要时重新 clone。4.3 用 EuRoC 数据集跑通第一遍launch 与 rosbag 播放顺序推荐先用 EuRoC 的V1_02_medium跑通全流程。这个序列是在一个纹理丰富的室内环境下录制的运动包含充分的旋转和变速初始化很少失败。# 终端 1启动 VINS-Mono roslaunch vins_estimator euroc.launch # 终端 2播放数据集 rosbag play V1_02_medium.bageuroc.launch会加载config/euroc/euroc_config.yaml默认订阅话题与 EuRoC bag 内置话题一致不要改。播放前先在 rviz 中确认vins_estimator/odometry和vins_estimator/point_cloud两个话题有没有显示。如果背板是空白的最常见的原因是 rosbag 的播放时间戳与系统时间差太大导致 TF 树时间跳变。此时在 rosbag 播放时加上--clock参数并确认 rviz 的全局固定坐标系设为world这样可以解决大部分显示异常。初始化成功时终端会打印IMU and vision align success并且 rviz 中开始出现稀疏点云。如果初始化失败按第 2 节提到的激励方式手动移动设备即可EuRoC 数据集本身也建议先快进到第 5 秒左右再播放。4.4 用 evo 评估轨迹质量APE 是关键指标跑通不是终点还得用数值来评估这一套标定和参数下来到底什么水平。VINS-Mono 源码中提供write_vins_result工具保存 TUM 格式的轨迹文件之后再配合 evo 工具做精度评估。rosrun vins_estimator write_vins_result /path/to/output_folder evo_ape euroc /path/to/groundtruth/data.csv /path/to/output/vins_result.csv -a-a表示对轨迹做刚性对齐去除整体旋转和平移带来的系统性误差只评估每段局部的轨迹精度。EuRoC 的 groundtruth 数据是 CSV 格式直接下载数据集自带的data.csv即可。在V1_02_medium上标定和参数正常时 APE 一般能到 0.1 米到 0.2 米的量级。如果跑出来超过 1 米基本不用怀疑调参能力而是说明内参、外参或时间偏移里有一样是不对的回到第 3 节重新过一遍标定流程。4.5 给 PPT 留一页VINS-Mono 的数据流与讲解顺序如果这份材料是要直接用于分享讲解建议按「输入传感器 → 前端提取与跟踪 → 初始化 → 滑窗优化 → 回环修正」画一条横向数据流每个模块对应一到两张关键图。前端放光流跟踪的效果图初始化放 IMU 对齐前后的位姿对比后端放滑窗和边缘化的示意回环放全轨迹闭合的前后对比。这张图上的每一环正好就是前面几节讲到的 yaml 参数、模块目录与话题名称的落点别人照着这张图问细节你可以直接跳到对应的代码目录。5. 验证 VINS-Mono 标定与运行质量零偏曲线、时间偏移和一段静止数据标定完之后不要急着进入正式采集先用一段静止数据做快速验证这是我最常用的方式。把设备放在桌面上静止两分钟播放 bag 并启动 VINS-Mono然后观察两个东西IMU 零偏估计曲线是否收敛以及静止时里程计位置是否有持续漂移。rostopic echo -n 1 /vins_estimator/imu_propagate rostopic echo /imu/data -n 10/vins_estimator/imu_propagate会输出当前估计的陀螺仪和加速度计零偏静止状态下这四个值应该在几十秒内趋于平稳不会出现单调递增或大幅振荡。如果零偏始终在两个方向上来回跳说明 IMU 噪声参数填得偏小后端过度信任了瞬时测量。此时把acc_n和gyr_n调大 20% 到 30%再重复一次验证。另一种情况是静止两分钟里里程计位置缓慢飘走这不一定是标定问题也可能是 IMU 零偏未在初始化阶段被充分激励重启系统后先让设备静止预热 30 秒再开始初始化通常能缓解。时间偏移 td 的验证方式和标定验证不同。把estimate_td打开跑完一段含明显加速和旋转的数据后在终端的初始化信息里能看到 td 的收敛值。把这个值和 kalibr 联合标定输出的 td 做对比差距小于 5 毫秒算正常如果差距超过 10 毫秒说明硬件采集链路里有一路的驱动时间戳不稳定优先检查 IMU 驱动是否存在丢帧缓冲而不是在配置文件里手工覆盖。至于自己的传感器接入 VINS-Mono最小改动就三个地方yaml 里的imu_topic和image_topic改成实际话题相机内参替换成第 3 节标定的值最后用rostopic hz确认图像话题频率与 yaml 内image_rate一致。很多人在这一步漏掉图像分辨率图像尺寸是 640×480 却在 yaml 里写 752×480导致内参的 cx、cy 整体偏移轨迹直接扭曲。先用一台新设备把相机和 IMU 联合标定做完再跑五分钟静态验证零偏收敛曲线之后 VINS-Mono 的调试过程基本就不用再去怀疑传感器层面的问题了。本文还有配套的精品资源点击获取
