业务痛点:某电子厂(年产PCB板100万块,含焊点5000万个)存在三大问题:
- 人工检测低效:依赖技工目检,每块板检测耗时2分钟,日均产能仅240块(需求500块),漏检率15%(导致售后返工成本年超800万元)
- 缺陷类型复杂:需识别虚焊(30%)、连锡(25%)、漏焊(20%)、偏移(15%)、多余焊(10%)5类缺陷,人工易混淆相似缺陷(如虚焊与偏移)
- 质量追溯困难:人工记录缺陷位置不精确,无法定位产线设备问题(如贴片机偏移导致批量缺陷)
算法团队:图像预处理(去噪/裁剪)、数据增强(旋转/翻转)、CNN模型构建(ResNet迁移学习)、模型训练与评估、模型存储(MinIO)
业务团队:API网关、缺陷检测服务(调用CNN模型)、质检系统集成(缺陷标注/统计分析)、分拣控制系统(PLC联动)、监控告警
CNN算法原理与场景结合点
CNN(卷积神经网络)是专为图像设计的深度学习模型,通过局部感知(卷积核提取局部特征)、权值共享(减少参数)、层次化特征提取(浅层边缘→中层纹理→深层语义),实现高效图像理解
- 卷积层(Convolutional Layer):用滑动卷积核(如3×3)提取局部特征(如焊点边缘、锡膏形状)
- 池化层(Pooling Layer):降维(如最大池化保留显著特征),增强平移不变性
- 激活函数(ReLU):引入非线性,缓解梯度消失
- 全连接层(FC Layer):整合高层特征,输出分类/定位结果
开发工具和工具链
算法团队
- 语言:Python 3.9(模型构建/训练)、C++(TensorRT加速)
- 深度学习框架:PyTorch 2.0(模型定义/训练)、TorchVision 0.15(数据增强)、TensorRT 8.6(推理加速)
- 图像处理:OpenCV 4.8(去噪/裁剪)、Albumentations 1.3(高级数据增强)
- 数据处理:Pandas 2.0(标注文件解析)、NumPy 1.24(数值计算)
- 实验跟踪:MLflow 2.8(记录超参数/指标/模型)、Weights & Biases(可视化训练曲线)
- 版本控制:git@github.com:pcb-factory/algorithm-defect-detection.git
业务团队
- 语言:Go 1.20(高性能API)、Java 17(质检系统集成)
- 服务框架:FastAPI 0.104(检测API)、Spring Boot 3.1(质检系统后端)
- 前端:React 18(产线终端可视化)、ECharts 5.4(缺陷统计图表)
- 数据库:PostgreSQL 15(存储检测结果/缺陷记录)、Redis 7.0(缓存热点模型)
- 版本控制:git@github.com:pcb-factory/business-defect-detection.git
数据准备与特征变化
(1)原数据结构(图像+标注数据,来自产线)
原始数据包括PCB板高清图像(产线摄像头采集)、缺陷标注(人工标注工具生成)、设备参数(贴片机日志),存储于数据湖(MinIO),含噪声(灰尘/反光)、尺寸不一、标注缺失等问题。
① PCB图像数据(pcb_images,MinIO存储)
- 格式:JPG/PNG,分辨率4096×2160(原始)、1024×768(压缩备份)
- 命名规范:PCB_ID_SN.jpg(如PCB_20231001_S001.jpg,SN为序列号)
- 示例:PCB_20231001_S001.jpg(含虚焊、连锡缺陷)
② 缺陷标注文件(defect_labels.csv,人工标注)

(2)数据清洗(详细代码,算法团队负责)
目标:去除模糊图像、修正标注错误、统一图像格式
代码文件:data_processing/image_cleaning.py
python
import cv2
import os
import pandas as pd
import numpy as np
from PIL import Image
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def clean_image(image_dir:str,label_path:str,output_dir:str)->pd.DataFrame:
"""清洗PCB图像:去模糊、统一格式、修正标注"""
# 1.创建输出目录
os.makedirs(output_dir,exist_ok=True)
# 2.加载标注文件
labels_df = pd.read_csv(label_path)
valid_image = []
for idx,row in labels_df.iterrows():
img_path = os.path.join(image_dir,row["image_path"])
if not os.path.exists(img_path):
logger.warning(f"图像不存在:{img_path},跳过")
continue
# 3.读取图像并检查清晰度(拉普拉斯方差<100视为模糊)
img = cv2.imread(img_path)
if img is None:
logger.warning(f"图像读取失败:{img_path},跳过")
continue
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
laplacian_var = cv2.Laplacian(gray,cv2.CV_64F).var()
if laplacian_var < 100:
logger.warning(f"图像模糊(方差={laplacian_var}):{img_path},跳过")
continue
# 4.统一格式(Resize到1024*768,转为RGB)
img_resized = cv2.resize(img,(1024,768))
img_rgb = cv2.cvtColor(img_resized,cv2.COLOR_BGR2RGB)
# 保存清洗后图像
output_path = os.path.join(output_dir,row["image_path"])
cv2.imwrite(output_path,cv2.cvtColor(img_rgb,cv2.COLOR_RGB2BGR))# OpenCV默认BGR
# 5.修正标注
h,w = img_resized.shape[:2]
x,y,bw,bh = row["bbox_x"],row["bbox_y"],row["bbox_w"],row["bbox_h"]
x = max(0,min(x,w-1))
y = max(0,min(h-1))
bw = min(bw,w-x)
bh = min(bh,h-y)
valid_images.append({
"image_path": output_path, "defect_type": row["defect_type"],
"bbox_x": x, "bbox_y": y, "bbox_w": bw, "bbox_h": bh, "is_defective": row["is_defective"]
})
# 6.保存清洗后标注
cleaned_labels = pd.DataFrame(valid_images)
cleaned_label_path = os.path.join(output_dir,"cleaned_labels.csv")
cleaned_labels.to_csv(cleaned_label_path,index=False)
logger.info(f"清洗完成:有效图像{len(cleaned_labels)}张,保存至{output_dir}")
return cleaned_labels
if __name__ == "__main__":
# 路径配置(MinIO挂载目录)
image_dir = "/mnt/minio/raw/pcb_images"
label_path = "/mnt/minio/raw/defect_labels.csv"
output_dir = "/mnt/minio/cleaned/pcb_images"
clean_image(image_dir,label_path,output_dir)
(3)特征工程与特征数据生成(明确feature_path/label_path)
将清洗后图像转换为CNN可输入的标准化数据集,核心是数据增强(扩充样本)、归一化(像素值→0-1)、通道调整(RGB→3通道)
- 数据增强:针对小样本缺陷(如虚焊样本少),采用旋转(±15°)、水平翻转、亮度调整(±20%)、高斯噪声(σ=0.01)扩充样本
- 特征标准化:像素值归一化(除以255)、均值方差归一化(ImageNet均值[0.485, 0.456, 0.406],方差[0.229, 0.224, 0.225])
- 特征数据存储:feature_path指向预处理后的图像数据集(按train/val/test划分,含增强后图像),label_path指向标注文件(含图像路径、缺陷类型、边界框)
代码文件:feature_engineering/image_preprocessor.py(预处理)、feature_engineering/data_augmentation.py(增强)、feature_engineering/generate_feature_data.py(特征数据生成)
① 数据增强(data_augmentation.py)
python
import albumentations as A
import cv2
import os
import pandas as pd
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def augment_image(image,transform):
"""对单张图像应用增强变换"""
return transform(image=image)["image"]
def augment_dataset(cleaned_labels:pd.DataFrame,output_dir:str,augment_factor: int = 3)->pd.DataFrame:
"""数据集增强:对缺陷样本(is_defective=1)扩充augment_factor倍"""
os.makedirs(output_dir,exist_ok=True)
autmented_labels = []
# 定义增强管道(针对焊点缺陷特点:小目标、局部特征)
transform = A.Compose([
A.RandomRotate90(p=0.5),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.5),
A.GaussNoise(var_limit=(0.01, 0.05), p=0.3),
A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.1, rotate_limit=15, p=0.5) # 小角度旋转
],bbox_params=A.BboxParams(format="pascal_voc", label_fields=["class_labels"]))
for idx,row in cleaned_labels,iterrows():
img_path = row["image_path"]
img = cv2.imread(img_path)
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) # 转为RGB
bboxed = [[row["bbox_x"], row["bbox_y"], row["bbox_x"]+row["bbox_w"], row["bbox_y"]+row["bbox_h"]]] # pascal_voc格式
class_labels = [row["defect_type"]]
# 原始样本保留
augmented_labels.append(row)
cv2.imwrite(os.path.join(output_dir, os.path.basename(img_path)), cv2.cvtColor(img, cv2.COLOR_RGB2BGR))
# 对缺陷样本增强
if row["is_defective"] == 1:
for _ in range(augment_factor-1): # 总样本数=1+(augment_factor-1)=augment_factor
augmented = augment_image(img, transform)
aug_img_path = os.path.join(output_dir, f"aug_{idx}_{_}.jpg")
cv2.imwrite(aug_img_path, cv2.cvtColor(augmented, cv2.COLOR_RGB2BGR))
# 增强后边界框(简化处理:假设增强不改变bbox相对位置,实际需用transform同步变换)
augmented_labels.append({
"image_path": aug_img_path, "defect_type": row["defect_type"],
"bbox_x": row["bbox_x"], "bbox_y": row["bbox_y"],
"bbox_w": row["bbox_w"], "bbox_h": row["bbox_h"], "is_defective": 1
})
# 保存增强后标注
augmented_df = pd.DataFrame(augmented_labels)
logger.info(f"数据增强完成:原始{len(cleaned_labels)}张,增强后{augmented_df['image_path'].nunique()}张")
return augmented_df
② 特征数据生成(generate_feature_data.py,明确feature_path/label_path)
python
import pandas as pd
import os
from data_augmentation import augment_dataset
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def generate_feature_data(cleaned_label_path: str, output_dir: str) -> tuple:
"""生成特征数据集(feature_path)和标注文件(label_path)"""
# 1. 加载清洗后标注
cleaned_labels = pd.read_csv(cleaned_label_path)
# 2. 数据增强(扩充缺陷样本3倍)
augmented_labels = augment_dataset(cleaned_labels, output_dir, augment_factor=3)
# 3. 划分数据集(train:val:test=7:2:1)
from sklearn.model_selection import train_test_split
train_df, test_val_df = train_test_split(augmented_labels, test_size=0.3, random_state=42)
val_df, test_df = train_test_split(test_val_df, test_size=0.33, random_state=42) # 0.3 * 0.33≈0.1
# 4. 定义文件路径(算法团队存储位置)
feature_path = { # 特征数据集路径(按划分存储)
"train": os.path.join(output_dir, "train"),
"val": os.path.join(output_dir, "val"),
"test": os.path.join(output_dir, "test")
}
label_path = { # 标注文件路径(按划分存储)
"train": os.path.join(output_dir, "train_labels.csv"),
"val": os.path.join(output_dir, "val_labels.csv"),
"test": os.path.join(output_dir, "test_labels.csv")
}
# 5. 保存划分后数据
train_df.to_csv(label_path["train"], index=False)
val_df.to_csv(label_path["val"], index=False)
test_df.to_csv(label_path["test"], index=False)
logger.info(f"""
【算法团队特征数据存储说明】
- feature_path: {feature_path}
存储内容:预处理+增强后的图像数据集(按train/val/test子目录存储),图像格式JPG,尺寸1024×768,RGB通道
示例:train/PCB_20231001_S001.jpg(原始)、train/aug_0_0.jpg(增强后)
- label_path: {label_path}
存储内容:划分后的标注文件(CSV格式),含列:
image_path(图像路径)、defect_type(缺陷类型:虚焊/连锡/漏焊/偏移/多余焊/正常)、
bbox_x/y/w/h(边界框,仅缺陷样本有值)、is_defective(1=缺陷,0=正常)
示例(train_labels.csv前2行):\n{train_df.head(2)}
""")
return feature_path, label_path, train_df, val_df, test_df


代码
算法团队
text
algorithm-defect-detection/
├── data_processing/ # 数据清洗(图像去模糊/格式统一)
│ ├── image_cleaning.py # 清洗代码(含详细注释)
│ └── requirements.txt # 依赖:opencv-python, pandas, pillow
├── feature_engineering/ # 特征工程(增强/归一化)
│ ├── image_preprocessor.py # 图像归一化/通道调整
│ ├── data_augmentation.py # 数据增强(Albumentations)
│ ├── generate_feature_data.py # 特征数据生成(含feature_path/label_path说明)
│ └── requirements.txt # 依赖:albumentations, scikit-learn
├── model_training/ # CNN模型训练(核心)
│ ├── cnn_model.py # CNN模型定义(ResNet迁移学习)
│ ├── train_cnn.py # 训练入口(损失函数/优化器/早停)
│ ├── evaluate_model.py # 评估(准确率/召回率/mAP)
│ └── cnn_params.yaml # 调参记录(lr=0.001, batch_size=32)
├── model_storage/ # 模型存储(MinIO)
│ ├── save_model.py # 保存模型(.pt格式)
│ └── load_model.py # 加载模型(推理用)
└── mlflow_tracking/ # MLflow实验跟踪
└── run_cnn_experiment.py # 记录超参数/指标/模型
(1)算法团队:CNN模型构建与训练(model_training/cnn_model.py,含原理注释)
重点解析:基于ResNet-18迁移学习,冻结浅层特征,微调深层适配焊点缺陷,输出5类缺陷分类结果
python
import torch
import torch.nn as nn
from torchivision.models import Resnet18_Weights
import torch.nn.functional as F
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class PCBDefectCNN(nn.Module):
"""基于ResNet-18的PCB焊点缺陷检测CNN模型"""
def __init__(self,num_classes=6,pretrained=True):
"""
:param num_classes: 缺陷类别数(5类缺陷+1类正常=6)
:param pretrained: 是否加载ImageNet预训练权重(迁移学习)
"""
super(PCBDefectCNN,self).__init__()
#加载预训练ResNet-18
self.base_model = resnet18(weights=ResNet18_Weights.DEFAULT if pretrained else None)
# 冻结浅层特征(前4层卷积,保留通用边缘/纹理提取能力)
for param in list(self.base_model.parameters())[:-10]: # 冻结除最后2层外的参数
param.requires_grad = False
# 替换最后一层全连接层(适配6类分类)
in_features = self.base_model.fc.in_features
self.base_model.fc = nn.Sequential(
nn.Linear(in_features,256),#中间层降维
nn.ReLU(),
nn.Dropout(0.5),#防止过拟合
nn.Linear() # 输出6类概率
)
logger.info(f"CNN模型初始化完成:num_classes={num_classes},pretrained={pretrained}")
def forward(self,x):
"""前向传播:输入图像张量->输出类别概率"""
# 卷积层提取特征(前层->:边缘-纹理-缺陷语义)
x = self.base_model.conv1(x) # 卷积层1(7×7核,输出64通道)
x = self.base_model.bn1(x) # 批归一化
x = self.base_model.relu(x) # ReLU激活
x = self.base_model.maxpool(x) # 最大池化
# 残差块(Residual Block)提取高层特征(如焊点形状/锡膏分布)
x = self.base_model.layer1(x) # 残差块1(输出64通道)
x = self.base_model.layer2(x) # 残差块2(输出128通道)
x = self.base_model.layer3(x) # 残差块3(输出256通道)
x = self.base_model.layer4(x) # 残差块4(输出512通道)
# 全局平局池化(将512×7×7→512×1×1)
x = self.base_model.avgpool(x)
x = torch.flatten(x,1) # 展平为一维向量(512维)
#全连接层输出分类概率
x = self.base_model.fc(x) #输出6类logits
return x
# 示例:模型训练配置(损失函数+优化器)
def get_loss_optimizer(model,lr=0.001,weight_decay=1e-4):
"""获取损失函数(交叉熵)和优化器(AdamW)"""
criterion = nn.CrossEntropyLoss(weight=torch.tensor([1.0, 2.0, 2.0, 2.0, 2.0, 1.0]))# 缺陷类权重更高(防漏检)
optimizer = torch.optim.AdamW(model.parameters(),lr=lr,weight_decay=weight_decay)
return criterion, optimizer
(2)算法团队:模型训练与评估(model_training/train_cnn.py)
python
import torch
from torch.utils.data import DataLoader,Dataset
from torchvision import transforms
import pandas as pd
from cnn_model import PCBDefectCNN,get_loss_optimizer
from PIL import Image
import mlflow
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 自定义数据集(加载图像+标注)
class PCBDataset(Dataset):
def __init__(self,label_path,transform=None):
self.df = pd.read_csv(label_path)
self.transform = transform
self.classes = ["正常", "虚焊", "连锡", "漏焊", "偏移", "多余焊"] # 6类
def __len__(self):
return len(self.df)
def __getitem__(self,idx):
row = self.df.iloc[idx]
img = Image.open(row["image_path"]).convert("RGB") # 加载RGB图像
label = self.classes.index(row["defect_type"])if row["is_defective"] == 1 else 0 # 正常类索引0
if self.transform:
img = self.transform(img)
return img,label
def train_cnn(train_label_path: str, val_label_path: str, epochs=50, batch_size=32):
"""训练CNN模型"""
# 1.数据预处理(归一化+Resize)
transform = transforms.Compose([
transforms.Resize((224,224)),# ResNet输入尺寸
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]) # ImageNet归一化
])
# 2.加载数据集
train_dataset = PCBDataset(train_label_path,transform=trainsform)
val_dataset = PCBDataset(val_label_path,transform=transform)
train_loader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True)
val_loader = DataLoader(val_dataset,batch_size=batch_size,shuffle=False)
# 3.初始化模型、损失函数、优化器
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = PCBDefectCNN(num_classes=6,pretrained=True).to(device)
criterion,optimizer = get_loss_optimizer(model,lr=0.001)
# 4.循环训练(含早停)
best_val_acc = 0.0
for epoch in range(epochs):
model.train()
train_loss = 0.0
for imgs,labels in train_loader:
imgs,labels = imgs.to(device),labels.to(device)
optimizer.zero_grad()
outputs = model(imgs)
loss = criterion(outputs,labels)
loss.backward()
optimizer.step()
train_loss += loss.item() * imgs.size(0)
train_loss /= len(train_dataset)
# 验证集评估
model.eval()
val_correct = 0
with torch.no_grad():
for imgs,labels in val_loader:
imgs,labels = imgs.to(device),labels.to(device)
outputs = model(imgs)
_,preds = torch.max(outputs,1)
val_correct += (preds == labels).sum().item()
# 记录MLflow实验
with mlflow.start_run(run_name=f"cnn_epoch_{epoch}"):
mlflow.log_metric("train_loss", train_loss, step=epoch)
mlflow.log_metric("val_acc", val_acc, step=epoch)
if val_acc > best_val_acc:
best_val_acc = val_acc
torch.save(model.state_dict(),"model/pcb_defect_cnn_best.pt") # 保存最佳模型
mlflow.log_artifact("model/pcb_defect_cnn_best.pt")
logger.info(f"Epoch {epoch+1}/{epochs}, Train Loss: {train_loss:.4f}, Val Acc: {val_acc:.4f}")
logger.info(f"训练完成,最佳验证准确率:{best_val_acc:.4f}")
return model
if __name__ == "__main__":
train_label_path = "/mnt/minio/processed/train_labels.csv"
val_label_path = "/mnt/minio/processed/val_labels.csv"
train_cnn(train_label_path,val_label_path,epochs=50,batch_size=32)
算法团队服务:模型训练任务(Ray集群Job)、MinIO模型存储(持久化卷挂载)
业务团队
text
business-defect-detection/
├── api_gateway/ # API网关(Kong配置)
├── defect_detection_service/ # 缺陷检测服务(Go)
│ ├── main.go # FastAPI风格Go服务(调用CNN模型)
│ ├── model_loader.go # 加载MinIO模型(.pt→TensorRT引擎)
│ └── Dockerfile # 容器化配置
├── quality_inspection_system/ # 质检系统(Java+React)
│ ├── backend/ # Spring Boot后端(缺陷统计/根因分析)
│ ├── frontend/ # React前端(产线终端可视化)
│ └── sql/ # PostgreSQL表结构(检测结果/缺陷记录)
├── sorting_control_system/ # 分拣控制系统(PLC联动)
└── monitoring/ # 监控告警(Prometheus+Grafana)
代码文件:defect_detection_service/main.go(Go语言API服务)
go
package main
import (
"encoding/json"
"fmt"
"image"
_ "image/jpeg"
"log"
"net/http"
"os"
"gorgonia.org/tensor" // 伪代码:张量处理库
"path/to/torchscript" // 伪代码:PyTorch模型加载库
)
// 请求/响应结构体
type DefectRequest struct {
ImagePath string `json:"image_path"` // 产线图像路径(或Base64编码)
}
type DefectResponse struct {
ImagePath string `json:"image_path"`
DefectType string `json:"defect_type"` // 缺陷类型(如"虚焊")
Confidence float64 `json:"confidence"` // 置信度(0-1)
Bbox []int `json:"bbox"` // 边界框[x,y,w,h]
IsDefective bool `json:"is_defective"`
}
// 全局变量:加载CNN模型
var model *torchscript.Model
func init() {
// 加载算法团队训练的模型(MinIO路径)
modelPath := "s3://pcb-factory-models/pcb_defect_cnn_best.pt"
loadedModel, err := torchscript.Load(modelPath)
if err != nil {
log.Fatalf("模型加载失败:%v", err)
}
model = loadedModel
log.Println("CNN模型加载成功")
}
// 缺陷检测API处理函数
func detectDefectHandler(w http.ResponseWriter, r *http.Request) {
var req DefectRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "无效请求", http.StatusBadRequest)
return
}
// 1. 读取图像并预处理(Resize到224×224,归一化)
file, err := os.Open(req.ImagePath)
if err != nil {
http.Error(w, "图像读取失败", http.StatusInternalServerError)
return
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
http.Error(w, "图像解码失败", http.StatusInternalServerError)
return
}
// 预处理(简化:实际用OpenCV实现Resize+归一化)
inputTensor := preprocessImage(img) // 输出3×224×224张量
// 2. 模型推理(调用CNN)
output, err := model.Forward(inputTensor)
if err != nil {
http.Error(w, "模型推理失败", http.StatusInternalServerError)
return
}
// 3. 解析结果(取最大概率类别)
probs := output.Data().([]float32) // 6类概率
maxProb := 0.0
classIdx := 0
for i, p := range probs {
if p > maxProb {
maxProb = p
classIdx = i
}
}
defectTypes := []string{"正常", "虚焊", "连锡", "漏焊", "偏移", "多余焊"}
resp := DefectResponse{
ImagePath: req.ImagePath,
DefectType: defectTypes[classIdx],
Confidence: float64(maxProb),
IsDefective: classIdx != 0, // 0=正常
}
// 4. 返回响应
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func main() {
http.HandleFunc("/api/detect-defect", detectDefectHandler)
log.Println("缺陷检测服务启动,监听端口8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
业务团队服务:缺陷检测API(Go服务,Deployment部署,4副本)、质检系统(Java服务,2副本)、分拣控制系统(Edge端部署)
部署后应用流程
Step 1:产线图像采集与上传
产线摄像头(500万像素)拍摄PCB板图像(4096×2160),通过Kafka实时传输至数据湖(MinIO),文件名含板ID与序列号(如PCB_20231001_S001.jpg)
Step 2:实时缺陷检测
质检系统调用缺陷检测API(POST /api/detect-defect),传入图像路径;服务加载CNN模型,预处理后推理,返回缺陷类型、置信度、边界框
Step 3:结果反馈与分拣
产线终端(React前端)实时显示检测结果:绿色(正常)/红色(缺陷+类型),分拣控制系统(PLC)根据is_defective信号自动剔除缺陷板
Step 4:监控与迭代
监控系统跟踪漏检率,若连续3天漏检率>2%,触发Airflow调度算法团队重训模型(补充新缺陷样本+调整权重);每月生成《缺陷分析报告》,定位产线根因(如贴片机压力异常导致连锡)