
1. 从六个视角各说各话到一张鸟瞰图BEVFormer要解决的核心矛盾做过自动驾驶感知的朋友大概都有这种体会车上装了六七个摄像头前视、侧视、后视各管一片每个摄像头输出的检测框都在自己的图像坐标系里。前视摄像头说前方30米有辆车侧视摄像头说右侧5米有辆车可这两辆车到底是不是同一辆它们之间的空间关系是什么下游的规划控制模块拿到这一堆各自为政的检测结果根本没法直接做决策。传统做法是分别在各视角做2D检测再通过后处理规则去融合。这套路我试过光是处理跨视角的目标关联就够头疼的——遮挡、截断、尺度不一致规则写得再多也总有漏网之鱼。问题的根源在于2D图像空间和真实世界的3D空间之间隔着一道鸿沟你在2D空间里再怎么融合都是在错误的坐标系里打转。BEVBirds Eye View鸟瞰图思路的价值就在这里。它把六个摄像头的图像统一投影到一个俯视的栅格空间里每个格子代表真实世界的一块地面区域所有感知任务——检测、分割、预测——都在这个统一的BEV特征图上完成。这样一来跨摄像头的空间关系天然就对齐了下游模块拿到的是一张上帝视角的语义地图决策逻辑一下子清爽了。但要把多摄像头图像塞进BEV空间难点在于图像是透视投影近大远小BEV是正交投影远近一样大。这个投影关系的转换本身就非平凡更别说还要处理不同摄像头之间的特征对齐、时序信息的融合。BEVFormer这篇工作给出的答案是用时空Transformer来做这件事。它不依赖深度估计的显式监督而是通过可学习的BEV查询query去主动从多摄像头特征里抽取信息再用时序自注意力把历史帧的信息串起来。这篇文章我会从工程落地的角度把BEVFormer的空间交叉注意力、时序自注意力、BEV查询设计这几个核心机制拆开讲清楚补充实际复现时容易踩的坑以及为什么这样设计的底层逻辑。不管你是刚接触BEV感知的新手还是已经在做多传感器融合的从业者应该都能从中拿到一些能直接用的东西。2. BEV查询一组可学习的探针如何定义BEV空间2.1 为什么用查询而不是显式投影理解BEVFormer首先要理解它的核心抽象——BEV查询BEV Query。你可以把它想象成铺在BEV平面上的一个网格比如200×200个格子每个格子对应真实世界中一块固定大小的区域比如0.5米×0.5米。每个格子初始化一个可学习的向量这就是一个BEV查询。这里有个关键的设计选择BEV查询不是从图像特征投影过来的而是凭空初始化、通过训练学出来的。这和LSSLift-Splat-Shoot那类方法的思路完全不同。LSS是先对每个像素预测深度分布然后把图像特征抬到3D空间再拍扁到BEV深度估计的准确性直接决定了BEV特征的质量。而BEVFormer绕开了显式深度估计让BEV查询自己去图像特征里找它需要的信息。为什么这样设计更稳我的理解是显式深度估计在远距离和纹理稀疏区域误差极大一旦深度预测偏了投影到BEV上就是灾难性的错位。而可学习的查询通过注意力机制能够隐式地学到这个BEV格子应该关注图像中的哪些位置这种软关联比硬投影鲁棒得多。实测中在远处车辆和路面标线这些深度估计容易翻车的地方BEVFormer的表现确实更稳定。2.2 BEV查询的初始化与网格划分具体实现上BEV查询的网格划分需要和实际场景匹配。假设你的感知范围是前后各50米、左右各50米BEV网格分辨率是0.5米那么网格大小就是200×200。每个格子对应一个查询向量维度通常是256。这里有个实操细节网格分辨率的选取是个权衡。分辨率太高比如0.2米BEV特征图会变得很大显存吃紧而且远处区域本身信息就稀疏高分辨率是浪费分辨率太低比如1米近处的小目标行人、锥桶可能就糊成一团了。我一般建议近处用高分辨率、远处用低分辨率或者干脆用多尺度BEV特征。BEVFormer原版用的是固定分辨率但在实际项目中根据你的ODD运行设计域来调整网格划分是很有必要的。另外BEV查询在初始化时是全部相同的还是随机的原版实现里是随机初始化然后训练的。但有个技巧可以在初始化时给不同区域的查询加上位置编码让靠近自车的查询和远处的查询从一开始就有区分度收敛会快一些。这个不是必须的但在数据量不够大的时候能帮上忙。2.3 查询与图像特征的交互逻辑BEV查询本身只是一组向量它怎么和图像特征产生关联这就是空间交叉注意力Spatial Cross-Attention要做的事。简单说每个BEV查询会通过一个投影函数计算出它在各个摄像头图像平面上的参考点然后在这些参考点周围采样图像特征通过注意力加权聚合回来。这个过程的直觉是每个BEV格子都在问我对应的真实世界位置在六个摄像头里分别长什么样然后它从所有摄像头的对应位置收集信息融合成一个综合表示。这就把多视角的信息天然地统一到了BEV空间里不需要任何后处理融合规则。3. 空间交叉注意力BEV查询如何在六个摄像头里找信息3.1 从BEV坐标到图像坐标的投影链路空间交叉注意力的第一步是建立BEV查询和图像像素之间的对应关系。这个投影链路是这样的BEV平面上的一个点x, y先假设一个高度z因为BEV是2D的但真实世界是3D的变成3D点x, y, z然后通过相机外参和内参投影到图像平面得到像素坐标。这里有个关键问题z怎么定真实世界里的物体高度各不相同车高1.5米行人1.7米路牌3米。BEVFormer的做法是在z方向上采样多个高度比如4个点每个BEV查询在3D空间里变成一列柱子然后这一列柱子上的每个点都投影到图像上形成多个参考点。为什么要在高度上采样因为如果只用一个固定的z那么对于不同高度的物体投影位置会偏。比如你用z0地面去投影那么一辆车的车顶在图像上的位置和地面位置差很远采样到的特征就不对。多高度采样相当于给每个BEV查询一个立体感知的能力让它能同时关注到地面、车身、车顶等不同高度的信息。3.2 可变形注意力的采样策略有了参考点之后怎么从图像特征里采样BEVFormer用的是可变形注意力Deformable Attention。普通的注意力是全局的每个查询要和所有位置计算注意力权重计算量爆炸。可变形注意力只在一组参考点周围采样少量位置比如每个参考点周围采4个点大大降低了计算量。具体来说每个BEV查询在每个摄像头上会生成若干个参考点高度采样数×每高度采样数每个参考点再通过一个可学习的偏移量在图像特征图上采样。这些采样点的特征通过注意力权重加权求和就得到了这个BEV查询在该摄像头上的特征。六个摄像头的特征再聚合起来就是最终的BEV查询表示。这里有个工程上的坑参考点的有效性判断。不是所有BEV查询投影到图像上都在画面内有些会落到图像外面比如自车正下方的BEV格子可能所有摄像头都拍不到。这些无效的参考点必须被mask掉否则会引入噪声。实现的时候要仔细处理这个mask逻辑我见过有人忘了做这个结果BEV特征图上出现莫名其妙的伪影。3.3 多摄像头特征聚合的权重分配六个摄像头的特征聚合时权重是怎么分配的理论上注意力机制会自动学到哪个摄像头对这个BEV格子更重要。比如一个位于车辆正前方的BEV格子前视摄像头的权重应该更高一个位于车辆右侧的格子右侧摄像头的权重更高。但实际训练中这个权重分配不一定能学得很好特别是当某个摄像头的数据质量差比如雨天侧视摄像头全是水珠时它可能仍然被分配了不合理的权重。一个实用的技巧是在训练时对摄像头做随机dropout模拟某个摄像头失效的情况强迫模型学会不依赖单一摄像头。这个trick在实车上特别有用因为摄像头被遮挡或故障是常态。另外聚合时的归一化也很重要。如果某个BEV查询只被一个摄像头看到那它的特征就只来自那一个摄像头尺度和被多个摄像头看到的查询不一致。实践中可以在聚合后加一个LayerNorm把尺度拉齐。4. 时序自注意力让BEV特征记住上一帧看到的东西4.1 为什么单帧BEV不够用如果只做单帧BEV感知你会遇到几个典型问题。第一遮挡。一辆车被前车挡住了单帧图像里根本看不到但上一帧它可能还在视野里。第二速度估计。单帧无法直接得到物体的运动状态需要多帧才能算速度。第三远处小目标。远处物体在单帧里可能只有几个像素检测置信度很低但多帧累积后就能稳定检出。BEVFormer的时序自注意力就是来解决这些问题的。它的核心思想是当前帧的BEV查询不仅要从当前帧的图像里抽取信息还要从历史帧的BEV特征里抽取信息。这样即使当前帧某个位置被遮挡历史帧的信息也能补上。4.2 历史BEV特征的存储与对齐时序融合的第一步是存储历史BEV特征。BEVFormer维护一个BEV特征队列保存最近几帧的BEV特征图。但这里有个关键问题车辆在动历史帧的BEV特征和当前帧的BEV特征在空间上是不对齐的。比如上一帧车辆在位置A这一帧移动到了位置B那么上一帧BEV特征图里的前方10米和这一帧的前方10米对应的真实世界位置是不同的。如果直接融合就会产生错位。BEVFormer的做法是根据自车的运动信息位移和旋转把历史BEV特征对齐到当前帧的坐标系。这个对齐是通过一个空间变换通常是仿射变换或更一般的网格采样实现的。具体来说对于当前帧BEV特征图上的每个格子计算它在上一帧BEV特征图上的对应位置然后通过双线性插值采样。这个对齐的精度直接影响时序融合的效果。如果自车运动估计有误差比如IMU漂移对齐就会偏融合反而会引入噪声。实践中我会建议用高精度的里程计或者在训练时加入运动噪声做数据增强让模型对对齐误差有一定鲁棒性。4.3 时序注意力的计算与门控机制对齐之后当前帧的BEV查询会和历史BEV特征做自注意力计算。具体来说每个BEV查询会同时关注当前帧的BEV特征和历史帧的BEV特征通过注意力权重决定从哪个时间步抽取多少信息。这里有个细节历史帧的BEV特征本身也是通过空间交叉注意力生成的它已经包含了历史帧的图像信息。所以时序注意力实际上是在做一种递归的信息传递——当前帧从历史帧拿信息历史帧又从更早的帧拿信息。这种递归结构让模型能够利用较长时间跨度的信息但也带来了误差累积的风险。如果某一帧的BEV特征有偏差这个偏差会通过时序注意力传播到后续帧。为了缓解这个问题BEVFormer在时序注意力里加了一个门控机制。具体来说每个BEV查询会计算一个门控值决定它从历史帧拿多少信息。如果当前帧的图像信息足够可靠比如光照好、无遮挡门控值就低主要依赖当前帧如果当前帧信息不可靠门控值就高更多依赖历史帧。这个门控是自适应学出来的不需要人工设定阈值。实测下来这个门控机制在遮挡场景下效果很明显。我做过一个测试让一辆车从另一辆车后面驶出单帧模型在遮挡解除后的第一帧往往检测不到而带时序的BEVFormer在遮挡期间就能通过历史信息记住这辆车的存在遮挡解除后立刻就能稳定跟踪。5. 从论文到实车复现BEVFormer时最容易翻车的几个地方5.1 相机标定误差的放大效应BEVFormer的整个空间交叉注意力都依赖于相机内外参的准确性。如果标定有误差投影链路就会偏BEV查询采样到的图像特征就是错的。更麻烦的是这个误差会随着距离放大——近处偏几厘米远处可能偏几米。我踩过的一个坑是标定的时候用的是理想针孔模型但实际镜头有畸变特别是广角镜头边缘的畸变很大。如果畸变矫正没做好边缘区域的投影误差会非常明显。后来我的做法是在标定后用实际场景做验证——在地上放几个已知位置的标志物看BEV特征图上它们的位置对不对。如果偏了就回去调标定参数直到误差在可接受范围内。另外相机外参在车辆行驶过程中可能会有微小变化震动、温度变形所以在线标定或者标定参数的自适应调整是很有必要的。BEVFormer原版没有做这个但实车上不加这个跑一段时间就会偏。5.2 BEV网格分辨率与显存的平衡前面提到过BEV网格分辨率的权衡这里展开说下显存的问题。BEV查询的数量是网格大小的乘积比如200×200就是40000个查询。每个查询都要和六个摄像头做交叉注意力计算量和显存占用都不小。我实测过在单张24G显存的卡上200×200的BEV网格加上ResNet-101 backbonebatch size只能开到2左右。如果要更大的batch size要么降分辨率要么用更小的backbone要么用梯度累积。一个实用的优化是用稀疏的BEV查询。不是所有BEV格子都需要同等对待——大部分格子是背景路面、天空只有少部分格子包含感兴趣的目标。可以用一个轻量的网络先预测哪些格子是重要的然后只对这些格子做精细的交叉注意力。这个思路在后续的一些工作里有实现能显著降低计算量。5.3 时序融合中的对齐误差处理时序对齐的误差来源主要有两个自车运动估计误差和BEV特征本身的空间分辨率限制。自车运动估计误差前面说了用高精度里程计能缓解。BEV特征的空间分辨率限制则是说BEV特征图是离散的网格对齐时的插值本身就会引入误差特别是当自车运动较大时。我的处理经验是在训练时加入随机的运动噪声。具体来说在对齐历史BEV特征时给变换矩阵加上一个小的随机扰动模拟运动估计误差。这样训练出来的模型对对齐误差更鲁棒。另外时序注意力的门控机制本身也能在一定程度上抑制对齐误差的影响——如果对齐不准门控值会学得比较低减少对历史帧的依赖。还有一个细节历史帧的数量不是越多越好。保存太多历史帧会增加显存和计算量而且太早的历史帧和当前帧的相关性已经很低了。实践中保存2-3帧历史BEV特征通常就够了再多收益递减。5.4 训练策略与收敛问题BEVFormer的训练不是端到端从零开始的。原版用了两阶段训练先训练空间交叉注意力单帧再加载权重训练时序部分。为什么要这样因为如果一开始就同时训练空间和时序时序部分的梯度会干扰空间部分的学习导致收敛慢甚至不收敛。我复现的时候试过直接端到端训练确实收敛很慢而且最终精度不如两阶段。后来老老实实按两阶段来先让空间部分学好怎么看图像再让时序部分学怎么用历史效果就好多了。另外学习率的设置也很关键。空间交叉注意力部分的学习率可以大一些比如2e-4时序部分的学习率要小一些比如1e-4因为时序部分的参数更敏感。还有BEV查询的初始化学习率也要注意太小了学不动太大了容易震荡。6. 时空Transformer之外BEVFormer的边界与我的实际取舍6.1 它擅长什么不擅长什么BEVFormer在几个场景下表现很突出多摄像头视野重叠区域的融合、遮挡场景下的目标保持、远处小目标的时序累积检测。这些是它的强项也是时空Transformer设计的直接收益。但它也有明显的边界。第一对极端天气的鲁棒性有限。大雨、大雾、夜间低光照下图像特征本身质量就差BEV查询再厉害也变不出信息来。第二对非规则物体的BEV表示不够好。BEV网格是规则的但真实世界里的物体形状各异把一辆斜停的车塞进规则网格里边界会有锯齿。第三计算量还是偏大。虽然比全局注意力省了很多但在车端芯片上实时跑200×200的BEV网格还是有压力。6.2 和其他BEV方案的对比取舍BEV感知这个方向有好几条技术路线BEVFormer只是其中之一。我在选型的时候会考虑几个维度方案类型代表思路优势劣势适用场景显式深度投影先估深度再投影到BEV几何可解释性强深度估计误差敏感深度传感器可用的场景可学习查询BEV查询交叉注意力鲁棒性好无需深度监督计算量大可解释性弱纯视觉方案点云投影激光雷达点云投影到BEV精度高依赖激光雷达有激光雷达的场景隐式神经表示用MLP学习BEV映射灵活训练不稳定研究探索阶段BEVFormer属于第二类。如果你的项目是纯视觉方案且对遮挡和时序融合有需求它是很合适的选择。但如果你有激光雷达点云投影到BEV可能更直接、更准。如果算力非常受限可能需要考虑更轻量的方案。6.3 实际项目中的裁剪与优化在实际项目中我很少直接照搬BEVFormer的全部设计通常会做一些裁剪。比如减少高度采样数原版在z方向采4个点我试过减到2个精度掉得不多但计算量省了不少。降低BEV网格分辨率从200×200降到150×150远处精度略降但近处几乎无感显存省了快一半。时序帧数从3减到2收益递减很明显2帧已经能覆盖大部分遮挡场景。用更轻的backboneResNet-50替代ResNet-101精度掉1-2个点但速度快很多。这些裁剪要根据你的具体场景来定。我的建议是先跑通原版建立一个精度基线然后逐项裁剪每次只动一个变量看精度掉多少再决定是否接受。不要一上来就大改否则出了问题都不知道是哪个改动导致的。6.4 后续可以探索的方向BEVFormer这个框架还有不少可以挖的地方。比如多模态融合——把激光雷达点云也通过类似的查询机制融合进BEV空间纯视觉和点云互补应该能提升鲁棒性。还有Occupancy预测——BEV是2D的但真实世界是3D的把BEV扩展到3D Occupancy能更好地表示立体物体。另外端到端的BEV感知预测规划也是趋势把下游任务也放到BEV空间里做整个链路更一致。我自己在实际操作中的体会是BEVFormer的价值不仅在于它本身的精度更在于它提供了一套统一的BEV空间抽象。一旦你有了这个统一的表示上层任务的开发就变得非常灵活——检测、分割、预测、规划都可以在这个空间里做模块之间的接口也清晰了。这套思路的影响可能比具体的网络结构更深远。