
1. 为什么我最终选了VINS-Fusion视觉惯性导航系统的选型逻辑1.1 视觉惯性导航到底解决了什么问题先聊一个很实际的场景你有一台无人机想在仓库、停车场、桥洞这些GPS信号被遮挡的环境里让飞机稳住位置或者沿着一条走廊自主飞行。光靠视觉SLAM快速旋转和剧烈运动时位姿容易飘光靠IMU十几秒就开始天翻地覆。视觉惯性导航系统Visual-Inertial Navigation System, VINS做的事情就是把摄像头的图像信息和IMU的角速度、加速度信息紧耦合在一起互相弥补短板输出一个长时间稳定、短时间又跟得住的位姿估计。VINS-Fusion是香港科技大学空中机器人实验室开源的项目在VINS-Mono的基础上扩展而来。它最大的特点是不挑传感器配置单目IMU、双目IMU、纯双目、双目IMUGPS都能跑而且带全局重定位和闭环功能。这个项目我从大三做毕业设计开始接触到现在在几个实际项目里落地过可以说是踩坑踩出来的经验。这篇文章我想把从零搭建的完整路线讲清楚重点是ROS环境配置、传感器标定、参数理解还有无人机上真机部署时那些文档里不会写的事。适合谁来读打算用VINS-Fusion做无人机定位、机器人导航或者正在选型视觉SLAM方案的开发者。如果你是第一次听说这个项目也不用慌我会从环境准备到实机部署一步一步讲保证你照着做能把demo跑起来。1.2 为什么是VINS-Fusion而不是ORB-SLAM3、MSCKF圈子里的开源方案不少我简单对比一下你就明白为什么我最终把VINS-Fusion作为主力方案了。ORB-SLAM3是目前纯视觉和视觉惯性混合方案里效果很能打的一个支持单目、双目、RGB-D也支持IMU。但它的源码结构偏重对初学者不太友好而且在视觉惯性紧耦合的细节上VINS-Fusion的代码可读性更好也更容易做二次开发。MSCKF是另一个经典的基于滤波器的方案松耦合速度快但精度和鲁棒性不如基于滑窗优化的紧耦合方案。VINS-Fusion采用滑动窗口形式的紧耦合优化什么意思呢就是它不会把历史上所有关键帧都拿来优化而是维护一个长度有限的窗口新帧进来旧帧踢出去控制算力消耗的同时保证精度。这种设计非常适合无人机这种算力有限、实时性要求高的平台。另外它还带一个全局位姿图能消除长时间运行时的累积漂移这对巡检、测绘这类长时间任务非常重要。在我看来选VINS-Fusion最大的理由有三个第一传感器配置灵活单目双目随便切第二GPS融合模块是现成的户外无人机可以直接用第三社区活跃、资料多遇到问题至少有迹可循。当然它也有短板比如对动态物体比较敏感、对曝光变化大的场景鲁棒性一般这些后面讲调试时会细说。2. 先别急着写代码ROS环境和依赖库的版本陷阱2.1 ROS版本选择和安装方式VINS-Fusion是基于ROS开发的所以第一步是把ROS装好。ROS版本框架我已经很熟悉了但还是要提醒一句ROS的选择跟Ubuntu版本强绑定Ubuntu 20.04对应ROS NoeticUbuntu 18.04对应MelodicUbuntu 22.04对应ROS 2的Humble。VINS-Fusion官方是基于ROS 1写的所以如果你用的是Ubuntu 20.04直接装Noetic最省事这也目前社区里验证过的最稳组合。这里分享一个我个人的经验装ROS环境的时候与其自己折腾源和依赖不如直接用国内社区维护的一键安装脚本比如“鱼香ROS一键安装”。这个脚本会自动配置软件源、安装ROS、初始化rosdep几分钟就能把环境搞定。我第一次手动配源的时候踩了一下午的坑apt源、rosdep update、密钥过期全是拦路虎。后来改用脚本命令行里敲一下剩下就是等。当然动手能力强的同学也可以自己配本质上就是那几步但新手我真心建议用脚本省时间。注意不管用哪种方式安装装完后一定记得执行source /opt/ros/noetic/setup.bash并且把这句话写进~/.bashrc里不然每次新开终端都找不到ros命令。2.2 依赖库编译顺序与版本陷阱VINS-Fusion的核心依赖有三个Eigen3、Ceres Solver、OpenCV。这里面最容易出幺蛾子的是Ceres Solver它的版本非常敏感。先说Eigen3它是一个头文件库理论上不用编译直接把头文件路径指过去就行。但要注意VINS-Fusion对Eigen版本有要求太新的版本比如3.4以上可能会跟Ceres产生兼容性问题。我建议用apt安装系统自带的版本Ubuntu 20.04自带的Eigen 3.3.7搭配Ceres 1.14.0是验证过的组合千万别手痒去编译最新版Eigen没必要也没好处。Ceres Solver是VINS-Fusion优化求解的核心。官方推荐1.14.0版本太新的版本可能会在编译VINS-Fusion时报错因为API有变化。我之前试过用Ceres 2.1.0直接编译结果一堆函数签名对不上最终还是老老实实切回1.14.0。编译Ceres之前还要先装gflags和glog这两个依赖不然会报找不到库。命令顺序大概是sudo apt-get install libgoogle-glog-dev libgflags-dev libatlas-base-dev git clone https://github.com/ceres-solver/ceres-solver.git cd ceres-solver git checkout 1.14.0 mkdir build cd build cmake .. make -j4 sudo make installOpenCV的话ROS Noetic自带的OpenCV 4.2一般够用。VINS-Fusion的camera_model模块对OpenCV版本要求不高4.x完全没问题。唯一要注意的是如果你在同一个环境里装了多个版本的OpenCV编译时cmake可能会找错头文件建议用系统自带版本别自己源码编译OpenCV全家桶。2.3 编译VINS-Fusion源码的正确姿势环境装好之后开始编译VINS-Fusion本体。官网的README已经写得很清楚了我先说标准流程再说怎么避坑。mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone https://github.com/HKUST-Aerial-Robotics/VINS-Fusion.git cd ~/catkin_ws catkin_make理论上这三条命令就能编完但实际编译过程中最容易遇到三个问题。第一个是Ceres版本不匹配导致的编译失败如果你不巧已经装了高版本的Ceres建议先卸载再装1.14.0。第二个是缺少一些小的依赖库比如libsuitesparse-dev报错信息会提示找不到SuiteSparse相关的头文件直接sudo apt-get install libsuitesparse-dev就能解决。第三个是编译时内存爆掉catkin_make默认并行编译如果在虚拟机或老电脑上编译建议加上-j2限制并行度不然容易卡死。编译完成之后用source devel/setup.bash激活环境然后跑一下官方提供的欧洲数据集测试包如果能跑起来看到画面说明环境搭建这一步就算彻底通了。3. 硬件选型与标定决定系统上限的关键一步3.1 相机与IMU选型的关键参数很多人以为算法是决定SLAM效果的全部其实不是。在无人机视觉惯性导航里硬件选型的重要性一点都不比算法低传感器数据质量差了算法再强也救不回来。相机方面我强烈建议用全局快门Global Shutter的相机不要用卷帘快门Rolling Shutter。无人机在飞行中时刻在旋转和震动卷帘快门会让画面出现果冻效应特征点位置直接扭曲VINS的前端跟踪和三焦约束都会出问题。我见过很多人图便宜买普通USB摄像头结果跑起来位姿疯狂漂移还以为是参数没调好其实就是快门类型不对。另外一个关键参数是帧率。VINS-Fusion官方建议相机帧率在20到30Hz之间IMU频率在100到200Hz之间。有些低成本方案用10Hz的相机前端特征跟踪会非常吃力动态性能也很差。之前有个项目用了15Hz的相机跑倒是能跑但稍微快速运动一下位姿就剧烈波动后来换了30Hz的全局快门工业相机问题立刻消失。IMU方面VINS-Fusion需要的是线性加速度计和陀螺仪数据。关键的指标是噪声密度和随机游走这两个参数会直接影响标定结果和系统精度。无人机飞控板载的IMU一般能用但如果追求更好的效果可以考虑独立的IMU模块比如MicroStrain系列。有一点要注意IMU的量程要够大无人机在飞行中角速度经常会超过300度/秒如果量程不够数据直接饱和系统就废了。3.2 传感器标定内参、噪声、外参一次说清传感器装好了不能直接跑必须先做标定。VINS-Fusion对标定的依赖比我第一次用的时候想象的要大得多尤其是相机和IMU之间的外参不对齐的话整个系统就是废的。相机内参标定有两种方式。第一种是用Kalibr工具适合要同时做相机-IMU外参标定的场景第二种是直接用ROS的camera_calibration功能包打印一张棋盘格在相机前来回晃动录制数据包几分钟就能出结果。对新手来说先走camera_calibration把内参搞定后续熟悉了再上Kalibr。IMU噪声标定是用imu_utils工具操作很简单把IMU固定在一个绝对静止的平面上录制至少2个小时的静止数据然后交给imu_utils分析。它会输出噪声密度和随机游走参数这些数字后面要填进VINS-Fusion的配置文件中。这里有个容易忽略的细节静止数据的时间越长标定结果越可信网上有人说录10分钟就够实测下来数据波动还是挺大的我建议至少1小时起步。相机-IMU外参标定是很多人觉得最难的一步其实原理不复杂。用Kalibr的kalibr_calibrate_imucam工具先录制一段同时包含IMU数据和棋盘格图像的数据包录制时让系统做充分的旋转和位移激励然后工具会根据视觉运动和IMU运动的偏差反算出外参。录制这段数据时有一个技巧动作要快、要剧烈但又不能让棋盘格出画面。太慢的话IMU信号弱标定结果方差大出画面的话视觉跟踪断了这段数据就废了。大概录2到3分钟反复几次挑一组结果最好的。3.3 用rosbag采集高质量数据标定完成之后还有一个最关键的数据采集步骤。很多人忽略rosbag录制时的话题发布频率和消息时间戳问题结果后面分析时发现数据根本对不上。采集数据前先确定要录制的传感器话题。比如你用的是双目相机加IMU那rostopic list里至少应该有左右图像话题和IMU话题。录制命令大概是rosbag record -O my_data /cam0/image_raw /cam1/image_raw /imu/data录制过程有一些硬性要求尽量在纹理丰富的环境下采集避免面对白墙或者大面积纯色区域相机运动不要太突兀旋转和直线运动交替进行模拟无人机典型飞行状态录制时间不要太短针对后期测试和算法调优2到5分钟比较合适数据量大的时候注意存储空间30Hz的双目图像加上200Hz的IMU一分钟就能吃掉几百MB。还有一个容易踩的坑IMU话题的坐标系定义。VINS-Fusion要求IMU数据遵循ROS标准REP-103约定即x轴向前y轴向左z轴向上。但很多飞控输出的IMU坐标系定义不一样比如PX4是NED坐标系x向前y向右z向下。如果你直接用飞控的IMU数据发到ROS里坐标系不对会导致整体算法输出完全错乱。解决方法是看清楚驱动代码里的坐标变换在发布时把它转成ENU约定。4. 数据集先跑通VINS-Fusion配置与运行实操4.1 配置文件的每一个参数是什么意思在跑数据之前得先把配置文件搞懂。VINS-Fusion的配置文件在config目录下每个传感器配置对应一个yaml文件。我拿双目的配置文件举个例子逐个说重点参数。imu_topic和image_topic不用多说就是订阅的话题名必须跟你的传感器驱动发布的话题完全一致。imu_rate和image_rate是IMU和图像的频率填错的话系统的传感器时间同步会出问题。roll_in_cam、pitch_in_cam、yaw_in_cam是外参的旋转部分来自标定结果x_in_cam、y_in_cam、z_in_cam是外参的平移部分。estimate_extrinsic这个参数很有意思0表示使用文件中的外参不进行在线标定1表示完全不信任外参文件VINS会在线估计2表示外参文件作为初始值在线优化。我建议第一次跑的时候设成0等你确认外参标定准确后再往上加。如果你设成1或2系统有了“重新估外参”的退路有些时候标定不准也跑得起来但那样可能掩盖了问题。max_solver_time和max_num_iterations是优化器的时间限制和迭代次数限制。这个参数不是越大越好它决定了每个优化周期花多少算力。无人机上机的时候我一般把max_solver_time设为0.03秒既能保证精度又不会拖垮实时性。keyframe_parallax是关键帧选择的视差阈值单位是像素。数值越小关键帧越密集跟踪越稳定但算力消耗越大数值越大关键帧越稀疏算力省但精度可能下降。默认值10是一个比较均衡的设置。4.2 下载公开数据集把整个流程先跑通一遍不要一上来就用自己采集的数据先用公开数据集把流程走一遍这是最稳妥的做法。VINS-Fusion官方提供了一个Euroc数据集的示例这个数据集包含双目图像、IMU数据和地面真值轨迹而且有Vicon运动捕捉系统提供的真值方便你量化评估精度。拿到数据集之后运行命令非常简单roslaunch vins_estimator euroc.launch rosbag play MH_01_easy.bag第一次跑通的时候你可能会发现RViz里一片空白什么都不显示。这大概率不是算法问题而是RViz的配置文件路径不对。解决方案是手动添加显示项订阅/vins_estimator/odometry话题选择对应的坐标系就能看到轨迹和位姿了。这个坑虽然小但第一次遇到真的会卡很久。跑通之后你可以用EVO工具评估定位精度。这个工具是SLAM领域做轨迹评估的标准工具支持多种数据格式。VINS-Fusion运行结束后会输出vins_result_loop.csv这个文件包含估计轨迹。用EVO和数据集自带的真值对比就能得到APE绝对位姿误差等指标。以MH_01这个序列为例跑得好的话APE大概在0.1到0.2米之间。如果你跑出来精度差很多大概率是传感器参数标定有问题或者配置文件的参数没填对。4.3 实机数据调试从崩溃到稳定的调参路线公开数据集跑通后就可以尝试用自己采集的rosbag数据了。这一步是真正的考验。第一次跑自己数据常见的结果就是轨迹发散、定位崩掉。不要慌按顺序排查。先看前端特征点跟踪是否稳定在RViz里打开特征点显示如果特征点快速消失或者跳来跳去说明图像质量或者帧率有问题回到数据采集环节找原因。再看IMU预积分是否正确打开/vins_estimator/imu_propagate显示如果预积分轨迹和前端视觉轨迹方向相反大概率是IMU坐标系方向搞反了。我印象最深的一次调试是在一个工厂车间里环境灯光是50Hz交流电荧光灯相机画面有明显的频闪。特征点提取数量倒是不少但每次闪烁都会导致特征点位置抖动VINS出来轨迹一直在漂。后来换成了抗频闪能力更好的工业相机并且在相机驱动里锁定了曝光时间问题才解决。这件事给我的教训是SLAM系统是个完整链路任何一环数据质量不过关最后都是算法背锅。参数调优方面如果VINS输出有轻微抖动可以适当降低max_solver_time让优化器不要过度迭代如果跟踪容易丢可以适当降低keyframe_parallax让关键帧更密集如果IMU权重不够可以把imu_weight调大让优化更相信IMU的短期预测。调参的核心原则是一次只改一个参数观察效果再改下一个。5. 上机部署从算法位姿到飞控控制环的最后一公里5.1 机载平台选择与算力规划到这一步你的VINS-Fusion已经在电脑上运行得很稳定了接下来要把它搬到无人机上。机载计算平台的选择直接影响系统表现。VINS-Fusion对算力要求不算极端但也不是随便一个树莓派就能带得动。双目的前端特征提取、光流跟踪、滑窗优化30Hz运行时CPU占用基本在50%到80%之间。NVIDIA Jetson Orin Nano是目前性价比很高的选择64GB版本的模组跑VINS-Fusion配合PX4飞控整套流程CPU和GPU都有富余。如果预算有限Jetson Nano也能勉强跑但只能跑14到15Hz实时性比较吃力。运行系统的过程中要特别注意散热。Jetson系列在无人机这种密闭空间里跑满载温度很容易冲到85度以上一旦降频VINS输出的实时性就会下降。我之前在夏天试飞忘记给Jetson改散热结果起飞后一分多钟就高温强制降频VINS输出频率掉到10Hz以下飞控直接切手动模式。后来换了大面积散热片并加了小型风扇问题才解决。5.2 坐标系变换ENU与NED的恩怨把VINS的输出喂给飞控之前必须先解决坐标系对齐问题。这里有一个新手几乎必踩的坑。VINS-Fusion输出的世界坐标系是ENU约定也就是x向东y向北z向上这是ROS的标准坐标系约定。但无人机飞控尤其是PX4和ArduPilot内部用的是NED坐标系x向北y向东z向下。如果你直接把VINS的位姿发给飞控飞控拿到的相当于一个“歪”的坐标系飞机会朝各种奇怪的方向乱飞甚至直接翻车。正确的做法是在发布VINS位姿到MAVROS之前做一次坐标系变换。这个变换的旋转矩阵是固定的只要你确认了VINS输出是ENU、飞控接收是NED就可以写死在代码里。具体的变换公式涉及到绕z轴旋转180度再绕x轴旋转90度这样的顺序细节容易搞混。我建议在代码里做这个变换时先做几个小时的静态测试把无人机平放在地上初始化VINS然后手动把机头朝各个方向转看飞控接收到的位置和姿态是否跟实际情况一致。确认方向没问题了再装桨试飞这一步真的不能省。5.3 通过MAVROS把位姿喂给飞控坐标系搞定了接下来就是通信层面的问题。最常用的方式是用MAVROS把VINS的位姿发布到/mavros/vision_pose/pose话题飞控通过MAVLink协议接收视觉位姿数据实现精确定位。MAVROS的配置文件需要做一些调整。关键的一个参数是mavros/px4_config.yaml中的坐标系设置把vision相关的坐标系设置为与你的VINS输出一致避免MAVROS内部再额外做一次变换。另外在PX4的QGroundControl里需要把EKF2_AID_MASK设置为包含视觉位置融合否则飞控根本不理你发的视觉数据。上线测试时不要直接切到Offboard模式起飞。先在 Position 模式下让飞机起飞悬停时加入视觉定位观察飞机是否有异常的漂移或震荡。确认视觉位姿稳定后再切换模式做更激进的测试。这里还有一个实践经验VINS输出来自相机天然有几十到上百毫秒的延迟。飞控融合时如果不对这个延迟做补偿快速运动时会出现明显的滞后。MAVROS里可以设置时间戳偏移VINS代码里也会输出每帧的时间戳你可以在数据采集时做一下时间戳对比把固定延迟补偿掉。这个细节做好了飞行的跟手性会好很多。6. 实战翻车总结常见问题与排查技巧6.1 编译阶段的高频报错先说编译阶段的问题这是很多人第一个卡住的地方。最典型的是Ceres版本问题症状是编译VINS-Fusion时报no matching function for call to之类的错误。解决办法我已经在前面说过卸载高版本Ceres装1.14.0。另一个高频报错是找不到Eigen3这通常是系统里有多个Eigen版本apt安装的路径和源码编译的路径混在一起导致cmake找到了错误版本。可以用echo $EIGEN3_INCLUDE_DIR来排查环境变量。还有一个小众但很坑的问题如果你在Ubuntu 18.04上用Melodic版本OpenCV默认是3.2VINS-Fusion的camera_model模块里有些API在OpenCV 3.2上跑会有deprecated警告虽然不影响编译但编译输出一大片黄色警告容易掩盖真正的错误信息。建议编译时加-DCMAKE_BUILD_TYPERelease减少噪音。6.2 运行阶段的经典症状运行阶段的故障五花八门我把见过次数最多的几个整理成一个速查表症状可能原因初步排查方法RViz里没有轨迹显示没有订阅正确话题或坐标系错误检查是否订阅/vins_estimator/odometry确认固定坐标系设为world启动后长时间不初始化运动不够、纹理少、IMU数据未到检查rostopic hz确认IMU有数据尝试拿着相机晃动几秒轨迹在几个方向上跳动IMU坐标系方向错误或外参不准重新验证外参检查IMU数据的方向和加速度方向运行一会后位姿漂移严重某段数据质量差、曝光异常或动态物体太多回放bag观察画面帧确认无过曝和大量移动物体前端特征点数量极少图像灰度化问题或曝光过大检查相机曝光设置检查是否需要开启自动白平衡这里我想特别强调一下“启动后长时间不初始化”这个现象。VINS-Fusion的单目模式需要足够视差才能初始化如果你把相机固定在一个位置不动那它永远初始化不了。正确的做法是启动后让无人机手动原地转几圈或者拿着相机做平移旋转运动给它足够的视差。很多新手以为程序卡死了其实是初始化条件没满足。6.3 精度与稳定性的提升心得如果系统能运行但精度不够可以从下面几个角度逐一排查和优化。首先是时间同步这是最容易忽视但影响最大的因素。理想情况下相机和IMU应该有硬件同步信号比如相机触发输出同步IMU采集。没有硬件同步的情况下尽量用软件同步确保相机和IMU的时间戳是基于同一个时钟源。我见过一个案例相机驱动和IMU驱动分别用不同的系统时钟结果两者时间戳差了200毫秒VINS跑出来的轨迹严重扭曲后来统一了时间戳来源精度提升了一个量级。其次是动态物体。VINS-Fusion本身没有动态物体剔除机制如果在巡检场景里频繁有人或车经过特征点会被动态物体带着跑造成轨迹偏移。简单的做法是在预处理前端加一个动态物体检测把动态物体上的特征点过滤掉系统稳定性会有明显提升。还有一个提升精度的小技巧把max_solver_time适当调大给优化器更多迭代时间精度会有小幅提升。这个参数跟算力是直接换算的关系。在机载平台算力有余量的情况下从0.03秒加到大0.05秒配合max_num_iterations同步加大能获得约10%到20%的精度提升。6.4 我建议新手先走的顺序最后分享一下我对新手的学习路径建议这是我带过不少实习生之后总结出来的。第一步在纯电脑上用官方Euroc数据集把VINS-Fusion跑起来体会它的输出和RViz显示。第二步用手持相机录制自己的bag数据做一次内参标定、IMU标定和外参标定跑通自己的数据。第三步把传感器装到无人机上在地面测试坐标系、时间同步和位姿输出。第四步小范围低空飞行测试关注逻辑和参数的稳定性。第五步逐步扩大飞行范围和环境复杂度验证系统的鲁棒性。很多人第一步和第二步都顺利到了第三步就开始翻车。根本原因是从“数据来自公开数据集”到“数据来自自己的无人机硬件”之间多了很多工程问题。我自己走过这条路最大的体会是VINS-Fusion作为一个开源算法框架已经相当成熟真正决定系统好不好用的是传感器标定、时间同步、坐标系对齐这些工程细节。如果你在这些基础工程上下了功夫VINS-Fusion会在后面的实际使用中给你足够的回报。