Github Repo: https://github.com/HuangJunJie2017/BEVDet/tree/dev2.0
Arxiv Paper: https://arxiv.org/abs/2211.17111
1. 解决了什么问题?
多相机 3D 目标检测是自动驾驶领域的基本任务,受到学术界和工业界的大量关注。Lift-Splat-Shoot view transformation 模块在部署时最大的问题是推理速度慢和显存占用多,虽然之前 MIT 的 BEVFusion,旷视的 BEVDepth 和 BEVStereo 通过 GPU 并行,加速了特征合并的过程,但是真正上车时还是不尽如人意,归根结底是Lift 时需要计算、存储和预处理多个超大的视锥特征,这个大问题之前的实现一直都没有做针对性的优化。为了彻底解决这个问题,在BEVDet dev2.0 中作者搞了个 BEVPoolv2。BEVDet dev2.0 着重于工程部署,很好地平衡了准确率和速度。它继承了 BEVDet 的优势,从工程优化的角度做了改进。
2. 提出了什么方法?
BEVPoolv2 对 BEVDet 的视角变换过程做了工程优化,计算和存储都得到了大幅度的释放。它省略了视锥特征的计算和预处理。对于一张输入为 640 × 1600 640\times 1600 640×1600 的图像,处理速度可以达到 0.82 m s 0.82ms 0.82ms,要比之前最快的实现快了 15.1 15.1 15.1倍。和其它实现相比较,它所需要的缓存也更小,它不再需要存储视锥特征。这使得 BEVDet 在其它平台上部署更加容易。作者在 dev 2.0 分支提供了 TensorRT 的部署。此外,作者也整合了 2022 年提出的一些有效的方法,最终 BEVDet4D-R50-Depth-CBGS 在 nuScenes 验证集上取得了 52.3 52.3 52.3 NDS,PyTorch 的推理速度是 16.4 16.4 16.4 FPS。
2.1 BEVPoolv2
LSS 的 view transformer 有一个问题就是它必须计算、存储,并要预先处理一个非常庞大的视锥特征,其形状是 ( N , D , H , W , C ) (N,D,H,W,C) (N,D,H,W,C), N , D , H , W , C N,D,H,W,C N,D,H,W,C 分别代表了 views 的个数、深度的类别、特征的高度、宽度和通道数。如上图所示,它使用形状为 ( N , D , H , W ) (N,D,H,W) (N,D,H,W)的深度得分和形状为 ( N , H , W , C ) (N,H,W,C) (N,H,W,C)的特征计算得到视锥特征。然后,根据体素索引来单独预处理该视锥特征,体素索引表示视锥点属于哪一个体素(根据相机的内外参计算得到)。预处理会过滤掉所有体素以外的视锥点,并根据体素索引对视锥点做排序。然后,同一个体素内的视锥点会通过累积求和(cumulative sum)被聚合起来。BEVFusion 里的 BEVPool 没有用 cumulative sum,而用了多线程来加速这项计算。但它仍要计算、存储和预处理视锥特征,仍然消耗计算量和存储资源。使用 VoxelPool 的 BEVDepth 和 BEVStereo 也有这样的问题。当输入分辨率变大时,计算效率会显著地退化,内存消耗变得非常高。比如,对于深度维度是 118 118 118、输入分辨率是 640 × 1760 640\times 1760 640×1760的输入,以前最快的实现的处理速度是 81 81 81 FPS,缓存占用是 2964 2964 2964 MB,无法部署在边缘设备上。
本文提出了 BEVPoolv2 通过工程优化摆脱了这些问题。如下图所示,它的思想很简单。用视锥特征的索引作为视锥特征点影子,和根据相机内外参得到的体素索引一起离线预处理,推理时根据这个视锥特征的索引去获取计算该视锥特征的图像特征和深度得分。这样避免了计算存储和在线预处理视锥特征。这样,我们避免了直接去计算、存储和预处理视锥特征。因此就节约了内存和计算资源,加快了推理速度。体素索引和视锥索引都可以提前计算好,离线地预处理。推理时,它们只是固定的参数而已。
利用脚本tools/analysis_tools/benchmark_view_transformer.py分析得到的推理速度和显存占用如下图所示。下图给出了 LSS view transformation 不同实现的速度比较,现有最快的实现是旷视的BEVStereo,低分辨率还能勉强能用,高分辨率和 D = 118 D=118 D=118时,推理速度就掉到 100 100 100 FPS以下了。当深度类别设定为 D = 59 D=59 D=59时,当输入分辨率较小( 256 × 704 256\times 704 256×704)时,BEVPoolv2 的推理速度加速到了 4863 4863 4863 FPS,它要比之前最快的实现快了 3.1 3.1 3.1倍。当输入分辨率是 640 × 1760 640\times 1760 640×1760时,速度达到 1509 1509 1509 FPS,它要比之前最快的实现快了 8.2 8.2 8.2倍。BEVPoolv2 使得 view transform 不再是一个计算瓶颈。节省的内存也很可观。
目前这个推理速度放在所有的 view tranformation 中不是一骑绝尘,也应该是第一梯队了,至少绝对不会是拖后腿那个模块,性能方面就不用多说了,目前应该没有显著优于 Lift-Splat-Shoot 的其他方案……
下图展示了 LSS view transformation 不同实现所需的内存。当深度类别设定为 D = 59 D=59 D=59时,当输入分辨率为 256 × 704 256\times 704 256×704,BEVPoolv2 所需的内存是之前最快实现所需内存的 5.7 % 5.7\% 5.7%。当输入分辨率为 640 × 1760 640\times 1760 640×1760,BEVPoolv2 所需的内存是之前最快实现所需内存的 2.0 % 2.0\% 2.0%。
2.2 TensorRT
这应该是环视多目感知,少有的支持 TensorRT 部署的 codebase。环视多目 BEV 感知转其他 Backend 最大的麻烦就是视角变幻时实现太复杂,BEVPoolv2 算是比较简单的,利用 TensorRT Plugin 和 Pytorch 共享cuda 实现,转 TensorRT 这个事情就比较简单了。推理速度确实比 Pytorch 后端要快很多。在下表中,作者列出了推理延迟。使用 TensorRT-FP16,在 NVIDIA 3090 显卡上,BEVDet-R50 对于 256 × 704 256\times 704 256×704的输入图像的处理速度可以达到 138.9 138.9 138.9 FPS。
2.3 Receptive Field
在上一版本的 BEVDet 中,原封不动地继承了 CenterPoint 的感受野。CenterPoint 的感受野以 LiDAR 坐标系的原点为中心点,但评测时是以 IMU (自车)坐标系的原点为感受野的中心。这个 gap 会造成表现变差,于是作者按照 BEVDepth 的设置,将自车坐标系的原点为感受野中心。
2.4 其它改进
BEVDepth
支持了 BEVDepth 的全部改进,包括 LiDAR 的深度监督、深度修正、camera-aware 的深度预测。
Temporal Fusion
引入了 SOLOFusion 的长期融合,改进 BEVDet4D 的时域融合方式。
Stereo Depth Estimation
到目前为止,还没有引入任何关于双目深度预测的技巧,如 BEVStereo、STS、SOLOFusion。作者认为它们为了少量的准确率提升,而推理时间增加了许多,还不如增大图像分辨率和主干网络的大小。
2.5 BEVDet4D-R50-Depth-CBGS
作者构建了BEVDet4D-R50-Depth-CBGS 网络,加入了所有上述的改进。在 NVIDIA 3090 显卡上,PyTorch 实现取得了 52.3 52.3 52.3 NDS,推理速度达到了 16.4 16.4 16.4 FPS。