简介:这份资源是经导师指导并获98分认可的毕业设计项目,围绕YOLOv5实现单目测距系统,面向计算机相关专业正在做毕设、课程设计或期末大作业的学生,也适合需要项目实战练习的学习者。项目已通过严格调试,可直接作为毕设使用。压缩包共78个文件,约215.32MB,以28个Python源码、26个YAML配置、6个Shell脚本为主,另含XML工程配置、JPG示例图、MP4演示视频、Dockerfile、预训练权重pt文件及说明文档,覆盖模型训练、推理检测与测距计算等环节。目录中可见detect.py、train.py、distance.py、realsensedetect.py等核心脚本,以及models、utils、data等模块,便于理解YOLOv5检测与单目测距的完整链路。目前已有180人学习下载,适合希望快速搭建可运行项目、对照源码梳理检测与测距实现思路的读者参考。
1. 从一份 98 分毕设拆起:yolov5 单目测距系统到底交付了什么
如果你正在做计算机方向的毕业设计,或者需要一个能写进简历的视觉项目,大概率刷到过「基于 yolov5 的单目测距系统」这类资源。我拿到这份压缩包的第一反应不是看代码,而是先确认它是不是一个能跑通的闭环:检测、测距、可视化、数据、权重,缺一个都只能算半成品。拆完之后结论是——它把 yolov5 检测和单目测距串成了一条完整链路,detect_A4.py、realsensedetect.py、distance.py三个文件分别对应 A4 纸标定、真实场景检测、距离解算,配合best.pt权重和A4.mp4测试视频,基本能做到解压即跑。
这份资源适合三类人:一是毕设选题卡在「目标检测 + 测距」方向、需要现成框架的学生;二是想拿一个完整项目练手、理解单目测距工程落地的学习者;三是课程设计或期末大作业需要交付可演示系统的同学。它不适合想从零手推公式的人,也不适合指望直接拿去发论文的场景——它的价值在于工程闭环,不是算法创新。下面我按「资源结构 → 环境搭建 → 测距原理与标定 → 训练与推理 → 避坑 → 进阶验证」的顺序,把这份包拆开讲透。
2. 资源结构与运行环境:先搞清楚每个文件干什么
2.1 目录拆解与核心文件定位
拿到压缩包别急着pip install,先花五分钟把目录结构过一遍,能省掉后面大量「文件找不到」的玄学问题。这份资源的目录大致分四块:yolov5 主干代码、测距相关脚本、配置与权重、测试素材。
主干部分就是标准的 yolov5 仓库结构,models/下是网络定义(yolov5s.yaml到yolov5x.yaml五个规格,还有common.py、experimental.py、yolo.py),utils/下是工具函数(general.py、metrics.py、loss.py、plots.py、datasets.py等),train.py和detect.py是训练和推理入口。这部分和官方仓库基本一致,说明作者没有魔改主干,降低了理解成本。
测距相关的核心文件有三个,这是整份资源的灵魂:
| 文件 | 作用 | 依赖 |
|---|---|---|
distance.py | 单目测距核心解算,输入像素坐标输出物理距离 | 相机内参、目标真实尺寸 |
detect_A4.py | 用 A4 纸做标定和测距验证 | A4.mp4、best.pt |
realsensedetect.py | 真实场景下的检测 + 测距 | RealSense 相机或普通摄像头 |
配置和权重方面,data/下有coco128.yaml、coco.yaml、voc.yaml、argoverse_hd.yaml等数据集配置,weights/best.pt是训练好的权重,yolov5s.pt是官方预训练权重,download_weights.sh负责拉取权重。hyp.finetune.yaml和hyp.scratch.yaml是两套超参配置,前者用于微调,后者用于从头训练。
测试素材给了A4.mp4,这个设计很聪明——A4 纸尺寸已知(210mm × 297mm),用它做测距验证,真值明确,方便你判断测距模块到底准不准。
提示:
.idea/目录是 PyCharm 的工程配置,yolov5-main.iml、vcs.xml、misc.xml这些是 IDE 文件,不影响运行,但说明作者是在 PyCharm 里开发的,用其他编辑器的话可以忽略。
2.2 环境搭建与依赖安装
环境这块,yolov5 对 PyTorch 版本比较敏感,我一般建议先看requirements.txt再动手。这份资源的依赖清单是标准 yolov5 那一套,核心是torch、torchvision、opencv-python、numpy、pyyaml、tqdm、matplotlib、pillow。
# 建议用 conda 建独立环境,避免和系统 Python 打架 conda create -n yolodist python=3.8 -y conda activate yolodist # 先装 PyTorch,版本按你的 CUDA 来,没有 GPU 就用 CPU 版 # CUDA 11.3 示例 pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html # 再装其余依赖 pip install -r requirements.txt这里有个参数要说明:Python 版本建议 3.8 或 3.9,3.10 以上部分旧版 torch 轮子可能找不到。CUDA 版本决定了你装哪个 torch 轮子,cu113对应 CUDA 11.3,cpu就是纯 CPU。如果你不确定自己的 CUDA 版本,跑nvidia-smi看右上角的CUDA Version。
装完之后验证一下:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"输出里True表示 GPU 可用,False就是 CPU 模式。CPU 也能跑推理,只是慢,训练就别想了。
权重文件如果weights/best.pt已经在了,直接跳过下载。如果缺失,download_weights.sh里是拉取脚本,但注意它默认拉的是官方预训练权重,不是这份项目的best.pt。best.pt是作者训练好的,丢了就得自己重训,这个后面训练章节会讲。
注意:
requirements.txt里如果锁定了torch>=1.7.0这种宽范围,pip 可能给你装最新版,反而和代码不兼容。稳妥做法是手动指定 torch 版本,别完全交给 pip 解析。
3. 单目测距原理与 A4 标定:像素怎么变成米
3.1 相似三角形测距的数学基础
单目测距的核心就一个公式,但坑全在参数上。原理是相似三角形:已知目标真实宽度W,在图像中占w像素,相机焦距f(像素单位),那么目标到相机的距离D满足:
D = (W × f) / w这个公式成立的前提是目标平面和相机光轴垂直,且目标尺寸已知。A4 纸之所以适合做标定,就是因为它的宽高是标准值(210mm × 297mm),真值确定,测出来的距离准不准一眼就能判断。
焦距f怎么来?两个途径:一是查相机规格书,二是自己标定。规格书给的焦距通常是毫米单位,要换算成像素:f_pixel = f_mm × 图像宽度 / 传感器宽度。自己标定更靠谱,用已知距离和已知尺寸的目标反推。
distance.py里封装的就是这套解算逻辑,输入是检测框的像素宽高和预设的真实尺寸,输出是距离。我拆了一下,核心函数大概长这样:
# distance.py 核心逻辑示意 def calculate_distance(pixel_width, real_width, focal_length): """ pixel_width: 检测框像素宽度 real_width: 目标真实宽度(米) focal_length: 相机焦距(像素) 返回:目标到相机的距离(米) """ if pixel_width <= 0: return -1 # 防止除零 distance = (real_width * focal_length) / pixel_width return distance逻辑说明:pixel_width来自 yolov5 检测框的宽度,real_width是你预设的目标物理宽度,focal_length是标定得到的焦距。三个参数里,real_width和focal_length的误差会直接线性放大到距离结果上,所以标定环节不能糊弄。
参数说明:real_width单位要和focal_length保持一致,都用米或都用毫米,混用就是经典翻车点。focal_length的标定精度决定了整体精度,建议用多个距离点拟合,而不是单点反推。
3.2 用 A4.mp4 做焦距标定与精度验证
detect_A4.py配合A4.mp4就是干这个的。操作步骤:
第一步,先跑一遍检测,确认best.pt能正常识别 A4 纸(或者识别的是 A4 纸上的目标,取决于训练类别)。
python detect_A4.py --weights weights/best.pt --source A4.mp4 --view-img参数说明:--weights指定权重路径,--source指定输入视频,--view-img实时显示检测结果。如果报错找不到best.pt,检查weights/目录是否存在,或者路径写的是相对路径还是绝对路径。
第二步,从检测结果里读出 A4 纸在画面中的像素宽度,结合已知真实宽度 0.21 米,反推焦距:
# 标定焦距:已知距离和像素宽度反推 known_distance = 1.0 # 标定时 A4 纸到相机的实际距离,单位米 pixel_width = 320 # 该距离下检测框的像素宽度 real_width = 0.21 # A4 纸真实宽度,单位米 focal_length = (pixel_width * known_distance) / real_width print(f"标定焦距: {focal_length:.2f} 像素")逻辑说明:这是测距公式的逆运算。你先量出相机到 A4 纸的实际距离(用卷尺,别估),再从画面里读出像素宽度,代入反推焦距。参数说明:known_distance一定要实测,pixel_width建议取多帧平均,单帧有抖动。
第三步,用标定好的焦距去测其他距离,验证误差:
# 验证:用标定焦距测新距离 test_pixel_width = 160 # 新位置下 A4 纸像素宽度 predicted_distance = (real_width * focal_length) / test_pixel_width print(f"预测距离: {predicted_distance:.2f} 米")如果预测距离和实测距离误差在 5% 以内,标定就算合格。超过 10%,要么焦距标错了,要么目标平面和光轴不垂直,要么检测框抖动太大。
提示:A4 纸标定的最大价值是给你一个「真值可控」的验证环境。真实场景里目标尺寸未知、姿态多变,误差会大很多,所以别拿 A4 的精度去承诺真实场景的精度。
3.3 焦距、真实尺寸与检测框的三角关系
这三个参数是测距精度的铁三角,任何一个出问题,结果都会偏。焦距标定错了,所有距离系统性偏移;真实尺寸设错了,距离按比例缩放;检测框抖动,距离就跳变。
检测框抖动是工程里最烦的,因为模型输出本身就有波动。常见做法是对连续多帧的检测框做滑动平均,或者用卡尔曼滤波平滑。realsensedetect.py里如果用了 RealSense,还能拿到深度信息做交叉验证,但纯单目就只能靠滤波。
真实尺寸这块,如果你检测的是固定类别(比如锥桶、行人),可以预设一个平均尺寸。但行人高矮胖瘦差异大,测距误差自然大。这也是单目测距的边界——它适合尺寸相对固定的目标,不适合尺寸变化剧烈的类别。
4. 训练自己的数据集与推理调参
4.1 数据集配置与 hyp 超参选择
如果你想用自己的数据重训,data/下的 yaml 就是入口。以coco128.yaml为模板,改成自己的路径和类别:
# data/mydata.yaml train: ../mydata/images/train val: ../mydata/images/val nc: 3 names: ['person', 'cone', 'car']参数说明:train和val是图片目录路径,nc是类别数,names是类别名列表,顺序要和标注文件里的 class id 对应。标注格式是 yolov5 标准的 txt,每行class x_center y_center width height,坐标归一化到 0-1。
超参选择上,hyp.finetune.yaml用于微调——你已经有best.pt或者官方预训练权重,只想在自己的小数据集上适配,学习率低、epoch 少。hyp.scratch.yaml用于从头训练,学习率高、数据增强强。毕设场景一般用微调就够了,除非你的类别和预训练差异极大。
# 微调训练 python train.py --data data/mydata.yaml --weights weights/best.pt --cfg models/yolov5s.yaml --hyp data/hyp.finetune.yaml --epochs 50 --batch-size 16 --img 640参数说明:--weights指定初始权重,微调时用预训练权重收敛快;--cfg指定网络结构,yolov5s最轻量,yolov5x最重但精度高;--epochs训练轮数,小数据集 50 够用;--batch-size按显存调,8G 显存跑yolov5s用 16 差不多;--img输入尺寸,640 是默认值。
4.2 推理脚本参数与测距联动
训练完得到新的best.pt,推理入口是detect.py。但这份资源的重点在测距,所以要看detect_A4.py和realsensedetect.py怎么把检测结果喂给distance.py。
# 标准检测推理 python detect.py --weights weights/best.pt --source data/images --conf-thres 0.5 --iou-thres 0.45 --img-size 640参数说明:--conf-thres置信度阈值,低于这个值的框被丢弃,调高减少误检但可能漏检;--iou-thresNMS 的 IoU 阈值,控制重叠框合并;--img-size推理尺寸,要和训练时一致,否则精度掉。
测距联动部分,核心是从检测结果里取出框的像素宽度,传给distance.py:
# 检测 + 测距联动示意 from distance import calculate_distance focal_length = 800.0 # 标定得到的焦距 real_width = 0.21 # 目标真实宽度 for *xyxy, conf, cls in det: pixel_width = xyxy[2] - xyxy[0] # 框的像素宽度 dist = calculate_distance(pixel_width, real_width, focal_length) print(f"类别 {cls} 距离 {dist:.2f} 米")逻辑说明:xyxy是检测框的左上角和右下角坐标,xyxy[2] - xyxy[0]就是像素宽度。参数说明:focal_length和real_width必须和标定时一致,换相机就要重新标定。
注意:
--img-size改了之后,检测框的像素坐标会跟着缩放,焦距也要按比例调整,否则测距全错。这是很多人换分辨率后测距突然不准的原因。
5. 避坑与常见问题排查
5.1 权重加载失败与路径问题
现象:运行detect_A4.py报FileNotFoundError: weights/best.pt或者RuntimeError: Error(s) in loading state_dict。
原因:一是best.pt文件缺失或路径写错,二是权重和网络结构不匹配(比如用yolov5s.pt的权重去加载yolov5x.yaml的结构)。
解决:先确认weights/目录下文件存在,用绝对路径排除相对路径歧义。结构不匹配的话,检查--cfg和权重是否对应,yolov5s的权重只能配yolov5s.yaml。如果只是缺权重,用download_weights.sh拉官方预训练权重先跑通流程,再换自己的。
5.2 测距数值跳变或明显偏大偏小
现象:距离输出在几米范围内乱跳,或者稳定偏大/偏小一个固定比例。
原因:跳变通常是检测框抖动,偏大偏小是焦距或真实尺寸标定错误。
解决:跳变问题对多帧做滑动平均,窗口取 5-10 帧。偏大偏小先检查real_width单位,0.21 米写成 21 厘米就是 100 倍误差。再检查焦距标定时的known_distance是不是实测值,估的值必然偏。
5.3 CUDA 显存不足与 CPU 回退
现象:训练或推理时报CUDA out of memory。
原因:--batch-size或--img-size太大,显存扛不住。
解决:先降--batch-size到 8 或 4,再降--img-size到 416。还不行就加--device cpu强制 CPU,慢但能跑。推理阶段显存占用比训练小很多,一般不会 OOM。
5.4 检测框坐标与测距公式的坐标系不一致
现象:测距结果和实际差一个数量级,或者随目标位置变化规律不对。
原因:检测框坐标是相对原图的,但如果你做了 resize 或 letterbox,坐标被缩放了,焦距没跟着调。
解决:确认推理时的--img-size和标定焦距时的分辨率一致。如果必须改分辨率,焦距按新焦距 = 原焦距 × 新宽度 / 原宽度等比缩放。letterbox 的填充偏移也要考虑进去,否则框位置会偏。
5.5 A4 标定准但真实场景不准
现象:A4 纸测距误差 3%,换成真实目标误差 30%。
原因:A4 纸是平面正对相机,真实目标有姿态角、尺寸不一致、部分遮挡。
解决:这是单目测距的固有边界,不是代码 bug。能做的优化是选尺寸稳定的目标类别,加姿态估计补偿,或者引入深度相机做融合。别指望纯单目在所有场景都准。
6. 进阶验证:把测距误差量化成一张表
跑通只是第一步,毕设答辩时老师大概率会问「你这个测距精度多少」。与其含糊说「挺准的」,不如做一张误差表,用数据说话。我的习惯是固定几个距离点,每个点测 30 帧,算平均误差和标准差。
import numpy as np # 误差统计:每个距离点采集多帧 def evaluate_distance(pred_list, gt_distance): """ pred_list: 多帧预测距离列表 gt_distance: 实测真值 """ pred_arr = np.array(pred_list) mean_pred = pred_arr.mean() std_pred = pred_arr.std() abs_error = abs(mean_pred - gt_distance) rel_error = abs_error / gt_distance * 100 print(f"真值 {gt_distance}m | 预测均值 {mean_pred:.3f}m | 标准差 {std_pred:.3f}m | 绝对误差 {abs_error:.3f}m | 相对误差 {rel_error:.1f}%") return rel_error # 示例:1 米处采集 30 帧 preds_1m = [0.95, 1.02, 0.98, 1.05, 0.97] * 6 evaluate_distance(preds_1m, 1.0)逻辑说明:pred_list是同一距离下多帧的预测值,gt_distance是卷尺实测真值。参数说明:帧数建议 30 以上,少了统计意义不足;真值一定要实测,别用另一个测距工具的值当真值,那是循环论证。
把 0.5m、1m、2m、3m 几个点的结果整理成表:
| 真值(m) | 预测均值(m) | 标准差(m) | 相对误差 |
|---|---|---|---|
| 0.5 | 0.52 | 0.03 | 4.0% |
| 1.0 | 0.97 | 0.04 | 3.0% |
| 2.0 | 2.11 | 0.08 | 5.5% |
| 3.0 | 3.24 | 0.12 | 8.0% |
这张表能直接写进毕设论文的实验章节,也能在答辩时堵住「精度多少」的追问。规律很明显:距离越远,相对误差越大,因为像素宽度变小,同样的像素误差对应的物理误差被放大。这是单目测距的物理极限,不是调参能解决的。
还有一个验证技巧:把A4.mp4里每一帧的测距结果和真值画成曲线,看跟踪是否平滑。如果曲线锯齿严重,说明检测框抖动大,加滤波;如果曲线整体偏移,说明标定参数错。这个可视化用 matplotlib 几行就能画:
import matplotlib.pyplot as plt plt.plot(range(len(preds)), preds, label='predicted') plt.axhline(y=gt_distance, color='r', linestyle='--', label='ground truth') plt.xlabel('frame') plt.ylabel('distance (m)') plt.legend() plt.savefig('distance_curve.png')从那以后我每次拿到测距类项目,都强制先跑一遍误差表再谈其他——没有量化误差的测距系统,等于没有测距。这份资源的distance.py和detect_A4.py已经把框架搭好了,你只需要把标定做扎实、把误差表填上,毕设的实验部分就稳了。希望帮到你。
本文还有配套的精品资源,点击获取