4、轨迹数据格式:TUM格式、KITTI格式、EuRoC格式、ROS bag格式

做SLAM定位评估,说白了就是跟轨迹数据打交道。我见过不少新手,算法写得挺溜,结果在数据格式转换上栽了跟头。嗯,今天咱们就把这几种主流格式掰扯清楚。

核心观点:轨迹数据格式是评估的「通用语言」。格式不对,评估白费。

4.1 TUM格式——最直观的「时间戳+位姿」

TUM格式来自慕尼黑工业大学,是学术界用得最多的格式之一。我个人习惯用它做快速验证,因为结构简单到一眼就能看懂。

每行数据包含:时间戳 tx ty tz qx qy qz qw

举个例子:

# 时间戳(秒)     tx(m)    ty(m)    tz(m)    qx       qy       qz       qw
1403715273.542  1.234    0.567    0.123    0.707    0.000    0.707    0.000
1403715273.643  1.245    0.578    0.131    0.708    0.001    0.706    0.001

这里要注意几个坑:

  • 时间戳单位是秒,不是纳秒。我在项目中遇到过有人把ROS的纳秒时间戳直接塞进来,结果轨迹全乱了。
  • 四元数必须归一化。我曾经调试一个诡异问题,折腾了两天,最后发现是四元数模长不是1。
  • 平移单位是米,别跟毫米搞混了。

我的小技巧:写一个Python脚本,用numpy.loadtxt()直接读TUM格式,然后转成Nx7的矩阵,处理起来特别顺手。

4.2 KITTI格式——自动驾驶界的「老大哥」

KITTI格式跟TUM最大的区别是:它用3x4的变换矩阵,而不是分开的平移+四元数。你想想看,自动驾驶领域对实时性要求高,矩阵运算比四元数转换要快不少。

格式长这样:

# 每行12个数字,对应3x4变换矩阵(行优先)
r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz

实际数据:

0.9999 -0.0100 0.0100 1.234
0.0100 0.9999 -0.0100 0.567
-0.0100 0.0100 0.9999 0.123

嗯,这里有个容易踩的坑:KITTI格式没有时间戳。它默认每帧按顺序排列,帧率固定10Hz。如果你用不同帧率的数据去评估,记得先做时间对齐。

警告:KITTI的坐标系是「右-下-前」(RGB相机坐标系),而TUM是「左-上-前」(惯用坐标系)。直接拿来对比会出大问题。我刚开始做评估时就吃过这个亏。

4.3 EuRoC格式——微小型飞行器的「标准答案」

EuRoC数据集来自苏黎世联邦理工学院,主要针对无人机场景。它的格式跟TUM很像,但多了一个关键字段:协方差矩阵。

格式:时间戳 tx ty tz qx qy qz qw 协方差(6x6上三角)

为什么要加协方差?说白了,无人机飞得快、抖动大,每个位姿的置信度不一样。有了协方差,评估工具就能知道哪些点「靠谱」,哪些点「可疑」。

实际数据片段:

1403715273.542 1.234 0.567 0.123 0.707 0.000 0.707 0.000
0.001 0.000 0.000 0.000 0.000 0.000
0.001 0.000 0.000 0.000 0.000
0.001 0.000 0.000 0.000
0.001 0.000 0.000
0.001 0.000
0.001

注意看,协方差矩阵只存上三角部分,共21个数字。我建议你写解析代码时,先读前8个字段(时间戳+位姿),再读21个协方差值,别搞混了。

4.4 ROS bag格式——机器人界的「万能容器」

ROS bag不是专门的轨迹格式,它是一个容器,可以装任意ROS消息。轨迹数据通常以nav_msgs/Odometry或geometry_msgs/PoseStamped的形式存在bag里。

我个人觉得,bag格式最大的好处是:时间戳自动对齐。ROS的rosbag play会按照录制时的真实时间戳回放数据,省去了手动同步的麻烦。

提取轨迹的常用命令:

# 从bag中提取odometry话题
rostopic echo -b your_bag.bag -p /odometry > trajectory.txt

# 或者用Python脚本
import rosbag
bag = rosbag.Bag('your_bag.bag')
for topic, msg, t in bag.read_messages(topics=['/odometry']):
    # 提取位置和姿态
    pos = msg.pose.pose.position
    ori = msg.pose.pose.orientation
    print(f"{t.to_sec()} {pos.x} {pos.y} {pos.z} {ori.x} {ori.y} {ori.z} {ori.w}")

避坑指南:我曾经用rostopic echo -p导出数据,发现时间戳精度丢失了。后来改用Python脚本直接读msg.header.stamp,精度才保住了。记住,-p选项会截断纳秒部分。

4.5 四种格式对比一览

特性 TUM KITTI EuRoC ROS bag
位姿表示 平移+四元数 3x4矩阵 平移+四元数+协方差 ROS消息类型
时间戳 有(秒) 无(按帧序) 有(秒) 有(纳秒)
适用场景 学术评估 自动驾驶 无人机/VIO 机器人系统
文件大小 小 中 大(含协方差) 很大(含传感器数据)
解析难度 简单 中等 中等 需要ROS环境

4.6 格式转换实战

实际工作中,你经常需要在不同格式之间来回切换。我整理了一个通用的转换思路:

  1. 统一中间格式:先把所有格式转成TUM格式(因为最简单)
  2. 时间戳对齐:用线性插值或最近邻插值对齐时间戳
  3. 坐标系转换:注意不同数据集的坐标系定义
  4. 输出目标格式:从中间格式转成你需要的格式

举个例子,KITTI转TUM的核心代码:

import numpy as np

def kitti_to_tum(kitti_pose):
    """
    kitti_pose: 3x4 变换矩阵
    返回: [tx, ty, tz, qx, qy, qz, qw]
    """
    # 提取平移
    tx, ty, tz = kitti_pose[0,3], kitti_pose[1,3], kitti_pose[2,3]
    
    # 提取旋转矩阵并转四元数
    R = kitti_pose[:3,:3]
    qw = np.sqrt(1 + R[0,0] + R[1,1] + R[2,2]) / 2
    qx = (R[2,1] - R[1,2]) / (4 * qw)
    qy = (R[0,2] - R[2,0]) / (4 * qw)
    qz = (R[1,0] - R[0,1]) / (4 * qw)
    
    return [tx, ty, tz, qx, qy, qz, qw]

重要提醒:四元数转换时,如果qw接近0(旋转180度的情况),要用其他公式。我建议用scipy的Rotation.from_matrix(),它内部处理了所有边界情况。

4.7 知识体系总览

下面这张图总结了四种格式的核心关系,以及它们在实际评估流程中的位置:

轨迹数据格式知识体系 TUM格式 平移+四元数 KITTI格式 3x4变换矩阵 EuRoC格式 位姿+协方差 ROS bag 消息容器 统一中间格式(TUM) 时间戳对齐 → 坐标系转换 → 输出目标格式 轨迹评估(ATE / RPE)

从这张图可以看得很清楚:不管你用什么格式采集数据,最终都要经过「统一中间格式」这个环节,才能进行公平的轨迹评估。我建议你把这个流程固化到自己的工具链里,每次做评估都走一遍,能省下不少调试时间。

可可精品资料库