自动驾驶轨迹预测这个方向我从早期做规则-based的卡尔曼滤波跟踪开始到后来转深度学习方案前后折腾了差不多四年。中间踩过的坑、推翻的方案、重写的训练脚本加起来能堆满一个移动硬盘。今天聊的这个基于深度卷积网络的多模态轨迹预测是我认为目前工程落地性价比最高的一条路线——它不像Transformer方案那样吃数据吃到令人绝望也不像纯LSTM那样在长时域预测上拉胯而是在特征提取效率和多模态输出能力之间找到了一个很实用的平衡点。这篇文章主要面向两类人一是刚进入自动驾驶预测模块的算法工程师想快速搞清楚多模态轨迹预测到底在做什么、怎么做二是已经有一定深度学习基础但对多模态这个概念的工程实现还比较模糊的开发者。我会从问题定义开始把深度卷积网络在轨迹预测中的具体角色、多模态输出的实现机制、数据集的选择与处理、训练中的实际坑点以及部署时的性能取舍全部拆开讲一遍。不会只给公式也不会只给代码而是把为什么这样做和实际做下来什么感受都讲清楚。1. 多模态轨迹预测到底在预测什么1.1 从单模态到多模态一个本质的认知转变很多人刚接触轨迹预测时第一反应是把它当成一个回归问题输入历史轨迹输出未来轨迹。用MSE损失训一个LSTM或者时序卷积网络跑出来一条曲线任务就算完成了。这个思路在简单场景下能跑通但一上路就出问题。原因很简单未来本身就不是确定的。一个车在前方路口它可能直行、可能左转、可能右转甚至可能掉头。你让模型只输出一条轨迹它学到的必然是所有可能性的平均值——一条既不直行也不转弯的、物理上根本不存在的轨迹。这就是所谓的**模式平均mode averaging**问题是单模态回归方案的致命伤。多模态轨迹预测的核心思路是不要求模型输出唯一答案而是让它输出一组候选轨迹每条轨迹代表一种合理的未来可能性同时给出每条轨迹的置信度。这样一来下游的规划模块就可以根据这些候选轨迹做风险评估和决策而不是被一条平均轨迹误导。这个转变看似简单但在工程实现上涉及一整套设计输出头怎么设计、损失函数怎么定义、训练时怎么分配标签、推理时怎么筛选轨迹。后面我会逐一展开。1.2 深度卷积网络在时序建模中的独特优势说到时序建模大多数人第一反应是RNN、LSTM或者Transformer。卷积网络不是天然为序列设计的那为什么在轨迹预测里用卷积这里有几个实际考量。第一卷积的感受野可以通过堆叠和膨胀dilation灵活控制。轨迹预测需要同时捕捉短时运动特征比如最近几帧的加速度变化和长时意图特征比如过去几秒的行驶方向趋势膨胀卷积恰好能在一个网络里同时覆盖这两个尺度而不像LSTM那样所有信息都压缩在一个隐状态里。第二卷积是并行计算的。训练时LSTM必须按时间步串行展开GPU利用率上不去卷积网络可以一次性处理整个时间窗口训练效率高出一大截。我在实际项目里做过对比同样的数据量一维卷积网络的单epoch训练时间大约是LSTM的60%到70%。第三卷积对局部模式的捕捉非常高效。轨迹数据本质上是局部平滑的相邻帧之间的变化量很小卷积核天然适合提取这种局部运动模式。而Transformer的自注意力机制虽然表达能力强但在数据量不够大的时候容易过拟合而且计算复杂度是序列长度的平方对长时域预测不太友好。当然卷积也有它的局限——对全局依赖的建模不如注意力机制直接。所以现在很多方案会采用卷积注意力的混合结构但纯卷积方案在中等数据规模下依然是性价比很高的选择。1.3 多模态输出的三种主流实现路径多模态轨迹预测的输出机制目前主流有三种做法各有各的适用场景。第一种是锚点回归Anchor-based Regression。预先定义一组锚点轨迹比如用K-means对训练集里的真实轨迹聚类得到模型不直接输出轨迹坐标而是输出对每个锚点的偏移量和置信度。这种做法训练稳定收敛快但锚点的质量和数量直接决定了模型的上限。锚点太少覆盖不全太多则计算量大且容易冗余。第二种是高斯混合模型GMM输出。模型输出多个高斯分布的参数均值、方差、相关系数以及每个分布的权重最终预测就是这些高斯分布的混合。这种做法理论上更优雅能建模轨迹的不确定性但训练时容易出现数值不稳定需要仔细调参。第三种是直接多分支输出。网络末端接K个独立的回归头每个头输出一条完整轨迹和一个置信度分数。这种做法实现最简单但训练时存在哪个头负责哪条轨迹的分配问题通常需要用Winner-Takes-AllWTA损失或者EM-style的迭代优化。我在实际项目中用得最多的是第一种和第三种结合用锚点做初始化但允许模型输出锚点之外的偏移同时用多个回归头增加多样性。这样既保证了训练稳定性又保留了一定的灵活性。2. 网络架构设计从输入编码到多模态解码2.1 输入表示不只是历史轨迹坐标很多人做轨迹预测输入就是过去N帧的(x, y)坐标然后直接喂给网络。这样做不是不行但信息利用率太低。实际路上跑的车它的未来行为不仅取决于自己的历史位置还取决于周围车辆、车道线、交通信号等环境因素。一个完整的输入表示通常包含以下几类信息自车历史轨迹过去2到3秒的位置序列通常以0.1秒或0.2秒为采样间隔。坐标需要做归一化一般以当前时刻自车位置为原点车头方向为x轴正方向这样可以让模型不受绝对坐标的影响。邻居车辆历史轨迹周围一定范围内比如50米其他车辆的历史轨迹。数量不固定需要做padding和mask处理。地图信息车道线的几何形状、车道连接关系、限速信息等。这部分通常用折线或者栅格化的方式编码。交通信号状态如果预测的是路口场景信号灯状态是非常强的先验。在实际工程中输入信息的丰富程度直接决定了模型性能的上限。我做过一组消融实验只用车轨迹、加上地图、再加上信号灯三组配置的minADE指标差距能达到30%以上。所以如果条件允许尽量把能拿到的信息都喂进去。2.2 一维卷积编码器的具体设计输入数据准备好之后下一步是用卷积网络做特征提取。这里我详细讲一下我常用的一维卷积编码器结构。整个编码器由若干个残差卷积块组成每个块包含两层一维卷积、批归一化和ReLU激活。卷积核大小通常选3或5膨胀率逐层递增1, 2, 4, 8...这样可以在不增加参数量的情况下扩大感受野。具体来说假设输入序列长度为T20过去2秒采样间隔0.1秒特征维度为D包括坐标、速度、加速度、朝向角等那么import torch import torch.nn as nn class ResidualConvBlock(nn.Module): def __init__(self, in_channels, out_channels, kernel_size3, dilation1): super().__init__() padding (kernel_size - 1) * dilation // 2 self.conv1 nn.Conv1d(in_channels, out_channels, kernel_size, paddingpadding, dilationdilation) self.bn1 nn.BatchNorm1d(out_channels) self.conv2 nn.Conv1d(out_channels, out_channels, kernel_size, paddingpadding, dilationdilation) self.bn2 nn.BatchNorm1d(out_channels) self.relu nn.ReLU(inplaceTrue) self.downsample nn.Conv1d(in_channels, out_channels, 1) \ if in_channels ! out_channels else nn.Identity() def forward(self, x): residual self.downsample(x) out self.relu(self.bn1(self.conv1(x))) out self.bn2(self.conv2(out)) return self.relu(out residual)这个结构看起来简单但有几个细节值得注意。批归一化放在卷积之后、激活之前这是标准做法但在轨迹预测任务中如果batch size比较小比如小于16BatchNorm的效果会不稳定这时候可以考虑用LayerNorm替代。残差连接是必须的否则深层网络训练时梯度会衰减得很厉害。膨胀率的选择要跟预测时域匹配如果预测未来3秒感受野至少要覆盖过去2秒加上未来3秒对应的运动尺度。编码器的输出是一个形状为(batch_size, feature_dim, T)的特征图接下来需要做一个时间维度的聚合把整个序列压缩成一个固定长度的上下文向量。常用的做法是注意力池化或者最后时间步取特征。我倾向于用注意力池化因为它能给不同时间步分配不同的权重让模型自己决定哪些历史时刻更重要。2.3 多模态解码器的三种输出头实现解码器部分是多模态预测的核心。我以锚点回归方案为例讲一下具体实现。假设我们预先通过K-means聚类得到了K6个锚点轨迹每个锚点是一条未来T_f30帧3秒的轨迹。解码器的任务是对每个锚点预测一个偏移量和一个置信度。class MultiModalDecoder(nn.Module): def __init__(self, context_dim, num_modes6, future_len30): super().__init__() self.num_modes num_modes self.future_len future_len # 每个模态的偏移量预测头 self.offset_head nn.Sequential( nn.Linear(context_dim, 256), nn.ReLU(), nn.Linear(256, future_len * 2) ) # 每个模态的置信度预测头 self.confidence_head nn.Sequential( nn.Linear(context_dim, 128), nn.ReLU(), nn.Linear(128, 1) ) def forward(self, context, anchors): # context: (B, context_dim) # anchors: (K, future_len, 2) B context.shape[0] offsets self.offset_head(context).view(B, self.num_modes, self.future_len, 2) confidences self.confidence_head(context).view(B, self.num_modes) # 最终轨迹 锚点 偏移量 trajectories anchors.unsqueeze(0) offsets return trajectories, confidences这里有个关键设计偏移量而不是绝对坐标。直接预测绝对坐标的话不同场景下坐标范围差异很大模型很难学。预测相对于锚点的偏移量数值范围小且分布集中训练稳定得多。置信度头输出的是logits推理时需要做softmax归一化。训练时通常用交叉熵损失让模型学会给更接近真实轨迹的模态更高的置信度。2.4 特征融合如何处理多源异构输入实际场景中输入信息来自不同来源维度和语义都不一样。自车轨迹是时序坐标地图是折线集合信号灯是离散状态。怎么把这些异构信息融合到一起我的做法是分路编码、后期融合。每一类输入用独立的编码器处理得到各自的embedding然后通过拼接或者注意力机制融合。具体来说自车轨迹和邻居轨迹用共享的一维卷积编码器处理地图折线用一个轻量级的PointNet或者简单的MLP编码信号灯状态直接做embedding。融合时我用的是多头注意力以自车特征为query其他特征为key和value让模型自适应地选择最相关的信息。这种融合方式的好处是模块化程度高增加或减少某类输入时不需要大改网络结构。缺点是多了一层注意力计算推理延迟会略有增加。在实际部署中如果算力紧张可以把注意力融合换成简单的拼接全连接性能损失通常在5%以内。3. 数据集选择与预处理中的实际问题3.1 主流数据集的特点与适用场景轨迹预测领域有几个常用的公开数据集各有各的特点。Argoverse是我用得最多的一个。它包含美国多个城市的行驶数据有高清地图和丰富的车道线标注场景类型也比较多样。它的预测任务设定是给定过去2秒的轨迹预测未来3秒。数据量大约在30万条左右对于中等规模的模型来说够用。缺点是场景主要集中在美国城市道路如果要做国内场景的落地需要额外做域适应。nuScenes的数据量更大传感器配置也更丰富包含激光雷达和毫米波雷达但它的轨迹预测任务设定和Argoverse略有不同采样频率是2Hz比Argoverse的10Hz低不少。低采样率意味着运动细节丢失较多对模型的特征提取能力要求更高。INTERACTION数据集专注于交互场景包含环岛、无保护左转等复杂交互场景适合研究车辆之间的博弈行为。但它的数据量相对较小如果模型参数量大容易过拟合。Waymo Open Motion Dataset是目前规模最大的之一场景丰富度和标注质量都很高但对算力要求也最高。如果实验室资源有限建议先从Argoverse入手。数据集采样频率预测时域数据量地图信息适用场景Argoverse10Hz3s~30万高清车道线城市道路通用nuScenes2Hz6s~100万语义地图多传感器融合INTERACTION10Hz3s~4万车道连接交互博弈Waymo Open10Hz8s~50万高清地图大规模训练3.2 数据预处理中最容易踩的坑数据预处理看起来是脏活累活但它对最终性能的影响远超很多人的想象。我在这上面踩过的坑比在网络结构上踩的还多。第一个坑是坐标系不统一。Argoverse的数据里不同场景的坐标系原点不一样有的以地图原点为准有的以自车起始位置为准。如果不做统一模型学到的就是一堆混乱的坐标映射。我的做法是统一以当前时刻自车位置为原点车头朝向为x轴正方向做变换。这样每条轨迹的起点都是(0, 0)朝向都是0度模型只需要学相对运动模式。第二个坑是异常值处理。真实数据里总有一些跳变点可能是GPS漂移或者标注错误。如果不处理这些异常值会在训练时产生巨大的梯度把模型带偏。我通常用速度阈值做过滤如果相邻两帧之间的位移超过物理上可能的加速度对应的距离就标记为异常并做插值修复。第三个坑是类别不平衡。直行场景占了绝大多数转弯和变道场景相对稀少。如果直接按原始分布训练模型会倾向于预测直行对转弯场景的预测能力很差。解决办法有两种一是对稀有场景做过采样二是在损失函数里给稀有场景更高的权重。我一般用后者实现更简单效果也不错。3.3 数据增强哪些操作真正有效轨迹预测的数据增强不像图像领域那么直观不能随便旋转翻转因为交通规则和道路结构是有方向性的。以下是我实测有效的几种增强方式全局旋转把整个场景包括地图和所有车辆轨迹绕原点旋转一个小角度比如±10度。这个操作模拟的是不同朝向的道路场景不改变交通规则的语义。时间缩放把轨迹的时间轴做轻微缩放比如0.9倍到1.1倍模拟不同驾驶风格下的速度差异。注意缩放后要重新计算速度和加速度。邻居车辆dropout随机丢弃一部分邻居车辆的历史轨迹模拟传感器漏检的情况。这个增强能提升模型对不完整输入的鲁棒性。轨迹加噪在历史轨迹上叠加小幅高斯噪声模拟定位误差。噪声的标准差要控制在实际定位精度的范围内通常0.1到0.3米。需要提醒的是不要做镜像翻转。左右翻转会改变左转和右转的语义在靠右行驶的交通规则下这会产生不合理的训练样本。4. 训练策略与损失函数设计4.1 多模态损失的组合方式多模态轨迹预测的损失函数设计是整个训练过程中最需要仔细斟酌的部分。单一损失很难同时兼顾轨迹精度和模态多样性。我通常用三个损失加权组合第一是回归损失衡量预测轨迹和真实轨迹之间的偏差。常用的是Smooth L1损失或者Huber损失比纯MSE对异常值更鲁棒。但这里有个关键问题多模态输出有K条轨迹应该用哪条来计算回归损失答案是只用在当前模型下最接近真实轨迹的那条这就是Winner-Takes-All策略。具体做法是先计算K条预测轨迹和真实轨迹的距离选出距离最小的那条只对它做回归损失。这样每个模态可以专注于学习一类特定的未来模式而不是所有模态都去拟合同一条轨迹。第二是分类损失让模型学会判断哪个模态最可能接近真实轨迹。用交叉熵损失标签是距离最小的那个模态的索引。这里有个细节如果多条轨迹距离真实值都很近硬标签可能会让模型困惑。我试过用soft label根据距离做softmax得到软标签效果比硬标签略好但提升幅度不大。第三是多样性损失防止所有模态收敛到同一条轨迹。最简单的做法是惩罚模态之间的重叠如果两条预测轨迹过于相似就加一个惩罚项。更优雅的做法是使用EM-style的迭代优化但实现复杂度高不少。def multimodal_loss(predictions, confidences, ground_truth, anchors): # predictions: (B, K, T, 2) # confidences: (B, K) # ground_truth: (B, T, 2) B, K, T, _ predictions.shape # 计算每条预测轨迹与真实轨迹的距离 distances torch.norm(predictions - ground_truth.unsqueeze(1), dim-1).mean(dim-1) # (B, K) # Winner-Takes-All: 选出距离最小的模态 best_mode distances.argmin(dim1) # (B,) best_pred predictions[torch.arange(B), best_mode] # (B, T, 2) # 回归损失 reg_loss nn.SmoothL1Loss()(best_pred, ground_truth) # 分类损失 cls_loss nn.CrossEntropyLoss()(confidences, best_mode) # 多样性损失惩罚模态之间的过度重叠 diversity_loss 0 for i in range(K): for j in range(i1, K): dist_ij torch.norm(predictions[:, i] - predictions[:, j], dim-1).mean() diversity_loss torch.exp(-dist_ij) diversity_loss diversity_loss / (K * (K-1) / 2) total_loss reg_loss 0.5 * cls_loss 0.1 * diversity_loss return total_loss三个损失的权重需要根据具体任务调。我的经验是回归损失权重设为1分类损失在0.3到0.8之间多样性损失在0.05到0.2之间。多样性损失不能太大否则会牺牲预测精度来换取多样性得不偿失。4.2 学习率调度与训练稳定性轨迹预测模型的训练学习率调度非常关键。我一般用余弦退火热重启的策略初始学习率设为1e-3每训练20个epoch做一次余弦退火到1e-5然后热重启回1e-3。这样可以让模型在训练后期跳出局部最优找到更好的解。另外梯度裁剪是必须的。轨迹预测的损失函数在某些困难样本上会产生很大的梯度如果不裁剪一次异常更新就可能毁掉之前所有的训练成果。我通常把梯度范数裁剪到1.0。还有一个容易被忽略的点BatchNorm的动量参数。默认值是0.1但在轨迹预测任务中由于batch内样本差异较大默认动量可能导致running statistics不稳定。我一般调到0.01让统计量更新更平滑。4.3 训练过程中的监控指标训练时不能只看loss还要监控几个关键指标minADE最小平均位移误差K条预测轨迹中与真实轨迹最接近的那条的平均位移误差。这个指标衡量的是模型是否至少给出了一条靠谱的预测。minFDE最小最终位移误差最接近的那条轨迹在终点处的位移误差。衡量的是长时域预测能力。Miss Rate如果所有K条预测轨迹的终点都偏离真实终点超过2米就算一次miss。这个指标衡量的是模型是否完全预测失败。模态利用率统计每个模态被选为最优模态的频率。如果某个模态几乎从不被选中说明它是冗余的可以考虑减少模态数量。我在训练时会把minADE和minFDE作为主要监控指标每5个epoch在验证集上跑一次。如果连续3次验证指标不下降就触发学习率衰减或者早停。5. 推理部署中的性能优化与工程取舍5.1 模型量化与推理加速训练好的模型要上车载平台推理效率是绕不开的问题。一个标准的卷积轨迹预测模型参数量大约在2M到10M之间在服务器GPU上推理延迟通常在10ms以内但到了车载嵌入式平台可能就变成50ms甚至100ms了。我常用的加速手段有三种第一是INT8量化。把模型权重和激活值从FP32量化到INT8推理速度可以提升2到3倍精度损失通常在1%到3%之间。关键是量化校准集的选择——要用有代表性的真实场景数据不能随便拿训练集的一个子集凑数。第二是算子融合。把卷积、批归一化、ReLU融合成一个算子减少内存访问次数。大部分推理框架如TensorRT、ONNX Runtime都支持自动融合但需要确保模型导出时结构是干净的。第三是剪枝。去掉不重要的通道或层。轨迹预测模型里解码器部分的冗余通常比编码器大因为多模态输出头之间有很多重复计算。我试过对解码器做结构化剪枝去掉30%的通道minADE只下降了2%左右但推理速度提升了近40%。5.2 多模态输出的后处理策略模型输出K条轨迹和对应的置信度之后不能直接丢给下游规划模块还需要做后处理。第一步是置信度过滤。把置信度低于阈值的轨迹直接丢弃。阈值的选择需要根据实际场景调我一般设在0.05到0.1之间。太低起不到过滤作用太高可能把一些合理的预测也丢掉。第二步是轨迹平滑。模型输出的轨迹可能在时间上不够平滑有轻微的抖动。可以用样条插值或者简单的移动平均做平滑。但要注意平滑不能改变轨迹的整体形状否则可能把转弯轨迹平滑成直行。第三步是物理约束检查。检查预测轨迹是否满足车辆运动学约束曲率是否超过最大转向能力、加速度是否在合理范围内、是否与地图上的不可行驶区域重叠。不满足约束的轨迹要降权或者直接剔除。第四步是NMS非极大值抑制。如果两条预测轨迹过于相似保留置信度高的那条抑制另一条。这样可以在不减少模态数量的情况下让最终输出的轨迹更具多样性。5.3 实际部署中的延迟与精度平衡在实际项目中延迟和精度永远是一对矛盾。我的经验是先确定延迟预算再在这个预算内最大化精度。假设整个预测模块的延迟预算是30ms那么模型推理本身不能超过20ms剩下10ms留给预处理和后处理。在这个约束下如果INT8量化后的模型推理需要15ms那就可以考虑用更大的模型或者更多的输入特征来提升精度如果量化后还需要25ms那就必须做剪枝或者减少模态数量。还有一个实用技巧根据场景复杂度动态调整计算量。在简单场景比如高速直行下用轻量级模型或者减少模态数量在复杂场景比如城市路口下切换到完整模型。这种动态推理策略可以在平均延迟和精度之间取得更好的平衡。6. 几个我踩过的坑和对应的解决方案6.1 模态坍缩所有预测轨迹变成同一条这是多模态训练中最常见的问题。训练一段时间后你会发现K条预测轨迹几乎完全重合模型退化成了单模态。根本原因是WTA策略下的梯度分配不均。训练初期某个模态可能偶然比其他模态更接近真实轨迹于是它获得更多的梯度更新变得更好更好的表现又让它更容易被选为winner形成正反馈。最终一个模态主导了所有预测其他模态因为长期得不到有效梯度而饿死。解决办法有几种。我试过最有效的是在训练初期使用soft WTA不是只对距离最小的模态做回归而是对所有模态按距离的softmax加权做回归。这样每个模态都能获得一定的梯度避免早期分化。训练到后期再逐渐过渡到硬WTA。另一种方法是定期重置模态每隔一定epoch把表现最差的模态的参数重新初始化给它一个重新开始的机会。这个做法简单粗暴但在实践中确实有效。6.2 长时域预测的误差累积预测时域越长误差累积越严重。预测未来1秒的时候minFDE可能只有0.5米到未来3秒就变成2米以上了。这个问题本质上是因为模型在自回归式的预测中每一步的误差都会传递到下一步。但我们的模型是一次性输出整条轨迹的不存在自回归的问题。那误差累积来自哪里来自特征表示的模糊性。当预测时域变长时历史轨迹中包含的信息不足以唯一确定未来模型的不确定性增加。解决办法是显式建模不确定性让模型不仅输出轨迹还输出每条轨迹的方差。这样下游模块可以根据方差来判断预测的可靠程度。我在解码器里加了一个方差预测头输出每条轨迹在每个时间步的方差。训练时用高斯负对数似然损失。这样做虽然增加了输出维度但对长时域预测的可靠性提升很明显。6.3 地图信息利用不充分早期我做模型的时候地图信息只是简单地做embedding然后拼接。后来发现这样做效果很差模型基本忽略了地图信息主要靠历史轨迹做预测。问题在于地图信息的编码方式太粗糙。车道线是折线结构直接做embedding会丢失几何信息。后来我改成用折线编码器把每条车道线表示为一个点序列用一维卷积或者PointNet提取特征然后再和轨迹特征做注意力融合。另一个关键是地图信息的粒度。太粗比如只给车道中心线信息不够太细比如给每个车道边界点计算量太大。我的经验是给车道中心线加上车道连接关系这个粒度对大多数场景够用了。6.4 不同场景下的性能波动模型在直行场景下minADE可能只有0.3米到了复杂路口就变成1.5米以上。这种性能波动在实车测试中非常明显。原因是场景分布不均衡。直行场景多模型见得多学得好路口场景少模型见得少学得差。解决办法除了前面提到的过采样和加权损失还有一个很有效的策略场景自适应归一化。具体做法是根据场景类型直行、转弯、路口等对输入特征做不同的归一化。比如在路口场景下把速度特征单独做标准化因为路口的速度分布和直行差异很大。这个操作实现简单但效果提升明显。我在一个项目里加了场景自适应归一化后路口场景的minADE下降了约15%。7. 从实验到落地一些工程经验7.1 离线指标好不代表线上效果好这是我在实际项目中最深刻的体会。离线评测时minADE做到0.5米觉得已经很不错了但上车测试后发现规划模块经常做出不合理的决策。后来分析发现问题出在离线指标和线上需求的错位。离线指标衡量的是平均精度但规划模块关心的是最坏情况下的安全性。一条平均误差很小但偶尔会预测出危险轨迹的模型在线上是不可接受的。所以后来我在离线评测中增加了安全性指标统计预测轨迹与真实轨迹的偏差超过安全阈值的频率以及预测轨迹是否会导致规划模块做出急刹车或急转向的决策。这些指标比单纯的minADE更能反映实际表现。7.2 数据闭环的重要性模型上线后最重要的不是继续调网络结构而是建立数据闭环。把线上遇到的困难场景收集回来标注后加入训练集重新训练模型。这个循环跑得越快模型迭代的效率越高。我在项目中搭建了一个简单的数据闭环流程线上模型推理时记录预测置信度低或者与真实轨迹偏差大的场景片段定期导出并做人工标注然后加入训练集。每跑一轮闭环模型在困难场景下的表现都有明显提升。7.3 模型版本管理与回滚机制轨迹预测模型直接关系到行车安全所以版本管理和回滚机制必须做好。我的做法是每次模型更新都保留完整的训练配置、数据版本和评测报告上线新模型时先做影子模式shadow mode让新模型和旧模型并行推理对比输出差异如果新模型在某些场景下表现异常可以快速回滚到旧版本。这套机制看起来增加了工作量但在实际项目中帮我避免了好几次潜在的事故。特别是当训练数据分布发生变化时新模型可能会在某些边缘场景下产生完全错误的预测如果没有回滚机制后果会很严重。7.4 关于模态数量的选择最后一个实际问题是K应该设多少理论上K越大覆盖的未来可能性越多但计算量也越大而且模态之间的冗余也会增加。我试过K3、K6、K9三种配置。K3在简单场景下够用但复杂路口容易漏掉一些可能性K6是比较平衡的选择能覆盖大多数场景的主要可能性K9的边际收益已经很小了minADE相比K6只提升了不到3%但推理时间增加了近50%。所以我的建议是从K6开始根据实际场景的复杂度和算力预算做调整。如果算力充裕且场景特别复杂可以上到K8或K9如果算力紧张K4或K5也能凑合用但要做好复杂场景下预测不全的心理准备。另外模态数量不一定非要固定。可以根据场景复杂度动态调整简单场景用3个模态复杂场景用6个或更多。这种动态模态策略在工程上完全可行只需要在推理时根据场景分类结果选择不同的解码器分支即可。