← 课程首页 | M0 理论地基 论文深度拆解
ECCV 2020 · NVIDIA · BEV 感知的另一条主干道

Lift,
Splat,
Shoot

抬起来、拍扁、射出去

你已经学过 BEVFormer:BEV 格子主动去图像里查询。这一篇走的是完全相反的方向——让每个像素自己"猜"深度,把特征推到 3D,再压成 BEV。理解了它,你才算真正看懂 BEV 感知。

深度桶 41 BEV 网格 200×200 分辨率 0.5m 特征维度 C=64
00 / 定位

同一个目的地,两条相反的路

你学 BEVFormer 时已经彻底理解了它的做法:BEV 网格上的每个格子拿着自己的 3D 坐标,用相机标定参数反投影到图像上,去采样特征。方向是 BEV → 图像,是"拉取"(pull)。

LSS 比 BEVFormer 早两年,走的是相反方向:从图像出发,让每个像素预测自己有多深,把特征"推"到 3D 空间,再拍扁成 BEV。方向是 图像 → BEV,是"推送"(push)。

LSS · 推送 push 相机图像 每个像素 预测深度 主动推出去 BEV 网格 被动接收 「我这个像素大概在 12 米远, 我把特征放到那个格子去」 BEVFormer · 拉取 pull 相机图像 被动被查 投影采样 主动来查询 BEV 网格 每格发起查询 「我这个格子在前方 12 米, 我去图像里看看那儿有什么」 同样是把 2D 图像变成 BEV 表示,但谁主动、谁被动,完全相反 核心差异:LSS 必须显式预测深度,BEVFormer 用注意力绕开了深度
图 1 · 两大流派的方向对比。这张图是本文最该记住的一张——面试被问"LSS 和 BEVFormer 有什么本质区别",答案就从这里展开。
● 为什么这两派都要学

它们不是"新旧替代"关系,而是两条并行发展、各有优劣的技术路线,今天都在量产中大量使用。LSS 这一派后来演化出 BEVDet、BEVDepth、BEVFusion;BEVFormer 这一派演化出 BEVFormer v2 等。只懂一派,等于只看了半张地图。

01 / 总览

论文标题就是完整流程

这是我见过命名最诚实的论文——三个词就是三个步骤,读完标题你已经知道它在干什么了。

6 路相机 图像 LIFT 抬起来 每个像素预测 一个「深度分布」 2D 像素 → 3D 点云 沿射线摊开 41 个点 SPLAT 拍扁 3D 点落进 BEV 柱子并求和 3D 点云 → BEV 特征图 200×200×64 SHOOT 射出去 把候选轨迹「射」进 代价图,选最优 BEV → 规划轨迹 端到端可训 前两步是感知(本文重点),第三步是规划 今天业界大量使用的是 Lift + Splat 这两步,Shoot 部分后来被更强的规划模块替代
图 2 · LSS 三步流程。名字即流程:Lift 把 2D 抬到 3D,Splat 把 3D 拍成 BEV,Shoot 在 BEV 上做规划。
02 / Lift

深度的困境,和一个"不做选择"的答案

这一节是整篇论文的灵魂。理解了这里,LSS 就通了。

先看问题有多难

一张普通照片里,某个像素对应现实中多远的位置?单张 2D 图像在数学上无法确定深度——同一个像素,可能是 5 米外的小物体,也可能是 50 米外的大物体,投影到图像上一模一样。这是几何上的根本歧义,不是算法不够好。

LSS 要把像素特征"推"到 3D 空间,就必须知道推到多远。这就撞上了这个根本困境。

直觉方案的失败

最自然的想法:让网络预测一个深度值,比如"这个像素在 12.3 米处",然后把特征放到那儿。

但这样做很脆弱:网络必须做一个硬性承诺。一旦猜错(比如实际是 20 米),特征就被放到了完全错误的位置,而且错误无法挽回——后续网络不知道这里曾经有过不确定性。

● LSS 的关键洞察

既然无法确定深度,那就不要强迫模型做选择。

让每个像素输出一个深度上的概率分布——"我有 60% 可能在 12 米,25% 在 13 米,10% 在 11 米,剩下的分散在别处"。把不确定性显式地保留下来,一路传给后续网络。

具体怎么做:离散深度桶 + 外积

LSS 把连续的深度范围切成离散的桶。在 nuScenes 配置里是从 4 米到 45 米,每米一个桶:

# 深度范围配置 dbound = [4.0, 45.0, 1.0]
depths = [4, 5, 6, ..., 44]     # 共 41 个桶(右开区间)

# 网络对每个像素输出两样东西:
c = 上下文特征                   # C = 64 维,"这里是什么"
α = 深度分布                     # D = 41 维,softmax 后和为 1,"这里有多远"

然后做一个外积——这是 Lift 这一步的核心运算:

# 外积: 41 个深度 × 64 维特征
点云特征 = α ⊗ c        # shape: [41, 64]

# 含义: 沿这条射线摆 41 个点,第 d 个点的特征是 α_d × c
# 概率高的位置,特征强;概率低的位置,特征弱(但不是零)

第 8 个点(12米处): 0.60 × c    # 主要能量在这
第 9 个点(13米处): 0.25 × c
第 7 个点(11米处): 0.10 × c
其余 38 个点:      很小 × c    # 微弱但存在
◆ 一个比方

普通深度估计像用图钉钉一个点——必须选一个位置,钉错了就全错。

LSS 像用喷雾罐沿着射线喷一道渐变——最可能的位置喷得最浓,周围渐淡,远处极淡但也有。不确定性被完整地保留在浓淡分布里,后续网络自己决定怎么用。

规模感:这一步产生了多少数据

# 单个相机(图像经骨干网络下采样 16 倍后)
特征图尺寸: 8 × 22              # 128÷16, 352÷16
每个位置摊开 41 个深度点
→ 单相机点数 = 41 × 8 × 22 = 7,216

# 6 个相机
总点数 = 7216 × 6 = 43,296 个 3D 点

# 每个点带 64 维特征 —— 这就是"隐式反投影"生成的伪点云

注意论文标题的副标题里那个词:Implicitly Unprojecting to 3D(隐式地反投影到 3D)。"隐式"就是指不做硬性深度决策,而是用概率分布软性地铺开。

◆ 这也埋下了 LSS 的最大隐患

整条路线的质量完全取决于那个深度分布 α 预测得准不准。而这个分布在原始 LSS 里没有任何直接监督——它只能通过最终的检测/分割损失间接学习。

后来 BEVDepth 那篇论文专门指出:LSS 学到的深度其实相当不准。这个问题怎么解决,第 7 节讲。

03 / 动手

点一个像素,看它的深度分布怎么摊开

上一节讲的"概率分布 + 外积"比较抽象,这个演示让你直接看到。点击图像上任意位置,下面会显示这个像素预测出的 41 个深度桶的概率分布,以及它沿射线摊开后的样子。

前视相机图像 · 点击任意位置
像素位置 —
峰值深度 —
置信度 —
该像素的深度分布(41 个桶)
4m 近24m44m 远
侧视图 · 特征沿射线摊开
点击左边图像上的车辆、路面、天空或树木,对比它们的深度分布有什么不同。
● 三种典型情况,一定要点一遍

① 点车辆——分布会很尖锐,集中在一两个桶上。物体边界清晰、有纹理,模型对深度很有把握。

② 点空白路面——分布会宽而平。没有纹理线索,模型说不准是 15 米还是 25 米,于是把概率摊开。这正是保留不确定性的价值。

③ 点天空——概率全堆在最远端。天空无限远,模型学会了把它推到深度范围的尽头。

看完这个演示,回头再看"外积"就非常具象了:右上那张柱状图的每一根柱子,乘上这个像素的 64 维特征,就变成射线上一个 3D 点。柱子高的地方点亮,柱子矮的地方点暗。41 根柱子 = 射线上 41 个点。

04 / Splat

4 万个 3D 点,怎么拍成一张 BEV 图

Lift 之后手上有 4 万多个带特征的 3D 点。Splat 要把它们塞进 200×200 的 BEV 网格。

STEP 1 · 用标定参数算出每个点的真实 3D 坐标

Lift 阶段的点是在相机坐标系的射线上("沿着这个方向 12 米处")。用相机内参和外参,换算成车身坐标系的 (x, y, z)。

这一步的数学和你学 BEVFormer 时的投影完全一样,只是方向相反:BEVFormer 是 3D→2D 找像素,这里是 2D+深度→3D 定位置。

STEP 2 · 判断每个点落进哪个 BEV 格子

BEV 网格覆盖车周围 -50m 到 +50m,格子边长 0.5m,所以是 200 × 200。用 (x, y) 除以格子大小取整,就知道落在第几行第几列。z 方向不分层——同一个 (x,y) 上不同高度的点,全归到同一个"柱子"(pillar)里。

STEP 3 · 同一格子内的所有点特征求和

一个格子里可能落进几十个点(来自不同相机、不同深度桶)。把它们的 64 维特征直接相加,得到这个格子的最终特征。

为什么用求和而不是取最大或平均?因为求和天然满足排列不变性——点的顺序不影响结果,相机的顺序也不影响结果。这个性质后面会讲为什么重要。

STEP 4 · 过一个 BEV 上的卷积网络

得到 200 × 200 × 64 的 BEV 特征图后,用一个普通的 CNN 进一步提特征,最后接检测头或分割头。

一个漂亮的工程技巧:累积求和

STEP 3 的"把落在同一格的点求和"听起来简单,但在 GPU 上高效实现并不容易——每个格子里的点数不一样多,没法直接开一个固定大小的张量。

笨办法是补零到最大点数,但那样浪费大量显存。LSS 用了一个很巧的做法:

# 累积求和技巧 (cumsum trick)
1. 把所有点按「所属格子编号」排序
   → 同一格子的点自然排在一起

2. 对整个序列做累积求和 cumsum
   → 得到前缀和

3. 在每个格子的边界处相减
   → 差值就是该格子内所有点的和

# 全程不需要补零、不需要知道每格有多少点
# 论文里把这一步写成了自定义的可导算子
◆ 为什么这个细节值得你注意

这是典型的部署工程思维——算法上"求和"一句话的事,落到 GPU 上却是性能瓶颈,需要专门设计数据结构和算子。

你 Project 3 第 3 章要手写 TensorRT plugin、第 5 章要写 CUDA 体素化 kernel,遇到的正是同一类问题。LSS 这个 cumsum trick 是一个很好的范本:先想清楚数学上等价的变形,再让它对硬件友好。面试聊部署优化时,这是个能加分的例子。

一个被低估的优点:对相机配置极其鲁棒

因为最后是无序求和,LSS 天然具备几个好性质:

论文标题里那句 "from Arbitrary Camera Rigs"(来自任意相机组合)说的就是这个。对车企来说这个性质非常实用——不同车型摄像头布局不同,一套模型能通用,省下巨大的适配成本。

05 / Shoot

在 BEV 上"射"出一条路

前两步是感知,这一步是规划。做法直白得有点复古,但思路很值得理解。

LSS 不去回归一条连续轨迹,而是:

◆ 一个比方

像在地图上铺一叠透明胶片,每张画着一条路线。把它们逐一盖到"危险热力图"上,看哪条路线穿过的红色区域最少——选那条走。

这个设计的好处是整个流程端到端可导:规划的好坏可以反向传播回感知,逼着 BEV 特征学出"对规划有用"的表示,而不只是"检测得准"的表示。

✓ 这个思想后来长成了 UniAD

"让感知为规划服务、端到端联合优化"——这正是你学过的 UniAD 的核心哲学(Planning-oriented)。LSS 在 2020 年就埋下了这颗种子,只是当时的规划模块还很简单(模板打分)。

今天 Shoot 这一步基本被更强的方案取代了(UniAD 的 Planner、VAD 的矢量化规划、端到端 VLA 直接出动作),但 Lift + Splat 这两步仍在大量量产系统里跑着。

06 / 两派对决

LSS vs BEVFormer:面试必考的那道题

现在你两派都懂了,把它们放在一起对比。这一节是本文对你求职最有价值的部分。

维度LSS 派(推送)BEVFormer 派(拉取)
投影方向图像 → BEV(forward)BEV → 图像(backward)
谁主动像素主动把特征推出去BEV 格子主动去图像查询
深度处理显式预测深度分布不预测深度,靠注意力隐式解决
核心运算外积 + 体素池化(求和)可变形注意力(加权采样)
时序融合原版没有,后续版本加原生支持(时序自注意力)
最大优势结构简单、CUDA 友好、对相机配置鲁棒无需深度监督、时序天然、精度上限高
最大软肋深度预测不准就全盘皆输注意力计算重、部署优化难
部署难度相对友好(算子固定)较难(deformable attention 要写插件)
后续代表作BEVDet、BEVDepth、BEVFusionBEVFormer v2、DETR3D 系
● 如果面试官只给你一句话的时间

"LSS 是从图像往 BEV 推,必须显式预测深度分布;BEVFormer 是从 BEV 往图像拉,用注意力绕开了深度估计。前者简单高效但受制于深度精度,后者精度上限高但部署更重。"

能补一句就更好:"所以 LSS 一派后来的主要工作方向是怎么把深度学准(BEVDepth 加激光雷达深度监督),BEVFormer 一派的主要方向是怎么让注意力更高效。"

一个常见误解要澄清

很多人以为"BEVFormer 更新所以更好"。不是这样。两派各有战场:

工程选型看约束,不看论文发表年份。这个判断力本身就是资深工程师和新手的区别。

07 / 演化

LSS 之后:补上深度这块短板

LSS 开了路,但它的深度分布没有直接监督,学得并不好。后续工作基本都围绕这一点展开。

工作解决了什么关键改动
BEVDet把 LSS 做成一个完整的 3D 检测框架工程化:更强骨干 + BEV 数据增强 + 检测头
BEVDepth直接指出 LSS 的深度学得很差用激光雷达点云投影到图像,给深度分布加显式监督
BEVStereo单目深度歧义无法根除引入多帧之间的时序立体匹配提升深度
BEVFusion纯视觉深度终究不如直接测相机 BEV 与激光雷达 BEV 在统一空间融合
◆ BEVDepth 的那个发现值得记住

BEVDepth 团队做了个很犀利的实验:把 LSS 学到的深度换成随机值,检测性能居然没掉太多——这说明原始 LSS 里那个深度分布并没有学到真正准确的几何,模型是靠别的方式勉强工作的。

加上激光雷达深度监督之后,性能大幅提升。这个故事告诉我们:一个模块"存在"不等于它"有效",要有实验去证伪。这种批判性思维是读论文时最该学的东西。

✓ 直接关系到你 Project 3 第 6 章

你 Project 3 大纲里 Ch6 是"Camera + LiDAR 多传感器融合"。BEVFusion 就是这一章的学术原型——相机走 LSS 路线变成 BEV 特征,激光雷达走体素化变成 BEV 特征,两者在同一个 BEV 空间拼接。

你之前在 BEVFormer 讲解里学过的"BEV 是传感器无关的统一表示",在这里得到了最直接的印证。

08 / 收束

三篇读完,你的 BEV 地图完整了

把这三篇串起来看,脉络就非常清楚了:

Transformer(2017)· 通用序列机器

建立了 Q/K/V 查询-匹配-聚合的范式。一切的地基。

ViT(2020)· 图像也能变 token

证明了只要能 token 化,Transformer 通吃所有模态。打通了视觉这一侧。

LSS(2020)· 几何路线的 BEV

不用 Transformer,用显式深度分布 + 体素池化把多相机融进 BEV。证明了 BEV 这个表示本身的价值。

BEVFormer(2022)· 注意力路线的 BEV

把前三者合流:用 Transformer 的交叉注意力替代显式深度,用时序自注意力加入历史。你最早学的那一篇,其实是这条线的汇合点。

● 一句话总结这四篇的关系

LSS 回答了"BEV 该长什么样",Transformer + ViT 提供了"更强的工具",BEVFormer 是"用新工具重做 BEV"。

读完的自测

自测一 · 说清 Lift 为什么要用深度分布而不是单个深度值

关键词:单目深度存在根本歧义、硬性承诺不可挽回、保留不确定性交给后续网络。

自测二 · 手推 Lift 的形状变化

特征 c[64] ⊗ 深度分布 α[41] → 点云[41,64],单相机 41×8×22=7216 点。

自测三 · 讲清 LSS 与 BEVFormer 的方向差异

推送 vs 拉取、显式深度 vs 注意力隐式。这是面试最高频的 BEV 对比题。

自测四 · 解释 cumsum trick 解决了什么工程问题

变长分组求和在 GPU 上的高效实现。聊部署优化时的好素材。

下一篇按清单是 UniAD(CVPR 2023 最佳论文)——你 Project 2 学的就是它,读原文能把之前所有零散理解收拢成一个整体。或者如果你想先补占据网络那一块,也可以看 Tesla AI Day 的占据网络部分。