YAOTU INSIGHTS

占据网络重塑自动驾驶感知:从稀疏目标框到稠密体素空间

占据网络重塑自动驾驶感知:从稀疏目标框到稠密体素空间
最近这一年自动驾驶感知圈子里聊得最多的话题已经从“3D检测刷到多少mAP”变成了“你那边出occupancy了吗”。很多算法同行问我占据网络是不是换个名字的语义分割为什么各个量产项目宁可牺牲一点框精度也要把输出从目标框换成体素场。我的看法是传统三维目标检测解决的是“已知类别矩形框”的判别问题而占据网络试图回答“周围空间哪些位置被占用、被什么占用”这件事的边界和意义要大得多。这篇文章我尽量用做项目的口吻把原理、方案、训练和部署时的关键细节讲清楚也把我们在某预研项目里真实踩过的坑列出来。不管你是刚转到自动驾驶感知方向还是在做BEV感知、端到端系统都能从中找到可以直接上手的经验。1. 为什么“占据”突然成了感知圈的关键词1.1 传统3D目标检测的三块短板传统3D目标检测并不是不能用了而是它在某些关键地方存在结构性的短板。第一块短板是几何表达太粗糙。检测器输出的是中心点、长宽高、朝向角和类别最终落到场景里就是一个旋转矩形框。相机或激光雷达看到的物体轮廓千奇百怪挂满编织布的大货车、底盘悬架外露的工程机械、装载着异形货物的皮卡这些形状用矩形框去包边界误差比一个0.4米的体素还要大。规控如果想贴着物体边缘规划这种粗糙度会造成明显的浪费或者危险。第二块短板是类别封闭。检测器要在训练时定好的类别表里挑答案车、人、自行车、锥桶这些常见类别没问题但遇到没学过的类型比如倒在路中间的树干、被风吹来的塑料布、施工遗留的钢筋检测器要么报一个低置信度的“通用目标”要么干脆漏检。现实世界的开放类别根本枚举不完而自动驾驶最怕的就是未知物体没有被感知到。第三块短板是输出过于稀疏。一帧点云里真正有意义的物体可能只有几十个剩下的全是路面、墙壁、树木这些“背景”但背景恰恰也是规划要避让的对象。传统检测框架把大量空间状态排除在监督之外导致自由空间估计、路面边界估计这些关键能力只能靠额外模块去补。再加上检测框最终要转换成栅格给下游规控使用转换过程中又要经历一次投影和累积误差整个链路损耗很大。1.2 占据网络到底在输出什么占据网络的核心输出非常简单把自车周围的空间划分成一个个体素格子对每个格子判断“是否被占用”如果被占用再判断一下“这个格子属于什么语义类别”。输出不是几个离散的框而是一个密集的张量。你可以把它理解成一张有厚度的、按格子填好的空间地图。做个类比传统检测是“点名”它告诉你场景里有三辆车、两个人占据网络是“画座位图”它把整个教室每个格子都填满告诉你哪里空着、哪里坐人了、坐的是什么。前者是目标级的高层抽象后者是几何级的空间描述。对规控来说后者明显更接近“能不能开过去”这个真实问题。这也是为什么越来越多的量产预研项目愿意牺牲一部分检测框的精度也要把感知主输出切换成占据场。我们有一个内部项目原本的避障代价地图是靠检测框二次投影生成的后来换成了占据概率直接生成的栅格图窄路会车和绕障场景的稳定性提升非常明显。后面我会专门讲这一段改造细节。2. 表征革命从稀疏目标框到稠密空间场2.1 框与体素两种世界观差异检测框和体素场本质上是两种不同的世界模型。检测框是稀疏的、离散的、类别驱动的体素场是稠密的、连续的、几何驱动的。用表格对比会更直观对比维度传统3D目标检测占据网络输出形式目标框列表体素占用概率语义概率类别处理固定类别表开放语义/通用障碍物几何精度旋转框近似逐体素边界近似空区域表达不表达每个格子都有状态下游接口需二次投影成栅格天然就是栅格长尾目标容易漏检占用二分类可以兜底不要小看这个差异带来的连锁反应。检测框是“先分类再定位”的思路网络首先要确认目标属于哪一类然后才预测框如果类别这个前提错了框就跟着错了。占据网络则是“先占用再语义”的思路就算语义头判断不出来具体是什么只要占用概率高下游仍然可以把它当作障碍物来处理。这种设计天然对未知物体更鲁棒。我在项目里见过一个很典型的例子一辆工程车的机械臂横着伸出来比车身宽出一大截。检测框只会把车身包住机械臂在框外面规控如果只依赖框就会贴着机械臂规划距离算得不对。占据网络在机械臂的位置会有连续的占用响应边界自然而然地跟着形状走。2.2 开放语义与未知障碍物自动驾驶场景里有相当一部分危险障碍物是“没名字”的。它可能是一个突然掉落的纸箱可能是路边被撞歪的铁丝网也可能是施工区域一段没标识的钢管。传统检测器对没学过的东西有两种反应一个是硬分到相近类别比如把雕塑分类成行人另一个是漏检置信度太低直接丢掉。两种反应放在车上都很危险。占据网络换了一种问法这个格子到底有没有物理实体占用这个问题不需要知道具体类别也能回答。所以哪怕你只用一个二分类“占用/空闲”的占据网络也能兜住所有物理障碍物。语义头可以在这个基础上做锦上添花比如区分车辆、行人、骑车人但系统安全性不再完全依赖语义识别的正确性。这一点对纯视觉方案尤其重要。相机图像本身没有直接的三维几何信息但占据网络可以结合深度估计和BEV视角重建出稠密的空间占用分布。相比那些依赖“检测出多少类物体”的视觉方案占据网络在城市开放道路这种极端长尾场景下明显更踏实。2.3 几何边界与下游控制规控模块真正关心的是“这个位置能不能走、什么时候能走”。检测框给不了那么细的边界它给的是一个大概的长方体。体素网格则可以直接提供逐格子的占用概率下游拿到之后做几步简单操作就能用取阈值、二值化、膨胀、生成代价地图然后在这个代价地图上搜索路径。代价地图在自动驾驶里是很成熟的表达。传统路线要把检测框重新投影到栅格上这个过程涉及坐标系变换、类别到膨胀半径的映射、多帧累积去重等一堆麻烦。而且检测框的朝向误差、尺寸误差会在投影时被放大尤其在车体转向的时候检测框和真实物体之间会有明显错位。占据场因为是直接在自车坐标系下生成的同一个体素在时序上更容易对齐天然避开了这类问题。另一个容易被忽略的点是占据网络输出的“空闲空间”同样有价值。路径规划不仅要知道障碍物在哪还要知道哪些区域是可以自由通行的。检测框体系对自由空间的估计几乎为零传统方案还要额外做可行驶区域分割、路面边界检测来补全。占据网络把这些问题统一到一个表征里减轻了下游不少负担。3. 技术方案拆解从输入到占据场的完整流程3.1 体素分辨率与感知范围怎么定先想清楚你要多大的空间和多少细节。对于L2辅助驾驶前向感知范围通常到80米上下对于城市自动驾驶左右两侧也很关键所以很多方案把感知范围设成左右各40米前向80米。高度方向容易被忽略但路沿、限高杆、垂下的树枝都在高度维度里我一般取-1米到3米。体素大小是最直接的权衡点。0.5米的体素边缘误差就有半米对换道场景勉强能用0.2米的体素边界更精细但张量规模会大好几倍。以左右40米、前向80米、高度4米为例用0.4米体素网格大约是200×200×10总计40万个位置如果换成0.2米就变成400×400×20320万个位置整整八倍差距。所以第一步一定是按下游需求先定范围和体素大小而不是先定算力。规控如果要求20厘米以内的边界精度你硬上0.5米的体素后面调死也补不回来。我建议在预研阶段先用0.4米体素把闭环跑通验证整条链路再针对近距离关键区域做局部细化或者多分辨率策略。3.2 相机和激光雷达如何进到3D表示占据网络的输入可以是纯相机也可以是相机加激光雷达甚至相机加毫米波雷达。纯视觉方案里相机图像先经过一个2D骨干网络提取多尺度特征然后进入关键的“深度分布变换”阶段。早期做法是每个像素回归一个深度值误差很大现在主流做法是每个像素预测沿射线上多个离散深度区间的概率再把二维特征按深度概率“抬升”到三维空间的不同体素层里。这个过程业内通常叫深度分布变换它是纯视觉占据网络能够重建三维结构的基础。激光雷达侧相对直接点云体素化后每个体素里聚合点的坐标、强度、回波等信息然后用3D稀疏卷积或者pillar方式进一步编码。融合方式有早融合和晚融合两种选择。早融合是在体素化阶段就把图像特征补充到雷达点云上能缓解点云稀疏区域的信息缺失晚融合是图像和点云各自提特征最后再加一个注意力融合模块。我在项目里的做法是图像提供语义先验和远处的稠密信息雷达守住近处的几何精度双分支各自预测后再用一个轻量注意力模块合并。这样单模态退化时整体不会崩得太厉害但训练难度也会高一点。这里提醒一句传感器之间的外参标定精度直接影响融合效果。我们第一版融合模型在车辆周围出现重影排查到最后是环视相机外参偏差超过两厘米。特征融合之前一定要做坐标变换和遮挡校验生产环境下最好有在线外参监控机制。3.3 网络主干与解码分支主干网络不用追求特别复杂。输入是体素化后的3D特征体中间可以用3D UNet这种经典的编码解码结构也可以用稀疏3D卷积来减少计算量。考虑到自动驾驶的实时性要求网络层数不宜过深参数量也不是越大越好。一种比较稳定的组合是图像分支用中等规模的2D骨干深度分布变换后得到BEV或3D特征再经过一个小型3D稀疏卷积网络做特征重建最后上采样到目标分辨率解码。解码头通常有两个一个输出占用概率用sigmoid激活另一个输出语义类别用softmax。如果想支持动态障碍物追踪还可以加一个速度或者流头预测每个占用体素的运动向量。这个头很有用但标注成本高尤其拥挤场景下物体间的流场边界很难标注得干净不建议第一个版本就上。训练时要特别注意特征模糊问题。相机和雷达两种传感器的空间对齐如果做得不好特征在体素网格里就会互相“污染”。我们做法是特征融合后加一个空间对齐残差块让网络自己学习校正小的投影误差同时用输入侧的裁剪和遮挡校验保证大体不错。体素网格的角点坐标也要统一在同一个车体坐标系下计算避免出现半年一调的局部坐标系偏移。3.4 训练目标与损失函数设计占用二分类问题看起来简单但正负样本极度不平衡。一片场景里绝大多数格子是空的直接拿二值交叉熵训练网络很快学会“全部预测为空”因为这样loss已经很低。常规做法是用focal loss来处理难例或者叠加dice loss来缓解类别不平衡。我一般会把二分类损失权重设成1.0dice部分设成0.5左右语义头再用带类别权重的交叉熵。还有一个非常关键的概念unknown体素。激光雷达只能观测到最近障碍物之前的区域被障碍物挡住的后方状态是未知的。如果把这些位置标成free模型就会被错误地训练成“所有遮挡区域都是空的”。真值生成时必须对每个体素标记free、hit、unknown三种状态loss里只计算free和hitunknown直接忽略。这是我们项目踩过最大的坑后面我会单独展开。语义头可以等占用头训稳之后再逐步加载也可以一开始就联合训练。我的建议是先单独训占用头几个epoch确认几何能力没有明显塌陷再把语义头一起训。损失函数不要一次堆太多每加一个分支就做一次消融很多花哨的loss提升小、调参成本高并不适合工程落地。4. 数据、标注与评估体系4.1 真值从哪里来占据网络的真值最直接的来源是激光雷达点云。把落在每个体素内的点标记为hit沿着发射线没有被遮挡的位置标free遮挡后方的位置标unknown。听起来简单实际做起来问题很多。单帧激光雷达在远距离很稀疏一个大物体可能只扫到十几个点很多本应占用的体素根本没有点命中真值的召回率不够训练出来的模型就会系统性低估远处障碍物。常规解决办法是多帧聚合或者离线建图。把连续多帧点云配准到当前自车坐标系下可以显著提高真值密度。但多帧聚合又带来一个新问题动态物体。前一帧还在当前位置的车辆后一秒已经开走了直接叠加会把旧位置标成占用形成拖影。所以聚合时要先用检测跟踪结果把动态目标分离出来按各自的轨迹补偿到当前时刻再生成真值。很多开源占据方案都有类似逻辑但细节处理全靠自己打磨。另一个思路是用仿真数据。渲染引擎可以直接导出每个体素的占用真值和语义标签边界、遮挡关系都干净可控。把仿真数据和真实数据混在一起训练是不少量产项目缓解标注压力的现实选择但仿真和真实之间的域差异需要额外做适配不能无脑加大比例。4.2 评估指标别只看mIoU占据网络最常用的指标是IoU和mIoU前者算占用二分类后者算语义多分类。问题在于空体素在数量上通常占八成以上空体素的IoU本身就很高一平均就把差的部分盖过去了。所以我建议除了整体mIoU一定要看每个语义类别单独的IoU尤其是行人、骑车人、未知障碍物这些困难类别。边界指标也很重要。最近社区里常见的做法是只计算物体边界附近体素上的IoU用来衡量边缘质量。对规控来说边界才是真正影响安全距离的地方。另外要把感知范围按距离分段评测0到20米、20到50米、50米以上各出一组指标。近处的高分很容易掩盖远处的缺陷而远处恰恰是最容易出车规级问题的地方。我写评测报告时会固定一套上下文参数感知范围、体素大小、真值生成脚本、类别定义、GPU型号、推理框架、图像输入分辨率全部写清楚。没有这些上下文单独一个mIoU数字没有任何可比性。4.3 对比实验的公平性问题做对比实验的时候有一个不容易察觉的坑体素分辨率不同mIoU能差好几个点。0.4米体素训练出的模型和0.2米体素训练出的模型即便网络结构一样数字上也没有可比性。感知范围也一样范围越大难度越高同样一个模型在80米范围下的成绩会低于40米范围下的成绩但这不能说明模型变差了。所以组内对比一定要把范围、分辨率、真值流程、输入模态全部锁死。对外看论文时也要养成习惯先看实验设置再看结论。很多刷榜方案把图片分辨率提高、把推理时TTA打开、把没用的辅助分支去掉这些不算作弊但对比时得等价地还给对方。效率对比更要注意后端算子差异同样的模型在不同推理框架下帧率能差出一倍不能直接归因到算法结构。5. 我踩过的坑工程落地与调优实录5.1 显存和推理速度是第一个坎很多人看到占据网络的精度指标很兴奋一上来就按论文配置复现然后发现单卡根本跑不动。问题出在稠密3D特征上。输入如果是一个200×200×16的体素网格中间特征再带几十个通道显存占用是灾难级的。我们第一次在开发板上跑推理直接超时。工程上的第一个经验是能用稀疏卷积的地方不要用稠密3D卷积。三维空间中绝大多数体素是空的稀疏卷积只对有值的区域做计算内存和FLOPs都大幅下降。第二个经验是降低高度方向的体素层数。对乘用车场景把高度从20层压到8层精度损失往往很小但推理速度提升非常明显。我们曾经把体素从0.2米改成0.4米Z轴层数减半单卡推理从不到10帧提升到20帧上下边界IoU只掉了不到一个点。这种tradeoff在量产项目里是可以接受的后续再针对近处做局部高分辨率细化补回来。推理阶段还要记得开混合精度。显存能省不少速度也有提升大部分现代GPU对fp16的支持已经很成熟。如果模型里有某些层对精度敏感可以用分层精度把敏感层留在fp32。5.2 小目标和远距离是怎么丢的小目标在体素化之后可能只剩两三个格子语义头很难同时把占用和类别学准。远距离目标也是类似问题点云稀疏加上体素分辨率限制物体在网格里几乎要消失了。我们遇到过典型的案例一个放在50米外的小锥桶检测框也能给出结果但占据网络预测的占用区域断断续续语义头把它归成了背景。解决方向有几种。一是多帧时序融合让目标在历史帧里累积更多观测而不是只看当前一帧二是对远处采用非均匀体素布局近处小体素、远处大体素计算量增加有限但远距离表现明显改善三是在数据增强里对小目标做实例重采样让训练时这类样本的出现频率更均衡。还有一种比较取巧但有效的做法把图像分支得到的2D检测结果作为稀疏提示引导占据网络的注意力到对应位置。相当于给占据网络一个“这里大概率有东西”的先验能提升小目标占用响应的连续性也不会破坏整个网络的稠密输出结构。5.3 遮挡真值里的隐藏陷阱真值生成这里有一个隐藏得很深的问题遮挡区域必须标成unknown而不是free。我们第一版真值生成只做了free和hit二分类结果模型在自车正前方、前车后方这类被遮挡严重的区域反复出现“虚虚实实”的震荡。原因就是真值把明明看不见的地方标记成了空闲训练信号本身就是错的。修正方法并不复杂从传感器位置出发沿射线方向做一次体素遍历遇到第一个hit体素后继续向后的体素全部标记为unknown这些体素在loss里要ignore。但这里要注意雷达的扫描特性一条射线只覆盖一条很窄的路径相邻区域内没有点并不代表可见所以还要结合光束的角分辨率做膨胀把相邻未采样区域也视为unknown或者至少降低监督权重。多帧聚合时还会遇到一个拖影问题。前一帧目标的位置和当前帧对不上如果不处理聚合后的真值会在目标运动轨迹上留下一串假占用。我们用实例轨迹把动态目标的点云先补偿到当前时刻再做聚合拖影基本消失。如果你的系统还没有稳定的跟踪模块建议用最朴素的方式把聚合窗口内的动态物体按最近邻匹配先做简单补偿再逐步替换成正式轨迹。5.4 调参与训练技巧速查把工程里经常遇到的问题整理成一张速查表可以少走很多弯路问题现象常见原因对策远距离栅格全预测为空远处点云稀疏、真值召回不足多帧聚合、距离加权loss、时序融合物体边缘膨胀、连成一片边界体素缺少直接监督加边界IoU loss、局部细化分辨率遮挡物后方误检成占用真值中unknown未掩码射线投射生成unknownloss里ignore训练长时间不收敛正负样本失衡、学习率过高换focal/dice、降低初始学习率、EMA夜间、雨天精度骤降图像分支退化、点云噪声增加多模态融合、点云dropout增强、对抗域适应训练细节上我的习惯是先固定感知范围和体素大小再调网络宽度。损失权重按占用、dice、语义CE这样的顺序依次确定每加一项都做一次A/B。优化器用AdamW加cosine学习率衰减配合EMA权重平均通常能再涨零点几个点但稳定性提升明显。数据增强不要过度花哨点云随机dropout、图像亮度扰动、小幅旋转翻转已经能覆盖大部分量产场景。6. 再往前半步占据场如何连接规划控制6.1 从占据概率到代价地图规控侧拿到占据网络输出后第一步是转成代价地图。思路很直接把占用概率高于阈值的体素投影到二维BEV栅格上得到障碍物区域然后做膨胀处理。膨胀半径取决于车体宽度、安全距离和控制误差通常比车体实际尺寸再多留几十厘米。每个栅格的代价值可以按占用概率连续设置概率越高代价越大这样规划器能够权衡“贴着障碍物通过”和“绕远路”两种选择。我们在某预研项目里做过一个对比同一段窄路会车场景检测框方案给出的可通行宽度时宽时窄非常不稳定切换到占据网络生成的代价地图后规划轨迹的重复性和稳定度都好了很多。原因并不复杂检测框的边界由类别先验决定而占据网络的边界由几何观测决定后者在真实物理世界面前更诚实。如果进一步把时序信息用起来还可以做概率化的动态障碍预测。把连续几帧的占据场作为输入让网络预测下一帧、下两帧的占据状态规控就能在规划时避开未来可能被占用的空间而不只是当前碰到的障碍物。6.2 下一阶段会往哪里走占据网络之后的方向在我看来主要有三个。第一个是占据场和目标实例化结合既要稠密的几何边界又要知道每个占用块属于哪个具体目标这样路径规划和行为预测都能受益。第二个是动态占据流不再只预测当前帧静态占用而是输出未来几帧的占用变化趋势相当于把检测、跟踪、预测三件事压缩到一起。第三个是局部在线地图更新把占据场累积成高精地图级别的局部模型解决现在单帧感知“看得到但记不住”的问题。这些方向的技术细节还不算成熟但底层表征大概率还是占据场这种稠密空间描述。如果你准备从零开始做这个方向我会建议先把二分类占用、unknown掩码、边界评估这三件事做到位再谈更复杂的语义头、流头、实例头。占据网络革命真正有价值的地方不只是换了一个更时髦的网络结构而是换了一种问题定义方式——从“识别目标”转向“还原空间”。最后说一点个人体会。带项目这一年我最大的感受是检测不会消失但它在感知系统里的地位会从“主输出”变为“辅助模块”。当规控需要的是一张能算碰撞风险的空间场时框的高层抽象反而显得不够底层。如果你刚开始做这个方向别急着追最新模型先把体素化、深度分布变换、unknown掩码和评测脚本这些基本功跑通后面所有花活都建立在它们之上。踩过的坑往往比刷分技巧更有价值。