电子厂PCB板焊点缺陷检测(卷积神经网络CNN)

业务痛点:某电子厂(年产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调度算法团队重训模型(补充新缺陷样本+调整权重);每月生成《缺陷分析报告》,定位产线根因(如贴片机压力异常导致连锡)

相关推荐
风象南20 分钟前
Claude Code这个隐藏技能,让我告别PPT焦虑
人工智能·后端
Mintopia1 小时前
OpenClaw 对软件行业产生的影响
人工智能
陈广亮2 小时前
构建具有长期记忆的 AI Agent:从设计模式到生产实践
人工智能
会写代码的柯基犬2 小时前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
Mintopia2 小时前
OpenClaw 是什么?为什么节后热度如此之高?
人工智能
爱可生开源社区2 小时前
DBA 的未来?八位行业先锋的年度圆桌讨论
人工智能·dba
叁两5 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
前端付豪5 小时前
LangChain记忆:通过Memory记住上次的对话细节
人工智能·python·langchain
strayCat232555 小时前
Clawdbot 源码解读 7: 扩展机制
人工智能·开源