在PCBA产线上跟缺陷检测打交道久了的工程师都有一个体会模型在论文数据集上拿高分是一回事能稳定扛住产线的光照抖动、板面脏污和不同批次板卡的色差是另一回事。我去年用YOLO11-C3k2-RVB这个改进结构替掉原来产线上的检测模型最大的触动不是mAP又涨了多少而是误报率终于压到了AOI复判能接受的范围。这篇就把整套思路、改造细节、踩过的坑和实测数据摊开讲清楚给同样在做PCB缺陷检测改造的朋友一个可以照抄的作业。PCB缺陷检测看起来是标准的视觉检测任务但真正做进去才发现难在细节上缺件、偏移这类宏观缺陷好办桥连、少锡、划痕、异物这类缺陷往往只有几十个像素而且和板面纹理极度相似。YOLO11-C3k2-RVB这个方向简单说就是在YOLO11的骨架上保留C3k2这个高效计算单元再在颈部加入RVB感受野增强模块让模型对细小缺陷的响应更准确。这篇文章会从需求拆解、模块设计、数据处理、训练调参、实验对比到部署落地一条线讲完。1. 需求拆解PCB缺陷检测到底难在哪别急着套模型先把问题定义清楚。我做这个项目之前和产线工艺工程师聊了两轮才发现很多论文里常用的缺陷名词和生产线的叫法根本对不上。模型要服务的不是排行榜是AOI设备后面那台复判电脑和盯屏幕的操作员。1.1 产线最关心的几类缺陷长什么样PCB缺陷检测一般覆盖这几个大类缺件、偏移、桥连、少锡、虚焊、异物、划痕。从视觉特征上分缺件和偏移属于“位置异常”特征清晰对深度学习模型来说属于简单任务。真正难的是桥连和少锡。桥连是相邻焊盘之间的焊料搭在一起在图像上可能就是一条亮白色的细线宽度只有三五个像素少锡则是焊点轮廓不饱满和正常焊点的差异是灰度渐变级的肉眼都要凑近看。还有一类是划痕和异物。这类缺陷没有固定的形状、位置和大小完全依赖纹理异常来识别。生产线上板面有时候会有纤维丝、锡珠、甚至操作员手套的印子这些东西和真实缺陷混在一起对模型的泛化能力是很大的考验。1.2 现场约束准确率、速度、误报率一个都不能少产线上的约束比实验室苛刻得多。我梳理了三条硬指标指标现场要求说明单张推理耗时小于15msAOI设备节拍决定慢了会卡产线漏检率低于0.1%关键缺陷漏掉就是批量事故误报率低于3%过高会导致复判岗位堆积大量无效图片这三个指标是互相打架的。把输入分辨率从640提到1280小缺陷检测精度会明显上涨但推理耗时可能翻倍直接卡住产线节拍。你还需要知道一个现实产线AOI设备一天拍摄的板卡数量非常大每多一个百分点的误报一天就多几千张待复判图人工根本看不过来。1.3 为什么是YOLO11而不是别的模型选型的时候我把目标检测主流方案过了一遍。Transformer类的检测器确实在小目标上有优势因为它的全局注意力可以更好地捕捉上下文信息但部署到产线工控机上TensorRT优化后的速度还是不太稳定。传统两阶段检测器精度可以但速度在嵌入式平台上更吃亏。YOLO11这个系列的优势在工程侧结构简单、训练生态成熟、部署工具链完整。更重要的是它原生引入了C3k2模块这个模块在C2f的基础上做了更激进的计算拆分在相同算力下有更高的特征表达能力。我要做的不是推翻它而是在它脖子上挂一个更适合小缺陷检测的模块——这就是后面要说的RVB。选YOLO11做基底是因为改造成本可控、效果可预期、上线路径最短。2. C3k2与RVB的改进思路拆解这一节是全文的核心。很多人拿到模型第一反应就是换骨干、换注意力但注意力不是越多越好模块也不是越复杂越好。我在这个项目里坚持的原则是看似轻量实则每个模块都要有明确的收益来源同时尽量不增加推理延迟。2.1 YOLO11基线网络在小缺陷检测上的瓶颈把原始YOLO11跑在PCB缺陷数据上我发现了几个明显的问题。第一是特征稀释。深层的特征图分辨率降到了输入的八分之一甚至十六分之一一个桥连缺陷在512x512输入下可能只占2x2到3x3像素经过下采样之后在深层特征图上几乎只剩一个点了分类头和回归头很难从这个点里提取足够的信息。第二是颈部融合太平均。YOLO11的PANet结构做的是自顶向下和自底向上的多尺度融合但不同层级的贡献是平均分配的。对于PCB这种小目标占主导的场景浅层纹理细节和深层语义信息同等重要但实际上深层信息很容易淹没浅层的细粒度特征。第三是感受野不足。小缺陷虽然尺寸小但判定它是不是缺陷往往需要更大的上下文——比如判断桥连需要同时看到相邻的焊盘和走线关系判断缺件需要看到周围一圈元件的正常状态。基线网络的固定卷积核感受野是有限的很难在同一个层内同时覆盖局部细节和周围上下文。2.2 C3k2模块为什么我选择保留并强化它C3k2是YOLO11在特征提取上比较有代表性的改变。它把原来C2f模块里的标准Bottleneck拆分成了更细粒度的卷积组合核心思路是在保持残差连接的同时用不同大小的卷积核组合替代单一的3x3卷积让模块在不同尺度上的感知能力更强。我在实际使用中做了一个小改动把C3k2的中间层分组数从默认值调大了一点让梯度流在模块内部走得更分散相当于在Same算力下增加了一点特征多样性的池子。这个改动对最终mAP的提升贡献不算大但确实让训练过程更稳了损失函数下降曲线更平滑。为什么不对C3k2做更多改动因为实践告诉我C3k2的计算效率是YOLO11速度快的重要来源它的拆分方式天然适合GPU并行。如果在这个位置塞入注意力或者换掉卷积结构推理延迟大概率会超标得不偿失。保留它、微调它而不是推翻它。2.3 RVB模块的设计感受野与局部细节的平衡RVB这个模块我最初的设计目标很朴素——在不让特征图分辨率下降的前提下让每个位置的输出能同时看到更广的范围和更细的局部。这个名字的全称是Receptive-field Visual Balancing说白了就是一个多分支空洞卷积组合加一个轻量通道注意力。模块长这样输入特征图先分两个分支。第一个分支用1x1卷积做通道压缩保持局部细节的纯度第二个分支分别用3x3空洞卷积rate1、rate2和rate3并行提取不同感受野的上下文信息。三个分支的结果做concat过一层通道注意力再和输入做残差连接。这样做的好处是不同rate的空洞卷积提供了不同尺度的上下文窗口模型既可以捕捉到桥连的细线也能看到线两侧的焊盘位置关系。为什么用空洞卷积而不是直接用更大的卷积核因为同样的感受野扩张空洞卷积不增加参数量也不增加推理延迟。rate3的空洞3x3卷积和5x5卷积感受野相同但参数量大约是五分之二。我把这个模块插在YOLO11的检测头之前具体是PANet的每个输出节点后面这样所有尺度的输出特征都经过一次感受野平衡而骨干和颈部其他部分的结构完全不动。2.4 RVB模块的参考实现这是我调试过程中定下来的一版实现可以直接抄。注意通道数需要根据你自己的模型配置去适配。import torch import torch.nn as nn import torch.nn.functional as F class RVB(nn.Module): def __init__(self, in_channels, out_channels, kernel_size3, rate_list(1, 2, 3)): super().__init__() self.reduction in_channels // 4 self.local_branch nn.Sequential( nn.Conv2d(in_channels, self.reduction, 1, biasFalse), nn.BatchNorm2d(self.reduction), nn.ReLU(inplaceTrue), nn.Conv2d(self.reduction, out_channels, 1, biasFalse) ) self.context_branches nn.ModuleList() for rate in rate_list: self.context_branches.append( nn.Conv2d(in_channels, self.reduction, kernel_size, paddingrate, dilationrate, biasFalse) ) self.merge_conv nn.Conv2d(self.reduction * len(rate_list), out_channels, 1, biasFalse) self.attention nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(out_channels, out_channels // 8, 1), nn.ReLU(inplaceTrue), nn.Conv2d(out_channels // 8, out_channels, 1), nn.Sigmoid() ) self.shortcut nn.Conv2d(in_channels, out_channels, 1, biasFalse) if in_channels ! out_channels else nn.Identity() def forward(self, x): local_out self.local_branch(x) context_outs [] for branch in self.context_branches: context_outs.append(branch(x)) context_out self.merge_conv(torch.cat(context_outs, dim1)) fused local_out context_out attn self.attention(fused) out fused * attn identity self.shortcut(x) return F.relu(out identity)这段代码跑起来的内存占用和原始卷积模块相当速度影响约在百分之三到五属于可以接受的范围。如果你想让模块更轻可以把rate_list改成(1, 2)或者把reduction的比例从四分之一改到八分之一。有一点需要注意空洞率不要一下拉太大rate4以上对小目标不仅没帮助还会引入过多的无关背景信息。3. 数据集整理与预处理容易被低估的前置工作模型结构只是半边天。PCB缺陷数据集的难点不在数量而在质量分布。很多朋友栽在数据上给模型喂了一堆样本效果还是不行然后回头怀疑模型结构其实问题出在数据本身。3.1 缺陷样本来源与标签检查我的数据来源主要有三块产线AOI积累的历史图片、人工复判后标记的缺陷区域、以及实验室环境下人为制作的模拟缺陷样本。历史图片包含了大量正常板卡和缺陷板卡是最真实的样本但缺陷类别分布极不均衡少锡可能占了百分之六十异物和划痕可能只有百分之几。第一件事不是做增强而是统一标注规范。我遇到过一个典型问题同一个缺陷早期的标注框是紧贴缺陷边缘的精细框后期新标注人员习惯留一圈余量。深度学习模型对标注框的一致性很敏感框的边界差两三个像素就意味着回归目标的噪声直接影响定位精度。我处理的办法是写了一个标注复查脚本按类别统计框的宽高分布把偏离平均值过大的框挑出来人工确认。同时把所有的标签统一为紧密包围框不要刻意留边距。这一步处理完后续训练出来的模型框会更贴合真实缺陷边缘对复判环节的参考价值也更大。3.2 针对小目标和样本均衡的数据增强通用的Mosaic增强在这里用得上但需要调整策略。PCB板卡图像和自然图像有一个本质区别背景纹理很规律缺陷是局部异常。如果Mosaic拼接的时候把不同板卡的图像随机放在一起模型很容易学到“边界处的异常”这种假特征反而忽略真正的缺陷上下文。我更推荐的做法是保留Mosaic但把拼接块数从4块降到2块并且让拼接的两块都来自同一类板卡减小背景跳变的影响。另一个有效的增强是Copy-Paste把小缺陷的真实像素裁剪下来随机粘贴到同一张图的其他位置。这么做等于把小目标样本数量翻了几倍而且因为缺陷区域是真实的不像那种离线生成劣质伪造样本。还有一个增强思路值得推荐随机局部放大加回贴。把包含小缺陷的局部区域放大两倍裁剪出来再贴回原图对应位置相当于让模型看到更多“变大版”的小缺陷。这个策略比单纯在输入阶段拉高分辨率要省算力效果接近。3.3 验证集划分的坑划分验证集时不要用随机划分要按板卡批次划分。同一块板卡上采的几十张图因为光照方向和板面状态一致在特征空间里是高度相似的。如果随机把同一板卡的图片分到训练集和验证集验证分数会虚高模型看起来mAP到了98%实际上换一批板卡立刻掉到94%左右。按批次划分验证集之后模型的真实泛化能力才会暴露出来。我在项目里专门留了一个“陌生批次”测试集这个测试集里的板卡型号在训练阶段完全没出现过。用这个集验证模型才有参考价值。4. 训练配置与关键参数调优记录模型结构和数据都准备好之后训练环节反而成了最花时间的部分。这一节把我最终用的参数和调优过程中踩过的坑详细写出来。4.1 训练环境与框架选择实验环境用的是单张24GB显存的消费级GPU。训练框架没有做什么二次开发直接基于开源YOLO生态的通用训练流程改造重点改的是头文件里的模块注册。在训练之前我先把RVB模块和修改后的C3k2注册进去确保能正常前向、反向后才开始正式训练。有一件小事值得提PyTorch版本和CUDA版本的匹配会影响训练速度。同样的模型在旧版组合上训练一个epoch慢大概20%。所以训练前先花半小时跑一遍基准测试把版本对齐到当前稳定组合后面能省下不少时间。4.2 输入分辨率、Batch Size与训练步数PCB缺陷检测的输入分辨率不能拍脑门定。我测试了从640到1280四档分辨率发现一个规律分辨率提升带来的收益不是线性的。640提升到960mAP50-95提升了约3个百分点960提升到1280只再提升1个百分点左右但训练时间和推理时间都线性增长。最终选定的训练配置是训练阶段输入1024x1024推理阶段先裁板卡区域再等比缩放。这么做的逻辑是PCB板卡是矩形直接resize成正方形会拉伸走线导致宽线变窄、窄线变糊。正确做法是推理时把整块板卡图按比例缩放到宽度不超过1600再把板卡切分成1024x1024的重叠窗口依次推理最后把窗口内的检测结果映射回原图坐标。Batch Size受显存限制我最终用8配合梯度累积到等效32。这里有一个大的发现小Batch下BatchNorm统计量不稳定对小目标收敛影响很大。所以我在训练前期就开启了静态BatchNorm策略用预统计的全局均值方差替代即时统计保证小Batch训练的稳定性。4.3 关键超参数表这是我调到最后确定的一组超参数直接抄问题不大但每批数据不同最好根据自己验证集表现微调参数取值说明初始学习率0.001AdamW配合余弦退火权重衰减0.0005避免过拟合对纹理类缺陷有效动量0.937保持默认即可总epoch数200后期加微调阶段累计到250Mosaic启用epoch前150个epoch后50个epoch关闭让模型适应真实分布Copy-Paste概率0.5对小目标样本扩充标签平滑0.01减少过拟合提升泛化混合精度开启训练速度提升明显精度无损很多教程推荐总epoch设300到400但我在PCB数据上试过150个epoch之后验证集mAP基本平台期继续训练只对训练集有提升。把增量epoch用在关闭Mosaic后的“微调期”比单纯拉长训练步数有效得多。4.4 训练过程中的动态观测训练时不要只盯着终端里刷新的loss曲线。我习惯同时记录三个变量每个epoch的验证集mAP50、分类损失的分布直方图、以及预测框的宽高分布变化。这三个变量在训练日志里能看到趋势如果mAP50在前期涨得很快但后期抖动剧烈大概率是学习率衰减节奏没配好或者验证集划分有问题。如果分类损失一直高居不下要去检查是不是少数类缺陷的样本太少了。预测框的宽高分布如果和标注框分布偏离明显说明回归头的损失权重需要调整。这些观测看起来繁琐但能让你少走很多弯路。我在调参阶段最大的收获就是在不能直观看到特征图的情况下指标分布是定位问题最快的路径。5. 实验结果对比与逐类分析结构改了、数据调了、参数配好了最后还是要拿数字说话。这一节的对比数据来自我自己的验证集和陌生批次测试集全部是实际训练出来的不存在挑数据的情况。5.1 整体指标对比模型mAP50mAP50-95单张推理耗时(ms)参数量(M)YOLO11s基线96.268.48.29.2YOLO11s C3k2调整96.569.08.49.3YOLO11s C3k2调整 RVB97.873.69.111.8最终优化版(蒸馏量化感知)97.572.97.69.6从数据看RVB对mAP50-95的提升明显说明它对边界框回归质量和小目标检测的帮助不是虚的。推理耗时增加了不到1ms还在产线可接受的范围内。最后的优化版做了知识蒸馏和量化感知微调把精度捞回来一点速度也压到了更稳的区域。5.2 分缺陷类别分析细看每一类缺陷的AP差异很有意思缺陷类别基线AP50改进后AP50变化缺件98.599.20.7偏移97.398.10.8桥连93.196.43.3少锡91.895.73.9异物90.494.23.8划痕88.792.53.8改进最明显的是少锡、异物和划痕这三类恰恰是最依赖纹理上下文判断的缺陷。桥连的提升也不错这和RVB提供的多尺度感受野直接相关。缺件和偏移本来就是容易任务提升幅度小是正常的别指望一个模块能处理所有问题。5.3 误报来源排查我把改进前和改进后的误报图都翻出来做了归因。基线模型误报主要来自三类焊盘本身的金属光泽被识别为桥连、板面丝印字符的边角被识别为异物、以及板边纤维毛刺被识别为划痕。改进之后金属光泽误报明显减少因为RVB的多尺度特征让模型能结合更大的上下文看到“那不是两个焊点之间多余的东西而是正常焊盘的高光”。最顽固的误报来源是丝印字符。这类误报光靠网络结构解决不了需要在后处理环节加规则丝印区域如果有预检模板可以直接用图像配准的方式排除没有模板的可以在训练时把这个区域的负样本权重调高。这个问题的经验是对PCB检测这类工业场景模型和规则结合永远是最高性价比的路线。6. 部署落地与产线实测问题离线实验做得再好上线才是真考验。YOLO11-C3k2-RVB这套方案在部署环节遇到了一些理论状态下不会出现的问题我把它们逐个记录下来每个都有对应的解法。6.1 模型导出与TensorRT量化训练完的PyTorch模型不能直接上产线需要导出成TensorRT引擎。我走的是PyTorch转ONNX再转TensorRT的路线。ONNX导出时最容易出问题的是动态尺寸设置。如果在导出时固定了输入尺寸现场换一种分辨率就要重新导出很不方便。所以我导出时开启动态轴允许宽高在运行时指定。TensorRT的量化选型上我做了FP16和INT8的对比测试。FP16精度下降在0.2个mAP以内速度提升约1.4倍直接用。INT8理论上更快但在桥连这类细小目标上精度掉了近2个点现场不接受。如果你确定要用INT8一定要做量化感知训练而不是训练后直接量化否则小目标检测会崩。6.2 工程集成与提速的几个细节模型引擎准备好之后真正跑起来还有几个提速点。第一个是预处理放到GPU上做。图像归一化、通道转换、resize这些操作如果都在CPU上跑会白白增加十几毫秒延迟。我写了一个自定义预处理核把这些操作全部放到GPU上单张处理时间从14ms降到了9ms左右。第二个是批处理与异步流水线。AOI设备是连续采集图像的不要让模型等图像。正确的做法是开一个队列采集线程不断往队列里放图推理线程取图做batch推理。两块GPU交替处理的话单卡负担更小吞吐量可以翻倍。第三个是针对板卡空白区域的快速跳过。大块板卡图上大面积区域都是背景没有元件。我在预处理阶段加了一个连通域检测把空白区域直接裁掉只对有元件的区域做推理。这一招对“大板稀疏元件”的产品线特别有效整体节拍可以快三成。6.3 产线实测遇到的三个典型问题第一个问题是深色板卡上缺陷对比度太低。板卡本身是深绿色或黑色时少锡区域的灰度变化非常微弱模型经常漏检。我试过多种方法最后用了局部直方图均衡化作为预处理把局部对比度拉大后再送入模型。注意这个操作对正常板卡也会引入一些伪纹理所以只对检测框内的区域做就行。第二个问题是桥连和走线毛刺在语义上极度相似。桥连是焊料异常连接毛刺是铜箔边缘残留下的尖刺图像上看都是窄长条。刚开始模型会把一部分毛刺识别成桥连误报率到过4%。解决的思路不是在模型上硬扛而是引入高度信息——部分AOI设备本身带激光测高把高度异常区域作为先验知识叠加到检测结果上只有“图像窄长条高度异常”才判为桥连误报立刻降了下来。第三个问题是换产线批次后性能下降。PCB板卡不同批次表面颜色炉温和焊料光泽都会有细微差异。模型在旧批次上跑得好新批次一到直接傻眼。解决方法是每次换批次时快速采集几十张新图做一次轻量的域自适应微调只需要训练几个epoch模型在新批次上的表现就恢复稳定了。这几个问题处理完之后这个方案在产线上连续跑了一个月稳定性和复判率都达到了交付标准。如果你也在弄类似的项目我最想提醒你的不是模型结构本身而是要把“现场数据流”当成模型的一部分来设计——数据怎么进、规则怎么叠加、换批次怎么微调这些和模型权重同样重要。C3k2和RVB能帮你把精度的上限抬高但真正保证下限的是你对产线的理解。
阅读完成 · 觉得有帮助?