4、时序融合策略:单帧 vs 多帧、时序对齐方法、基于Attention的时序融合、显存优化技巧
时序融合,说白了就是让车「记住」之前看到的东西。
我刚开始做BEV感知时,总觉得单帧检测就够了。直到有一次,一辆黑色轿车被大货车完全挡住,等它重新出现时,我的模型愣是花了3帧才重新识别出来。嗯,那次之后,我老老实实开始搞时序融合。
4.1 单帧 vs 多帧:什么时候该「翻旧账」?
先聊个基础问题:到底用几帧?
我个人习惯把场景分成三类:
| 场景 | 推荐帧数 | 原因 |
|---|---|---|
| 高速巡航 | 2-3帧 | 场景变化慢,多帧收益递减 |
| 城市拥堵 | 4-6帧 | 遮挡频繁,需要历史信息补全 |
| 十字路口 | 6-8帧 | 行人、自行车轨迹复杂,需要长时序 |
你想想看,单帧检测就像「金鱼记忆」——看到什么算什么。多帧融合则像「大象记忆」,能利用历史信息推断被遮挡物体的位置。
核心结论:单帧适合实时性要求极高、遮挡少的场景;多帧适合复杂城市道路。我个人建议,量产项目至少用3帧起步。
4.2 时序对齐方法:别让历史数据「跑偏」
多帧融合最大的坑是什么?对齐错误。
我曾经在项目中遇到过,车辆转弯时,前一帧的障碍物位置和当前帧差了半个车身。如果不做对齐,融合后的特征图就像「重影」一样,检测框乱飘。
常用的对齐方法有三种:
- 基于里程计的对齐:利用IMU+轮速计,将历史帧的BEV特征变换到当前帧坐标系。优点是计算快,缺点是长距离漂移。
- 基于视觉特征的对齐:通过光流或特征匹配,找到前后帧的对应关系。精度高,但计算量大。
- 基于可变形卷积的对齐:让网络自己学偏移量。我试过,效果不错,但训练不稳定。
我的经验:量产项目建议用「里程计+视觉特征」的混合方案。先用里程计做粗对齐,再用视觉特征做细调。这样既保证实时性,又不会漂移太离谱。
4.3 基于Attention的时序融合:让模型学会「选择性记忆」
传统做法是把多帧特征直接concat或相加。但这样有个问题:所有历史帧的权重都一样。
你想想看,3秒前的帧和0.1秒前的帧,重要性能一样吗?
所以,我推荐用时序Attention。核心思路是:让模型自己决定「哪一帧的哪个位置更重要」。
代码示例(简化版):
class TemporalAttention(nn.Module):
def __init__(self, dim):
super().__init__()
self.query = nn.Linear(dim, dim)
self.key = nn.Linear(dim, dim)
self.value = nn.Linear(dim, dim)
def forward(self, x):
# x: [B, T, H, W, C] T是时序长度
B, T, H, W, C = x.shape
x_flat = x.view(B, T, -1, C) # [B, T, H*W, C]
Q = self.query(x_flat[:, -1]) # 只用当前帧做query
K = self.key(x_flat)
V = self.value(x_flat)
attn = torch.softmax(Q @ K.transpose(-2, -1) / sqrt(C), dim=-1)
out = (attn @ V).view(B, H, W, C)
return out
注意:Attention的计算量随帧数线性增长。我试过8帧的Attention,显存直接爆了。后面会讲怎么优化。
4.4 显存优化技巧:别让GPU「吃撑」
多帧融合最头疼的就是显存。我记得有一次,模型在3090上跑,4帧就占了18GB显存,根本没法部署。
分享几个我常用的优化技巧:
- 时序下采样:不是每帧都存,而是每隔1-2帧存一次。比如30fps的视频,我只取10fps。效果损失很小,显存省一半。
- 特征压缩:用1x1卷积把BEV特征的通道数从256降到64。嗯,这里要注意,压缩太狠会丢信息,我一般控制在4:1的比例。
- 梯度检查点:训练时只保留部分中间结果,反向传播时重新计算。时间换空间,显存能省30%-50%。
- 混合精度训练:FP16训练,显存直接减半。但要注意梯度溢出,我习惯在loss里加个scale。
避坑指南:我曾经为了省显存,把特征压缩到32通道,结果模型精度掉了5个点。后来发现,压缩后的特征丢失了细粒度信息,尤其是小目标。所以,压缩比例要根据目标大小动态调整。
最后说一句,时序融合不是越复杂越好。我见过有人把10帧的Transformer堆上去,效果还不如简单的3帧concat。关键是要理解你的场景需求——遮挡多就多帧,遮挡少就少帧,别为了炫技而炫技。