ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

VINS-Mono地图保存重载与evo轨迹评估实战指南

VINS-Mono地图保存重载与evo轨迹评估实战指南 VINS-Mono跑通一阵子之后最先被问到的问题通常不是“怎么调精度”而是地图能不能保存下来下次重新开机直接重定位以及怎么证明我改出来的轨迹比原版好前者要用到VINS-Mono的位姿图保存与重载后者几乎绕不开evo这套轨迹评测工具。这两个功能叠在一起恰好构成视觉惯性导航开发里最常用的调试闭环。这篇文章不讲花哨的算法推导直接围绕地图保存、重载和evo评估三个环节把配置、命令和踩坑点一次说清。如果你是正在做VINS二次开发或者刚入门SLAM想验证系统精度可以直接按步骤抄作业。1. 先认清VINS-Mono里的“地图”到底是什么1.1 地图的本质是位姿图不是导航栅格许多刚接触SLAM的人一听到“地图保存”就会下意识想到栅格地图、八叉树地图或者点云地图。这个直觉用在Cartographer或者LOAM系列里是对的但放到VINS-Mono身上就会跑偏。VINS-Mono的产品定位是视觉惯性里程计加上轻量姿态图它保存的地图核心是稀疏关键帧的位姿集合以及这些关键帧之间的共视关系和回环约束。换句话说保存的是“机器人走过哪些位置、从哪些角度看到过什么特征”这一类拓扑和几何信息而不是给路径规划用的二维占据栅格。这个差异很重要。VINS-Mono在重载地图后做的事情是通过词袋模型DBoW2检索当前帧的关键帧候选然后用PnP求当前位姿再把当前帧坐标系对齐到全局地图坐标系上。栅格地图做不到这件事因为栅格丢掉了关键帧的描述子信息而VINS-Mono的可复用地图恰恰是围绕关键帧组织和索引的。所以你在RVIZ里看到的地图是一串相机位姿和稀疏点不是一片能直接做避障的栅格这很正常。1.2 保存重载解决的真实痛点在建图场景重复性高的项目里每次重启都从零初始化是一种浪费。单目VINS初始化需要足够的视差和IMU激励如果机器人静止不动、或者场景纹理很弱初始化可能要等很久甚至在原地打转。保存地图以后下一次开机只要VIO完成了局部初始化系统就会尝试去匹配保存的关键帧一旦匹配成功位姿就会跳到全局地图里相当于省掉了从零建立全局约束的过程。这在实际产品里对应一堆硬需求AGV回到充电桩要能精确对位、巡检机器人每天走固定路线要能快速恢复、无人机降落后重新起飞不希望重新构建一遍环境模型。即便你的项目不做多机器人协同单会话内保存重载也能在程序崩溃恢复、长距离闭环优化这些场景里省下大量时间。需要注意VINS-Mono原版更偏单机单会话多机共享地图不是它的强项但单机场景的“回桩”“重启恢复”已经能覆盖不少落地需求。1.3 地图文件长什么样VINS-Mono保存地图时目录下通常会出现一个名为pose_graph_map.pkl的序列化文件。这个文件把关键帧位姿、全局位姿、回环边信息、词袋特征打包在一起。文件里除了回环检测要用的特征描述子外也会记录关键帧之间的相对变换所以在代码里可以看到保存函数会遍历优化过的位姿图把节点和边逐个序列化写入。除了这个主文件工作目录下往往还有keyframe_path.txt、vins_result_loop.csv、vins_result_no_loop.csv这类输出。它们不是地图的必要组成部分但在地图调试时特别好用vins_result_loop.csv就是带回环优化的轨迹vins_result_no_loop.csv是纯VIO轨迹两者对比能看出回环到底修正了多少漂移。后面用evo做精度评估的时候这两个文件可以直接拿来转TUM格式完全不需要重新录包。2. 保存与重载地图的完整配置方案2.1 关键参数逐个说清在VINS-Mono里跟地图保存重载直接相关的配置主要在Estimator的yaml文件里launch文件只是负责把配置文件路径传进去。通常你会在config/euroc/euroc_config.yaml一类文件里看到这样几行pose_graph_save_path: /home/yourname/catkin_ws/src/VINS-Mono/output/pose_graph/ load_previous_pose_graph: 0pose_graph_save_path是地图文件的保存目录保存时节点会往这个路径写pose_graph_map.pkl。这个路径必须是已经存在的目录而且最好以斜杠结尾否则拼接文件名时容易踩坑。load_previous_pose_graph置0表示不加载旧地图置1表示启动阶段把上述路径里的地图读进来。参数建图时重载时作用pose_graph_save_path相同相同地图读写路径load_previous_pose_graph01是否加载旧地图vins_path工作空间路径工作空间路径资源文件路径基准还有一个经常被忽略的点launch文件里的vins_path参数。VINS会用它计算模型文件、词典文件等资源的相对路径如果你改了工作空间目录vins_path不跟着改启动时就会报找不到文件地图自然也就保存不了。所以改完yaml之后先确认launch文件里的vins_path指向的是当前工作空间再跑程序。2.2 建图并保存地图的完整操作先跑一次完整建图。以一个EuRoC数据集为例通常分三步roslaunch vins_estimator euroc.launch rosbag play MH_01_easy.bag数据集播完后另开一个终端向保存话题发一个字符串消息rostopic pub /pose_graph/save_pose_graph std_msgs/String --once data: savepose_graph节点收到这个消息后就开始序列化位姿图。此时VINS终端通常会打印保存路径你可以看到类似Save pose graph to ...的输出。保存的时机建议放在数据集播放完毕后因为此时全局优化已经收敛回环约束已经充分传递保存出来的地图质量最高。如果你的程序改了话题名或者使用了自定义launch先确认话题是否存在rostopic list | grep save_pose_graph没有这个topic时多半是pose_graph节点没起来或者launch没有加载相应配置。我遇到过把vins_estimator和pose_graph分开launch的情况只启动了Estimator保存话题自然找不到。2.3 重载地图的参数切换与验证方法重载地图时把yaml里的load_previous_pose_graph改成1其他路径保持不变重新启动VINS。启动后先别看odometry先打开RVIZ、订阅/pose_graph和/path两个话题。如果地图加载成功你会看到保存过的关键帧路径立刻显示在地图坐标系里。这时候再播放当前环境的数据包VIO初始化完成后算法会尝试做重定位一旦匹配成功odometry会出现从局部坐标系跳变到全局地图坐标系的过程这是正常现象不是故障。重定位成功的关键前提有三个当前场景和保存地图的场景要有足够的共视区域当前帧能提取到足量稳定的特征点IMU参数和保存地图时的标定结果没有大的偏差。如果这三个条件不满足即使地图加载成功重定位也会反复失败轨迹会一直停留在局部坐标系里。判断重定位是否成功除了看RVIZ里的跳变还可以看终端日志里有没有出现类似“found match”或“relocalization”的记录VINS-Mono在这些关键节点都会有调试输出。2.4 视觉模式与视觉惯性模式的差异VINS-Mono默认是视觉惯性融合但也可以退化到纯视觉模式。地图保存重载在两种模式下行为不太一样。纯视觉模式保存的是纯粹的单目位姿图重载后只需要靠特征匹配恢复位姿视觉惯性模式则会额外把重力方向和IMU状态纳入全局优化。这意味着同一个场景的地图最好在同一类型的模式之间复用否则会出现初始化异常或者重定位阶段位姿跳变剧烈。实际操作中我还遇到过一种情况保存地图时机器人在地面运行IMU的z轴加速度分布很固定重载时把传感器拿到了手里晃动IMU激励模式和原来差异巨大。这时候即使地图加载成功VIO初始化的收敛也会很慢甚至重定位成功后过几秒又被优化拉回错误位置。所以视觉惯性系统保存地图尽量保证传感器安装方式、IMU激励水平与建图时一致。2.5 保存频率与地图更新的策略地图不是保存一次就一劳永逸。每次建图过程中累积误差不同保存出的地图质量也有高低。我的习惯是每跑一次完整闭环就保存一份带时间戳的副本cp output/pose_graph/pose_graph_map.pkl output/pose_graph/pose_graph_map_$(date %Y%m%d_%H%M%S).pkl这样出了问题还可以回退到之前的版本。如果你的场景经常变化比如货架位置调整、家具搬动建议定期重新建图覆盖旧地图否则重定位时检索到的关键帧和当前环境差异会越来越大定位精度肉眼可见地下降。地图维护这件事不复杂但漏掉它往往会让后续的精度评估数据失真。3. 用evo工具做轨迹精度测试3.1 evo到底解决了什么问题evo是SLAM领域最常用的轨迹评测命令行工具之一它的核心价值是把“轨迹对比”这件原本很麻烦的事情变成几条命令。它支持TUM、EuRoC、KITTI格式也能直接读取rosbag还能做Umeyama轨迹对齐、时间戳匹配、ATE/RPE指标计算和结果绘图。对我来说它不是锦上添花而是判断“改完参数到底有没有变好”的唯一客观依据。evo的命令分成几类evo_traj用来查看、转换、对齐和绘制多条轨迹evo_ape计算绝对位姿误差evo_rpe计算相对位姿误差evo_res汇总多个结果evo_zip把多个评估结果打包。日常调试最常用的是前三者。安装方式也不复杂Python环境下直接pip install evo如果遇到numpy版本冲突的老毛病可以考虑用虚拟环境或者指定pip install evo --upgrade --no-binary evo实测能避开不少编译方面的坑。3.2 把VINS-Mono轨迹导出成TUM格式evo虽然支持直接读rosbag但我更推荐先把VINS输出保存成TUM格式因为TUM格式就是纯文本方便检查和处理。TUM轨迹的每一行是timestamp tx ty tz qx qy qz qw其中四元数顺序是x、y、z、w这一点特别容易弄反。如果你用rostopic echo直接抄数据看到的是x y z w一旦在转换脚本里写错顺序评估结果就会完全失真。从bag提取最简单的方式是evo_traj bag vins.bag /vins_estimator/odometry --save_as_tum运行之后会在当前目录生成vins_estimator_odometry.tum。如果你手头有VINS输出的vins_result_loop.csv也可以用awk转。比如EuRoC真值文件groundtruth.csv的列顺序是时间戳、位置xyz、四元数wxyz要转成TUM的顺序awk -F, NR1 {printf %.9f %s %s %s %s %s %s %s\n, $1/1e9, $2, $3, $4, $6, $7, $8, $5} groundtruth.csv groundtruth.tum转完之后一定要用head命令看一眼时间戳顺序确认是从小到大递增并且和真值的时间戳单位一致。EuRoC的CSV时间戳通常是纳秒VINS输出多半是秒这个单位坑几乎人人都会踩一次。3.3 ATE和RPE两条命令吃透全局精度看ATE局部稳定性看RPE两条命令可以固定成模板。ATE命令evo_ape tum vins_loop.tum --ref groundtruth.tum -a -p-a是Umeyama相似变换对齐这一步对单目VINS来说是必须的因为单目轨迹的尺度是任意确定的直接跟真值比绝对位置没有意义。-p会弹出误差随时间的曲线图。输出里的rmse、mean、median等指标中我一般优先看rmse。RPE命令evo_rpe tum vins_loop.tum --ref groundtruth.tum -a --delta 1 -p--delta 1表示每隔1秒取一个增量段来计算相对位姿误差。如果你更关心短时间抖动可以改用0.5如果关心建图稳定性可以用2甚至5。RPE反映了相邻时刻之间的位姿增量误差它不像ATE那样会被全局漂移主导因此能暴露控制层面的高频抖动和VIO输出不平滑的问题。指标看什么常见用途ATE全局轨迹与真值的差距判断系统整体漂移、闭环效果RPE相邻位姿增量的误差判断局部抖动、输出稳定性3.4 参数调试时如何用evo做批量对比调参时一次只改一个变量然后用同样数据跑多次把每次结果打包起来对比。操作流程是evo_traj tum result_v1.tum result_v2.tum --ref groundtruth.tum -a -p evo_zip results_v1_v2.zip result_v1.zip result_v2.zip evo_res results_v1_v2.zip -p这样能在一张表里看到不同版本轨迹的APE和RPE统计值省得自己誊数据。尤其在你怀疑某个参数影响很大的时候这个对比能快速给出结论。要注意的是时间戳匹配参数在批量对比时也要保持一致否则不同文件的有效帧数不一样指标没有可比性。3.5 一条龙实操示例把保存重载和evo评估串起来一套命令大概是这样的# 1. 建图并保存 roslaunch vins_estimator euroc.launch rosbag play MH_01_easy.bag rostopic pub /pose_graph/save_pose_graph std_msgs/String --once data: save # 2. 修改 yaml把 load_previous_pose_graph 设为 1重新启动 # 播放同一段bag或者现场数据记录 /vins_estimator/odometry rosbag record -O vins_reload.bag /vins_estimator/odometry /vins_estimator/keyframe_pose # 3. 评估 evo_traj bag vins_reload.bag /vins_estimator/odometry --save_as_tum evo_ape tum vins_estimator_odometry.tum --ref groundtruth.tum -a -p这套流程跑完你能得到两个关键信息重定位后的轨迹和建图轨迹够不够接近重定位模式下的空间漂移是否在可接受范围内。如果两个候选版本分别评估再用evo_res汇总调参效率会明显提高。4. 保存重载与evo评估的常见坑4.1 地图文件生成了但重载后关键帧数量为零这种情况九成是路径不一致。保存时pose_graph_save_path指向A目录重载时配置文件被改成了B目录地图读不到关键帧数量自然是0。还有一成是权限问题尤其输出目录放在/home之外或者系统盘根目录时程序可能没有写权限但不报错。排查时先确认两件事yaml里保存路径和加载路径完全一致启动终端对目标目录有读写权限。其次再看文件大小正常的pose_graph_map.pkl即使场景很小也有几兆字节如果只有几百字节基本就是序列化失败。4.2 重定位成功但轨迹突然跳变重定位成功后odometry跳变是正常的它会从局部坐标系跳到地图坐标系。但如果你发现跳变之后轨迹和地图路径的衔接不自然比如出现明显断点那往往是重定位所选的关键帧不准确。这时候把VINS终端的日志打开搜索relocalization相关输出看当前帧最终被匹配到的关键帧序号是否合理。另一种可能是全局优化在重定位后立刻运行把之前VIO的局部轨迹强行拉偏这种情况如果影响实验数据可以用只记录重定位后的轨迹来评估或者给VIO留出足够的收敛时间再开始记录。4.3 evo报时间戳无法对齐evo要求估计轨迹和真值轨迹的时间戳有交集并且时间单位一致。报错的时候先检查是不是纳秒和秒混用了。很多数据集的真值时间戳是纳秒VINS输出是秒直接丢给evo会显示找不到匹配。处理办法是统一成秒或者用evo_traj转换时把时间戳除以1e9awk {printf %.9f %s %s %s %s %s %s %s\n, $1/1e9, $2, $3, $4, $5, $6, $7, $8} groundtruth_ns.tum groundtruth_s.tum如果时间戳本身有微小偏移可以在evo_ape里加--t_max_diff 0.01允许最大0.01秒的时间差来匹配。但t_max_diff设得太大也会把不同时刻的位姿强行匹配反而引入误差所以建议从0.01开始尝试。4.4 只评估重定位后的轨迹段原始bag里可能包含初始化前的废数据VIO还没收敛轨迹在地图坐标附近乱飘。如果整段去评估这部分垃圾数据会把指标拉得很差。这时候可以先在evo的绘图窗口里确认为什么很差然后用时间戳裁剪出有效区段awk $1 start_time $1 end_time vins_full.tum vins_valid.tum裁剪后重新评估才能看到重定位本身的精度。重要的是裁剪真值和估计值时要保留同一时间窗口否则评估结果没有意义。4.5 坐标系方向不一致导致指标虚高或虚低evo的-a选项能对齐尺度、旋转和平移但它无法处理坐标系朝向定义完全不同、比如z轴取反这类情况。VINS-Mono在大多数数据集里世界坐标系的朝向与真值并不完全一致但-a通常能够纠正。如果评估结果还是差得离谱可以先用evo_traj画图看两条轨迹朝向是否一致如果朝向在视觉上就差90度或180度就先做坐标变换统一朝向再进入ape/rpe环节。4.6 地图膨胀与长期运行退化保存的地图文件会随着关键帧数量增加而变大但有一个很容易忽视的问题是频繁保存重载后地图里会积累大量冗余关键帧导致启动时加载变慢匹配耗时增加。VINS-Mono原版没有自动剔除关键帧的机制所以长周期项目要定期重建地图别指望一份地图能无限期用下去。地图文件出来后也可以看一眼时间戳覆盖范围如果出现了异常的时间空洞说明建图过程里有丢帧这种地图重定位时会出现大片盲区。写在最后的个人体会这套流程我反复跑过很多次最深刻的体会是保存重载和evo评估必须配合着用不能只做一半。只保存地图不评估你不知道重载后的定位到底准不准只评估不保存你调参优化的对象只是一个不复用的临时轨迹。我习惯在每次大改动之后都跑一遍“保存地图、重载地图、重定位、再用evo对比原轨迹和重定位轨迹”的闭环这样谁改坏了谁改好了一眼就能看清楚。最后再分享一个实用技巧重载地图后如果时间充裕等待VIO初始化完成后先别急着播测试数据让系统静止几秒钟。这样IMU偏置和重力方向能快速收敛后续重定位的成功率和稳定度会明显提升。别看这个细节简单很多重定位失败其实就栽在启动阶段没有给IMU留够收敛时间上。
返回列表