资讯动态

Argoverse轨迹预测数据集精读与多模态基线复现

发布时间:2026/10/27 14:54:56 来源:尧图企业网站定制
做轨迹预测这块绕不开的一个基础活儿就是把数据集吃透。我自己的实验笔记里数据集那一节被反复改了七八版最后一版干脆单独拎出来编号叫“1.6.1 Argoverse”。之所以给这么重的篇幅是因为后面模型结构、损失函数、评测脚本全都长在它上面——数据没读懂模型再花哨也是白跑。Argoverse 是自动驾驶领域一套公开的多任务数据集与开发工具链核心包含轨迹预测、3D 检测与跟踪、传感器序列、高精地图四大块其中轨迹预测那部分是绝大多数人做多模态预测的第一个正式练手场。它能解决什么问题简单说给你一辆车和它周围一群交通参与者过去几秒的轨迹再加一张矢量化的车道级地图让你预测其中某个目标接下来几秒会怎么走。听起来像算命但它有明确的物理约束、有地图约束、有可量化的评测口径。这套东西适合谁刚进自动驾驶感知预测方向、需要跑通一条端到端流水线的同学做时序多模态建模、想找个真实场景验证想法的算法工程师还有做毕设、需要一份结构完整又能复现的数据支撑的研究者。下面就按我自己的理解顺序把它一层层拆开。1. 先把这个数据集的位置摆正1.1 它到底想解决什么样的问题单车的轨迹预测和行人轨迹预测最大的区别在于前者不是“一条曲线拟合”而是一个被结构强约束的多智能体问题。红绿灯、车道线、路口渠化、前车刹车、侧向加塞每一个都在给目标车辆的未来加约束。所以一个能打的预测数据集不能只给坐标点必须同时具备三样东西足够长的历史与未来窗口、与轨迹严格对齐的高精地图、以及一套能区分“简单场景”和“地狱场景”的划分方式。Argoverse 的价值就在这三样上都给了比较完整的答案——历史窗口够长地图是矢量级的车道中心线和车道边界场景采样上刻意偏向有交互的路口和变道片段。我个人的判断是它不算最庞大的数据集但它的“结构完整度”很高非常适合用来建立对预测任务的第一手直觉。1.2 三类子任务别一锅端很多人第一次接触会犯一个迷糊把检测跟踪和轨迹预测当成一件事。其实它分得挺清楚。轨迹预测任务给的是已经标注好的历史轨迹你只需要输出未来3D 跟踪任务给的是原始传感器序列你得自己先从点云和图像里把目标框出来、再关联成轨迹传感器序列那部分则是给感知、标定、时序融合这类任务准备的素材库。我建议的入门顺序是先啃轨迹预测因为它的输入是结构化表格不需要碰点云能把全部精力放在“怎么建模交互”上等预测这条线跑通了再回去看跟踪任务你会立刻明白为什么预测的上游误差那么致命——框抖一下预测的输入就脏一片。1.3 版本怎么选v1 和 v2 别混着用这里有个坑必须先说清楚。两个大版本在采样频率、窗口长度、坐标系约定上都不一样网上很多开源实现是混着写的你抄过来会莫名其妙 loss 不降。我整理了张表是我自己踩过之后记下来的维度v1 预测集v2 预测集采样频率10Hz10Hz历史窗口2 秒20 帧5 秒50 帧未来窗口3 秒30 帧5 秒50 帧单场景总长51 帧110 帧场景规模约 32 万条约 25 万条覆盖范围两座城市六座城市地图形式矢量车道图层矢量车道图层 可行驶区域多边形官方看板指标minADE / minFDE / MR / 可行驶区域合规增加概率校准项与区域合规项选型上我的建议很直接如果只是要验证一个交互建模的想法用 v1数据量小、读取快、教程多如果目标是往论文或正式项目上靠用 v2因为它的评估口径更严格概率校准那一项会逼着你把多模态的置信度真正学出来而不是随便撒六条线取最近的那条。1.4 为什么它值得花一周时间精读我见过太多人上来就 clone 一个开源仓库、改改配置文件、跑出个数就交差。这种做法的天花板很低因为你不知道数据里哪些是噪声、哪些是刻意设计。举个例子v2 里有些场景的目标车全程静止如果你不筛掉模型会学到一个“预测不动”的偷懒解在验证集上看着还行一上真实数据就崩。再比如地图里的车道段是有方向属性的你把方向丢了左右转就分不清。这些细节官方文档不会重点强调但决定了你的实验能不能复现。花一周时间把数据结构、坐标系、采样逻辑过一遍后面能省下至少一个月的瞎调参。2. 一份场景文件里到底装了些什么2.1 目录组织与命名约定v2 的预测数据是按分片打包的解压之后每个场景一个独立文件夹文件夹里通常有三个关键文件一个是存轨迹表的列式文件一个是存地图的 JSON还有一个是记录元信息的小文件。命名上有个规律场景 ID 会在三个文件名里重复出现所以写加载器的时候别硬编码文件名用“目录 通配”的方式去找否则遇到个别命名不一致的分片就直接崩。我早期的加载脚本就是写死了后缀跑到第 300 多个分片时报错排查了半小时才发现是某个分片的元信息文件扩展名不同。这种坑很小但很耗人。2.2 轨迹表字段逐个说透轨迹表是整份数据的核心我按重要性排一下。时间戳字段决定一切时序对齐v2 是 10Hz也就是相邻帧差 100 毫秒但是要注意——不是所有目标都在每一帧有观测中间会有空洞官方是允许缺帧的。目标标识字段用来把同一个交通参与者串成一条轨迹这里要特别注意“焦点目标”这个概念每个场景只指定一个焦点目标作为预测对象其他目标都是上下文。类别字段区分轿车、行人、骑行者、卡车、公交等等实测下来这个字段里混着不少“未知”类别训练前必须过滤不然分类头会学出一个永远不用的空类。位置字段是城市坐标系下的绝对坐标速度与朝向字段是基于位置差分算出来的我一般会自己重算一遍因为官方的朝向在低速时抖动很明显。注意低速或静止目标的角度字段基本不可信。目标速度低于 1 米每秒时朝向会在正负一百八十度之间跳这时候别拿它当真值监督用速度方向或者直接屏蔽该帧的角度损失更稳。2.3 高精地图的矢量语义地图这块是 Argoverse 比较下功夫的地方。它以车道段为基本单元每个车道段带左右边界线、中心线、前驱和后继关系、是否位于路口、以及是机动车道还是非机动车道。可行驶区域是单独的多边形图层用来做区域合规判断。我一开始只用了中心线模型学出来的轨迹老是贴着车道边缘走后来把左右边界也加进去轨迹就居中多了。原因不难理解中心线只告诉你“这条路往哪拐”边界才告诉你“这条路有多宽”而宽度直接决定了变道的可行性和转弯的曲率上限。另外前驱后继关系很重要它是构建车道拓扑图的天然素材做图神经网络的时候可以直接当边用。2.4 场景切分与采样逻辑背后的考量为什么是 5 秒历史加 5 秒未来而不是 3 秒这里有个很实际的工程权衡。历史太短模型看不到目标的加速意图和变道前兆历史太长输入序列变长训练成本上升而且老早以前的信息对当下决策的意义在衰减。5 秒大概是城市道路上一个完整操作打灯、观察、变道、回正的时间尺度。未来 5 秒的选择逻辑不一样它是在保证“有意义”和“可预测”之间取平衡1 秒内的未来几乎是确定的预测难度太低10 秒后的未来受随机因素影响太大人自己都猜不准。5 秒刚好覆盖“会做出什么机动”的层面而不要求精确到每一厘米。这个设计思路值得记住你自己做任何时序预测任务窗口长度的选择都应该回到“我的任务需要多长的因果链”这个问题上。3. 从零跑通加载、归一化与第一次可视化3.1 环境搭建与依赖版本先说环境。这类官方工具链对 Python 版本和依赖版本比较敏感我一般会单独建一个环境Python 用 3.9避免太新的版本把老依赖顶掉。conda create -n av2 python3.9 -y conda activate av2 pip install av2 shapely numpy pandas matplotlib torch装完之后先别急着写模型跑一遍导入检查import av2 from av2.datasets.motion_forecasting import scenario_serialization from av2.map.map_api import ArgoverseStaticMap print(ok)注意不同版本的开发工具包模块路径和函数签名会变。你从网上抄的代码跑不通八成不是代码错是版本不匹配。先确认自己装的版本号再去对应该版本的文档别硬改。3.2 读一个场景并检查完整性读取一个场景的标准流程我固定成四步加载轨迹、加载地图、拿到焦点目标、做一次完整性检查。from pathlib import Path import numpy as np scenario_path Path(./train/0000abcd-1234/scenario_0000abcd-1234.parquet) scenario scenario_serialization.load_argoverse_scenario_parquet(scenario_path) map_path scenario_path.parent / flog_map_archive_{scenario.scenario_id}.json static_map ArgoverseStaticMap.from_json(map_path) focal scenario.get_focal_track() print(焦点目标类别:, focal.object_type) print(轨迹点数:, len(focal.timestamps)) # 完整性检查历史窗口是否够 50 帧 hist_mask focal.timestamps focal.timestamps[0] 5_000_000_000 print(历史帧数:, hist_mask.sum())这里的5_000_000_000是纳秒因为时间戳单位是纳秒。我见过有人直接拿它当毫秒算结果历史窗口只有 5 毫秒模型输入全空loss 却还在降——因为模型学会了输出均值。这种错误最阴不报错只是结果烂。检查的时候顺手看一眼焦点目标的历史轨迹是不是几乎不动如果是直接跳过这个场景别让它污染你的训练集。3.3 坐标归一化为什么必须平移到焦点目标原点这一步是新手最容易忽略、也最容易埋雷的地方。原始数据是城市坐标系的绝对坐标数值能到几万几十万米。你直接喂给网络一是数值范围太大不利于收敛二是每个场景的绝对位置都不同模型会学到一堆无意义的偏置。正确做法是把整个场景做一次刚体变换以焦点目标在当前时刻的位置为原点以它的朝向为 x 轴正方向把所有目标轨迹和地图矢量一起旋转平移过来。def to_focal_frame(xy, origin, heading): c, s np.cos(-heading), np.sin(-heading) R np.array([[c, -s], [s, c]]) return (xy - origin) R.T写这段代码有两个点要盯死旋转角度的正负号以及 y 轴是左手还是右手。我在这上面栽过一次模型训练三十轮 loss 卡在 1.2 不动最后发现是 y 轴方向反了整个地图镜像了左转场景全被当成右转学。判断方法很简单归一化后焦点目标的历史轨迹应该基本沿着 x 轴正向分布y 值在零附近小幅波动。如果你看到历史轨迹是竖着的那百分之百转错了。3.4 矢量编码和栅格编码选哪个地图怎么喂给模型是第二个分岔口。我把两种方式的取舍整理如下对比项矢量编码栅格编码表达精度高保留车道拓扑低受分辨率限制计算开销中等随车道段数量增长固定取决于画布尺寸拓扑关系天然保留需要卷积隐式学习实现难度偏高要做采样和聚合低画出来就行适合的模型图网络、注意力卷积、多任务共享主干我的实际做法是前期用栅格跑通全流程因为可视化直观、调试方便错误一眼能看出来等流程稳了再切矢量把车道段采样成固定数量的点每点带坐标、方向、类型三个特征。切换的过程会带来一次指标波动别慌那是模型在适应新的输入分布不是你的实现错了。4. 拼一个能打的基线输入、损失与评估4.1 输入特征怎么构造输入特征我一般分成三组。第一组是目标自身的历史状态归一化后的 xy 坐标、速度、加速度、朝向的正余弦。加速度一定要算因为刹车和匀速在位置序列上几乎看不出区别但加速度一阶差分立刻暴露意图。第二组是邻车上下文只取焦点目标一定半径内的目标按距离排序取前若干个每个目标带相对位置、相对速度、类别独热。半径别设太大五十米外目标对当前决策的影响基本可以忽略反而增加噪声和算力。第三组是地图上下文沿焦点目标当前朝向取前方一定范围内的车道段采样点。feat_self np.concatenate([xy_norm, vel_norm, acc_norm, np.stack([np.cos(h), np.sin(h)], -1)], axis-1)特征工程这块我有条经验不要一次性把所有特征都加上。先跑一个只有目标自身历史的版本记下指标然后加邻车再加地图每加一组看一次提升幅度。这样你能明确知道收益来自哪里写报告、写论文都有话说。我见过的多数“模型不 work”的案例问题不在模型在于特征里混进了大量噪声或者归一化做错了。4.2 输出头与多模态的坑多模态输出的标准做法是出 K 条候选轨迹每条带一个置信度。K 取 6 是这套数据集的传统因为官方评测就是按 K 算的。损失一般是两部分相加分类损失用交叉熵回归损失用对最近真值轨迹的平滑 L1 或者高斯负对数似然。这里有个很隐蔽的坑叫“模态坍缩”——训练几十轮之后六条轨迹慢慢收敛到几乎重合分类头退化成一个常数输出minADE 看着还行其实模型已经完全放弃了多模态。我的应对手段有三个。一是用赢家通吃的硬分配每轮只让离真值最近的那条轨迹回传梯度逼着不同模态去覆盖不同区域二是加多样性正则惩罚六条轨迹之间的过度相似三是分类头用温度系数的软化标签别用独热让它有一点容错空间。实测下来第一条效果最直接第三条最稳。4.3 评估指标自己实现一遍官方看板固然权威但我强烈建议自己实现一遍指标因为你需要理解每个数字背后的含义也需要在本地快速迭代。def min_ade(pred, gt): # pred: (K, T, 2) gt: (T, 2) d np.linalg.norm(pred - gt[None], axis-1) # (K, T) return d.mean(axis1).min() def min_fde(pred, gt): d np.linalg.norm(pred[:, -1] - gt[-1][None], axis-1) return d.min() def miss_rate(pred, gt, thr2.0): return float(min_fde(pred, gt) thr)minADE 是各模态平均位移误差的最小值minFDE 是终点误差的最小值miss rate 是终点误差超过两米的样本比例。这三个指标有个共同特点都取最小值意味着只要六条轨迹里有一条蒙对了指标就好看。这就是所谓的“撒网式作弊”。所以只看 minADE 会高估模型能力你还得看分布——比如六条轨迹的置信度和准确率是否一致。v2 引入概率校准项本质上就是在治这个病你把某条轨迹的置信度标成九成结果它错了惩罚会很重。4.4 训练配置上的几个取舍批大小我一般设 64 到 128取决于显存。学习率用 1e-3 配余弦退火warmup 一千步左右。训练轮数别贪多这套数据在 v2 上三十轮左右就能看出趋势超过五十轮基本是过拟合验证集指标开始原地打转。硬件上单张消费级显卡跑 v2 的轻量模型是够的但如果你想做大规模对比实验建议先用 v1 或者 v2 的一个子集把想法筛一遍再上全量。我自己的习惯是固定一个两千场景的小子集做“冒烟测试”任何改动先在这个子集上跑五轮看趋势趋势不对立刻停别浪费算力。提示每次改完数据管线第一件事是把一个 batch 的输入可视化出来看轨迹、地图、邻车是否对得上。这一步花两分钟能省掉后面几小时的困惑。5. 常见问题与排查实录5.1 症状对照表我把这几年被问得最多、自己也踩过的问题整理成表按症状反查原因效率高很多症状最可能的原因处理办法loss 完全不降坐标系旋转方向反了或 y 轴镜像可视化历史轨迹应沿 x 正向loss 降到某个值就卡住多模态坍缩六条轨迹重合成一条改硬分配加多样性正则训练指标好、验证指标崩场景级泄漏或过滤逻辑不一致检查划分统一过滤规则时序对齐错位用索引下采样而不是按时间戳一律按时间戳取窗口地图和轨迹对不上地图未随轨迹同步做刚体变换变换矩阵要作用于所有几何内存爆掉一次性加载全部场景改流式迭代加载个别分片读取报错文件名硬编码用通配符匹配5.2 几条只在真跑起来才会遇到的坑第一个坑是缺帧。官方允许目标在序列中某些帧缺失观测你如果假设每帧都有数据、直接用索引切片就会把后面所有帧整体错位。正确做法是先按时间戳重采样到固定网格缺的地方用插值补并且给补出来的帧打上掩码让损失函数跳过它们。我早期没做掩码模型把插值出来的平滑轨迹当真值学结果预测出来的轨迹异常平滑转弯半径大得离谱。第二个坑是静止场景。城市数据里有相当比例的目标全程几乎不动比如等红灯。这类样本如果占比过高模型会退化成“预测不动”的局部最优。我的做法是统计焦点目标历史位移低于阈值的降采样或者直接剔除同时保留一部分作为难例避免模型对静止目标完全失去感知。第三个坑是地图与轨迹的时间一致性。高精地图是静态文件但采集时的车道可能已经变了这种不一致在数据里是客观存在的。你不用去修它但要知道指标的天花板在哪里——有些场景你怎么调都上不去不是模型的问题。第四个坑是评测口径。自己写的指标和官方看板经常差个零点零几原因是边界情况的处理不同有的实现把无效样本剔除了有的保留了。我的经验是提交前一定用官方脚本跑一遍自己的实现只用于日常迭代。5.3 数据增强的边界在哪里增强能提点但这类任务的增强很容易做过头。平移和旋转是安全的因为场景整体刚体变换不改变语义。加高斯噪声也还行模拟定位误差但幅度别超过半米否则轨迹会穿出车道。镜像要特别小心左右镜像之后车道拓扑的连通关系、路口渠化都可能变得不合理左转变右转本身没错但如果你的地图没有跟着一起镜像模型学到的是矛盾的信号。我的实践是镜像只用在纯轨迹的预训练阶段正式训练不开。5.4 从实验到落地的量级意识最后说个容易被忽略的点。实验室里我们关心的是 minADE 小数点后第二位真到工程里更关心的是单帧推理延迟和内存占用。这套数据集的场景是切好的、干净的、标注完备的真实车上拿到的是带噪的、有时延的、可能丢帧的输入。我的建议是在模型定型之前就做一次“退化测试”人为把输入轨迹的精度降一半、随机丢掉两成历史帧、给邻车加个百毫秒延迟看指标掉多少。掉得少的模型才是能用的模型。我自己做过一次这样的测试minADE 从零点八掉到一点四才知道之前的漂亮数字有多脆弱——那次之后我所有实验都会在报告里附上退化测试的结果。我个人的体会是Argoverse 这套东西的价值不在于它有多大而在于它把“地图约束、多智能体交互、多模态不确定性”这三件事用一套统一的格式串起来了。你把它读透一遍再去看其他同类数据集会发现结构大同小异迁移成本很低。真正需要花时间的是那几条永远不会写在文档里的细节时间戳单位、坐标轴方向、缺帧处理、场景过滤。这些地方不出错实验就已经成功了一半。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑