☰
YOLOv5单目测距实战:从相机标定到距离估计代码全攻略
2026/10/10 16:08:23 网站建设 项目流程

简介:基于YOLOv5与单目测距的Python实现资源,面向计算机视觉开发者、自动驾驶与机器人导航方向学习者,解决单摄像头场景下目标检测与距离估计的融合问题。资源共132个文件,约13.87MB,RAR打包,包含40个yaml、34个py、16个pyc以及yml、xml、sh、md等类型,覆盖模型配置、训练与推理脚本、预训练权重、说明文档与辅助配置文件,整体结构清晰,便于二次开发与学习。已有3858人学习下载。读者可获得YOLOv5目标检测与单目深度估计的完整框架,结合预训练权重与示例图像,实践锚框机制、特征提取、深度映射以及物体框与距离信息的融合;借助Python源码可调整数据增强、锚框等参数,利用测试脚本在图片上输出检测框和距离估计结果,逐步掌握单目测距全流程。资源内还包含Dockerfile、Jupyter示例等,便于在不同环境复现,适合在智能监控、无人系统等场景中快速搭建原型。

1. YOLOv5+单目测距:一个自带“尺子”的检测方案,到底能测多准?

做视觉测距的兄弟应该都有同感:手里只有一台普通USB摄像头,想同时干“检测目标”和“量距离”两件事,不想上双目、不想上毫米波雷达,预算就几百块,那这就是你最现实的选项——用YOLOv5框出目标,再用单目几何把像素换算成米。这套方案在安防球机、果园巡检小车、河道漂浮物监测这些场景里特别常见,它不挑硬件,一张标定板和一台带摄像头的电脑就能起步。

但先把丑话说在前面:单目测距本质上是在“猜”距离,它依赖的是几何假设,不是激光雷达那种直接测量。你用YOLOv5+单目测距能测到什么精度,完全取决于你怎么标定相机、怎么选测距模型、怎么处理检测框的抖动。这篇不绕弯子,从相机模型的底层逻辑一直写到能跑的Python代码,把标定、参数、代码和坑都摊开讲,适合想快速落地又不想走弯路的人。

2. 从像素到米:单目测距的几何原理与相机模型

2.1 小孔成像与针孔模型:为什么单目能测距但要先标定

很多人第一次接触单目测距时都问同样一个问题:一个摄像头没有深度信息,凭什么能算出距离?答案是小孔成像。把相机想象成一个针孔,外界物体反射的光线穿过针孔打在感光元件上,物体的尺寸、它在图像中的像素大小、以及它到相机的距离,这三者之间有一个确定的三角关系。只要我们知道物体的真实尺寸,再量出它在图像里占了多少像素,距离就能反推出来。

这个关系用公式写出来极其简单:d = (W_real * f) / w_pixel,其中W_real是目标的真实宽度(或高度),w_pixel是目标在图像中对应的像素宽度,f是焦距(以像素为单位)。你可能会说,这不就是初中相似三角形吗?对,单目测距的全部核心就是这条相似三角形,但问题在于:f从哪里来?图像里的w_pixel为什么经常不准?这就是为什么必须先做相机标定——标定的目的就是把这个针孔模型的参数(焦距、主点、畸变)从“理想值”修正为“这台相机的真实值”。

不标定行不行?行,但误差会非常玄学。市面上买来的摄像头标称焦距是2.8mm或4mm,那是镜头的光学焦距,不是像素焦距;再加上镜头畸变,画面边缘的像素比例跟中心完全不同。我见过有人拿标称参数去算距离,1米以内的近距离误差还能看,超过3米直接偏出去半米多。所以动手写测距代码之前,先把标定这块地基打牢,否则后面所有东西都是空中楼阁。

2.2 四大坐标系与内外参:标定板与张正友标定的物理含义

相机标定绕不开四个坐标系:世界坐标系、相机坐标系、图像物理坐标系、图像像素坐标系。听着唬人,其实逻辑链很简单——世界坐标系里的一个三维点(Xw, Yw, Zw),先通过外参(旋转矩阵R和平移向量t)变换到相机坐标系,再通过内参(焦距fx、fy,主点cx、cy)投影到图像平面,最后加上畸变修正,得到像素坐标(u, v)。整个过程的矩阵形式就是经典的[u, v, 1]^T = K * [R|t] * [Xw, Yw, Zw, 1]^T,其中K就是内参矩阵。

张正友标定法是目前最常见的标定方案,只需要一个打印出来的棋盘格,从不同角度拍十几到二十张照片,算法就能自动提取角点并求解内外参。它为什么能成为事实标准?因为棋盘格的角点检测极其稳定,而且每个格子的大小是已知的(比如30mm),这就给算法提供了真实的物理尺度参照。你不需要昂贵的标定设备,一张A4纸打印的棋盘格贴在硬纸板上就能干活,这也是它被广泛采用的原因。

外参在单目测距里往往被忽略,但它其实同样重要。单目测距公式里其实隐含了目标平面与相机成像面平行的假设:目标正对着摄像头,距离是沿着光轴方向的。一旦相机有俯仰角,或者目标不在画面中央,公式就得重新推导。所以做标定时,除了拿到内参,最好顺手记录一下相机的安装俯仰角,后面处理远距离目标时会用得上。

2.3 两种落地路线:已知目标高度 vs 地面投影假设

实际写代码时,单目测距主要有两条路线,选哪条直接决定你代码的复杂度和适用场景。

第一条路线是“已知目标尺寸法”,典型应用是行人测距。人类的平均身高是已知的(比如1.7米),你用YOLOv5检测出行人框,用框的高度当作像素高度,套用d = (H_real * fy) / h_pixel就能算出距离。这套方案实现最简单,代码不超过十行,但它的弱点也很明显:检测框不仅仅是人的身体,经常包含一部分背景;而且行人弯腰、蹲下时真实高度不再等于1.7米,误差就会瞬间放大。

第二条路线是“地面投影法”,也叫地平面假设。它假设目标底部点落在地平面上,利用相机的高度、俯仰角和像素坐标,通过几何关系解算出目标到相机的水平距离。公式比第一种复杂一些,涉及d = H_cam / tan(theta + arctan((v - cy) / fy))这样带俯仰角的表达式,但它不依赖目标尺寸,对车辆、船、动物这类高度不确定的目标更友好。实际项目里,如果检测目标是行人,我一般先用第一种快速跑通;如果测的是车辆或者装在移动机器人上,我通常直接上第二种,避免目标尺寸方差带来的坑。

这两种路线并不互斥。理解它们的本质区别,你才能在自己项目里选对公式——看到“YOLOv5+单目测距”源码时,先分辨它用的是哪条路线,再决定是直接拿去用还是改造,这个分辨能力比背公式本身更重要。

3. 在本地跑通YOLOv5检测:环境、模型与检测输出解析

3.1 Python环境准备与cv2、numpy安装的版本坑

不管你是Windows还是Linux,第一步都是把Python环境备好。YOLOv5官方仓库对Python版本的要求不算苛刻,3.8到3.10都能跑得很顺。Windows用户去python官网下载安装包时记得勾选“Add Python to PATH”,否则后面在cmd里敲pip会提示找不到命令。装完之后打开终端,先把基础依赖装上再考虑PyTorch的事:

# 创建独立的虚拟环境,避免污染系统Python python -m venv yolo_env # Windows下激活 yolo_env\Scripts\activate # Linux下激活 source yolo_env/bin/activate # 先装基础库,注意opencv-python的版本 pip install numpy opencv-python

这里有一个常见翻车点:opencv-python和opencv-contrib-python不能同时装,否则cv2会被整出各种奇怪冲突。而且numpy版本和opencv版本之间也有隐性依赖,如果你用的是较新的opencv(4.8以上),numpy最好在1.24以上,不然导入cv2时会报_ARRAY_API not found之类的错。我一般建议先把这两个装好,再按YOLOv5仓库里的requirements.txt装PyTorch,这样能少踩很多环境的坑。

PyTorch的安装建议直接去pytorch官网按你的CUDA版本生成命令。如果没有独立显卡,装CPU版也够跑通流程和做推理,只是训练会慢很多。装完之后在Python交互环境里执行下面的验证命令,确认环境是干净的再往下走:

import cv2 import numpy as np import torch # 检查关键库的版本,确认都能正常导入 print("OpenCV version:", cv2.__version__) print("NumPy version:", np.__version__) print("PyTorch version:", torch.__version__) # 如果输出正常,说明环境基本可用

这段验证代码的作用是在正式跑YOLOv5之前就暴露环境问题。很多人跳过了这一步,直接跑detect.py,结果报错后根本分不清是环境问题还是代码问题,白白浪费半天。版本号不一定要跟我上面写的完全一致,只要大版本对得上、能正常导入就行——黑匣子里的报错大多不是算法问题,而是环境问题。

3.2 用YOLOv5官方仓库跑通检测:最小命令与关键参数

环境就绪后,克隆YOLOv5官方仓库并安装依赖,这是最稳妥的起步方式。官方仓库维护活跃,预训练权重可以直接从Release页面下载,不需要自己训练就能先看到检测效果:

# 克隆仓库并进入目录 git clone https://github.com/ultralytics/yolov5 cd yolov5 # 安装依赖,或者用requirements.txt里的完整列表 pip install -r requirements.txt # 跑一张官方示例图片,--weights会自动下载yolov5s.pt python detect.py --weights yolov5s.pt --source data/images/bus.jpg

执行完上面的命令,你会在runs/detect/exp目录下看到标注好的图片。第一次跑的时候它会自动下载yolov5s.pt权重文件,大约14MB,如果下载慢可以考虑用镜像源。这里解释一下几个核心参数:--weights指定模型权重,yolov5s是small版本,速度和精度的平衡点,适合快速验证;--source指定输入,可以是图片、视频文件、摄像头设备号(比如0表示第一个USB摄像头)或者一个目录;--conf-thres是置信度阈值,默认0.25,如果检测框太多或太少可以调这个值。

如果你想用自己的图片或视频来测,把--source换成文件路径就行。如果想把检测结果实时叠加距离信息,后面章节里我们会直接改detect.py或者写独立脚本调用模型,而不是在命令行层面做文章。先确保这条最小命令能跑通,产出一张带框的图片,再谈改造它做测距。

3.3 解析YOLOv5检测输出:拿到bbox、类别与置信度的代码

命令行能跑通只是第一步,做单目测距必须拿到检测框的像素坐标。YOLOv5预测结果的原始格式是[x_center, y_center, width, height, obj_conf, class_conf, class_id],但一般我们用的是非极大值抑制(NMS)之后的输出,也就是detect.py里det张量的xyxy格式——即每个检测框的左上角和右下角坐标(x1, y1, x2, y2)。

下面的代码展示了如何加载模型并解析每个检测框:

import torch # 加载预训练模型,autoshape会自动处理输入尺寸 model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) model.conf = 0.25 # 置信度阈值,低于该值的框会被过滤 model.iou = 0.45 # NMS的IoU阈值,控制重叠框的合并力度 model.classes = None # 限制检测类别,None表示检测所有80个类别 # 推理一张图片 results = model('data/images/bus.jpg') # results.pandas().xyxy[0] 是DataFrame格式的检测结果 det_df = results.pandas().xyxy[0] for _, row in det_df.iterrows(): x1, y1, x2, y2 = int(row['xmin']), int(row['ymin']), int(row['xmax']), int(row['ymax']) conf = float(row['confidence']) cls = int(row['class']) name = row['name'] # 计算检测框的像素高度和宽度 h_pixel = y2 - y1 w_pixel = x2 - x1 print(f"类别: {name}, 置信度: {conf:.2f}, 框高: {h_pixel}px, 框宽: {w_pixel}px")

这段代码就是把YOLOv5和单目测距“缝合”起来的关键桥梁。h_pixel和w_pixel就是测距公式里需要的输入。需要重点理解的是:model.conf和model.iou这两个参数直接影响检测框的质量——置信度阈值太低会出现大量误检框,太高会漏检;IoU阈值则影响重叠目标的框合并行为。我在项目里一般把conf设在0.25到0.4之间,具体值取决于场景的误检率和漏检率哪个更难接受。

另外注意results.pandas().xyxy[0]这个接口返回的是DataFrame,列名是xmin, ymin, xmax, ymax,跟原生的results.xyxy[0]张量是对应的。熟手可以直接操作张量,新手用DataFrame更直观。拿到检测框后,下一步就是把它喂给测距函数——如何标定相机、如何把h_pixel换算成米,这是本文第4章要解决的问题。

4. 把测距接进YOLOv5:相机标定、焦距计算与距离估计代码

4.1 张正友标定实操:用棋盘格拿到fx与畸变系数

标定这一步没有捷径,需要用棋盘格拍照片,然后用OpenCV内置的张正友标定API跑一遍。先用下面的脚本采集标定图片,注意棋盘格要占画面不同位置、不同角度,并且尽量让格子纹理清晰:

import cv2 import os # 棋盘格参数:内角点数量,注意是角点数不是格子数 CHESS_COLS = 9 # 棋盘格列内角点数量 CHESS_ROWS = 6 # 棋盘格行内角点数量 SQUARE_SIZE = 0.03 # 每个格子的边长,单位:米(30mm) cap = cv2.VideoCapture(0) save_dir = "calib_imgs" os.makedirs(save_dir, exist_ok=True) count = 0 while count < 30: ret, frame = cap.read() if not ret: continue gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 寻找棋盘格内角点 found, corners = cv2.findChessboardCorners(gray, (CHESS_COLS, CHESS_ROWS), None) if found: # 在画面上画出角点供确认 cv2.drawChessboardCorners(frame, (CHESS_COLS, CHESS_ROWS), corners, found) # 按空格键保存当前帧 cv2.imshow("Calibration", frame) if cv2.waitKey(1) & 0xFF == ord(' '): cv2.imwrite(f"{save_dir}/calib_{count:02d}.jpg", frame) count += 1 print(f"已保存 {count}/30 张标定图") else: cv2.imshow("Calibration", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

采集完成后,用下面的脚本完成标定并保存相机参数:

import cv2 import numpy as np import glob CHESS_COLS = 9 CHESS_ROWS = 6 SQUARE_SIZE = 0.03 # 准备棋盘格对应的世界坐标点 objp = np.zeros((CHESS_ROWS * CHESS_COLS, 3), np.float32) objp[:, :2] = np.mgrid[0:CHESS_COLS, 0:CHESS_ROWS].T.reshape(-1, 2) objp *= SQUARE_SIZE obj_points = [] # 世界坐标点 img_points = [] # 图像坐标点 images = glob.glob("calib_imgs/*.jpg") for fname in images: img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, (CHESS_COLS, CHESS_ROWS), None) if ret: obj_points.append(objp) # 亚像素角点精化,提高标定精度 corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) img_points.append(corners2) # 执行标定,获得相机内参和畸变系数 ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, gray.shape[::-1], None, None) print("内参矩阵 fx, fy, cx, cy:") print(f"fx = {mtx[0, 0]:.4f}") print(f"fy = {mtx[1, 1]:.4f}") print(f"cx = {mtx[0, 2]:.4f}") print(f"cy = {mtx[1, 2]:.4f}") print(f"畸变系数: {dist.ravel()}") # 保存标定结果到文件,后面测距代码直接读取 np.savez("camera_params.npz", mtx=mtx, dist=dist)

这段代码最需要关注的是fx和fy两个值,它们就是测距公式里的像素焦距。理想情况下fx和fy应该接近相等,如果差异过大,说明棋盘格照片拍得不够好或者镜头畸变严重。标定结果直接保存成npz文件,测距脚本里用np.load读回来就行——这样做的好处是标定只做一次,运行时免去重复计算的开销。

4.2 核心测距代码:基于目标高度的单目距离估计

拿到相机内参后,测距代码就非常简洁了。下面这个函数实现了“已知目标物理高度,利用检测框高度估算距离”的完整逻辑,直接对接上一章的检测结果:

import numpy as np def estimate_distance_by_height(h_pixel, f_pixel, h_real_meter): """ 通过目标物理高度和检测框像素高度计算距离 :param h_pixel: 检测框的高度,单位:像素 :param f_pixel: 相机焦距(通常用fy),单位:像素 :param h_real_meter: 目标的真实物理高度,单位:米 :return: 估计距离,单位:米 """ if h_pixel <= 0: return -1 # 相似三角形:距离 = (真实高度 * 焦距) / 像素高度 distance = (h_real_meter * f_pixel) / h_pixel return distance # 读取上一章标定得到的相机参数 calib_data = np.load("camera_params.npz") mtx = calib_data["mtx"] fy = mtx[1, 1] # 示例:假设检测到行人,真实身高取1.7米 H_REAL = 1.7 # 行人平均身高,单位:米 h_pixel = 245 # 这里替换为YOLOv5检测输出的实际框高 distance = estimate_distance_by_height(h_pixel, fy, H_REAL) print(f"估计距离: {distance:.2f} 米")

这段代码就是整条链路的最后拼图。fy之所以放在函数参数里,是因为不同相机、不同分辨率下焦距不同。需要注意:由于YOLOv5检测框的坐标是相对原始输入图像分辨率而言的,所以fy也必须是从原始分辨率标定得到的值。如果在detect.py里做了imgsz=640的resize,那么像素高度也要对应到640分辨率下的值,或者提前把fy按缩放比例换算,否则结果会整体偏移。

一个实用的改造是把上一章的检测循环和这里的测距函数结合,实时计算出画面中每个目标的距离并叠加到画面上。常见做法是引入一个目标跟踪器(比如ByteTrack),为每个目标分配ID并维护预测距离的历史值,用EMA(指数移动平均)滤波平滑距离输出,避免单帧检测抖动带来的距离跳变。

4.3 参数怎么调:近距离、远距离、俯仰角对结果的影响

测距代码跑通后,你会很快发现调参的乐趣和痛苦。首先是近距离(1米以内)的情况:这时目标占画面比例很大,检测框高度的几个像素误差对距离计算的影响相对小,但镜头畸变的影响会被放大,尤其是画面边缘的目标,误差可能达到10%以上。解决方法有两种:一种是对图像做畸变矫正(用标定得到的dist系数做cv2.undistort),另一种是把目标尽量保持在画面中心区域测距。

远距离(10米以上)是单目测距最脆弱的工作区间。以行人测距为例,当距离为20米时,一个1.7米高的人在640分辨率画面里可能只有20到30像素高,检测框上下浮动3到5像素就会造成15%以上的距离误差。这不是调参能解决的,而是几何条件决定的分辨率极限。常见做法是提高输入分辨率,比如把--imgsz从640调到1280,或者换用检测精度更高的yolov5m/yolov5l模型,让检测框更稳定。

还有一个常被忽略的参数是相机的俯仰角。如果你把摄像头装在高处向下俯视,行人检测框的底部点并不在地面垂直投影处,直接用相机光轴方向的深度公式会产生系统性偏差。这时的正确做法是用4.3开头提到的地面投影模型,把相机高度H_cam和俯仰角theta代入几何公式。这也就是为什么我在第2章强调“两条路线”的原因——参数调不明白的时候,回头看公式,可能问题就出在你选错了测距模型。

5. YOLOv5+单目测距实战避坑:5个必踩的经典坑

5.1 坑一:检测框高度不等于目标物理高度——检测框的暴力截断问题

现象:行人明明站在3米外,测出来距离是2.5米,误差20%以上。反复调整焦距和置信度阈值都没用。原因:YOLOv5输出的框是矩形,它框住的是“整个行人的视觉范围”,可能包含头顶上方的空白和脚部以下的阴影背景;当目标部分出画面或被遮挡时,框的高度更会直接“缩水”,导致距离被高估。解决:不要直接用完整框高,而是先对目标的真实高度做一次标定修正——在几个已知距离(比如2米、4米、6米)拍目标,反推出一个“等效物理高度”,用这个值替代经验值。等效高度相当于把检测框的系统性偏差吸收掉了,虽然不完美,但比直接套1.7米靠谱得多。

5.2 坑二:标定棋盘格拍得越多越好?照片分布不均导致的内参偏移

现象:标定重投影误差很低,但实际测距在画面边缘区域的误差就是比中心大很多。原因:照片虽然拍了30张,但棋盘格总是出现在画面中心附近,边缘区域的畸变没有被充分采样,标定算法对边缘畸变的拟合就是“瞎猜”。解决:重拍标定照片,确保棋盘格覆盖画面的九宫格所有区域——左上、右上、中左、中右、中心、左下、右下都要有,并且包含明显的前倾、后仰等角度变化。一个直观的检验标准:标定完之后,棋盘格放在画面四个角落拍摄时,角点重投影误差应该都和中心位置相当,而不是“角落更差”。

5.3 坑三:距离越远误差越大,不是算法问题而是几何条件退化

现象:近距离测得很准,到了15米外误差动辄数米,你以为是代码写错了。原因:单目测距的误差与距离的平方成正比。假设检测框高度的像素误差为delta_h_pixel,距离误差约等于(d^2 * delta_h_pixel) / (H_real * f_pixel)。距离翻倍,误差翻四倍;远距离下检测框只有几个像素高,一个像素的框高波动就是很大的距离变化。解决:认清单目测距的工作区间,不要把它的有效距离硬顶到20米以上。如果一定要测远距离,考虑换更高分辨率相机、更大焦距镜头、更高精度模型,或者接受误差并做距离分段加权——近距离信任测距结果,远距离只给“大于某个值”的模糊估计,别输出一个假的精确数字误导下游逻辑。

5.4 坑四:相机有俯仰角时公式直接失效——光轴方向不是水平方向

现象:把摄像头装在2米高的杆子上向下俯视,近距离测距还行,到了5米外距离突然变得忽近忽远。原因:你还在用d = (H_real * fy) / h_pixel这个公式,它假设相机光轴与目标平面垂直,但俯视安装时光轴是斜的,目标像素高度和真实距离不再是简单的相似三角形关系。解决:改用地面投影模型,公式变为d = H_cam / tan(theta + arctan((v - cy) / fy))。其中H_cam是相机离地高度,theta是相机俯仰角,v是检测框底部中点的像素纵坐标。这个模型不依赖目标高度,只依赖目标脚尖落地的假设,对行人和车辆都适用。实测下来,俯仰角标定精确到0.5度以内时,5到10米距离误差能控制在5%左右。

5.5 坑五:距离输出抖动严重——单帧检测不够稳,要加滤波

现象:目标站着不动,测距结果在2.8米和3.4米之间来回跳,视频里距离数字跳得人眼晕。原因:YOLOv5单帧检测框不是静止的,目标姿态、光线变化、轻微遮挡都会让框高波动几个像素,这些波动直接传导到距离结果。解决:两步走。第一步对检测框高度做时间维度的滤波,典型做法是用指数移动平均(EMA):h_smooth = alpha * h_current + (1 - alpha) * h_smooth,alpha取0.3到0.5比较合适。第二步是配合目标跟踪器,为每个目标分配ID并单独维护滤波状态,避免多目标场景下距离值串号。我见过有人直接对距离值做均值滤波,效果也可以,但响应延迟会变大,要注意取舍。

6. 进阶落地:在RK3568或树莓派4B上跑通YOLOv5单目测距

6.1 模型量化与部署:YOLOv5s转ONNX再量化的常见做法

如果你不满足于在电脑上跑演示,而是想装到RK3568开发板或树莓派4B上做边缘部署,那就要认真考虑算力问题了。YOLOv5s在RK3568上直接跑PyTorch推理是没有意义的,必须走“训练/导出ONNX→量化→NPU推理”的链路。RK3568的NPU通常采用RKNN工具链,流程是把.pt权重先导出为ONNX:

# 在YOLOv5仓库目录下执行导出 python export.py --weights yolov5s.pt --include onnx --opset 12

导出后用RKNN-Toolkit2做模型转换和量化,量化一般用int8,校准数据集选200张跟实际场景接近的图片。这里有两条产品血泪经验:第一,模型量化后精度会掉,尤其小目标检测,所以训练时建议用yolov5s起步而不是更小的yolov5n,给量化留出精度余量;第二,板端NPU对某些算子支持不完整,导出前把YOLOv5的--opset固定到12是一个相对稳妥的选择,遇到不支持的算子再回退版本,不要盲目追新。

树莓派4B没有NPU,只能靠CPU跑,建议用yolov5s的ONNX版本在ONNX Runtime下运行,实测640分辨率大概能做到每秒5到8帧,这个速度做一些低速巡检够用。如果想让树莓派更快,可以考虑换用更轻量化的模型结构,或者把输入分辨率降到416。核心理念是:边缘部署的瓶颈从来不只是模型精度,而是算力、帧率和检测精度的三角平衡,先想清楚你要的帧率就能倒推出模型规模。

6.2 现场验证方法:用标定物和误差曲线判断系统能不能用

部署完成后,第一件事不是看效果,而是做一次系统的距离误差标定。方法很简单:找一个已知高度的标志物(比如身高1.7米的同事,或者一个固定高度的纸箱),从1米到20米每隔1米测一次,记录算法输出的距离和实际距离,然后画一条误差曲线。

根据这条曲线你会看到几个关键指标:最小有效距离、最大有效距离、全程误差的均值与标准差。我在项目里通常以“误差≤10%”作为可用区间,落在区间内的距离才允许输出给下游逻辑,超出区间就直接标记为无效。很多方案翻车不是因为算法跑不通,而是因为没有做这一步现场验证,直接把实验室里偶尔准的测距值当成了真实产品的输出,上线后用户一比测就露馅了。误差曲线还有一个额外好处:它可以帮助你发现系统性的偏差规律,比如“5米处总是偏近20厘米”,这种情况往往是等效高度的修正值没设好,而不是随机噪音。

6.3 最后的一条经验:先跑通最小闭环,再谈优化

如果你准备启动一个YOLOv5+单目测距的项目,我最想留给你的一条建议是:先不要优化,先跑通最小闭环。最小闭环的定义是——摄像头实时出图,YOLOv5实时出框,测距公式实时出距离,三个环节连起来能在屏幕上稳定显示目标距离。这个闭环不需要很准,但一定要完整跑通。

很多工程师包括我自己都有过这样的经历:一上来就想做多目标跟踪、做距离平滑、做畸变矫正,结果调了一个月还没有一个能现场演示的版本,最后在汇报时拿不出手。正确节奏是先用最小闭环验证整条管线的合理性,确认数据流没有断点,然后再一个环节一个环节地优化。做标定修正、加跟踪滤波、换更高精度的模型,这些优化都是锦上添花,但前提是“锦”已经织好了。这个方向值不值得做,其实答案很清楚——只要有预算限制和实时测距需求,YOLOv5+单目测距就是当前性价比最高的方案之一,关键是别让那些坑把你的耐心耗尽。希望这篇能帮你少走几段弯路,也希望以后你回来说一句“单目测距也就这么回事”的时候,是真的把那些坑都填平了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询