
最近在做深度补全相关的实验收到好几个私信都在问同一件事稀疏的LiDAR点云怎么和单目相机配合在不用稠密深度真值的情况下训练出一个能输出稠密深度图的网络。问的人多了我干脆把这块的思路、结构和踩过的坑一次性整理出来。“Self-Supervised Sparse-to-Dense”这个方向的本质是让网络从“稀疏的LiDAR点 单目RGB图像”出发自己学会预测出稠密的深度图。这里的“自监督”是关键——它意味着我们不依赖昂贵的高精度稠密深度传感器作为真值而是利用其他一致性约束去训练网络。这个思路对自动驾驶感知、机器人导航、三维重建都有直接的落地价值尤其适合那些想用低成本传感器方案做感知系统的团队和个人开发者。如果你是做视觉感知算法的同学或者正在研究深度估计、点云处理的工程师这篇内容应该能帮你少走不少弯路。我会把这套自监督深度补全方案的思路拆开讲包括网络结构怎么搭、不同损失函数为什么有效、实验中让人头疼的细节问题以及一些可以抄作业的训练技巧。1. 内容整体设计与思路拆解1.1 深度补全任务的痛点先说说深度补全到底是什么问题。假设你车顶上装了一颗64线LiDAR它每帧能给你大约几十万个点。这听起来很多但转成二维深度图之后大概只有不到5%的像素位置有深度值其余全是洞。相比之下图像传感器的像素动辄上百万每个像素都需要一个深度值。把稀疏的几万个有效点扩散到整个画面这个任务就叫“深度补全”或者“深度稠密化”。有人会问那直接用单目深度估计网络不行吗当然可以但是单目网络的深度预测存在尺度模糊的问题——它可以从图像结构推断出“远和近”却很难告诉你从相机到那个物体到底是1米还是10米。LiDAR虽然稀疏但给出的深度是物理测量值绝对尺度是准的。深度补全的聪明之处在于把图像的密度和LiDAR的精度结合起来用网络在两者之间搭一座桥。如果想用“有监督”的方式训练深度补全网络那你需要什么呢你需要每一个像素都对应的真实稠密深度。在自动驾驶场景里想要拿到全覆盖的高精度稠密深度只能依赖高线束LiDAR搭配多次扫描拼接或者用价格昂贵的激光扫描仪操作成本极高。在室内小场景里深度相机可以输出稠密深度但它的量程短、容易受光照干扰而且视场角有限很难直接用到室外大尺度场景。这就是自监督方案的价值所在——如果我们能找到一种不需要稠密真值、也能让网络学到稠密深度映射的方法整个训练流程的数据成本就会大幅降低。1.2 自监督的两条技术路线对比在自监督深度补全这个大方向上主流的技术路线其实可以分成两派我把它们放在一起对比方便理解对比维度位姿一致性路线知识蒸馏路线监督来源相邻帧图像的重投影误差单目深度网络的预测结果核心组件位姿估计网络 深度估计网络单目预训练网络 深度补全网络优势不需要任何深度真值纯图像自监督收敛快深度尺度准确稳定性高劣势对动态物体和遮挡敏感训练不稳定依赖单目网络的初始预测质量适用场景海量未标注视频数据预训练有一批稀疏LiDAR标注数据的基础场景标题里的“Self-Supervised Sparse-to-Dense”更偏向知识蒸馏路线。基本逻辑是这样的先用一个在大量图像上预训练好的单目深度估计网络让它对当前帧图像输出一个“看起来不错但不太准”的稠密深度图。这个预测结果虽然绝对尺度可能不对但结构信息丰富可以作为“教师”。真正要训练的“学生”网络输入是RGB图像加稀疏LiDAR深度输出的则是稠密深度。在训练时有LiDAR点的位置用LiDAR的真实值做硬约束没有LiDAR点的位置用单目网络预测值做软引导。这样既保留了绝对尺度的准确性又利用了图像结构的稠密信息。我第一次看到这个思路的时候就觉得这本质上是把“测量数据”和“经验先验”做了一次漂亮的信息融合。LiDAR是测量是物理事实单目网络的预测是经验判断是结构联想。自监督训练就是让网络学会在不同区域信任不同的信息来源。1.3 为什么选择“稀疏监督 稠密伪监督”的组合如果只看有LiDAR点的那几个位置训练网络完成补全当然是不可能的所以必须引入某种稠密监督。但问题在于这种稠密监督从哪来第一个选择是多帧融合。连续几帧LiDAR数据通过位姿变换投影到当前帧可以形成比单帧密得多的点云。这个方法有效但需要一个好的位姿估计器而且动态物体依然会留下“拖影”效应。第二个选择就是刚才说的单目网络预测。这个方法有一个特别实用的好处——单目深度网络可以预先训练好训练深度补全网络时不需要再更新它的参数推理时甚至可以在线用。整体训练开销比端到端多任务联合训练小很多。第三个选择是两者结合。LiDAR点上的真实深度负责“锚定”绝对尺度单目预测负责“填充”无点区域的形态。我自己的实验经验是这两种监督的权重不需要调得很精细但引入一个置信度掩码很关键——对于单目预测不可靠的区域例如极度模糊、反光或者过曝区域教师预测的置信度应该由学生网络自己学会评估。这一点后面在损失函数部分还会细说。2. 核心细节解析与实操要点2.1 网络结构设计输入编码与主干网络选择整个模型属于典型的“编码器-解码器”结构通常以U-Net为骨架。为什么选U-Net而不是简单的全卷积网络因为深度补全本质上是一个“输入到输出的像素级映射”问题需要保留空间分辨率。U-Net的跳跃连接能让解码器直接拿到编码器在不同尺度上的特征图边缘细节保留得更好。输入部分有两个RGB图像和稀疏LiDAR深度图。这里有个容易踩坑的细节——不要简单地把两张图直接叠起来作为输入。更合理的做法是额外拼接两个信息通道一个是稀疏深度掩码也就是标记“哪些位置真实存在LiDAR点”的二值图另一个是LiDAR深度值本身。为什么掩码很重要因为网络需要知道哪些深度值是真实测量、哪些是空白占位。如果不给掩码网络很难区分“这里确实是0.5米”和“这里没有数据”。实现上将稀疏LiDAR深度投影到图像平面后会得到一个和图像同分辨率的稀疏深度图。有效点的深度值直接填入无效点可以用0填充或者用一个很大的常数填充然后单独生成一个1/0掩码。我把三样东西——RGB图、稀疏深度图、深度掩码——沿着通道维拼起来得到C5的输入张量。这个简单的预处理对最终精度的影响非常显著。主干网络我推荐用带空洞卷积的ResNet变体作为编码器。空洞卷积可以在不降低分辨率的情况下扩大感受野对深度补全这种需要“看全局趋势”的任务很友好。解码器使用双线性上采样配合跳连不需要用反卷积——反卷积容易产生棋盘格伪影在处理深度这种连续数值输出时尤其明显。2.2 监督信号的设计意图为什么要用多尺度深度监督深度补全网络常见的输出方式是从解码器的不同层各接出一个深度预测头对应不同分辨率。每个预测头都会和缩放到相应尺寸的真值计算损失。这个过程叫作“多尺度深度监督”会让网络的前期层就学会深度推理而不只是在最终层才发力。在自监督场景下多尺度监督依然有效但操作起来要多一个心眼。稠密真值来自单目网络不同尺度下的预测质量并不一样。下采样之后细小物体的深度误差会被平均掉反而可能更稳定。因此我在每个尺度计算损失时会单独给权重深层的权重稍大浅层权重稍小。这不是拍脑袋定的而是因为在深层特征图上网络已经聚集了足够的上下文信息深度判断的可信度更高。提示多尺度损失在视觉任务中很常见但深度补全任务里要控制好各尺度的权重比例。我最开始贪省事采用等权重结果边缘区域出现了不该有的模糊。后面把低分辨率头的损失权重调低了0.2倍结果一下子就干净了。2.3 损失函数构成与权重选择损失函数的设计直接决定自监督训练能不能收敛。我将完整的损失函数拆成三个部分来看。第一部分是稀疏深度损失只在LiDAR有效点上计算预测值和真实LiDAR测量值之间的误差。这部分是绝对尺度监督的核心通常用L1损失。为什么不用L2L2对大误差惩罚过重当LiDAR点本身有噪声或者边缘投影出现微小对齐偏差时L2会强制网络去拟合这个噪声反而造成局部区域预测值异常。L1对离群值更稳健实测效果明显更好。第二部分是稠密深度损失在所有像素上计算预测值和单目网络预测值之间的误差。这部分的“真值”虽然不是物理测量结果但提供了结构先验。可以用L1也可以用L2我推荐L2。单目网络输出的深度在平滑区域变化缓慢L2可以让预测值在结构和连续性上更好地贴合教师网络。第三部分是平滑损失通常使用预测深度的梯度幅值作为惩罚项。它的作用是让深度图在物体内部保持平滑减少条纹状伪影。计算时一般对梯度做一个边缘感知加权图像梯度大的位置物体边界允许深度突变图像梯度小的位置物体内部强制深度连续。三部分损失合起来的公式如下total_loss λ_sparse * L_sparse λ_dense * L_dense λ_smooth * L_smooth经验上稀疏损失的权重最高因为那是尺度锚点稠密损失的权重次之平滑损失只是辅助不能给太高否则会把物体的边缘磨平。我常用的一组比例是 1.0 : 0.5 : 0.1调参时可以以此为基准。3. 实操过程与核心环节实现3.1 数据准备LiDAR点云到图像的投影第一步是数据对齐。LiDAR坐标系下的三维点要投影到相机像素坐标需要知道LiDAR和相机之间的外参以及相机的内参。这个环节从标定参数读取开始然后对每个LiDAR点做刚体变换和透视投影。投影之后需要过滤掉三类无效点位于相机后方的点、投影到图像范围之外的点、以及深度值超出合理阈值的点。如果不做这些过滤深度图上会出现脏数据——那些位于相机后方的点投影后可能落到图像边缘造成很大的错误深度值。过滤后的有效点直接落到对应的像素位置。如果同一个像素位置落入多个点取距离相机最近的点的深度值这个原则在自动驾驶数据集中几乎无一例外。原因很直观近处的物体遮挡远处的物体最近的深度才是该像素的期望深度。掩码的生成就简单了有效投影位置置1其他位置置0。注意拿到公开数据集后不要直接信任它自带的预处理脚本。我碰到过一次原始数据集的掩码和深度图分辨率不一致导致训练时掩码偏移了半个像素越到图像边缘误差越大。这种问题在可视化时不明显但精度的下降是实打实的。3.2 单目教师网络的准备单目深度网络是稠密伪监督的唯一来源这步不能马虎。我常用的做法是在当前任务相关的数据子集上再微调它几十个epoch让它见过LiDAR投影后的空间分布输出的深度更好用。这里有个细节值得关注——单目网络输出的是视差或者逆深度需要使用 s depth_scale 将它换算成绝对距离。换算之后的深度分布和LiDAR测量值基本在一个量级上但显示范围可能更窄。可以做一个动态的截断把超出设定量程范围的低置信度区域直接置为无效不参与后续损失计算。在训练期间教师网络处于“冻结”状态。如果让它随着学生网络一起更新短期的收益有但长期看很容易陷入坍塌——学生和教师在错误的方向上互相“印证”。所以即使是自监督也要保持一个稳定的参考标准。3.3 训练流程与损失代码实现整个训练逻辑可以用下面的伪代码概括# 深度补全训练的核心逻辑 for batch in dataloader: rgb batch[rgb] # (B, 3, H, W) sparse_depth batch[sparse_depth] # (B, 1, H, W) depth_mask batch[depth_mask] # (B, 1, H, W) with torch.no_grad(): teacher_depth teacher_model(rgb) # 冻结教师网络 teacher_depth inverse_depth_to_depth(teacher_depth) # 学生网络输入 net_input torch.cat([rgb, sparse_depth, depth_mask], dim1) pred_depth student_model(net_input) # 稀疏监督只在有LiDAR点的位置计算L1损失 sparse_mask_bool depth_mask.bool() sparse_loss F.l1_loss( pred_depth[sparse_mask_bool], sparse_depth[sparse_mask_bool] ) # 稠密监督在所有有效教师区域计算L2损失 valid_teacher (teacher_depth 0.1) (teacher_depth 80.0) dense_loss F.mse_loss( pred_depth[valid_teacher], teacher_depth[valid_teacher] ) # 平滑损失梯度惩罚 dx torch.abs(pred_depth[:, :, :, 1:] - pred_depth[:, :, :, :-1]) dy torch.abs(pred_depth[:, :, 1:, :] - pred_depth[:, :, :-1, :]) smooth_loss dx.mean() dy.mean() total_loss 1.0 * sparse_loss 0.5 * dense_loss 0.1 * smooth_loss total_loss.backward() optimizer.step()实际跑下来我建议训练初期让“稀疏损失”的权重占据绝对主导——甚至可以先用稀疏损失单独训几十个batch让网络快速学到“有LiDAR点的地方深度就该是那个值”。然后再加入稠密损失和平滑损失去细化。这种分阶段训练策略比直接上全量损失收敛稳定得多。提示我在训练里偶尔会遇到深度图整体偏小而掩码区域却准确的情况。这说明网络已经学会“作弊”——只在有LiDAR点的位置输出正确值其余区域输出低方差但不合理的常数。解决办法是提高平滑损失的权重同时将稠密损失的输出范围从“全部教师有效点”缩小到“教师置信度高于阈值的点”。3.4 数据增强与多帧融合经验数据增强在自监督深度补全里比在有监督任务里更敏感。翻转、旋转、缩放、裁剪这些操作都会改变LiDAR投影的坐标关系所以不能再像纯图像任务那样随意操作。我常用的安全方案是整图刚性变换即对RGB图、稀疏深度图、掩码图、以及教师网络的输出使用完全相同的几何变换参数。翻转时的左右翻转会导致深度值分布方向反转吗不会因为深度是纯数值不依赖于图像方向。但要注意旋转角度过大的话图像边缘会出现无效区域这部分区域的深度预测值也没有意义。多帧融合的坑更多。相邻帧LiDAR通过位姿投影到当前帧如果位姿估计有误差深度图上会出现双重边缘。解决这个问题可以在融合时对每个像素保留最近帧的深度值或者计算一个“时间一致性”权重越新的帧权重越高。我试过简单的衰减权重效果立竿见影。4. 评测指标与实验结果的客观解读4.1 深度补全任务中的核心评价指标只有Loss值是不够的深度补全领域有几个约定俗成的评估指标。理解它们你才能判断一个模型到底“好”在哪、“差”在哪。RMSE均方根误差是最常用的指标。它将所有像素误差平方后求平均再开根号对远距离大误差的惩罚非常重。两个模型如果RMSE接近不代表它们输出质量一样——一个可能在近处表现好但远处崩溃另一个可能在近处一般但远处稳定。MAE平均绝对误差对离群值的敏感度低。在部署到实际系统时MAE往往比RMSE更能反映“绝大多数像素的体验”。如果你的用户只关心绝大多数点的深度准不准MAE就是优先级更高的指标。iRMSE和iMAE是“逆深度”上的误差指标。逆深度是1/depth物理意义接近视差。在自动驾驶里逆深度误差大意味着近距离物体的深度估计误差很大——这是最危险的情况。指标计算方式关注的误差类型我的建议RMSEsqrt(mean((d_pred - d_gt)^2))大误差、远距离主优化目标MAEmean(d_pred - d_gt平均精度辅助参考iRMSEsqrt(mean((1/d_pred - 1/d_gt)^2))近处物体精度单独关注4.2 训练过程中应该观察哪些指标训练时我最在意的不是验证集上的RMSE绝对值而是它的变化趋势。RMSE下降曲线的形态能告诉你很多信息——如果前几个epoch下降得很快说明稀疏监督在发挥作用如果后期下降变慢但是MAE还在持续降低说明稠密监督在活跃地填充结构细节。一个有用的调试技巧是在训练过程中定期保存几张可视化预测图。我一般每500步存一次用三个子图并排展示稀疏输入、预测稠密深度、教师网络深度。有时候loss曲线几乎不波动但可视化里能明显看出预测深度图的纹理越来越平滑。Loss是总体的可视化是局部的两者结合才能真正判断模型状态。另一个重要习惯是在训练中监控“近处物体深度误差”。自动驾驶场景中5米以内的物体感知最要命。如果RMSE总体下降但近处误差上升很可能是损失函数里平滑损失权重过大把近处的细节强行抹平了。5. 常见问题与排查技巧实录5.1 反射面和暗光区域的伪影这个坑我印象极深。汽车表面的镀铬装饰、车窗玻璃、光滑地面在图像中会有强烈的反射或光晕单目网络在这些区域输出的深度预测经常出现“虚胖”的错误结构——比如把玻璃上倒映的树影预测成一个实心物体。如果深度补全网络全盘接受教师的预测就会把这种错误结构固化下来。我的方案是引入“边缘置信度惩罚”计算图像的颜色梯度如果某个区域的梯度异常混乱就降低该区域稠密损失的权重。这背后的逻辑是颜色梯度混乱意味着该区域存在复杂的光学现象无论是反射、折射还是透明表面单目深度估计的置信度都应该大幅下调。注意不要尝试在纯自监督框架内“修复”这些困难区域的绝对深度值——那是不可能的。它们的绝对深度只能靠未来的帧间一致性或者多传感器融合来约束。在当前帧的自监督训练中基础做法是不让这些区域的错误预测污染整体损失。5.2 深度图边缘模糊与边界泄漏深度补全常见的视觉缺陷之一是边缘模糊物体的轮廓扩散到了背景区域看起来像是深度值在物体边缘“渗水”。原因多是因为平滑损失施加了整个图像区域的连续性约束而边缘区域本应是深度突变的地方。一个可靠的修复方式是“边缘感知平滑损失”。具体做法是使用RGB图像的梯度幅值作为平滑损失的权重。在RGB梯度大的像素位置平滑损失权重降到接近零在梯度小的位置权重保持正常。这样网络在平滑区域内被强制保持深度连续而在物体边界处获得“自由裁量权”。代码实现也很简单# 边缘感知平滑损失 img_grad_x torch.abs(rgb[:, :, :, 1:] - rgb[:, :, :, :-1]).mean(dim1, keepdimTrue) img_grad_y torch.abs(rgb[:, :, 1:, :] - rgb[:, :, :-1, :]).mean(dim1, keepdimTrue) weights_x torch.exp(-img_grad_x) weights_y torch.exp(-img_grad_y) smooth_loss (weights_x * dx).mean() (weights_y * dy).mean()这个损失在deployed系统中几乎是标配强烈建议直接用。5.3 LiDAR点投影与图像对齐偏差我不知道你是不是也遇到过这种情况训练Loss正常下降但可视化时总感觉预测深度在物体边缘有一层细细的“描边”像是把深度图错位了半个像素。排查后发现是LiDAR投影参数的问题。LiDAR的外参标定结果本身存在零点几度的误差投影到图像平面后在图像边缘部分会产生1~2像素的偏移。训练时网络会尝试同时拟合正确边缘和左右偏移边缘结果导致物体轮廓变粗、深度过渡变宽。解决这个问题的标准操作是“投影偏移补偿”。对LiDAR点云做一次约1像素的随机平移增强让网络自动学会容忍这种微小的系统偏差。我在训练时对稀疏深度图和掩码做一个随机偏移偏移范围在[-1, 1]像素之间学习效果会有明显改善。5.4 教师网络预测的尺度不稳定性再好的单目网络在不同场景下输出的绝对尺度也不稳定。同一段路上晴天和阴天的预测深度可能会系统性偏差10%以上。这个问题表面上看是“单目网络的事”但实际上会直接影响深度补全网络学到的“稠密分布”。我对教师预测做了一步“尺度对齐”在掩码区域内计算教师预测和LiDAR真实测量之间的均值比例然后用这个比例去缩放教师的整幅深度图。这个操作要小心在极端遮挡条件下LiDAR点少统计量的方差大。所以我会设定最小点数阈值低于阈值则跳过缩放。这样做之后教师网络的预测分布就能和LiDAR的物理测量尺度保持一致深度补全网络也学得更稳。6. 自监督深度补全的扩展方向6.1 多帧时序一致性约束当前帧自监督深度补全最大的限制在于它只“看见”了当前帧的RGB和LiDAR没有利用时间维度的信息。但深度补全任务天然适合多帧输入因为相邻帧之间的视差变化包含了丰富的结构信息。可以在训练时增加一个“时序深度一致性”损失将预测深度图反投影到前一帧的相机坐标系再和前一帧的预测深度做点级距离比对。如果两帧预测一致说明这些区域深度可信如果不一致说明至少有一帧在这些区域预测错误需要调整。时序一致性约束的本质是把视频序列变成了免费的监督信号。它不要求稠密真值只要求位姿可靠非常适合在量产车数据上进行半自动的数据清洗和模型迭代。6.2 与位姿一致性方案结合前面我对比了两条技术路线但这两条路线并不是互斥的。一个非常适合实践的组合是以单目教师网络提供初始监督同时让位姿网络提供几何一致性约束。具体来说教师网络负责提供稳定的“结构先验”位姿网络负责在时序维度上维护“几何闭环”。我试过把两者合并训练的版本。教师网络参与前期的预训练阶段随后可以逐渐降低它对损失函数的贡献权重让网络更多依赖自带的几何自监督能力。这种方式训练出的模型既保留了知识蒸馏路线的快速收敛能力又具备位姿一致性方案的抗漂移特性。6.3 模型轻量化与端侧部署深度补全网络在自动驾驶的落地场景中往往要跑在嵌入式平台上算力预算吃紧。所以在实用导向的项目中我习惯把U-Net的backbone从大模型切换到轻量化变体。实测下来用MobileNetV3风格的编码器做backbone在保持输入分辨率不降低的前提下模型的参数量可以压缩到原来的三分之一而精度损失在可接受的范围内。压缩的主要技巧是把标准卷积替换成深度可分离卷积并把解码器的通道数整体缩小。这个过程没有听起来那么玄——先用原模型跑通全部流程再把backbone替换掉用训练日志对比RMSE的变化。如果掉点超过5%宁可模型大一点也不要强上轻量级结构。结尾的几句心里话从第一次在这个课题上踩坑到现在基本形成一套稳定的训练流程我最大的体会是自监督深度补全的真正难点不在网络结构而在训练哲学上。你没有办法像有监督方案那样依赖真值只能通过各种间接信号去约束网络而哪些信号更可靠、哪些信号容易产生误导完全需要靠实验去验证。整个过程里最值得记住的一点是别把单目教师网络的输出当成“准真值”。它是结构先验不是物理事实。每次使用它的时候都该问自己这个区域教师网络真的靠谱吗如果它错了我现在的损失函数会不会帮它把错误固化下来这种“怀疑一切监督信号”的习惯恰恰是自监督领域工程师最有价值的能力。如果你打算在这个方向上继续深挖我会建议你先完整地复现我上面写流程把推理效果跑出来再进行后续扩展创新。很多细节比如掩码的生成方式、教师网络微调的范围、平滑损失的权重只有在亲自动手之后才能形成真正属于自己的经验。祝顺利。