
干了这么多年工业视觉质检各种表面缺陷检测项目没少碰但飞机表面缺陷识别这个场景真的算比较“磨人”的一类。先说个直观感受产线上检测螺丝、检测PCB板背景干净、光照可控、缺陷特征固定模型跑起来通常比较“听话”。飞机表面不一样蒙皮材质反光、铆钉缝隙干扰、裂纹细到只有几个像素还要应对户外机坪的自然光照变化整套系统从数据到部署的链路会明显长一截。这次要分享的是我们基于一套飞机表面缺陷检测数据集数据集系列编号恰好是第10618期做的一次完整实践覆盖了缺陷类型梳理、YOLO格式标注规范、模型选型、训练调参、漏检排查全流程数据集中包含的裂纹、凹陷、腐蚀、漆面剥落、划痕等缺陷类别正好是航空维修中最常见的五大类表面异常。如果你正在做工业自动化质检或者想用目标检测解决表面缺陷识别问题尤其是刚接触YOLO、对数据集怎么组织还拿不准的开发者这篇实测记录应该能帮你省掉不少踩坑的时间。1. 项目思路与数据集背后的价值1.1 为什么会盯上飞机表面缺陷检测飞机表面缺陷检测不是一个“锦上添花”的需求而是直接关系到飞行安全和维修成本的硬需求。按常规的维修流程飞机在定检或航线检查时机务人员需要绕着机身、机翼、尾翼一圈圈做目视检查找蒙皮裂纹、凹陷、腐蚀这一类表面损伤。这个活儿极度依赖老师傅的经验同一个划痕新人可能直接忽略老师傅会拿着反光镜反复确认。但人眼检查有两个绕不开的痛点一是长时间重复劳动后疲劳感上来注意力必然下降二是很多细微裂纹必须在特定光照角度下才能看到现场检查环境很难保证每次都理想。所以业界一直在尝试用机器视觉替代或辅助人工目检。但飞机表面缺陷识别难就难在“场景太脏”蒙皮不是纯净背景上面有铆钉、检修口盖板标记、乘客窗口边缘、油漆标识线这些东西在模型眼里全是干扰。再加上缺陷本身的形态差异极大一道裂纹可能只有十几个像素长一处腐蚀却可能覆盖大半个机翼前缘。这种多尺度、多类别、强干扰的检测任务恰好是目标检测模型最头疼的情况。当时接手这个项目首要任务不是训练模型而是先确认数据集靠不靠谱。市面上的公开数据集要么类别太单一要么标注格式混乱直接用很容易翻车。最终采用的这套第10618期数据集至少有三个让我愿意拿它做基准的理由缺陷类别覆盖了航空维修手册中提到的高频表面损伤类型标注是标准YOLO txt格式数据分布不是刻意平衡过的“实验室数据”而是贴近真实巡检场景的不均衡分布。这一点非常重要后面训练时你会发现样本不均衡本身就是一个需要正面处理的工程问题。1.2 数据集怎么撑起整个质检系统任何监督学习项目数据质量直接决定模型上限这句话在缺陷检测领域体现得尤其明显。工业场景里缺陷样本的获取成本很高真实飞机不可能为了采数据故意砸几个坑、划几道口子所以公开数据集更像是这个行业的“公共基础设施”它承担着验证算法思路、评估模型可行性、打磨训练流程的角色。第10618期数据集的组织方式挺有意思它不是简单丢给你几千张图片就完了而是按缺陷类型做了细分目录每张图片对应一个同名的txt标注文件。标注坐标是归一化的中心点加宽高格式和YOLO系列模型原生兼容拿过来几乎不需要做格式转换。这种处理方式对入门者太友好了省掉了理解标注坐标系映射的时间。我当时把压缩包解压之后第一件事就是用脚本统计了一遍各类别的标签数量、图片尺寸分布、目标框大小分布摸清家底再动手这个习惯建议每个做检测的人都保留。从自动化质检系统的角度看数据集承担的角色还不只是训练。一个完整的工业质检流程包含图像采集、缺陷定位、缺陷分类、尺寸测量、结果上报几个环节数据集帮助实现的是中间两块。检测模型定位出缺陷边框和类别之后后端程序可以根据边框像素尺寸结合拍照分辨率换算成物理尺寸判断这个缺陷是否超出维修手册允许的范围。整个过程可以做成无人值守的自动化流程检测结果自动入库维修人员拿着平板就能看到飞机编号、缺陷位置、建议处理等级。这才是数据集的真正价值它让后续一系列工程环节都有了可依赖的算法底座。1.3 五类缺陷的形态特征与检测难点先把这个数据集的类别构成说清楚我用实际标注中看到的情况做个梳理具体分类如下crack裂纹细长线状缺陷经常沿着蒙皮受力方向延伸宽度可能只有两三个像素和蒙皮表面的划痕在外观上高度相似。检测难点是目标太小常规分辨率下很容易被背景纹理吞掉。dent凹陷局部区域蒙皮下凹边缘通常有弧度过渡光照下会形成明显的明暗变化。难在阴影容易被误判为别的缺陷需要模型学习上下文特征。corrosion腐蚀蒙皮表面氧化或化学腐蚀呈现片状、斑驳状颜色偏暗边缘不规则。这类目标面积通常偏大但边界模糊标注容易产生分歧。paint_peeling漆面剥落漆层脱落露出底材形状不规则边缘锐利颜色与周边反差大。相对容易检测但容易与腐蚀混淆因为两者都是片状区域。scratch划痕线状、条带状的机械损伤宽度通常比裂纹粗边缘相对规整。难点是细长目标的长宽比极大模型在特征提取时容易丢失局部连续性。这几类缺陷放在同一个模型里检测最大的挑战就是尺度差异。裂纹和划痕是细长型小目标腐蚀和漆面剥落是面积型中大型目标凹陷又依赖光影线索模型要同时兼顾这些完全不同的视觉特征。前面提到的锚框设计、多尺度训练、损失函数调整本质上都是在解决“一类模型同时适应多类差异极大目标”的矛盾。理解了这个底层冲突后面所有调参动作就不难理解了。2. 模型选型与训练前准备2.1 YOLO系列怎么选更合适目标检测模型可选范围很广但工业缺陷检测场景对模型的偏好其实很集中推理速度要够快部署要方便训练生态要成熟出了问题要容易排查。这些条件下YOLO系列几乎是绕不开的选项。我当时在YOLOv5和YOLOv8之间做了个对比YOLOv5的社区资料确实丰富网上随便一搜就是几十篇教程遇到问题好搜答案YOLOv8的优势在于ultralytics官方维护得特别好API统一、配置文件清晰、训练指标可视化做得漂亮还顺手给你把导出ONNX、TensorRT的路径都铺好了。从实测经验来看做工业缺陷检测不要一上来就追求最顶配的模型。YOLOv8s和YOLOv8m的量级对于飞机表面缺陷这个任务就够用了。更重的YOLOv8l或x版本推理延迟会明显上升而精度提升在缺陷检测任务里往往没有想象中那么大因为缺陷检测的瓶颈通常不是模型容量而是小目标的特征表达和数据的丰富程度。另外有一点值得单独说如果你所在的公司有部署环境约束比如只能用公司自研的推理框架那模型选型就要先确认框架支持哪种格式。通常YOLOv5的export.py能导出TorchScript、ONNX、TensorRT多种格式兼容性很好YOLOv8的话ultralytics库已经把所有导出入口统一成了model.export()用起来更省事。我们最后选定的是YOLOv8s作为baseline再根据效果决定是否升级到m尺寸这是比较稳妥的迭代路径。2.2 数据集目录结构与标注格式自查拿到数据集后第一步不是急着训练而是把数据从头到尾“盘”一遍。我当时是写了个小脚本统计图片尺寸分布、标注框数量和类别分布这一步花不了多少时间但能帮你提前发现很多坑。比如如果图片尺寸非常不统一从600x800到4000x3000都有就需要考虑训练时统一resize到同一尺寸是否合理比如如果某个类别的标注框数量比平均值低一个数量级那基本可以判定样本不均衡问题会很严重。第10618期数据集的目录结构相对规范images和labels分开放train/val/test三个子目录各有一份数据。YOLO格式的标注文件是每行一个目标内容为类别id x_center y_center width height坐标值都是归一化到0~1之间的浮点数。解压后我先随机抽了几张图把标注框画回去人工核对了一遍确保标注框贴合缺陷边缘没有大面积空白或框错位置的情况。这里要特别提醒网上能下载到的数据集标注质量参差不齐一定要人工抽检不要盲目信任文件和标注的一致性。如果数据集的图片和标注已经按YOLO格式对齐训练前的目录结构长这样aircraft_defects/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── aircraft_defects.yamlaircraft_defects.yaml的内容就是告诉ultralytics框架你的数据路径和类别信息path: /root/datasets/aircraft_defects train: images/train val: images/val test: images/test nc: 5 names: [crack, dent, corrosion, paint_peeling, scratch]2.3 训练环境搭建与依赖安装训练环境这块其实没什么玄学核心依赖就是PyTorch和ultralytics再配一块显存稍微够用的显卡。我用的是单张RTX 3060 12G显存训练YOLOv8s、分辨率1280、batch设为8时刚好能压进显存。训练完一轮全量数据大概要40多分钟200个epoch挂一晚上基本能跑完。如果你只有更小显存的卡建议把分辨率降到1024、batch降到4或者直接用YOLOv8n这种更轻量的模型先跑通流程再说。安装命令很简单pip install ultralytics torch安装后可以用一行命令验证环境是否正常yolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg能正常给出预测结果说明环境没问题。接着把数据集yaml文件里的路径改成你本机实际路径训练就呼之欲出了。环境这块有几个容易踩的坑。一个是PyTorch版本与CUDA版本不匹配torch.cuda.is_available()返回False那就只能龟速跑CPU训练另一个是数据集路径用了相对路径切换工作目录后模型找不到图片报错信息还不太直观会提示找不到某个索引的图片数据。我建议所有路径都写绝对路径能省掉很多莫名其妙的排查时间。3. 训练实操全流程记录3.1 数据划分与防泄漏处理训练前的数据划分直接决定了评估结果能不能反映真实效果。数据集自带的train/val/test划分一般可以直接用但我还是检查了一下同一个飞机不同角度的图片是否被同时分到了训练集和验证集。这种情况属于“数据泄漏”会导致验证集指标虚高。因为模型在训练时见过同一个飞机其他角度的表面纹理实际部署时面对的全是新飞机、新场景效果会打折扣。如果发现泄漏需要按“对象”而不是按“图片”来划分数据但公开数据集一般不会给你这么细的元信息所以能做的就是抽查一部分样本确认重复程度。从最终效果看第10618期数据集的划分质量还是可以的我在测试集上跑出来的结果与验证集指标差距不大没有明显的过拟合式虚高。数据划分的另一个要点是类别分布是否跨集合一致防止出现验证集中某一类缺陷占比过高或过低的情况。训练集里crack类占全部标注的35%验证集里如果crack变成了60%那指标就失真了。3.2 训练命令与关键参数解读直接给训练命令这是我调好的完整版本yolo train \ data/root/datasets/aircraft_defects/aircraft_defects.yaml \ modelyolov8s.pt \ epochs200 \ imgsz1280 \ batch8 \ lr00.01 \ lrf0.01 \ optimizerSGD \ patience30 \ cacheTrue \ workers8 \ projectaircraft_defect_0421几个关键参数的选择逻辑我展开说一下。imgsz1280是整个配置里最重要的一项。飞机表面裂纹在原始照片里特别细小如果用默认的640分辨率训练下采样几次之后裂纹特征几乎就消失在一堆高频噪声里了。推到1280之后小目标的像素占比提高一倍多crack类别的召回率提升非常明显。代价是训练变慢、显存占用变大但对缺陷检测来说多花点训练时间换取漏检率的下降完全值得。epochs200不是随便拍的。工业检测数据集的样本量通常不大几千张图片的情况下模型一般会在80到150个epoch之间收敛后续epoch主要用来继续微调细节但如果配上早停机制200的上限足够让模型充分收敛又不会浪费太多无效训练时间。patience30的意思是连续30个epoch验证集指标没有提升就提前停止训练实测下来大多在160轮左右自动停了。batch8是显存约束下的折中。Batch size影响梯度估计的稳定性工业任务里常用的是16到32但1280分辨率下光图片张量就非常大12G显存塞8张已经是极限了。如果显存富余优先加到16收敛会更稳。3.3 训练过程观察与指标解读训练跑起来之后不要只看loss曲线要定期看验证集上的指标变化。我习惯关注三个东西mAP0.5的变化趋势、每个类别的单独AP、还有训练集和验证集loss之间的距离。如果训练收敛后train loss继续下降但val loss开始回升说明过拟合苗头出现需要回退到指标最好的那个checkpoint或者提前停掉训练。最终这套配置在验证集上的表现大概是整体mAP0.5约0.86mAP0.5:0.95约0.52。分开看各类别的话corrosion和paint_peeling这类面积型目标的AP基本都在0.9上下dent表现中规中矩最难的是crackAP0.5只有0.72AP0.5:0.95更是掉到0.3左右。这说明小目标检测拉低了整体高IoU指标属于这类任务的普遍现象。如果你发现自己的结果也是“大目标AP高、小目标AP低”恭喜你说明模型和数据都没啥大毛病问题聚焦在目标尺度的挑战上。3.4 推理部署与导出落盘训练完成后把模型导出成ONNX格式yolo export modelbest.pt formatonnx imgsz1280导出的时候注意输入尺寸要和训练时保持一致不然推理精度会受影响。ONNX本身只是中间格式如果部署环境支持TensorRT可以进一步转成engine格式推理速度能再快一倍以上。我在本机测试时裸PyTorch推理平均大约45毫秒一帧ONNX走CUDA执行器大约30毫秒TensorRT优化后能压到20毫秒以内完全满足实时检测场景的需求。部署后的简单推理代码大概是这样的from ultralytics import YOLO model YOLO(best.onnx) results model.predict( sourceinference/port_wing_01.jpg, conf0.25, imgsz1280, device0, ) boxes results[0].boxes for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(f缺陷类别: {model.names[cls_id]}, 置信度: {conf:.3f}, 位置: {xyxy})conf0.25是我实际项目里调出来的默认置信度阈值。工业质检场景里漏检比误检的危害更大所以这个阈值调得偏低。如果误检报警太频繁可以适当往上抬到0.3或0.35但不能太高否则把真正的小裂纹滤掉就得不偿失了。4. 常见问题与排查技巧实录4.1 细小裂纹漏检严重怎么办训练完第一版模型后最大的问题就是crack类漏检。我把测试集上几张漏检图片逐个看了一遍总结出两类典型场景一类是裂纹太短太细在1280分辨率下依然只有三四百个像素的细线模型把它当成蒙皮表面的纹理噪声过滤掉了另一类是裂纹处于铆钉、缝隙附近模型把注意力分给了周围的高对比度结构漏掉了真正需要关注的损伤。针对这个问题我试了几种方案按效果排序如下使用切片推理SAHI把大图切成若干小图分别推理再合并结果相当于变相提高了小目标的分辨率。实测crack的召回率能提升5到8个点代价是推理时间线性上涨。提高训练分辨率到1536有效果但显存压力太大而且对长宽比极大的细线目标帮助有限。给细长目标做定向数据增强包括旋转、小角度仿射变换等增加裂纹方向的多样性。这个方案性价比很高尤其当你发现模型对水平方向裂纹敏感、对垂直方向裂纹迟钝时大概率是训练数据中方向分布不均。我个人最推荐先做切片推理因为不需要重新训练直接就能评估效果。如果你想先改模型侧还可以试一下在loss里加大crack类别的权重让模型更关注样本量少且难学的类别。4.2 样本不均衡怎么处理样本不均衡几乎是缺陷检测的默认设定。第10618期数据集中scratch类标注数量最多crack类只有scratch的一半左右而corrosion更少。模型天然偏向多数的类别表现为crack和corrosion的召回率明显偏低。处理不均衡的常见路径有三条。第一重采样训练时对样本量少的类别提高采样概率比如crack样本每轮多取几次。第二损失加权在计算box loss和cls loss时给稀有类别更大的权重强迫模型更认真地学这些类。第三使用合成数据这一步工作量稍大但对corrosion这种边界模糊、形态多样的缺陷帮助很大可以手工合成一些形态近似的腐蚀区域补充样本数量。我们的实际做法是先用类别权重做了第一轮训练效果有改善但不明显后来配合数据增强在原来基础上随机加上了灰度扰动和局部高斯噪声模拟轻微的金属氧化反光变化corrosion的AP提升了4个点左右。这种增强手段不改变标注框位置只改变图像纹理安全性高推荐尝试。4.3 标注质量如何把关数据集的标注质量是另一个容易被忽视的坑。我抽检时发现部分腐蚀区域的标注边界画得比较随意有的框把周边正常蒙皮也圈进去了有的框又只框住了腐蚀区域的中心部分。缺陷类任务的标注主观性很强尤其腐蚀和漆面剥落这种边界模糊的目标十个标注员可能画出来十种不同的框。如果标注边界不统一模型训练时学到的目标边界自然也是“飘”的推理时输出的预测框置信度就会偏低严重时会导致误检。碰上这种情况我的建议是不要指望整个数据集重标成本太高。更务实的做法是训练第一版模型后专门拿测试集的预测结果做人工复核把置信度高但判定错误的框挑出来看这些样本的原始标注是否就有问题。如果发现是标注问题改掉这些样本后重新训练效果通常会有明显提升。这个过程其实就是“模型辅助人工校验标注”在工业项目里非常实用。4.4 部署现场与实验室的差距实验室训练时用的是相对干净的数据集图片部署现场却会遇到各种“脏情况”。比如机坪上不同时间段的光照角度完全不同上午的裂纹在阴影里下午的裂纹在强光直射下表现差异很大。再比如数据采集设备的分辨率、拍摄距离是否稳定都会直接影响模型的输入尺寸和预测效果。我在部署时踩过最深刻的一个坑是相机自动白平衡。实验室图片基本都是固定光源下拍摄的颜色分布稳定现场相机如果开着自动白平衡会把色调调过来调过去模型误检率直线上升。解决方式是稳定白平衡参数、锁死曝光时间让输入图像的统计特征尽量和训练集保持一致。这一点务必写进部署方案里比调任何模型参数都重要。5. 几点值得反复回看的实验心得5.1 先花时间摸清数据再考虑调模型这个项目做到后来我最大的体会是缺陷检测项目的绝大多数问题根源都在数据和标注而不是模型结构不够先进。当你发现某个类别漏检多先别急着换更大的模型、加注意力机制第一步应该是打开这个类别的训练样本仔细看一遍图片本身的特征是否足够区分、标注是否准确、样本量是否严重不足。很多时候问题的答案就藏在“多看几张图”里。5.2 建立一套可复用的评估模板工业质检场景的模型迭代是持续性的这次飞机表面缺陷做完下次可能就换成了风电叶片表面检测。我强烈建议在项目初期就把数据检查脚本、训练命令、指标统计脚本固化成一个模板目录每次新项目只需要替换数据集yaml和类别名就能快速跑出第一版baseline。有了稳定的基线后续优化才有比较的锚点否则每次从零开始写脚本效率太低了。5.3 小技巧预测框后处理里的置信度阈值最后分享一个实战小技巧。很多人在部署YOLO时直接使用默认的conf0.25和iou0.7但这两个参数的设置应该结合你的检测任务来定。如果现场误检报警很频繁先不要动模型试着把conf从0.25逐步上调到0.35大概率误检率能下降一半以上而漏检率不会增加太多。如果漏检是主要矛盾可以把conf往下调低到0.2同时配合切片推理找回漏检目标。阈值的调整因为不涉及重新训练是性价比最高的“现场调优”手段建议多试试。