公路养护和市政巡检里,“路面坑洼检测”是一个很典型的视觉落地场景:问题肉眼可见,但靠人跑断腿也看不完。这次我们来看一个把 YOLOv8 和 PyQt5 组合起来的桌面检测系统——用 YOLOv8 做坑洼、破损、裂缝等路面缺陷的识别,再用 PyQt5 做图形界面,把模型推理包装成图片上传、视频检测、摄像头实时检测这类可操作功能。它解决问题的方式很直接:训练一个自己的路面缺陷模型,然后包一个带界面、能选文件、能出结果的应用,后续无论是给巡检人员用,还是作为毕设、课程设计的完整项目,都比单纯跑命令行推理更容易交付。
这篇文章会按照从方案到落地的顺序展开:先讲 YOLOv8 + PyQt5 这套组合适不适合这个任务,再给环境准备、数据集组织、模型训练与导出、PyQt5 界面集成的完整思路,最后是图片、视频、摄像头、批量任务的功能测试方法,以及常见坑的排查清单。如果你正在做同类检测项目,可以直接把这里的流程和代码模板作为骨架,替换成自己的数据集和业务逻辑。
先快速给结论:这类系统的技术门槛不高,YOLOv8 的官方库把训练、验证、导出、推理都封装得比较完整,PyQt5 解决的是“怎么让模型结果变成可用工具”的问题。真正的难点在于数据、界面交互和推理性能的平衡。
1. 核心能力速览
在动手之前,先把这套系统的关键指标列出来,方便判断它适不适合你的场景。
| 能力项 | 说明 |
|---|---|
| 项目类型 | 桌面端视觉检测系统(PyQt5 GUI + YOLOv8 目标检测) |
| 检测目标 | 坑洼、破损、裂缝、修补块等路面缺陷(由训练数据集决定) |
| 检测引擎 | YOLOv8,Ultralytics 官方开源系列模型 |
| 界面框架 | PyQt5 |
| 训练硬件 | 建议 NVIDIA GPU,显存越大越稳;纯 CPU 也可以训练,但速度慢 |
| 推理硬件 | GPU 优先;CPU 能跑,适合低分辨率、低并发场景 |
| 显存占用 | 取决于模型尺寸(n/s/m/l/x)和输入分辨率,需按实际测试为准 |
| 启动方式 | Python 脚本启动桌面程序 |
| 是否支持 API | 原始项目主体是 GUI,接口需自行封装 FastAPI 或 Flask |
| 是否支持批量任务 | 可以,遍历图片/视频目录后调用模型推理即可 |
| 模型导出 | 支持 PyTorch 权重、ONNX、TensorRT 等格式 |
| 适合场景 | 道路巡检辅助、养护前筛查、高校项目、目标检测 + Qt 工程实践 |
从材料看,这套方案的核心卖点不是算法创新,而是“训练 + 界面 + 部署”的完整闭环。YOLOv8 负责把图片里的缺陷位置找出来,PyQt5 负责让普通用户能选择文件、点击按钮、看到结果,而不是面对黑乎乎的终端。
2. 适用场景与使用边界
这种系统最常用的场景是道路巡检辅助。巡检人员拍下路面照片,回来后批量导入系统,系统自动标出可疑坑洼位置和置信度,人工再做二次复核。相比纯人工翻照片,效率提升非常明显,这是它最大的价值。
还有一些常见用途:
- 市政道路日常巡检:用行车记录仪或者手机拍摄路面素材,事后用系统筛查。
- 低等级公路养护前摸底:先快速扫一遍,把明显破损区域挑出来。
- 高校毕设/课程设计:把 YOLOv8 训练、PyQt5 界面、目标检测结合起来,是一个很完整的工程实践题目。
- 实验教学:用来演示“模型如何被包装成产品”,比单纯跑检测脚本更有说服力。
但它不是万能的,有几个边界必须说清楚。
第一,模型泛化能力受训练数据限制。不同地区路面材质、光照条件、拍摄角度差异很大,在 A 城市训练的数据集,拿到 B 城市可能漏检严重。所以真正要落地,最好用目标地区的真实照片重新训练或微调。
第二,它不能代替养护决策。系统只负责“找出可疑区域”,坑洼深度、面积、损坏等级这些更细的评估,仍然需要人工到现场确认。把检测系统当作最终判断依据,风险很大。
第三,存在误检和漏检风险。井盖、阴影、水渍、轮胎痕迹都可能被误判为坑洼;而雨天积水、夜间低照度场景下漏检率也会上升。使用时要合理设置置信度阈值,保留人工复核环节。
第四,数据合规问题。如果使用了公开数据集,商用前要先确认数据集许可证;如果自己采集数据,要注意拍摄对象是否涉及个人隐私。摄像头实时检测场景下,更要考虑拍摄范围和个人信息脱敏。
3. 环境准备与前置条件
开发这套系统,主要涉及 Python、PyTorch、Ultralytics、PyQt5、OpenCV 这几个组件。安装之前,先明确自己的硬件和系统环境。
3.1 基础依赖
- 操作系统:Windows / Linux / macOS 都可以,Windows 上调试 PyQt5 最方便。
- Python 版本:建议 3.8 到 3.11 之间,具体以当前
ultralytics和PyQt5的兼容说明为准。 - GPU 驱动:NVIDIA 显卡用户需要装好显卡驱动,训练前先看清楚 CUDA 版本。
- 磁盘空间:数据集、模型权重、虚拟环境加在一起,建议预留 20GB 以上。
先检查 Python 和显卡状态:
python --version pip --version nvidia-smi如果nvidia-smi有输出,说明驱动正常。然后安装 PyTorch,注意要选择和你的 CUDA 版本匹配的安装命令,建议到 PyTorch 官网获取最新安装指令,不要盲目复制旧命令。
3.2 安装核心库
安装 YOLOv8 官方库和 PyQt5:
pip install ultralytics pip install PyQt5如果下载速度慢,可以临时切换国内镜像源:
pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple pip install PyQt5 -i https://pypi.tuna.tsinghua.edu.cn/simpleOpenCV 一般会作为ultralytics的依赖自动安装。安装完成后可以验证一下:
import ultralytics import PyQt5.QtCore print(ultralytics.__version__) print(PyQt5.QtCore.QT_VERSION_STR)这两行如果能正常输出,说明主体环境已经就绪。
4. 数据集准备与 YOLOv8 模型训练
一个好的坑洼检测系统,八成功夫花在数据上。模型结构不是瓶颈,标注质量和数据多样性才是。
4.1 数据集目录结构
YOLOv8 训练时推荐的数据集目录结构是 images 和 labels 分离,训练集、验证集分开:
dataset/ ├── data.yaml ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/每个图片对应的标注文件是同名的.txt文件,放在 labels 对应目录下,格式为:
class_id x_center y_center width height其中坐标都做了归一化,范围在 0 到 1 之间。比如一张图上只有一个坑洼,标注内容可能是:
0 0.5218 0.6342 0.2745 0.1981如果类别是 1 个,class_id 就是 0。如果有坑洼、裂缝、修补块等多个类别,class_id 依次为 0、1、2。
4.2 编写 data.yaml
训练前需要一份数据集配置文件:
path: D:/road_dataset train: images/train val: images/val nc: 1 names: 0: pothole如果你的业务里要检测多个类别,把nc和names改成自己的类别列表。注意路径里的path建议使用绝对路径,避免相对路径找不到数据集。
4.3 开始训练
安装好依赖、准备好数据后,可以用命令行启动训练:
yolo train data=D:/road_dataset/data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0参数含义:
| 参数 | 推荐值 | 说明 |
|---|---|---|
model | yolov8n.pt / yolov8s.pt | 模型越小显存占用越少,速度越快 |
epochs | 100 | 根据数据量调整,小数据集可以先用 50 次 |
imgsz | 640 | 原始 YOLOv8 默认训练分辨率 |
batch | 16 | 根据显存调整,显存不足就调小 |
device | 0 | 0 表示第一块 GPU;没有 GPU 就写cpu |
第一次训练建议先跑小模型、小 batch、少轮数,确认整个流程没问题,再加大参数。训练结束后,结果会输出到runs/detect/train目录,里面包括weights/best.pt、weights/last.pt、混淆矩阵、曲线图等文件。best.pt就是后面 PyQt5 界面要加载的权重文件。
4.4 模型导出为 ONNX
如果后续要部署到没有 PyTorch 的环境,或者想用 OpenCV DNN、TensorRT 推理,可以先把模型导出为 ONNX:
yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640导出成功后,同级目录会出现best.onnx。这个文件不依赖 PyTorch,可以被很多推理框架加载。
5. PyQt5 界面架构与模块划分
模型训练好之后,重点就转移到 PyQt5 界面上。很多人在这一步出现问题,原因不是某个控件不会用,而是把推理逻辑直接写在按钮回调里,导致点击按钮后界面卡死。
正确做法是把界面、推理、数据管理分成几个模块,界面只负责交互,推理放到单独线程里执行。
5.1 推荐项目结构
pothole_detector/ ├── main.py ├── detector.py ├── ui/ │ ├── main_window.py │ └── resources/ ├── weights/ │ └── best.pt ├── inputs/ └── outputs/main.py:程序入口,启动 PyQt5 应用。detector.py:封装 YOLOv8 模型的加载和推理逻辑。ui/main_window.py:主窗口界面代码。weights:存放训练好的模型权重。inputs/outputs:测试图片输入和结果输出目录。
因为材料中没有给出具体的源码文件,这里的代码是通用模板,实际使用时需要根据自己的类名、布局和业务逻辑调整。
5.2 推理模块封装
把 YOLOv8 加载和推理封装成独立模块,方便界面调用:
# detector.py from ultralytics import YOLO class RoadDetector: def __init__(self, weights_path: str, conf_threshold: float = 0.35): self.model = YOLO(weights_path) self.conf_threshold = conf_threshold def infer_image(self, image): results = self.model.predict( source=image, conf=self.conf_threshold, verbose=False ) return results[0]这样写的好处是:界面代码不关心 YOLO 内部逻辑,后续想换成其他模型,也只需要改detector.py。
5.3 主窗口骨架
PyQt5 主窗口里,建议至少包含这几部分:
- 图片/视频打开按钮
- 摄像头开关
- 检测结果画布
- 检测信息文本区域
- 置信度滑块
下面是一个最小骨架示例:
# main.py import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel from PyQt5.QtGui import QImage, QPixmap from detector import RoadDetector class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("路面坑洼检测系统 - YOLOv8 + PyQt5") self.resize(960, 640) self.detector = RoadDetector("weights/best.pt") self.result_label = QLabel(self) self.result_label.setText("检测结果区域") self.result_label.setGeometry(20, 20, 800, 450) self.btn_detect = QPushButton("选择图片检测", self) self.btn_detect.setGeometry(840, 30, 100, 40) self.btn_detect.clicked.connect(self.on_detect_clicked) def on_detect_clicked(self): # 这里应该用 QFileDialog 选择图片,然后在子线程里推理 # 下面只是占位逻辑,实际项目不要阻塞主线程 pass if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())这里最需要注意的问题是线程。模型推理可能耗时几百毫秒到几秒,如果直接放在按钮回调里,界面会冻结。更稳妥的做法是使用QThread把推理放到子线程,推理完成后再通过信号把结果传回主线程更新界面。这也是 PyQt5 开发中最常见的性能坑之一。
6. 功能测试与效果验证
系统搭建完成后,要按功能模块逐步验证,不要一上来就全流程测试。
6.1 图片检测测试
测试目的:验证模型能否在单张图片上正确框出坑洼区域,并显示类别和置信度。
操作步骤:
- 打开系统主界面。
- 选择一张含明显坑洼的测试图片,最好是训练集中没有出现过的。
- 点击检测按钮,等待推理完成。
- 查看输出图片上是否有正确的目标框。
判断标准:
- 坑洼位置被框住,框的位置合理。
- 置信度分数显示正常。
- 没有把路面裂缝、阴影误判为坑洼,或者误判数量在可接受范围内。
如果漏检严重,先降低置信度阈值再试;如果误检严重,说明模型训练不充分或测试图片与训练集分布差异大。
6.2 视频检测测试
测试目的:验证系统能否处理连续帧,观察推理速度是否满足实时性要求。
操作步骤:
- 选择一段包含坑洼路面的短视频。
- 点击视频检测按钮。
- 播放过程中观察画面是否流畅,检测框是否抖动。
判断标准:
- 视频能逐帧读取和推理。
- 大多数帧能识别出坑洼,偶尔丢帧可以接受。
- 推理速度要按硬件实测,GPU 通常明显快于 CPU。
如果视频检测掉帧严重,可以考虑降低输入分辨率、使用更小的模型、或者跳帧检测,不要追求每一帧都推理。
6.3 摄像头实时检测测试
如果系统里集成了摄像头检测,测试时要检查设备号和权限。
操作步骤:
- 用系统自带相机应用确认摄像头能正常打开。
- 在系统里选择对应摄像头设备号(通常是 0)。
- 将摄像头对准路面,观察实时画面和检测框。
常见问题:摄像头画面黑屏,多半是权限没开或设备号不对;画面卡顿,大概率是推理速度跟不上帧率,需要降低分辨率或改用更小模型。
6.4 批量检测测试
批量检测适合巡检结束后处理大量照片,逻辑上就是遍历文件夹里所有图片,逐张推理并保存结果。
from ultralytics import YOLO model = YOLO("weights/best.pt") results = model.predict( source="inputs/road_photos/", imgsz=640, conf=0.35, save=True, save_txt=True, project="outputs/batch_results", name="detect" ) print("批量检测完成,共处理图片数量:", len(results))当source指向目录时,YOLO 会自动遍历目录下可识别的图片格式。save=True保存画框图片,save_txt=True保存检测结果的 txt 标注文件。批量完成后检查输出目录,看有没有明显漏检的图片,同时统计一张图的平均耗时,评估整体处理效率。
7. 批量任务设计与输出管理
如果检测图片数量很大,建议在批量逻辑里增加异常处理和输出整理。
7.1 批量任务代码模板
import os import traceback from pathlib import Path from ultralytics import YOLO input_dir = Path("inputs/road_photos") output_dir = Path("outputs/road_photos_results") output_dir.mkdir(parents=True, exist_ok=True) model = YOLO("weights/best.pt") failures = [] success_count = 0 for img_path in input_dir.glob("*.jpg"): try: result = model.predict( source=str(img_path), imgsz=640, conf=0.35, save=True, project=str(output_dir), name="detect", exist_ok=True ) success_count += 1 except Exception: failures.append(str(img_path)) traceback.print_exc() print("成功处理:", success_count) print("失败数量:", len(failures)) for f in failures: print("失败文件:", f)这样做的好处是:单张图片损坏不会中断整批任务,失败文件会被记录在案,方便后续单独处理。
7.2 输出结果管理
结果目录建议按日期或批次命名,避免多次运行相互覆盖:
outputs/ └── batch_20250101/ ├── detect/ │ ├── image_001.jpg │ ├── image_001.txt │ └── ... └── summary.csv有检测结果的图片和 txt 文件放在一起,方便后续人工复核或二次筛选。如果要做统计报表,可以遍历 txt 文件统计每张图的检测数量,汇总后输出 CSV。
8. 资源占用与性能观察
很多人做完界面后,最关心的问题就是“同样一张图,我的配置能跑多快”。这类性能数据不能凭空给结论,但可以给一套观察方法和调优思路。
8.1 查看 GPU 占用
训练和推理过程中,可以通过nvidia-smi实时观察显存占用:
nvidia-smi -l 1每 1 秒刷新一次,可以看到显存使用率、GPU 利用率等数据。观察时重点看两个指标:显存使用量是否接近显卡上限,GPU 利用率是否稳定在高位。如果是训练阶段,关注模型大小、batch、分辨率对显存的影响;如果是推理阶段,关注单次推理耗时和显存峰值。
8.2 CPU 推理和 GPU 推理的差异
纯 CPU 推理在 YOLOv8 上也能跑,但速度明显慢于 GPU,尤其是大模型、高分辨率输入时差距会非常明显。做桌面工具时,建议优先用 GPU 推理;没有 GPU 的机器上测试,要尽量使用yolov8n这样的小模型,并把推理分辨率降到 640 甚至 480。
8.3 调低显存的常见手段
如果出现显存不足(Out of Memory),优先做这几件事:
- 调小
batch,训练时从 16 改成 8 或 4。 - 调小
imgsz,从 640 改成 512 或 416。 - 换更小的模型,
yolov8s换yolov8n。 - 训练时开启 AMP 混合精度:
yolo train data=dataset/data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0 amp=True推理单张图片时显存占用不大,但如果批量处理或视频检测,也要注意显存峰值。出现卡顿时先看是不是显存被打满。
8.4 关于端口和进程
PyQt5 桌面程序默认不占用固定端口,所以一般不存在端口冲突问题。但如果你后来用 FastAPI 或 Flask 封装了检测服务,就要注意端口占用。启动服务前先查端口:
- Windows:
netstat -ano | findstr 8000 - Linux/macOS:
lsof -i :8000
如果端口被占用,换一个端口或结束占用进程。
9. 常见问题与排查方法
把项目开发中最容易踩的坑整理成清单,遇到问题时可以对照排查。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
pip install ultralytics失败 | 网络波动或依赖冲突 | 查看 pip 报错信息 | 换国内镜像源,或升级 pip |
torch.cuda.is_available()返回 False | CUDA 和 PyTorch 版本不匹配 | 运行该命令查看结果 | 安装匹配的 PyTorch 版本 |
| PyQt5 界面点按钮后卡死 | 推理阻塞了 UI 线程 | 在回调里打印耗时 | 用 QThread 子线程推理 |
| 点击检测没反应 | 模型路径错误或权重损坏 | 打印模型文件是否存在 | 检查路径,重新下载/导出权重 |
| 摄像头黑屏 | 设备号错误或权限未开启 | 换 0/1,测试系统相机 | 授权摄像头或更换设备号 |
| 训练时显存溢出 | batch 或 imgsz 太大 | 查看 OOM 日志 | 调小 batch/imgsz,换小模型 |
| 批量检测中途报错 | 个别图片损坏 | 在循环里加 try-except | 跳过坏图,记录失败文件 |
| 漏检严重 | 训练数据中缺陷样本少 | 查看验证集指标和混淆矩阵 | 扩充数据、数据增强、降低置信度 |
| 误检严重 | 数据分布和测试场景差异大 | 分析误检样本 | 补充目标场景数据,重新训练 |
| ONNX 导出失败 | torch 版本或算子问题 | 查看导出日志 | 升级 ultralytics/torch,或调整 opset |
| 打包成 exe 后模型加载失败 | 权重没有被打进包里 | 检查打包目录 | 用 PyInstaller--add-data把权重加入 |
10. 最佳实践与下一步
做一个“能跑”的检测系统不难,但做一个“好用”的系统需要额外花功夫。
第一个建议是先用最小配置跑通全流程。第一次训练不要直接上大模型和大 batch,先用yolov8n、少量 epoch 验证数据格式、代码逻辑和环境是否正常。全流程通了,再逐步加大参数。这样可以避免把时间浪费在排查数据和环境的低级问题上。
第二个建议是做好文件目录管理。建议在项目根目录下固定datasets、weights、inputs、outputs四个目录,训练数据、模型权重、测试素材、推理结果分开存放。时间一长,这个习惯能省下很多找文件的麻烦。
第三个建议是保留一份最小可运行配置。记录你训练用的data.yaml、best.pt和推理用的参数,最好写进 README。这样即使电脑换了,也能快速恢复环境。
后续如果想继续扩展,方向也很多:
- 用 FastAPI 把推理封装成 HTTP 接口,方便其他团队调用。
- 导出 ONNX 后用 OpenCV DNN 或 TensorRT 推理,摆脱对 PyTorch 的依赖。
- 在 PyQt5 界面里增加 FPS 显示、检测数量统计、结果导出 Excel 等实用功能。
- 加入多类别检测,比如坑洼、裂缝、修补块,让系统更接近实际养护需求。
- 增加雨雾、低光照数据增强,提高恶劣天气下的鲁棒性。
最后再提醒一个很容易被忽略的问题:如果项目要商用或者公开发布,一定要确认训练数据的来源和许可证。路面图片如果来自公开数据集,要先看授权条款;如果是自己采集的数据,涉及街道和行人时要做好脱敏处理。这一步不做好,功能再完善也有风险。初次尝试时,建议先跑yolov8n加 50 个 epoch,把数据集准备、界面集成、批量检测流程全部走通,再考虑用更大模型提升精度。