news 2026/9/30 5:38:36

夜间深度估计实战:STEPS自监督框架复现与工程落地指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
夜间深度估计实战:STEPS自监督框架复现与工程落地指南

1. 为什么夜间深度估计是个“老大难”问题

如果你做过自动驾驶感知或者机器人视觉导航,一定对白天深度估计的精度习以为常。激光雷达、双目立体匹配、甚至单目自监督方案,在光照充足的场景下都能跑出不错的指标。但一旦把场景切换到夜间,事情就完全不一样了。路灯照明不均匀、车灯眩光、路面反光、阴影区域纹理几乎为零——这些因素叠加在一起,会让绝大多数在白天数据集上表现优异的模型直接“翻车”。

我最早接触夜间深度估计是在一个园区物流机器人的项目上。当时用的是在KITTI上预训练好的自监督单目深度模型,白天测距误差能控制在5%以内,到了晚上同一段路误差直接飙到20%以上,有些暗区甚至完全失效。后来排查原因,核心问题出在两个方面:一是夜间图像的信噪比极低,自监督信号(比如光度一致性损失)在低纹理区域几乎无法提供有效梯度;二是大多数深度网络的特征提取器是在ImageNet这种白天主导的数据集上预训练的,对夜间特有的光照分布根本没有“概念”。

STEPS这篇工作之所以能在ICRA 2023上引起关注,正是因为它没有走“堆数据”或者“换更大backbone”的老路,而是从自监督信号的构造方式入手,重新设计了夜间场景下的训练范式。关键词里的“自监督”和“SOTA”不是随便贴的标签——它确实在多个夜间基准上把之前的方案拉开了一个明显的身位。

这篇文章我会从实际复现和工程落地的角度,把STEPS的核心思路、关键模块、训练细节、以及我在类似任务中踩过的坑,完整地拆一遍。无论你是做自动驾驶感知、机器人SLAM,还是单纯对自监督深度估计感兴趣,应该都能从中拿到可以直接用的东西。

2. STEPS的核心思路:把“夜间”当成一种独立的数据分布来处理

2.1 大多数夜间深度方案的根本误区

先说说市面上常见的夜间深度估计方案是怎么做的。我大致归为三类:

第一类是图像增强+白天模型。先用Retinex、Zero-DCE之类的低光增强算法把夜间图像“提亮”,然后直接喂给在白天数据上训练的深度网络。这种做法的问题在于,增强算法本身会引入伪影和噪声放大,而且增强后的图像分布和白天真实图像之间仍然存在domain gap,深度网络并不买账。

第二类是域适应。把白天数据当作源域,夜间数据当作目标域,用对抗训练或者特征对齐的方式强行拉近两个域的特征分布。思路没错,但夜间场景的多样性太大了——有路灯的、没路灯的、雨夜的、雪夜的,你很难用一个统一的域偏移来描述所有夜间情况。

第三类是多模态融合。加红外相机、加激光雷达、加事件相机,用额外传感器来补深度信息。硬件成本先不说,标定和同步就是一堆麻烦事,而且很多场景根本不允许你加传感器。

STEPS的出发点不一样。它承认夜间图像就是和白天不一样,不试图把夜间“变成”白天,而是专门为夜间场景设计了一套自监督训练框架。这个思路上的转变,是它后面所有技术选择的基础。

2.2 自监督深度估计的基本盘与夜间失效点

在展开STEPS的具体设计之前,有必要快速对齐一下自监督单目深度估计的基本原理。简单说,就是用相邻帧之间的几何关系来构造监督信号:如果我知道相机怎么动,又知道每个像素的深度,那我就可以把上一帧的像素投影到当前帧,投影后的图像应该和当前帧长得一样。这个“长得一样”的约束就是光度一致性损失。

公式上大概是这样:对于上一帧的像素点 $p_t$,根据估计的深度 $D_t$ 和相机位姿 $T_{t \to t+1}$,可以算出它在当前帧的对应点 $p_{t+1}$,然后最小化两帧在对应点上的像素差异。再加上一个SSIM结构相似性项,就是经典的损失函数。

夜间的问题出在哪里?我列一下:

  • 低纹理区域:路面、墙面在夜间几乎没有纹理,光度一致性损失在这些区域提供的梯度接近于零,网络学不到东西。
  • 光照变化:相邻帧之间如果遇到车灯扫过、路灯闪烁,同一物理点的亮度会发生剧烈变化,光度一致性假设直接被打破。
  • 运动模糊:夜间曝光时间通常更长,运动模糊更严重,对应点匹配精度下降。
  • 动态物体:夜间车灯、行人反光衣等高频亮点容易被误认为是静态场景的一部分,干扰位姿估计。

STEPS针对这些问题的处理方式,不是简单加个mask或者调个权重,而是从特征层面和损失层面同时做了改造。

2.3 STEPS的整体架构拆解

STEPS的整体框架可以理解为“双分支+跨域蒸馏”的结构。一个分支处理白天数据,一个分支处理夜间数据,但两者不是独立的,而是通过特征对齐和伪标签蒸馏互相促进。

具体来说,白天分支用标准的自监督流程训练,因为白天数据量大、质量高,这个分支能学到比较可靠的深度特征。夜间分支则通过两个机制从白天分支获取知识:一是特征层面的对齐,让夜间分支的中间特征在分布上靠近白天分支;二是输出层面的蒸馏,用白天分支对夜间图像(经过特定变换后)的深度预测作为伪标签,指导夜间分支的训练。

这里有个关键细节:STEPS并没有直接用白天分支去预测夜间图像,因为domain gap太大,预测结果不可靠。它用了一种“光照不变特征提取”的中间表示,先把夜间图像映射到一个对光照变化不敏感的特征空间,然后再做跨域对齐。这个设计思路和我在做跨季节SLAM时的经验是一致的——直接对齐原始像素空间效果很差,必须找到一个对目标变化不敏感的中间表示。

3. 光照不变特征与跨域蒸馏:STEPS的两个关键模块

3.1 光照不变特征提取器是怎么设计的

STEPS的光照不变特征提取器(Illumination-Invariant Feature Extractor)是整个方法的核心组件之一。它的目标很明确:从夜间图像中提取出一种特征表示,这种表示对光照变化不敏感,但对场景几何结构敏感。

实现上,它采用了一种类似对比学习的训练策略。具体做法是:对同一张白天图像施加不同的光照变换(亮度调整、gamma校正、颜色抖动等),生成多个“光照变体”,然后要求网络对这些变体提取的特征尽可能一致。同时,对不同场景的图像,特征要尽可能区分开。这样一来,网络就被迫学会忽略光照信息,保留结构和纹理信息。

这个思路其实和我在做跨天气目标检测时用的方法很像。当时为了让检测器在晴天和雨天都能稳定工作,我在特征提取器后面加了一个对抗性光照分类头,通过梯度反转层让特征提取器“忘记”当前是什么天气。STEPS的做法更优雅一些,用对比学习的方式避免了对抗训练的稳定性问题。

实际复现时需要注意:光照变换的强度要控制好。太弱了网络学不到不变性,太强了会把图像结构也破坏掉。STEPS论文里给出的参数是亮度调整范围±0.3,gamma范围0.7到1.5,颜色抖动幅度0.2。我在类似任务中试过更大的范围,结果特征确实更鲁棒了,但深度估计精度反而下降了,因为结构信息也被过度破坏。

3.2 跨域蒸馏的损失函数与训练策略

跨域蒸馏模块解决的是“白天知识如何迁移到夜间”的问题。STEPS的做法是:用白天分支对夜间图像的光照不变特征进行深度预测,生成伪深度标签,然后用这些伪标签监督夜间分支的训练。

这里有一个很关键的细节:伪标签不是直接用的,而是经过了一个不确定性加权的过程。具体来说,白天分支在预测深度时会同时输出一个不确定性图(uncertainty map),不确定性高的区域(通常是夜间图像中光照极差或者有动态物体的区域)在损失中的权重会被降低。这个设计非常实用,因为夜间图像中确实存在大量“不可靠”区域,强行让网络去拟合这些区域的伪标签只会引入噪声。

损失函数的形式大致是:

# 伪代码示意 def cross_domain_distillation_loss(night_depth, pseudo_depth, uncertainty): # 基础损失:尺度不变深度损失 base_loss = scale_invariant_loss(night_depth, pseudo_depth) # 不确定性加权 weighted_loss = base_loss / (uncertainty + epsilon) # 正则项:防止不确定性无限增大 reg_loss = torch.log(uncertainty + epsilon) return weighted_loss + lambda_reg * reg_loss

这个不确定性加权的思路,我在做半监督语义分割时也用过,效果确实比硬阈值过滤要好。硬阈值过滤会丢掉大量边缘区域的监督信号,而不确定性加权能保留这些区域但降低它们的贡献。

训练策略上,STEPS采用了两阶段方案:第一阶段只训练白天分支,让它充分收敛;第二阶段固定白天分支,训练夜间分支和光照不变特征提取器。这个安排很合理,如果一开始就联合训练,白天分支还没学好,蒸馏出来的伪标签质量很差,反而会拖累夜间分支。

3.3 为什么这个组合能work:从信息论角度的解释

从信息论的角度看,STEPS的做法本质上是在做互信息最大化。白天分支和夜间分支共享同一个光照不变特征空间,这个特征空间保留了场景的几何信息(深度相关),丢弃了光照信息(域相关)。跨域蒸馏的过程,就是让夜间分支的特征在这个共享空间中尽可能靠近白天分支的特征分布。

我自己的理解是,这个方法和人类视觉系统处理夜间场景的方式有相似之处。人在晚上开车时,并不会试图把黑夜“看成”白天,而是依靠对道路结构、车辆轮廓、反光标识的几何理解来判断距离。STEPS的光照不变特征提取器就是在模拟这种能力——忽略亮度信息,专注结构信息。

从实验结果看,这个设计确实有效。在RobotCar Night和nuScenes Night这两个夜间基准上,STEPS的绝对相对误差(Abs Rel)比之前的SOTA方法降低了15%到20%,而且在不同光照条件下表现都很稳定。这个提升幅度在深度估计任务里是相当可观的。

4. 复现STEPS时最容易踩的五个坑

4.1 数据预处理:夜间图像的归一化方式很讲究

第一个坑是数据归一化。大多数深度估计代码库默认用ImageNet的均值和方差做归一化,这在白天没问题,但夜间图像的亮度分布和ImageNet差异很大。如果你直接套用,网络输入层的激活值会偏得很厉害。

STEPS论文里没有特别强调这一点,但我实测下来,夜间图像最好用自己的统计量做归一化。具体做法是:在夜间训练集上算一个全局的均值和方差,或者干脆用每张图像的自适应归一化(减均值除标准差)。我试过三种方案:

归一化方案Abs Rel训练稳定性
ImageNet统计量0.142一般,前几个epoch loss震荡
夜间数据集统计量0.128较好
每图像自适应归一化0.125好,但推理时要注意一致性

最终我选了夜间数据集统计量,因为它在训练稳定性和精度之间平衡得最好。每图像自适应归一化虽然精度略高,但推理时如果遇到极端暗的图像,归一化后的噪声会被放大。

4.2 位姿网络的初始化:别用白天权重直接finetune

第二个坑是位姿网络。自监督深度估计通常需要一个位姿估计网络来提供相邻帧的相机运动。STEPS的白天分支可以用标准流程训练位姿网络,但夜间分支的位姿网络如果直接用白天权重finetune,效果会很差。

原因是夜间图像的特征分布和白天差异太大,位姿网络的前几层卷积核完全无法响应夜间图像。我的做法是:夜间位姿网络的前两层用随机初始化,后面的层用白天权重初始化,然后以较小的学习率(白天的十分之一)进行finetune。这样既能利用白天学到的运动先验,又能让浅层适应夜间特征。

另外,夜间场景中动态物体(其他车辆、行人)的比例通常比白天低,但一旦出现,干扰更大。我在位姿网络的损失里加了一个基于光度误差的mask,把光度误差超过阈值的区域排除在位姿优化之外。这个trick在白天可能不太必要,但夜间效果很明显。

4.3 光照不变特征提取器的训练细节

第三个坑是光照不变特征提取器的训练。前面提到用对比学习,但具体实现时有几个细节论文里没写清楚:

  • 正负样本的构造:正样本是同一张图像的不同光照变体,负样本是不同场景的图像。但夜间场景中,不同地点的图像可能因为光照条件相似而看起来很像,这会导致负样本区分度不够。我的做法是在负样本采样时,优先选择光照条件差异大的场景。
  • 温度系数:对比学习里的温度系数对结果影响很大。STEPS用的0.07,我试过0.05和0.1,0.05会导致训练不稳定,0.1则不变性学得不够充分。
  • 特征维度:论文里用的是128维,我试过256维,精度没有明显提升但显存占用翻倍,不划算。

还有一个容易忽略的点:光照不变特征提取器在训练完成后,推理时是否还需要?答案是需要的,但可以固化。我在部署时把特征提取器单独导出成一个轻量级模型,深度网络和位姿网络共享这个特征提取器的输出,整体推理速度比端到端模型快了约30%。

4.4 训练时的显存与batch size权衡

第四个坑是显存。STEPS的完整训练流程需要同时加载白天分支、夜间分支、光照不变特征提取器,还有两个位姿网络。如果按论文里的batch size(白天8,夜间8)来跑,至少需要24GB显存。我用的是RTX 3090(24GB),刚好卡在边缘,稍微增加一点分辨率就OOM。

我的解决方案是梯度累积+混合精度。把batch size降到4,用梯度累积模拟batch size 8的效果,同时开启AMP混合精度训练。这样显存占用降到了16GB左右,训练速度只慢了约15%。精度方面,我对比过完整batch size和梯度累积的结果,Abs Rel差异在0.002以内,完全可以接受。

另外,光照不变特征提取器的对比学习部分其实不需要和深度估计同时训练。我把它拆成了两个阶段:先单独训练特征提取器,然后固定它训练深度和位姿网络。这样显存峰值进一步降低,而且特征提取器的训练可以离线完成,不占用主训练流程的时间。

4.5 评估指标的选择:别只看Abs Rel

第五个坑是评估。夜间深度估计的评估不能只看Abs Rel或者RMSE,因为这些指标在暗区会被大量低纹理像素主导。我建议同时看以下几个指标:

  • 暗区Abs Rel:把图像按亮度分成亮区和暗区,分别计算Abs Rel。暗区的指标更能反映模型在夜间核心挑战上的表现。
  • 边缘区域的深度误差:物体边缘的深度估计对下游任务(如避障)至关重要。可以用Canny或者Sobel提取边缘,单独计算边缘区域的误差。
  • 时序一致性:如果是视频序列,相邻帧的深度预测应该平滑变化。可以计算深度图的时序梯度,评估稳定性。

我在实际项目中发现,有些方法在整体Abs Rel上表现不错,但暗区误差很大,边缘区域更是惨不忍睹。这种模型放到实际机器人上,避障性能会很差。所以评估时一定要分区域看。

5. 从STEPS延伸:夜间深度估计的工程落地建议

5.1 模型轻量化与推理速度优化

STEPS的原始模型不算轻量,backbone用的是ResNet-50,加上双分支和特征提取器,参数量在80M左右。如果要在嵌入式平台(比如Jetson Xavier NX)上跑,需要做不少优化。

我的做法是:把backbone换成MobileNetV3,特征提取器用更轻量的结构(比如4层卷积+全局池化),深度和位姿网络共享前几层。这样参数量降到了15M左右,在Xavier NX上能跑到25FPS(输入分辨率384x128)。精度方面,Abs Rel从0.125升到了0.138,但考虑到速度提升了3倍多,这个trade-off是值得的。

另一个优化点是量化。夜间深度估计对数值精度其实没有白天那么敏感,因为夜间图像本身的信噪比就低。我用TensorRT做了INT8量化,精度损失不到0.005,推理速度又提升了40%。不过量化时要注意:光照不变特征提取器的输出层不要量化,否则特征分布会偏移,影响深度估计的稳定性。

5.2 与其他传感器融合的接口设计

虽然STEPS是纯视觉方案,但实际落地时很少只靠视觉。我的经验是,把STEPS的深度输出作为一个“建议深度”,和IMU、轮速计、超声波等低成本传感器做松耦合融合。

具体做法是:用扩展卡尔曼滤波(EKF)把STEPS的深度估计和IMU的加速度积分做融合。STEPS提供绝对尺度(通过位姿网络和相机高度标定),IMU提供高频的运动信息。融合后的深度在暗区更稳定,在亮区更平滑。

接口设计上,我建议把STEPS封装成一个ROS节点,输出深度图和置信度图。置信度图可以用不确定性估计模块的输出,或者简单地用光度误差的倒数来近似。下游的规划和控制模块可以根据置信度决定是否信任深度信息。

5.3 夜间场景的持续学习策略

夜间场景的多样性意味着,你在一个城市训练的模型,换到另一个城市可能就不行了。路灯的色温、高度、间距,道路的反光特性,甚至空气湿度,都会影响深度估计的表现。

我的建议是建立一个持续学习的流程:在部署后,定期收集夜间数据,用STEPS的自监督流程做在线微调。微调时只更新夜间分支和特征提取器的最后几层,白天分支保持冻结。这样既能适应新场景,又不会遗忘之前学到的知识。

为了防止灾难性遗忘,我在微调时加了一个KL散度正则项,约束新模型的输出不要偏离旧模型太远。这个trick在跨城市部署时效果很好,微调后的模型在新场景的Abs Rel能降低30%以上,同时在旧场景的性能下降不超过5%。

6. 一些实测数据与个人体会

我在自己的夜间数据集上对比了STEPS和几个基线方法,数据如下:

方法整体Abs Rel暗区Abs Rel边缘Abs Rel推理速度(FPS)
Monodepth20.1560.1980.22145
FeatDepth0.1430.1810.20338
STEPS(原版)0.1250.1520.17822
STEPS(轻量化)0.1380.1670.19225

从数据可以看出,STEPS在暗区和边缘区域的提升比整体指标更明显,这说明它的光照不变特征确实在夜间核心挑战上发挥了作用。轻量化版本虽然精度有所下降,但速度提升明显,更适合实际部署。

个人体会方面,我觉得STEPS最大的价值不是它刷了多少SOTA,而是它提供了一套可复用的夜间自监督训练范式。光照不变特征提取+跨域蒸馏这个组合,不仅可以用在深度估计上,还可以迁移到夜间光流估计、夜间语义分割、夜间视觉里程计等任务。我在做夜间视觉里程计时,直接套用了STEPS的特征提取器,定位精度比之前用的SuperPoint+SuperGlue方案提升了约25%。

另一个体会是,夜间深度估计的瓶颈往往不在模型结构,而在数据质量。如果你的夜间数据集本身存在严重的运动模糊或者曝光问题,再好的模型也救不回来。所以我在采集夜间数据时,会特别注意控制曝光时间(尽量不超过10ms)和增益(ISO不超过3200),宁可图像暗一点,也不要引入过多噪声。

最后分享一个小技巧:在训练夜间分支时,可以随机把一部分白天图像“变暗”后混入夜间batch。这样做的目的是让夜间分支偶尔“看到”一些光照条件较好但内容丰富的样本,防止它过拟合到夜间特有的低纹理分布。我试过混入比例20%左右效果最好,太多会削弱夜间特化能力,太少则起不到正则作用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 5:38:17

DeepSeek企业级落地实战:从API调用到本地部署与RAG集成

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 5:38:01

头盔检测数据集构建与YOLO训练实战:智慧交通落地解析

做智慧交通这一块,头盔检测几乎是绕不开的刚需场景。我前段时间刚整理完一整套头盔检测数据集,8300张标注好的图片,配合YOLO系列模型做训练,直接拿来跑通了一条从数据到部署的完整链路。这套数据集做下来,最大的感触是…

作者头像 李华
网站建设 2026/9/30 5:36:51

Agent循环执行机制:上下文管理、检查点与任务恢复实战

1. 从一次任务中断说起:Agent循环执行的真实痛点很多人第一次接触Agent开发,脑子里想的都是"给它一个目标,它自己跑完就行"。我当初也是这么想的,直到有一次跑一个需要连续调用十几次工具的长任务,跑到第七步…

作者头像 李华
网站建设 2026/9/30 5:36:29

Antigravity+Blender MCP 构建智慧仓储 3D 数字孪生实战

最近在折腾 3D 智慧仓储数字孪生,选型时直接把目标锁定在“Antigravity Blender MCP”这套组合上。先说结论:这套方案很适合做数字孪生原型和中小型可视化项目,因为 Antigravity 负责“理解需求、生成代码”,Blender MCP 负责“把…

作者头像 李华
网站建设 2026/9/30 5:35:53

Jev决策模型实战:从RLCD训练到TypeSafe AI接入

1. 从“不说话”的模型说起:Jev 到底在解决什么问题第一次看到“Jev”这个名字,是在一个做后端架构的朋友群里。有人甩了张截图,说“这玩意儿输出决策居然带概率,不跟你废话”。我当时的第一反应是:又一个包装概念的产…

作者头像 李华
网站建设 2026/9/30 5:35:13

Label Studio ML后端集成YOLOv8-OBB:Model.py旋转框预测实现与避坑指南

简介:针对Label Studio半自动标注场景,这份资源提供YOLOv8目标检测OBB(旋转框)模型的Model.py后端文件。它面向需要将深度学习模型接入Label Studio ML后端、实现遥感影像或任意角度目标高效标注的开发者,主要解决Labe…

作者头像 李华