PyTorch ve YOLO ile Gıda Kalite Kontrolü için Bilgisayarlı Görme
Üretim sırasında tespit edilemeyen gıda kusurları her yıl küresel endüstriye zarar veriyor yaklaşık 7 milyar dolar: ürünün geri çekilmesi, piyasadan toplatılması, ürünün zarar görmesi marka itibarı, düzenleyici yaptırımlar ve en ciddi durumlarda tüketicinin sağlığına yönelik riskler. Kavanozda kalan çürük bir domates, çizgiyi aşan metalik bir yabancı cisim paketleme, gizli küflü meyve partisi: manuel denetim sistemlerinin yaptığı durumlar hat saniyede 10-20 parça hızla dönerken yeterince tutarlı bir şekilde müdahale edemezler.
İnsanın görsel muayenesi etkilidir ancak doğası gereği sınırlıdır: Sağlıklı bir denetçi, iyi eğitilmiş ve dinlenmiş, yüksek hızlı hatlarda %60-70 civarında doğruluk elde eder, yorgunluk, aydınlatma ve öznellikle bağlantılı önemli değişkenliklerle. Sistemleri Ancak derin öğrenmeye dayalı bilgisayar görüşü, doğruluk %98'den fazla, Günde 24 saat, gece vardiyasında performansta düşüş olmadan ve değerlendirmede mutlak tutarlılıkla.
Bu bağlamda Ultralytics'in YOLO (Yalnızca Bir Kez Bakarsınız) ailesi standart haline geldi gerçek zamanlı endüstriyel denetim için fiili. Eylül 2024'te piyasaya sürülen YOLO11 şunları sunuyor: olağanüstü performans: Karşılaştırmada daha yüksek mAP ile YOLOv8'e göre %22 daha az parametre COCO, T4 GPU'larda 2 ms'nin altındaki gecikmeler ve milimetrik boyutlu kusurları tespit etme yeteneği yüksek hızlı konveyör bantlarında. Gıda kalite kontrolü için yapay zeka pazarı ve gıda güvenliği sağlandı 2025'te 2,7 milyar dolar ve 2030 yılına kadar %30,9'luk bir Bileşik Büyüme Oranıyla 13,7 milyara ulaşacak.
Bu makale eksiksiz bir teknik kılavuzdur: endüstriyel görüntüleme sistemlerinin mimarisinden, gıda kusurlarına ilişkin belirli veri kümelerinin oluşturulmasına ve açıklanmasına, eğitim hattına PyTorch ve YOLO11 ile endüstriyel donanım ile üretim hattında kuruluma kadar, PLC entegrasyonu ve otomatik sıralama sistemi. Her bölüm çalışan ve test edilmiş Python kodunu içerir.
Bu Makalede Neler Öğreneceksiniz?
- Gıda endüstrisine uygulanan bilgisayarlı görüntünün temelleri: belirli zorluklar ve geleneksel endüstriyel CV'den farklılıklar
- YOLO evrimi: YOLOv5'ten YOLO11'e, mimari, kıyaslamalar ve YOLO'nun gıda denetimi için Daha Hızlı R-CNN'de neden kazandığı
- Gıda kusurlarına yönelik bir veri kümesinin oluşturulması: CVAT/Roboflow ile açıklama, sınıflar, gıdaya özel veri artırma
- PyTorch ve YOLO11 ile eğitim hattını tamamlayın: Python kodu, hiperparametre ayarı, doğrulama
- Tespit, Sınıflandırma ve Segmentasyon: kalite kontrol için hangi yaklaşımın ne zaman kullanılması gerektiği
- Bir denetim hattının donanım mimarisi: GigE Vision kamera, yapısal aydınlatma, tetikleyici, PLC
- Kalite ölçütleri: mAP, hassasiyet, geri çağırma, gıda endüstrisi için kabul edilebilir eşikler
- Otomatik sıralama sistemi: pnömatik aktüatörlerin ve al ve yerleştir robotlarının entegrasyonu
- Tam örnek olay: YOLO11'li meyve ayıklama hattı, 10 parça/saniye, %98,3 doğruluk
- Düzenlemeler: Gıda ortamlarındaki görüntü sistemleri için IFS Food, BRC, HACCP
FoodTech Serisi - Tüm Makaleler
| # | Öğe | Seviye | Durum |
|---|---|---|---|
| 1 | Python ve MQTT ile Hassas Tarım için IoT Pipeline | Gelişmiş | Mevcut |
| 2 | Mahsul İzleme için ML Edge: Tarlalarda Bilgisayarlı Görme | Gelişmiş | Mevcut |
| 3 | Uydu API ve Bitki Örtüsü Endeksleri: Python ve Sentinel-2 ile NDVI | Orta seviye | Mevcut |
| 4 | Gıdada Blockchain izlenebilirliği: Tarladan süpermarkete | Orta seviye | Mevcut |
| 5 | PyTorch YOLO ile Kalite Kontrol için Bilgisayarlı Görme (şu anda buradasınız) | Gelişmiş | Akım |
| 6 | FSMA ve Dijital Uyumluluk: Düzenleyici Süreçlerin Otomasyonu | Orta seviye | Yakında gelecek |
| 7 | Dikey Tarım: IoT ve ML ile Çevresel Kontrol | Gelişmiş | Yakında gelecek |
| 8 | Prophet ve LightGBM ile Gıda Perakendesinde Talep Tahmini | Orta seviye | Yakında gelecek |
| 9 | Çiftlik Zekası Kontrol Paneli: Grafana ile Gerçek Zamanlı Analiz | Orta seviye | Yakında gelecek |
| 10 | Tedarik Zinciri Gıda Optimizasyonu: Atıkların Azaltılması için ML | Orta seviye | Yakında gelecek |
Gıda Endüstrisinde Bilgisayarla Görme: Zorluklar ve Fırsatlar
Gıda kalite kontrolüne yönelik bilgisayarlı görme, yalnızca "uygulamalı endüstriyel CV" değildir. yemeğe". Onu daha karmaşık ve aynı zamanda daha ilginç kılan benzersiz özelliklere sahiptir. mekanik bileşenlerin veya baskılı devre kartlarının muayenesiyle karşılaştırıldığında. Bu özellikleri anlayın ve sağlam ve güvenilir bir sistem oluşturmanın ilk adımı.
Gıda Ürünlerinin Doğal Değişkenliği
Arızalı bir cıvata, hassas ve değişmez geometrik kriterlerle uyumlu bir cıvatadan ayırt edilebilir: çap, adım, uzunluk. Bununla birlikte, "mükemmel" bir domates neredeyse sonsuz bir aralıkta mevcuttur. sadece farklı çeşitler arasında değil, aynı zamanda farklı şekillerde, renklerde, dokularda ve boyutlarda aynı ürün içinde. Görme sistemi değişkenliği ayırt etmeyi öğrenmelidir kusurdan kabul edilebilir doğal (biraz asimetrik ama tamamen sağlıklı bir domates) gerçek (bir güneş yanığı, bir darbe göçüğü, bir mantar saldırısı).
Bu zorluk, temsili örneklerle birlikte çok büyük ve dengeli eğitim veri kümeleri gerektirir ürünün tüm normal değişkenliklerinin dikkate alınması ve kalibrasyona özellikle dikkat edilmesi Hem yanlış negatifleri (tespit edilemeyen kusurları) hem de i'yi önlemek için karar eşiklerinin belirlenmesi yanlış pozitifler (işletme maliyetlerine doğrudan etki edecek şekilde uyumlu ürünler atılır).
Aydınlatma ve Çevre Sorunları
Gıda üretim hattının ortamı optik sistemler için düşmanca bir ortamdır: su buharı Yıkama işlemlerinden, soğutulmuş ortamlarda mercekler üzerinde yoğunlaşmadan, renk değişimlerinden konveyör bandı boyunca aydınlatma, parlak yüzeylerden gelen aynasal yansımalar mumlar veya sırlar. Yapılandırılmış aydınlatma (halka ışık, arka ışık, koaksiyel aydınlatma, polarize ışık) temeldir ve görüş sistemiyle birlikte tasarlanmalıdır, sonradan düşünülerek eklenmemiştir.
Şeffaf veya yarı şeffaf ürünler için (jöleler, içecekler, PET kaplar), Kalıntıları ve hava kabarcıklarını görünür kılan arka aydınlatma kullanılır. Meyve ve sebze gibi opak ürünler için dağınık aydınlatma kombinasyonu 45 derecede koaksiyel ışıkla yanık, ezik ve yüzeysel lezyonları iyi ortaya çıkarır. Metalik yabancı cisimlerin tespiti için görüş sistemi desteklenmektedir indüksiyon veya X-ışını metal dedektörü.
Hat Hızı ve Gerçek Zamanlı İşleme
Bir meyve paketleme hattı tipik olarak kanal başına saniyede 8-15 parça hızında çalışır. Bir bisküvi üretim hattı dakikada 200-400 adete ulaşabilmektedir. Görüş sistemi görüntüyü almalı, işlemeli ve kararı iletmelidir. Ürünün mesafeyi kat etmesi için geçen sürede reddetme aktüatörüne Muayene istasyonu ile sınıflandırma istasyonu arasında: tipik olarak 200-500 ms.
Bu zaman kısıtlaması, modeller gibi hesaplama açısından ağır yaklaşımları hariç tutar. optimizasyon olmadan yüksek çözünürlüklü segmentasyon ve ödül mimarileri GPU üzerinde tek bir ileri geçişte algılamayı gerçekleştiren YOLO gibi tek çekimler.
Gıda Denetimi için YOLO evrimi: YOLOv5'ten YOLO11'e
YOLO ailesi neredeyse on yıldır endüstriyel gerçek zamanlı algılamaya hakim olmuştur. Gelişiminin izini sürmek, YOLO11'in neden en uygun seçim olduğunu anlamaya yardımcı olur 2025 yılında yeni bir gıda denetim sistemi için.
YOLOv5 (2020) - Dönüm Noktası
Ultralytics'in YOLOv5'i endüstriyel tespitin erişilebilirliğinde devrim yarattı: ilk yerel modüler PyTorch uygulaması, ONNX'e basitleştirilmiş aktarım, TensorRT ve CoreML, belgelenmiş ve tekrarlanabilir eğitim hattı. O yaptı CV konusunda derin bir uzmanlığa sahip olmayan ekipler için özel olarak erişilebilen tespit Dağıtım kolaylığı sayesinde küresel üretim hatları. Birçok tesis 2021 ile 2023 arasında kurulan sürümler hâlâ YOLOv5'te çalışıyor.
YOLOv8 (2023) - Modern Mimarlık
YOLOv8, ihtiyacı ortadan kaldıran çapasız bir mimariyi tanıttı. Gıda veri kümelerine ilişkin eğitimi basitleştirerek önceden tanımlanmış bağlantı kutularını tanımlayın nesnelerin boyutlarının çok değişken olduğu durumlarda (bir kalıp noktasından) milimetreden bütün bir elmaya kadar). C2f omurgası (2 darboğazlı Çapraz Aşamalı Kısmi) antrenman sırasında gradyan akışını iyileştirir. Ayrı algılama kafası (ayrışmış kafa) sınıflandırma ve regresyon için yakınsamayı iyileştirir. COCO'da mAP@50: 25,9M parametrelerle YOLOv8m için %50,2.
YOLO11 (Eylül 2024) - Son Teknoloji
YOLO11, hesaplama verimliliği açısından en önemli sıçramayı temsil ediyor: YOLO11m modeli mAP@50'ye ulaşır COCO'da %51,5 yalnız 20,1 milyon parametreyani aynı görevler için YOLOv8m'den %22 daha az. C3k2 mimarisine sahip geliştirilmiş omurga, daha zengin özellik çıkarımı sunar. Boyun SPPF (Uzamsal Piramit Havuzlama - Hızlı) ölçekli nesneleri daha iyi tutar farklı ve hem milimetrik kusurları hem de tam nesneleri tespit etmek için çok önemli. Nano modeli ve NVIDIA T4 GPU'larında gecikme 1,5 ms, 600+ FPS'de işlemeye izin verir.
Gıda Denetimi için YOLO ve Alternatif Mimarilerin Karşılaştırılması
| Mimarlık | mAP@50 COCO | Gecikme (ms) | Parametreler | Gıda Kalite Kontrolüne Uygun |
|---|---|---|---|---|
| YOLO11n | %39,5 | 1,5 ms | 2,6 milyon | Mükemmel (uç dağıtım) |
| YOLO11 dk | %51,5 | 4,7 ms | 20,1 milyon | Mükemmel (bütçe) |
| YOLO11x | %54,7 | 11,3 ms | 56,9 milyon | İyi (yüksek doğruluk) |
| YOLOv8m | %50,2 | 5,1 ms | 25,9 milyon | İyi (eski sistemler) |
| Daha hızlı R-CNN | %55,0 | 120-200 ms | 41,8 milyon | Zayıf (çok yavaş) |
| MobileNet SSD'ler | %23,2 | 1,1 ms | 6,8 milyon | Marjinal (düşük acc.) |
| RT-DETR | %53,1 | 8,9 ms | 42.0M | İyi (NMS olmadan) |
Statik kıyaslamalardaki yüksek doğruluğuna rağmen daha hızlı R-CNN ve pratik olarak konveyör bantlarında gerçek zamanlı inceleme için kullanılamaz: 120-200 ms gecikme yani 10 parça/saniyede sistem 12-20 parçadan sadece 1 tanesini görüyor. 4,7 ms'lik YOLO11m, 200 FPS'yi kolayca işleyerek, oyun için bol miktarda boşluk bırakır. PLC ve atık aktüatör ile iletişim boru hattı.
Gıda Kusurlarına İlişkin Veri Kümeleri: Yapım ve Ek Açıklama
Eğitim veri setinin kalitesi performans için en belirleyici faktördür görüş sisteminden. Zayıf bir veri kümesi üzerinde eğitilmiş mükemmel bir YOLO11 modeli vasat sonuçlar verecektir. Tersine, üzerinde eğitim verilen daha basit bir model bile zengin, dengeli ve iyi açıklamalara sahip bir veri kümesi, önemli ölçüde daha iyi sonuçlar üretir.
Gıda Endüstrisindeki Yaygın Kusur Sınıfları
Veri kümesine dahil edilecek sınıflar ürüne ve sürece bağlıdır ancak mevcutlar genel olarak gıda endüstrisinde ortak olan kategoriler:
Meyve ve Sebzelerde Görüntüleme Sistemi İçin Kusur Sınıfları
| Sınıf | Tanım | Tipik Neden | Kritiklik Eşiği |
|---|---|---|---|
mold | Yüzeysel veya gizli küf | Nem, cilt yaraları | Yüksek (zorunlu ret) |
bruise | Darbe göçüğü | Toplama, taşıma | Orta (şiddete bağlıdır) |
burn | Güneş yanığı veya soğuk yanık | Doğrudan UV'ye maruz kalma, donma | Orta-Yüksek |
crack | Çatlak veya çatlak | Hızlı büyüme, kuraklık | Yüksek (patojen vektörü) |
foreign_object | Yabancı cisim (yapraklar, taşlar, plastik) | Mekanik hasat | Eleştiri |
rot | Gelişmiş çürüme | Bakteriler, mantarlar | Yüksek (zorunlu ret) |
insect_damage | Böcek yaralanmaları | Entomolojik saldırılar | Orta-Yüksek |
size_defect | Kalibre spesifikasyon dışı | Çeşit değişkenliği | Düşük (yönlendirme) |
color_defect | Atipik renk (fazla/az olgunlaşmış) | Toplama zamanlaması | Ortalama |
ok | Uyumlu ürün | - | - |
Ek Açıklama Araçları: CVAT, Label Studio ve Roboflow
Endüstriyel veri kümelerine açıklama eklemek için her biri üç ana araç vardır. belirli güçlü yönleriyle:
CVAT (Bilgisayarlı Görme Ek Açıklama Aracı) Intel ve bir araç Veri gizliliği gereksinimleri olan ekipler için ideal, kendi kendine barındırılan sağlam açık kaynak veya hava boşluklu ortamlar. Sınırlayıcı kutu, çokgen, çoklu çizgi ve nokta açıklamalarını destekler ve video izleme. Roboflow ile entegrasyon, önceden eğitilmiş modelleri kullanmanıza olanak tanır Açıklama yardımcıları olarak manuel süreyi %60-70 oranında azaltır.
Etiket Stüdyosu ve çok modlu veri kümeleri (resimler, metin, ses) için daha esnek ve MLOps işlem hatlarıyla kolayca entegre olur. İşbirliğine dayalı açıklama eklemeyi destekler birden fazla inceleme ve fikir birliği oyu, aynı görüntüler üzerinde birden fazla açıklayıcı çalıştığında kullanışlıdır.
Robo akışı en entegre boru hattını sunar: açıklama, ön işleme, Tek bir bulut iş akışında YOLO formatında büyütme ve dışa aktarma. Gıda veri kümeleri için herkese açık, Roboflow Evreni yüzlerce kamuya açık veri kümesini (meyve, bitkiler, yüzey kusurları) transfer öğrenmede başlangıç noktası olarak kullanılabilir.
Veri kümesi boyutlandırma
Tek bir üründe 8-10 sınıf kusur içeren bir tespit sistemi için, Önerilen minimum boyut ve:
- Eğitim setleri: Sınıf başına minimum 500 resim, ideal olarak 1000+
- Doğrulama seti: Sınıfa göre sınıflandırılmış eğitimin %15-20'si
- Test setleri: %10-15, tamamen ayrılmış, gerçek hat koşullarında elde edilmiş
- Nadir sınıflar: gibi sınıflar için
foreign_objectüretimde nadiren ihtiyaç duyulan aşırı örnekleme ve agresif artırma kullanın
Gıdaya Özel Veri Arttırma
Gıda ürünlerine yönelik büyütme, gerçek varyasyonları simüle etmelidir. sistem üretimde buluşacak. Standart teknikler (yatay çevirme, döndürme, mahsul) gıdaya özel takviyeyle entegre edilmelidir:
# augmentation_food.py
# Configurazione augmentation specifica per food quality inspection
import albumentations as A
import cv2
import numpy as np
def build_food_augmentation_pipeline(image_size: int = 640) -> A.Compose:
"""
Pipeline di augmentation per dataset di difetti alimentari.
Simula variazioni reali di illuminazione, orientamento e condizioni di linea.
"""
return A.Compose([
# Geometria: prodotti alimentari arrivano in orientamenti casuali
A.RandomRotate90(p=0.5),
A.HorizontalFlip(p=0.5),
A.VerticalFlip(p=0.3),
A.ShiftScaleRotate(
shift_limit=0.1,
scale_limit=0.2,
rotate_limit=45,
border_mode=cv2.BORDER_REFLECT,
p=0.7
),
# Illuminazione: variazioni reali in linea (vapore, sporco sulle lenti)
A.OneOf([
A.RandomBrightnessContrast(
brightness_limit=0.3,
contrast_limit=0.3,
p=1.0
),
A.RandomGamma(gamma_limit=(70, 130), p=1.0),
A.CLAHE(clip_limit=4.0, tile_grid_size=(8, 8), p=1.0),
], p=0.8),
# Colore: variazioni stagionali e di maturazione
A.HueSaturationValue(
hue_shift_limit=15, # Piccolo: non cambiare il colore del prodotto
sat_shift_limit=30,
val_shift_limit=20,
p=0.5
),
# Rumore: sensore camera industriale, interferenze EMI
A.OneOf([
A.GaussNoise(var_limit=(10.0, 50.0), p=1.0),
A.ISONoise(color_shift=(0.01, 0.05), intensity=(0.1, 0.5), p=1.0),
A.MultiplicativeNoise(multiplier=(0.9, 1.1), p=1.0),
], p=0.4),
# Blur: motion blur da velocità nastro, defocus per DOF limitata
A.OneOf([
A.MotionBlur(blur_limit=7, p=1.0), # Più realistico per nastro
A.GaussianBlur(blur_limit=(3, 7), p=1.0),
], p=0.3),
# Riflessioni: superfici cerate, glazze, film di acqua
A.RandomSunFlare(
flare_roi=(0.0, 0.0, 1.0, 0.5),
num_flare_circles_lower=1,
num_flare_circles_upper=3,
src_radius=100,
p=0.1
),
# Ritaglio e padding finale
A.PadIfNeeded(
min_height=image_size,
min_width=image_size,
border_mode=cv2.BORDER_REFLECT
),
A.Resize(image_size, image_size),
# Normalizzazione finale
A.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
),
], bbox_params=A.BboxParams(
format='yolo',
label_fields=['class_labels'],
min_area=100, # Ignora bbox troppo piccole dopo crop
min_visibility=0.3 # Ignora bbox con meno del 30% visibile
))
def augment_rare_class(
image: np.ndarray,
bboxes: list,
class_labels: list,
n_augmentations: int = 10
) -> list:
"""
Over-sampling per classi rare come foreign_object.
Genera n varianti augmentate di un singolo campione.
"""
pipeline = build_food_augmentation_pipeline()
augmented_samples = []
for _ in range(n_augmentations):
result = pipeline(
image=image,
bboxes=bboxes,
class_labels=class_labels
)
augmented_samples.append({
'image': result['image'],
'bboxes': result['bboxes'],
'class_labels': result['class_labels']
})
return augmented_samples
PyTorch ve YOLO11 ile Eğitim Hattını Tamamlayın
Gıda kalite kontrolü için özelleştirilmiş bir YOLO11 modelinin eğitimi yapılandırılmış bir boru hattını takip eder: ortamın hazırlanması, veri kümesinin hazırlanması, Dağıtım için transfer öğrenimi, doğrulama ve optimizasyon içeren eğitim.
Eğitim Ortamı Kurulumu
# Requisiti: Python 3.11+, CUDA 12.1+, NVIDIA GPU con 8GB+ VRAM
# Consigliato: RTX 3080/4080 per training locale, A100 per training veloce
# 1. Installazione dipendenze
# pip install ultralytics==8.3.0 albumentations roboflow torch torchvision
# 2. Struttura directory dataset (formato YOLO)
# dataset/
# ├── images/
# │ ├── train/ (70% campioni)
# │ ├── val/ (20% campioni)
# │ └── test/ (10% campioni)
# ├── labels/
# │ ├── train/ (file .txt corrispondenti)
# │ ├── val/
# │ └── test/
# └── data.yaml (configurazione dataset)
# data.yaml - Configurazione dataset
DATA_YAML_CONTENT = """
path: /data/food_quality_dataset
train: images/train
val: images/val
test: images/test
nc: 10 # Numero di classi
names:
0: ok
1: mold
2: bruise
3: burn
4: crack
5: foreign_object
6: rot
7: insect_damage
8: size_defect
9: color_defect
"""
import yaml
with open('/data/food_quality_dataset/data.yaml', 'w') as f:
f.write(DATA_YAML_CONTENT)
Roboflow ile Veri Seti İndirme ve Hazırlama
# dataset_preparation.py
# Scarica dataset da Roboflow Universe o usa dataset locale
from roboflow import Roboflow
import os
def download_food_dataset(api_key: str, workspace: str, project: str, version: int) -> str:
"""
Scarica dataset da Roboflow e prepara per training YOLO11.
"""
rf = Roboflow(api_key=api_key)
proj = rf.workspace(workspace).project(project)
dataset = proj.version(version).download("yolov8") # Formato YOLO11 compatibile
dataset_path = dataset.location
print(f"Dataset scaricato in: {dataset_path}")
print(f"Training images: {len(os.listdir(os.path.join(dataset_path, 'train', 'images')))}")
print(f"Validation images: {len(os.listdir(os.path.join(dataset_path, 'valid', 'images')))}")
return dataset_path
def analyze_class_distribution(dataset_path: str) -> dict:
"""
Analizza la distribuzione delle classi nel dataset.
Identifica classi sbilanciate che richiedono over-sampling.
"""
import glob
from collections import Counter
class_counts = Counter()
label_files = glob.glob(os.path.join(dataset_path, 'train', 'labels', '*.txt'))
for label_file in label_files:
with open(label_file, 'r') as f:
for line in f:
class_id = int(line.split()[0])
class_counts[class_id] += 1
total = sum(class_counts.values())
distribution = {
class_id: {
'count': count,
'percentage': round(count / total * 100, 2),
'needs_oversampling': count < total / len(class_counts) * 0.3 # < 30% della media
}
for class_id, count in sorted(class_counts.items())
}
return distribution
# Analisi distribuzione per identificare classi rare
if __name__ == "__main__":
dataset_path = "/data/food_quality_dataset"
distribution = analyze_class_distribution(dataset_path)
print("\nDistribuzione classi nel dataset:")
class_names = ['ok', 'mold', 'bruise', 'burn', 'crack',
'foreign_object', 'rot', 'insect_damage', 'size_defect', 'color_defect']
for class_id, info in distribution.items():
name = class_names[class_id] if class_id < len(class_names) else f"class_{class_id}"
warning = " *** RICHIEDE OVERSAMPLING ***" if info['needs_oversampling'] else ""
print(f" {name}: {info['count']} campioni ({info['percentage']}%){warning}")
Transfer Öğrenme ile YOLO11 Eğitimi
# train_yolo11_food.py
# Training pipeline completa per food quality inspection
from ultralytics import YOLO
import torch
import yaml
import os
from pathlib import Path
def train_food_quality_model(
dataset_yaml: str,
output_dir: str = "./runs/food_quality",
epochs: int = 150,
batch_size: int = 16,
image_size: int = 640,
model_variant: str = "yolo11m.pt" # n/s/m/l/x
) -> dict:
"""
Training YOLO11 per food quality inspection con ottimizzazioni specifiche.
Args:
dataset_yaml: Path al file data.yaml del dataset
output_dir: Directory per salvare i risultati
epochs: Numero di epoche (150 e un buon punto di partenza)
batch_size: Dimensione batch (16 per 8GB VRAM, 32 per 16GB+)
image_size: Dimensione immagine (640 standard, 1280 per difetti piccoli)
model_variant: Variante YOLO11 da usare come punto di partenza
Returns:
dict con metriche finali del training
"""
# Verifica GPU disponibile
device = 'cuda' if torch.cuda.is_available() else 'cpu'
if device == 'cpu':
print("ATTENZIONE: Training su CPU, sarà molto lento. Usa una GPU.")
print(f"Device: {device}")
if device == 'cuda':
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"VRAM: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB")
# Carica modello pre-addestrato su COCO (transfer learning)
model = YOLO(model_variant)
# Configurazione training
training_args = {
'data': dataset_yaml,
'epochs': epochs,
'batch': batch_size,
'imgsz': image_size,
'device': device,
'project': output_dir,
'name': 'food_quality_v1',
# Ottimizzatore: AdamW e ottimo per fine-tuning
'optimizer': 'AdamW',
'lr0': 0.001, # Learning rate iniziale
'lrf': 0.01, # Learning rate finale = lr0 * lrf
'momentum': 0.937,
'weight_decay': 0.0005,
# Learning rate scheduling: cosine annealing
'cos_lr': True,
'warmup_epochs': 5, # 5 epoche di warmup per stabilizzare
# Augmentation integrata (albumentations)
'hsv_h': 0.015, # Hue shift (piccolo per preservare colore prodotto)
'hsv_s': 0.7, # Saturation
'hsv_v': 0.4, # Value (luminosita)
'degrees': 30.0, # Rotazione
'translate': 0.1, # Traslazione
'scale': 0.5, # Scaling
'shear': 5.0,
'perspective': 0.0001,
'flipud': 0.3, # Flip verticale (utile per frutta)
'fliplr': 0.5, # Flip orizzontale
'mosaic': 1.0, # Mosaic augmentation (4 immagini)
'mixup': 0.1, # MixUp augmentation
'copy_paste': 0.1, # Copy-paste per classi rare
# Loss weights: aumenta peso classificazione per difetti rari
'cls': 1.5, # Classification loss weight (default 0.5)
'box': 7.5, # Box regression loss weight
'dfl': 1.5, # Distribution Focal Loss weight
# Valutazione e salvataggio
'val': True,
'save': True,
'save_period': 10, # Salva checkpoint ogni 10 epoche
'patience': 30, # Early stopping se no miglioramento per 30 epoche
# Performances
'workers': 8, # DataLoader workers
'cache': True, # Cache dataset in RAM per velocità
'amp': True, # Automatic Mixed Precision (FP16)
# Metriche
'plots': True, # Genera grafici di training
'verbose': True,
}
# Avvia training
print(f"\nAvvio training YOLO11 per food quality inspection")
print(f"Epoche: {epochs}, Batch: {batch_size}, Immagini: {image_size}x{image_size}")
results = model.train(**training_args)
# Estrai metriche finali
metrics = {
'mAP50': float(results.results_dict.get('metrics/mAP50(B)', 0)),
'mAP50_95': float(results.results_dict.get('metrics/mAP50-95(B)', 0)),
'precision': float(results.results_dict.get('metrics/precision(B)', 0)),
'recall': float(results.results_dict.get('metrics/recall(B)', 0)),
'best_model_path': str(Path(output_dir) / 'food_quality_v1' / 'weights' / 'best.pt')
}
print(f"\nTraining completato!")
print(f"mAP@50: {metrics['mAP50']:.4f}")
print(f"mAP@50-95: {metrics['mAP50_95']:.4f}")
print(f"Precision: {metrics['precision']:.4f}")
print(f"Recall: {metrics['recall']:.4f}")
print(f"Modello salvato in: {metrics['best_model_path']}")
return metrics
if __name__ == "__main__":
metrics = train_food_quality_model(
dataset_yaml="/data/food_quality_dataset/data.yaml",
output_dir="./runs/food_quality",
epochs=150,
batch_size=16,
image_size=640,
model_variant="yolo11m.pt"
)
Ray Tune ile Hiperparametre Ayarlama
# hyperparameter_tuning.py
# Ricerca automatica degli hyperparametri ottimali con Ray Tune
from ultralytics import YOLO
from ray import tune
from ray.tune.schedulers import ASHAScheduler
import torch
def objective(config: dict) -> dict:
"""Funzione obiettivo per Ray Tune."""
model = YOLO("yolo11m.pt")
results = model.train(
data="/data/food_quality_dataset/data.yaml",
epochs=50, # Epoche ridotte per tuning rapido
batch=config['batch'],
lr0=config['lr0'],
lrf=config['lrf'],
momentum=config['momentum'],
weight_decay=config['weight_decay'],
cls=config['cls'],
hsv_s=config['hsv_s'],
mixup=config['mixup'],
amp=True,
verbose=False,
plots=False,
)
mAP50 = results.results_dict.get('metrics/mAP50(B)', 0)
return {"mAP50": mAP50}
def run_hyperparameter_search(n_trials: int = 20) -> dict:
"""Esegue ricerca hyperparametri con ASHA scheduler."""
search_space = {
'batch': tune.choice([8, 16, 32]),
'lr0': tune.loguniform(1e-4, 1e-2),
'lrf': tune.uniform(0.001, 0.1),
'momentum': tune.uniform(0.85, 0.98),
'weight_decay': tune.loguniform(1e-5, 1e-3),
'cls': tune.uniform(0.5, 2.0),
'hsv_s': tune.uniform(0.4, 0.9),
'mixup': tune.uniform(0.0, 0.3),
}
scheduler = ASHAScheduler(
max_t=50,
grace_period=10,
reduction_factor=2
)
analysis = tune.run(
objective,
config=search_space,
num_samples=n_trials,
scheduler=scheduler,
metric="mAP50",
mode="max",
resources_per_trial={"cpu": 4, "gpu": 1},
)
best_config = analysis.best_config
print(f"Migliori hyperparametri trovati:")
for key, value in best_config.items():
print(f" {key}: {value}")
return best_config
Gıda Kalite Kontrolünde Tespit, Sınıflandırma ve Segmentasyon
YOLO11 üç ana paradigmayı destekler: nesne algılama (sınırlayıcı kutu), sınıflandırma (tüm görüntü sınıfı) ve örnek bölümleme (piksel düzeyinde maske). Seçim, kusurun türüne, gereken hıza ve mevcut donanıma bağlıdır.
Hangi Yaklaşım Ne Zaman Kullanılmalı
| Yaklaşmak | Çıkışlar | Tipik Gecikme | Kullanım Örneği Gıda Kalite Kontrolü | Artıları/Eksileri |
|---|---|---|---|---|
| sınıflandırma | Sınıf + güven | 0,5-1,5ms | Kategori/kaliteye göre meyve sınıflandırması; olgunlaşma | Artıları: çok hızlı. Eksileri: kusurlu yerelleştirme yok |
| Nesne Algılama | Bbox + sınıf + paket | 1,5-5 ms | Aynı parçada birden fazla kusur tespiti; yabancı cisimler | Artıları: optimum hız/bilgi dengesi. Eksileri: kesin bir şekil yok |
| Segmentasyon | Piksel maskesi + sınıfı | 3-15 ms | Kusur alanını ölçün; hassas boyut kontrolü; sınıflandırma | Artıları: ayrıntılı bilgi. Eksileri: daha yavaş ve daha karmaşık |
| Poz Tahmini | Önemli Noktalar | 2-6 ms | Al ve yerleştir robotları için parça oryantasyonu; saymak | Artıları: Hassas yönlendirme. Eksileri: Anahtar nokta veri kümesi gerektirir |
Çoğu gıda kalite kontrol sistemi için,nesne algılama YOLO11 ile bu en uygun seçimdir: aynı üründeki birden fazla kusuru tespit eder ve bulur, sınırlayıcı kutunun boyutuna göre ciddiyeti belirlemenize olanak tanır, ve yüksek hızlı hatlarla uyumlu gecikme sürelerini korur. Segmentasyon haklı Tesviye için kusur alanının hassas ölçümüne ihtiyaç duyduğunuzda (örneğin yanıkları "hafif" < %5 yüzey, "şiddetli" > %15 yüzey olarak sınıflandırın).
Kalite Metrikleri: Gıda Endüstrisine Yönelik Yorumlama
Standart CV metriklerinin (mAP, hassasiyet, geri çağırma) belirli sonuçları vardır gıda bağlamında anlaşılması ve sorumlulara açıkça iletilmesi gereken bir konu canlı yayına geçmeden önce üretimin.
mAP (ortalama Ortalama Hassasiyet)
mAP, tüm sınıflar için kesinlik geri çağırma eğrisini özetler. mAP@50 şunu kullanır: Bir tespitin dikkate alınması için 0,5'lik IoU (Birleşim Üzerinden Kesişme) eşiği doğru. mAP@50-95'in eşik değeri 0,5 ile 0,95 arasındadır ve daha şiddetlidir. Gıda kalite kontrolü için mAP@50 genellikle birincil ölçümdür çünkü doğruluk kesin yerelleştirme (IoU 0,95) ve doğruluğundan daha az kritik kusur sınıflandırması.
Hassasiyet ve Geri Çağırma: Kritik Denge
Gıda kalite kontrolünde hassasiyet ve geri çağırmanın temel maliyet asimetrileri vardır:
- Düşük Hatırlama (yanlış negatifler): aşan kusurlu bir ürün denetleyerek tüketiciye ulaşır. Maliyet: ürünün geri çağrılması, hasar marka itibarı, sağlığa olası zarar. Kabul edilemez son derece kritik kusurlar için (küf, yabancı cisimler).
- Düşük Hassasiyet (yanlış pozitifler): uyumlu bir ürün geliyor atıldı. Maliyet: ürün kaybı, hat performansında azalma. Sınırlar dahilinde kabul edilebilir ürünün değerine bağlıdır.
Güven eşiği kalibrasyonu, bunu yönetmenin ana mekanizmasıdır
bu ödünleşim: eşiğin düşürülmesi hatırlamayı artırır (daha az yanlış negatif) a
kesinliğin zararına (daha fazla yanlış pozitif). Yabancı cisimler gibi kritik kusurlar için,
Daha fazla hatalı pozitifliği kabul edecek şekilde çok düşük bir eşik (0,3-0,4) belirlenmiştir.
Gibi kusurlar için size_defecteşik daha yüksek olabilir (0,6-0,7).
# evaluate_model.py
# Valutazione completa del modello con metriche per food QC
from ultralytics import YOLO
import numpy as np
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
from pathlib import Path
def evaluate_food_model(
model_path: str,
test_dataset_yaml: str,
class_names: list,
confidence_thresholds: dict # Soglie per classe
) -> dict:
"""
Valutazione completa con metriche specifiche per food quality inspection.
Args:
model_path: Path al modello addestrato (best.pt)
test_dataset_yaml: Path al data.yaml del test set
class_names: Nomi delle classi
confidence_thresholds: Soglie confidence per classe {"mold": 0.35, "ok": 0.6}
"""
model = YOLO(model_path)
# Validazione standard YOLO
results = model.val(
data=test_dataset_yaml,
split='test',
imgsz=640,
conf=0.001, # Basso per calcolare la curva completa
iou=0.6,
plots=True,
verbose=False,
)
# Metriche per classe
class_metrics = {}
for i, name in enumerate(class_names):
class_metrics[name] = {
'precision': float(results.box.p[i]) if i < len(results.box.p) else 0,
'recall': float(results.box.r[i]) if i < len(results.box.r) else 0,
'mAP50': float(results.box.ap50[i]) if i < len(results.box.ap50) else 0,
'mAP50_95': float(results.box.ap[i]) if i < len(results.box.ap) else 0,
}
# Soglie di accettabilita per l'industria alimentare
acceptance_thresholds = {
'mold': {'recall_min': 0.98, 'precision_min': 0.85},
'foreign_object': {'recall_min': 0.995, 'precision_min': 0.80},
'rot': {'recall_min': 0.97, 'precision_min': 0.87},
'bruise': {'recall_min': 0.90, 'precision_min': 0.85},
'burn': {'recall_min': 0.88, 'precision_min': 0.83},
'crack': {'recall_min': 0.93, 'precision_min': 0.85},
'insect_damage': {'recall_min': 0.92, 'precision_min': 0.84},
'size_defect': {'recall_min': 0.85, 'precision_min': 0.88},
'color_defect': {'recall_min': 0.85, 'precision_min': 0.88},
'ok': {'recall_min': 0.95, 'precision_min': 0.92},
}
# Verifica accettabilita
go_nogo_results = {}
for class_name, metrics in class_metrics.items():
thresholds = acceptance_thresholds.get(class_name, {})
recall_ok = metrics['recall'] >= thresholds.get('recall_min', 0.0)
precision_ok = metrics['precision'] >= thresholds.get('precision_min', 0.0)
go_nogo_results[class_name] = {
'go': recall_ok and precision_ok,
'recall_ok': recall_ok,
'precision_ok': precision_ok,
'recall': metrics['recall'],
'precision': metrics['precision'],
}
# Report finale
print("\n=== VALUTAZIONE FOOD QUALITY MODEL ===\n")
print(f"mAP@50 globale: {float(results.box.map50):.4f}")
print(f"mAP@50-95 globale: {float(results.box.map):.4f}")
print("\nRisultati per classe:")
print(f"{'Classe':-20} {'Recall':>8} {'Precision':>10} {'mAP50':>8} {'GO/NO-GO':>10}")
print("-" * 60)
for class_name, gng in go_nogo_results.items():
status = "GO ✓" if gng['go'] else "NO-GO ✗"
print(f"{class_name:-20} {gng['recall']:8.4f} {gng['precision']:10.4f} "
f"{class_metrics[class_name]['mAP50']:8.4f} {status:>10}")
overall_go = all(gng['go'] for gng in go_nogo_results.values())
print(f"\nVALUTAZIONE FINALE: {'SISTEMA APPROVATO PER DEPLOY' if overall_go else 'NON APPROVATO - RICHIEDE MIGLIORAMENTI'}")
return {
'class_metrics': class_metrics,
'go_nogo': go_nogo_results,
'overall_go': overall_go,
'global_mAP50': float(results.box.map50),
}
# Esecuzione valutazione
if __name__ == "__main__":
class_names = ['ok', 'mold', 'bruise', 'burn', 'crack',
'foreign_object', 'rot', 'insect_damage', 'size_defect', 'color_defect']
confidence_thresholds = {
'mold': 0.35,
'foreign_object': 0.30,
'rot': 0.40,
'bruise': 0.50,
'burn': 0.50,
'crack': 0.45,
'insect_damage': 0.45,
'size_defect': 0.60,
'color_defect': 0.60,
'ok': 0.60,
}
results = evaluate_food_model(
model_path="./runs/food_quality/food_quality_v1/weights/best.pt",
test_dataset_yaml="/data/food_quality_dataset/data.yaml",
class_names=class_names,
confidence_thresholds=confidence_thresholds
)
Endüstriyel Muayene Hattı için Donanım Mimarisi
Gıda kalite kontrolüne yönelik endüstriyel görüntüleme sistemi yalnızca yazılımdan ibaret değildir: Donanım model kadar temeldir. Optik zincir (lens, sensör, aydınlatma) görüntü kalitesini ve görüntünün kalitesini belirler. düşük kalite, en iyi yapay zeka modeliyle son işlemlerde kurtarılamaz.
Muayene Hattı Bileşenleri
Eksiksiz bir denetim hattı dört farklı bölgeyi içerir: Satın Alma görüntü, yapay zeka işleme, karar verme ve sıralama. Bu alanlar arasındaki entegrasyon donanım sinyalleri (kodlayıcı, tetikleyici, PLC) ve iletişim aracılığıyla gerçekleşir endüstriyel (Ethernet/IP, PROFINET, OPC-UA).
Gıda Kalitesi Kontrolü için Donanım Karşılaştırması
| Bileşen | Giriş Seviyesi | Profesyonel | Yüksek Performans |
|---|---|---|---|
| Oda | USB3 Vision 5MP (Basler ace) | GigE Vision 12MP (Basler ace2) | CoaXPress 25MP (Müttefik Vizyon Altın Göz) |
| Kare Hızı | 30-60 FPS | 100-200 FPS | 300-500FPS |
| Koruma | IP40 (ofis) | IP67 (sıçrayan su) | IP69K (yüksek basınçlı jet) |
| Aydınlatma | Genel LED halkası | Denetleyicili LED çubuk | Programlanabilir LED + IR flaş |
| GPU Çıkarımı | NVIDIA Jetson Orin NX (16GB) | Hailo-8 + Endüstriyel CPU | Endüstriyel PC'de RTX 4080 |
| Çıkarım gecikmesi | 8-15 ms (Jetson) | 2-5 ms (Selam-8) | 1-3 ms (RTX 4080) |
| Sistem maliyeti | 5.000-15.000 Avro | 20.000-50.000 Avro | 60.000-150.000 Avro |
| Maksimum verim | 3-5 adet/sn | 10-20 adet/sn | 30-60 adet/sn |
GigE Vizyonu: Endüstriyel İletişim Standardı
GigE Vision (GenICam), kameradan kameraya iletişim için endüstri standardıdır ve Gigabit Ethernet üzerinden işleme sistemi. USB3'e göre avantajları: genişletici olmadan 100 metreye kadar kablo uzunluğu, PoE (Ethernet Üzerinden Güç) desteği, Hassas Zaman Protokolü (PTP) aracılığıyla deterministik gecikme, çoklu kamera açık tek anahtar. Yapılandırma, jumbo çerçevelere sahip özel bir NIC gerektirir etkin (MTU 9000) ve satın alma işlemi için CPU benzeşimi.
Tetikleme ve Senkronizasyon Sistemi
Taşıma bandı ile görüntü alma ve eleştiri arasındaki senkronizasyon Hareket bulanıklığını ve orta ürün görüntülerini önlemek için. Tipik olarak A kullanılır Her N mm'lik ilerlemede bir darbe üreten, kayışa bağlı döner kodlayıcı. PLC (Programlanabilir Lojik Denetleyici) darbeyi alır ve sinyali üretir GPIO aracılığıyla kamera için donanım tetikleyicisi. Donanım tetikleyicisi önemlidir: yazılım tetikleyicisi, hızlı bantlarda 1-5 ms'lik bir titreşime neden olur kabul edilemez sapmalara neden olur.
# camera_gige_acquisition.py
# Acquisizione immagini da camera GigE Vision con trigger hardware
import pypylon.pylon as pylon
import numpy as np
import cv2
import threading
import queue
import time
from dataclasses import dataclass
from typing import Optional
@dataclass
class AcquiredFrame:
"""Frame acquisito dalla camera con metadati."""
image: np.ndarray
timestamp_ns: int
frame_id: int
trigger_counter: int
class GigEVisionCamera:
"""
Wrapper per camera GigE Vision via Basler pylibpylon.
Gestisce trigger hardware, acquisizione e buffer.
"""
def __init__(
self,
device_index: int = 0,
trigger_mode: str = "Line1", # Trigger hardware su Line1
exposure_us: int = 500, # 500 microsec: congela il moto a 2m/s
gain_db: float = 6.0,
buffer_count: int = 10,
) -> None:
self._device_index = device_index
self._trigger_mode = trigger_mode
self._exposure_us = exposure_us
self._gain_db = gain_db
self._buffer_count = buffer_count
self._camera: Optional[pylon.InstantCamera] = None
self._frame_queue: queue.Queue = queue.Queue(maxsize=100)
self._acquiring = False
self._frame_counter = 0
def connect(self) -> None:
"""Connette e configura la camera GigE Vision."""
transport_factory = pylon.TlFactory.GetInstance()
devices = transport_factory.EnumerateDevices()
if len(devices) == 0:
raise RuntimeError("Nessuna camera GigE Vision trovata")
if self._device_index >= len(devices):
raise RuntimeError(f"Camera index {self._device_index} non disponibile")
self._camera = pylon.InstantCamera(
transport_factory.CreateDevice(devices[self._device_index])
)
self._camera.Open()
# Configurazione trigger hardware
self._camera.TriggerMode.SetValue("On")
self._camera.TriggerSource.SetValue(self._trigger_mode)
self._camera.TriggerActivation.SetValue("RisingEdge")
# Configurazione esposizione
self._camera.ExposureTime.SetValue(self._exposure_us)
self._camera.Gain.SetValue(self._gain_db)
# Pixel format: Mono8 per velocità massima, BayerRG8 per colore
self._camera.PixelFormat.SetValue("BayerRG8")
# Buffer pool
self._camera.MaxNumBuffer.SetValue(self._buffer_count)
print(f"Camera connessa: {self._camera.DeviceModelName.GetValue()}")
print(f"Risoluzione: {self._camera.Width.GetValue()}x{self._camera.Height.GetValue()}")
print(f"Frame rate max: {self._camera.AcquisitionFrameRate.GetValue():.1f} FPS")
def start_acquisition(self) -> None:
"""Avvia acquisizione continua in thread separato."""
if self._camera is None:
raise RuntimeError("Camera non connessa")
self._acquiring = True
self._camera.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
acquisition_thread = threading.Thread(
target=self._acquisition_loop,
daemon=True
)
acquisition_thread.start()
print("Acquisizione avviata in modalità trigger hardware")
def _acquisition_loop(self) -> None:
"""Loop di acquisizione continua."""
while self._acquiring and self._camera.IsGrabbing():
try:
grab_result = self._camera.RetrieveResult(
5000, # Timeout 5 secondi
pylon.TimeoutHandling_ThrowException
)
if grab_result.GrabSucceeded():
# Conversione immagine
converter = pylon.ImageFormatConverter()
converter.OutputPixelFormat = pylon.PixelType_BGR8packed
converted = converter.Convert(grab_result)
image = converted.GetArray()
frame = AcquiredFrame(
image=image.copy(),
timestamp_ns=grab_result.TimeStamp,
frame_id=grab_result.ImageNumber,
trigger_counter=self._frame_counter
)
# Non-blocking: scarta se coda piena (priorità ai frame più recenti)
try:
self._frame_queue.put_nowait(frame)
except queue.Full:
# Scarta frame vecchio, inserisci nuovo
try:
self._frame_queue.get_nowait()
except queue.Empty:
pass
self._frame_queue.put_nowait(frame)
self._frame_counter += 1
grab_result.Release()
except pylon.TimeoutException:
pass # Nessun trigger ricevuto nel timeout, normale
except Exception as e:
print(f"Errore acquisizione: {e}")
def get_frame(self, timeout: float = 1.0) -> Optional[AcquiredFrame]:
"""Recupera il prossimo frame dalla coda."""
try:
return self._frame_queue.get(timeout=timeout)
except queue.Empty:
return None
def stop(self) -> None:
"""Ferma acquisizione e disconnette la camera."""
self._acquiring = False
if self._camera and self._camera.IsGrabbing():
self._camera.StopGrabbing()
if self._camera:
self._camera.Close()
print("Camera disconnessa")
Otomatik Sıralama Sistemi: PLC ve Aktüatörlerle Entegrasyon
Görüş sisteminin çıktısı fiziksel bir eyleme dönüşmelidir: sınır dışı etme Arızalı ürünün hattan alınması. Bu, pnömatik aktüatörler aracılığıyla gerçekleşir (basınçlı hava nozulları) veya al ve yerleştir robotları, PLC tarafından kontrol edilir. AI sisteminin kararının temeli.
Sıralama Sisteminin Zamanlaması
Zamanlama kritiktir: sistem, olayın gerçekleştiği andan itibaren hesaplama yapmalıdır. tetikleyici (görüntü alma), ürünün tam olarak ulaşacağı zaman fırlatma istasyonuna gidin ve aktüatörü milisaniyelik hassasiyetle kontrol edin. Zaman zinciri şunları içerir: edinme süresi, yapay zeka çıkarım süresi, PLC ile iletişim süresi, pnömatik aktüatörün tepki süresi (tipik olarak valf açma gecikmesi dahil 30-80 ms).
# sorting_system.py
# Sistema di sorting integrato con PLC via OPC-UA
import asyncio
import asyncua
from ultralytics import YOLO
import numpy as np
import time
from dataclasses import dataclass
from typing import Optional
from enum import IntEnum
class SortingDecision(IntEnum):
"""Decisioni di sorting basate sulla criticita del difetto."""
ACCEPT = 0 # Prodotto conforme: procede
REJECT_DEFECT = 1 # Difetto standard: canale scarto generico
REJECT_CRITICAL = 2 # Difetto critico (corpo estraneo, muffa): canale separato
REINSPECT = 3 # Bassa confidence: re-ispezione manuale
@dataclass
class InspectionResult:
"""Risultato ispezione con decisione di sorting."""
frame_id: int
timestamp_ms: float
decision: SortingDecision
primary_defect: Optional[str]
confidence: float
defect_count: int
inference_time_ms: float
class FoodInspectionEngine:
"""
Engine principale di ispezione: integra vision AI e logica di sorting.
"""
# Classificazione criticita difetti
CRITICAL_DEFECTS = {'foreign_object', 'mold', 'rot'}
STANDARD_DEFECTS = {'bruise', 'burn', 'crack', 'insect_damage'}
QUALITY_DEFECTS = {'size_defect', 'color_defect'}
# Soglie confidence per classe
CLASS_THRESHOLDS = {
'foreign_object': 0.30,
'mold': 0.35,
'rot': 0.38,
'crack': 0.45,
'bruise': 0.50,
'insect_damage': 0.48,
'burn': 0.50,
'size_defect': 0.60,
'color_defect': 0.60,
'ok': 0.60,
}
def __init__(self, model_path: str) -> None:
self._model = YOLO(model_path)
self._class_names = self._model.names
self._inspection_count = 0
self._defect_count = 0
self._reject_count = 0
def inspect(self, image: np.ndarray, frame_id: int) -> InspectionResult:
"""
Esegue l'ispezione AI su un frame e restituisce la decisione di sorting.
"""
start_time = time.perf_counter()
# Inference YOLO11
results = self._model(
image,
conf=0.25, # Soglia bassa: filtraggio per classe dopo
iou=0.45,
verbose=False,
half=True, # FP16 per velocità
)
inference_time_ms = (time.perf_counter() - start_time) * 1000
# Analisi detections
detections = []
for result in results:
if result.boxes is None:
continue
for box in result.boxes:
class_id = int(box.cls[0])
class_name = self._class_names[class_id]
confidence = float(box.conf[0])
# Applica soglia per classe
class_threshold = self.CLASS_THRESHOLDS.get(class_name, 0.5)
if confidence >= class_threshold and class_name != 'ok':
detections.append({
'class': class_name,
'confidence': confidence,
'bbox': box.xyxy[0].tolist(),
})
# Logica di decisione sorting
decision, primary_defect, max_confidence = self._make_sorting_decision(detections)
self._inspection_count += 1
if decision != SortingDecision.ACCEPT:
self._reject_count += 1
return InspectionResult(
frame_id=frame_id,
timestamp_ms=time.time() * 1000,
decision=decision,
primary_defect=primary_defect,
confidence=max_confidence,
defect_count=len(detections),
inference_time_ms=inference_time_ms,
)
def _make_sorting_decision(
self,
detections: list
) -> tuple[SortingDecision, Optional[str], float]:
"""
Logica di decisione sorting basata sui difetti rilevati.
Priorità: REJECT_CRITICAL > REJECT_DEFECT > REINSPECT > ACCEPT
"""
if not detections:
return SortingDecision.ACCEPT, None, 0.0
# Verifica presenza difetti critici (priorità assoluta)
critical_detections = [
d for d in detections
if d['class'] in self.CRITICAL_DEFECTS
]
if critical_detections:
primary = max(critical_detections, key=lambda x: x['confidence'])
return SortingDecision.REJECT_CRITICAL, primary['class'], primary['confidence']
# Verifica difetti standard
standard_detections = [
d for d in detections
if d['class'] in self.STANDARD_DEFECTS
]
if standard_detections:
primary = max(standard_detections, key=lambda x: x['confidence'])
# Se confidence bassa (0.45-0.55) su difetto standard: reinspect
if primary['confidence'] < 0.55:
return SortingDecision.REINSPECT, primary['class'], primary['confidence']
return SortingDecision.REJECT_DEFECT, primary['class'], primary['confidence']
# Solo difetti qualità
quality_detections = [
d for d in detections
if d['class'] in self.QUALITY_DEFECTS
]
if quality_detections:
primary = max(quality_detections, key=lambda x: x['confidence'])
return SortingDecision.REJECT_DEFECT, primary['class'], primary['confidence']
return SortingDecision.ACCEPT, None, 0.0
def get_statistics(self) -> dict:
"""Statistiche di ispezione in tempo reale."""
reject_rate = self._reject_count / max(self._inspection_count, 1) * 100
return {
'total_inspected': self._inspection_count,
'total_rejected': self._reject_count,
'reject_rate_pct': round(reject_rate, 2),
'throughput': self._inspection_count, # Da normalizzare nel tempo
}
class PLCInterface:
"""
Interfaccia OPC-UA verso PLC Siemens/Beckhoff per controllo attuatori.
"""
# Indirizzi OPC-UA nodi PLC (configurati nel TIA Portal o TwinCAT)
SORT_COMMAND_NODE = "ns=2;s=FoodLine.Sort.Command"
SORT_DELAY_MS_NODE = "ns=2;s=FoodLine.Sort.DelayMs"
CONVEYOR_SPEED_NODE = "ns=2;s=FoodLine.Conveyor.SpeedMps"
INSPECTION_TO_EJECTOR_MM = 450.0 # Distanza fisica stazione ispezione -> espulsore
def __init__(self, plc_url: str = "opc.tcp://192.168.1.100:4840") -> None:
self._plc_url = plc_url
self._client: Optional[asyncua.Client] = None
async def connect(self) -> None:
"""Connette al PLC via OPC-UA."""
self._client = asyncua.Client(url=self._plc_url)
await self._client.connect()
print(f"Connesso al PLC: {self._plc_url}")
async def get_conveyor_speed(self) -> float:
"""Legge velocità nastro in m/s dal PLC."""
node = self._client.get_node(self.CONVEYOR_SPEED_NODE)
return await node.read_value()
async def send_sort_command(
self,
decision: SortingDecision,
conveyor_speed_mps: float
) -> None:
"""
Invia comando di sorting al PLC con delay calcolato.
Il delay compensa il ritardo di trasporto dalla stazione di ispezione
all'attuatore di espulsione.
"""
if decision == SortingDecision.ACCEPT:
return # Nessuna azione necessaria
# Calcola delay: distanza / velocità - anticipo attuatore
transport_delay_ms = (self.INSPECTION_TO_EJECTOR_MM / 1000) / conveyor_speed_mps * 1000
actuator_lead_ms = 60 # Il valvola pneumatica richiede ~60ms per aprirsi
total_delay_ms = max(0, int(transport_delay_ms - actuator_lead_ms))
# Codice comando per PLC
plc_command = 1 if decision in [SortingDecision.REJECT_DEFECT, SortingDecision.REJECT_CRITICAL] else 0
# Scrivi delay e comando
delay_node = self._client.get_node(self.SORT_DELAY_MS_NODE)
command_node = self._client.get_node(self.SORT_COMMAND_NODE)
await delay_node.write_value(total_delay_ms)
await command_node.write_value(plc_command)
async def disconnect(self) -> None:
"""Disconnette dal PLC."""
if self._client:
await self._client.disconnect()
Uçta Dağıtım: Endüstriyel Donanım için Optimizasyon
Eğitilen modelin hedef donanımda dağıtım için optimize edilmesi gerekir. YOLO11, gecikmeyi azaltabilen birden fazla dışa aktarma formatını destekler yerel PyTorch modeline kıyasla %30-70 oranında.
# deploy_optimization.py
# Export e ottimizzazione modello per deploy industriale
from ultralytics import YOLO
import torch
import time
import numpy as np
def export_optimized_model(
model_path: str,
target_hardware: str = "tensorrt", # tensorrt, openvino, onnx, hailo
image_size: int = 640,
batch_size: int = 1,
) -> str:
"""
Esporta il modello YOLO11 nel formato ottimizzato per l'hardware target.
Formati supportati per applicazioni industriali:
- TensorRT (NVIDIA GPU): massima velocità su CUDA hardware
- OpenVINO (Intel CPU/iGPU): ottimale per sistemi embedded Intel
- ONNX (universale): compatibile con ONNX Runtime su qualsiasi hardware
- Hailo: formato proprietario per chip Hailo-8/Hailo-15
"""
model = YOLO(model_path)
export_args = {
'format': target_hardware,
'imgsz': image_size,
'batch': batch_size,
'half': True, # FP16: dimezza memoria, +30% velocità
'int8': False, # INT8 richiede calibration dataset
'simplify': True, # Semplifica grafo ONNX
'dynamic': False, # Batch size fisso per latenza deterministica
'verbose': False,
}
if target_hardware == "tensorrt":
export_args.update({
'workspace': 4, # GB di workspace TensorRT
'device': '0', # GPU 0
})
exported_path = model.export(**export_args)
print(f"Modello esportato: {exported_path}")
return str(exported_path)
def benchmark_inference(model_path: str, n_iterations: int = 1000) -> dict:
"""
Benchmark di latenza per confronto tra formati export.
"""
model = YOLO(model_path)
# Immagine di test casuale (simula frame camera)
dummy_image = np.random.randint(0, 255, (640, 640, 3), dtype=np.uint8)
# Warmup
for _ in range(50):
model(dummy_image, verbose=False)
# Benchmark
latencies = []
for _ in range(n_iterations):
start = time.perf_counter()
model(dummy_image, verbose=False, half=True)
latencies.append((time.perf_counter() - start) * 1000)
latencies = np.array(latencies)
results = {
'mean_ms': float(np.mean(latencies)),
'median_ms': float(np.median(latencies)),
'p95_ms': float(np.percentile(latencies, 95)),
'p99_ms': float(np.percentile(latencies, 99)),
'max_fps': round(1000 / np.mean(latencies), 1),
}
print(f"\nBenchmark {n_iterations} iterazioni:")
print(f" Latenza media: {results['mean_ms']:.2f} ms")
print(f" Latenza P95: {results['p95_ms']:.2f} ms")
print(f" Latenza P99: {results['p99_ms']:.2f} ms")
print(f" FPS massimo: {results['max_fps']:.1f} FPS")
return results
# Esempio: confronto latenze per hardware diversi
# Risultati tipici su linea produttiva reale:
# PyTorch FP32: 12.4 ms -> 80 FPS
# PyTorch FP16: 7.1 ms -> 140 FPS
# TensorRT FP16: 2.8 ms -> 357 FPS
# TensorRT INT8: 1.9 ms -> 526 FPS
# ONNX Runtime: 5.2 ms -> 192 FPS
# OpenVINO: 3.8 ms -> 263 FPS
Örnek Olay: YOLO11'li Meyve Ayırma Hattı
Bir kooperatif için vizyon sisteminin gerçek bir uygulamasını rapor ediyoruz Güney İtalya'dan meyve çiftliği, kapasiteli elma ve portakal paketleme hattı arasında Kanal başına saniyede 10 parça, 3 paralel kanal, çalışır durumda Hasat mevsiminde (Ekim-Ocak) günde 24 saat.
Sistem Özellikleri
- Ürünler: Elmalar (Fuji, Gala, Golden), portakallar (Tarocco, Navel)
- Astar: Her biri 10 adet/sn olan 3 kanal = toplam 30 adet/sn
- Oda: Basler ace2 GigE, 12MP, 200 FPS, IP67, koaksiyel LED aydınlatma
- Donanım çıkarımı: NVIDIA Jetson AGX Orin 64 GB (kanal başına 1 adet)
- Aktüatörler: Kanal başına 6 bar'da 3 pnömatik nozul (kritik/kusur/yeniden inceleme)
- PLC: OPC-UA iletişimli Siemens S7-1500
- Algılanan sınıflar: tamam, küf, morluk, yanık, size_defect, yabancı_nesne
Veri Kümeleri ve Eğitim
- Veri kümesi: 3 koleksiyon sezonunda (2022-2024) toplanan toplam 28.400 görüntü
- Ek açıklama: 4 ek açıklamalı CVAT, belirsiz sınıflar için fikir birliğiyle oylama
- Büyütme: hava koşulları simülasyonu ile özel albümentasyon hattı
- Model: YOLO11m, AWS p3.2xlarge'de (Tesla V100) 120 dönem boyunca eğitildi
- Eğitim süresi: 4,7 saat
Üretimdeki Sonuçlar
Üretimde Sistem Metrikleri (Ortalama Sezon 2024-2025)
| Metrik | Amaç | Sonuç | Değerlendirme |
|---|---|---|---|
| mAP@50 küresel | > %90 | %93,7 | Harika |
| Kalıbı geri çağır | > %98 | %98,4 | Onaylı |
| Yabancı_nesneyi geri çağır | > %99,5 | %99,6 | Onaylı |
| Morluğu hatırla | > %90 | %91,8 | Onaylı |
| Hassasiyet tamam | > %92 | %94,2 | Harika |
| Çıkarım gecikmesi | < 8 ms | 6,3 ms (Jetson AGX) | Onaylı |
| Toplam sistem gecikmesi | < 80 ms | 71 ms | Onaylı |
| Kanal başına verim | 10 adet/sn | 10,0 adet/sn | Ulaşmış |
| Yanlış pozitif ret oranı | < %3 | %2,1 | Harika |
| Sistem çalışma süresi | > %99 | %99,7 | Harika |
Yatırım Getirisi ve Ekonomik Etki
Sistem, 6 manuel denetçiyi (vardiya başına 2 x 3 vardiya) bir maliyetle değiştirdi kurulumunun 145.000 Avro (donanım, yazılım, entegrasyon, eğitim, devreye alma). Hesaplanan ekonomik faydalar:
- Denetim personelinin azaltılması: 180.000 EUR/yıl (masraflar dahil brüt)
- Kaçırılan uygunsuzlukların azaltılması: 45.000 EUR/yıl (geri çağırmalardan, yaptırımlardan kaçınıldı)
- Yanlış pozitiflerin manuel incelemeye göre azaltılması: 28.000 EUR/yıl (geri kazanılan ürün)
- Yatırım getirisi geri ödemesi: 8,5 ay
- 3 yıllık yatırım getirisi: %478
Gıda Ortamlarında Görüntüleme Sistemlerine İlişkin Düzenlemeler ve Sertifikalar
Sertifikalı bir gıda hattına kurulu endüstriyel görüş sistemi Yalnızca yapay zeka performansının ötesine geçen belirli düzenleyici gereksinimlere uymak zorundadır. Bu gerekliliklere uyulmaması tesisin sertifikasyonlarını tehlikeye atabilir.
Fiziksel Gereksinimler: IP69K ve Gıdaya Uygun Malzemeler
Gıda ortamlarına yönelik endüstriyel odalar derecelendirilmelidir IP69K: toz girişine (6) ve su jetlerine karşı tam koruma yüksek basınç (9K) - 80 derecede basınçlı yıkayıcılarla temizlik, 100 bar birçok kuruluşta standarttır. Gıda ürünüyle temas eden malzemeler (kapaklar, montaj parçaları, destekler) içinde olmalıdır AISI 316L çelik veya polimerler için FDA/EC 10/2011 sertifikalı malzemeler.
Kablolar ve bağlantılar gıda ortamları için sertifikalı olmalıdır (TPU kapağı deterjan asitlerine karşı dayanıklı, M12 IP67+ konektörler). Endüstriyel PC'ler işlemler hattan uzakta IP65 kalaylama dolaplarında barındırılır, Korumalı GigE kablosu aracılığıyla kameraya video/sinyal çıkışı ile.
IFS Food ve BRC: Otomatik Denetim Sistemlerine İlişkin Gereksinimler
Standart IFS Gıda 8 (Uluslararası Öne Çıkan Standartlar) e BRC Global Standardı Gıda Güvenliği Sayı 9 bu sistemleri gerektirir Otomatik denetimin özellikleri şunlardır:
- Tespit özellikleriyle belgelenmiştir (sınıflar, eşikler, kapsam dahilindeki ürünler)
- Bilinen referans örnekleriyle periyodik olarak kalibre edilir (zorlama testi)
- Belgelenmiş protokol (OQ/PQ) ile ilk doğrulamaya tabi tutulur
- Riske göre HACCP planına CCP veya OPRP olarak entegre edilir
- Arıza alarm sistemi ile donatılmıştır (AI sistemi çevrimdışı ise hat durur)
- Belgelenmiş koruyucu bakıma tabidir (lens temizliği, kamera kalibrasyonu)
HACCP: ÇKP Olarak Görüş Sistemi
Yabancı cisimlerin (metal, plastik, cam, taş) tespiti için, görüş sistemi şu şekilde nitelendirilebilir: Kritik Kontrol Noktası (CCP) HACCP planında geleneksel metal dedektörünün yerine veya yanında. Bu, sistemin kapasitesini gösteren bilimsel doğrulamayı gerektirir. minimum boyuttaki yabancı cismin türünü kritik olarak tespit etmek (genellikle metaller için 2-3 mm, plastikler için 5-10 mm).
Dikkat: Yabancı Cisim Görüş Sisteminin Sınırlamaları
Görüş sistemi yalnızca yabancı cisimleri algılar yüzeyde görülebilir. Ürünün içine yabancı cisimler girmiş (örn. domatesin içindeki metal) optik kamera tarafından görülemezler. Dahili yabancı cisimlerin tespiti için, indüksiyon metal dedektörü veya X-ışını sistemi zorunlu olmaya devam ediyor ve görüş sistemini tamamlayıcı niteliktedir.
Sistem Doğrulaması: OQ ve PQ
# validation_protocol.py
# Protocollo di validazione OQ/PQ per sistema vision alimentare
import json
import datetime
from dataclasses import dataclass, field, asdict
from typing import Optional
from ultralytics import YOLO
import numpy as np
@dataclass
class ChallengeTestResult:
"""Risultato di un singolo challenge test."""
defect_class: str
defect_severity: str # 'mild', 'moderate', 'severe'
n_samples: int
detected_correctly: int
false_positives_on_ok: int
recall: float
precision: float
pass_fail: str
@dataclass
class ValidationReport:
"""Report di validazione OQ/PQ del sistema vision."""
system_id: str
product_type: str
validation_date: str
model_version: str
hardware_config: dict
challenge_results: list[ChallengeTestResult] = field(default_factory=list)
overall_result: str = "PENDING"
validated_by: str = ""
notes: str = ""
def to_json(self, output_path: str) -> None:
"""Esporta il report in formato JSON per documentazione normativa."""
report_dict = asdict(self)
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(report_dict, f, indent=2, ensure_ascii=False)
print(f"Report salvato: {output_path}")
def run_challenge_test(
model: YOLO,
challenge_images_dir: str,
defect_class: str,
severity: str,
n_ok_images: int = 50,
acceptance_recall: float = 0.95,
acceptance_precision: float = 0.85,
) -> ChallengeTestResult:
"""
Esegue un challenge test su campioni noti.
I challenge samples sono immagini acquisite dalla linea reale,
etichettate da ispettori esperti, con difetti di gravita nota.
Sono conservati fisicamente (campioni di riferimento) e fotografati
in condizioni di linea controllate.
"""
import glob
import os
# Immagini con difetto della classe specificata
defect_images = glob.glob(
os.path.join(challenge_images_dir, defect_class, severity, "*.jpg")
)
# Immagini ok di riferimento
ok_images = glob.glob(
os.path.join(challenge_images_dir, "ok", "*.jpg")
)[:n_ok_images]
detected = 0
total_defect = len(defect_images)
for img_path in defect_images:
result = model(img_path, verbose=False)
detections = [
r for r in result[0].boxes
if model.names[int(r.cls[0])] == defect_class
and float(r.conf[0]) >= 0.35 # Soglia del sistema
]
if len(detections) > 0:
detected += 1
# Falsi positivi su immagini ok
fp_count = 0
for img_path in ok_images:
result = model(img_path, verbose=False)
fp_detections = [
r for r in result[0].boxes
if model.names[int(r.cls[0])] == defect_class
and float(r.conf[0]) >= 0.35
]
if len(fp_detections) > 0:
fp_count += 1
recall = detected / max(total_defect, 1)
precision = detected / max(detected + fp_count, 1)
passed = recall >= acceptance_recall and precision >= acceptance_precision
return ChallengeTestResult(
defect_class=defect_class,
defect_severity=severity,
n_samples=total_defect,
detected_correctly=detected,
false_positives_on_ok=fp_count,
recall=round(recall, 4),
precision=round(precision, 4),
pass_fail="PASS" if passed else "FAIL"
)
Üretimde İzleme ve Geri Bildirim Döngüsü
Üretimdeki bir yapay zeka görüş sistemi statik bir eser değildir: koşullar hat değişikliği (ürünün mevsimselliği, aydınlatmada aşınma ve yıpranma, kir lenslerde) ve modelin performansı sürekli olarak izlenmelidir. Kaliteyi etkilemeden önce model sapmasını tespit etmek için.
# production_monitor.py
# Monitoring continuo del sistema vision in produzione
import sqlite3
import time
import json
from collections import deque
from typing import Optional
from dataclasses import dataclass
@dataclass
class ProductionStats:
"""Statistiche di produzione per monitoraggio."""
timestamp: str
window_minutes: int
total_inspected: int
reject_rate_pct: float
defect_distribution: dict
avg_inference_ms: float
p99_inference_ms: float
alert_triggered: bool
alert_reason: Optional[str]
class ProductionMonitor:
"""
Monitor continuo per sistema vision alimentare.
Rileva anomalie statistiche che indicano degradazione del modello.
"""
# Soglie di alert
MAX_REJECT_RATE_PCT = 15.0 # >15% scarto e anomalo
MIN_REJECT_RATE_PCT = 0.1 # <0.1% potrebbe indicare modello non funzionante
MAX_INFERENCE_P99_MS = 15.0 # Latenza P99 non deve superare 15ms
MAX_CONSECUTIVE_ACCEPTS = 500 # 500 ok di fila = modello probabilmente bloccato
def __init__(self, db_path: str = "/data/production_log.db") -> None:
self._db_path = db_path
self._inspection_buffer: deque = deque(maxlen=10000)
self._consecutive_accepts = 0
self._init_db()
def _init_db(self) -> None:
"""Inizializza database SQLite per log produzioni."""
with sqlite3.connect(self._db_path) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS inspections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp REAL,
frame_id INTEGER,
decision INTEGER,
primary_defect TEXT,
confidence REAL,
inference_ms REAL,
line_speed_mps REAL
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS alerts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp REAL,
alert_type TEXT,
details TEXT,
acknowledged INTEGER DEFAULT 0
)
""")
def log_inspection(self, result: dict) -> Optional[str]:
"""
Logga un'ispezione e verifica anomalie.
Ritorna il motivo dell'alert se presente, None altrimenti.
"""
self._inspection_buffer.append(result)
# Tracking accept consecutivi
if result['decision'] == 0: # ACCEPT
self._consecutive_accepts += 1
else:
self._consecutive_accepts = 0
# Alert: troppi accept consecutivi (modello potrebbe essere bloccato)
if self._consecutive_accepts >= self.MAX_CONSECUTIVE_ACCEPTS:
alert_msg = (
f"Alert: {self._consecutive_accepts} accept consecutivi. "
f"Verificare funzionamento sistema vision."
)
self._log_alert("CONSECUTIVE_ACCEPTS", alert_msg)
self._consecutive_accepts = 0 # Reset dopo alert
return alert_msg
# Controlla reject rate su finestra recente (ultimi 100 pezzi)
if len(self._inspection_buffer) >= 100:
recent = list(self._inspection_buffer)[-100:]
reject_count = sum(1 for r in recent if r['decision'] != 0)
reject_rate = reject_count / len(recent) * 100
if reject_rate > self.MAX_REJECT_RATE_PCT:
alert_msg = f"Alert: tasso scarto {reject_rate:.1f}% (soglia: {self.MAX_REJECT_RATE_PCT}%)"
self._log_alert("HIGH_REJECT_RATE", alert_msg)
return alert_msg
# Controlla latenza
if result.get('inference_ms', 0) > self.MAX_INFERENCE_P99_MS:
alert_msg = f"Alert: latenza inference {result['inference_ms']:.1f}ms supera soglia"
self._log_alert("HIGH_LATENCY", alert_msg)
return alert_msg
return None
def _log_alert(self, alert_type: str, details: str) -> None:
"""Logga alert nel database."""
with sqlite3.connect(self._db_path) as conn:
conn.execute(
"INSERT INTO alerts (timestamp, alert_type, details) VALUES (?, ?, ?)",
(time.time(), alert_type, details)
)
print(f"[ALERT] {alert_type}: {details}")
def get_hourly_report(self) -> dict:
"""Genera report orario delle prestazioni di produzione."""
recent = list(self._inspection_buffer)
if not recent:
return {}
total = len(recent)
rejects = sum(1 for r in recent if r['decision'] != 0)
latencies = [r.get('inference_ms', 0) for r in recent]
defect_dist = {}
for r in recent:
defect = r.get('primary_defect') or 'ok'
defect_dist[defect] = defect_dist.get(defect, 0) + 1
return {
'total_inspected': total,
'reject_rate_pct': round(rejects / total * 100, 2),
'defect_distribution': defect_dist,
'avg_inference_ms': round(sum(latencies) / len(latencies), 2),
'p99_inference_ms': round(sorted(latencies)[int(len(latencies) * 0.99)], 2),
}
En İyi Uygulamalar ve Anti-Kalıplar
Gıda Görüş Sistemi İçin En İyi Uygulamalar
- Model öncesi aydınlatma: Bütçenizin %30'unu yatırın Kaliteli yapısal aydınlatmada donanım. ile elde edilen bir veri kümesi Sabit ve tutarlı aydınlatma, numune sayısını %40 azaltır. Aynı performansı elde etmek için gereklidir.
- Bol miktarda negatif örnek: Veri kümesi şunları içermelidir Kusurlardan en az 3 kat daha fazla "tamam" görseli olmalı ve sorunsuz görseller kapsamalıdır ürünün tüm doğal değişkenliği (farklı yaşlar, boyutlar, çeşitler).
- Aylık meydan okuma testi: Resmi bir meydan okuma testi gerçekleştirin nedeniyle desen kaymasını tespit ettiği bilinen fiziksel örneklerle her ay sezonluk ürün değişiklikleri.
- Zaman aşımları ve geri dönüşler: Yapay zeka sistemi 2 kattan fazla zaman alırsa nominal gecikme süresi, resmin geçersiz olduğunu kabul edin ve ürünü gönderin manuel yeniden inceleme kanalına.
- Her kareyi günlüğe kaydedin: Görüntüyü ve her birinin sonucunu kaydedin En az 72 saat süreyle denetim. Olay sonrası hata ayıklama için gereklidir ve yeni eğitim örnekleri toplamak için.
- Donanım yedekliliği: Kritik hatlar için bir tane takın otomatik geçişli, birincil odayla aynı şekilde yapılandırılmış yedekleme odası başarısızlık durumunda.
Kaçınılması Gereken Anti-Desenler
- Akıllı telefon görüntüleri ile eğitim: Yakalanan görüntüler Laboratuvarda akıllı telefonla yapılan çalışmalar gerçek koşulları temsil etmiyor çizgi. Veri kümesini toplamak için daima son endüstriyel odayı kullanın.
- Tüm sınıflar için tek güven eşiği: Bir eşik üniforma en çok temsil edilen sınıfları tercih eder. Eşikleri sınıfa göre kullanın, kusurun kritikliğine göre kalibre edilir.
- Gölge modu olmadan dağıtma süresi: Kontrol etmeden önce AI'ya, sistemi en az bir süre manuel incelemeye paralel olarak çalıştırın 2 hafta, kararların karşılaştırılması. Aşırı yanlış pozitifleri düzeltin canlı yayına geçmeden önce.
- Model sapmasını göz ardı etmek: Model performansı düşer ürün farklılıkları, aydınlatmanın aşınması, kirlenme nedeniyle zamanla mercek üzerinde. Aktif izleme olmadan bozulma sessiz ve tehlikelidir.
- Olağanüstü durum kurtarma planı yok: Yapay zeka sistemi başarısız olursa, Bir geri dönüş planı olmalıdır (manuel inceleme, hat yavaşlatma) periyodik olarak belgelenir ve test edilir.
Sonuçlar ve Sonraki Adımlar
YOLO11 ile bilgisayarlı görme günümüzün en olgun ve erişilebilir teknolojisini temsil ediyor Gıda endüstrisinde otomatik kalite kontrolü için. Artık bir teknoloji değil laboratuvar: örnek olayda açıklanana benzer yüzlerce sistem çalışır durumda %98'in üzerinde doğruluk oranıyla belgelenmiş sonuçlarla küresel fabrikaların 12 aydan kısa yatırım getirisi ve %99'un üzerinde çalışma süresi.
Gıda güvenliği ve kalitesine yönelik yapay zeka pazarı 2,7 milyardan 13,7 milyara çıkacak Giderek katılaşan düzenleyici gerekliliklerin etkisiyle 2030 yılına kadar dolar (%30,9 CAGR), uzman işgücü eksikliği ve geri çağırma maliyetlerindeki artış. Gıda şirketleri Bugün yapay zeka görüş sistemlerine yatırım yapanlar kendilerini rekabet avantajıyla konumlandırıyor sonradan doldurulması zor yapısal.
Veri kümesi toplamadan ek açıklamalara kadar bu makalede açıklanan teknik yol CVAT/Roboflow ile YOLO11 eğitiminden, meydan okuma testleri ile doğrulamaya ve dağıtıma kadar PLC entegrasyonu ve sürekli izleme ile her üretim hattına uygulanabilir Belirli ürüne gerekli uyarlamaları yaparak besleyin.
Gıda Vizyon Projenizi Başlatmak için Kontrol Listesi
- Ürününüz için öncelikli kusur sınıflarını tanımlayın (başlangıçta maksimum 10)
- Gerçek hat koşullarında son kamerayla sınıf başına en az 200 örnek alın
- Ek açıklama aracını seçin (gizlilik için kendi kendine barındırılan CVAT, hız için Roboflow)
- Yaklaşımı doğrulamak için sınırlı veri kümesinde YOLO11m ile başlayın (2-3 günlük çalışma)
- Kabul edilebilirlik eşiklerini (geri çağırma/hassasiyet) kalite yöneticisiyle tanımlayın
- Canlı yayına geçmeden önce gölge modu dönemini planlayın
- IFS/BRC/HACCP gereksinimlerine ilişkin her şeyi baştan belgeleyin
FoodTech Serisine devam edin
Bu makale serinin bir parçasıdır Gıda Teknolojisi federicocalo.dev'de. Bir sonraki makale dijital mevzuat uyumluluğunu ele alıyor: FSMA ve Dijital Uyumluluk: Düzenleyici Süreçlerin Otomasyonu, HACCP belge akışlarını nasıl otomatikleştireceğimizi, planları nasıl yöneteceğimizi göreceğiz dijital araçlarla kontrol edin ve sistemlerle FDA/IFS/BRC denetimlerine hazırlanın entegre izlenebilirlik.
Diğer serilerden ilgili makaleler:
- MLOps Serisi: YOLO modelleri üretime nasıl geçirilir ve MLflow ve Açıkçası ile nasıl izlenir?
- Bilgisayarlı Görme Serisi: TensorRT ile gelişmiş CNN'ler, semantik segmentasyon ve uç konuşlandırma
- Yapay Zeka Mühendisliği Serisi: Triton Inference Server ile ölçeklenebilir çıkarım hatları
- Gelişmiş Derin Öğrenme Serisi: Küçük veri kümeleri için öğrenme ve alan uyarlama tekniklerini aktarın







