资讯动态

YOLOV5+双目视觉测距:从像素坐标到3D距离的完整实现

发布时间:2026/10/21 12:10:31 来源:尧图企业网站定制
简介基于YOLOV5与双目相机实现三维测距的新版资源面向自动驾驶、机器人导航、无人机避障等场景的开发者解决目标检测与深度估计结合时实时性与精度不足的问题。压缩包共141个文件约40.38MB以yaml/yml配置、py/pyc源码、sh脚本、md文档为主同时包含pt预训练权重、mp4演示视频、jpg样例和Dockerfile等覆盖配置修改、模型调用、环境部署与结果演示各环节。目前已有2420人学习下载。内容除YOLOv5-6.1工程代码外还提供模型权重、双目相机参数配置、图像对样例及Jupyter教程便于直接复现双目标注、深度图生成与三维坐标计算流程pyc缓存与docker配置可减少环境搭建成本。配合样例图片与视频可对照输出结果验证目标框选、视差计算与真实距离估计效果适合需要快速搭建双目测距原型的开发者和学习者。1. 当 YOLOV5 只给像素坐标距离从哪来一个双目测距项目的完整拆解之前做辅助驾驶预警项目时被单目测距坑过YOLOV5识别行人和车辆很准但距离全靠经验公式估车一颠坡一变误差就飞到天上。换成双目相机做立体匹配之后输出从像素框变成了相机坐标系下的3D坐标宽度、高度、距离全是实测算出来的不用猜任何先验。这个项目就是一套YOLOV5检测双目视觉测距的落地实现左右两个USB摄像头采集图像先标定内外参数做极线校正再用SGBM立体匹配生成视差图YOLOV5在左图上检测目标框最后从视差图对应区域提取深度并反投影出3D位置。适合正在做机器人避障、ADAS预警、安防周界以及任何既要认得出目标、又要知道目标在哪的开发者新手可以直接照这套链路跑通老手可以拿它的模块划分做二次开发。2. 技术选型与整体架构从双目视差到目标3D坐标的完整数据流先交代清楚技术栈里每个角色干了什么再讲数据是怎么一级一级传下去的。这套项目的核心依赖是 OpenCV、PyTorch 和 YOLOV5 检测模型。OpenCV 负责相机标定、去畸变、极线校正、SGBM 立体匹配这些计算机视觉底层的活PyTorch 负责加载和运行 YOLOV5 模型做目标检测最后的融合阶段把检测框和视差图对齐。模块之间的耦合度很低每一级都可以单独替换——这也是我把标定、匹配、检测拆成独立工具模块的原因。2.1 为什么最终选了双目而不是单目或结构光单目测距最诱人的地方是省钱一个摄像头就能跑但代价是它测不出绝对深度。常见做法是地面假设 目标真实高度去反推距离这在平直路面上勉强能用可一旦目标部分遮挡、姿态变化或者相机俯仰角稍有变动误差就会成倍放大。我之前那版单目方案在白天平路效果还行傍晚逆光或者雨天就彻底没法用属于典型的玄学调参换个场景就得重新调一套参数。双目测距的原理是三角测量两个相机从不同位置看同一个目标目标在左右图像上的像素位置会有差异这个差异叫视差。已知两个相机之间的基线长度和相机的焦距就能用视差算深度。它的优势在于深度是物理测量出来的不依赖任何目标先验同一个YOLOV5检测框里不管站的是人还是皮卡只要视差算得准距离就能对得上。结构光方案精度很高但工作距离一般在1米以内户外强光下还会被太阳光干扰。双目在0.5米到十几米都有可用距离而且民用USB摄像头就能干成本比激光雷达低一个量级。综合精度、成本、距离范围三个维度双目是这个场景下的最优解。说白了这套方案的性价比在于检测用现成的YOLO生态测距用开源计算机视觉库硬件只多花一个摄像头的钱。2.2 整体数据流六个环节逐级传递从相机读入一帧左右图到输出目标3D坐标中间经过六个环节采集左右摄像头同时曝光得到一张left_frame和一张right_frame最好用免驱动的USB UVC摄像头两个必须型号一致、分辨率一致、帧率一致。标定与去畸变用棋盘格标定出每个相机的内参焦距、光心和畸变系数再用cv2.undistort去掉镜头畸变不然图像的桶形畸变会让远处的视差计算直接偏差。极线校正stereoRectify把左右图像重投影到同一个平面上让左右图对应的特征点处在同一水平线上。这一步之后立体匹配才能用一行扫描的方式去搜索对应点。立体匹配对校正后的左右图做SGBM匹配得到每个像素的视差值除以16得到浮点视差再通过 z f*b/disp 转成深度图。目标检测YOLOV5模型在左图上推理输出每个目标的类别、置信度和矩形框坐标。融合输出把检测框映射到深度图上在框内提取有效的深度值取中位数作为目标距离结合相机内参反投影算出目标中心点的3D坐标。整个链路里最耗时的是YOLOV5推理如果用yolov5s模型和CUDA加速单帧推理大约在2到5毫秒SGBM虽然只是CPU实现但全图搜索也不慢640x480分辨率下大概几十毫秒。这个节奏做实时测距是够用的。如果后面要做更高分辨率的测距优先优化的是SGBM的搜索范围而不是换检测模型——检测模型已经有成熟的加速方案SGBM的耗时反而容易被忽略。2.3 目录结构拿到源码先按这个顺序读整个工程的目录划分是按功能走的没有把检测和测距代码混在一起。拿到源码建议按这个顺序读project/ ├── checkpoints/ # 存放YOLOV5权重文件如 yolov5s.pt ├── configs/ │ └── camera_config.py # 相机参数、SGBM参数的集中配置 ├── data/ │ ├── calibration/ # 标定用的棋盘格图片 │ └── test_video.mp4 # 测试视频 ├── utils/ │ ├── calibration.py # 标定、极线校正、参数保存 │ ├── stereo_matcher.py # SGBM立体匹配与深度图生成 │ ├── detector.py # YOLOV5模型加载与推理封装 │ └── geometry.py # 深度提取、3D反投影 ├── scripts/ │ ├── calibrate_camera.py # 离线标定入口 │ └── run_calibration_check.py # 标定结果可视化验证 ├── detect_3d.py # 主入口实时检测测距 └── requirements.txt配置集中放在camera_config.py里包含图像分辨率、标定文件路径、基线长度、YOLOV5权重路径、置信度阈值和SGBM参数。我做项目习惯把所有可能想调的数字都集中到一个文件里避免为了改一个阈值翻遍代码。改参数的时候只需要改配置文件核心代码一个字符都不用动。这个习惯在调试双目的时候尤其有用因为SGBM的参数一调就是一组散落在代码里根本记不住哪个是哪个。2.4 硬件准备两个摄像头怎么选才不给自己挖坑双目方案对相机一致性要求很高这里说几个血泪经验。首先两个镜头必须完全相同芯片型号、焦距、接口、分辨率都要一致最省事的是直接买厂家做好的双目模组它们出厂前已经做过对齐和固定基线固定好也省得每次测距都量。如果自己用两个USB摄像头拼最大的坑是自动曝光——左右相机各自根据环境自动调亮度导致同一目标在左右图上亮度不一致SGBM匹配精度直接崩。解决办法是在代码里关掉自动曝光强制设置相同的手动曝光值。其次基线长度决定测距范围基线越宽远处测距精度越好但近处的公共视野会变窄基线太窄远处的视差太小深度噪声很大。我的经验是室内机器人用60到90mm的基线室外车辆预警用200到300mm。这个值标定之后会算到相机外参里程序里也能手动配置到时候根据实际场景调整就好。如果你不确定选多长先按场景里最远需要测距的距离来估算基线长度大约是最近测距距离的十分之一到十五分之一。3. 核心模块拆解标定、立体匹配与YOLOV5深度提取的逐段实现这一章把六个环节里最难、最容易改错的三段代码拆开讲离线标定、SGBM立体匹配、检测框与深度融合。每一段代码都可以直接抄进自己的工程里改着用。如果你只想先跑通不想深究可以直接跳到第4章但后面调试的时候大概率还得回来看这一章。3.1 相机标定棋盘格照片怎么变成内外参数标定的本质是求解相机内参、畸变系数和左右相机的相对位姿。标定的输入是一组棋盘格照片输出是一个npz参数文件。先看单个相机的标定循环# utils/calibration.py 节选 def calibrate_single_camera(image_paths, pattern_size, square_size): criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-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) objp * square_size # 每个格子的实际边长单位毫米 obj_points [] # 世界坐标系下的棋盘格角点 img_points [] # 图像坐标系下的棋盘格角点 for path in image_paths: img cv2.imread(path) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners cv2.findChessboardCorners(gray, pattern_size, None) if not ret: continue corners2 cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) obj_points.append(objp) img_points.append(corners2) ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera( obj_points, img_points, gray.shape[::-1], None, None) return mtx, dist逻辑说明findChessboardCorners先粗检测角点cornerSubPix再在粗位置周围做亚像素精化这样角点坐标能到亚像素级别标定结果更稳。objp里每个角点的坐标是格子数 × 格子边长square_size必须用真实值它是后续所有3D坐标的尺度基准——如果这里填错了测出来的距离会整体按比例偏移这个错误非常隐蔽画面看起来一切正常就是距离差了一个固定的倍数。拿到左右各自的内参后还要做双目标定求解右相机相对左相机的旋转矩阵R和平移向量T这个R、T决定了极线校正的参数也是三角测量里基线长度的来源# 左右相机联合标定 ret, _, _, _, _, R, T, E, F cv2.stereoCalibrate( obj_points_l, img_points_l, img_points_r, mtx_l, dist_l, mtx_r, dist_r, image_size, flagscv2.CALIB_FIX_INTRINSIC)标定完成后把所有参数存成npz内参、畸变系数、R、T、以及stereoRectify算出来的映射表。常见做法是把映射表直接算好存起来运行时用cv2.remap查表校正比每帧现算快得多R1, R2, P1, P2, Q, roi1, roi2 cv2.stereoRectify( mtx_l, dist_l, mtx_r, dist_r, image_size, R, T, alpha0, new_image_sizeimage_size) map1_l, map2_l cv2.initUndistortRectifyMap( mtx_l, dist_l, R1, P1, image_size, cv2.CV_32FC1)这里再提醒一次标定照片要拍20到30张并且让棋盘格在画面里覆盖不同位置、不同倾斜角、不同距离。只拍正中央一种姿态的标定结果内外参全都不可用这是最容易把新手带进沟里的坑。标定文件丢失或者换了一个摄像头之后别偷懒复用旧参数——每个摄像头的光学中心和畸变都不同复用旧参数等于让测距结果随缘。3.2 SGBM立体匹配视差图是怎么算出来的极线校正之后左右图上同一个物理点在同一个水平线上接下来要做的是逐像素匹配。OpenCV的StereoSGBM_create是现成且效果稳定的选择def create_matcher(config): block_size config.sgbm_block_size # 建议 5~11必须为奇数 stereo cv2.StereoSGBM_create( minDisparityconfig.min_disparity, numDisparitiesconfig.num_disparities, # 必须能被16整除 blockSizeblock_size, P18 * 3 * block_size ** 2, P232 * 3 * block_size ** 2, disp12MaxDiff1, uniquenessRatio10, speckleWindowSize100, speckleRange32, modecv2.STEREO_SGBM_MODE_SGBM ) return stereo # 每帧计算视差图 disp stereo.compute(left_rect, right_rect).astype(np.float32) / 16.0 # 16是固定因子逻辑说明SGBM本质上是在水平方向做块匹配同时对整幅图像做能量最小化约束。numDisparities决定了搜索范围它越大能测的近距离越近但计算量也上升。blockSize是匹配窗口越大视差图越平滑但边缘越糊我一般先用11试边缘不行再降到7。P1和P2是平滑惩罚系数P2通常是P1的4倍这两个值影响深度图的连续性。disp12MaxDiff限制左右一致性检查的最大差值大于0就认为无效匹配可以过滤掉。uniquenessRatio要求最佳匹配的代价比其他候选低一定的百分比抑制纹理重复区域的误匹配。speckleWindowSize和speckleRange用来滤掉视差图上的小噪块——比如100像素范围内视差变化小于32的才认为是有效块其余置0。注意SGBM返回的原始值是整数要除以16才是真正的浮点视差单位是像素。这个16是OpenCV内部固定因子新手常常忘记除导致所有距离都偏小16倍——我在项目里专门写了个冒烟测试来抓这种低级错误。视差图转深度图就一行depth np.zeros_like(disp, dtypenp.float32) valid disp 1.0 depth[valid] f * baseline / (disp[valid] 1e-6) # f是左相机焦距fx, baseline单位需与square_size一致参数说明f来自标定的内参矩阵第0行第0列baseline可以用标定的T向量模长算出来单位与标定时square_size保持一致。这里加了一个1e-6的epsilon防止除以0。如果你的镜头是广角或者鱼眼SGBM的效果会明显变差这时候优先检查是不是真的完成了极线校正而不是急着调SGBM参数。3.3 YOLOV5检测把PyTorch推理封装成一行调用检测部分我习惯封装成一个detector类让主程序里只有一个model.infer(frame)接口。加载模型用本地YOLOV5工程而不是在线hub避免每次运行都要联网下权重import torch class Detector: def __init__(self, weights_path, conf_thres0.45, iou_thres0.45): self.model torch.hub.load( yolov5, # 本地仓库目录 custom, # 加载自定义权重 pathweights_path, sourcelocal, # 强制本地加载 force_reloadFalse) self.model.conf conf_thres self.model.iou iou_thres self.model.classes None # None表示识别全部类别 def infer(self, frame): results self.model(frame) # 内部自动做letterbox预处理 preds results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, conf, cls] return preds逻辑说明YOLOV5的xyxy输出是预处理后图像坐标系里的坐标而预处理letterbox会在原图边缘补灰边所以这个坐标跟原图不是一一对应的。很多人在把检测框映射到深度图时发现位置错位问题就出在忘了做letterbox的反变换。我的处理方式是用results.xyxy之前先记录原图的shape再用results.ims里保存的原始尺寸信息做还原更省事的做法是推理前自己手动resize成模型输入尺寸并记录缩放比例。参数说明conf_thres是置信度阈值检测远距离小目标时适当降低到0.3能多框出目标但误检也会变多iou_thres是NMS的IoU阈值目标重叠严重的场景可以调低到0.3。如果你用的是自己训练的权重建议先花半天在不同距离和光照条件下测试置信度分布再决定阈值不要拍脑袋填0.5。3.4 深度提取与3D坐标反投影检测框如何变成可用的距离这是整个项目最关键的一步也最容易出错。检测框的坐标来自YOLOV5的输入图深度图的坐标来自remap之后的校正图两者坐标系虽然都是像素坐标系但图像尺寸和缩放可能不一样。首先要把检测框映射到深度图的坐标系def extract_depth(disp, box, depth_scale1.0): x1, y1, x2, y2 [int(v * depth_scale) for v in box] roi disp[y1:y2, x1:x2] valid roi[roi 1.0] # 过滤无效视差 if valid.size 0: return None, None median_disp float(np.median(valid)) # 取中位数而不是均值 return median_disp, valid.size def disp_to_depth(median_disp, fx, baseline): if median_disp 0: return None return fx * baseline / median_disp逻辑说明为什么取中位数而不是均值因为目标边缘、反光区域和背景交界处经常混入少量误匹配的异常视差均值会被极端值拉偏中位数稳健得多。如果你在测试视频里看到距离值一帧一个样、跳得很厉害大概率就是用了均值还敢不滤噪。valid.size是有效像素数量我一般会要求它至少占检测框面积的20%否则直接判定距离不可靠。深度拿到之后目标中心的三维坐标通过相机内参反投影def project_to_3d(u, v, depth, fx, fy, cx, cy): x (u - cx) * depth / fx y (v - cy) * depth / fy return x, y, depth # 单位与baseline一致通常为毫米 # 目标中心点 cx_ratio (box[0] box[2]) / 2.0 / frame_width u int(cx_ratio * depth_width) v int(((box[1] box[3]) / 2.0 / frame_height) * depth_height) depth_value fx_real * baseline / median_disp x, y, z project_to_3d(u, v, depth_value, fx, fy, cx, cy)参数说明fx、fy、cx、cy全部取自标定得到的内参矩阵u和v是深度图坐标系下目标中心的像素坐标。这个3D坐标的单位由两个地方决定标定时的square_size和baseline的单位。如果square_size用毫米、baseline也从标定结果里取毫米值那最终的x、y、z单位就是毫米。输出之前如果想转成米除以1000即可。这里算出来的x和y是相对相机光心的横向与纵向偏移正值代表目标在光心右侧或下方。4. 在自己的相机上跑起来环境、标定流程与实时测距前面把原理和核心函数讲完了这一章是照着就能跑通的完整流程。从新建虚拟环境开始到标定自己的相机再到把YOLOV5权重接上跑出距离。4.1 环境准备Python依赖与YOLOV5仓库推荐Python 3.8到3.10PyTorch的CPU或CUDA版本都能跑但如果你要用实时摄像头测距强烈建议装CUDA版否则YOLOV5推理一帧要几百毫秒压根谈不上实时。依赖装在requirements.txt里python -m venv .venv source .venv/bin/activate # Windows用 .venv\Scripts\activate pip install torch opencv-python numpy pyyaml matplotlib pip install -r yolov5/requirements.txt # 如果本地有yolov5仓库参数说明opencv-python的版本建议4.5以上太老的版本没有StereoSGBM_create或者参数名对不上。torch版本根据你的显卡装对应CUDA版本比如CUDA 11.8装torch 2.0.1。装好之后运行python -c import cv2验证没报错再进入下一步。这里提一下我踩过的坑pip install torch默认装的是CPU版运行detect_3d.py时模型推理耗时显示几百毫秒我还以为是模型太大后来一看torch.cuda.is_available()返回False。所以环境装完第一件事先跑这个检查。另外如果你用的是两个摄像头插上电脑后先在系统里确认设备号不要想当然地认为左边设备号是0右边是1——USB枚举顺序经常乱画面上左右颠倒或者只有一个设备在线都是设备号配错了。4.2 标定自己的双目相机把两个相机固定好基线根据你要测的距离范围定。然后用棋盘格拍摄标定图片打印一张6x9或者7x10的棋盘格贴在完全平整的硬板上从不同角度、不同距离拍30张左右左右相机同步拍摄。文件命名用left_01.jpg、right_01.jpg这种方式方便脚本配对。python scripts/calibrate_camera.py \ --left_dir data/calibration/left \ --right_dir data/calibration/right \ --pattern_size 6 9 \ --square_size 24 \ --output configs/stereo_params.npz脚本会逐张检测棋盘格角点并显示可视化结果检不出来的图片会跳过。跑完之后看一眼终端输出的重投影误差一般小于0.5像素就算合格超过1像素就需要检查棋盘格是不是不平整或者照片数量不够。标定结果保存成npz之后运行scripts/run_calibration_check.py做一次极线校正可视化把左右校正图并排显示在图上画几条水平辅助线如果同一物体在左右图上的位置基本落在同一条水平线上校正就是成功的。这一步非常关键它验证的是从标定到remap的整条链路不是只看标定误差。提示极线校正验证这一步不能省。左右图对应点不在同一水平线后面所有视差计算都白做。4.3 实时测距主入口配置文件的参数确认无误后运行python detect_3d.py \ --weights checkpoints/yolov5s.pt \ --calib configs/stereo_params.npz \ --left_cam 0 --right_cam 1 \ --vis 1主程序流程是循环读左右帧 → remap校正 → SGBM算视差 → YOLOV5推理 → 检测框与深度图融合 → 在画面上绘制检测框和距离标签 → 按q退出。画面里每个目标框上方会显示类似 person 2.35m 的文本左下角实时显示当前帧的FPS。如果你手上没有双摄像头也可以用录制好的左右同步视频文件测试python detect_3d.py --left_video data/test_left.mp4 \ --right_video data/test_right.mp4 --vis 1用视频文件调试有一个好处摄像头实时预览时场景不可控很难复现同一个目标在同一个位置的测距效果视频文件可以反复暂停回放改一次SGBM参数跑一遍比较前后两帧的差异效率高很多。我建议把调试过程录下来当基准每次改参数都跑同一段视频用固定画面对比结果。4.4 调参与验证不同距离下怎么判断结果靠不靠谱拿到实时画面后最简单的验证方式是拿卷尺放在目标实际位置对比画面上的读数。最好分近、中、远三个距离段做测试——比如2米、5米、8米各测10次记录平均值和误差。实际距离(米)测距均值(米)误差说明2.02.042%近距离视差大SGBM表现稳定5.05.183.6%中间段基线够用误差主要来自角点定位8.08.455.6%距离越远视差越小误差进一步放大这个表格是我在某个项目里的实测记录数据会随相机基线和分辨率变化但趋势是一致的。如果你发现远处误差特别大优先加长基线、提升图像分辨率、增大numDisparities。如果你的场景是近距机器人避障300mm以上的基线会让近处目标超出公共视野此时反过来要缩短基线。要记住测距误差是距离的平方级增长的不要指望一个参数让全距离段都精准。5. 避坑指南双目测距最容易翻车的五个环节写代码和调优的时候最容易让人崩溃的往往不是算法本身而是某些环节的细节。这一章把常见的五个坑按现象→原因→解决列出来。5.1 坑一标定重投影误差很大但找不到原因现象标定输出的重投影误差超过1像素距离测量结果整体偏小或偏大。原因棋盘格贴在不平的纸板或弯曲的杂志上或者标定照片全部集中在画面中央相机发生轻微位移导致左右标定文件不匹配。解决把棋盘格用双面胶贴在玻璃板或铝合金平板上拍摄时让棋盘格出现在画面四角和中间远近交替拍。左右相机的标定图片必须是在同一时间内同步拍摄的帧不能左边拍了一套右边又拍另一套因为两个相机只要相对位置变了R和T就全不对了。5.2 坑二SGBM参数不合法直接抛异常现象运行到stereo.compute直接报错提示disp12MaxDiff或者blockSize非法。原因OpenCV对参数有硬性约束——numDisparities必须能被16整除、blockSize必须为奇数且大于1。解决在配置文件里做一次校验启动时检查参数再创建matcher。我的做法是写一个小守卫函数def validate_sgbm_params(cfg): assert cfg.num_disparities % 16 0, numDisparities必须是16的倍数 assert cfg.block_size % 2 1 and cfg.block_size 1, blockSize必须为奇数且1 return True这个校验放在构造函数里避免每次改配置都要翻源码才能发现参数错误。另外numDisparities也不要设得过大它和minDisparity加起来的范围如果超过图像宽度计算结果会浪费很多内存速度还慢。5.3 坑三左右画面亮度不一致导致视差全是噪点现象视差图看起来像雪花屏有效深度区域很少。原因两个USB摄像头都开着自动曝光左右各自适应亮度同一目标在左右图上的亮度差异很大SGBM的像素块匹配直接失效。解决在打开摄像头之后立即关闭自动曝光并设置固定曝光值。如果是普通UVC摄像头用cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0)关掉自动曝光然后手动设置曝光值比如100或200具体值以画面不偏暗不偏亮为准。这个坑在室内灯光和室外阳光下反差最大如果你看到视差图白天正常、傍晚全崩先检查是不是自动曝光又偷偷打开了。5.4 坑四检测框和深度图错位现象目标框画在深度图上的位置明显偏移或者目标距离时有时无。原因YOLOV5的letterbox预处理和SGBM的remap尺寸不一致检测框坐标没有映射回深度图坐标系。解决在detector类里不要直接使用xyxy的结果而是自己维护letterbox的缩放比例和padding偏移推理后再按比例映射回原图。更稳妥的方案是直接对remap之后的左图做检测这样YOLOV5输入的坐标系和深度图完全一致不用做任何映射# 推荐做法检测输入直接用校正后的左图 left_rect cv2.remap(left_frame, map1_l, map2_l, cv2.INTER_LINEAR) preds detector.infer(left_rect) # 检测框直接对应深度图坐标这个做法牺牲了一点检测精度校正后的图像边缘有裁剪但换来的是坐标对齐的简单可靠。如果你发现矫正图里目标形状被拉伸变形检查一下stereoRectify的alpha参数alpha0会让校正图自动裁剪掉非公共区域画面边缘的变形是正常的。5.5 坑五小目标和远距离测距完全失效现象YOLOV5框出了5米外的一个小瓶子或8米外的行人但距离标签不显示。原因目标在画面上只有几十个像素高对应的视差搜索窗找不到足够多的有效匹配深度图里那一块基本都是无效值。解决对这类场景有三个方向的调整——加大numDisparities提升近距离小目标的视差范围降低conf_thres到0.3让更多远目标被框出来给深度提取加一个最小有效像素数量阈值小于这个阈值就放弃测距并标记为距离不可靠避免输出噪声数据让下游控制器误判。如果你在室外场景测试顺光远距离的测距效果通常还可以一转到逆光小目标的有效深度就急剧减少。这不是代码bug而是摄像头动态范围不够换宽动态范围的摄像头或者降低测距目标尺寸要求才能缓解。6. 二次开发方向稳定测距、目标跟踪与轻量化部署项目跑通只是起点真正给业务用还得做三件事让测距值稳定、让目标持续可追踪、把推理延迟降下来。稳定测距最直接的手段是对深度值做时间域滤波。实测中即使取了中位数远距离目标的距离值依然会有小幅抖动。我一般会维护一个长度为5的滑动窗口取窗口内的中位数或一阶指数平均alpha0.3作为输出值。注意是先滤波再输出不是每帧直接用原始值这样控制端拿到的距离曲线才平滑下游做紧急制动或者抓取动作时不会因为距离跳变误触发。目标框中心点的位置同样会抖动如果在3D坐标上直接做差算速度噪声会被放大到不可用。更稳的做法是接入一个简单追踪器IOU跟踪或者卡尔曼滤波让每个目标有稳定的ID再用相邻帧的深度差和相机帧间隔算速度遇到遮挡时用追踪器的预测值补帧。卡尔曼滤波的参数不用调太复杂状态量取x、y、z和它们的速度就够用维度过高反而在目标快速加减速时跟不上。轻量化部署方面YOLOV5转ONNX再转TensorRT能在GPU上进一步提速或者换成更小的yolov5n。如果你跑在边缘设备上SGBM本身用CPU算也不会成为瓶颈真正的瓶颈还是检测模型把yolov5s换成yolov5n精度掉一点但FPS能上来一截。另外一个容易被忽略的优化点是SGBM的输入分辨率——如果业务不要求看到50米外的小目标把校正图缩放到640x360再算视差耗时可以下降一大半。最后说一个我从这次项目里养成的习惯双目测距的任何改动都要先在录制好的左右视频上跑一遍离线回归确认距离误差没有增大再接线到实时摄像头。因为实时调参很容易被现场光照和抖动干扰误以为没改好实际是环境变了。从那以后我每次改SGBM参数或者检测阈值都强制走一遍离线回放再上真机这套验证流程省下的调试时间比写代码的时间还多。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑