简介一套针对下水管道缺陷检测的YOLO数据集共980张已标注图像覆盖关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵、碎片等典型缺陷适配YOLOv5至YOLO11各主流版本可直接用于模型训练、验证与测试。资源总计2000个文件主要有980个YOLO格式txt标注、980个VOC格式xml标注、39张jpg图像和1个data.yaml配置压缩包约33.89MB已划分好数据并配好config下载即可快速接入训练流程。目前已有417人浏览学习适合计算机视觉初学者进行缺陷识别实践也适合市政管道维护相关项目直接作为基础数据。通过该数据集使用者能省去繁琐的采集标注工作同时对比YOLO与VOC两种标注格式的差异并在YOLO系列框架中验证不同模型的检测性能是一份实用且轻量的目标检测资源。1. 980张带标签图像能训练出可用的YOLO管道缺陷模型吗拿到一份管道CCTV内窥录像画面上有接头错位、有裂纹、有碎石块人眼能分辨但让模型自动把它们框出来第一步缺的就是一份带标签的训练数据。这个标题里的数据集解决的就是这件事980张下水管道缺陷图像已经按缺陷类别做好了标注用来喂给YOLO系列的检测框架让模型学会识别关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵、碎片七类缺陷。它能不能直接产出一个能用的模型答案是分情况但多数时候够用。替代方案是自己从录像抽帧标注900多张图一个人标也得消耗一两个工作日还不算返工。适合正在做市政管网智能巡检、CCTV检测批处理或者想用YOLOv5/YOLOv8快速验证缺陷检测方案是否可行的工程师。下文从拆数据、搭环境、调参数、排故障到验收按实际落地的顺序走一遍。2. 拆开数据集七类缺陷在管道CCTV画面上长什么样、标签怎么对齐2.1 七类缺陷的工程含义和标注边界下水管道缺陷检测不是通用的目标检测它的难点在于缺陷的定义高度依赖行业标准。关节偏移指的是管节连接处出现错位或者间隙在CCTV画面里通常是一条横向的明显台阶或暗缝障碍物是管道内的石块、树根、沉积物、施工遗留物往往没有固定形状裂纹在混凝土管和塑料管上表现完全不同混凝土管裂纹往往带分叉塑料管裂纹则是细长的亮线带扣是管道修复时使用的不锈钢卡箍或缠绕带属于后加构件洞是结构性破损能看到管道背后的泥土或空洞公用设施入侵指其他管线从侧向穿入或者挂在管壁上最常见的是电线或通信缆碎片则是掉落的管壁碎块、砖石。七类之间有大量混淆比如障碍物和碎片在俯视画面里都是块状物裂纹和洞在小分辨率下都表现为暗色区域公用设施入侵和带扣都是管壁上的异形条状物。标注文件里的边界框往往带有大量主观判断训练时要把这种主观性当成噪声来处理而不是当成标准答案。2.2 图像与标签文件的组织方式VOC格式还是YOLO格式这类打包数据集通常解压后是一个图像文件夹加上一个同名的标注文件夹。标注格式最常见的是两种Pascal VOC的XML以及YOLO风格的TXT。前者把类别名、边界框坐标、图片尺寸写在XML标签里后者每行是一个类别id加上归一化后的中心点x、中心点y、宽、高。用YOLO训练时ultralytics仓库和YOLOv5的官方代码都直接消费TXT如果数据集给的是XML要先跑一遍转换脚本。解压后第一步不要急着看图片好不好看先统计图像数量、检查标注文件是否和图像一一对应。注意文件名乱序、中文名、解压后多了一层嵌套目录这三类问题在打包数据集里出现频率极高。先把根目录结构固定好后面每一步都会少踩坑。2.3 检查标签完整性的三个维度缺失、越界、类别失衡用一段Python脚本快速摸清数据集的底细。不要依赖人眼翻图直接统计。import os from collections import Counter img_dir images # 图像目录 label_dir labels # YOLO txt 标签目录 class_names [joint_offset, obstacle, crack, strap, hole, utility_penetration, debris] missing_labels 0 empty_labels 0 all_boxes [] class_counter Counter() for fname in os.listdir(img_dir): base, ext os.path.splitext(fname) if ext.lower() not in [.jpg, .jpeg, .png]: continue label_path os.path.join(label_dir, base .txt) if not os.path.exists(label_path): missing_labels 1 continue with open(label_path) as f: lines [line.strip() for line in f if line.strip()] if not lines: empty_labels 1 continue for line in lines: parts line.split() cls int(parts[0]) x, y, w, h map(float, parts[1:]) class_counter[cls] 1 all_boxes.append((cls, x, y, w, h)) print(missing labels:, missing_labels) print(empty labels:, empty_labels) print(class distribution:, dict(class_counter)) # 检查归一化坐标是否越界 out_of_range [(c, x, y, w, h) for c, x, y, w, h in all_boxes if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1)] print(out of range boxes:, len(out_of_range))逻辑说明这个脚本先建立图像到标签的映射关系逐一比对同名文件是否存在然后把每个txt里的类别id和归一化坐标解析出来统计类别分布最后检查坐标是否在0到1的合法区间内。参数说明class_names数组的顺序一定要和标签文件里的类别id一致YOLO的类别id是整数索引不是字符串顺序错了模型会学得乱七八糟。检查坐标越界时w和h大于1通常表示标注时把图片尺寸和归一化尺寸搞混了。2.4 图像质量参差下的预处理选择管道CCTV图像和自然图像差异很大。首先是光照管道内要么是LED补光的强中心光要么是探头自带光源打在管壁上的环形光画面边缘通常过暗。其次是纹理混凝土管的粗糙颗粒、污水残留、水渍、苔藓都会成为背景噪声。分辨率方面这类数据集往往混合了不同探头的输出有的是1920x1080有的是1280x720有的甚至是从视频里抽出来的960x540帧。这种情况下直接统一缩放到640x640训练会损失小目标信息特别是裂纹和碎片这种本身占画面比例很小的目标。我的做法是先按原始分辨率训练一轮看baseline再用imgsz960或1280微调对比。图像缩放原理上YOLO训练时默认用letterbox填充而不是直接拉伸保持宽高比的前提下补灰边避免目标变形。3. YOLOv8训练前准备Anaconda环境、图片与标签目录、最小启动命令3.1 Anaconda环境配置要求避开Python版本和依赖的坑YOLOv8需要PyTorch环境而PyTorch对Python版本有硬性要求。YOLOv8在Python 3.8到3.10之间都能正常跑Ultralytics官方仓库会跟随新版本发布适配但没必要追新。用Anaconda建独立环境不要装在base环境里因为ultralytics会更新依赖独立环境翻车了直接删掉重建这是最快的后悔药。conda create -n yolo python3.9 -y conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics逻辑说明conda create指定Python 3.9激活后安装CPU或CUDA版PyTorch。这里用了CUDA 11.8的PyTorch安装源如果你的显卡驱动是更早的版本降级到cu117或cu113。参数说明--index-url参数决定了装什么版本的CUDA编译产物用CPU训练的话直接pip install torch torchvision就行但训练速度会慢几十倍980张图的一轮验证也够用。装完后跑python -c from ultralytics import YOLO; print(ok)验证。3.2 把解压后的数据整理成YOLO能直接吃的目录结构无论数据集原先是VOC还是YOLO格式都要整理成固定结构并且要划分train和val。常见做法是dataset/ train/ images/ labels/ val/ images/ labels/如果原始数据集本身就是YOLO格式的txt标签直接按文件比例划分如果是XML标签需要先转换。划分要按文件级随机分配不要按缺陷类型分配因为一张图里通常同时存在多种缺陷。import os import random import shutil random.seed(42) # 固定随机种子保证可复现 src_img dataset/images src_lab dataset/labels train_img yolo_dataset/train/images train_lab yolo_dataset/train/labels val_img yolo_dataset/val/images val_lab yolo_dataset/val/labels for d in [train_img, train_lab, val_img, val_lab]: os.makedirs(d, exist_okTrue) files [f.split(.)[0] for f in os.listdir(src_img) if f.endswith(.jpg)] random.shuffle(files) split_idx int(len(files) * 0.85) # 85%训练15%验证 for i, base in enumerate(files): if i split_idx: img_dst, lab_dst train_img, train_lab else: img_dst, lab_dst val_img, val_lab shutil.copy(os.path.join(src_img, base .jpg), img_dst) shutil.copy(os.path.join(src_lab, base .txt), lab_dst)逻辑说明按名为基准把所有图像和标签一起分配避免图像拷过去了标签没跟上。随机种子固定后多次划分结果一致换模型对比时公平公正。参数说明85/15的划分对980张图是合理的val集保留约147张足够看出过拟合如果后续用交叉验证可以改成K折但训练成本会线性上升。3.3 训练命令的正确写法模型选择、epoch数和batch sizeYOLO系列的模型选择上yolov8n是最小的nano版本参数量最少训练最快适合先验证数据是否有问题yolov8s和yolov8m是工程上常用的折中。用s起步、看n的结果决定要不要升m这个思路在论文里看不到一线调试时几乎每次都有效。yolov8预训练模型下载是自动的第一次运行ultralytics会自动把权重下载到本地网络慢的话手动下载后放到YOLO权重目录。yolo taskdetect modetrain \ modelyolov8s.pt \ datapipe_defect.yaml \ epochs100 \ imgsz640 \ batch16 \ patience20 \ projectruns \ namepipe_defect_v1逻辑说明task指定检测任务mode是训练模式model用yolov8s.pt这是带COCO预训练权重的起点比从零训练收敛快得多data指向YOLO格式的数据集配置文件epochs设100轮配合patience20做早停连续20轮验证集没有提升就自动结束。参数说明batch16在16GB显存下比较稳妥如果显卡只有8GB降到8或4imgsz先640跑通流程后续微调再调大。数据配置文件里要写清train和val的绝对路径以及类别名列表这个文件写错会导致整个训练白跑。path: /home/user/yolo_dataset train: train/images val: val/images names: 0: joint_offset 1: obstacle 2: crack 3: strap 4: hole 5: utility_penetration 6: debris逻辑说明path是数据集根目录train和val是相对根目录的图像路径names是类别索引表。注意这个索引顺序和前面脚本里class_names的顺序要一致和标签文件里的类别id也要一致。任何一处错位训练不会报错但模型学出来的框和真实类别会全部对不上。训练完成后在runs/pipe_defect_v1目录下能找到weights/best.pt和last.ptbest按验证集指标选last是最后一轮的权重。后面所有验证都用best.pt。4. 把精度从“能跑”提到“能用”损失函数、图像尺寸与类别不平衡的调参4.1 类别不平衡的真实影响mAP虚高和个别类全灭980张图里七类缺陷的分布几乎一定是不均匀的。按常规缺陷出现频率裂纹、碎片、障碍物会占掉大头公用设施入侵和带扣可能只有几十个实例。这种数据直接训练mAP看起来还不错因为占比大的类别把平均分拉高了但按类别单独看公用设施入侵的AP可能接近0模型完全没学会识别它。这是初学者最容易误判的一点只看mAP会以为模型已经能用了。常用应对手段是调整每个类的损失权重。YOLOv8的loss计算中分类损失用的是BCEWithLogitsLoss可以在数据配置或者训练代码里修改class weights。轻量做法是在损失函数里给少数类更高的权重防止梯度被多数类淹没。实用做法是复制少数类样本做数据增强或者用mosaic增强时强制保留少数类。4.2 YOLO损失函数里能动的三个旋钮box、cls、dflYOLOv8的损失函数不是单一公式而是三个损失加权求和边界框回归损失用的是CIoU分类损失是BCE再加上DFLDistribution Focal Loss用来优化边界框的分布预测。训练参数里有三个权重对应这三块box_loss_gain、cls_loss_gain、dfl_loss_gain默认值分别是7.5、0.5、1.5。调参原则是如果你的数据集边界框标注本身很粗糙就把box的权重适当降低因为它会把模型往“精确回归却过拟合标注噪声”的方向带如果类别混淆严重把cls权重提高让模型更关注“是什么”而不是“在哪”。训练命令里加参数的方式是yolo taskdetect modetrain \ modelyolov8s.pt \ datapipe_defect.yaml \ epochs100 \ imgsz640 \ batch16 \ cls1.0 \ box7.5 \ dfl1.5 \ mosaic0.5逻辑说明cls、box、dfl直接对应损失权重mosaic是马赛克增强的概率。参数说明在类别不均衡的数据集上我会优先试cls1.5到2.0多数类被抑制少数类召回率通常有提升。mosaic降到0.5是因为管道CCTV图像里的目标形状比较规整过度mosaic会把完整缺陷切成碎片反而学坏。4.3 图像尺寸和anchor小目标检测的直接抓手裂纹和碎片在640x640输入下只有十几个像素宽下采样32倍之后特征图上的响应非常弱。两个直接有效的做法提高推理输入尺寸到960或1280或者用YOLOv8的tiling思路把原图切成几块分别检测。imgsz1280时显存占用是640的四倍16GB显卡批量大小要降到4。YOLOv8的anchor是自适应学习的不需要像YOLOv5那样手动k-means聚类anchor框但超参数里有个anchor_split阈值它决定特征图上的anchor怎么分配给不同层级。显存不够又需要小目标精度时优先开tiling而不是硬上大分辨率。4.4 数据增强管道CCTV图像不能照搬自然图像的参数默认的hsv_h、hsv_s、hsv_v增强会把管道内壁的颜色变异拉得很大而缺陷检测依赖纹理细节色彩扰动太强反而模糊了裂纹和洞的边界。管道图像的增强重点是几何变换旋转、平移、缩放、翻转。还有一个关键点是cutout把图像的一部分随机挖掉强制模型学上下文这对障碍物遮挡场景很有效。翻译成YOLOv8的训练参数是yolo taskdetect modetrain \ modelyolov8s.pt \ datapipe_defect.yaml \ epochs100 \ imgsz960 \ batch8 \ hsv_h0.0 \ hsv_s0.0 \ hsv_v0.1 \ degrees15.0 \ translate0.1 \ scale0.3 \ fliplr0.5逻辑说明关闭色调和饱和度扰动只保留0.1的明度扰动让模型聚焦纹理而不是颜色几何增强里旋转15度、平移0.1、缩放0.3对应管道探头视角的轻微变化。参数说明如果发现验证集上有大量目标被旋转后漏检把degrees加大到30但要注意标签框跟着旋转后可能超出图像边界ultralytics会丢弃出界的框导致部分标签等于失效所以我一般不超过15度。fliplr保持0.5水平翻转对管道图像安全垂直翻转要关掉因为探头不会倒着拍。5. 训练下水道缺陷模型的避坑指南五个真实翻车现场5.1 现象训练loss下降很快但验证集mAP几乎不动原因标注文件里存在类别id错位模型学到了错误的映射关系。这是最隐蔽也是最常见的坑。有一类打包数据集的txt标签用0做背景类YOLO训练时0就是第一个类别于是一切都偏移了一位。解决跑一遍检查脚本分别打印每个类别图像的标签分布随机挑几张画框可视化人眼确认框和类别对得上再开训练。5.2 现象多数类AP到0.9以上少数类AP却是个位数原因类别严重不平衡而且没有对少数类做针对处理。解决先把train和val里的实例数打印出来看分布找出数量最少的类别。如果标注里连30个实例都没有这个类不要硬训练先把它从训练集剔除模型学好其他六类再用小样本学习单独补这个类。硬加的后果是模型把所有不确定目标都往多数类上猜。5.3 现象有一张图loss特别大训练过程波动剧烈原因标注框越界或者图像损坏。YOLO在数据加载时会对越界框做裁剪但偶尔会把框裁成负宽度产生异常loss。部分数据集里混有0字节的损坏图片读不到图像内容导致训练中断。解决在训练前跑一下图片完整性检查用OpenCV读一遍所有图像把读不出来的挑出来删除或者重新抽帧同时检查每个标签里的w和h是否为正数。5.4 现象管道反光区域的缺陷全部漏检原因CCTV图像里管道底部的积水或管壁反射会在图像中央形成一个高亮区缺陷的对比度在这个区域被严重压缩。YOLO的输入归一化层会把高光和暗影同时拉伸反而压掉中间灰度的纹理。解决对训练图像做自适应直方图均衡化的预处理把过亮区域的高光压暗暗部细节提亮。ultralytics本身不做这个预处理要先进一个preprocess.py处理后再喂给训练。管道CCTV图像上效果非常明显尤其是裂纹和洞对比度提升一个量级。5.5 现象训练到一半显存溢出降低batch后重新训练反而效果更差原因全连接层或者BN层的统计量在小batch上不稳定导致batch size一降模型收敛速度立刻变慢最终精度下降。解决不要直接降batch硬撑先减少输入尺寸imgsz从960降到640显存占用降低一倍以上再不行就换小模型yolov8n。还有一个思路是梯度累积ultralytics的accumulate参数可以模拟更大的batchbatch16配accumulate2等效于32的batch训练过程平稳很多。6. 别只盯着mAP用混淆矩阵和置信度门限验出模型真正的短板一个模型能不能上线不是看训练日志里mAP多少而是看它在一个真实场景的剪辑片段上表现如何。我的习惯是训练完成后先跑验证集推理做完三步第一步生成混淆矩阵和F1-confidence曲线。混淆矩阵能直接看出哪两类缺陷互相混淆比如来根电缆模型的框落在了带扣上说明这两个类的特征在模型内部没有分开F1-confidence曲线的峰值对应的置信度门限就是推理时conf_thres的最优设置。YOLO默认conf0.25对管道图像我经常发现0.35甚至0.45才是最大F1点因为训练数据里背景噪声大低置信度的误检框多。from ultralytics import YOLO model YOLO(runs/pipe_defect_v1/weights/best.pt) results model.val(conf0.3, iou0.5, plotsTrue)逻辑说明val方法会重新跑一遍验证集conf传的是置信度门限iou是NMS的IoU阈值plotsTrue会输出混淆矩阵、F1曲线、PR曲线到当前目录。参数说明conf影响的是预测框的保留数量门限高了召回率下降低了误检上升iou影响的是重叠框合并的敏感度管道缺陷的框通常比较挤0.5过小会留下重复框0.7会让密集裂纹被合并成一个框。第二步把验证集上漏检的图像专门挑出来按类别统计漏检原因。是目标太小、被遮挡、还是光照过强这个统计比mAP数值更直接地告诉你该补什么数据。第三步如果做视频推理要注意单帧检测的抖动问题相邻帧的框跳来跳去不能直接交付。常见做法是把模型输出的框加一个简单的跟踪逻辑记录前一帧的检测结果当前帧没有检出时沿用上一帧的框过滤掉闪烁。我现在做管道检测项目有个习惯训练集里永远留一小部分真实现场的高难度负样本只把它们放入验证集。结果是模型会在这些图像上反复翻车但这些翻车记录恰好保留了缺陷检测这个任务最真实的边界单张图像上绝大多数缺陷都是可判可考的但这个“可判可考”常常依赖上下文于是单帧检测和视频检测之间就出现了落差。而数据集本身的价值不在于它让你在训练集上刷出多漂亮的mAP而在于它逼你把这个问题从头到尾走一遍数据检查、环境配置、参数调整、缺陷排查、验证验收。980张图不够大但配合迁移学习和合理的增强策略足够产出一个能进试点运行的第一版模型。希望这篇笔记对准备下水道缺陷检测这一方向的你有帮助。本文还有配套的精品资源点击获取
