深度估计最近在视觉社区里的热度上升得很快。过去想做单目深度估计,第一反应是 MiDaS、DPT、Depth Anything 这类独立模型,环境要单独配,输出格式要自己接,想和检测框融合还得写不少胶水代码。如果团队成员只熟悉 YOLO 系列,学习成本就会更高一层。
现在情况有了变化:Ultralytics 生态在 YOLO26 中把深度估计(Depth Estimation)正式纳入了标准任务体系。这意味着模型训练、数据集组织、指标评估、推理导出,都可以和 detection、segmentation、pose 共用一套流程。你不需要学习一套全新的框架,只需要把你熟悉的 YOLO 经验平移过来,就能在深度估计任务上快速做出可用的结果。
这篇文章不打算只罗列功能。我尽量站在实际开发的角度,把 YOLO26 深度估计能力拆开看:它能解决什么问题、和传统深度估计方案比有什么差异、环境怎么搭、推理怎么跑、自定义数据集怎么训、模型怎么导出部署,以及最容易踩的坑在哪里。如果你正在做缺陷检测、机器人抓取、自动驾驶感知或者视频深度分析,这篇内容可以帮你省掉不少起步时间。
1. YOLO26 深度估计任务到底解决了什么问题
先说一个判断:YOLO26 加入深度估计,真正的意义不是“多了一个模型”,而是把深度估计从独立任务变成了 YOLO 多任务体系里的一等公民。
在传统工作流里,深度估计的落地路径通常是这样的:
- 从 GitHub 找一个预训练深度模型,比如 DPT、MiDaS、Depth Anything。
- 单独准备一套 Python 环境,处理输入预处理、归一化、输出后处理。
- 如果要做目标检测,再单独跑一个 YOLO,拿检测框去截取深度值。
- 最后自己写脚本对齐两个模型的输入输出尺寸、坐标系和推理速度。
这个流程问题很明显:模型维护分散、部署包体积翻倍、多模型串行推理延迟高。尤其在做边缘设备落地时,两套模型同时跑,显存和算力都很紧张。
YOLO26 的做法不同。它把深度估计 head 直接集成到 Ultralytics 标准架构里。你训练一个模型,输出里既能有检测框,也能有深度图。对于多任务型应用,比如机器人需要同时知道“物体在哪里”和“物体离我多远”,这种一体化设计会省掉大量工程工作。
从任务本质上看,YOLO26 做的是单目深度估计,也就是只用一张 RGB 图片预测每个像素的深度值。它和双目视差、ToF 这类需要特殊硬件的方案不同,纯视觉方案的最大好处是传感器成本低、部署方便。代价是精度存在上限,尤其是对纹理稀疏、光照变化大的场景会比较吃力。
如果把 YOLO26 的深度估计放进 Ultralytics 生态里看,它的核心价值可以总结成三点:
- 统一训练链路:数据标注格式、训练配置、指标评估都复用 YOLO 的体系。
- 统一推理输出:一个模型可以同时输出 box、mask、pose、depth。
- 统一部署链路:export 到 ONNX、TensorRT、OpenVINO 等格式的流程保持一致。
这意味着,只要你会用 YOLO 做检测,上手深度估计的学习成本就被压得很低。对一个快速迭代的项目来说,这个优势是实打实的。
2. 深度估计的核心概念与 YOLO26 实现思路
2.1 深度估计在做什么
深度估计的输入是一张普通 RGB 图像,输出是和图像尺寸对应的深度图。深度图中每个像素的数值表示该位置物体到相机平面的距离。
单目深度估计本质上是“从 2D 图像推断 3D 信息”的逆问题。这个任务在数学上是不适定的,因为一张 2D 图像可以由无数种 3D 场景投影得到。深度学习模型之所以能做这件事,靠的是从大量训练数据中学习场景结构、物体大小、透视关系、遮挡关系等先验知识。
要理解深度估计的输出,最重要的是区分两种深度表示:
| 深度表示 | 含义 | 输出范围 | 常见用途 |
|---|---|---|---|
| 逆深度 | 深度的倒数 | 0 到 1 之间 | 适合监督训练、数值稳定 |
| 绝对深度 | 真实物理距离 | 0 到 N 米 | 自动驾驶、机器人导航 |
| 相对深度 | 像素间远近关系 | 0 到 1 之间归一化 | 图像虚化、背景分割 |
从 Ultralytics 的常规实现思路看,模型输出往往会经过归一化处理,便于统一 loss 计算和可视化。如果做具体场景落地,通常需要根据相机内参或者真实距离标定,把网络输出映射到真实物理单位。
2.2 YOLO26 的深度估计 head 长什么样
从架构设计的角度理解,YOLO26 做深度估计的方式并不是把原来检测、分割的 head 直接替换掉,而是在 backbone 提取的多尺度特征上,增加一个面向像素级预测的 head。这个 head 负责把高层语义特征逐步上采样到输入分辨率,最终输出单通道深度图。
理解这一点很重要。因为很多第一次接触 YOLO26 深度估计的开发者会误以为它只是在模型后面接了一个全连接层回归“一个深度值”。实际上它是像素级 dense prediction,和分割任务的输出形式类似,只是通道数不同:
- 分割任务输出:每个像素的类别 logits,通道数=类别数。
- 深度估计输出:每个像素的深度值,通常单通道。
所以你在训练时大概率会看到和分割任务类似的现象:需要控制 loss 尺度、需要做多尺度监督、需要处理边缘区域的预测模糊问题。
2.3 YOLO26 深度估计与分割任务的本质区别
这也是新手最容易混淆的地方。从数据结构上看,深度图和分割 mask 都是逐像素输出,但它们的监督信号完全不同:
- 分割 mask 的标签是离散类别。
- 深度图的标签是连续数值。
这意味着深度估计和分割在 loss 函数、评估指标、后处理上都有本质差异。分割任务常用 CrossEntropyLoss、Dice Loss;深度估计则更常用 L1 Loss、L2 Loss、SILog Loss。评估指标上,分割看 mIoU,深度估计通常看 AbsRel、SqRel、RMSE、d1 等指标。
如果你直接把分割的训练配置套用到深度估计上,大概率会出现 loss 不收敛、深度图输出异常的问题。后面我会专门讲训练配置中需要注意的参数。
3. 环境准备:YOLO26 深度估计的前置条件
YOLO26 深度估计的起步环境并不复杂,核心就是 Ultralytics Python 包和对应的 PyTorch 环境。因为 Ultralytics 会同时依赖 PyTorch,所以建议先确认本机有可用的 NVIDIA GPU 和合适的 CUDA 环境。没有 GPU 也可以跑通推理,但训练会非常慢,不建议新手用 CPU 训练深度模型。
以下是通用环境要求,具体版本请以实际发布文档为准,本文重点演示通用流程:
| 依赖组件 | 建议要求 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu 20.04 或 Windows 10/11 | 均在支持范围内 |
| Python | 3.8 及以上 | 建议使用虚拟环境 |
| PyTorch | 2.x 及以上 | 需与 CUDA 版本匹配 |
| CUDA | 11.8 或 12.x | 根据显卡驱动选择 |
| Ultralytics | 最新版,支持 YOLO26 深度估计 | 通过 pip 安装 |
安装 Ultralytics 最简单的方式是:
pip install ultralytics如果你已经安装了旧版本,建议升级到最新版本:
pip install -U ultralytics安装完成后,可以验证一下导入是否正常:
python -c "from ultralytics import YOLO; print(YOLO.__name__)"如果看到输出,说明环境基本可用。如果在导入时遇到依赖冲突,可以使用虚拟环境隔离:
python -m venv yolo26_env source yolo26_env/bin/activate # Linux/macOS # 或 yolo26_env\Scripts\activate # Windows这里真正容易踩坑的地方是 PyTorch 和 CUDA 的匹配问题。建议先确认torch.cuda.is_available()返回 True,再开始后面的流程,否则训练时看起来在跑,实际全在 CPU 上,速度会慢到无法接受。
4. 快速上手:用 YOLO26 跑通深度估计推理
环境就绪后,先不要急着训练。用官方预训练权重跑一次深度估计推理,是理解模型输入输出最直接的方式。
4.1 推理代码示例
新建一个depth_infer.py文件,写入以下代码:
from ultralytics import YOLO # 加载 YOLO26 深度估计模型 # 具体权重名称请以当前 YOLO26 实际发布的权重文件为准 model = YOLO("yolo26n-depth.pt") # 运行推理 results = model("test.jpg", task="depth") # 遍历结果 for result in results: # 查看原始深度图张量 if result.depth is not None: print("depth shape:", result.depth.shape) print("depth dtype:", result.depth.dtype) # 保存可视化深度图 result.save_depth_img("depth_visual.png")这段代码的逻辑很简单:加载模型、输入图片、输出结果。result.depth是深度图张量,原始的 shape 通常和输入分辨率有关。save_depth_img会将归一化后的深度图以伪彩色或其他可视化形式保存下来,方便直观检查。
4.2 命令行推理方式
如果不想写代码,直接用命令行也行:
yolo predict model=yolo26n-depth.pt source=test.jpg task=depth运行完成后,结果图片默认保存到runs/segment/predict/目录。这里要提醒一句:因为深度估计和分割在输出结构上比较接近,Ultralytics 的目录命名有时会沿用类似结构,不要因为目录名带 segment 就觉得奇怪。
4.3 判断推理是否成功
拿到输出后,建议从三个维度判断结果:
- 深度图是否和目标场景结构一致:近处物体亮、远处物体暗,墙面过渡平滑。
- 边缘是否清晰:物体轮廓处深度应该有明显跳变。
- 异常区域是否过多:如果整张图黑白噪点密集,说明模型不适用当前场景。
如果输出深度图全黑或全白,大概率不是模型 bug,而是可视化时没有做归一化。深度图原始数值范围可能很窄,直接转为 8 位图会损失信息。需要根据当前场景的动态范围做 min-max 归一化。
5. 训练自定义深度估计数据集
推理只是热身。如果你有具体业务场景,比如产线缺陷高度测量、机器人抓取深度感知,就必须训练领域数据。YOLO26 深度估计的训练流程,可以沿用 Ultralytics 的标准流程,但数据集组织方式需要按 dense prediction 任务的格式准备。
5.1 数据集目录结构
推荐的数据集目录如下:
depth_dataset/ ├── images/ │ ├── train/ │ │ ├── scene_001.jpg │ │ └── scene_002.jpg │ └── val/ │ ├── scene_010.jpg │ └── scene_011.jpg ├── depth/ │ ├── train/ │ │ ├── scene_001.png │ │ └── scene_002.png │ └── val/ │ ├── scene_010.png │ └── scene_011.png └── depth_dataset.yaml这里关键是:images目录放原图,depth目录放对应的深度图。文件名一一对应。深度图的格式建议使用无损格式保存,比如 PNG 或 TIFF,避免 JPG 压缩引入边缘伪影。
5.2 数据集配置文件
新建depth_dataset.yaml:
# 数据集路径,建议使用绝对路径或相对项目根的路径 path: depth_dataset # 图像目录 train: images/train val: images/val # 深度图目录,YOLO26 深度估计需要读取对应的 depth 目录 depth: train: depth/train val: depth/val # 如果原始深度图是 16bit 存储,可能需要指定 depth_scale depth_scale: 1.0这里depth_scale取决于你的深度图数值单位。如果深度值单位是毫米,而模型的监督目标希望是米,就需要设置depth_scale=1000.0或者在数据加载时自行换算。不同数据集的单位可能不同,这一步非常容易出错。
5.3 编写训练脚本
Ultralytics 风格下,训练深度估计非常简单。新建train_depth.py:
from ultralytics import YOLO # 使用 YOLO26 深度估计配置 model = YOLO("yolo26n-depth.yaml") # 开始训练 model.train( data="depth_dataset.yaml", epochs=100, imgsz=640, batch=16, lr0=0.01, device=0, workers=8, task="depth", cache=True, )这段配置里重点参数的含义:
epochs:训练轮数。深度估计数据集一般比检测数据更复杂,建议先用 100 轮跑通,再根据收敛情况调整。imgsz:输入分辨率。分辨率越高,深度图细节越丰富,但显存占用也越大。batch:根据显存调整。如果训练时 OOM,先降低 batch。task="depth":明确指定任务类型。
5.4 从预训练权重继续训练
如果不想从零训练,推荐加载官方预训练模型做迁移学习:
from ultralytics import YOLO # 加载预训练权重,而不是重新创建网络 model = YOLO("yolo26n-depth.pt") # 冻结部分主干层可以降低显存,提升训练稳定性 model.train( data="depth_dataset.yaml", epochs=100, imgsz=640, batch=16, freeze=10, task="depth", )这个方式在数据量不足的时候特别有用。深度估计模型需要学习很强的场景先验,如果从头训练,收敛速度和最终精度通常都不如微调预训练权重。
6. 多任务训练与自定义结构:谈谈 Ultralytics 里的 yaml 调整
很多关注 YOLO26 的开发者同时也在研究 RT-DETR-R18/R34 这类结构,想知道怎么在 Ultralytics 架构里改 yaml 文件,加入深度估计 head,或者把检测、分割、深度估计组合成多任务模型。这里有必要展开讲一讲。
6.1 为什么要改 yaml
Ultralytics 的模型结构是通过*.yaml文件描述的。网络有几层、每层是什么模块、特征图升维还是降维,都由 yaml 里的backbone和head两段配置决定。你想在 RT-DETR-R18/R34 这类主干上加入深度估计头,核心步骤就是在 yaml 的 head 段中注册深度估计分支。
一个简化的思路是:先准备一个 RT-DETR-R18 的 yaml,然后在其 head 末尾增加一个额外的卷积上采样分支,输出通道数为 1,得到深度图。
以伪配置为例:
# 简化的 yaml 结构示意,具体参数需参考 Ultralytics 实际格式 backbone: - [-1, 1, RTDETRReplC2f, [512, 3]] # P4 - [-1, 1, RTDETRReplC2f, [1024, 3]] # P5 head: - [-1, 1, RTDETRTransformer, [768, 6, 4, 1, 1024]] # 原检测头上下文 - [-1, 1, RTDETRConv, [256, 3]] # 新增深度估计分支,上采样到原图 1/4 分辨率 - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [-1, 1, Conv, [64, 3, 1]] - [-1, 1, Conv, [1, 3, 1]] # 输出单通道深度图这只是示意图,不代表官方结构。真正要跑通,需要你对 Ultralytics 的任务注册机制有一定了解。如果对模块细节不熟悉,最稳妥的方式是复制官方提供的 depth 模型 yaml,在其基础上逐步添加其他任务分支,而不要从零写一个结构。
这里真正容易踩坑的地方是:yaml 中模块的from索引、输入通道数和输出通道数必须对上。一旦某一个中间层通道数写错,训练时会出现维度不匹配的错误,而且这类错误往往要跑到第一次 forward 才会报出来,排错成本比较高。
6.2 多任务联合训练的意义
如果你的业务里既需要检测目标位置,又需要知道目标距离,多任务联合训练会比两个模型分开部署更高效。联合训练时,模型共享 backbone 特征,检测分支和深度分支可以互相促进,比如检测分支学到清晰的目标边界,有助于深度分支在边缘处输出更锐利的深度跳变。
代价是训练显存会变大、超参数调优更复杂,损失权重也需要平衡。建议从低权重开始,先让检测分支收敛,再逐步加大深度分支的 loss 权重。
7. 部署落地:导出、C++ 推理与边缘设备适配
模型训练完成后,最终要落地到业务系统。Ultralytics 生态在这块已经很成熟,深度估计模型的导出链路和检测模型完全一致。
7.1 导出 ONNX 模型
from ultralytics import YOLO model = YOLO("yolo26n-depth.pt") model.export(format="onnx", opset=17, imgsz=640)导出的 ONNX 模型可以在 CPU 或 GPU 环境推理。如果你想进一步提升 GPU 推理速度,可以导出 TensorRT:
model.export(format="engine", imgsz=640, half=True)7.2 C++ 部署思路
导出 ONNX 后,C++ 部署通常使用 ONNX Runtime 或 TensorRT。核心流程是:
- 加载 ONNX 模型创建推理会话。
- 将输入图片裁剪缩放到模型要求的分辨率。
- 转为 CHW 格式,float 类型,并做归一化。
- 执行推理。
- 将输出张量 reshape 为 1x1xHxW 的深度图。
- 根据实际范围将深度值映射到可视化区间。
ONNX Runtime C++ API 的最小推理片段如下(示意):
#include <onnxruntime_cxx_api.h> #include <vector> #include <algorithm> Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "yolo26-depth"); Ort::SessionOptions session_options; Ort::Session session(env, "yolo26n-depth.onnx", session_options); // 构造输入张量 std::vector<int64_t> input_shape = {1, 3, 640, 640}; Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu( OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input_tensor = Ort::Value::CreateTensor<float>( memory_info, input_data.data(), input_data.size(), input_shape.data(), input_shape.size()); // 运行推理 auto output_tensors = session.Run( Ort::RunOptions{nullptr}, input_names.data(), &input_tensor, 1, output_names.data(), output_names.size()); // 拿到深度图原始张量 const float* depth_data = output_tensors[0].GetTensorData<float>();C++ 部署需要注意三个问题:预处理逻辑必须和训练时一致;输出后处理要根据模型输出格式做通道维度和归一化处理;如果使用 TensorRT 导出,模型输入名称可能变化,需要在导入时确认。
7.3 RK3588 等边缘设备适配
RK3588 这类边缘设备目前是很多工业项目的落地目标。它们对 PyTorch 支持不友好,比较通用的路线是:训练结束导出 ONNX,再通过 RKNN 工具链转换为 RKNN 格式。
流程一般如下:
导出 ONNX(PyTorch 侧完成) -> 使用 RKNN 工具做模型转换和量化 -> 在板端使用 RKNN C API 或 Python API 推理转换时,最影响深度估计精度的环节是量化。深度图的数值范围跨度较大,如果简单做 uint8 量化,远近距离差异可能被压缩。建议保留至少一个输出层为 float16,或者使用混合量化策略,优先保证深度 head 的精度。
8. 常见问题与排查思路
YOLO26 深度估计虽然上手快,但实际开发中还是会遇到不少问题。这里整理几种高频情况:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 安装 ultralytics 后导入报错 | 与旧版依赖冲突 | 查看完整报错日志,检查 torch 版本 | 新建虚拟环境重新安装 |
| 推理输出深度图全黑 | 深度图动态范围窄,未做归一化 | 打印深度值 min/max | 使用 min-max 归一化或直方图均衡 |
| 深度图全白 | 输出中包含大量无效区域 | 检查原始深度图中是否有 NaN 或 0 | 在预处理阶段过滤无效深度值 |
| 训练 loss 下降缓慢 | 学习率过大或过小 | 尝试不同 lr0 初始值 | 使用预训练权重微调 |
| 训练时显存不足 | batch imgsz 设置过大 | 查看 nvidia-smi 显存占用 | 调小 batch 或 imgsz,开启梯度累计 |
| 边缘处深度模糊 | 模型对边缘监督不够 | 检查训练数据深度图是否清晰 | 提高深度图标注质量,增大边缘样本权重 |
| 导出 ONNX 后输出 shape 异常 | 模型配置的 imgsz 与导出不一致 | 检查导出参数 | 导出时显式指定 imgsz |
| RKNN 转换后深度精度下降 | 量化精度损失 | 检查量化配置 | 对深度 head 使用 float16 或混合精度 |
遇到问题时,第一步先看日志,第二步确认数据,第三步再怀疑代码。很多深度估计的“异常输出”其实是可视化问题或数据预处理问题,不是模型本身有问题。
9. 最佳实践与工程建议
9.1 数据层面
深度估计模型对数据质量非常敏感。训练数据里如果混入了深度标注缺失的图像,模型会在这些区域学到错误的输出。建议在训练前做一次全量数据检查:
- 确认每张深度图都能和原图对齐。
- 确认深度图没有大面积空洞。
- 确认深度值单位统一,不要混用米、毫米、厘米。
- 跨场景数据需要保持相机内参一致,至少要在配置中记录相机型号。
如果深度图来自 RGB-D 相机,还要注意同步问题。原图和深度图拍摄时间存在微小差异,会造成边缘错位,对精度影响很大。
9.2 训练层面
- 优先使用预训练权重微调,不要轻易从随机初始化开始训练。
- 使用
cache=True提前缓存数据集,可以显著减少训练时的图片加载瓶颈。 - 深度估计的建议初始学习率一般比检测低一些,如果 loss 振荡明显,可以降低 lr0。
- 训练过程中定期保存可视化深度图,用眼睛看比只看指标更直观。
- 指标上除了看 AbsRel、RMSE,也要关注
d1这类阈值准确率指标,因为对业务更可解释。
9.3 部署层面
- 深度估计模型导出前,建议在一个固定分辨率下训练和验证,导出时保持相同分辨率。
- CPU 部署优先考虑 ONNX Runtime,GPU 部署优先考虑 TensorRT。
- 边缘设备部署要尽早做量化验证,不要等模型训练全部结束后才开始转换。
- 如果深度图要用于后续测量,建议在业务代码中做中值滤波或边缘保持滤波,降低单帧预测噪声。
- 生产环境需要记录每一帧深度图的 min/max 和缺失率,一旦出现大面积无效输出可以及时告警。
9.4 安全与合规提醒
无论深度估计用于哪种场景,都要注意数据合规问题。采集真实场景的深度数据时,如果涉及人员、车辆、厂区等敏感区域,需要确认数据来源合法、脱敏到位。模型发布前,建议评估深度输出是否可能被用于安全敏感用途,并遵循最小授权原则,避免数据被滥用。
9.5 多任务部署注意
如果你在同一个模型中同时跑检测和深度估计,要注意两个 head 对输入分辨率的敏感度可能不同。检测在大分辨率上通常涨点明显,深度估计则更依赖特征细节。建议在实验阶段分别评估两个任务在imgsz=640和imgsz=1280下的表现,再做权衡。
10. 总结与下一步实践建议
YOLO26 把深度估计纳入 Ultralytics 统一体系,确实降低了视觉团队上手深度任务的门槛。核心收益可以概括为:一套环境、一套流程、一个模型,同时解决检测和深度预测需求。对于有具体业务场景的团队,这是值得验证的技术路线。
这篇文章里,我们完整走了一遍环境搭建、预训练推理、自定义数据集训练、yaml 结构调整、ONNX/TensorRT 导出、C++ 和边缘设备部署的思路。你可以照着这篇文章,先用自己的几张真实图片跑通预训练模型推理,感受深度图输出是否符合预期,再决定是否投入标注资源做微调。
如果你已经在用 RT-DETR-R18/R34 这类架构,探索把检测和深度估计分支做进同一个 yaml 是很好的进阶方向。不过建议先跑通官方标准任务,再动手改结构,避免一开始就陷入维度匹配的排错泥潭。
下一步,可以重点关注三件事:一是在你自己的业务数据上评估预训练模型的零样本效果;二是熟悉 Ultralytics 训练日志中深度估计相关指标的含义;三是提前设计好部署阶段的量化验证方案。这三个点都做扎实了,YOLO26 深度估计在项目里落地就会顺畅很多。