芯片封装表面溢胶缺陷检测技术【附代码】
✨ 长期致力于小目标检测、YOLOv5s-SOP、Unet-glue、OpenCV、Matlab-GUI研究工作擅长数据搜集与处理、建模仿真、程序编写、仿真设计。✅ 专业定制毕设、代码✅如需沟通交流点击《获取方式》1基于Zemax仿真的图像采集装置与Unet-glue分割网络设计针对传感器芯片尺寸小于1cm导致溢胶缺陷采集困难的问题首先在Zemax中光学仿真确定最佳成像距离和镜头参数。采用远心镜头放大倍率0.5x工作距离110mm景深±2mm搭配500万像素工业相机光源为环形白光LED。自主设计图像采集装置平台包括三轴微调支架和防抖动底座采集到的芯片原始图像分辨率为2592x1944ROI裁切后为1024x1024。然后提出Unet-glue分割网络在标准Unet的跳跃连接中加入注意力门控模块增强对溢胶边缘特征的响应并在解码器末端增加胶水分割头和一个辅助的分类头判断溢胶类型过多、过少、合格。损失函数为Dice损失与交叉熵损失的加权和权重系数0.7和0.3。训练数据集为采集的3200张芯片图像手工标注溢胶区域按7:2:1划分。Unet-glue模型在验证集上Dice系数达到0.916溢胶分类准确率94.2%比标准Unet分别提升4.5%和6.1%。在测试集上溢胶过多检测召回率96%溢胶过少检测召回率91%。该模块部署在工控机上单张图像推理时间35ms。2YOLOv5s-SOP表面缺陷检测模块与图像矫正算法针对芯片表面其他缺陷划痕、污点、崩边同时检测的需求改进YOLOv5s网络。在主干网络中添加SE注意力模块压缩比为16提升对通道特征的敏感性。特征金字塔部分增加一个针对小目标的高分辨率检测头160x160使得整体检测头变为四个尺度。锚框聚类使用K-means重新计算得到六组锚框。训练集除了溢胶图像外额外标注2000张包含划痕和崩边的芯片图像。YOLOv5s-SOP在芯片表面缺陷检测任务上的平均精度均值达到0.928相比原YOLOv5s提升5.1个百分点。图像矫正模块针对采集图像中芯片倾斜的情况先使用Hough变换检测芯片边界直线计算旋转角度再通过仿射变换进行校正确保后续溢胶检测在标准姿态下进行。矫正模块的处理时间为12ms矫正后芯片角度误差小于0.3度。两个检测模块可以串联运行首先YOLOv5s-SOP检测整片区域有无明显缺陷若有则报警若无则进入Unet-glue精细分割溢胶区域。该串联检测使平均处理时间控制在60ms以内满足生产线100ms的节拍要求。3OpenCV溢胶判别与Matlab-GUI交互平台集成针对Unet-glue输出的分割掩膜使用OpenCV进行后处理判别溢胶缺陷。首先计算掩膜中溢胶区域的像素面积转换为实际面积标定系数0.0015mm²/像素。然后提取溢胶轮廓的最小外接矩形计算溢胶区域距离芯片边缘的最大扩散距离。设定阈值溢胶面积小于0.05mm²为合格0.05-0.25mm²为轻微溢胶可接受大于0.25mm²为过多溢胶不合格若溢胶区域未覆盖整个焊盘边缘则判为溢胶不足。同时计算溢胶的圆度过大的圆度表明胶水扩散不良。开发Matlab-GUI用户交互界面包含实时视频显示、检测结果图像叠加、历史数据统计和报告导出功能。用户可通过界面选择检测模式单帧/连续查看溢胶区域的伪彩图并将检测结果保存为Excel。界面底层通过TCP/IP与检测算法工控机通信接收检测结果。在航伟光电科技的封装产线上试运行两周共计检测芯片8000颗系统检测准确率98.1%漏检率0.6%过检率1.3%达到合同要求的检测精度。相比人工目检检测速度提升20倍且避免了人为疲劳误判。import cv2 import numpy as np import torch import torch.nn as nn import torch.nn.functional as F class AttentionGate(nn.Module): def __init__(self, F_g, F_l, F_int): super().__init__() self.W_g nn.Sequential(nn.Conv2d(F_g, F_int, 1), nn.BatchNorm2d(F_int)) self.W_x nn.Sequential(nn.Conv2d(F_l, F_int, 1), nn.BatchNorm2d(F_int)) self.psi nn.Sequential(nn.Conv2d(F_int, 1, 1), nn.BatchNorm2d(1), nn.Sigmoid()) def forward(self, g, x): g1 self.W_g(g) x1 self.W_x(x) psi self.psi(F.relu(g1 x1)) return x * psi class UnetGlue(nn.Module): def __init__(self, n_channels3, n_classes1): super().__init__() # Encoder 简略 self.enc1 self.conv_block(n_channels, 64) self.pool1 nn.MaxPool2d(2) self.enc2 self.conv_block(64, 128) self.pool2 nn.MaxPool2d(2) self.enc3 self.conv_block(128, 256) self.pool3 nn.MaxPool2d(2) self.enc4 self.conv_block(256, 512) self.bottleneck self.conv_block(512, 1024) self.up4 nn.ConvTranspose2d(1024, 512, 2, stride2) self.att4 AttentionGate(512, 512, 256) self.dec4 self.conv_block(1024, 512) self.up3 nn.ConvTranspose2d(512, 256, 2, stride2) self.att3 AttentionGate(256, 256, 128) self.dec3 self.conv_block(512, 256) self.up2 nn.ConvTranspose2d(256, 128, 2, stride2) self.att2 AttentionGate(128, 128, 64) self.dec2 self.conv_block(256, 128) self.up1 nn.ConvTranspose2d(128, 64, 2, stride2) self.att1 AttentionGate(64, 64, 32) self.dec1 self.conv_block(128, 64) self.out_conv nn.Conv2d(64, n_classes, 1) self.classifier nn.Linear(1024, 3) # 三分类 def conv_block(self, in_ch, out_ch): return nn.Sequential(nn.Conv2d(in_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue)) def forward(self, x): e1 self.enc1(x); p1 self.pool1(e1) e2 self.enc2(p1); p2 self.pool2(e2) e3 self.enc3(p2); p3 self.pool3(e3) e4 self.enc4(p3); p4 self.pool3(e4) b self.bottleneck(p4) d4 self.up4(b); a4 self.att4(d4, e4); d4 torch.cat([a4, d4], dim1); d4 self.dec4(d4) d3 self.up3(d4); a3 self.att3(d3, e3); d3 torch.cat([a3, d3], dim1); d3 self.dec3(d3) d2 self.up2(d3); a2 self.att2(d2, e2); d2 torch.cat([a2, d2], dim1); d2 self.dec2(d2) d1 self.up1(d2); a1 self.att1(d1, e1); d1 torch.cat([a1, d1], dim1); d1 self.dec1(d1) out_mask torch.sigmoid(self.out_conv(d1)) return out_mask def opencv_postprocess(mask, pixel_to_mm20.0015): mask (mask 0.5).astype(np.uint8) * 255 contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return no_glue area cv2.contourArea(max(contours, keycv2.contourArea)) * pixel_to_mm2 if area 0.05: return qualified elif area 0.25: return minor_excess else: return excess_glue