简介:这份风电叶片缺陷目标检测数据集包含约1200条已标注样本,采用YOLO标注格式,覆盖blade、drenaj、erozyon、etiket等10个类别,适用于风电叶片表面缺陷识别、无人机巡检影像分析等计算机视觉任务。包内共2000个文件,其中1275个txt标注文件、724张jpg图像以及1个Python可视化脚本,压缩包大小45.2MB,便于快速下载与解压。目前已有83人浏览学习,属于面向风电视觉检测的小型实用数据集。该数据集已做好训练集、验证集、测试集划分,运行show脚本即可直观查看标注效果,省去手动整理样本的流程;10个细分类别贴近实际风机叶片巡检场景,可直接用于YOLOv5等目标检测模型的训练与验证。对风电智能运维、缺陷检测算法入门及网络结构改进研究均有实用价值。
1. 这套风电叶片缺陷目标检测数据,到底能解决什么问题
风电叶片缺陷图像目标检测数据,对风电运维或工业视觉方向的工程师来说,是一个典型的“开局资源包”:约1200张已标注图片,YOLO格式,拿到就能进训练管线。它的价值在于跳过采集和标注环节,直接验证叶片裂纹、腐蚀、雷击损伤这类缺陷能不能用目标检测模型自动找出来。但1200张属于小样本规模,缺陷形态细长、类别不均衡、现场背景纹理复杂,这些都比常规目标检测更考验数据组织和参数处理。这篇内容从标注格式核查、数据集划分、最小训练命令,一路讲到避坑和增强手段,适合想从零跑通 YOLO 训练管线的新手,也适合准备把模型推向现场的风电工程师。先看数据,再谈训练。
2. 读懂YOLO标注数据:缺陷类别、txt结构与质量核查脚本
拿到这样规格的数据资源后,我最先做的事不是安装环境,而是打开 labels 目录,把 txt 文件一个个读出来看。因为 YOLO 格式的标注信息量很少,每一行只有类别 ID 和四个归一化坐标,数据质量到底怎么样,光看文件数量完全判断不了。
2.1 叶片缺陷有哪些类型,标注难度差在哪
风电叶片缺陷不是“一个类别”那么简单。从这类巡检数据集的常见标注习惯来看,缺陷大致会覆盖:表面裂纹(细长、方向任意、贯穿或半贯穿)、前缘腐蚀与砂蚀(大范围、边界过渡模糊)、涂层剥落(不规则块状)、雷击烧蚀(焦黑色斑块)、覆冰(亮白色附着物)等。有些数据集还会把维修痕迹、密封胶缺失单独标出来,这类目标形态更杂,标注一致性也最差。
不同缺陷类别的矩形框标注难度差异很大,这会直接影响后续训练策略。我把常见类别、形态特点和训练时最可能遇到的问题整理成一张表:
| 缺陷类别 | 形态特点 | 矩形框标注难度 | 训练时常见问题 |
|---|---|---|---|
| 表面裂纹 | 细长、方向任意、可能分叉 | 中上,水平框难以贴紧斜向裂纹 | 小目标漏检,框内背景占比高 |
| 前缘腐蚀 | 大面积、边界渐变过渡 | 高,边界无法用矩形框贴合 | 框内背景多,误检率高 |
| 涂层剥落 | 块状、形状不规则 | 中 | 与阴影、油污互相混淆 |
| 雷击烧蚀 | 黑褐色斑块,边缘相对清晰 | 低 | 样本量少,类别不平衡严重 |
| 覆冰 | 白色半透明,对比度差 | 高 | 与亮白色叶片本体难以区分 |
一个反直觉的点:直接从 COCO 预训练权重迁移,并不等于万事大吉。COCO 里的目标以人、车、日常物体为主,特征是边缘清晰、形态完整;叶片缺陷则很多是细线、大面积的渐变伪影,预训练权重只能带来基础纹理表达和梯度特征,它并不天然认识“裂纹”这个抽象概念。所以对这套数据来说,读懂标注内容、确认框的贴合度,比急着跑训练更重要。
2.2 YOLO 格式的 txt 结构:每一行坐标怎么读
YOLO 的标注文件是每张图对应一个同名 txt。每一行代表一个目标框,由空格分隔五段内容:class_id x_center y_center width height。后四个值全部做了归一化,除以图像自身的宽和高,取值范围在 0 到 1 之间。这里的 x_center、y_center 是框中心的相对位置,width、height 是框的宽度和高度相对整张图的比例。
举个例子,一张 2000×1000 像素的图像,某个裂纹框左上角在像素坐标 (100, 400),右下角在 (260, 560),那中心点就是 (180, 480),宽 160,高 160。归一化之后得到 x=180/2000=0.09,y=480/1000=0.48,w=160/2000=0.08,h=160/1000=0.16,对应的一行就是0 0.09 0.48 0.08 0.16。为了把归一化坐标还原成能直接对图的像素坐标,我一般先跑下面这段脚本:
from PIL import Image from pathlib import Path img_path = Path("images/train/blade_001.jpg") txt_path = img_path.with_suffix(".txt") img = Image.open(img_path) w, h = img.size print(f"image size: {w} x {h}") for line in txt_path.read_text().splitlines(): cls, xc, yc, bw, bh = map(float, line.split()) px_x = int(xc * w) px_y = int(yc * h) px_w = int(bw * w) px_h = int(bh * h) x1 = px_x - px_w // 2 y1 = px_y - px_h // 2 x2 = x1 + px_w y2 = y1 + px_h print(f"class={int(cls)} box px=({x1},{y1})-({x2},{y2})")这段脚本的作用是把 YOLO 的归一化坐标还原成图像素坐标,方便在原图上核对边界框是否贴合缺陷区域。参数说明:line.split()按空白切分,能同时兼容空格和 Tab;如果标注文件里混入了逗号分隔,需要先把逗号替换成空格再处理。取整用的是int()向下取整,换算前后可能有一两个像素的偏差,对训练没有影响,但排查标注质量时完全够用。核对时重点看两类问题:一是框是不是把缺陷周围的纹理大片包进去了,二是细长裂纹的框是不是被标成了接近正方形的区域。
2.3 数据质量核查脚本:训练前必做的一步
拿到数据后最省时间的操作,是写一个脚本把全部 txt 刷一遍。我多次见过因为标注文件里有坐标越界、空 txt、或者 class_id 和类别表对不上,导致训练中途静默跳过部分图像的情况。先检查,后训练,能省掉大量排错时间。下面这段脚本检查四类问题:类别 ID 是否越界、归一化坐标是否非法、是否存在空标注文件、标注文件能否对应上图像文件。
from pathlib import Path img_dir = Path("images/train") label_dir = Path("labels/train") category_names = {0: "crack", 1: "erosion", 2: "peel", 3: "lightning"} bad_count = 0 empty_files = 0 checked = 0 for label_path in label_dir.glob("*.txt"): checked += 1 lines = [line.strip() for line in label_path.read_text().splitlines() if line.strip()] if not lines: empty_files += 1 continue for line in lines: parts = list(map(float, line.split())) cls = int(parts[0]) x, y, w, h = parts[1:] if cls not in category_names: print(f"bad class: {label_path} -> {line}") for v in (x, y, w, h): if not (0 < v <= 1): print(f"bad value: {label_path} -> {line}") bad_count += 1 print(f"checked={checked} empty={empty_files} bad_value={bad_count}")脚本逻辑说明:category_names需要按实际数据集的类别数量改,如果只有两类缺陷就只保留两个键值对。校验条件用0 < v <= 1而不是0 <= v,是因为宽度或高度等于 0 的框几乎可以判定为标注失误,而中心点恰好在 0 或 1 上也说明框多半贴着图像边缘,宁可标红复查也不能直接放行。另一个容易踩的坑是文件后缀大小写不一致:图片是.JPG而 txt 是.jpg,或者图片重命名过但 txt 没跟上,都会导致图像被跳过。这种情况我在工程里见得比坐标越界还多。
2.4 类别分布与宽高比统计:判断数据好不好训
质量核查通过之后,还需要看两个统计量:类别框数分布和边界框宽高比分布。类别分布直接决定样本不平衡程度,宽高比分布则决定输入分辨率和增强策略的调整方向。我用下面这段脚本快速统计:
import numpy as np from pathlib import Path wh_list = [] for label_path in Path("labels/train").glob("*.txt"): for line in label_path.read_text().splitlines(): parts = line.split() if len(parts) >= 5: w, h = float(parts[3]), float(parts[4]) wh_list.append((w, h)) arr = np.array(wh_list) ratios = arr[:, 0] / np.maximum(arr[:, 1], 1e-6) print(f"boxes={len(arr)}") print(f"median_ratio={np.median(ratios):.2f}") print(f"ratio>10 or <0.1: {np.sum((ratios > 10) | (ratios < 0.1))}")参数说明:np.maximum(arr[:, 1], 1e-6)是为了防止高度为 0 时除零报错。这个统计能给出两个重要信息:如果宽高比大于 10 或小于 0.1 的框数量很多,说明数据里有大量细长形态缺陷,后续推理时直接缩放整图会在缩小的图像上丢失细线特征,需要提高输入分辨率或考虑切片检测;如果缺陷框大量出现在图像边缘,说明拍摄习惯把缺陷放在了画面边缘,此时推理阶段要关注边缘漏检。1200 张图不算多,但这两个统计量已经能把训练方向定下来一大半。
3. 跑通最小训练流程:数据目录、data.yaml与YOLO入门命令
数据读完、核查完,下一步就是组织目录并用 YOLO 工具链把训练跑起来。yolo 入门教程里经常跳过目录组织这个环节,但目录错了,后面所有训练命令都会在数据加载阶段出问题。
3.1 数据目录怎么组织,train/val 按什么比例切
用 ultralytics 的 YOLO 工具训练,数据集目录结构必须是两套平行目录:images 下放图像,labels 下放同名 txt。常见结构如下:
dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/images 和 labels 下的文件名必须完全一致,只是扩展名不同。切分比例一般按 9:1 或 8:2。我还会额外留出 20 到 30 张完全不参与训练和验证的图作为盲测,因为验证集也是模型见过的分布,只有没参与任何训练的盲测图才能模拟现场新拍照片的效果。下面是一个按 9:1 切分的脚本:
mkdir -p dataset/images/train dataset/images/val dataset/labels/train dataset/labels/val shopt -s nullglob imgs=(dataset/images/*.JPG) total=${#imgs[@]} val_n=$((total / 10)) for i in "${!imgs[@]}"; do img=${imgs[$i]} label=${img/images/labels} label=${label/JPG/txt} if (( i < val_n )); then mv "$img" dataset/images/val/ mv "$label" dataset/labels/val/ else mv "$img" dataset/images/train/ mv "$label" dataset/labels/train/ fi done脚本说明:shopt -s nullglob是为了防止目录里没有匹配文件时,把字面量通配符当作文件名传进去。label=${img/images/labels}和label=${label/JPG/txt}是 bash 的字符串替换,先把路径里的 images 替换成 labels,再把扩展名替换成 txt。如果图片后缀是小写的.jpg,把脚本里两处 JPG 都改成 jpg 即可。切分前建议先把所有图像统一重命名为纯英文加下划线,例如blade_001.jpg,避免中文或空格在部分环境里导致路径读取失败。
3.2 data.yaml:路径、类别名与常见配置错误
训练前还要写一个 data.yaml,指向数据目录并声明类别名。最小可用的配置如下:
path: /home/user/dataset train: images/train val: images/val names: 0: crack 1: erosion配置说明:path建议直接写绝对路径,避免相对路径的基准目录和 yaml 文件所在目录不一致导致加载失败。train、val都是相对于path的子路径。names字典里的数字 ID 必须和 txt 文件里的 class_id 严格对应,一旦写错,模型会把类别 A 学成类别 B,训练不报错但验证结果完全不可信。yolo 入门新手最容易犯的错是不写val这一项,导致训练时拿训练集当验证集评估,指标虚高,换到现场立即露馅。
3.3 最小训练命令与四个必须调的参数
环境准备上用 ultralytics 开源工具,安装命令一行就能完成:pip install ultralytics。这里不多展开依赖环境配置,pytorch 版本按官方推荐装即可。进入训练环节,跑通这套数据的最小命令如下:
yolo detect train \ data=dataset.yaml \ model=yolo11n.pt \ epochs=150 \ imgsz=1280 \ batch=8 \ device=0 \ patience=30 \ project=runs/wind参数说明逐个来:model=yolo11n.pt是 ultralytics 提供的轻量级预训练权重,首次运行时会自动下载,不需要手动找链接。epochs=150在小数据集上足够判断收敛趋势,但配合patience=30早停,真正跑满的情况不多。imgsz=1280是这套数据最关键的一步——叶片裂纹属于细长小目标,在 640 分辨率下缩得只剩几个像素,特征基本丢失,提升到 1280 后召回会明显改善。代价是显存占用上升:1280 输入加 batch=8 大约需要 12GB 显存,如果显存只有 8GB,先降 batch 到 4,再考虑降到 960,尽量不要低于 960。device=0指定第一块 GPU,没有 GPU 时改用device=cpu,但训练速度会慢一个数量级以上。
3.4 训练日志里的 YOLO 损失函数与收敛判断
训练开始后,终端会实时输出三个损失:box_loss、cls_loss、dfl_loss。box_loss 衡量预测框和标注框在中心点与宽高上的误差,cls_loss 是缺陷类别分类的交叉熵损失,dfl_loss 是 YOLO 系列使用的分布焦点损失,负责让边界回归更精确。三个值都在波动下降是正常状态,重点要看验证集指标而不是训练集 loss。训练完成后,ultralytics 会输出 best.pt 和 last.pt 两个权重,我习惯用 best.pt 做验证。需要强调的是,YOLO 的损失函数在训练后期如果出现某一步骤突然变成 nan,优先检查数据里是否存在类别 ID 越界或标注坐标异常的样本,这比调整学习率更重要。
4. 训练避坑与排查:标注边界异常、类别失衡与过拟合
1200 张规模的数据集,问题往往不在模型结构,而在数据和训练设置的配合上。下面五条是我在类似项目里反复踩过的坑,按现象、原因、解决的顺序写清楚。
4.1 训练一启动就崩:坐标越界、空txt与文件名不一致
现象:运行yolo detect train后十几秒就报错退出,报错内容常包含 “all bounding boxes should have positive height and width”,或者训练卡在 loading 阶段很久后直接中断。另一个隐蔽版本是训练正常开始,但日志里显示的图像数量明显少于数据目录里的文件数。
原因:标注文件里存在中心点坐标或宽高为 0、负数的行;txt 文件为空;或者 txt 文件名与对应图片名不一致。还有一种常见情况是图片后缀大小写不统一,导致 ultralytics 按名称匹配标签时找不到对应文件。
解决:先用第 2.3 节的质量核查脚本全量刷一遍,把非法坐标和空文件清理掉。文件名问题用一句话就能排查:在 dataset 目录下对每个图片文件检查同名 txt 是否存在,缺哪个补哪个。我建议拿到数据后第一时间统一重命名,全部转成纯英文小写加下划线格式,这是成本最低的后悔药。
4.2 框都在但位置不准:mAP50虚高,mAP50-95上不去
现象:训练结束 mAP50 超过 0.8,看起来不错,但 mAP50-95 只有 0.3 上下。画出的框确实框住了缺陷区域,但边界偏移明显,尤其斜向裂纹的框像“套歪了”。
原因:这是矩形框在细长缺陷上的典型失效模式。水平矩形框包一条斜向裂纹,框内不可避免混入大量背景,而 mAP50-95 对边界框和真实框的交并比要求更严格,只要偏移一点分数就掉得厉害。1200 张图里如果大量标注是这种斜向细长框,模型学到的边界自然不准。
解决:首选把imgsz从 640 提到 1280,小缺陷的特征分辨率上来后,定位精度会明显提升。如果斜向目标特别多,可以按裂纹方向做旋转增广:把原图旋转 15 度、30 度后再训练,相当于人工扩充了角度样本。项目允许的情况下,缺陷检测采用旋转框 OBB 方案会显著改善,但标注格式需要从水平框换成旋转框,这是额外成本,按实际需求取舍。
4.3 某个类别precision一直是0:类别不平衡与标签噪声
现象:模型对裂纹、腐蚀都检得不错,但雷击烧蚀这个类别的 precision 一直为 0,一个都检不出来。打开统计结果,雷击烧蚀的标注框只有四五十个,而裂纹有两千多个框。另一种情况是类别存在,但标注时把部分雷击框错标成了裂纹,模型输出概率始终混乱。
原因:小类别的梯度信号被大类别淹没,训练时模型倾向于把所有目标都预测成高频类别;标签噪声则让模型学到错误的类别映射关系。YOLO 的类别 ID 不要求必须从 1 开始,但必须和 names 配置严格对应,一旦错一位,多少轮训练都救不回来。
解决:先检查类别分布表,如果小类别框数不足 50,优先做复制粘贴增强——把该类缺陷的小图从原图中裁下来,贴到叶片背景的随机位置生成新标注,能把有效样本量提到 100 以上。然后重新核对整流数据里该类别的 class_id 是否统一。这两件事做完再回来训练,比换任何模型结构都有效。
4.4 训练后期val指标崩了:小样本数据过拟合
现象:epoch 60 之前验证集 mAP 稳步上升,epoch 70 之后训练损失还在下降,验证集指标却开始回落抖动。最后 best.pt 停在 epoch 60 附近,继续训练也不能突破。
原因:1200 张图对深度学习来说很少,训练后期模型开始记住训练集里的背景纹理、光照条件,而不是缺陷本身的语义特征。典型表现是训练损失一路走低,验证集却原地踏步或下滑。
解决:训练命令里加上patience=20到patience=30,让训练在验证集不再改善时自动回退到最佳权重,而不是硬跑满 epochs。比较 best.pt 和 last.pt 在验证集上的表现,差异越大说明过拟合越严重。数据增强方面不要贪多,马赛克增强在小数据集上会把缺陷目标缩小到几乎看不见,建议把mosaic关掉或调到 0.2 以下,具体原因在第 5 章展开。
4.5 白天检测稳定、背光图像漏检:光照分布不均的隐藏坑
现象:验证集上的指标不错,但拿现场巡检拍的逆光、暗角、雾气图像一测,漏检率突然飙升。
原因:数据集的拍摄条件相对单一,光照变化没有在 1200 张图里充分覆盖。叶片表面反光或阴影被框进了标注区域,模型学到的可能是光照纹理而不是缺陷边界,换到背光场景立刻失效。
解决:训练时打开颜色增强参数,把hsv_h、hsv_s、hsv_v的扰动调大一些,让模型在训练阶段见过更多饱和度、亮度变化。推理阶段可以先对输入图像做直方图均衡化再送进模型,对背光图效果立竿见影。严格来说这个问题应该在数据采集阶段通过补充多光照样本解决,但事后用增强和预处理也能缓解大部分。
提示:小数据集上的验证集指标波动明显,选择模型权重时不要只看最后一轮的数值,统一用验证集效果稳定的 best.pt。
5. 把1,200张数据用到极致:迁移学习、增强参数与盲测验证
5.1 模型选型与预训练权重选择
1200 张数据规模,直接训练大模型不是好选择。我一般从 yolo11n 这种轻量级模型起步,参数量小、训练收敛快,在 1280 分辨率下显存压力也可控。预训练权重由 ultralytics 在首次运行时自动下载,等于把 COCO 上的通用特征迁移到叶片缺陷任务上。常见误区是为了追求“干净”而关闭预训练权重从头训,小样本场景下不建议这么做,预训练带来的梯度起点远比自己从头学更可靠。
5.2 针对叶片缺陷的增强参数调整
小数据集能不能学好,数据增强的取舍比模型结构更关键。我会单独写一个增强配置,核心是关掉马赛克、保留适当的颜色扰动:
hsv_h: 0.02 hsv_s: 0.7 hsv_v: 0.5 scale: 0.4 mosaic: 0.0 fliplr: 0.2参数说明:mosaic设为 0 是因为叶片图像以中大型缺陷为主,四张图拼接后每个缺陷都被进一步缩小,细纹特征更容易丢失,弊大于利。fliplr水平翻转可以用 0.2,因为裂纹和腐蚀的方向性对检测影响不大。hsv_v调到 0.5 是为了模拟现场背光和明暗变化,缓解第 4.5 节提到的光照分布问题。scale=0.4控制随机缩放的幅度,太大同样会把缺陷缩没了。
5.3 盲测验证与现场交付
训练完成不代表可以交付。我习惯从巡检视频里抽出 20 张模型从未见过的帧,用yolo detect predict批量跑一遍,数清楚误报数和漏报数,再统计单帧推理耗时。这个盲测环节的价值在于它能暴露验证集被“记住”的假象。部署时如果用到 TensorRT,注意输入分辨率提升到 1280 后推理耗时和并发路数都会明显变化,需要按现场实际算力重新压测,不要拿着 640 分辨率的性能数字估算 1280 的能力。
数据集的终点不是训练完一个模型,而是模型能力被现场验证过。我在风电项目上吃过亏——当时过于信任验证集指标就把模型放上线了,结果现场第一周误报率高到运维同事直接要求下线。后来养成的习惯是:只要是这种千张级别的小数据集,盲测验证永远比验证集指标可靠。希望帮到你。
本文还有配套的精品资源,点击获取