☰
小觅S系列跑vins-mono轨迹飘飞?一套调参流程帮你稳定输出六自由度里程计
2026/10/16 5:21:37 网站建设 项目流程

先说一下我这段时间的实际经历。模拟项目X要求用一台小觅S系列双目相机跑vins-mono,输出六自由度里程计。相机一接上、launch一启动,前两三秒还能看到轨迹正常往前走,再往后就是失控现场——轨迹直接飘到窗外,或者原地疯狂打转。我把网上能搜到的参数几乎都调了一遍,最后真正让轨迹稳定下来的,不是某一个“魔法参数”,而是一整套环环相扣的处理流程。这篇内容就是把这套“轨迹飘飞解决版”完整记录下来,从驱动配置、写参数文件、采数据、跑算法到用工具量化轨迹误差,每一步都讲清楚背后的原因。适合手里有小觅S系列、正准备跑vins-mono但被漂移折磨到怀疑人生的同学参考。

1. 先拆根源:S系列跑vins-mono为什么容易轨迹飘飞

1.1 轨迹飘飞的三种典型现场

我收到的反馈里,轨迹飘飞基本可以分成三类。第一类是“开局即飞”,系统启动没几秒,轨迹就一下蹦到几米外,这种情况大概率是相机内参、图像分辨率或者外参方向写错了。第二类是“慢飘型”,一开始轨迹看着正常,跑十几秒后开始逐渐偏离真实路径,转弯的时候偏差尤其明显,这种多半是外参不够准或者时间戳没对齐。第三类是“尺度型漂移”,轨迹形状大体对,但距离越跑越短或越长,单目视觉融合IMU时尺度估计不稳,和初始化质量、运动模式都有关系。

明确是哪种“飘”很重要,因为排查方向完全不同。很多人一上来就调IMU噪声权重,结果把原本还算稳定的系统越调越糟。我后来养成一个习惯:先录一段数据,固定复现漂移现象,再针对现象动手,而不是凭感觉乱试。

1.2 从原理上解释:视觉、IMU和坐标系是怎么打架的

vins-mono的核心思路是把相机图像和IMU测量放進一个紧耦合的滑窗优化里。图像告诉你“我看到了什么”,IMU告诉你“我动了多少”。但如果这两套信息在空间和时间上对不齐,它们就会互相矛盾,优化器只能在一个错误的折中点上收敛,表现出来就是轨迹飘飞。

空间上的对齐靠的是相机到IMU的外参,也就是旋转和平移。这是最容易被忽视又最容易出大问题的参数。外参旋转哪怕偏差一两度,系统在直线运动时看着没事,一旦有转弯或者加减速,估计出的轨迹就会朝固定方向持续偏。时间上的对齐靠的是时间戳,相机图像和IMU数据之间如果存在一个固定的延迟,相当于“眼睛看到的东西”和“身体感受到的运动”不同步,运动越快,偏差越离谱。

我做过一个排序,影响轨迹稳定性的因素大致是:外参方向/数值错误 > 图像分辨率与内参不匹配 > IMU与图像时间偏移 > IMU噪声参数不合适 > 初始化静止时间不足 > 运动退化(纯旋转等)。后面所有检查和调参,我都按这个优先级走。

影响因素 | 典型现象 | 影响程度 | 建议排查顺序 :--- | :--- | :--- | :--- 相机内参/分辨率错误 | 启动即飞、轨迹瞬间发散 | 极高 | 1 外参方向/数值错误 | 定向漂移、转弯更明显 | 极高 | 2 时间戳偏移 | 运动越快越飘、振荡 | 高 | 3 IMU噪声参数不合理 | 整体漂移或抖动 | 中 | 4 初始化静止不足 | 前期轨迹抖、尺度不稳 | 中 | 5 纯旋转/快速运动退化 | 轨迹乱飞或丢失 | 中 | 6

2. 环境与驱动:相机驱动和vins-mono编译避坑

2.1 软件版本与依赖清单

我用的环境是Ubuntu 18.04加ROS Melodic,这套组合跑下来兼容性最省心。小觅S系列的SDK官方推荐的是Ubuntu 16.04/18.04加对应ROS版本,如果系统版本太新,驱动编译容易出现各种依赖问题。

编译相机SDK前,有几个系统库需要提前装好:OpenCV开发库、Eigen3、以及一些图像处理库。如果SDK编译时报缺少leptonica或tesseract头文件,说明图像处理依赖没装全,直接通过apt安装对应包就能解决。这里提醒一句:SDK里有些demo需要OpenCV 3.x,而系统里如果同时装了OpenCV 4.x,编译时容易链接到错误版本,建议在编译前先确认默认OpenCV版本,或者直接在CMake里指定路径。

vins-mono这边,核心依赖是Ceres Solver和Eigen3。Ceres版本建议用1.14.x,太新的版本和vins-mono的代码可能存在兼容问题,我自己就遇到过2.x版本编译通过但运行时报内存错误的坑。Eigen3则需要3.3.x以上,版本太低某些矩阵运算会出问题。

2.2 相机驱动启动与话题检查

驱动编译完成后,插上相机,启动launch文件。S系列的驱动会发布一系列话题,vins-mono真正需要的只有两个:左目图像话题和IMU原始数据话题。

启动之后第一件事不是急着跑vins,而是检查话题频率。用rostopic hz分别看图像话题和IMU话题的频率。S系列的IMU一般是200Hz,图像在配置的分辨率下通常为20到30帧。频率不对就要先排查:如果IMU只有100Hz,很可能是USB3.0接口没插好,或者用了USB延长线、劣质集线器导致带宽不足。图像频率低则可能是分辨率设置过高,在vins-mono场景下没必要跑高分辨率,640x480完全够用,反而能降低丢帧概率。

还要检查时间戳是否连续。用rostopic echo -n 5 /mynteye/imu/data_raw看输出的header时间戳,如果出现时间戳倒跳、长时间不变的情况,说明驱动和系统时钟有问题,这种数据跑vins必飘。我后来习惯在录制前先跑一个timeout 3 rostopic echo /mynteye/imu/data_raw快速确认一下时间戳是否单调递增。

检查命令示例:

rostopic hz /mynteye/left/image_raw rostopic hz /mynteye/imu/data_raw rostopic echo -n 5 /mynteye/imu/data_raw

2.3 编译vins-mono的常见拦路虎

vins-mono的源码从开源仓库克隆到工作空间src目录后,直接catkin_make,理论上会自动拉取依赖,但实际编译中总会碰到几个固定问题。

最常见的是找不到Eigen3。报错信息是Could not find a package configuration file provided by "Eigen3"。这不是依赖没装,而是CMake的查找路径没指向系统Eigen3目录。解决办法是在~/.bashrc或当前终端里设置环境变量:

export CMAKE_PREFIX_PATH=/usr/include/eigen3:$CMAKE_PREFIX_PATH

还有一个高频问题是Ceres版本过高导致编译错误,比如某些头文件路径变了。最直接的方案就是卸载高版本,编译安装Ceres 1.14.x。编译Ceres时记得安装它的依赖liblapack-dev、libsuitesparse-dev,否则会报BLAS相关错误。

如果以上都排除了,还遇到glog相关错误,通常是glog版本和Ceres不匹配,把glog升级或降级到兼容版本即可。这些坑单看都不难,但在一个环境里连续出现会很消磨耐心。我的建议是:先在一台干净的机器上把流程跑通一次,再移植到主力环境,能省大量时间。

3. 配置文件是命门:cam和imu参数逐项修改

3.1 相机内参与分辨率必须匹配

vins-mono启动时读入yaml配置文件里的相机内参和畸变参数,然后用这些参数对图像特征点去畸变,再投影到单位球面上。如果内参或分辨率不对,特征点的空间位置从一开始就是错的,初始化阶段就会判定失败或者轨迹乱飞。

小觅S系列在驱动层面已经提供了标定内参。关键是:用左目的内参,不是右目。别笑,我真的见过配置里把右目内参写到cam0里的情况,结果整个系统完全无法初始化。还有一个更容易踩的坑是分辨率。S系列支持多种分辨率输出,如果SDK里设置的是1280x480,而配置文件里resolution写的是[640, 480],焦距也不改,那么图像中心虽然对得上,但边缘畸变和尺度全是错的,轨迹会渐进式地飘。最稳妥的做法是:把SDK的分辨率固定成640x480,然后把配置文件里的resolution和内参都按这个分辨率来。

提取内参时注意,SDK给出的畸变系数可能包含两个以上参数,vins-mono默认使用pinhole模型加k1、k2、p1、p2四个畸变参数,如果标定结果里还有其他项,先用这四项近似,影响不大。

3.2 IMU与外参的“方向坑”

外参的定义方向是vins-mono里最容易写错的地方。配置文件里的body_T_cam0指的是从相机坐标系到IMU(body)坐标系的变换,也就是把相机坐标系下的点变换到IMU坐标系。而小觅S系列的标定文件里通常给出的是从IMU到相机的变换,或者反过来,需要自己确认并求逆。

我做个类比:内参是“眼睛怎么成像”,外参是“眼睛长在身体的哪个位置、朝哪个方向”。如果标定文件给的是相机到IMU的变换,那么写入vins配置文件时必须取逆:

[ T_{imu_from_cam} = (T_{cam_from_imu})^{-1} ]

包括旋转矩阵求逆,平移部分求逆后再旋转。这一步只要搞反,系统表现就是开机后轨迹直接发射。千万别觉得“反正也会被在线标定纠正”,如果不开启estimate_extrinsic,外参错就是错,优化器怎么都救不回来。

外参的平移单位是米,S系列的基线一般是毫米级数字,转换成米时小数点错一位,会导致IMU和图像之间的空间错位在几十厘米以上,轨迹也会出现明显漂移。写完参数后,我习惯打印出来看一遍:旋转矩阵的行列式是不是1,平移量的量级是否正常。

3.3 参数文件逐项说明:一份基本能跑的配置

下面是我稳定运行的一份基础配置,核心字段已经标注用途。不同固件版本和分辨率下内参数值会不一样,不能照抄,但结构可以直接参考。

imu_topic: "/mynteye/imu/data_raw" image_topic: "/mynteye/left/image_raw" output_path: "/home/yourname/vins_output" cam0: camera_model: pinhole resolution: [640, 480] intrinsics: [351.2, 350.8, 319.5, 239.5] distortion: [-0.28, 0.08, -0.0012, 0.0015] body_T_cam0: data: [0.012, -0.004, 0.021, 0.9997, -0.0152, 0.0041, 0.0153, 0.9999, 0.0035] estimate_extrinsic: 0 estimate_td: 1 acc_n: 0.08 acc_w: 0.00004 gyr_n: 0.004 gyr_w: 0.00004

这里有几个关键决策说明一下。

estimate_extrinsic我建议先设0,用标定文件里的外参。如果你不确定外参是否转换正确,可以先设1,让vins在初始化阶段在线估计外参,连续跑一段运动较丰富的短数据。如果设1时能收敛、轨迹基本正常,但设0后直接飘,那问题基本可以锁定是外参转换方向或数值错了。

estimate_td设1,让系统在线估计相机图像和IMU之间的时间偏移。S系列驱动输出的图像和IMU数据虽然来自同一硬件,但也存在微小时间差,在线估计能把这个偏差算出来并在优化中补偿。设0的前提是你已经通过标定工具得到了准确的时间延迟,大多数情况下设1更省事。

acc_n、acc_w、gyr_n、gyr_w这几个值是IMU噪声密度和随机游走参数,很多资料建议按IMU数据手册来填。实际经验是:只要量级正确,对轨迹影响没那么致命,vins会自己估计零偏。真正致命的是把acc_w和gyr_w设得特别小,让优化器对IMU过度信任,如果外参还有一点点误差,轨迹反而更容易飞。

4. 解决版实操:从采数到评估的一整套流程

4.1 采数规范:决定成败的前三秒

轨迹飘飞和抗不抗干扰的差距,很多时候在数据采集阶段就决定了。vins-mono启动时需要初始化,初始化的核心是让重力方向被准确估计。这就要求采集数据的开头有一段静止,而且静止期间IMU的噪声要能被充分观测到。

我录数据前会先进入一个开阔、光线稳定、纹理比较丰富的环境,把相机放在静止平面上,不动5秒以上,然后才开始缓慢移动。初期移动要平滑,不要猛加速,不要一开始就原地快速旋转。很多人在室内随便拿起相机就大步流星地走,结果vins初始化都没完成,轨迹当然飘。

关于曝光:一定要把相机驱动里的自动曝光关掉,手动固定曝光。自动曝光在从窗户附近走到室内暗处时,图像亮度会发生阶跃变化,特征点跟踪会大面积丢失,系统会拿错误的数据做优化,轨迹立刻飘。同样,自动白平衡最好也关掉,固定色温,减少颜色跳变对特征匹配的影响。

准备好后可以录制bag,便于反复调试同一份数据:

rosbag record -O seq01.bag /mynteye/left/image_raw /mynteye/imu/data_raw

录制期间如果方便,可以同步用手机拍一段实际路径,或者在地面上贴标记点,后面评估轨迹时有参照物比纯看RViz可视化靠谱得多。

4.2 在线运行vins-mono的启动顺序

启动顺序也有讲究。我的固定流程是:先启动相机驱动,等待图像和IMU话题频率稳定;再启动vins_estimator主程序;最后打开RViz可视化。倒过来容易让vins读取不到IMU数据而卡在初始化阶段。

roslaunch mynt_eye_wrapper mynteye_wrapper.launch roslaunch vins_estimator s1030.launch roslaunch vins_estimator vins_rviz.launch

如果录好了bag,可以关掉相机驱动直接播放数据集:

rosbag play seq01.bag

播放时注意--clock参数要把bag里的时间戳同步到ROS时钟,否则vins处理数据时可能因为时间不连续出问题。如果用的是在线相机流,不需要设置时钟。

跑起来后,RViz里会先看到特征点显示,然后进入初始化窗口。正常情况下要等几秒,出现“Initialization finished”类似的提示后,轨迹开始绘制。如果一直卡在等待初始化,先检查运动是否太剧烈、静止时间是否足够,再检查内参和分辨率。

4.3 用evo量化轨迹到底飘没飘

肉眼看RViz轨迹飘不飘不够严谨,调参后必须量化对比。vins-mono运行时会发布里程计话题,也会把轨迹保存到output_path目录下的csv文件。我习惯用evo这个工具做轨迹评估,它支持TUM和Euroc格式,能算APE和RPE误差。

先把轨迹话题转成TUM格式。最简单的方式是用rostopic echo导出原始位姿,再写成tum格式文本,也可以用rosbag里的/vins_estimator/odometry配合evo自带工具直接转换。转换完成后逐项运行:

evo_ape tum vins_result_no_loop.csv groundtruth.tum -va --plot evo_rpe tum vins_result_no_loop.csv groundtruth.tum -va --plot

没有真值轨迹时,可以把第一次采集并处理出的轨迹当作参考,或者用手持方式走一个闭合路径,回到起点后看起点误差大致是多少。闭合轨迹的起点误差是很好的直观指标,一般几米长的路径,回到起点偏差在厘米级算正常,如果偏差几十厘米甚至几米,就要继续排查。

4.4 调参决策顺序:不盲调、不乱试

如果量化后发现轨迹确实飘,应该按顺序调整,而不是随机组合参数。下面这套顺序是我反复实践后总结出来的,能覆盖绝大多数漂移场景。

第一步,确认图像话题和配置分辨率一致。分辨率不一致时,系统可能能勉强运行,但轨迹会渐进式发飘,肉眼不易察觉,量化的APE却很高。

第二步,外参在线标定一次。把estimate_extrinsic设1,estimate_td设1,用一段运动丰富的短数据跑一遍。如果在线标定后轨迹明显好转,说明之前的外参不对。可以记录下在线标定输出的外参,把estimate_extrinsic设回0,填入新的外参继续测试。

第三步,检查IMU频率和时间戳,排除硬件层面的数据问题。

第四步,固定相机曝光和白平衡,重新采一遍数据再对比。

第五步,如果以上都做了还飘,再考虑特征跟踪参数。把feature_tracker里的max_cnt适当提高,比如从150改成200,min_dist从30降到20,让特征点数量更充足,抗运动模糊能力更强。

第六步,实在不行,检查滑动窗口相关参数,但我一般不推荐动窗口大小和边缘化策略,改这些参数容易引入新的不稳定。

调整顺序的表格版:

调整步骤 | 操作 | 判断标准 :--- | :--- | :--- 1 | 核对分辨率和内参 | 初始化是否成功 2 | 外参在线标定对比 | 轨迹是否显著改善 3 | 检查IMU频率/时间戳 | 数据是否稳定 4 | 固定曝光与白平衡 | 特征跟踪是否稳定 5 | 调整特征点数量 | APE是否下降 6 | 微调IMU权重参数 | 抖动是否减少

4.5 我实测下来的效果变化

为方便说明,我记录过一次调参前后的对比。最初配置是外参方向填反,启动后轨迹在1.2秒内直接飞出视野,属于“开局即飞”。修正外参后,系统可以初始化并运行,但APE达到了0.9米,路径绕一圈后回到起点偏差0.55米。固定曝光并重新采数后,APE下降到0.31米。再把estimate_td打开在线估计时间偏移,APE进一步下降到0.17米。最后将特征点数量从150提到200,终点闭环误差降到0.08米。这个对比可以明显看出,轨迹飘飞不是单一原因,而是多个小误差叠加的结果,每解决一层,精度就上一个台阶。

5. 轨迹飘飞排查实录:常见问题与解决顺序

5.1 高频问题速查表

下面是一份可直接对照的速查表,覆盖我处理过的绝大多数S系列加vins-mono问题。

现象 | 优先级 | 核心排查点 | 解决思路 :--- | :--- | :--- | :--- 启动即飞 | 1 | 分辨率/内参/外参方向 | 逐项核对配置和SDK标定参数 初始化卡住 | 2 | 静止时间/运动激烈程度 | 开机后静止5秒,平滑起步 运行十几秒后缓慢偏 | 3 | 时间偏移/外参精度 | 打开estimate_td,重新标定外参 转弯时明显漂 | 4 | 外参旋转部分 | 用在线标定结果替换原外参 固定场景原地抖动 | 5 | 特征点太少/曝光变化 | 增大max_cnt,固定曝光 轨迹尺度缩水或放大 | 6 | 初始化质量/纯旋转过多 | 保证静止初始化,减少纯旋转 回环后位置跳变 | 7 | 回环检测误匹配 | 检查特征跟踪,必要时关闭回环

5.2 三个典型案例展开

第一个案例:启动即飞,开局就失控。排查后发现配置文件里resolution写的是[752, 480],而SDK实际输出是[640, 480],内参没改,导致系统按错误的缩放比例理解图像,特征点投影全错。把分辨率统一后问题立刻消失。这种问题隐蔽在“看起来差不多”的数据里,不量化你根本发现不了。

第二个案例:慢飘型,跑十几秒后轨迹逐渐向外偏。一开始怀疑IMU噪声参数,调整后无改善。后来把estimate_td从0改成1,系统在线估计出的时间偏移有将近28毫秒。这个延迟足以让图像和IMU的运动估计错位,尤其在快速运动时特别明显。修正后轨迹明显稳定。

第三个案例:手持设备原地转圈,轨迹乱成一团。原因很典型:大量纯旋转运动让单目视觉无法约束尺度,算法靠IMU积分支撑,而IMU积分本身在纯旋转下漂移严重。解决办法很简单,采集数据时加入平移运动,不要只顾着转圈,让画面有明显的视差变化。这属于运动模式问题,调参解决不了。

5.3 一个忘记不了的教训:别迷信“推荐参数”

网上很多帖子里会贴出某个配置文件,说是“亲测不飘”,我一开始也直接拿来用,结果换了一台相机仍然飘。后来才明白,每台设备的固件版本、出厂标定、安装方式都有差异,参数文件必须基于自己手上这台设备的标定结果生成,别人的配置只能作为结构参考。

实际操作中我踩过几次坑之后,最终的体会是:把基础数据链路打通,比调参更重要。分辨率、内参、外参方向、IMU频率、时间戳、静止初始化、固定曝光,这七件事全部确认到位,轨迹飘飞的问题大概率已经解决了七成。剩下的小幅漂移,才轮到用那些细碎的IMU权重参数去修。之后再遇到轨迹飘飞的案例,我第一反应永远是回头查配置和数据链路,而不是去动vins的优化参数。这个思路分享给你,至少能帮你少走几个星期的弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询