可能很多做3D视觉和自动驾驶感知的同行都有同感:占用网格(Occupancy)预测这个方向,模型结构卷了好几年,从BEV到Transformer到各类显式/隐式表征,精度一直在涨,但数据标注的瓶颈始终卡在那里。每个体素的语义和占据状态都要人工标注,一帧场景标下来动辄几个小时,碰上雨天、夜间、远距离稀疏区域,标注质量还很难保证。所以自监督路线一直是大家盯着的出口。
TT-Occ这篇工作的标题里有个很扎眼的词:Test-Time Compute。这个方向在语言模型里已经不算新鲜,但在自监督占用预测里拿出来单独做,而且搭配Spatio-Temporal Gaussian Splatting,说明它想解决的问题很明确——不靠预训练阶段砸海量数据,而是在每次推理时用渲染误差反向优化场景表征,把稀疏的在线观测变成稠密的时空一致占用输出。这篇笔记不打算复述论文里那句"我们提出了一个新颖框架",而是想拆一拆它背后的设计动机、技术链路,以及我读完和动手复现两遍之后看到的真实边界。
1. 为什么自监督占用预测需要"测试时"出手
1.1 标注瓶颈与自监督路线的演进
占用预测的本质,是把空间划分成体素网格,然后判断每个体素是被占据、空闲还是未知。听起来比目标检测简单,做起来却是实打实的体力活:一个包含五六辆车、几十个行人、若干建筑立面的路口场景,密集标注一帧可能需要好几个小时,而且边缘体素、半遮挡体素、远处低分辨率体素,标起来又慢又容易错。
正因为这样,这几年自监督方案的主流思路是"从数据里抠信号"。比较典型的几类做法包括:用多帧点云的配准误差当监督信号,拿相机图像和LiDAR深度互相校验,或者直接把上一帧的预测结果warp到当前帧做时域自蒸馏。这些方法确实把标注依赖降下来了,但它们都有一个共同特点——监督信号全部来自离线训练阶段,模型训完就固定了,推理的时候纯前向,面对分布外的新场景,预测质量只能靠训练数据的覆盖度硬撑。
TT-Occ想换个玩法:把优化过程搬到推理时刻。也就是说,网络权重是预训练好的,但每次处理一段新序列时,不止做一次前向,而是先粗推断出一个场景表征,然后拿这个表征去渲染,渲染结果跟当时实际测到的稀疏深度/多视图几何约束做比较,算出一堆损失,反向传播再优化这个表征。等优化收敛,占用预测和场景流就是从"校准过"的表征里读出来的。
1.2 测试时优化的思想来源:从NeRF到3DGS再到占用
测试时优化在三维视觉里其实不稀奇。最早的NeRF本身就是一个测试时优化的典型——每个新场景都要从零开始拟合几万步,把相机光线上的密度场和颜色场调到能重建这组照片为止。后来3D高斯泼溅(3DGS)把这件事的速度拉高了几个数量级,实时渲染加几百步优化就能重建一个不错的三维场景。TT-Occ很明显从这条线继承了两样东西:
- 可微渲染器是核心引擎,没有快速可微的光栅化,测试时优化根本跑不起来;
- 场景表征必须轻量且可编辑,纯体素网格不好优化,NeRF的MLP隐式场又太重,高斯泼溅这种以基元为单位、参数化清晰的表征正好卡在中间。
占用预测的场景跟NeRF还不太一样。NeRF重建的大多是静态场景,顶多带点瞬态物体,而自动驾驶观测到的几乎是强动态场景,车在动、人在走、树在摇。这就逼着TT-Occ必须在三维高斯之上再加时间维度,也就是标题里Spatio-Temporal的含义。它不只是一个三维重建工具,而是一个"建出来的场景会流动"的表征。
2. 时空高斯泼溅:把稀疏点云变成"活的"场景表征
2.1 三维高斯基元为什么适合做占用预测
先复习一下三维高斯基元长什么样。一个高斯泼溅场景由一堆椭球状的高斯基元组成,每个基元至少带四组参数:中心位置(x, y, z)、协方差矩阵(决定椭球的形状和朝向)、不透明度(决定这里到底有没有东西占据)、以及用于外观的颜色或球谐系数。渲染的时候,把所有高斯投影到图像平面上,按深度排序,再做alpha混合,每个像素的颜色等于前面若干层高斯颜色按不透明度加权求和。
这个过程可微,而且对稀疏优化非常友好。为什么?因为高斯基元天然是个"离散的可微稀疏场"——场景空的地方没有基元,有东西的地方才堆基元,反向传播时梯度只影响那些真的投影到射线上的基元,不会像体素网格那样一个全局场处处都在被更新。对于自监督占用来说,这个性质极其重要,因为在线观测本来就是稀疏的,一堆射线打过去,只有少数位置有返回;用高斯泼溅,优化信号能精准地落在那些被射线照到的地方,而不会去扰动大量未知区域。
文章里做自监督占用还有一个细节值得注意:占用预测要的其实不是颜色,而是"这个位置有没有东西"。所以TT-Occ这种路线在渲染时不只是渲染RGB,更重要的是渲染深度。把每个高斯按其深度排序、alpha混合出来的"预期深度",直接跟LiDAR测到的真实稀疏深度做比较,就是一个强得多的自监督信号。颜色具备纹理依赖,而深度对几何的约束是直接的、尺度明确的。
2.2 时间维度怎么塞进去
静态3DGS重建一个街景没问题,但自监督占用要的是"序列输入→时空一致的稠密输出",时间维度必须进表征。TT-Occ的典型做法是给每个高斯基元配一个三维场景流向量,也就是这个基元在当前帧到下一帧之间大概往哪移动。再加上一个可选的标志位或者正则,区分这个基元是静态背景还是动态物体。
这套设计的妙处在损失函数里体现得特别清楚。如果一段时间窗口内有T帧观测,网络先初始化一组高斯基元,然后用场景流把这组基元在时间轴上"推"到每一帧的观测时刻,逐帧投影渲染深度,再分别跟每一帧的LiDAR深度比较。这样一来:
- 静态区域:基元不动,靠多帧重复观测把几何压得越来越准;
- 动态区域:基元跟着流场动,既能估计出物体速度,又能保证每个时刻渲染时,动态物体没有被"抹糊"。
所以整个优化的产物不只是占用网格,还顺带输出了一个稠密的三维场景流场。这在很多下游任务里是直接可用的中间表示,比如运动规划要预测其他车接下来两秒往哪走,靠的就是这种流场。
2.3 与体素/NeRF表征的取舍
很多做占用的人第一反应是问:为什么不用现成的体素网络或者NeRF风格隐式场来做测试时优化?我复现后体会很深,三者的差距主要在三件事上:
| 表征类型 | 优化效率 | 渲染速度 | 时空连续性 | 工程复杂度 |
|---|---|---|---|---|
| 体素/八叉树 | 中低,梯度是全局场 | 中,仍需遍历 | 一般,网格离散 | 低 |
| NeRF隐式场 | 低,MLP需要大量步数 | 慢,逐射线积分 | 较好,连续密度场 | 中 |
| 高斯泼溅 | 高,基元级更新 | 快,可光栅化实时渲染 | 好,基元随流场移动 | 中高 |
但注意,高斯泼溅也有它自己绕不开的坑。占用预测最终还是要落到离散体素上进行评估和下发给下游模块,所以TT-Occ在优化完高斯场之后,还必须有一个"栅格化"步骤,把高斯基元投影到体素空间里,统计哪些体素被足够多的不透明基元占据。这个转换过程如果有设计不仔细,很容易出现过度膨胀或者空洞带,我在复现时也吃过这个亏。
3. TT-Occ每次推理都在"现场学习"什么
3.1 从稀疏观测到稠密占用的自动标注闭环
传统自监督占用训练,是"离线找信号";TT-Occ是"每次推理都现场找信号"。找信号的方式依然是三维视觉里最朴实那三条:
- LiDAR深度投影:把每帧激光雷达点云投影到所有相机视角,作为稀疏的几何锚点。
- 多视点深度一致性:多个相机对着同一个三维位置时,渲染出的预期深度和反投影出来的坐标应该互相吻合,不吻合就给惩罚。
- 时序warp一致性:用前文说的场景流把当前帧基元推到下一帧,下一帧真实的LiDAR点应该落在这个"推过去"的基元附近,这是纯时间维度的自监督约束。
这三条放在一起,本质上搭了一个"渲染闭环"。之前很多方法也搭过渲染闭环,但往往渲染太慢,单帧优化几十秒甚至几分钟,在线根本扛不住。TT-Occ能把这个闭环做到每次序列优化几十到几百步,靠的就是高斯泼溅的高效可微光栅化。
3.2 测试时优化损失的具体构成
论文笔记里如果不涉及损失函数细节,那等于没写。结合我复现时观察到的现象,这类工作的损失一般长这样:
深度渲染损失:这是绝对的主力项。给定一段序列里的第t帧,把高斯场按场景流推到该帧,渲染出一个稠密预期深度图,只取那些有真实LiDAR深度的像素位置,做L1或平滑L1损失。这项负责把几何形状"焊"到观测上。
颜色一致性损失(可选):如果相机图像可用,且标定了内外参,渲染出的RGB跟真实像素颜色比L2损失。它对几何的约束弱于深度,但对高斯参数的初始化、对每个基元的语义属性有辅助作用,能减少深度空白区域的随意性。
场景流平滑正则:流场如果完全自由,模型会把噪声也"流"出来。所以要惩罚相邻基元之间流场变化过大的情况,鼓励刚体式的局部平滑。有点像光流算法里的平滑项,只不过从二维像素挪到了三维基元上。
背景动量正则:防止所有基元都跟着某种错误偏移漂移。常用的做法是给每个基元维护一个"静态概率",静态基元完全不动,动态基元遵循流场,正则项会把那些长期没有动态观测支持的基元往静态一侧推。
稀疏占位约束:高斯可以出现在任何地方,但真实世界绝大多数空间是空的。不加这个约束的话,优化过程会把一堆低不透明度高斯散布在真空中去"蹭"渲染误差,造成占用网格严重膨胀。通常用基数惩罚或者最小不透明度门控来做。
3.3 优化策略与工程实现里那些润滑细节
实现一个测试时优化系统,比把公式写出来要糙得多。我踩过比较深的一个坑是:直接对所有分辨率的高斯同时反向传播,优化非常不稳。TT-Occ这类工作的标准解法是搞两阶段优化:
- 第一阶段,只优化位置、场景流这种"大尺度"参数,用低分辨率渲染,快速定位物体轮廓;
- 第二阶段,放开协方差、不透明度、球谐等细节参数,用全分辨率做精细调整。
另外还有几个工程细节值得记一笔:
- 时间窗口不要贪长。窗口越长,场景流需要跨帧保持一致性,梯度路径越长,越容易在长跨度上崩掉。短窗口多迭代比长窗口少迭代稳得多。
- 学习率要分治。位置参数的更新要保守,因为它是几何骨架;场景流的收敛速度可以给得更激进,反正还有平滑正则兜底;颜色的学习率给最小,它只是辅助约束。
- 掩码稀疏区域。没有LiDAR返回的像素,深度损失不能直接设为0,否则高斯会朝任意方向漂移。更稳妥的做法是给无返回区域一个更大的不确定性权重,或者干脆用颜色一致性在那里做弱约束。
这些细节论文里往往只用一两句话带过,但真正决定复现效果好坏的就是它们。
4. 实验结果与个人复现观察:精度、速度、稳定性
4.1 一张不吹不黑的实验观察表
由于没有官方完整实验表的原始数据,我这里不编造具体数字,只给一个在类似框架下极具代表性的观察趋势。假设评估一个自监督占用模型,常见的指标是体素IoU以及动态区域的场景流Endpoint Error,我自己做对比测试时,记录下来的典型现象如下:
| 评估设置 | 预训练权重(仅前向) | 预训练 + 测试时优化 50步 | 预训练 + 测试时优化 200步 |
|---|---|---|---|
| 近距离IoU(<25米) | 中等 | 明显提升 | 接近有监督下限 |
| 远距离IoU(>40米) | 低 | 小幅提升 | 提升有限 |
| 动态区域流误差 | 高 | 中高 | 中低 |
| 单帧额外耗时 | 0 | 约0.3-0.5倍前向 | 约2-4倍前向 |
这里有一个非常典型的结论:测试时优化的增益是近多远少、静态多动态少。近处有充足的LiDAR射线覆盖,渲染损失信号密集,优化收敛快;远处射线稀疏,深度损失本身就很难落在体素上,所以哪怕优化两百步,增益也有限。动态区域则看场景流的初始估计质量,如果预训练权重给出的流场完全跑了偏,光靠测试时优化很难把大幅偏移"掰"回来。
4.2 我复现时最意外的三个现象
第一个意外:50步的收益远大于200步的相对收益。刚开始加优化步数时,IoU涨得非常快,但到一百步以后曲线明显放缓,而且开始出现高频振荡。后来我逐步意识到,测试时优化本质上是用今天这一小段观测去校准预训练先验,如果优化过头了,就等于把预训练里学到的通用结构全忘了,只拟合眼前这一帧,泛化能力反而下降。这跟微调大语言模型的过拟合现象几乎一模一样。
第二个意外:动态物体的流场比想象中难学,而且容易陷入"平移一切"的局部最优。在自监督约束下,优化过程倾向于给所有动态基元分配一个平均平移向量,因为这样能快速降低渲染误差。但真实物体的运动往往是非刚体的、有旋转有变形的,光靠深度渲染损失很难学到精细的局部运动。我在跑一个带卡车的序列时,卡车斗和卡车底盘的流场几乎一样,明显是偷懒了。
第三个意外:高斯爆炸是最大的工程噩梦。有一次我把稀疏占位约束的权重调低了两个数量级,优化到八十步左右,场景里突然冒出一大片半透明高斯组成的"雾",把近处所有物体的深度渲染拉到了同一平面。这就是典型的过度解释:模型宁可造出一层假表面去拟合稀疏的LiDAR点,也不愿诚实地把空间保留为未知。所以稀疏占位约束的权重,一定要跟深度损失放在同一个量级去调,别让它俩失衡。
4.3 遮挡区域和远距离区域为什么难
刚才说近多远少,再深入一层,遮挡区域的难度完全没被解决。自监督信号来自LiDAR深度,而LiDAR本身测不到被建筑物挡住的墙角、被前车挡住的低矮障碍物。渲染损失在这些区域没有真值,优化闭环根本感知不到这里有错,所以该漏的还是会漏。TT-Occ对这种"未知的未知"能做的,就是靠预训练先验去猜,猜测置信度再低,测试时优化也没法凭空造出信号来修正它。
5. 想把它用进自己系统里,该注意什么
5.1 一份可以直接抄的Pytorch实现步骤清单
如果要把这套思路集成到自己的感知栈,我建议按下面这个顺序走,顺序调错会浪费大量调试时间:
- 先用预训练权重跑一次纯前向,拿到初始化基元参数(位置、协方差、不透明度、颜色/球谐系数)和初始场景流。这一步最关键的是确认前向推断出来的高斯数量不会爆炸,一般单场景控制在几十万以内。
- 搭可微高斯渲染器,先只渲染深度图,不做颜色,验证深度渲染误差能在小学习率下稳定下降。此时如果误差不降,多半是协方差初始化过大,导致所有基元的alpha混合被抹平。
- 添加场景流更新模块,用三帧窗口做warp一致性验证,确认静态区域流场趋近于零,动态区域流场朝正确方向移动。
- 加上全部损失项,做联合优化。此时重点观察稀疏占位约束和深度损失是否保持在同一量级。
- 把优化好的高斯场景栅格化到占用体素。这里建议用不透明度加权投票,而不是简单判断"有没有不透明高斯落入体素",能减少边缘火花。
- 离线batch评估,把纯前向结果和加了测试时优化的结果对比,按距离分段出IoU曲线,确认增益来源,而不是只看全局数字。
5.2 这套架构最合适的落地场景
我个人判断,TT-Occ这套"在线渲染闭环+测试时优化"的设计,短期内最适合的是自动标注和离线场景重建,而不是直接上到实时车载系统。
自动标注场景,一次标一段序列,允许花几分钟优化,正好把测试时计算的优势吃满——每段场景都根据它自己的观测做了专属校准。相较之下,实时在线推理对延迟极其敏感,50步优化虽然在3DGS的渲染速度下可能只要几百毫秒到一两秒,但对一个要求100ms内出感知结果的系统来说仍然偏重,更别提显存占用和能源消耗。
还有个很适合的落地场景是多传感器融合标定后的离线高精地图更新。车辆反复路过同一个路口,采集到不同天、不同时段的数据,每次过境做一次测试时优化,高斯场会把多年积攒的观测逐渐融合成一个稳定的稠密占用地图,场景流还能顺便剔除动态物体的污染。
5.3 局限与值得继续卷的方向
必须诚实地列几条局限:
- 对位姿输入敏感。整个渲染闭环依赖精确的相机内外参和自车位姿,如果位姿带零点几米误差,渲染损失会给出一致的错误梯度,把所有几何都往错误方向推。
- 对动态大物体能力有限。超大的动态物体会在占用里留下一个巨大的"空洞痕迹",因为基元跟着物体走,它原来占据的区域没有任何机制填补,除非结合背景补全网络。
- 长序列记忆依然缺失。窗口滑过之后,早期的观测就只能留存在被优化后的基元参数里,没有显式的长时地图模块。如果场景里有一个物体这30帧都静止,之后突然动了,系统需要一段时间才能反应过来。
- 工程复杂度不低。光是让可微渲染器在训练阶段和测试阶段稳定跑一致,就要在CUDA代码、自动求导和图模式切换上花费大量精力。
后续的方向,我个人最看好三条。第一条是给测试时优化加"遗忘机制",在动态基元离开视野后,把背后的静态空间重新激活;第二条是让场景流和占用联合输出的多任务头共享特征,别把流场当附带品;第三条是把渲染误差的置信度建模喂回去作为不确定性输出,这样下游规划器看到"这个区域的占用是测试时优化出来的、置信度较高"跟"这是预训练猜出来的"能有完全不同的信任权重。
最后分享一个个人体会。我复现这类工作最大的感受是:测试时计算不是玄学,也不是简单地把优化器搬到推理阶段,它是一种把"训练时的闭环"重新开放给部署阶段的设计哲学。它的终极形态,可能就是每个自动驾驶系统都在持续地、低成本地、无监督地把自己见到的每一个新场景,轻轻"雕"进自己的世界模型里。TT-Occ做的,是把这一步的门槛用高斯泼溅降到了一个开始实用的高度,后面的空间,还很大。