
简介基于YOLOv8的热轧带钢表面缺陷检测项目是一套适合毕业设计、课程设计与期末大作业的完整工程包面向有一定深度学习基础、希望掌握目标检测从数据到部署全流程的学习者。压缩包共2000个文件、约74.49MB其中1800余个txt标注文件构成数据集核心161个md文档覆盖环境配置与使用说明14个Python脚本承载训练/推理逻辑YAML文件定义模型参数少量C/XML/HTML文件则提供工程化调用与可视化展示参考目录结构清晰可按数据、文档、代码三类快速定位。源码经本地编译可运行内容由助教老师审定评审分达98分难度适中既能支撑从数据准备、模型训练到缺陷检测推理验证的完整实验流程也适合直接作为高分毕业设计或课程报告的项目基础。已有251人学习下载对于需要复用成熟带钢缺陷检测方案、节省自建数据与调试时间的学习者具有实用参考价值。1. 热轧带钢表面缺陷检测为什么这条产线最后选了YOLOv8一条热轧产线每分钟跑过上百米带钢表面缺陷如果靠人工目检熟练工也只能盯住其中一小段漏检和误检几乎是常态。把检测任务交给视觉模型之后核心问题变成用什么模型、拿什么数据、怎么在产线上落地。基于YOLOv8实现热轧带钢表面缺陷检测正是当前毕设和中小型工厂视觉改造里最常见的完整方案它自带公开数据集可训练、推理速度快、部署成本低适合在生产节拍内做逐帧检测。这套方案对两类人最有用一是要做缺陷检测方向毕设、需要完整跑通数据到模型全流程的学生二是工厂里想用低成本视觉替代人工抽检的工程师。2. 缺陷长什么样、为什么难检再定数据与模型选型2.1 热轧带钢六类缺陷的特征与难点热轧带钢的表面缺陷不是一种常见的公开数据集和现场质检标准里通常分成六类氧化铁皮rolled-in scale、麻点patches、龟裂crazing、夹杂inclusion、划伤scratches、压痕pitted surface。每一类的成像特征差别很大氧化铁皮是一大片灰黑色块状区域边缘模糊跟正常钢面灰度接近人眼都要凑近看划伤是细长的亮线方向随机在打光角度变化时有时亮有时暗麻点和压痕是密集小坑尺度很小在yolov8的原生输入尺寸下可能只占十几个像素。难点在于这几类缺陷之间没有清晰的边界。夹杂和氧化铁皮在灰度直方图上高度重叠划伤和边缘阴影容易混淆龟裂又像是麻点的密集版。这跟检测行人、车辆不一样那些目标有清晰的轮廓和结构而钢表面缺陷是弱边缘、低对比度、高类间相似度的对象。再加上热轧现场有氧化皮碎屑、水渍、光照不均这些干扰模型很容易把水渍当成缺陷或者把真实缺陷当成背景纹理放过。数据层面还要面对一个现实现场真实缺陷样本永远不够。带钢缺陷属于长尾分布划伤和氧化铁皮常见龟裂可能一周也出不了几条。所以做这个项目不能只指望现场采集通常的做法是用公共数据集做预训练和验证再用现场少量缺陷图做微调。公共数据集里最常用的是东北大学的NEU-DET六类缺陷、每类1800张、共10800张带标注图像标注框基本准确用来做毕设和算法验证完全够。2.2 从Faster R-CNN到YOLOv8我为什么选v8同样做缺陷检测两年前的方案可能是Faster R-CNN或者SSD再早一点是传统图像处理灰度阈值分割、形态学滤波、边缘检测。传统方法在单一缺陷类型、稳定光照下还能用一旦换产线、换钢种、换光源阈值参数全部要重调这就是典型的“玄学调参”而且每一类缺陷要单独设计特征。Faster R-CNN这类两阶段检测器精度不差但推理速度上不去。热轧带钢产线速度按米每秒算相机拍一帧、模型推一帧留给算法的处理时间通常只有几十毫秒两阶段模型很难在这个时间窗里跑满帧率。YOLOv8作为单阶段检测器在精度和速度之间取得了一个比较实用的平衡点这也是它成为缺陷检测默认选择的原因。对比YOLOv5v8有几个关键变化值得知道骨干网络从C3模块换成了C2f模块梯度流更丰富对细小目标特征提取有实际帮助检测头从anchor-based换成了anchor-free省掉了针对缺陷尺寸设计先验框的步骤损失函数换成了DFL CIOU的组合边界框回归更稳。对热轧带钢这种目标尺度跨度大、缺陷形状不规则的场景anchor-free头确实省了不少事。如果你要对比实验写进毕设把v5和v8在同一个数据集上跑一遍最后结论大概率是v8的mAP50高两三个点速度基本持平。2.3 数据集获取与标注复核NEU-DET为主现场数据为辅NEU-DET的获取渠道是公开的关键词直接搜“NEU-DET dataset”就能找到下载地址学术用途免费。下载下来是JPEG图片加XML标注文件格式是VOC的。这里有一个必须做的步骤不要拿到数据集直接开训先做一轮标注复核。复核看三点第一图片尺寸是否统一。NEU-DET原始图片是200x200像素这个尺寸对yolov8来说偏小直接训练容易欠拟合常见做法是把图片缩放或者拼接成大图再训练或者用更大输入尺寸配合数据增强。第二标注框是否紧贴缺陷边缘。有些标注框框得松背景占了很大比例模型会学到错误的前景概念。第三类别名是否一致。XML里的name字段是crazing还是crazing_rolling写错了训练时类别就全乱了。如果厂里有现场采集的数据建议自己用LabelMe或者X-AnyLabeling补标注。标注缺陷的规则是框要尽量贴合缺陷的视觉边界氧化铁皮这种大面积缺陷可以拆成多个框一个框只框缺陷最显眼的部分不要把整片正常纹理包进去。标注完后要按7:2:1划分训练集、验证集、测试集划分时注意同一张图不能出现在两个集合里更不能让同一缺陷的不同裁剪版本跨集合出现否则验证集分数虚高这属于典型的数据泄漏。3. 先把环境跑通、再做VOC转YOLO格式的数据集3.1 Ubuntu20.04下用conda装YOLOv8环境CPU版和GPU版分开说装环境这一步没有太多玄学但很多人在CUDA版本上翻车。先说CPU版本适合只有普通笔记本、先跑通流程的情况训练速度慢但不影响学原理。Ubuntu20.04下先装Miniconda然后建一个独立环境不要用系统自带的Python避免跟其他项目的依赖打架conda create -n yolov8 python3.9 -y conda activate yolov8 pip install ultralytics装完ultralytics之后它会自动拉取所需的torch、torchvision、opencv等依赖。注意这一步CPU版安装的torch是CPU版如果你后来装了CUDA版驱动也没关系CPU版torch照样能跑只是训练时用不了显卡。CPU版验证环境是否正常直接跑一下官方预测命令即可。GPU版多两步先确认自己的显卡型号和驱动支持的CUDA版本再装对应的PyTorch。以GTX 1660 Ti为例驱动支持CUDA 11.8的话先装PyTorch再装ultralyticsconda create -n yolov8gpu python3.9 -y conda activate yolov8gpu pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics装完验证一下GPU是否可用python -c import torch; print(torch.cuda.is_available()); print(torch.__version__)输出第一行是True说明能用GPU如果输出False八成是PyTorch装成了CPU版或者CUDA版本对不上。用nvidia-smi看一下驱动的CUDA版本再对照PyTorch官网的whl列表选对应版本就行。3.2 下载权重并跑通第一张图的检测环境装好后先把官方预训练权重拉下来跑通一次推理。这一步的作用是验证环境、熟悉命令行、同时也是后续训练的一个预训练起点。用yolov8n.pt跑一张带钢表面缺陷图yolo predict modelyolov8n.pt source/path/to/your/image.jpg第一次执行时ultralytics会自动下载yolov8n.pt到当前目录不用手动去网页上找。这个文件大概6MB左右下载速度取决于网络。跑完后会在runs/detect/predict目录下生成标注好的结果图。如果一切正常说明opencv、torch、ultralytics之间的依赖没有问题。注意这里的model参数不只是指定了权重文件还决定了网络结构。yolov8n是nano版本网络最浅、速度最快但精度最低往上还有s、m、l、x四个档。做缺陷检测如果推理设备是工控机GPU用yolov8m起步比较稳如果是嵌入式设备比如RK3588用n或者s。3.3 VOC标注转YOLO格式转换脚本与四个边界坑NEU-DET的标注是VOC XML格式而YOLOv8训练需要的是txt格式每行一条标注格式是“类别id x_center y_center width height”全部归一化到0到1之间。写一个转换脚本很直接import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size).find(width).text) img_h int(root.find(size).find(height).text) out_lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 转为YOLO格式中心点坐标 宽高并归一化 x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h out_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(os.path.join(out_dir, os.path.basename(xml_path).replace(.xml, .txt)), w) as f: f.write(\n.join(out_lines)) # 使用前确认类别顺序必须和后续data.yaml保持一致 class_names [crazing, inclusion, patches, pitted_surface, rolled-in_scale, scratches] os.makedirs(labels/train, exist_okTrue) for xml_file in os.listdir(annotations/train): if xml_file.endswith(.xml): voc_to_yolo(os.path.join(annotations/train, xml_file), labels/train, class_names)逻辑上需要注意四件事。第一class_names的顺序就是txt里类别id的编码顺序这个顺序必须和后面data.yaml里的names完全一致否则训练时类别全错位。第二VOC里normalized坐标是像素值转换时必须除以图片宽高忘记归一化是新手最常见的错误后果是训练时loss直接暴涨、完全没有收敛迹象。第三有些XML文件里存在difficult或occluded标记的框要不要保留取决于你的场景建议先保留后续如果发现误检严重再过滤。第四NEU-DET的图片是200x200归一化之后坐标值差别很小如果发现某行坐标变成负数或者大于1说明原始标注框越界了这种样本要直接删掉不要硬留。4. 训练自己数据集data.yaml、命令行参数与判读4.1 data.yaml怎么组织路径、类别顺序与train/val划分YOLOv8训练前要准备一个data.yaml文件它决定了模型读哪里、分几类。以NEU-DET为例path: /home/user/neu_det train: images/train val: images/val nc: 6 names: [crazing, inclusion, patches, pitted_surface, rolled-in_scale, scratches]path是数据集的根目录train和val是相对于path的子路径。这里是目录路径不是图片列表文件ultralytics会自动遍历目录下所有图片。nc是类别数量names的类别顺序必须和转换脚本里的class_names一致这是整个训练流程里最容易出错、也最不容易发现的一环——如果names顺序跟txt里的class_id不对应训练不会报错但各类的AP值全乱。目录结构上推荐把images和labels分开train和val各一份不要把所有数据放在一个目录里让程序自动划分。手动划分的好处是可以控制分布来自同一条带钢的连续帧要分到同一个集合避免模型“记住”了特定纹理背景。如果你的数据集比较小也可以用k-fold交叉验证但那会明显增加训练时间毕设场景不划算一次7:2:1划分足够。4.2 训练命令逐个参数拆解epochs、imgsz、batch、patience与预训练权重准备好data.yaml后训练命令长这样yolo detect train \ modelyolov8m.pt \ dataneu_det.yaml \ epochs100 \ imgsz640 \ batch16 \ patience20 \ device0 \ workers4 \ cacheTrue每个参数都值得单独说。model指定的是预训练权重路径yolov8m.pt不仅提供初始参数还决定了模型结构换成yolov8l.pt就会加载大模型。这里预训练权重的意义是让模型从COCO上学到的通用特征迁移到钢表面缺陷上比从零训练收敛快得多一般训练到第10个epoch就能看到loss明显下降从零训练要到30个epoch以后。epochs是完整遍历训练集的次数。100是缺陷检测的常见默认值NEU-DET这类单张图片小、样本量一万出头的数据集跑满100轮在GTX 1660 Ti上大概一个多小时。如果数据量更小比如只有两三千张建议降到50-80否则容易过拟合。imgsz是训练时缩放到的输入尺寸。NEU-DET原始图只有200x200直接输进去的话缺陷特征太粗。常见做法是用imgsz640配合Mosaic增强让模型看到更多上下文。注意这个参数在训练和推理时必须一致如果训练用640、推理用1280边界框坐标会整体漂移。batch大小取决于显存。16在8GB显存上跑yolov8m比较稳gtx 1660ti 6GB显存建议降到8。如果显存不够又不方便降batch可以加ampTrue用混合精度训练能省不少显存。patience20是早停参数连续20个epoch验证集mAP没有提升就自动停止。它跟epochs不冲突epochs是上限patience是提前退出的条件。device0指定用第一块GPUCPU训练时改成devicecpu但速度会慢几十倍。workers是数据加载线程数Windows上建议设0Linux可以设4到8设太高会出现内存爆炸或者死锁的玄学问题。cacheTrue会把图片缓存到内存里训练迭代时不用每次重新读磁盘如果你的内存有16GB这个参数强烈建议打开——它在NEU-DET上差不多能让每个epoch提速30%。4.3 训练完看哪些指标mAP50与mAP50-95的意义、损失曲线怎么画训练结束后终端会打印一行验证结果里面有Precision、Recall、mAP50、mAP50-95四个核心数字同时runs/detect/train目录下会生成results.csv、confusion_matrix.png、PR_curve.png等一系列图表。mAP50指IoU阈值取0.5时的平均精度是最常用的指标。对缺陷检测来说mAP50在0.85以上算是可用水平0.9以上算优秀。mAP50-95是IoU从0.5到0.95逐步提升后取平均它更严格反映模型对框位置精度的把握。如果mAP50很高但mAP50-95很低说明框虽然在目标附近但不够准这时可以检查是不是imgsz太小或者标注框本身不紧贴缺陷边缘。results.csv里存着每个epoch的各项loss和指标ultralytics给它也画了一张results.png但那个图只有四条曲线不够细。自己画损失曲线更有意义能看出模型是欠拟合还是过拟合import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) plt.figure(figsize(12, 4)) # box_loss和cls_loss是主要观察对象 plt.subplot(1, 2, 1) plt.plot(df[epoch], df[train/box_loss], labeltrain_box_loss) plt.plot(df[epoch], df[val/box_loss], labelval_box_loss) plt.legend() plt.subplot(1, 2, 2) plt.plot(df[epoch], df[train/cls_loss], labeltrain_cls_loss) plt.plot(df[epoch], df[val/cls_loss], labelval_cls_loss) plt.legend() plt.savefig(loss_curve.png)画这个图的目的是看训练有没有跑偏。正常情况下train_loss和val_loss应该同时下降并且两条线之间差距不会拉得太大。如果train_loss降得很低、val_loss却不降反升就是过拟合处理办法是加数据增强或者减小epochs如果两条线都降不下去先看学习率是不是默认值再看数据集标注是不是有问题。这里有一个血泪经验yolov8对学习率比较敏感如果你把lr0从默认的0.01调高到0.1大概率前几个epoch就NaN不要随便改。5. YOLOv8做钢表面缺陷的典型踩坑现象、原因与解决5.1 训练到一半loss变NaN白跑两小时现象训练前几个epoch一切正常某一步突然打印出lossnan之后所有epoch的loss全是nan模型彻底报废。原因最常见的是学习率设置过高导致梯度爆炸其次是数据里有空标注文件或者标注框越界的坏样本模型读到异常值后loss计算崩了还有一个隐蔽原因是batch_size设置太大导致显存溢出ultralytics报错不明显直接表现为loss变成nan。解决先用git检查数据把所有标注txt文件都读一遍看有没有全零行或者坐标大于1的行有就删掉再把学习率从默认的0.01降到0.005或者加warmup_epochs5让模型逐步进入稳定训练最后把batch_size降到显存占用低于80%的水平。5.2 mAP达到0.9但实际检测时漏检一片现象验证集指标很好看但拿现场拍的真实缺陷图去测模型漏检严重尤其是在光照变化或者带钢运动模糊时。原因训练集和测试集分布不一致。NEU-DET是静态实验室相机拍的现场是高速产线实时流运动模糊、拖影、反光这些干扰在训练集里根本不存在。另外一个原因是验证集划分时手滑同一张原始图被增强成不同版本后分到了两个集合导致验证分数虚高。解决验证指标只能作为参考最终要以现场测试为准。把现场采集的图片补充进数据集中不要只依赖公开数据集划分集合时用图片文件名前缀做分组保证同一源图的所有增强版本都在同一个集合里。这个过程很费功夫但确实能救回漏检问题。5.3 标注转格式后训练不报错但各类AP全错位现象训练正常结束mAP整体看着还行但单独的某一类缺陷AP是0而且预测出来的框全部对应到错误的缺陷类别上。原因class_names顺序在转换脚本和data.yaml里不一致。比如转换脚本里inclusion排在第二位、class_id1但data.yaml里names的顺序把inclusion放到了第三位模型把类别1学成了第三类别的特征表现出来就是类别标签整体平移。解决转换脚本和data.yaml的names数组必须逐字一致不能靠肉眼对比用一个脚本统一生成先定义一份class_names转标注时用这份生成data.yaml时也从这份读取。从源头上锁死顺序比训练完再对标签要省心得多。5.4 训练慢得离谱GPU利用率只有百分之十几现象nvidia-smi看GPU占用率很低但CPU占用接近100%训练一个epoch要很长时间。原因数据加载成了瓶颈。图片读取、缩放、增强都在CPU上跑如果workers设成0全部串行执行GPU一直在等数据。NEU-DET的图虽然小但Mosaic增强会频繁读写。解决workers设成4或8cacheTrue把图片缓存到内存。如果内存不够优先保证cacheTrue而workers设2。还有个细节是图片存储放在SSD上机械硬盘上跑训练IO时间有时候比计算时间还长。5.5 打印了很多警告预测结果框全是歪的现象推理时模型跑得动也不报错但画出来的框位置明显偏移甚至框到正常表面上。原因大概率是推理时imgsz和训练时不一致模型在640下训练在1280下推理anchor-free头的感受野和对齐方式发生了变化。另一类原因是换机器推理时PyTorch版本不一致导致权重兼容性出问题尤其多见于老代码配新torch。解决推理命令里显式写明imgsz和训练时保持相同的值。换机器跑之前用同一张图做一次基准推理把输出框坐标和原来机器上的结果对比一遍坐标偏差超过几个像素就说明环境不一致。5.6 现场环境光一变氧化铁皮全被漏掉现象实验室测得好好的到产线上白天正常、夜间灯一换漏检率立刻上去。原因氧化铁皮这类缺陷的灰度特征跟光照强度强相关模型学到的是固定光照条件下的灰度纹理分布。一旦光源角度或亮度变化输入域的分布跟着漂移。解决训练时加大光照数据增强把hsv_h、hsv_s、hsv_v三个参数调高让模型见过更多不同亮度的样本。更彻底的做法是在现场采集不同工况下的数据反复迭代。这里的规律是缺陷检测项目的排错最终都指向数据分布模型结构反而很少成为瓶颈。6. 上产线前的验证与部署混淆矩阵、按类AP与ONNX导出如果只是把训练好的模型用来交毕设到上一章已经达标了。但如果你想真正把它用起来还需要一条完整的验证和部署链路。先用验证集生成按类别的AP报告yolo detect val \ modelruns/detect/train/weights/best.pt \ dataneu_det.yaml \ imgsz640 \ save_jsonTrue这个命令会输出一个JSON文件里面包含每一类的AP、Precision、Recall。重点看classes_AP50那一栏哪一类低就说明哪一类没学好去补那一类的数据或增强而不是整体重训。再配合混淆矩阵看具体的类别混淆对氧化铁皮和夹杂常互相混淆、麻点和龟裂容易互相误判这几组是热轧带钢缺陷检测的标准难点。部署导出最简单的方式是转ONNXyolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640转出来的ONNX文件可以直接用ONNXRuntime在CPU上推理也可以在RK3588、Hi3516这类嵌入式平台上用NPU工具链转成板端推理格式。对于工控机场景ONNX跑起来的速度和PyTorch差不多但省掉了整个PyTorch环境部署和维护成本都更低。这一步做完整个方案才算闭环。最后说点我的个人习惯每个项目我都会保留一张“坏样本墙”把现场实际跑出来的漏检图、误检图存档标注清楚当时的工况、光照、钢种和缺陷类型。下次调参或者加数据直接对着这些图改而不是凭印象猜。这套做法帮我避开了很多凭感觉调参的弯路。希望这个从数据集整理到部署导出的完整流程能帮你在热轧带钢缺陷检测上少踩几个坑。本文还有配套的精品资源点击获取