一、轨迹回放基础:从数据到可视化

做SLAM的人,每天打交道最多的东西是什么?我猜就是轨迹了。

你想想看,一个SLAM系统跑完,你拿到手的无非就是两样东西:一张地图,一条轨迹。地图咱们后面再聊,今天先说说轨迹。说白了,轨迹就是相机或机器人在空间里走过的路径,是一连串带时间戳的位姿数据。

我个人习惯把轨迹数据看作「时空坐标的集合」。每个点都包含三个信息:什么时间、在什么位置、朝向哪里。就这么简单,但里面的门道可不少。

核心定义:轨迹数据 = { (ti, pi, qi) | i = 1, 2, ..., N }

其中 ti 是时间戳,pi 是三维位置 (x, y, z),qi 是用四元数表示的朝向 (w, x, y, z)。

1.1 三种最常见的轨迹格式

我在项目里接触过的轨迹格式少说也有十几种,但真正称得上「行业标准」的,就下面这三个。你只要搞懂了它们,其他格式基本都能触类旁通。

TUM 格式

TUM 格式来自慕尼黑工业大学,是学术界用得最广的格式之一。它的结构非常直观:

# timestamp tx ty tz qx qy qz qw
1403636579.647200 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000
1403636579.648200 0.001000 -0.001000 0.000000 0.000000 0.000000 0.000000 1.000000
...

每行8列,分别是时间戳、位置xyz、四元数xyzw。嗯,这里要注意:四元数的顺序是 qx qy qz qw,不是 qw qx qy qz。我曾经在这个顺序上吃过亏,跑完评估发现轨迹误差大得离谱,查了半天才发现是四元数顺序搞反了。

KITTI 格式

KITTI 格式就有点不一样了。它不直接给你位置和朝向,而是给一个 3x4 的变换矩阵:

# 每行12个数字,构成 3x4 变换矩阵 [R | t]
1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0
0.9999 -0.0098 0.0072 -0.0001 0.0100 0.9999 -0.0043 -0.0001 -0.0072 0.0044 1.0000 0.0001
...

为什么 KITTI 要用矩阵而不是分开的位置+四元数?我个人理解,这跟 KITTI 数据集主要面向自动驾驶有关。自动驾驶里经常需要做坐标系变换,矩阵形式用起来更直接。你想想看,如果你要算两个位姿之间的相对变换,矩阵乘法一步就搞定了。

小技巧:用 evo 工具时,KITTI 格式会自动帮你把矩阵拆成位置和朝向。但如果你自己写代码解析,记得矩阵是按行存储的,每行4个数,一共3行。

EuRoC 格式

EuRoC 格式跟 TUM 很像,但多了一个东西——时间戳的单位是纳秒。而且它的数据通常包含 IMU 信息和视觉信息,轨迹只是其中一部分。

# timestamp [ns], p_RS_R_x [m], p_RS_R_y [m], p_RS_R_z [m], q_RS_R_w [], q_RS_R_x [], q_RS_R_y [], q_RS_R_z []
1403636579647200000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000
...

看到那个 1403636579647200000 了吗?这是纳秒级的时间戳。如果你要跟 TUM 格式混用,记得除以 1e9 转换成秒。我刚开始用 EuRoC 时,直接把时间戳当秒用了,结果轨迹对齐全乱套了。

1.2 三种格式对比

特性 TUM KITTI EuRoC
时间戳单位 秒 (浮点) 无 (按帧索引) 纳秒 (整数)
位姿表示 位置 + 四元数 3x4 变换矩阵 位置 + 四元数
适用场景 室内SLAM评估 自动驾驶 无人机/VIO
文件扩展名 .txt 或 .tum .txt .csv 或 .txt

1.3 轨迹可视化:evo 和 plotly

数据拿到手了,怎么看?我推荐两个工具:evo 和 plotly。前者是SLAM圈的老牌工具,后者是通用可视化库,但用在轨迹上效果也很好。

evo:SLAMer 的瑞士军刀

evo 是我用得最多的轨迹工具,没有之一。它最牛的地方是:一行命令就能画出轨迹对比图。

# 画单条轨迹
evo_traj tum trajectory.txt --plot

# 对比两条轨迹
evo_traj tum estimated.txt ground_truth.txt --plot

# 计算绝对轨迹误差 (ATE)
evo_ape tum ground_truth.txt estimated.txt --plot

# 计算相对位姿误差 (RPE)
evo_rpe tum ground_truth.txt estimated.txt --plot

我记得有一次做项目,客户说我们的SLAM精度不行。我直接用 evo 跑了个 ATE 分析,把误差分布图甩过去,对方立马闭嘴了。为什么?因为可视化比数字更有说服力。

避坑指南:用 evo 做轨迹对齐时,默认是用 Umeyama 算法做相似变换对齐。这意味着它会自动缩放你的轨迹。如果你确定尺度是准确的,记得加 --no_scale 参数。我曾经因为这个默认行为,误以为自己的VIO系统尺度漂移很严重,排查了两天才发现是 evo 自动缩放了。

plotly:交互式轨迹可视化

evo 适合快速分析,但如果你想做更炫酷的交互式可视化,plotly 是更好的选择。它支持鼠标拖拽旋转、缩放、悬停显示数据点信息。

import plotly.graph_objects as go
import numpy as np

# 假设你已经加载了轨迹数据
trajectory = np.loadtxt('trajectory.txt')
x, y, z = trajectory[:, 1], trajectory[:, 2], trajectory[:, 3]

fig = go.Figure(data=[
    go.Scatter3d(
        x=x, y=y, z=z,
        mode='lines+markers',
        marker=dict(size=2, color=np.arange(len(x)), colorscale='Viridis'),
        line=dict(color='darkblue', width=2)
    )
])

fig.update_layout(
    scene=dict(
        xaxis_title='X (m)',
        yaxis_title='Y (m)',
        zaxis_title='Z (m)'
    ),
    title='3D Trajectory Visualization'
)

fig.show()

这段代码会生成一个3D轨迹图,颜色从紫色渐变到黄色,表示时间顺序。你想想看,用鼠标拖拽旋转,从各个角度观察轨迹,是不是比看一堆数字直观多了?

1.4 本章知识体系

下面这张图总结了本章的核心内容,你可以把它当作一个快速参考:

轨迹回放基础 轨迹数据定义 常见轨迹格式 TUM 格式 位置+四元数 KITTI 格式 3x4 变换矩阵 EuRoC 格式 纳秒时间戳 轨迹可视化工具 evo (命令行) plotly (交互式)

1.5 写在最后

轨迹数据看着简单,但它是SLAM系统最直接的输出。你想想看,一个系统好不好,看轨迹就知道了——平滑还是抖动、有没有漂移、回环检测有没有起作用,全写在轨迹里。

我个人建议,刚开始接触SLAM的同学,先把轨迹可视化玩熟。用 evo 跑几条公开数据集的轨迹,看看真实轨迹和估计轨迹的差异,比看十篇论文都管用。

一个小练习:去 TUM 数据集官网下载一个 RGB-D 序列,用 ORB-SLAM2 跑一遍,然后用 evo 对比估计轨迹和 ground truth。看看 ATE 是多少,误差主要出现在哪些区域。做完这个练习,你对轨迹数据的理解会上一个台阶。

可可精品资料库