下水道缺陷检测YOLO实战:2364张标注数据集与训练避坑指南
发布时间:2026/10/1 13:17:55 作者:尧图编辑部 阅读量:1,286

简介面向YOLO系列算法的下水道缺陷检测标注数据集覆盖关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵、碎片等典型缺陷类别适合目标检测入门、算法对比与工程验证可用于排水管道巡检、市政设施维护等场景的缺陷自动识别。压缩包共2000个文件包含1636个VOC格式xml标注与364个YOLO格式txt标注两种格式分别存放配合数据集配置文件data.yaml和已划分好的训练/验证目录可直接用于yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等模型训练与测试。YOLO格式标注采用class x_center y_center width height的归一化坐标写法便于快速读取xml格式则可与传统检测流程或标注工具衔接。标注文件名末尾带有缺陷类别关键词方便按关节偏移、裂纹、碎片等目标筛选数据。整个压缩包约7.43MB学习人数已达66人适合需要现成下水道缺陷样本开展实验的学生和算法工程师。1. 下水道缺陷检测的 YOLO 实战2364 张带标签图像先把数据缺口补上做排水管网检测的人都有同感CCTV 检测车跑一天能拍几千张管内图像能用来训练模型的标注数据却少得可怜。不少团队卡在 YOLO 算法训练第一步——数据不够、标签格式不统一、类别定义对不上现场缺陷标准。这份下水道缺陷数据集共 2364 张带标签图像覆盖关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵、碎片七类缺陷YOLO 的 txt 和 VOC 的 xml 两种标签都配齐还带 data.yamlzip 解压后就能进 YOLOv5 到 YOLO11 的训练流程。它主要服务两类人刚入行的算法工程师以及做排水管网智能检测、想拿真实工业数据练手的团队——省掉的是最耗时的人工标注和格式整理阶段。下面按拆包顺序把格式、训练、调参与踩坑逐条说清楚。2. 数据集结构与双标注格式txt 与 xml 各管什么用拿到压缩包第一件事不是急着训练而是先把目录结构和标签内容看清楚。这个数据集最讨喜的地方是同一份标注给了两种格式YOLO 直接读取的 txt以及 VOC 惯例的 xml。只跑训练的话用 txt 就够了但这套数据把 xml 也一起给了意味着你可以在 labelImg 里复查、补标注不破坏原始数据。我先把两种格式怎么解析、怎么互相换算讲透后面调数据、写脚本时能省不少事。2.1 解压后的目录与文件对应关系zip 解压后典型的目录结构是这样的下水道缺陷数据集/ ├── images/ │ ├── train/ # 训练图像jpg 格式 │ └── val/ # 验证图像jpg 格式 ├── labels_yolo/ # YOLO txt 标签与 images 中图像一一对应 ├── labels_voc/ # VOC xml 标签内容与 txt 等价 └── data.yaml # YOLO 训练入口配置文件命名是 img_编号 的方式比如 img_0319_1048.txt 对应同一编号的图像文件txt 和 xml 描述的是同一批目标框区别只在编码方式。train 和 val 已经划分好这一点很实用——很多开源数据集只给一堆图和标签划分要自己做而这份省掉了随机抽样的环节也避免了自己切分时把同一类缺陷图全部塞进训练集或验证集导致指标虚高的问题。我的习惯是进训练前先抽查几个 txt 和对应 xml确认图像大小、目标框位置能对上。具体做法是打印出 txt 里所有行再用 labelImg 打开同编号的 xml 看框的位置两边一致再往下走。这一步花不了五分钟但能挡掉后面大部分训练完发现标签错位的返工。2.2 YOLO txt 标签的归一化坐标含义YOLO 格式的 txt 每一行代表一个目标框五个值分别是类别索引、中心点 x、中心点 y、框宽、框高。其中坐标全部做了归一化范围在 0 到 1 之间也就是除以了图像自身的宽和高。写个十来行的脚本就能把标签内容读出来# 读取一个 YOLO txt 标签文件并解析 label_file labels_yolo/train/img_0319_1048.txt with open(label_file, r, encodingutf-8) as f: lines f.read().strip().splitlines() for line in lines: parts line.split() if len(parts) ! 5: continue cls int(parts[0]) x_center, y_center, w, h map(float, parts[1:]) print(f类别索引{cls} 中心({x_center:.4f},{y_center:.4f}) 宽{w:.4f} 高{h:.4f})cls 是从 0 开始的类别索引对应 data.yaml 里 names 列表的下标。x_center 和 y_center 是目标框中心点相对图像宽高的比例w 和 h 是框宽高占图像宽高的比例。如果图像实际是 640×480某个框的 x_center0.5、w0.25那它在像素坐标系里的中心点 x 就是 320框宽就是 160。反过来要把 VOC 的像素坐标转成 YOLO 格式就是用框的像素值除以图像的宽和高。提示检查标签时不要只看坐标范围还要看 w 和 h 是否接近 0。w 或 h 为 0 的框在训练时会直接让 loss 变成异常值这类脏标签一定要在训练前清掉。2.3 VOC xml 与类别索引的映射关系VOC 的 xml 就直观多了坐标是绝对值像素类别直接用名字写在name标签里。一个典型的标注长这样annotation folderimages/train/folder filenameimg_0319_1048.jpg/filename size width640/width height480/height depth3/depth /size object namecrack/name bndbox xmin120/xmin ymin80/ymin xmax340/xmax ymax210/ymax /bndbox /object /annotationxml 里的 name 是字符串YOLO txt 里是数字索引两者靠 data.yaml 的 names 列表对应起来。比如 names 列表第 2 项是 crack那所有 name 为 crack 的 xml 目标在 txt 里类别索引就是 2。批量转换时最容易出错的就是 names 顺序没对上——xml 转 txt 时直接把字符串丢到字典里查索引查不到就说明 names 列表写错了。两套格式在这个数据集里同时存在还有个实际好处你可以用 AnyLabeling 或 labelImg 打开 xml 做增量标注改完再导出成 txt不用从头标一遍。我一般把 labels_voc 当作可编辑的工作副本labels_yolo 当作训练用的只读目录新标注完跑一遍转换脚本覆盖训练标签防止两边不一致。3. 用 YOLOv8 训练缺陷检测模型从 data.yaml 到训练命令与断点恢复这一章直接进入能跑的部分。数据集已经配好了 data.yaml理论上把路径一改就能开训。但 data.yaml 里的路径、类别顺序、验证集指向这三个点任何一个错了都会让训练白跑半天所以我把每一步都拆开讲顺便把超参数怎么根据现场图像调整说清楚。3.1 环境准备与 data.yaml 核对环境部分没有玄学装好 ultralytics 就行pip install ultralytics nvidia-smi # 确认 GPU 可用显存大小决定 batch 能开到多少之后打开 data.yaml把它改成你本机的实际路径。原始内容应有以下关键字段# data.yaml 示例路径按实际解压位置修改 path: D:/datasets/sewer_defect # 数据集根目录 train: images/train # 训练图像目录相对 path val: images/val # 验证图像目录相对 path nc: 7 # 类别数量必须与 names 长度一致 names: 0: joint_offset # 关节偏移 1: obstacle # 障碍物 2: crack # 裂纹 3: buckle # 带扣 4: hole # 洞 5: utility_invasion # 公用设施入侵 6: debris # 碎片path 这一项经常被忽视。如果写成相对路径YOLO 会拿它和当前工作目录拼拼不对就报 dataset not found。我习惯把 path 写成绝对路径train 和 val 保持相对 path 的写法这样换机器时只需要改 path 一行。还要确认 val 目录里确实有图像有些数据集的 val 目录是空的训练时不报错验证时直接全 0等跑完才发现指标没法看。类别顺序必须和 txt 里的索引一致这一点我在第 2 章已经强调过。如果你发现自己数据里 crack 在索引 2而 data.yaml 里写成了 3训练不会报错但模型学到的类别全是错的推理时输出的标签对不上现场缺陷名这种错误最难查。3.2 训练命令与超参数选择数据集配齐、yaml 改好后用 YOLOv8 的命令行直接开训yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ device0 \ projectsewer_runs \ nameexp1模型先用 yolov8n 跑通流程n 是 nano 版权重小、训练快适合先验证数据有没有问题。等流程跑通、确认指标合理再换 yolov8s 或 yolov8m 提精度。数据是 2364 张n 模型在单卡上大概半小时到一小时能跑完 150 轮具体看 GPU 型号。epochs 我一般先给 150配合早停 patience30也就是连续 30 轮 mAP 不涨就自动停。imgsz 默认 640这套数据里裂纹是细长目标后面可以试 960 甚至 1280代价是显存占用和训练时间翻倍。batch 16 在 12GB 显存上跑 yolov8n 没问题显存不够就降到 8不要硬撑否则会 OOM 中断。device0 指第一块 GPU没 GPU 就删掉这一行用 CPU训练会慢很多。提示想看每轮训练后的验证指标命令后面加plotsTrue训练结束会生成 results.png内含 loss、mAP、精度、召回的变化曲线判断收敛情况直接看这张图。3.3 断点恢复与多版本兼容训练中断是常态电源波动、显存不够、远程连接断开都可能打断。YOLOv8 每轮都会把 last.pt 和 best.pt 写到输出目录恢复训练只需要一条命令yolo detect train resume modelsewer_runs/exp1/weights/last.ptlast.pt 是最近一轮的权重best.pt 是根据验证集 mAP 选出的最优权重。恢复训练时用 last.pt因为它保证从断点继续最终拿来推理和验证用 best.pt因为它指标最好。这个习惯我踩过一次坑后才彻底改过来——以前直接用 best.pt 恢复结果学习率被重置后面几十轮的曲线完全乱掉。这套数据同样能跑 YOLOv5 和 YOLOv7。YOLOv5 用仓库里的 train.py命令大同小异python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 150YOLOv9、YOLOv10 和 YOLO11 的命令行格式与 YOLOv8 一致都是 ultralytics 系只需要把 model 参数换成对应的预训练权重名比如 yolo11s.pt。data.yaml 在这几个版本间通用不用改格式这也是这份数据集省事的地方。4. 七类缺陷的类别边界与类别平衡裂纹、碎片、带扣怎么区分数据集好不好的关键不仅在看图数量和标注格式还在类别定义是否贴近现场。下水道缺陷的类别之间本来就容易互相混淆比如细长的裂纹在低分辨率下看起来像碎片带扣变形区域又容易被标成洞。这一章我讲七类缺陷的现场形态和典型混淆点再给出类别不平衡时怎么处理。4.1 七类缺陷的形态特征与混淆点先把类别和现场特征对齐我用一张表说明类别现场形态容易混淆的类别关节偏移 joint_offset管节接口错位、位移通常有环形痕迹障碍物障碍物 obstacle石块、树根、沉积物堆体积大碎片、公用设施入侵裂纹 crack细长线状裂缝方向不规则碎片、关节偏移带扣 buckle管道局部凹陷变形呈扣状或波浪状洞、关节偏移洞 hole圆形或不规则破损开口可见背后管壁裂纹、碎片公用设施入侵 utility_invasion其他管线、电缆穿入管内边缘整齐障碍物碎片 debris散落砖块、泥块、杂物形状不连续障碍物、洞训练前把这张表打印出来人工抽检 50 张左右的标注对照表看有没有标错的。我抽检时最常发现的是碎片和障碍物被混标——两者本身没有严格分界线很多人把大块石头标成障碍物把小砖块标成碎片模型训练时就会学到模糊的边界推理时同一个物体在不同帧里被识别成不同类别。遇到这种情况判断标准只有一个和检测目标挂钩。如果现场验收关心的是管壁结构破损那只看碎片和洞如果关心的是过流断面被侵占障碍物和公用设施入侵才是重点。标注口径统一了模型的决策边界才稳。数据集的标签是已经标好的如果你们项目有自己的验收口径建议先抽检再决定要不要微调标签。4.2 类别不平衡的统计与处理七类缺陷在真实管网里的出现频率差很多裂纹和碎片通常占比高公用设施入侵可能只有几十个实例。先跑个统计脚本看清每类的分布import os from collections import Counter label_dir labels_yolo/train counter Counter() for fn in os.listdir(label_dir): if not fn.endswith(.txt): continue with open(os.path.join(label_dir, fn), r, encodingutf-8) as f: for line in f: line line.strip() if not line: continue cls int(line.split()[0]) counter[cls] 1 names [joint_offset, obstacle, crack, buckle, hole, utility_invasion, debris] for cls_id in range(7): print(f{names[cls_id]}索引 {cls_id}: {counter[cls_id]} 个实例)统计结果出来后如果少数类样本明显偏少有几个常用处理手段。第一是开 mosaic 增强YOLOv8 默认开启 mosaic1.0四张图拼成一张对缓解类别不平衡和提升小目标检测都有帮助。第二是给少数类做图像过采样也就是把含公用设施入侵、带扣这些少样本的图像在训练集里多复制几份简单直接。第三是调整 loss 权重YOLOv8 命令行里没有直接的 class_weight 参数但可以用 ultralytics 的 Python API 改损失函数权重或者直接替换成带 class weights 的配置。我的经验是小样本类超过 5% 就先用过采样加 mosaic效果已经够如果某类连 50 个实例都没有硬训练意义不大不如考虑把这类并到相近大类里比如把公用设施入侵并入障碍物保证每个类别至少有上百个实例模型才能学到稳定特征。5. 训练与推理避坑五个常见问题的排查记录这一章我整理了训练这套下水道数据时最容易遇到的五个问题每个都按现象、原因、解决来写。这些坑不全是我一个人踩的也有团队同学反复问过我的按出现频率排序。5.1 现象loss 不降或震荡明显训练跑了二三十轮box_loss 一直在一个区间里来回跳mAP 基本没变化。最常见的原因是学习率过大加 batch 太小模型在损失面里跳来跳去没法收敛。另一个高频原因是训练集里存在没有标签的图像文件图像本身没被过滤掉但对应的 txt 文件不存在或内容是空的模型在空标签图像上计算出的 loss 是噪声。解决方法是先清空标签再调参。写脚本把 images 里的图片和 labels_yolo 里的 txt 做一次差集核对把没有对应标签的图像移出训练目录。然后检查超参数用预训练权重时把 lr0 从默认 0.01 降到 0.005batch 低于 8 时更要调小学习率否则 BN 层统计不稳定loss 曲线会一直抖。5.2 现象训练过程中 loss 突然变成 NaNloss 在某个 epoch 之后变成 nanval 指标也全部变 nan这种情况多半是梯度爆炸或者输入图像里有损坏文件。下水道图像来自 CCTV 采集偶尔会有截断损坏的 jpg解码出来是异常像素喂给网络后产生极大梯度。解决分两步。先用 PIL 写个脚本扫描全部图像把打不开的文件单独移出来from PIL import Image import os img_dir images/train for fn in os.listdir(img_dir): path os.path.join(img_dir, fn) try: with Image.open(path) as im: im.verify() except Exception as e: print(f损坏图像: {fn} - {e})跑完删掉这些损坏文件同时把对应的 txt 和 xml 一起移走。然后再把 lr0 降一档比如 0.01 改 0.005weight_decay 保持默认即可。如果用的是从头训练而不是预训练权重学习率更要保守从头训练的收敛对 lr 敏感得多。5.3 现象裂纹这类细长小目标几乎检不到训练完的模型对碎片、洞的检测没问题但裂纹基本漏检或者框的位置偏了一大截。本质是裂纹在 640 分辨率下只占几十个像素经过网络下采样后在特征图里只剩一两格信息基本丢了。解决方向有三个。一是把 imgsz 提到 960 甚至 1280直接增加小目标在输入图像里的像素占比显存不够就减小 batch。二是用切片推理把原图切成四块分别检测再合并结果推理时间变长但小目标召回率明显提升。三是换模型结构YOLOv8 没有原生 P2 输出层如果裂纹是主要检测对象可以换用 YOLOv9 或者给 YOLOv8 加自定义小目标检测头。我的建议路径是先试 imgsz960 加 mosaic看裂纹的召回率变化还不行再上切片推理。5.4 现象验证集 mAP 不错现场图像效果差验证集上 mAP50 到了 0.85拿现场新拍的照片一测漏检一堆。这个坑几乎是工业数据的通病验证集和训练集来自同一批 CCTV 设备、同一批管径和光照条件模型学到的其实是这批数据的分布特征而现场图像的管径、镜头角度、光源强度全变了。解决思路是让验证集更硬。把现场采集到的不同管径、不同光照的图像单独归一个测试文件夹用训练好的模型在上面测看哪些场景掉点。然后针对性做数据增强比如扩增 HSV 色域扰动、随机旋转、模拟不同光源方向再把现场图像少量加入训练集做微调。我在实际项目里最管用的一招是把现场最容易漏检的类别图像挑 20 到 30 张手工标注后加进训练集冻结前 50 层只训后面几轮效果立竿见影。5.5 现象txt 标签坐标出现大于 1 或负值训练前检查标签时发现某些 txt 行里出现 x_center 超出 [0,1] 范围或者 w、h 为负。这通常是 VOC 转 YOLO 时用了错误的图像宽高做归一化或者标注框画到了图像边界之外。解决方法是写一个清洗脚本把所有越界坐标 clamp 回合法范围并剔除宽高为 0 的框。clamp 之后框边缘会紧贴图像边界虽然不完美但至少不会让 loss 产生异常值。这类问题必须放在训练前处理因为 YOLO 训练时不会自动检查坐标越界只会默默把 loss 算错。6. 模型验证与批量推理从混淆矩阵到检测结果落盘训练结束不是终点能不能把模型用起来才是。这一章我给两个最常用的动作复现验证指标以及批量推理并导出结果。6.1 复现验证指标与混淆矩阵用训练时的 best.pt 重新跑一遍验证集确认指标是稳定的yolo detect val \ modelsewer_runs/exp1/weights/best.pt \ datadata.yaml \ batch32 \ plotsTrue输出里会给出 mAP50、mAP50-95、精确率和召回率plotsTrue 会生成混淆矩阵和样本预测图。混淆矩阵是我每次必看的图它能直接告诉你哪两个类别在互相误判比如裂纹被识别成碎片这和第 4 章的类别边界问题对得上。如果混淆矩阵里某个对角元素明显偏低回到标签抽检大概率是标注口径问题而不是模型问题。6.2 批量推理脚本与参数落盘验证通过后用 Python API 对一批现场图像做推理from ultralytics import YOLO model YOLO(sewer_runs/exp1/weights/best.pt) results model.predict( sourcetest_images/, conf0.25, iou0.45, saveTrue, save_txtTrue, projectsewer_inference, namefield_test, ) for r in results: boxes r.boxes print(f{r.path}: 检出 {len(boxes)} 个目标) for box in boxes: cls_id int(box.cls[0]) print(f 类别{model.names[cls_id]} 置信度{float(box.conf[0]):.3f})conf 是置信度阈值现场图像光照差、目标模糊时0.25 比默认的 0.25 更激进但会把误检也放进来如果对误检敏感调到 0.4 以上。save_txtTrue 会把检测结果按 YOLO 格式写成 txt便于后续接报表系统。我一般先跑一轮全 conf 阈值扫描找现场误检和漏检的平衡点再固定最终参数。从那以后我每次拿到新数据集不管来源是哪都强制先走一遍标签合法性检查、类别分布统计、抽检三个动作再进训练。这套流程在不少水务和管网项目上帮我省了整周的返工时间希望帮到你。数据集的 zip 包在项目下载页可以直接拿到里面 images、两套标签和 data.yaml 都齐了解压改完 path 就能复现上面的全部过程。本文还有配套的精品资源点击获取