做SLAM和里程计的人,早晚会碰上一个灵魂拷问:手里这条轨迹到底准不准?跑完算法拿到一条条姿态文件,别人问你精度多少,总不能说“感觉挺好的,你看地图多整洁”。evo就是解决这个问题的工具。它能读取TUM、KITTI、EuRoC这些主流格式的轨迹数据,帮你算绝对轨迹误差(APE)、相对位姿误差(RPE),还能把估计轨迹和真值画在同一张图里,一键输出能写进报告或论文的指标表。
这篇文章适合正在跑视觉SLAM、激光SLAM、惯性导航、或者任何“会输出位姿序列”的算法的朋友。无论你是刚入门想知道怎么量化精度,还是已经跑通流程但被各种参数和格式坑过的老手,这篇文章都能给你一套可以直接照抄的做法。我尽量把每个参数为什么要这么设都讲清楚,不搞“照着敲就行”的黑盒。
1. evo到底是干什么的,解决什么问题
1.1 从一条轨迹到一组数字
先理清一个基本概念。SLAM跑完之后,算法会输出一串位姿,每帧包括时间戳、位置(x、y、z)和姿态(四元数),这串数据合起来就是一条轨迹。同时,数据集会提供真值轨迹(ground truth),比如动捕系统或者高精度定位设备记录下来的“正确答案”。所谓精度评估,本质上就是对比这两条轨迹的差异。
但是直接对比有个大问题:估计轨迹和真值通常不在同一个坐标系下。比如视觉SLAM启动时,它的世界坐标系原点往往是第一帧所在位置,而真值的坐标系是数据集定义的,两者可能有旋转、平移,甚至尺度都不一样(单目VIO尤其明显)。你如果直接把坐标相减,算出来一个天文数字,那没有意义。evo做的事情,本质上就是三步:读入两条轨迹、按时间戳对齐、用某种数学方法把估计轨迹“配准”到真值坐标系上,然后再逐点比较。
配准那一步就会引出一堆名词:SE(3)对齐、Sim(3)对齐、原点对齐。第一次用的人很容易被这些东西绕晕,后面我会专门展开讲怎么选。
1.2 三条主命令各管一摊
evo不是只有一个命令,它是一套工具链。最常用的有这几个:
| 命令 | 作用 |
|---|---|
evo_ape | 计算绝对轨迹误差(ATE),评估全局精度 |
evo_rpe | 计算相对位姿误差(RPE),评估局部平滑性和漂移 |
evo_traj | 查看、转换、合并轨迹,画轨迹对比图 |
evo_res | 把多个评估结果文件拉在一起比较,生成统计表 |
evo_config | 修改绘图风格、全局配置 |
我的习惯是先用evo_traj对轨迹做“体检”,确认格式和时间戳都没问题,然后跑evo_ape得到全局指标,再跑evo_rpe看局部漂移,最后用evo_res对比这个版本和上一个版本的改进。这一套流程走下来,项目汇报也好、写论文也好,数据都拿得出手。
2. 装好它,准备好数据
2.1 安装环境和避坑
安装evo非常简单,一条pip命令就够:
pip install evo不过有几个坑我提前说一下。evo依赖PyQt5来做交互式绘图,如果你的运行环境是纯命令行服务器,没有图形界面,那画图功能可能起不来,会报一个类似“cannot connect to display”的错误。这种情况下别慌,你可以改用非交互模式,或者把绘图结果保存成图片文件,不用弹窗。
另一个常见问题和numpy版本有关。evo早期版本对numpy版本比较敏感,如果你系统里的numpy太新,或者之前装过别的深度学习框架导致依赖混乱,可能会遇到不兼容的报错。我的建议是给evo单独建一个虚拟环境,别把它和其他大项目混在一起。你可以用conda或者venv,怎么干净怎么来:
python -m venv evo_env source evo_env/bin/activate pip install evo装完之后可以跑evo_ape --help看看帮助信息,如果正常显示,就说明环境没问题。
安装完成后,我强烈建议花一分钟配置一下evo的默认绘图风格。用evo_config可以改很多全局设置,比如保存图片的格式、字体、是否显示图例等。我一般会先跑一句:
evo_config set plot_export_format png这样后面所有命令保存出来的图片都是png格式,方便直接贴到文档里。
2.2 三种数据格式长什么样
evo支持多种轨迹格式,但在实际项目中,最常遇到的就是TUM、KITTI和EuRoC这三种。它们的结构差异很大,搞错了,程序可能直接报错,或者算出完全不可信的结果。
TUM格式是文本文件,每行8列,依次是时间戳、平移x、y、z、四元数qx、qy、qz、qw。时间戳单位是秒,平移单位是米。一个典型的行长这样:
1403715282.1740 1.2454 0.8852 0.2251 0.0001 0.7071 0.0000 0.7071KITTI格式也是文本文件,但每行只有12个数字,是一个3x4的变换矩阵按行优先展开的。它没有时间戳,时间信息通过文件名或者单独的索引文件关联。12个数字的排列是:
r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tzEuRoC格式是CSV,逗号分隔,前8列是时间戳和位姿,后面还带着速度、角速度这些额外的状态量。时间戳单位是纳秒,这个细节特别容易把人坑了,转换的时候要注意统一单位。
如果你手里的数据是其他格式,比如ROS bag导出的位姿,那一般需要先写个小脚本把它转成TUM格式,再喂给evo。做转换之前,务必先搞清楚三个事情:时间戳的单位是什么、四元数顺序是xyzw还是wxyz、平移单位是米还是毫米。这三个点错了,后面全白做。
2.3 先给轨迹做“体检”
很多新手拿到轨迹就直接去算APE,结果报错或者算出来的值高得离谱,转头就骂工具不好使。其实evo自带一个体检功能,非常好用。
evo_traj tum estimated.txt --full_check这条命令会读取轨迹并检查它的完整性,然后输出统计信息,包括轨迹包含多少帧、持续时长、轨迹总长度、平均速度、最大最小坐标等。跑完之后你看一眼这些数字是否符合直觉。如果轨迹长度才0.5米,而你跑的是楼宇场景,那说明数据单位或者坐标系肯定有问题;如果最大速度每秒1000米,那八成是时间戳单位搞错了。
体检没问题之后,再去做格式转换。比如你只有KITTI格式的位姿,想转成TUM格式方便和真值比较,用--save_as_tum参数:
evo_traj kitti estimated.kitti --save_as_tum estimated_tum.txt反过来,如果你有带时间戳的轨迹想转成KITTI格式,也可以:
evo_traj tum estimated.txt --save_as_kitti estimated.kitti这类转换我几乎每次项目都会用到,因为不同数据集格式不一样,而转换完之后才能用同一套流程评测。
3. 开始评估:APE、RPE的计算和参数取舍
3.1 APE:全局精度怎么算
APE,全称Absolute Pose Error,绝对轨迹误差。它的思路很直接:把估计轨迹和真值对齐之后,对每一帧求两个位姿之间的变换误差,最后统计成一个RMSE之类的数值。
在evo命令里,计算APE用evo_ape。最基础的命令是:
evo_ape tum ground_truth.txt estimated.txt -va这里的-v表示显示详细信息,-a表示自动进行SE(3)对齐。运行完之后,你会看到类似这样的输出:
APE w.r.t. translation part (m) RMSE: 0.1234 Mean: 0.0987 Median: 0.0876 Std: 0.0543 Min: 0.0123 Max: 0.3456重点看RMSE和Median。RMSE对大的离群误差非常敏感,如果某一段轨迹突然跳了一下,RMSE会被拉得很高;Median则相对稳健。两个值放在一起看,能快速判断误差是普遍偏大还是个别点爆表。
我实际使用时,几乎总会把画图打开:
evo_ape tum ground_truth.txt estimated.txt -va --plot --plot_mode xyz --save_plot ape_result.png--plot_mode xyz的意思是画位姿的x、y、z三个方向,配合误差颜色条,可以直观看到哪些地方漂移最严重。加上--save_plot之后图片直接存成文件,方便写报告用。--plot_mode还可以设置成xy、yx、xz这些组合,看你需要观察哪个平面。
3.2 RPE:局部平滑度怎么算
RPE,全称Relative Pose Error,相对位姿误差。它和APE最大的区别在于,不关心全局绝对位置,而是观察在某个固定时间间隔内,估计轨迹和真值的变化量是否一致。
打个比方,APE就像你考试的总分,RPE就像你每次做题的步骤分。一个人总分高,不代表每一步都走得稳;同样,一条轨迹全局误差小,不代表每个局部区间没有抖动。很多系统全局精度看着没问题,但局部一圈一圈地漂移,这时候RPE就能把它们揪出来。
evo_rpe tum ground_truth.txt estimated.txt --delta 1 --delta_units m --align --correct_scale -va --plot --plot_mode xyz这里的--delta和--delta_units是RPE的关键参数。--delta_units m表示间隔单位是米,即每相隔1米的路程计算一次相对误差。你也可以把间隔改成帧数,比如每10帧算一次,具体怎么选取决于你的场景和关注点。一般来说,里程计评测习惯用固定距离间隔,因为这样不受速度波动影响;而视觉SLAM的话,看按帧间隔的RPE也能反映出轨迹平滑度。
还有一个细节:RPE对全局对齐没有APE那么敏感,因为比较的是相对增量。但如果你希望结果的参考系完全一致,也可以在命令里保留--align --correct_scale,这样全局对齐之后再提取相对运动,结果更干净。
3.3 对齐模式怎么选
对齐是evo里最容易被忽略、又最容易出错的环节。我见过有人拿着单目的轨迹和真值比较,没有加任何对齐和尺度修正,RMSE算出来几十米,然后说算法“崩了”。其实算法没崩,是坐标系和尺度都没对齐。
evo的对齐参数主要有这几个:
| 参数组合 | 效果 | 适用场景 |
|---|---|---|
| 不加任何对齐参数 | 直接使用原始坐标系对比 | 估计轨迹和真值已经严格在同一坐标系时(很少见) |
--align | SE(3)刚体变换对齐,包含旋转和平移 | 双目、RGB-D、激光SLAM,尺度可信,但坐标系不同 |
--align --correct_scale | Sim(3)相似变换对齐,在刚体变换基础上还修正尺度 | 单目VIO、单目SLAM,尺度未知 |
--align_origin | 只把原点对齐,不做额外旋转平移 | 快速看轨迹大致形态时 |
我的一般原则是:只要是单目系统,必须加--correct_scale,否则别谈绝对精度;如果是双目或者激光,通常加--align就够了,或者先用--align_origin快速看一眼形态,再决定要不要做完整的刚体对齐。
还有一个容易忽略的选项是--ref。当你有真值轨迹时,--ref可以指定它作为参考,所有对齐计算都基于它进行。比如:
evo_traj tum estimated.txt --ref ground_truth.txt --align --correct_scale -p这条命令直接把估计轨迹对齐到真值上,然后画出对比图。如果你要在多个算法之间横向比较,这招特别好用。
4. 可视化与结果管理
4.1 一次画出多条轨迹
实际做项目的时候,你往往不是只有一条估计轨迹,而是有两个版本要对比,一个是老的,一个是新的,还有一个真值要放在一块看。evo_traj这个命令最擅长处理这种事情:
evo_traj tum estimated_v1.txt estimated_v2.txt --ref ground_truth.txt --align --correct_scale -p --plot_mode xyz这条命令会把两条估计轨迹和真值画在一张三维图里,同时你一眼就能看出哪条轨迹更贴近真值。颜色和图例是自动生成的,保存下来的图片可以直接放进技术评审文档里。
这里我多说一句,画图对比虽然直观,但眼睛的判断会有偏差。两条轨迹整体看着差不多,但局部某个转角可能一条很顺滑,一条抖成筛子。所以画图只能作为辅助,最终还是要落到APE和RPE的具体数字上。
如果觉得画出的图不符合自己的审美,evo也提供了一些配置项。比如可以关掉网格线,或者调整坐标轴比例。我更常用的是设置--plot_mode xy,把三维轨迹投影到xy平面,这样在小范围场景里看得更清楚。
4.2 把多个指标结果汇总
当你需要对比多个算法或者同一个算法的多个参数组合时,一个一个跑命令看输出太累了,而且结果零散,不好归档。这时候可以用evo_res。
思路是,先用evo_ape或者evo_rpe把每个评估结果保存成zip文件:
evo_ape tum ground_truth.txt estimated_v1.txt --align --correct_scale -va --save_results result_v1.zip evo_ape tum ground_truth.txt estimated_v2.txt --align --correct_scale -va --save_results result_v2.zip然后把这些zip文件统一丢给evo_res:
evo_res result_v1.zip result_v2.zip -p它会生成一个并排的统计表,把所有算法的RMSE、Mean、Median、Std都放在一起,同时画一个误差对比图。这样你就能清楚看到:算法B的RMSE比算法A低了百分之多少,是整体变好还是靠压低个别峰值。
这个流程我几乎每次改算法都会跑一遍。以前没有用evo的时候,我都是自己写脚本算指标、拼表格,费时费力,还容易算错。现在全部交给evo,回归测试的效率提升非常明显。
4.3 在Python脚本里调用evo的API
命令行虽然方便,但有时候你需要把评估嵌入到自己的训练或者后处理流程里。比如你有一套自动化脚本,每天跑完实验自动生成精度报表。这时候直接用Python调用evo的库函数会更优雅。
evo提供了Python API,核心模块是evo.core、evo.tools和evo.main。一个最小化的评估流程大概长这样:
from evo.core import sync, metrics from evo.tools import file_interface # 读取轨迹 traj_ref = file_interface.read_tum_trajectory_file("ground_truth.txt") traj_est = file_interface.read_tum_trajectory_file("estimated.txt") # 时间戳同步 traj_ref, traj_est = sync.associate_trajectories( traj_ref, traj_est, max_diff=0.01) # 计算APE ape_metric = metrics.APE(metrics.PoseRelation.translation_part) ape_metric.process_data(traj_ref, traj_est) stats = ape_metric.get_statistic(metrics.StatisticsType.rmse) print("APE RMSE:", stats)这只是示意代码,具体API会随版本有些许变化,但整体思路是一致的:读取轨迹、同步时间戳、选择指标、计算统计量。
有了这个基础,你可以在自己的脚本里批量读取几十条轨迹,算完所有指标之后输出到一个JSON文件,或者直接在训练过程中监控验证集的轨迹误差。我自己的经验是,把评估流程脚本化之后,再也不用手动敲命令了,也不会漏算某个指标。
5. 常见问题和我的排查经验
5.1 一张问题速查表
这么多年用下来,evo出问题的情况我基本都遇到过。我整理成了一张速查表,按“症状-原因-解法”排列,给大家一个快速定位的入口。
| 症状 | 常见原因 | 解决办法 |
|---|---|---|
| 命令执行时报“file format not supported” | 数据格式头缺失,或者行列数不对 | 先用head看一下文件,对照标准格式修正 |
| 轨迹看起来有巨大跳变,RMSE异常高 | 时间戳没对齐,或者单位不对 | 检查时间单位(秒还是纳秒),执行--full_check看速度是否合理 |
| 单目轨迹误差几米到几十米 | 没有做尺度修正 | 命令里加上--correct_scale,配合--align一起使用 |
| 四元数报错,或者姿态乱转 | 四元数顺序弄反了 | TUM格式是qx qy qz qw,检查写入端顺序 |
| 图片无法弹出,绘图报错 | 环境没有图形界面,或者缺失PyQt5 | 改用--save_plot保存图片,或安装PyQt5 |
| 两条轨迹时间范围完全不重叠 | 使用的时间戳基准不同,或者同步阈值太小 | 增加--t_max_diff,比如设成0.01或者更大,再开启同步 |
5.2 两个最常踩的格式坑
第一个坑是四元数顺序。TUM格式的四元数顺序是qx qy qz qw,但很多其他工具或者库输出的是qw qx qy qz,也就是w在前。如果你直接把这个文件喂给evo,它在解析时会把第一个值当成qx,导致姿态完全错乱。这种错误非常隐蔽,因为你看到的四元数数值本身看着很正常,只有对比轨迹时才会发现姿态偏了。
第二个坑是时间戳同步。evo在比较两条轨迹时会根据时间戳找对应关系,但前提是两条轨迹的时间必须足够接近。如果你的估计轨迹和真值之间存在固定的时间偏移,比如相机触发延迟了100毫秒,那就得通过时间同步来解决。在命令里加上--sync可以自动做同步,并用--t_max_diff控制允许的最大时间差。设得太小会丢掉很多匹配点,设得太大又可能把不同时刻的帧错误配对。我自己一般先跑一下evo_traj --full_check,看看时间戳的分布范围,再决定阈值,通常0.01秒(10毫秒)是一个不错的起点。
5.3 怎么把评估结果汇报得让人信服
最后聊一个软件之外的话题。工具算出了数字,但怎么把这些数字讲给其他人听,是一门学问。我的建议是不要只丢一个RMSE就完事,那样太单薄。
首先,至少同时汇报APE和RPE两组指标。APE说明整体精度,RPE说明局部平滑性,两者是互补的。只报APE,别人会担心你局部抖得厉害;只报RPE,别人会怀疑你整体漂移严重。
其次,必须注明对齐方式。如果你用的是单目,一定要写清楚“Sim(3)对齐后APE为xxx”,否则别人拿你的数字和双目系统比,完全没有意义。反过来,如果别人没做尺度修正就报了很小的误差,那你也要心里有数,这个数字可能不真实。
还有一个细节是统计量。除RMSE外,我还习惯把Max误差一并列出来,因为机器人领域里,最大误差往往比平均误差更能暴露问题。一个系统平均误差很小,但在某些位置突然跳了一米,这对实际部署来说是很危险的。evo的输出默认就包含这些统计量,顺手贴进报告里就行。
最后分享一个实用习惯
我用evo最大的感受是,它把“算法精度评估”这件事从玄学变成了标准化流程。以前我判断一个算法改得好不好,靠的是看轨迹图上的漂移程度,主观且容易被情绪带跑。现在无论谁跟我说他算法“更准了”,我都会丢给他一句话:把轨迹文件发我,我跑一下evo,用数字说话。
踩过几次坑之后,我养成了一个固定习惯,每次评测前先跑一次evo_traj --full_check,确认轨迹很健康,再开始算APE和RPE。磨刀不误砍柴工,这一步能省掉后面排查问题的几个小时。另外一定要把每次评估的zip结果归档好,文件名带上日期和版本号,对比多个算法时会方便很多。这个方法我现在一直在用,效果很稳,推荐你也试试。