需要中文版论文的朋友,可以关注公众号「于小咸」后台回复 「BEVFormer」获取
Motivation
- 要生成 BEV Feature,一种思路是从图像空间投影到 BEV 空间,比如之前提到的 BEV学习笔记之-LSS,另一种思路就是找 BEV 空间下每个像素对应的相机空间特征,BEVFormer 采用了第二种思路
- 因为传感器数据是连续的,那么前一帧的结果,对当前帧也会有帮助,特征可以在时序上做融合
Method
总体框架
BEVFormer 的整体框架图如下,按照时间顺序排布如下
- 图像特征提取:通过骨干网络提取多视角特征
- 用当前 BEV Query 与上一帧的 BEV Feature 做 self-attention,得到更新后的 BEV Feature
- 用第二步中的 BEV Feature 与多视角图片图像特征做 cross-attention,得到新的 BEV Feature
- 对第三步中得到 BEV Feature 过一个前馈神经网络,得到新的 BEV Feature
- 将上一步中的 BEV Feature 作为 query,重复六次步骤 2-4,得到最终的 BEV-Feature
- 在 BEV-Feature 结果上加多种 Head,实现不同任务
BEV Query 鸟瞰图请求
BEV Query 为 WxHxC 的张量,表示宽 W 长 H 特征长度 C 的 BEV 特征,BEV Query 与 BEV 特征没有本质区别,当前层的 Feature 会作为下一层的 Query
需要注意区分的是参考点,参考点是 WxHxN 的张量,N 表示 z 方向采样点的数量,采样点分布在雷达为原点,向上 3m、向下 5m 的空间。考虑到雷达安装在车顶,距离地面大约 2m,实际 BEV 参考点的采样范围大约是 -3m ~ 5m 的空间,这样不仅可以覆盖绝大多数的高大障碍物,还可以感知到下坡路、上坡路
下面来介绍怎么把三维采样点,变为二维的 BEV 特征
Spatial Cross-Attention 空间注意力
已有三维空间中的参考点,根据外参和内参,可以将参考点投影到图像空间中,对应一个图像特征,把该图像特征转换到 BEV 空间下,通常有三种思路
- 该图像特征与特征图中所有特征做 Attention
- 该图像特征与附近的特征做 Attention
- 只输出该 Attention
方法一资源消耗太高;方法三提取的特征不够丰富,对内参外参精度要求较高,可能会漏掉关键的信息;因此作者选择方案二,实现方案二的方式叫做 Deformable Attention
DeformAttm通过估计 offset ,跟特征图中对应的像素做 attention 其中 表示命中的相机,由于相机视野问题,通常每个BEV参考点命中的只有 1~2 个相机
将所有的特征求和,就得到了 的 BEV 特征
Temporal Self-Attention 时间注意力
时间注意力也用到了 Deformable Attention,考虑到车辆的运动速度, 时刻坐标为 的物体, 时刻也应该在 附近,因此通过在 附近做特征融合,就可以得到当前时刻的 BEV 特征。
代码
相比于 LSS,BEVFormer 的代码复杂了许多,不是一个单独的代码库,而是基于 MMLab 的框架开发,使用了许多封装好的模块与函数,比如 Deformable Attention 就是用 CUDA 实现的。本文不过多涉及 MMLab,直接梳理 BEVFormer 的框架结构。大家可以参考我 fork 出来的项目 BEVFormer-annotated,我会把注释更新到代码库里。考虑到阅读体验,文章内只贴索引,不贴大段代码。
模型训练和验证的代码在 BEVFormer/tools/ 目录下,模型生成调用了 mmdetection3d 库中的 build_model,输入是模型结构配置文件,下面直接从配置文件开始介绍
模型结构概览
BEVFormer 的模型结构定义在 projects/configs/bevformer/ 目录下,总共包含 base、small、tiny 三个版本,三个版本网络结构基本一致,差别在于每个模块的参数配置。模型主要有三个组成部分:
bevformer
├── img_backbone:使用 ResNet
├── img_neck:使用 FPN
└── pts_bbox_head:BEVFormer 的核心模块+bbox检测头
pts_bbox_head 的组织结构如下,由编码器(encoder)、解码器(decoder)、bbox 检测头 和 位置编码组成:
- 其中 encoder 用来提取 BEV 特征,总共由6层transformer,核心部分就是 时序自注意模块 TemporalSelfAttention 和 空间交叉注意力模块 SpatialCrossAttention
- decoder 用来解码 BEV 特征,使用多头注意力输出特征给 bbox 检测头使用
transformer
├── encoder
│ ├── transformerlayers x 6
│ │ ├── TemporalSelfAttention
│ │ └── SpatialCrossAttention
│ │ └── MSDeformableAttention3D
└── decoder
├── transformerlayers x 6
│ ├── MultiheadAttention
│ └── CustomMSDeformableAttention
├── bbox_coder
└── positional_encoding
数据处理流程
BEVFormer 定义在 projects/mmdet3d_plugin/bevformer/detectors/bevformer.py,继承自 mmdet 的 MVXTwoStageDetector。从 BEVFormer 的 forward() 函数入手,可以把 bevformer 的整体流程串起来,依次执行了:
- 历史特征获取(图像&BEV)
- 图像特征提取 extract_feat
- bbox检测 pts_bbox_head
看配置文件我们可以得知,pts_bbox_head 的类型是 BEVFormerHead,定义在 projects/mmdet3d_plugin/bevformer/dense_heads/bevformer_head.py ;
在 BEVFormerHead 的 forward() 函数中,主要执行了以下两步:
- 生成 bev query 和 obj query
- 调用 transformer 获取特征
transformer 类型为 PerceptionTransformer,定义在 projects/mmdet3d_plugin/bevformer/modules/transformer.py中,它的 forward() 函数执行以下步骤:
- get_bev_features 获取 bev 特征
- 初始化 bev query 和 位置编码
- 补偿车体运动,将历史 BEV feature 对齐到当前坐标系下
- 添加 CAN 信号
- 多层级特征展平与位置编码
- 调用 encoder 提取 BEV 特征
- 调用 decoder 输出特征给下游
经过这么多层的调用后,我们终于接触到了 BEVFormer 的核心 encoder 部分,encoder 类型是 BEVFormerEncoder,跟它调用的 BEVFormerLayer ,都定义在 projects/mmdet3d_plugin/bevformer/modules/encoder.py文件中。
BEVFormerEncoder 的 forward 主要执行了采样操作,生成了 ref_3d 和 ref_2d,分别用于 空间交叉注意力 和 时序BEV自注意力
BEVFormerLayer 继承自 MyCustomBaseTransformerLayer,定义在 projects/mmdet3d_plugin/bevformer/modules/custom_base_transformer_layer.py 中,MyCustomBaseTransformerLayer 主要作用是把 时序注意力 和 空间注意力的一部分通用能力抽象合并到一起
BEVFormerLayer 通过一个 for 循环,遍历所有 attention 模块
- 空间交叉注意力 的类型是 SpatialCrossAttention,定义在
projects/mmdet3d_plugin/bevformer/modules/spatial_cross_attention.py - 时序BEV自注意力 的类型是 TemporalSelfAttention,定义在
projects/mmdet3d_plugin/bevformer/modules/temporal_self_attention.py