YOLOv5与双目视觉融合:从目标检测到三维测距的工程实践

发布时间:2026/10/26 2:04:33
YOLOv5与双目视觉融合:从目标检测到三维测距的工程实践 简介面向计算机视觉学习者与自动驾驶、机器人导航、无人机避障等方向的开发者这份资源给出将YOLOV5目标检测与双目相机深度感知结合的三维测距实现方案帮助读者在实时检测基础上进一步获取目标的三维空间位置属于需要一定深度学习与立体视觉基础的中高阶实践项目。压缩包共141个文件约40.38MB以37个py源码、40个yaml配置、22个pyc编译文件为主辅以yml环境配置、sh脚本、md文档及少量xml、jpg样例与pt权重、mp4演示覆盖数据预处理、模型训练、目标检测与深度计算等环节。已有2420人学习下载。读者可据此了解YOLOV5定位目标后由双目视差计算深度的完整链路借助预训练权重与样例图像对快速跑通流程并通过配置文件和文档理解立体匹配与深度图构建的实现细节为复杂环境下的三维测量与感知系统开发提供可复用的工程参考。1. 从单目到双目三维测距方案要解决的核心问题单目相机做测距本质上是在猜。一张图里没有绝对尺度目标远近全靠先验假设——知道物体真实高度、知道地面平面、知道相机俯仰角缺一个就翻车。我在一个模拟项目里试过用单目加目标框高度反推距离目标一蹲下、一转身误差直接翻倍这种玄学结果没法交付。双目方案换了个思路用两个相机之间的固定基线通过同一物点在左右图像上的像素视差把深度算成几何量而不是统计量。这套方案面向的是要做视觉测距毕设、课程设计或小型交付的读者核心链路是 YOLOv5 负责“找到目标”双目立体匹配负责“算出距离”两者拼成一条从检测到测距的完整流水线。2. 总体方案设计检测与测距两条链路的职责划分先把整条链路拆开看。这套系统不是“YOLOv5 直接输出距离”YOLOv5 只回答“目标在哪、是什么”距离由双目几何计算回答。两条链路在“目标框中心点”这个位置交汇检测链路给出左图目标框取框中心像素坐标测距链路用双目校正后的视差图在该坐标处取视差值代入深度公式得到 Z。职责划清之后每个模块的输入输出都是确定的调试时能单独定位问题出在哪一段。2.1 四个模块与数据流整个系统我一般拆成四个模块数据流是单向的方便逐段验证模块输入输出关键依赖图像采集与同步左右相机原始帧同一时刻的左右图像对相机触发方式、时间戳双目标定与校正棋盘格图像序列内参、外参、畸变系数、校正映射表标定板精度、拍摄覆盖度目标检测左目校正图目标类别与框坐标YOLOv5 权重、置信度阈值视差计算与深度反演校正后左右图 目标框中心目标三维坐标匹配算法、视差搜索范围数据流的关键约束是“同一时刻”。左右相机如果不是硬件同步触发运动目标在两张图里位置对不上视差就是错的。这一点在静态标定时看不出来一上动态场景立刻暴露。2.2 坐标系约定与深度公式深度反演用的是标准双目模型。设基线为 B两相机光心距离焦距为 f像素单位某点在左右图的横坐标差为视差 d x_left - x_right则深度Z f * B / d这里 f 和 B 的单位要统一到“像素”和“毫米”这类可换算的量纲。实际工程里 f 通常直接用标定得到的 fx像素B 用毫米算出来的 Z 就是毫米。X、Y 再由像素坐标和深度反投影得到X (u - cx) * Z / fx Y (v - cy) * Z / fy提示视差 d 出现在分母上d 越小深度越大、误差放大越厉害。远距离目标测不准是双目方案的物理上限不是代码 bug方案设计时要把有效测距范围写进需求。2.3 技术选型说明检测端选 YOLOv5 的理由很实际权重成熟、推理速度快、导出 ONNX 或 TensorRT 都方便毕设场景下在普通显卡上跑实时没问题。测距端用 OpenCV 的 StereoSGBM 做稠密匹配比 BM 算法在弱纹理区域稳一些代价是慢一点。如果目标表面纹理丰富、对速度要求高BM 也能用如果目标是大面积纯色物体SGBM 也会在无纹理区失效这时候要么加结构光要么退回“只测目标框内可靠视差的中位数”这种保守策略。选型没有绝对优劣取决于目标材质和实时性要求。3. 双目标定与校正把两台相机变成一台“虚拟相机”双目测距的精度上限由标定决定。标定不准后面匹配再精细也是白搭。这一步的目标是拿到左右相机的内参、畸变系数以及两者之间的旋转平移关系然后通过立体校正把左右图拉到同一水平线上——校正后同一物点在左右图的纵坐标相同匹配只需沿水平方向搜索视差计算从二维搜索降成一维。3.1 标定流程与参数含义标定用棋盘格左右相机同时拍摄同一块板在不同位姿下的图像一般 15 到 25 组覆盖画面四角和不同距离。OpenCV 的stereoCalibrate一次解出全部参数import cv2 import numpy as np # 棋盘格内角点数量例如 9x6 pattern_size (9, 6) objp np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32) objp[:, :2] np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) # 方格实际边长单位毫米必须和打印尺寸一致 square_size 25.0 objp * square_size obj_points, img_points_l, img_points_r [], [], [] # left_images / right_images 为同一时刻左右图路径列表顺序必须一一对应 for lpath, rpath in zip(left_images, right_images): img_l cv2.imread(lpath, cv2.IMREAD_GRAYSCALE) img_r cv2.imread(rpath, cv2.IMREAD_GRAYSCALE) ok_l, corners_l cv2.findChessboardCorners(img_l, pattern_size) ok_r, corners_r cv2.findChessboardCorners(img_r, pattern_size) if not (ok_l and ok_r): continue # 任一侧没找到角点整组丢弃不能只留一侧 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners_l cv2.cornerSubPix(img_l, corners_l, (11, 11), (-1, -1), criteria) corners_r cv2.cornerSubPix(img_r, corners_r, (11, 11), (-1, -1), criteria) obj_points.append(objp) img_points_l.append(corners_l) img_points_r.append(corners_r) # 标定结果左右内参、畸变、旋转 R、平移 T、本质矩阵 E、基础矩阵 F ret, K1, D1, K2, D2, R, T, E, F cv2.stereoCalibrate( obj_points, img_points_l, img_points_r, None, None, None, None, imageSize(640, 480), flagscv2.CALIB_FIX_INTRINSIC # 若已单独标定过内参可固定内参只解外参 )几个参数必须说清楚。square_size是打印出来的方格边长单位毫米量错 1 毫米深度就系统性偏 1 毫米的比例这是血泪经验。CALIB_FIX_INTRINSIC标志表示内参已单独标定、此处只解外参如果直接联合标定就去掉这个标志。T是右相机相对左相机的平移向量它的模长就是基线 B标定完第一件事就是检查这个值是否接近卷尺量出来的物理距离偏差超过 5% 说明标定有问题。3.2 立体校正与映射表拿到 R、T 之后用stereoRectify计算校正变换再用initUndistortRectifyMap生成映射表。映射表只算一次之后每帧直接remap不要每帧重算R1, R2, P1, P2, Q, roi1, roi2 cv2.stereoRectify( K1, D1, K2, D2, (640, 480), R, T, flagscv2.CALIB_ZERO_DISPARITY, alpha0 ) # 生成左右校正映射表只做一次 map1x, map1y cv2.initUndistortRectifyMap(K1, D1, R1, P1, (640, 480), cv2.CV_32FC1) map2x, map2y cv2.initUndistortRectifyMap(K2, D2, R2, P2, (640, 480), cv2.CV_32FC1) # 每帧使用 left_rect cv2.remap(left_img, map1x, map1y, cv2.INTER_LINEAR) right_rect cv2.remap(right_img, map2x, map2y, cv2.INTER_LINEAR)alpha0表示校正后只保留有效像素、裁掉黑边alpha1保留全部像素但带黑边。做测距建议用 0避免黑边区域参与匹配产生虚假视差。Q是重投影矩阵如果要做整幅图的点云重建会用到只测目标距离的话用深度公式就够。校正是否成功肉眼验证方法很简单在左右校正图上各画一条水平线同一物点应该落在同一行上偏了就是校正没做对。4. 检测与视差融合从目标框到三维坐标的落地实现这一段是整套方案的核心也是踩坑最多的地方。检测和测距各自都能跑通但拼在一起时坐标对不上、视差取错、目标框内混入背景任何一个都会让距离跳变。4.1 YOLOv5 推理与目标框中心提取YOLOv5 用 PyTorch Hub 或本地权重加载都行推理前把输入图统一到模型要求的尺寸。关键点是检测必须在校正后的左图上做因为后续视差图也是在校正图上算的两者坐标系必须一致。如果拿原始图检测、校正图上取视差坐标会整体偏移。import torch # 加载本地权重避免依赖网络 model torch.hub.load(ultralytics/yolov5, custom, pathweights/best.pt) model.conf 0.4 # 置信度阈值太低会引入误检目标 model.iou 0.45 def detect_center(left_rect): results model(left_rect) boxes results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, conf, cls] targets [] for x1, y1, x2, y2, conf, cls in boxes: cx int((x1 x2) / 2) cy int((y1 y2) / 2) targets.append({ bbox: (int(x1), int(y1), int(x2), int(y2)), center: (cx, cy), cls: int(cls), conf: float(conf) }) return targetsconf阈值设 0.4 是个折中设太高漏检、设太低误检具体值要拿自己的测试集调。框中心(cx, cy)是后面取视差的锚点但直接用单点视差容易受噪声影响更稳的做法是在框内取一块小区域做视差统计。4.2 SGBM 视差计算与目标深度提取SGBM 的参数直接决定视差图质量numDisparities必须能被 16 整除且要覆盖最近目标的视差。最近测距距离 0.5 米、基线 60 毫米、焦距 700 像素时视差约 840那numDisparities就得设到 864 以上设小了近处目标直接测不出。# SGBM 参数numDisparities 必须为 16 的倍数且大于最大可能视差 num_disp 16 * 6 # 96适用于中远距离近距离场景需调大 block_size 5 stereo cv2.StereoSGBM_create( minDisparity0, numDisparitiesnum_disp, blockSizeblock_size, P18 * block_size * block_size, P232 * block_size * block_size, disp12MaxDiff1, uniquenessRatio10, speckleWindowSize100, speckleRange2, modecv2.STEREO_SGBM_MODE_SGBM_3WAY ) def target_depth(left_rect, right_rect, center, K_left, baseline): gray_l cv2.cvtColor(left_rect, cv2.COLOR_BGR2GRAY) gray_r cv2.cvtColor(right_rect, cv2.COLOR_BGR2GRAY) disp stereo.compute(gray_l, gray_r).astype(np.float32) / 16.0 # SGBM 输出放大 16 倍 cx, cy center # 在中心点周围取 5x5 窗口过滤无效视差后取中位数 win disp[max(0, cy-2):cy3, max(0, cx-2):cx3] valid win[win 0] if valid.size 5: return None # 有效视差太少判定为测距失败不要硬算 d float(np.median(valid)) fx K_left[0, 0] Z fx * baseline / d return Zdisp除以 16 是因为 SGBM 输出的是定点数放大了 16 倍忘了除会得到 1/16 的距离这是新手最常见的翻车点。取中位数而不是均值是为了抵抗窗口内混入的背景视差。valid.size 5时返回 None 而不是硬算是因为无效视差区域算出来的距离完全是噪声宁可报“测距失败”也不能给个假数。4.3 结果输出与可视化测出 Z 之后把目标框、中心点、距离文字画回图上方便现场核对。三维坐标 (X, Y, Z) 按 2.2 的公式反投影单位统一为毫米或米。可视化时建议同时显示视差图调试阶段能一眼看出是检测错了还是视差错了——如果视差图在目标位置一片黑无效那距离跳变就是匹配失败导致的跟检测无关。5. 避坑与排查双目测距最容易翻车的五个点这一章按“现象 → 原因 → 解决”写都是我在模拟项目里真实遇到过的。现象一距离整体偏大或偏小但重复性很好。原因通常是基线 B 或方格尺寸square_size量错或者标定用的棋盘格打印时被缩放。解决用卷尺实测两相机光心距离和标定输出的np.linalg.norm(T)对比偏差超过 5% 重新标定打印棋盘格后用卡尺量实际方格边长不要信打印设置里的标称值。现象二静态目标测距稳定一动就跳变。原因是左右相机没有硬件同步运动目标在两张图里不是同一时刻的状态。解决优先用支持外触发同步的相机没有硬件同步时降低目标运动速度或对连续多帧距离做滑动平均但要清楚这只是掩盖问题不是根治。现象三近距离目标测不出返回 None。原因是numDisparities设小了最近目标的视差超出了搜索范围。解决按 4.2 的公式反算最近距离对应的视差把numDisparities设到比它大且为 16 的倍数同时注意numDisparities增大会拖慢匹配速度要在范围和实时性之间权衡。现象四目标框内距离忽远忽近视差图目标区域有黑洞。原因是目标表面弱纹理或反光SGBM 在无纹理区匹配失败。解决优先选纹理丰富的目标做验证工程上取框内有效视差中位数而非单点实在不行在目标附近补一个纹理投影但这会改变方案复杂度毕设阶段慎用。现象五检测框和视差图对不上距离系统性偏移。原因是检测在原始图上做、视差在校正图上算两套坐标系混用。解决统一在remap之后的校正图上做检测或者把原始图检测框用校正映射变换到校正图坐标前者更简单推荐前者。注意这五条里同步和坐标系混用属于“设计级”问题改起来伤筋动骨参数和阈值属于“调参级”问题改起来快。排查时先确认设计级没问题再动参数否则会在错误的方向上越调越远。6. 验收与答辩怎么证明这套测距方案是可信的验收不是“跑起来看着差不多”而是拿数据说话。我一般按三步走。第一步固定距离验证把目标放在 1 米、2 米、3 米、5 米处每个距离测 30 帧记录均值和标准差画一张“设定距离 vs 实测距离”的散点图理想情况是落在对角线上。第二步误差统计算每个距离点的平均绝对误差和相对误差双目方案在有效范围内相对误差控制在 5% 以内算合格超过 10% 要回头查标定和视差参数。第三步动态验证目标以已知速度沿光轴移动看距离曲线是否平滑跟随跳变点要能对应到视差图失效的帧。答辩时最容易被问的三个问题提前准备好答案一是“基线怎么定的”答清楚基线越长远距离越准但近距离盲区越大是权衡结果二是“有效测距范围是多少”用第一步的数据直接给区间别说“大概几米”三是“误差来源有哪些”按标定误差、匹配误差、同步误差三类说每类给一个量级。我习惯在方案里留一张误差预算表把各环节的理论误差列出来答辩时指着表讲比空口说“精度还行”有说服力得多。从那以后我每次做测距类项目都强制先跑一遍固定距离验证再谈其他功能因为距离曲线一对角线后面所有花哨的可视化才有意义。希望这套结构能帮到你照着搭一遍验收数据拿得出手答辩也就稳了。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询