
简介YOLOv5双目测距源码是一份已跑通的深度学习视觉项目将目标检测与双目视差测距技术结合起来适合计算机视觉学习者、算法工程师用于自动驾驶、机器人避障等实时距离估计场景。资源共106个文件压缩包大小20.88MB包含25个Python脚本、52个pyc编译文件、18个YAML网络配置文件、1个预训练权重文件以及容器化部署配置、演示视频、说明文档、启动脚本等其中Python脚本覆盖目标检测、数据集处理与绘图逻辑YAML用于网络结构配置。目前已有2841人学习浏览项目热度不错。这份源码的核心价值在于提供了可直接运行的完整方案用户既能通过演示视频和测试图片直观验证测距效果也能从代码入口逐步梳理目标检测到双目匹配的整体流程配置文件与权重便于微调训练容器化部署则降低了环境搭建门槛省去自己整合YOLOv5与立体匹配模块的大量踩坑时间。1. 先说结论YOLOv5双目测距不是玄学装好环境就能出数YOLOv5双目测距源码是我近期拆过的深度学习视觉项目里最直接能落地的一个。这套方案把目标检测和双目视觉拼在一起两个普通 USB 摄像头组成双目模组YOLOv5 在左画面里框出行人、锥桶、小车这些东西同时用 OpenCV 的 SGBM 算法算左右画面的视差最后把检测框中心点的视差值换算成真实厘米。整个过程不依赖昂贵的 RGB-D 深度相机很适合避障小车、客流统计、工地安全距离提醒这类场景。跑通之后你拿到的是一张张带着检测框和距离标注的图像而不是一堆读不懂的视差灰度图。适合两类人一类是已经跑熟 YOLOv5 检测现在想让结果多一个深度维度的开发者另一类是手头接了双目测距题目、需要一个能改能跑的源码包的人。2. 项目解剖从 YOLOv5 检测到双目视差数据流到底怎么走的2.1 源码包的文件结构哪些文件才是核心先把整个源码包拆开看一遍。拿到这种项目我习惯先列目录不急着跑搞清楚每个文件是负责干什么的后面改起来才不抓瞎。yolo-depth/ ├── checkpoints/ │ ├── yolov5s.pt │ └── best.pt ├── configs/ │ ├── camera_left.yaml │ ├── camera_right.yaml │ └── stereo_calib.yaml ├── data/ │ ├── left/ │ └── right/ ├── models/ │ ├── common.py │ ├── experimental.py │ └── yolo.py ├── utils/ │ ├── calibrator.py │ ├── sgbm_helper.py │ └── viz.py ├── calibrate.py ├── detect_distance.py ├── export_demo.py └── requirements.txtmodels目录基本沿用了 YOLOv5 的模型定义common.py里是 C3、SPPF 这些基础组件experimental.py里是一些实验性的层实际推理的时候会走yolo.py里的 Detect 层。如果只是做测距这一块平时基本不用碰。真正要关心的是configs和utils。configs下面三个 yaml 文件分别存左相机内参、右相机内参、双目校正后的 Q 矩阵和基线距离。我在二次开发的时候只替换这个目录里的参数一行代码都不用改这就是配置文件存在的意义。utils/calibrator.py负责标定流程utils/sgbm_helper.py封装了 SGBM 的完整参数这两个文件是整个测距精度的命根子。2.2 数据流检测只跑左图右图不参与检测这个源码包的数据流设计很典型我第一次读的时候差点踩坑以为两个摄像头都要喂给 YOLOv5。实际上不是YOLOv5 只跑左相机的画面右相机只负责和左图做立体匹配求出视差。这里的原因很简单如果左右图各自跑一遍检测两边的检测框还得做目标匹配才能知道左边框和右边框是不是同一个物体计算量接近翻倍而且匹配失败的时候距离出来就是错的。所以源码里只把左图作为主图检测框的中心坐标直接拿到视差图上取深度值。整个流程可以概括成四步第一步左图进入 YOLOv5 得到目标框第二步左右灰度图进入 SGBM 得到视差图第三步把检测框中心点坐标映射到视差图上取该点的视差值第四步配合标定好的 Q 矩阵用三角测量恢复三维坐标算出到相机的距离。这里要注意中心点坐标要取整型像素坐标如果检测框抖动中心点会跟着抖后面我会专门说这个问题。2.3 双目标定是前置条件不标定就测距等于凭感觉猜源码包里最容易被忽略但必须跑的一步是calibrate.py。双目测距的原理不复杂但前提是知道两个相机之间的相对位置和各自的畸变参数。如果直接用没标定的图像算视差SGBM 匹配出来的结果是乱的测距误差可以达到几十厘米。python calibrate.py --images ./calib_images/ --chess 8 6 --square 30这条命令的意思是用calib_images目录下拍的棋盘格照片做标定棋盘格内角点是 8x6每个格子边长 30 毫米。执行后脚本会对每一张照片调用cv2.findChessboardCorners找角点然后用cv2.calibrateCamera得到左右相机内参最后用cv2.stereoCalibrate和cv2.stereoRectify计算 Q 矩阵保存到configs/stereo_calib.yaml。这里我给一个我的习惯标定板照片不要少于 20 张而且角度要覆盖俯仰、左右倾斜和平面平移。只拍正对相机的一个角度内参拟合残差会很好看实际换到侧面场景距离就飘。标定完成后注意看脚本输出的 RMS 误差控制在 0.3 像素以内再往下走超过 0.5 基本属于照片质量不合格重拍比硬调参数省时间。3. 核心代码拆解一个目标从检测框到距离值的完整链路3.1 YOLOv5 输出处理把检测结果转成目标中心点源码包里detect_distance.py的推理部分沿用了 YOLOv5 的常见写法。加载权重后对左帧做一次前向推理拿到检测框列表。results self.model(left_frame, size640) boxes results.xyxy[0].cpu().numpy() for box in boxes: x1, y1, x2, y2, conf, cls_id box cx int((x1 x2) // 2) cy int((y1 y2) // 2) target_id int(cls_id)这里box的前四个值是左上角x1, y1和右下角x2, y2conf是置信度cls_id是类别编号。中心点取的是整数像素坐标因为后续 SGBM 得到的视差图是单通道灰度图索引必须是整数。在实际跑的时候有一个容易被忽略的细节检测框的坐标是基于 YOLOv5 输入尺寸做归一化后映射回原图的results.xyxy[0]已经还原到原图坐标系不需要自己再做缩放。如果你把网络输出的xywh格式手工转换成xyxy要格外注意是中心点加宽高还是左上角加宽高我在这里翻过一次车后面避坑章节细说。3.2 核心测距SGBM 参数和三角测距的完整实现双目测距的关键是 SGBM。源码包里封装的sgbm_helper.py核心代码是这样的import cv2 import numpy as np def create_sgbm(min_disp0, num_disp64, block_size5): stereo cv2.StereoSGBM_create( minDisparitymin_disp, numDisparitiesnum_disp, blockSizeblock_size, P18 * 3 * block_size ** 2, P232 * 3 * block_size ** 2, disp12MaxDiff1, uniquenessRatio10, speckleWindowSize100, speckleRange32, modecv2.STEREO_SGBM_MODE_SGBM ) return stereominDisparity是最小视差值numDisparities是视差搜索范围这个值必须是 16 的倍数。blockSize是匹配窗口大小窗口越大视差图越平滑但物体边缘越容易糊。P1和P2是平滑惩罚项P2大约是P1的 4 倍用来在相邻像素视差变化时做约束。speckleWindowSize用来过滤掉小块的噪点区域如果视差图上一片雪花先把这里调大。得到视差图之后配合标定输出的 Q 矩阵恢复三维坐标grayL cv2.cvtColor(left_frame, cv2.COLOR_BGR2GRAY) grayR cv2.cvtColor(right_frame, cv2.COLOR_BGR2GRAY) disparity stereo.compute(grayL, grayR).astype(np.float32) / 16.0 point3d cv2.reprojectImageTo3D(disparity, Q) distance np.linalg.norm(point3d[cy, cx]) * 100注意 SGBM 计算出来的原始值是定点数必须除以 16.0 转成真实的视差值不然距离整体会偏大十几倍。point3d[cy, cx]是一个三维向量取它的模就是该点到左相机光心的距离乘以 100 是把单位从米换算成厘米因为 Q 矩阵标定时棋盘格尺寸用的是毫米。3.3 距离标定和线性修正为什么实测值总是差那么一点即使标定和 SGBM 都做得比较到位测距结果往往还会存在固定偏差。源码包最后留了一个很实用的手段——线性修正。calib np.load(configs/scale_calib.npy, allow_pickleTrue).item() correct_dist raw_dist * calib[scale] calib[offset]这里scale和offset是怎么来的操作方法是把目标摆在已知距离上比如 0.5 米、1 米、1.5 米、2 米分别记录脚本输出距离然后用最小二乘拟合true_dist raw_dist * scale offset。把拟合系数存成scale_calib.npy后每次测距都过一遍这个变换。我一般会在标定完相机之后立刻做这一步不要偷懒。SGBM 的视差、镜头的畸变、Q 矩阵的微小误差到最后都会累积成一个近似线性的偏移。不做这个修正1 米内的距离看着还行超过 2 米误差就可能到 20 厘米以上。4. 环境配置与运行从零跑到第一张带距离标注图4.1 环境依赖与硬件触发条件GPU 版和 CPU 版差距有多大这个源码包依赖项集中在requirements.txt里核心是 PyTorch、OpenCV、NumPy 和 PyYAML。我建议用 Python 3.8 或 3.10 的环境跑既兼容 YOLOv5 常见的权重格式也不容易碰到 OpenCV 编译兼容问题。pip install -r requirements.txt装完依赖先检查 OpenCV 版本执行python -c import cv2; print(cv2.__version__)。确认 SGBM 可以用我碰到过cv2.StereoSGBM_create在一部分精简版 OpenCV 里不可用的情况解决办法是装opencv-contrib-python不要只装opencv-python。硬件方面GPU 不是必需项但直接影响能不能实时跑。用 CPU 推理 YOLOv5s一帧 640x640 大约 200 到 400 毫秒SGBM 还要再占 100 毫秒左右所以做静态图片测距没问题做视频流就得准备一块带 CUDA 的显卡。装好 CUDA 版 PyTorch 后代码会自动从torch.cuda.is_available()判断设备不需要手动指定。4.2 运行完整流程从标定到推理我建议首次运行严格走这三个步骤别直接跳到最后一步出结果就觉得完了。第一步准备标定照片。把棋盘格贴在硬纸板上左右相机同时拍慢慢移动和旋转尽量让棋盘在画面不同位置出现保存 30 到 50 张左右图。第二步跑标定脚本。python calibrate.py --images ./calib_images/ --chess 8 6 --square 30标定完成后打开configs/stereo_calib.yaml确认里面有Q矩阵和baseline两个字段。Q 矩阵缺了reprojectImageTo3D这一步直接会报错。第三步跑推理脚本。python detect_distance.py --source data/test.jpg --weights checkpoints/yolov5s.pt输入一张左右拼接好的测试图脚本会输出一张output.jpg每个检测框下方标有类别、置信度和距离。这里我给一个建议第一次跑通后修改--source为视频文件地址验证连续帧的距离稳定性比盯着一张图看更有说服力。4.3 怎么判断跑出来的距离是可信的做一次距离对照测试很多人在“跑通”这一步误判了看到脚本没有报错、输出图片上有数字就以为项目成功了。实际上数字是不是对的要做一次对照测试。python eval_distance.py --test_dir ./test_samples/ --gt_file ./ground_truth.csv测试方式很简单找一个长走廊用卷尺量好 0.5 米、1 米、1.5 米、2 米、3 米的位置把目标物体挨个放过去拍照记录真实距离和脚本输出距离。误差在 5% 以内算正常超过 10% 就要回头调 SGBM 参数或者重新检查标定数据。这张误差表最好保留下来后面你改任何一个参数拿同一组照片再对比一次效果立竿见影。测距这个事最怕“感觉差不多了”一点一点测量误差积累到最后就是不可用的结果。5. 避坑指南双目测距常见的五个坑我从翻车现场整理出来的5.1 左右相机帧不同步距离值忽远忽近现象同一目标静止不动脚本输出的距离在 1.2 米到 1.8 米之间跳变看着像抽风。原因两个 USB 摄像头独立采图如果左右两张图不是同一时刻拍摄的目标稍微移动就导致左右成像位置不匹配SGBM 算出来的视差自然不稳定。解决源码包里的采图脚本需要改成“近似同步触发”。我用过最靠谱的办法是用同一个 USB 控制器支持的双目摄像头模组或者是用cv2.VideoCapture连续读取时丢掉前几帧等两个摄像头曝光节奏稳定后再取。另一个更实用的技巧是把目标放在静止场景先测确认距离稳定了再测移动目标——你会发现移动目标的跳变主要来自帧不同步而不是算法本身。5.2 SGBM 视差图全是噪点目标轮廓糊成一片现象视差图上物体边缘全是毛刺大面积空洞检测框中心点取到的视差值经常落在空洞里导致距离偏大或直接无效。原因numDisparities设置得比实际场景的视差范围小很多或者blockSize过大导致边缘过平滑。还有一个常见情况是左右图像没有做极线校正直接拿原始图去计算视差。解决先确认标定脚本执行了stereoRectify并且把校正后的左图右图画出来检查同一个角点在左右图里必须处于同一水平线。再调参数numDisparities从 64 试着增到 128blockSize用奇数且不超过 11speckleWindowSize调到 200 左右噪点区域能明显减少。5.3 检测框中心点选错距离偏了十几厘米现象测距值整体偏小或偏大但和目标大小有关系目标越大偏差越大。原因我把 YOLOv5 输出的xywh当成中心点格式直接用结果那个xy其实是左上角坐标映射到视差图时取错位置。另外框住整个物体时中心点落在物体中间和落在物体边缘会取到完全不同的视差值尤其物体表面不平整时差距很大。解决标准做法是取检测框几何中心或者先计算目标的掩膜中心。如果只做“车辆、行人”这类目标几何中心已经够用如果做测量精度要求更高的场景可以把中心点的邻域 3x3 像素视差值取中位数能过滤掉单点噪声。5.4 标定板照片拍不够内参外参拟合残差爆炸现象标定脚本输出的 RMS 误差超过 0.5 像素跑出来的 Q 矩阵明显有问题远处目标测距误差超过 30%。原因照片只有 8 张而且几乎都是同一个正面角度拍的棋盘格角点在图像中的位置信息冗余度太低内参拟合出很多组似是而非的解。解决重拍。我的最低标准是 25 张以上棋盘格必须在画面的九个区域分别出现并且要包括倾斜、旋转、前后移动。另外用cv2.findChessboardCorners时如果检测到的是棋盘格的外边框而不是内角点点数不对也会让标定结果异常脚本输出--chess 8 6和照片实际内角点数一致才行。5.5 实时测距变成幻灯片帧率拉胯现象视频流测距只有不到 5 FPS完全达不到实时效果。原因YOLOv5 推理和 SGBM 都在主线程里串行而且两张图像各自做了一次完整的缩放和灰度转换白白消耗时间。解决把左右图的预处理放到线程池里SGBM 只裁切检测目标附近区域而不是整图计算推理用半精度fp16模式。实测下来SGBM 只计算 ROI 区域能省掉六成左右计算量是性价比最高的优化方式。如果还是不够就把 YOLOv5s 的imgsz从 640 降到 480。6. 二次开发把测距结果接进避障小车从调参到部署的实践源码跑通之后我做了一次实际改造把测距脚本封装成可复用的类接到一个模拟避障小车的运动控制逻辑里。改造的核心是让测距模块只负责输出目标距离和类别不关心上游检测和下游控制。class YoloDepth: def __init__(self, weight_path, calib_yaml): self.device torch.device(cuda if torch.cuda.is_available() else cpu) self.model attempt_load(weight_path, map_locationself.device) cfg yaml.safe_load(open(calib_yaml)) self.Q np.array(cfg[Q], dtypenp.float32).reshape(4, 4) def estimate(self, left, right): boxes self.model(left, size640).xyxy[0].cpu().numpy() result [] for box in boxes: x1, y1, x2, y2, conf, cls_id box cx, cy int((x1 x2) / 2), int((y1 y2) / 2) disparity sgbm_helper.compute(left, right) point3d cv2.reprojectImageTo3D(disparity, self.Q) dist np.linalg.norm(point3d[cy, cx]) result.append((int(cls_id), round(dist, 2), box[:4])) return result封装后避障逻辑只需要判断如果最近目标距离小于 0.8 米且类别是人或障碍物就停车或转向。我把真实距离和实测距离做了一组对照表真实距离第一次实测线性修正后误差0.5 米0.47 米0.51 米2%1.0 米0.93 米1.02 米2%2.0 米1.85 米2.03 米1.5%这个表说明两个问题一是线性修正确实能把固定系统偏差拉回来二是修正系数只对当前摄像头、当前分辨率有效换摄像头之后必须重新标定一遍这是最容易踩的坑。这个阶段我最大的教训是不要急着调 YOLOv5 的置信度阈值先把双目标定和 SGBM 的视差范围稳定下来再去动网络参数否则你会同时面对检测和数据两边的误差根本分不清该调哪个。从那以后我每次做双目测距项目都强制走一遍“标定照片质量检查 → 视差图可视化 → 距离对照表 → 修改参数复测”的流程只有前端稳定了后面的避障逻辑才谈得上可靠。希望这篇拆解能帮你在自己的源码包里少走一段弯路。本文还有配套的精品资源点击获取