2、路采数据格式解析:常见格式、数据结构与元数据提取

好,咱们接着聊。上一章我们把路采的硬件和流程捋了一遍,这一章,咱们得聊聊数据本身了。你辛辛苦苦跑了一天,拿回来一堆文件,结果打不开,或者打开了一堆乱码,那可就尴尬了。

我个人习惯,拿到数据第一件事,就是先看它的「身份证」——也就是文件格式。ADAS 领域,常见的格式就那么几种:ROSbag、PCAP、HDF5、MDF4。每种格式背后,都对应着不同的采集工具和设计思路。说白了,选什么格式,往往取决于你用什么平台、做什么分析。

2.1 四大金刚:常见格式概览

咱们一个一个来看。我按我接触的频率排个序,你心里有个数。

格式 后缀名 典型场景 我的评价
ROSbag .bag ROS/ROS2 生态,算法开发 灵活,但体积大,适合调试
PCAP .pcap 以太网数据,激光雷达、摄像头裸数据 原始,解析麻烦,但信息无损
HDF5 .h5 / .hdf5 大规模数据存储,传感器融合 结构化好,压缩率高,我的最爱
MDF4 .mf4 车载总线数据,CAN/LIN/FlexRay 行业标准,工具链成熟

核心观点: 没有最好的格式,只有最合适的场景。你如果做纯视觉算法验证,ROSbag 最顺手;如果做多传感器时间同步分析,HDF5 的结构化优势就体现出来了。

2.2 ROSbag:算法工程师的瑞士军刀

ROSbag 这东西,做自动驾驶的没人不知道。它本质上就是一个容器,把 ROS 话题(Topic)上的消息按时间顺序存下来。

数据结构长什么样?

说白了,就是一个索引文件加一堆数据块。索引里记录了每条消息的时间戳、话题名、数据类型和数据在文件中的偏移量。你回放的时候,ROS 节点就按这个索引把数据「喂」出来。

我举个例子,你采集了一组数据,里面有摄像头图像、激光雷达点云、GPS 定位。在 ROSbag 里,它们可能是三个话题:/camera/image_raw/lidar/points/gps/fix。每个话题都有自己的消息类型和频率。

# 查看 bag 信息
rosbag info your_data.bag

# 输出示例
path:        your_data.bag
version:     2.0
duration:    120.5s
start:       Jan 01 2025 10:00:00.00
end:         Jan 01 2025 10:02:00.50
size:        4.2 GB
messages:    125000
compression: none [120/120 chunks]
types:       sensor_msgs/Image [12345]
             sensor_msgs/PointCloud2 [67890]
             sensor_msgs/NavSatFix [11111]
topics:      /camera/image_raw   1000 msgs
             /lidar/points       2000 msgs
             /gps/fix            100 msgs

我的小技巧: 我习惯在采集时就把话题名统一命名规范,比如 /sensor/camera/left 而不是 /cam1。不然数据多了,你自己都分不清哪个是哪个。我曾经就因为命名混乱,浪费了一整天去核对数据。

2.3 PCAP:最原始的「录音带」

PCAP 格式,说白了就是网络抓包文件。激光雷达、摄像头通过以太网把数据发出来,你拿一台工控机把网口数据全抓下来,存成 .pcap 文件。

为什么用它?

因为它最原始,最真实。你想想看,传感器发出的每一个字节,都被原封不动地记录下来了。没有中间件,没有序列化开销。这对于做底层驱动开发、时间戳精度验证来说,太重要了。

但坑也在这里。

PCAP 文件本身不包含任何语义信息。它只告诉你:某年某月某日某时某分某秒,网卡收到了多少个字节。至于这些字节代表什么——是点云的 xyz 坐标,还是图像的 yuv 数据——你得自己去解析。

# 用 tcpdump 抓取 10 秒的数据
tcpdump -i eth0 -w lidar_data.pcap -G 10

# 用 wireshark 或 tshark 查看
tshark -r lidar_data.pcap -T fields -e frame.time_epoch -e data.len

避坑指南: 我曾经遇到过一个问题:抓包时网卡开启了硬件时间戳,但驱动没配置好,导致时间戳全是乱的。后来回放数据时,点云和图像怎么都对不上。嗯,从那以后,我每次抓包前都会先跑一个简单的回环测试,确认时间戳链路是通的。

2.4 HDF5:结构化存储的王者

HDF5 是我个人比较偏爱的一种格式。它不像 ROSbag 那样依赖 ROS 生态,也不像 PCAP 那样原始。它提供了一种树状的数据结构,你可以把任意类型的数据组织进去。

数据结构与序列化

HDF5 内部是「组(Group)」和「数据集(Dataset)」的层次结构。你可以把一组传感器数据放在一个组里,每个传感器是一个数据集。而且,HDF5 支持多种数据类型,从简单的整数、浮点数,到复杂的结构体、变长数组,都能存。

举个例子,我要存一帧激光雷达数据,我会这样组织:

/sensor/lidar/timestamp   # 时间戳,uint64
/sensor/lidar/points      # 点云数据,N x 4 的 float32 数组 (x, y, z, intensity)
/sensor/lidar/pose        # 当前帧的位姿,4x4 的 float64 矩阵

这样做的好处是什么?你想想看,当你需要分析某个特定时刻的数据时,你不需要遍历整个文件。直接按路径读取对应的数据集就行了。而且 HDF5 支持并行 I/O,在大数据量场景下,读写速度非常快。

import h5py
import numpy as np

# 写入 HDF5 文件
with h5py.File('sensor_data.h5', 'w') as f:
    # 创建组
    lidar_group = f.create_group('sensor/lidar')
    # 写入数据集
    lidar_group.create_dataset('timestamp', data=np.uint64([1234567890]))
    lidar_group.create_dataset('points', data=np.random.rand(100, 4).astype(np.float32))

# 读取 HDF5 文件
with h5py.File('sensor_data.h5', 'r') as f:
    timestamps = f['/sensor/lidar/timestamp'][:]
    points = f['/sensor/lidar/points'][:]

我的经验: 在做多传感器融合项目时,我习惯把时间同步后的数据直接存成 HDF5。这样,后续做训练、做回放,都只需要读一个文件,省去了反复解析不同格式的麻烦。说白了,HDF5 就是你的「数据仓库」。

2.5 MDF4:车载总线的「官方语言」

MDF4 是 ASAM 标准定义的车载测量数据格式。你如果做 CAN、LIN、FlexRay 或者以太网 SOME/IP 的数据采集,大概率会碰到它。

它和前面几种格式最大的区别是什么?

MDF4 是面向信号的。它不关心你用什么协议,它只关心:某个信号(比如车速、方向盘转角)在某个时刻的值是多少。每个信号都有自己独立的采样率、单位、转换关系。

举个例子,一个 MDF4 文件里可能包含:

  • CAN 总线数据: 车速信号(100Hz)、发动机转速(50Hz)
  • LIN 总线数据: 车窗状态(10Hz)
  • FlexRay 数据: 转向角度(200Hz)

这些信号的时间戳可能不一致,但 MDF4 通过「通道组(Channel Group)」和「通道(Channel)」的概念,把它们组织得井井有条。

# 使用 asammdf 库读取 MDF4 文件
from asammdf import MDF

mdf = MDF('vehicle_data.mf4')
# 获取所有信号名称
signal_names = mdf.channels_db
# 读取车速信号
speed_signal = mdf.get('VehicleSpeed')
# 获取时间戳和数值
timestamps = speed_signal.timestamps
values = speed_signal.samples

实用建议: 如果你需要把 MDF4 数据和其他格式(比如 ROSbag)做联合分析,我建议先把 MDF4 里的信号按时间戳重采样,然后导出成 CSV 或者 HDF5。直接混着用,时间同步会搞得你头大。

2.6 元数据提取:数据的「户口本」

聊完了格式,咱们得说说元数据。什么是元数据?就是「关于数据的数据」。比如:采集时间、采集地点、传感器型号、标定参数、天气状况、驾驶员行为等等。

为什么元数据这么重要?

你想想看,你手里有 1000 个 ROSbag 文件,每个 10GB。你想找一个「雨天、夜间、城市道路、有行人横穿」的场景。如果没有元数据,你得一个一个回放,用肉眼去看。那得看到猴年马月?

我个人习惯,在采集数据的同时,就把元数据记录下来。通常有两种方式:

  1. 内嵌式: 把元数据作为 ROS 话题或 HDF5 数据集,直接写在数据文件里。比如,在 ROSbag 里加一个 /metadata/weather 话题。
  2. 外挂式: 单独写一个 JSON 或 YAML 文件,记录每个数据文件的元数据。比如 data_001.bag.json

我比较推荐外挂式。为什么呢?因为内嵌式会污染原始数据。你想想,如果你要修改元数据(比如发现天气标注错了),你难道要重新生成整个 ROSbag 吗?那太蠢了。外挂式就灵活得多,改一个 JSON 文件就行了。

{
    "file_name": "data_001.bag",
    "start_time": "2025-01-15 14:30:00",
    "end_time": "2025-01-15 14:35:00",
    "location": {
        "lat": 31.2304,
        "lon": 121.4737,
        "city": "Shanghai"
    },
    "sensors": {
        "camera": ["left", "right", "front"],
        "lidar": ["top"],
        "radar": ["front", "rear"]
    },
    "environment": {
        "weather": "rainy",
        "lighting": "night",
        "road_type": "urban"
    },
    "scenario": {
        "type": "pedestrian_crossing",
        "difficulty": "hard"
    }
}

避坑指南: 我曾经犯过一个错误:元数据里的时间戳用的是本地时间,而数据文件里用的是 UTC 时间。结果做时间对齐时,差了 8 个小时。嗯,从那以后,我统一要求所有时间戳都用 UTC,并在元数据里注明时区偏移量。

2.7 小结:选型建议

好了,四种格式和元数据提取都聊完了。最后给你一个选型建议,纯属个人经验:

  • 如果你在做算法开发, 用 ROSbag。生态好,工具多,调试方便。
  • 如果你在做底层驱动或时间同步验证, 用 PCAP。原始数据,信息无损。
  • 如果你在做大规模数据管理或训练集构建, 用 HDF5。结构化好,压缩率高。
  • 如果你在做车载总线分析, 用 MDF4。行业标准,工具链成熟。

至于元数据,别偷懒。从一开始就建立好规范,后面会省很多事。你想想看,数据采集只是第一步,后面的回放、分析、训练,才是真正出活的地方。数据格式搞清楚了,后面的路就好走了。