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。你想找一个「雨天、夜间、城市道路、有行人横穿」的场景。如果没有元数据,你得一个一个回放,用肉眼去看。那得看到猴年马月?
我个人习惯,在采集数据的同时,就把元数据记录下来。通常有两种方式:
- 内嵌式: 把元数据作为 ROS 话题或 HDF5 数据集,直接写在数据文件里。比如,在 ROSbag 里加一个
/metadata/weather话题。 - 外挂式: 单独写一个 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。行业标准,工具链成熟。
至于元数据,别偷懒。从一开始就建立好规范,后面会省很多事。你想想看,数据采集只是第一步,后面的回放、分析、训练,才是真正出活的地方。数据格式搞清楚了,后面的路就好走了。