简介:面向智慧城市地下管廊巡检场景,这套基于YOLOv8的积水渗漏检测系统提供了从模型训练到可视化部署的完整链路。资源内含Python源码、完整数据集、可视化界面与部署说明,涵盖训练脚本、检测脚本和界面交互模块,模型权重文件可直接用于推理。压缩包内共有8个文件,整体大小约15.91MB,其中3个Python脚本分别用于训练、检测与界面展示,3个.pt权重文件对应不同精度的预训练模型,另有2个txt文档包含README和使用说明。资源代码均经过测试运行成功,可一键生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图,便于快速评估模型效果。已有42人学习下载,适合计算机相关专业学生用于毕业设计、课程设计或项目立项演示,也便于深度学习初学者在现有基础上扩展功能。
1. 地下管廊积水渗漏检测:YOLOv8这套骨架省掉的远不止标数据
地下管廊的巡检一直是城市运维里最难受的环节:环境暗、湿度大,积水渗漏经常藏在电缆桥架背后或地面接缝处,人下去一趟既危险又低效。基于YOLOv8的智慧城市地下管廊积水渗漏检测系统,核心思路是让目标检测模型在摄像头画面上直接判断哪里有明水、哪里有渗漏痕迹,再通过可视化界面把检测结果变成看得懂的告警和回放记录。这套工程把源码、完整数据集、可视化界面和部署教程打包在一起,路径配好就能把模型训练并跑通,正适合要做毕设、课程设计、需要完整闭环的同学。一个提醒也得先说清:“简单部署”不等于双击就能跑,环境匹配、数据路径和界面线程这三处,是新手最常见的翻车点。
2. 管廊积水检测的五段主链路:数据流向决定模型能不能落地
2.1 从摄像头到告警:五个模块的分工与边界
一段项目级的YOLOv8检测系统,不等于一行model = YOLO("best.pt")后对着视频跑。真正能落地的结构是五段链路:数据采集与标注、模型训练与验证、模型导出、推理服务、可视化界面与告警。每一段的产出都是下一段的输入:标注段产出图片和TXT标签,训练段消费它们并产出best.pt权重,导出段把权重转成ONNX或Engine,推理服务加载权重对每一帧做预处理、前向推理和后处理,界面段把推理结果叠加到画面上再做告警和记录。
我把这五段分开的原因很朴素:管廊现场换相机、换角度、换场景时,只需要动采集和标注,重新训练几轮,后面的推理和界面不用改。更关键的是接口约定——推理服务输出统一为JSON或CSV记录,界面只消费这个输出,不直接触碰模型。课设小组就可以把训练和界面两件事并行开发,谁都不等谁。
2.2 数据集目录怎么排:训练脚本才不会找不到文件
YOLOv8大部分训练入口都约定一个镜像目录,images和labels平行,train和val的比例常规按照8:2。一个标准的排布长这样:
dataset/ ├── images/ │ ├── train/ # dlg_001.jpg, dlg_002.jpg ... │ └── val/ # 不同时段、不同光线的画面 ├── labels/ │ ├── train/ # dlg_001.txt, dlg_002.txt ... │ └── val/ └── dataset.yaml对应的数据集声明文件dataset.yaml写成:
path: ../dataset train: images/train val: images/val names: 0: water_puddle 1: leak_trace逻辑说明:path是相对dataset.yaml所在目录的路径,train和val再相对path去定位。names的序号必须和标签TXT第一列一一对应,序号错一位,训练出来的模型就会把两个类彻底搞反。
参数说明:val集不要只抽同一段视频的连续帧,管廊灯光变化大,连续帧之间信息量太低,验证结果会被严重高估。最好按天、按不同廊段、不同亮度抽帧。很多人训练时报found no images in data,多数就是path配了相对路径却在别的目录执行了命令,改成绝对路径最省事。
2.3 从Labelme标注到YOLO格式:一个批量转换脚本
Labelme导出的JSON里,points是绝对像素坐标,而YOLO需要的是归一化的中心点和宽高。管廊积水、渗漏这类目标形态不规则,标的时候常用多边形勾勒,转换时取外接矩形即可。我一般用下面这个脚本批量处理:
import json def convert_labelme_to_yolo(json_file, img_w, img_h, out_txt, class_map): with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) lines = [] for shape in data['shapes']: label = shape['label'] if label not in class_map: continue # 跳过未定义类别 points = shape['points'] xs = [p[0] for p in points] ys = [p[1] for p in points] x_min, x_max = min(xs), max(xs) y_min, y_max = min(ys), max(ys) w = x_max - x_min h = y_max - y_min if w <= 0 or h <= 0: continue # 过滤零面积框 x_center = (x_min + w / 2.0) / img_w y_center = (y_min + h / 2.0) / img_h w = w / img_w h = h / img_h lines.append(f"{class_map[label]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") with open(out_txt, 'w') as f: f.write("\n".join(lines))逻辑说明:取多边形所有顶点的外接矩形,再归一化到整图尺寸。除以img_w、img_h是YOLO格式的硬要求,坐标必须落在0到1之间。脚本里过滤未定义类别和零面积框,能避免训练时出现样本异常导致Loss不降。
参数说明:class_map是类别字典,比如{"water_puddle": 0, "leak_trace": 1};img_w、img_h必须读原图宽高,不要用界面里缩放后的显示尺寸;输出的TXT要和对应图片同名,dlg_001.json转出dlg_001.txt,文件名错一个字符,训练时就找不到标签。
2.4 类别语义与数据规模:管廊暗光下的两个先决条件
管廊画面大面积是深色背景,积水区域常常只占画面的百分之几,目标框普遍偏小。这种情况下,类别别急着细分:渗漏痕迹和水渍在视觉上高度相似,人眼都要贴近了才分得清,模型学到的是纹理和边缘梯度,很容易混淆。常见做法是先只分两个大类——明水和渗漏痕迹,等这两类mAP稳定到0.7以上,再考虑拆细类。如果项目包里已经给了标注好的数据集,先看类别数量和框的分布,不要拿到就开训。
数据规模上,几百张图能跑出一个demo级模型,但要论文级结果,建议每类至少800张,并且正样本里要包含不同廊段、不同时段、不同亮度。同一个场景连续帧抽10张和抽200张,信息量差别不大,管廊这种高度重复的背景,多样性比数量重要得多。训练之前我还习惯做一次快速质检:统计每张图的目标框数,把框数为0的图单拉出来怀疑漏标;再抽20张图把TXT转回画框可视化,确认框贴合目标边缘。这一步20分钟,能省掉后面两天训练翻车的时间。
3. 用YOLOv8训练管廊积水数据集:环境、参数与损失曲线判断
3.1 环境配置:先对CUDA与PyTorch版本,再装ultralytics
YOLOv8环境配置是新手卡关第一名。常见做法是:先确认CUDA版本,再装对应PyTorch,最后装ultralytics。如果上来就pip install ultralytics,它会把CPU版PyTorch一起拉进来,后续即使nvidia-smi显示有GPU,训练还是默默跑在CPU上,一个epoch慢到怀疑人生。
nvidia-smi # 看驱动支持的CUDA版本,比如 12.1 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics逻辑说明:nvidia-smi顶部显示的CUDA Version是驱动支持的版本,不等于当前Python环境里torch实际用的CUDA版本。安装torch时指定cu121的index-url,确保torch和torchvision都带CUDA支持。这两个包必须用同一条命令装,分开装最容易出现版本不匹配,import时直接报DLL加载失败。
参数说明:torch 2.0.1 + torchvision 0.15.1 + ultralytics 8.0.x是我常用的稳定组合;GTX 1660 Ti这类老显卡用cu118更稳,驱动兼容性更好。纯CPU机器也能训练小数据集,但百张图一百轮可能要跑一天,调参会非常痛苦。
3.2 模型选型与最小训练命令:为什么默认用yolov8s
YOLOv8按n/s/m/l/x五档区分,主干是CSPDarknet的改进版,neck是PAN-FPN,head是解耦头。网上流传的YOLOv8网络结构图基本都长一个样,s与m/l的差别主要体现在depth和width的倍数,结构图本身不因档位而变。管廊积水检测场景,我起步就用s档:精度和速度最均衡,6GB显存也能跑batch=16。管廊不是那种要数几百个密集小目标的场景,m/l带来的精度收益有限,显存和耗时却翻倍。
yolo detect train model=yolov8s.pt data=dataset.yaml epochs=100 imgsz=640 batch=16 device=0 patience=15各参数的作用如下:
| 参数 | 值 | 说明 |
|---|---|---|
| model | yolov8s.pt | 预训练权重,s档均衡 |
| data | dataset.yaml | 数据集声明文件 |
| epochs | 100 | 管廊小数据100轮足够 |
| imgsz | 640 | 训练输入尺寸,小目标多可加到960 |
| batch | 16 | 6GB显存上限,不够就降到8 |
| device | 0 | 第一块GPU |
| patience | 15 | 连续15轮验证集不提升就早停 |
这里有一个容易想错的地方:epochs不是越大越好。管廊数据集只有几百张时,100轮左右已经过拟合,patience会自动止损。爆显存时优先降batch而不是降imgsz——管廊积水目标本来就小,imgsz降到416会更难学。GTX 1660 Ti这类6GB卡跑s档,我一般直接batch=8并打开AMP混合精度,把显存留给图像尺寸。
3.3 损失曲线怎么看:results.csv里藏的过拟合信号
训练结束后,runs/detect/train/下会生成results.csv,热词里常说的“yolov8画损失函数曲线图”就是从这张表画的。表里有train/box_loss、val/box_loss、cls_loss、dfl_loss等列,画图脚本很短:
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('runs/detect/train/results.csv') df.columns = [c.strip() for c in df.columns] fig, axes = plt.subplots(1, 3, figsize=(14, 4)) for ax, col in zip(axes, ['train/box_loss', 'train/cls_loss', 'train/dfl_loss']): ax.plot(df['epoch'], df[col], label=col) ax.set_xlabel('epoch') ax.set_ylabel('loss') ax.legend() ax.grid(True) plt.tight_layout() plt.savefig('loss_curves.png', dpi=200)逻辑说明:只看训练集损失完全没有意义,必须对照验证集。val/box_loss开始上扬、train/box_loss还在降,就是过拟合的标准信号。ultralytics每个epoch结束后都会比较验证指标,保留最优权重为best.pt,最后做推理只取best.pt,不碰last.pt。这张损失曲线图放进论文里,比贴十行训练日志都有说服力。
参数说明:脚本里的['train/box_loss', 'train/cls_loss', 'train/dfl_loss']不是固定不变的,不同版本results.csv列名略有差异,先打印df.columns确认。另外results.png是训练过程中自动生成的汇总图,里面还有PR曲线、混淆矩阵,做论文分析时直接引用即可。
3.4 管廊场景训练的两个调整:类权重与Mosaic
管廊画面暗、目标小,两个常见问题:正样本偏少,以及目标框太小。应对方法上,对于少数类,比如water_puddle只有300张、leak_trace有900张,常见做法是做轻度HSV扰动扩充少数类:把300张积水图每张生成2到3个变体,再进训练集。YOLOv8没有现成的class_weight命令行参数,靠过采样最直接。
Mosaic增强默认开启,把四张图拼成一张,对小目标有利,能提供更多上下文。但管廊视频帧连续性强,Mosaic太强会让模型学到拼接痕迹,我踩过这个坑后把Mosaic启用概率调低到0.5左右。训练时如果显存吃紧,AMP是另一个保命手段,ultralytics默认就会自动开启,不需要手动干预。
4. 可视化界面怎么接YOLOv8:PyQt方案的最小骨架与线程取舍
4.1 PyQt还是Web:先按部署现场选
“可视化界面”在管廊场景里常见两种形态:桌面程序和Web页面。毕设、课设首选PyQt5,渲染视频帧、画检测框、弹告警弹窗都不需要搭前后端;如果以后要多人远程查看,再考虑Flask起一个Web服务,模型推理放在后端接口里。管廊巡检一般是单点值守,一台工控机接一个显示器,PyQt5桌面程序就是最顺手的方案。
界面至少要包含五个元素:视频画布、检测结果表格、告警指示灯、控制按钮和FPS显示。结果表格里每行记录时间、类别、置信度、框坐标;告警指示灯用三个色块表示正常、预警、告警。想做论文配图的话,界面里加一个“保存当前帧”的按钮,把检测结果带框导出来,比后期用OpenCV重新画方便得多。如果还想展示模型关注区域,可以用特征热力图工具生成检测层的激活图,很多毕设都会加这一张。
4.2 模型推理必须单独线程:QThread接法的最小骨架
YOLO推理一帧耗时50到150毫秒,直接放GUI主线程里,画面会卡成PPT,拖拽窗口都费劲。正确做法是开一个QThread,模型在子线程里初始化,循环读取帧做推理,结果通过信号发回主线程刷新页面:
from PyQt5.QtCore import QThread, pyqtSignal from ultralytics import YOLO import cv2 class DetectWorker(QThread): frame_ready = pyqtSignal(object, object) def __init__(self, model_path, source, conf=0.25, parent=None): super().__init__(parent) self.model = YOLO(model_path) # 模型放在子线程构造 self.source = source self.conf = conf self.running = True def run(self): cap = cv2.VideoCapture(self.source) while self.running: ret, frame = cap.read() if not ret: break result = self.model(frame, conf=self.conf, verbose=False)[0] dets = result.boxes.data.cpu().numpy() # [N,6]: x1,y1,x2,y2,conf,cls annotated = result.plot() # 自带画框与类别文字 self.frame_ready.emit(annotated, dets) cap.release()逻辑说明:detect = YOLO(...)写在__init__里,模型加载也在子线程,不阻塞主线程的窗口初始化。result.plot()是ultralytics自带的可视化方法,直接在帧上画好检测框和类别名,省掉手写OpenCV画框代码。frame_ready信号携带两个对象:画好的帧和检测数据,主线程的槽函数只负责往QLabel上贴图。
参数说明:conf=0.25是置信度阈值,管廊暗光误报多,建议起步设0.35,宁可漏检也不要告警刷屏。source可以是摄像头编号0、视频文件路径或RTSP流地址,cv2.VideoCapture对这三种输入统一处理,界面里加一个输入框让用户填地址即可。
4.3 告警逻辑:连续帧确认比单帧置信度可靠
管廊里地面反光、水汽、灯光折射都会造成偶发误报。只靠单帧置信度阈值压不住这些噪声,常见做法是叠加“连续帧确认”:同一位置连续5帧里至少3帧检到同类目标,才输出告警。实现上用dict记录检测框位置和命中次数,超过阈值就点亮告警灯,未达阈值就丢掉记录。
另一个有效过滤是按框面积占比:检测框面积小于整图0.5%的目标先忽略。管廊里灰尘颗粒、远处反光点经常被模型当成目标,但它们的框通常都很小。这两条规则叠加后,误报数量可以降一个量级。界面侧统计逻辑单独写在一个类里,不要埋在信号处理函数中,否则后期加回放功能时会很难调。
检测结果落盘成CSV,字段按“时间戳、类别、置信度、x1、y1、x2、y2”排,回放功能直接读CSV重建轨迹,不需要重新跑模型。这个文件同时也是论文里分析检测行为的基础数据。
5. 部署避坑:这套系统最容易翻车的5个点
不管源码包写得多顺,环境变了、数据变了、现场相机变了都会翻车。下面5条是我在管廊检测这类项目里反复踩过的血泪经验,按现象、原因、解决的顺序列清楚。
5.1 现象:pip装完ultralytics,import就报错
现象:import torch正常,import ultralytics报DLL加载失败或ModuleNotFoundError。
原因:torch和torchvision版本不匹配,或者先前装过CPU版torch,又混装了GPU版本,环境里出现两份torch。
解决:新建独立虚拟环境,强制统一版本重装。先pip uninstall torch torchvision,再执行:
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install ultralytics==8.0.221逻辑说明:固定ultralytics版本可以避免新版本自动升级依赖带来的连锁报错。装完后用python -c "import torch; print(torch.cuda.is_available())"验证GPU可用,输出了True再继续训练。
参数说明:CUDA 11.8的显卡驱动老,就换cu118对应的index-url;不要混用conda和pip各装一份torch,环境里出现两份CUDA组件时,报错会非常玄学。
5.2 现象:训练loss降到很低,但val mAP上不去
现象:train/box_loss降到0.03左右,val/box_loss停在0.08以上不下去。
原因:标注噪声太大。框画得不贴合、漏标严重、类别标错,小数据集对标注质量极其敏感,20%的坏标注就能把结果拉垮。
解决:做一次标注回归检查。随机抽50张训练图,把TXT标签按坐标画框回原图,逐张盯着看:框有没有明显偏移目标、有没有标漏、类别有没有标反。管廊暗光图里,标注者自己都可能看不清目标边缘,这一步尤其必要。把坏样本清出去重新训练,通常比加数据更有效。
5.3 现象:训练集上效果不错,到现场视频就漏检
现象:论文指标看着可以,但拿到实际现场视频一跑,积水明晃晃地漏检。
原因:训练数据和现场分布不一致。训练集如果是网上公开数据集或实验室环境拍的,现场的光照、相机角度、廊内管线背景、画面分辨率全变了,模型会把这些当成无关特征忽略掉。
解决:最有效的是现场抽帧补训。从现场视频里抽200张代表性画面,混合进原训练集重训一轮,不要把原数据集删掉,混合训练才稳。模型层面把imgsz从640提到960,conf降到0.2,配合连续帧确认压误报。
5.4 现象:摄像头画面卡顿、CPU占用100%
现象:界面打开后画面一帧一帧跳,拖动窗口都卡,任务管理器里CPU满载。
原因:模型推理放在了GUI线程,每帧推理阻塞界面刷新;或者模型用了l、x档,机器根本跑不动。
解决:按第4章的QThread方案把推理隔离到子线程,再对视频做跳帧处理,30fps输入只推理15fps,画面显示原帧、检测结果滞后一帧,人眼基本无感。如果还卡,把模型从yolov8s换成yolov8n重新训练,精度掉一点但帧率能翻倍。
5.5 现象:同一模型在两台电脑上结果不完全一致
现象:同一段视频,A机器检测到30个目标,B机器检测到26个,还偶有类别差异。
原因:GPU和CPU推理存在浮点差异,批量归一化和预处理细节不同;如果导出过ONNX,动态输入形状和letterbox填充也可能产生偏差。
解决:端侧部署时固定输入尺寸导出,预处理里的BGR转RGB、归一化、letterbox参数全部写死在端侧代码里,不要交给上层应用自己拼。验收以端侧实测为准,不要在PC和板卡之间比数值。ultralytics内部前处理是统一的,一旦自己重写预处理器,很容易引入不确定的偏差。
6. 模型导出与现场回归验证:交付前必跑的两个步骤
6.1 导出ONNX:从best.pt到端侧推理的第一步
如果要把这套系统迁移到边缘设备,比如RK3588这类常用板卡,PyTorch权重不能直接跑,得先导出ONNX再转端侧格式。第一步命令:
yolo export model=best.pt format=onnx imgsz=640 opset=12导出时imgsz必须和训练对齐,opset=12是为了兼容板卡的转换工具链。导出后用 onnxruntime 在PC上跑一遍,确认与原权重输出误差不超过0.5%,再做后续量化。量化后精度通常掉1到3个百分点,管廊这种大面积深色背景的场景,视觉影响一般不明显。
6.2 用一段现场视频做回归验证
交付前别只盯mAP,在目标机器上统计真实FPS和置信度分布:
import time import cv2 from ultralytics import YOLO model = YOLO('best.pt') cap = cv2.VideoCapture('field_clip.mp4') sample, fps_sum = 0, 0.0 while cap.isOpened(): ret, frame = cap.read() if not ret: break t0 = time.time() r = model(frame, conf=0.25, verbose=False)[0] fps_sum += 1 / (time.time() - t0) sample += 1 if sample == 100: break print(f"avg_fps={fps_sum / sample:.2f}")逻辑说明:统计的是端到端推理帧率,包含预处理、前向推理和后处理。桌面GPU和板卡差距可能到5倍以上,这个数字直接决定界面跳帧策略和可用的并发路数。
6.3 做一个阈值灵敏度基线
我每次交付前都会跑三组置信度——0.15、0.25、0.35,分别统计检测出的目标数和疑似误报数,把结果写进version.txt,记录模型sha、推理阈值、imgsz和预处理方式。这个习惯救过我很多次:现场反馈误报多的时候,先看version.txt确认对方用的是不是同一份配置,再谈调参。管廊项目最怕的不是精度低,而是换一台机器、换一个相机,行为就变了。把模型、阈值、预处理和版本号四样东西绑在一起交付,到现场再出问题,至少能快速定位是哪一环变了。希望帮到你。
本文还有配套的精品资源,点击获取