ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ZED + YOLO 双目测距:从原理到落地链路实战

ZED + YOLO 双目测距:从原理到落地链路实战 简介本资源面向计算机视觉初学者与机器人、自动驾驶方向的开发者提供ZED双目相机结合YOLO目标检测实现测距的完整工程代码核心运行脚本为zedceju.py。内容涵盖ZED SDK安装、numpy版本兼容处理、虚拟环境配置以及YOLO模型调用与深度距离计算逻辑可帮助读者快速搭建双目测距实验环境。资源包共42个文件包含20个Python脚本、5个XML配置、2个cfg与2个weights模型文件以及Makefile、CUDA源码、说明文档等压缩包约78.97MB目录结构清晰便于按模块查阅与二次开发。目前已有3004人学习下载适合希望掌握立体视觉与实时目标检测融合应用的读者参考实践。1. zed yolo 双目测距从 zedceju.py 看一条能跑通的落地链路双目测距这件事很多人第一次接触是在 ZED 这类双目相机上。它自带深度图输出看起来开箱即用但真到项目里你会发现纯靠相机 SDK 给的深度在远距离、弱纹理、反光面上经常飘尤其是要测某个具体目标人、车、零件的距离时整张深度图反而不好用。zedceju.py 这个脚本的思路很直接用 YOLO 先把目标框出来再在 ZED 提供的深度信息里取目标区域的深度换算成实际距离。这样做的价值在于你不需要自己写立体匹配也不用重新标定ZED 负责几何YOLO 负责语义两者一叠加就能得到一个「某个物体离我多远」的可用数字。这套方案适合做机器人避障、AGV 测距、工业现场目标定位的工程师也适合刚上手双目 检测、想先跑通一条完整链路再谈优化的人。下面我按自己实际搭这套东西的顺序把选型、代码、参数和踩过的坑讲清楚。2. 为什么是 ZED 出深度、YOLO 出框分工与选型理由2.1 双目测距的几何原理和 ZED 帮你省掉了什么双目测距的底层就是三角测量。左右两个相机有固定的基线 B同一个空间点在左右图像上的横坐标差叫视差 d深度 Z f·B / df 是焦距。理论上你自己拿两个工业相机、做极线校正、跑 SGBM 或 BM 立体匹配也能得到视差图但这条路的工程量在于标定要准、校正要稳、匹配参数要针对场景调弱纹理区域还会大面积空洞。ZED 系列相机把这一整套封装好了SDK 直接输出深度图深度以 32 位浮点存储单位是毫米你拿到的是已经算好的 Z。常见做法是直接用pyzed.sl里的retrieve_measure取MEASURE.DEPTH。这一步省下来的时间足够你把精力放在检测和业务逻辑上。所以选型上ZED 负责「几何正确」这是它的强项而「这是什么东西」交给 YOLO这是检测模型的强项。两者职责不重叠这是这套方案能成立的根本原因。需要提醒的是ZED 的深度精度随距离衰减很快。官方给的参考大致是0.33 米内比较可靠3 米以外误差开始明显超过 10 米基本只能当参考。所以如果你的场景是近距离测距这套组合非常合适如果是几十米开外得重新评估。2.2 YOLO 版本怎么选权重从哪来YOLO 这条线现在版本很多从 v5、v8 到 v11、v12 都有人用。做这套测距我一般推荐 YOLOv8 或更新版本原因是 Ultralytics 的 Python 接口足够简单model.predict()一行就能出框而且预训练权重覆盖 COCO 80 类人、车、常见物体都能直接测不用先训模型就能验证链路。权重获取上Ultralytics 会在你第一次调用时自动下载对应的.pt文件比如yolov8n.ptn 是 nano最轻量。如果你要测的是特定目标比如某种零件、某类工件那就得自己准备数据集训练这一步涉及标注、格式转换、训练配置属于另一条线本文不展开但链路是通的训练好的权重替换掉预训练权重即可。选 n 还是 s/m/l取决于你的算力。测距场景通常要求实时n 或 s 在普通笔记本 CPU 上也能跑到十几帧GPU 上更宽裕。先用 n 跑通再按精度需求往上换这是比较稳的顺序。2.3 zedceju.py 的整体数据流把整个脚本拆开看数据流是这样的初始化 ZED 相机打开深度模块循环抓取一帧同时拿到左目图像RGB和对应的深度图把左目图像送进 YOLO得到若干检测框对每个框在深度图的对应区域内取深度值通常取中位数或均值剔除无效值把深度值从毫米换算成米叠加到画面上显示。这里有个关键点YOLO 检测用的是左目图像深度图也必须取左目视角的深度两者像素坐标要对齐。ZED SDK 里MEASURE.DEPTH默认就是左目坐标系所以只要图像和深度图分辨率一致框的坐标可以直接用来索引深度图。如果分辨率不一致就得先做缩放对齐这是后面避坑章节要讲的一个点。3. 把 zedceju.py 跑起来环境、代码与参数3.1 环境搭建ZED SDK 和 Python 依赖ZED 相机要跑起来第一步是装 ZED SDK这个必须从官方渠道装版本要和你的 CUDA 驱动匹配。装完之后Python 侧需要pyzed它通常随 SDK 一起提供路径在 SDK 安装目录下的pyzed文件夹里需要手动加进PYTHONPATH或者用 SDK 自带的get_python_api.py脚本安装。YOLO 侧就是 Ultralytics# 安装 ultralytics会自动带上 torch 等依赖 pip install ultralytics # 验证 ZED 的 python 绑定是否可用 python -c import pyzed.sl as sl; print(pyzed ok) # 验证 yolo 是否可用 python -c from ultralytics import YOLO; print(yolo ok)逻辑说明ultralytics是 YOLO 的官方封装装它等于把推理、后处理、可视化都带上了。pyzed.sl是 ZED 的 Python 接口import成功说明 SDK 路径配对了。参数上没什么可调的这一步就是确认两个库都能 import任何一个报错都先解决环境别急着往下写代码。提示ZED SDK 对 CUDA 版本比较敏感装之前先确认显卡驱动支持的 CUDA 版本再选对应的 SDK 版本否则pyzed能 import 但一开相机就崩。3.2 打开 ZED 并取深度图的最小代码先单独把 ZED 这一半跑通确认能拿到深度再叠 YOLO。这样出问题好定位。import pyzed.sl as sl import numpy as np # 创建相机对象并设置初始化参数 zed sl.Camera() init_params sl.InitParameters() init_params.depth_mode sl.DEPTH_MODE.ULTRA # 深度模式精度优先 init_params.coordinate_units sl.UNIT.METER # 深度单位设为米 init_params.camera_resolution sl.RESOLUTION.HD720 # 分辨率 err zed.open(init_params) if err ! sl.ERROR_CODE.SUCCESS: print(相机打开失败:, err) exit(1) # 运行时参数 runtime sl.RuntimeParameters() image sl.Mat() depth sl.Mat() if zed.grab(runtime) sl.ERROR_CODE.SUCCESS: zed.retrieve_image(image, sl.VIEW.LEFT) # 左目彩色图 zed.retrieve_measure(depth, sl.MEASURE.DEPTH) # 深度图单位米 depth_np depth.get_data() # numpy 数组float32 print(深度图尺寸:, depth_np.shape) print(中心点深度:, depth_np[depth_np.shape[0]//2, depth_np.shape[1]//2]) zed.close()逻辑说明DEPTH_MODE.ULTRA是精度最高的模式代价是算力如果帧率不够可以降到PERFORMANCE。coordinate_units设成UNIT.METER后深度图里的数值直接就是米省得后面再除 1000。retrieve_measure取出来的depth_np是 float32 的二维数组无效像素是nan或inf取深度时一定要过滤。参数说明camera_resolution影响图像和深度图尺寸HD720 是 1280×720比较均衡要更高精度可以上 HD1080但帧率会掉。depth_mode在 ULTRA 和 PERFORMANCE 之间权衡实测 ULTRA 在近距离确实更稳。3.3 把 YOLO 检测框和深度图对齐取距离这是 zedceju.py 的核心逻辑。YOLO 出框框内深度取中位数。from ultralytics import YOLO import numpy as np model YOLO(yolov8n.pt) # 预训练权重首次运行自动下载 def box_distance(depth_np, box, min_valid0.1, max_valid20.0): 在检测框内取有效深度的中位数返回米 x1, y1, x2, y2 map(int, box) # 边界裁剪防止越界 h, w depth_np.shape x1, y1 max(0, x1), max(0, y1) x2, y2 min(w, x2), min(h, y2) if x2 x1 or y2 y1: return None roi depth_np[y1:y2, x1:x2] # 过滤 nan / inf 和超出合理范围的深度 valid roi[np.isfinite(roi)] valid valid[(valid min_valid) (valid max_valid)] if valid.size 0: return None return float(np.median(valid)) # 中位数比均值抗噪 # 假设 image_np 是左目 RGB numpy 数组 results model.predict(image_np, conf0.5, verboseFalse) for r in results: for b in r.boxes: xyxy b.xyxy[0].cpu().numpy() cls_id int(b.cls[0]) name model.names[cls_id] dist box_distance(depth_np, xyxy) if dist is not None: print(f{name} 距离 {dist:.2f} 米)逻辑说明box_distance先做边界裁剪因为 YOLO 的框可能贴边甚至略微越界直接索引会报错。然后过滤nan/inf再卡一个合理深度区间最后取中位数。用中位数而不是均值是因为框内难免混进背景像素均值容易被远处的异常值拉偏中位数更稳。参数说明conf0.5是置信度阈值测距场景宁可漏检也别误检因为误检会给出一个错误距离比没有更危险。min_valid/max_valid按你的场景设室内 0.110 米够用室外可以放宽到 20 米但要知道 ZED 在 10 米外的深度本身就不太可信。3.4 关键参数速查表参数位置推荐值说明depth_modeInitParametersULTRA精度优先帧率不够换 PERFORMANCEcoordinate_unitsInitParametersMETER深度直接输出米省换算camera_resolutionInitParametersHD7201280×720均衡confmodel.predict0.5测距宁漏勿误min_valid/max_validbox_distance0.1/10.0按场景调超范围深度不可信取深度方式box_distance中位数抗背景干扰这张表是我调这套东西时最常改的几个值先按推荐值跑通再根据实际表现微调。4. 避坑与排查双目 YOLO 测距最容易翻车的 5 个点4.1 现象距离数值乱跳同一物体一会儿 2 米一会儿 8 米原因框内混进了背景或远处的无效深度取均值时被拉偏或者深度图本身在弱纹理区域大面积无效中位数也不稳。解决先确认取的是中位数不是均值再把max_valid收紧到场景合理范围如果目标表面是玻璃、白墙这类弱纹理ZED 深度本身就不可靠这种情况要么换测距方式要么在目标上贴纹理标记。我一般会先把深度图可视化出来看一眼确认目标区域有没有有效值再谈算法。4.2 现象YOLO 框的位置和深度图对不上测出来的距离明显偏原因图像和深度图分辨率不一致或者用了右目图像去检测却拿左目深度去索引。ZED 的MEASURE.DEPTH默认是左目坐标系如果你retrieve_image取的是VIEW.RIGHT坐标就错位了。解决检测和深度都用左目。如果因为某些原因必须用右目图像那深度也要取右目视角或者做像素坐标映射。最稳的做法是全程左目分辨率统一用camera_resolution设定的值不要中途 resize 图像却不 resize 深度。4.3 现象一开相机就崩或者 grab 一直返回失败原因ZED SDK 版本和 CUDA 驱动不匹配或者相机被其他进程占用比如 ZED Explorer 还开着。解决先关掉所有可能占用相机的程序再确认 SDK 版本。zed.open()的返回码要打印出来看ERROR_CODE会告诉你具体原因。如果是驱动问题重装匹配版本的 SDK这一步没有捷径。4.4 现象帧率很低测距延迟大原因DEPTH_MODE.ULTRA本身吃算力YOLO 又占一份两个一起跑在 CPU 上必然慢。解决深度模式降到PERFORMANCE或NEURALYOLO 换更小的权重n 换到更小没有就保持 n确认 YOLO 跑在 GPU 上而不是 CPU。实测在带独显的机器上HD720 ULTRA yolov8n 能到 20 帧以上纯 CPU 就个位数了。4.5 现象近距离1 米内测距反而不准原因ZED 的最小工作距离有限制太近时左右目视场重叠不够立体匹配失效。解决确认目标在相机的最小工作距离之外ZED 2 大概 0.3 米具体看型号。如果场景就是超近距离这套方案不合适得换结构光或 ToF。这是选型边界问题不是调参能解决的。5. 进阶让测距更稳的几个具体技巧跑通之后真正决定这套东西能不能上项目的是稳定性。我踩过一圈之后留下几个习惯。第一深度取值不要只取一帧。同一目标连续多帧取中位数再对时间轴做一次滑动平均能压掉大部分抖动。代价是响应变慢但测距场景通常不需要毫秒级响应。实现上维护一个长度 5 的队列每次 push 新值取中位数即可。第二按目标类别设不同的深度策略。比如测人框的下半部分腿部深度通常比上半部分稳因为上半部分容易和背景混测车取框中心区域更靠谱。这个没有通用公式得按你的场景试。我一般会在box_distance里加一个roi_ratio参数控制取框内哪个子区域。第三把无效深度的比例作为置信度指标。如果框内有效深度像素占比低于某个阈值比如 30%这个距离直接丢弃不要显示。这比显示一个错误数字强得多。代码上就是valid.size / roi.size判断一下。第四YOLO 的conf和iou要一起调。iou控制重叠框的合并测距场景里同一个物体出两个框会导致两个距离取哪个都别扭。把iou适当调低比如 0.5让重叠框合并掉。第五长期跑的话给深度值加一个物理合理性校验。比如连续两帧距离突变超过 50%大概率是异常直接沿用上一帧的值。这是工程上的「后悔药」能避免画面上一秒 2 米下一秒 8 米的玄学跳动。这套 zed yolo 测距链路我自己的经验是跑通只要半天调稳要一周能不能上项目取决于你对场景边界的判断——ZED 的深度在它擅长的距离和纹理条件下很可靠超出边界就别硬撑。希望帮到你。本文还有配套的精品资源点击获取
返回列表