Реализация AI-детекции дефектов на производстве через камеру мобильного приложения
Визуальный контроль качества на производстве — область, где мобильное устройство конкурирует со стационарными machine vision системами (Cognex, Keyence). Мобильный инспектор дешевле, гибче, не требует интеграции в линию, но ставит другие инженерные задачи: нестабильное освещение, непостоянное расстояние до объекта, вибрация при ручном захвате.
Задача: специфика промышленного контроля качества
Дефекты на производстве кардинально отличаются по типу в зависимости от отрасли:
| Отрасль | Типичные дефекты | Критичный размер |
|---|---|---|
| Печатные платы (PCB) | Отсутствие компонента, неправильная ориентация, пайка | 0.5–2 мм |
| Текстиль | Затяжки, проколы, разрыв нити | 1–5 мм |
| Металлопрокат | Царапины, поры, включения | 0.1–3 мм |
| Стекло/керамика | Сколы, трещины, пузыри | 0.5–10 мм |
| Упаковка | Отсутствие этикетки, неправильная печать | >5 мм |
Для каждой отрасли — своя модель. Универсальная модель дефектов не работает: то, что является дефектом на PCB, может быть нормой на металле.
Архитектура on-device инференса
Для производственного использования приоритет — on-device. Интернет-зависимость неприемлема на линии. Размер модели ограничен RAM устройства, скорость — требованиями throughput.
// iOS: промышленная детекция дефектов через CoreML
class DefectDetectionEngine {
private let model: VNCoreMLModel
private var confidenceThreshold: Float = 0.5 // регулируется на стенде
private var iouThreshold: Float = 0.45
// Для стабильного FPS — отдельная очередь
private let inferenceQueue = DispatchQueue(
label: "defect.inference",
qos: .userInteractive
)
func analyze(sampleBuffer: CMSampleBuffer) async throws -> [DefectDetection] {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
throw DefectError.invalidFrame
}
return try await withCheckedThrowingContinuation { continuation in
inferenceQueue.async {
let request = VNCoreMLRequest(model: self.model) { req, error in
if let error = error {
continuation.resume(throwing: error)
return
}
let detections = (req.results as? [VNRecognizedObjectObservation])?
.filter { $0.confidence >= self.confidenceThreshold }
.map { obs in
DefectDetection(
type: DefectType(rawValue: obs.labels.first?.identifier ?? "") ?? .unknown,
confidence: obs.confidence,
boundingBox: obs.boundingBox, // нормализованный [0,1]
severity: self.classifySeverity(obs)
)
} ?? []
continuation.resume(returning: detections)
}
request.imageCropAndScaleOption = .scaleFill
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer)
try? handler.perform([request])
}
}
}
}
Производительность. YOLOv8n в CoreML на iPhone 14: ~15 мс на инференс (66 FPS потенциально). YOLOv8s: ~25 мс. Для линии с требованием > 30 кадров/сек при ручном удержании — n-вариант.
Стабилизация изображения для ручного сканирования
Вибрация рук — враг мелких дефектов. Несколько техник:
// Буферизация кадров + выбор наиболее чёткого
class StabilizedFrameSelector {
private var frameBuffer: RingBuffer<CMSampleBuffer> = RingBuffer(capacity: 8)
private var sharpnessScores: [Float] = []
func addFrame(_ buffer: CMSampleBuffer) {
let sharpness = computeLaplacianVariance(buffer)
frameBuffer.push(buffer)
sharpnessScores.append(sharpness)
}
// Для анализа — берём кадр с пиковой резкостью за последние N кадров
var bestFrame: CMSampleBuffer? {
guard let maxIdx = sharpnessScores.indices.max(by: { sharpnessScores[$0] < sharpnessScores[$1] }) else { return nil }
return frameBuffer[maxIdx]
}
}
Также: AVCaptureDevice.activeVideoMinFrameDuration + exposureMode = .continuousAutoExposure + стабилизация через videoStabilizationMode = .cinematic.
Обучение и дообучение на данных производства
Готовых датасетов для специфического производства нет. Нужна своя разметка.
Процесс:
- Съёмка 200–500 образцов на производстве (нормальные + дефектные)
- Разметка в Label Studio или CVAT (bounding boxes + классы дефектов)
- Аугментация: яркость ±30%, вращение ±15°, горизонтальный flip, gaussian noise — имитация реальных условий съёмки
- Обучение YOLOv8s/m (зависит от требований по скорости)
- Конвертация в CoreML (.mlpackage) или TFLite
- Итерационное дообучение по ошибкам с производства — каждые 2–4 недели
# Дообучение на новых данных производства
from ultralytics import YOLO
model = YOLO("defect_detection_v2.pt") # предыдущая версия как база
results = model.train(
data="production_defects.yaml",
epochs=50,
imgsz=640,
batch=16,
lr0=0.001, # меньший LR для fine-tuning
freeze=10, # заморозить первые 10 слоёв backbone
augment=True,
hsv_h=0.015,
hsv_s=0.7,
degrees=10.0,
translate=0.1,
scale=0.5,
mosaic=1.0
)
Интеграция с производственными системами
Мобильный инспектор должен фиксировать результаты в MES/ERP системе:
// Android: отправка результата инспекции
data class InspectionResult(
val productId: String,
val batchId: String,
val inspectorId: String,
val timestamp: Instant,
val detections: List<DefectDetection>,
val verdict: InspectionVerdict, // PASS, FAIL, REVIEW
val imageUrl: String, // сохранённое фото с аннотациями
val deviceId: String
)
suspend fun submitInspection(result: InspectionResult) {
// Сначала — локальная очередь (производство может быть без Wi-Fi)
localQueue.enqueue(result)
// Синхронизация при появлении сети
syncManager.triggerSync()
}
Offline-first принципиально важен: цеховое Wi-Fi нестабильно, потеря результата инспекции недопустима.
Ориентиры по срокам
MVP с базовой моделью (200–300 размеченных образцов), on-device инференс, локальная история инспекций — 3–4 недели. Полная система с дообученной моделью под конкретный производственный процесс, стабилизацией изображения, offline-first синхронизацией с MES/ERP, dashboard статистики дефектов и поддержкой iOS + Android — 2–3 месяца.







