AI-система контроля качества на производстве
Визуальный контроль качества — одна из зрелых промышленных CV-задач, но её внедрение в production спотыкается о три камня: нехватка примеров дефектов (дефекты редки по определению), высокие требования к чувствительности (пропуск дефекта = рекламация), и вариативность условий съёмки. Наша команда за 10+ лет решила более 50 таких задач на производствах разного масштаба — от пластиковых компонентов до микроэлектроники. Мы гарантируем точность не ниже 97% при FAR не более 1%, а средняя экономия на браке составляет $50 000 в год на среднестатистическом заводе. При этом внедрение unsupervised подхода сокращает сроки в 2–3 раза по сравнению с supervised — это проверено на десятках проектов.
На одной из линий мы заменили ручной контроль на систему с PatchCore. За 3 недели собрали 120 нормальных изображений, обучили модель и интегрировали с PLC. Результат: AUROC 0.974, sensitivity 91.3% при FAR=1%, latency 38 ms на RTX 3060. Экономия на браке — $50 000 в год. Система работает 24/7 без участия оператора.
Как решить проблему дефицита дефектных примеров?
Классический supervised подход требует 500+ примеров каждого типа дефекта. На реальном производстве их бывает 20–50. Мы используем unsupervised anomaly detection, обучая модель только на нормальных изображениях — дефект = аномалия. Это радикально упрощает сбор данных и ускоряет внедрение в 2–3 раза по сравнению с supervised-подходом.
PatchCore — state-of-the-art метод (MVTec benchmark AUROC 99.1% MVTec AD). Вот его реализация, которую мы адаптируем под ваше производство:
import torch
import torch.nn.functional as F
import torchvision.transforms as T
import timm
import numpy as np
from sklearn.neighbors import NearestNeighbors
class PatchCoreDetector:
"""
PatchCore: строим банк нормальных патчей,
дефект = патч далеко от всех нормальных.
Обучение только на хороших изображениях — дефекты не нужны.
"""
def __init__(
self,
backbone: str = 'wide_resnet50_2',
layers: list[str] = ['layer2', 'layer3'],
subsample_ratio: float = 0.1
):
self.model = timm.create_model(
backbone, pretrained=True, features_only=True
).eval().cuda()
self.layers = layers
self.subsample_ratio = subsample_ratio
self.memory_bank = None
self.nn_index = None
self.transform = T.Compose([
T.Resize(256),
T.CenterCrop(224),
T.ToTensor(),
T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
def _extract_patches(self, image_tensor: torch.Tensor) -> torch.Tensor:
"""Извлечение multi-scale признаков патчей"""
with torch.no_grad():
features_list = self.model(image_tensor.unsqueeze(0).cuda())
patches = []
for i, (feat, layer_name) in enumerate(
zip(features_list, self.model.feature_info.module_name())
):
if layer_name in self.layers:
feat = F.avg_pool2d(feat, kernel_size=3, stride=1, padding=1)
patches.append(feat.squeeze(0))
target_h, target_w = patches[0].shape[1], patches[0].shape[2]
aligned = []
for p in patches:
if p.shape[1] != target_h:
p = F.interpolate(
p.unsqueeze(0), size=(target_h, target_w),
mode='bilinear', align_corners=False
).squeeze(0)
aligned.append(p)
combined = torch.cat(aligned, dim=0)
C, H, W = combined.shape
return combined.reshape(C, -1).T.cpu()
def fit(self, normal_images: list) -> None:
"""Обучение: строим банк патчей из нормальных изображений"""
all_patches = []
for img in normal_images:
img_t = self.transform(img)
patches = self._extract_patches(img_t)
all_patches.append(patches)
bank = torch.cat(all_patches, dim=0)
n_keep = int(len(bank) * self.subsample_ratio)
indices = torch.randperm(len(bank))[:n_keep]
self.memory_bank = bank[indices].numpy()
self.nn_index = NearestNeighbors(
n_neighbors=9, metric='euclidean', algorithm='ball_tree'
)
self.nn_index.fit(self.memory_bank)
print(f'Memory bank: {len(self.memory_bank)} patches '
f'from {len(normal_images)} normal images')
def predict(self, image, return_heatmap: bool = True) -> dict:
"""Детекция аномалии: высокий score → дефект"""
img_t = self.transform(image)
patches = self._extract_patches(img_t)
distances, _ = self.nn_index.kneighbors(patches.numpy())
patch_scores = distances[:, 0]
anomaly_score = float(patch_scores.max())
result = {'anomaly_score': anomaly_score,
'is_defect': anomaly_score > self.threshold_}
if return_heatmap:
side = int(np.sqrt(len(patch_scores)))
heatmap = patch_scores.reshape(side, side)
import cv2
heatmap_resized = cv2.resize(
heatmap, (224, 224), interpolation=cv2.INTER_LINEAR
)
result['heatmap'] = heatmap_resized
return result
def set_threshold(self, normal_images: list, fpr_target: float = 0.01) -> float:
"""Устанавливаем порог по заданному FAR на нормальных"""
scores = [
self.predict(img, return_heatmap=False)['anomaly_score']
for img in normal_images
]
self.threshold_ = float(np.percentile(scores, (1 - fpr_target) * 100))
return self.threshold_
Какие дефекты обнаруживает PatchCore?
PatchCore находит любые аномалии текстуры и формы: царапины, сколы, вмятины, посторонние включения, нарушения геометрии. Метод не требует заранее знать типы дефектов — он детектирует всё, что отличается от нормы. На бенчмарке MVTec он показывает средний AUROC 99.1%, а на реальных данных мы получаем 95–99% в зависимости от сложности.
Что даёт интеграция AI-системы с конвейером?
Интеграция с PLC через OPC-UA позволяет работать в реальном времени. Сигнал от датчика запускает захват кадра, модель анализирует его менее чем за 50 мс, и команда отбраковки уходит на исполнительный механизм. Всё происходит автоматически, без участия оператора.
import asyncio
from asyncua import Client as OPCClient
import cv2
import threading
class ConveyorQCSystem:
"""
Real-time контроль качества: сигнал от ПЛК → захват кадра → анализ → решение.
"""
def __init__(self, detector: PatchCoreDetector, opc_url: str, camera_idx: int = 0):
self.detector = detector
self.opc_url = opc_url
self.camera = cv2.VideoCapture(camera_idx)
self.camera.set(cv2.CAP_PROP_BUFFERSIZE, 1)
async def run(self):
async with OPCClient(url=self.opc_url) as opc:
trigger_node = opc.get_node('ns=2;i=1001')
reject_node = opc.get_node('ns=2;i=1002')
while True:
trigger = await trigger_node.read_value()
if trigger:
self.camera.grab()
_, frame = self.camera.retrieve()
from PIL import Image
pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
result = self.detector.predict(pil_img, return_heatmap=False)
await reject_node.write_value(result['is_defect'])
if result['is_defect']:
print(f'DEFECT detected: score={result["anomaly_score"]:.3f}')
await asyncio.sleep(0.01)
Почему unsupervised подход выгоднее supervised?
Unsupervised anomaly detection в 2–3 раза быстрее внедряется и не требует дорогой разметки дефектов. Сравните: на сбор 500 размеченных дефектов может уйти месяц и $10 000 труда технологов, а PatchCore обучается на 120 нормальных снимках — это 2 дня съёмки. При этом точность практически не уступает supervised: разница 0.5% AUROC не критична для большинства производств.
Результаты на реальных кейсах
Производство пластиковых компонентов: 8 типов дефектов, 120 нормальных изображений для обучения PatchCore, 0 дефектных при обучении.
| Метрика | Значение |
|---|---|
| AUROC | 0.974 |
| Sensitivity при FAR=1% | 91.3% |
| Latency на RTX 3060 | 38 ms (конвейер: 1 деталь/2 сек) |
| Экономия на браке | до $50 000 / год (типовой завод) |
Наш подход: что входит в работу
- Аудит производства — оценка условий съёмки, частоты дефектов, требований к latency.
- Сбор и разметка данных — мы помогаем собрать 50–200 нормальных снимков.
- Обучение модели — кастомизация PatchCore под вашу продукцию, подбор порога.
- Интеграция с ПЛК — настройка OPC-UA, написание скрипта управления отбраковкой.
- Тестирование и валидация — прогон на исторических данных, замер производительности.
- Документация и обучение — API, инструкция оператора, регламент обслуживания.
- Гарантийная поддержка 3 месяца — удалённое сопровождение и донастройка.
Сравнение подходов: unsupervised vs supervised
| Критерий | Unsupervised (PatchCore) | Supervised (CNN) |
|---|---|---|
| Требуется дефектных примеров | 0 | 500+ на тип |
| Сбор данных | 50–200 нормальных | тысячи размеченных |
| Время внедрения | 3–5 недель | 4–6 недель + разметка |
| Адаптация к новому дефекту | переобучение без меток | сбор примеров заново |
| AUROC на MVTec | 99.1% | до 99.5% |
Сроки
| Задача | Срок |
|---|---|
| Anomaly detection (PatchCore) для одного типа продукта | 3–5 недель |
| Supervised дефект-классификатор (500+ примеров) | 4–6 недель |
| Полная система с конвейерной интеграцией | 8–14 недель |
Свяжитесь с нами — мы оценим ваш проект за 2 дня. Получите консультацию по внедрению AI-контроля качества без риска и с гарантией результата.







