Une caméra et un modèle de vision peuvent examiner chaque soudure de chaque unité, ce qu'un inspecteur ne peut pas tenir à la cadence de production. Ce guide montre comment capturer chaque soudure en pièce jointe, laisser un modèle signaler les soudures suspectes, et faire confirmer ou rejeter chaque signalement par l'opérateur depuis une invite opérateur TofuPilot, en gardant l'IA comme aide au signalement à côté des critères IPC-A-610.
Ce qu'un modèle de vision peut juger, et ce qu'il ne peut pas
Une caméra voit l'extérieur de la soudure. Tout ce qu'un modèle conclut vient de cette surface.
| Défaut | Visible par une caméra | Remarques |
|---|---|---|
| Pont de soudure entre pastilles | Oui | Contraste net, bonne cible pour un modèle |
| Soudure absente ou insuffisante | Oui | Forme et couverture du ménisque |
| Brins égarés hors de la soudure | Oui | Demande une résolution suffisante à la sortie du fil |
| Isolant brûlé ou fondu | Oui | Changement de couleur |
| Surface terne ou granuleuse (soudure froide possible) | En partie | Dépend de l'éclairage, de l'alliage et des résidus de flux |
| Fissure sous le ménisque | Non | Utiliser une mesure électrique sous flexion |
| Mauvais mouillage des brins dans la soudure | Non | Utiliser une chute de tension 4 fils |
| Vides dans la soudure | Non | Rayons X ou micro-section |
Les méthodes électriques pour les défauts cachés sont traitées dans Contrôler les soudures sans les détruire et Détecter les contacts intermittents en production.
Choisir le type de modèle
| Approche | Entraîné sur | Sortie | Force | Faiblesse |
|---|---|---|---|---|
| Classifieur supervisé | Images étiquetées de bonnes soudures et de chaque type de défaut | Une classe par soudure | Nomme le défaut | Demande de nombreux exemples étiquetés de chaque défaut, rares sur une bonne ligne |
| Détecteur d'anomalies | Images de bonnes soudures uniquement | Un score : l'écart à la « normale » | Démarre avec des unités saines seulement | Signale tout changement, y compris anodin (éclairage, résidus de flux, couleur du fil) |
La détection d'anomalies est le point de départ habituel, car les défauts sont rares au début. Les confirmations recueillies par la procédure ci-dessous deviennent plus tard le jeu étiqueté d'un classifieur.
Trois limites s'appliquent aux deux approches :
- Faux positifs. Un seuil assez bas pour détecter les vrais défauts signale aussi de bonnes soudures. L'étape de revue par l'opérateur les absorbe.
- Faux négatifs. Un défaut que le modèle n'a jamais vu, ou qui paraît normal en surface, passe. L'IA ne remplace pas l'inspection humaine selon les critères IPC-A-610 ; elle oriente l'attention.
- Sensibilité au montage. Une caméra déplacée, un éclairage modifié ou une nouvelle révision de carte décalent tous les scores. Figez le montage optique et revalidez le modèle après tout changement.
Prérequis
- Le CLI TofuPilot installé sur le poste de test
- Une caméra sur un support fixe, une source de lumière fixe (anneau lumineux ou dôme diffus) et un gabarit qui place chaque carte dans la même position
- Un modèle d'anomalies exporté en ONNX qui prend une vignette de soudure et renvoie un score ; le prétraitement de ce guide (RGB, 224 × 224, valeurs de 0 à 1) est un exemple et doit correspondre à l'entraînement de votre modèle
- Les positions des soudures dans l'image, sous forme de rectangles mesurés une fois sur une carte de référence
opencv-python-headless>=4.8onnxruntime>=1.17numpy>=1.24Étape 1 : écrire le plug caméra
Les méthodes d'un plug échangent des valeurs sérialisables en JSON avec la phase, le plug écrit donc l'image sur disque et renvoie son chemin, pas le tableau de pixels.
plugs/camera.py26 lines
import osimport cv2class Camera: def __init__(self, index=0, width=3840, height=2160): self._cap = cv2.VideoCapture(index) self._cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) self._cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) if not self._cap.isOpened(): raise RuntimeError(f"Camera {index} could not be opened") def capture(self, out_dir, name): # Vide les images mises en tampon par le pilote avant la mise en place de la carte. for _ in range(5): self._cap.read() ok, frame = self._cap.read() if not ok: raise RuntimeError("Camera returned no frame") path = os.path.join(out_dir, name) cv2.imwrite(path, frame) return path def __del__(self): self._cap.release()Étape 2 : écrire le plug d'inspection
L'inspecteur découpe chaque soudure, lui attribue un score, enregistre la vignette et renvoie une entrée par soudure.
plugs/inspector.py42 lines
import osimport cv2import numpy as npimport onnxruntime as ortclass JointInspector: def __init__(self, model_path, threshold, rois, input_size=224): # Résout un chemin relatif depuis le répertoire de la procédure, pas le répertoire courant. if not os.path.isabs(model_path): model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", model_path) self._session = ort.InferenceSession(model_path) self._input_name = self._session.get_inputs()[0].name self._threshold = threshold self._rois = rois self._size = input_size def inspect(self, image_path, out_dir): image = cv2.imread(image_path) results = [] for roi in self._rois: x, y, w, h = roi["x"], roi["y"], roi["w"], roi["h"] crop = image[y : y + h, x : x + w] crop_path = os.path.join(out_dir, f"{roi['joint']}.png") cv2.imwrite(crop_path, crop) # Doit correspondre au prétraitement utilisé à l'entraînement. rgb = cv2.cvtColor(crop, cv2.COLOR_BGR2RGB) tensor = cv2.resize(rgb, (self._size, self._size)).astype(np.float32) / 255.0 tensor = np.transpose(tensor, (2, 0, 1))[np.newaxis, ...] score = float(self._session.run(None, {self._input_name: tensor})[0].ravel()[0]) results.append( { "joint": roi["joint"], "score": score, "flagged": score >= self._threshold, "crop_path": crop_path, } ) return resultsÉtape 3 : capturer et noter dans une phase
phases/inspect.py22 lines
import base64import tempfileimport cv2def capture_and_score(camera, inspector, measurements, attach, log): work_dir = tempfile.mkdtemp(prefix="joint-inspect-") image_path = camera.capture(work_dir, "board.png") attach.file(image_path, "board.png") results = inspector.inspect(image_path, work_dir) flagged = [r for r in results if r["flagged"]] measurements.max_anomaly_score = max(r["score"] for r in results) measurements.flagged_count = len(flagged) measurements.flagged = { "joints": [r["joint"] for r in flagged], "crops": {r["joint"]: r["crop_path"] for r in flagged}, "scores": {r["joint"]: round(r["score"], 4) for r in flagged}, } log.info(f"{len(flagged)} of {len(results)} joints flagged")L'image complète de la carte est jointe à chaque run. Le dictionnaire flagged est une mesure JSON, que la phase suivante lit par les résultats précédents.
Étape 4 : faire confirmer chaque signalement par l'opérateur
Ajoutez une seconde fonction dans le même fichier. Elle affiche côte à côte les vignettes signalées et demande lesquelles sont de vrais défauts. Les options de la checklist sont remplies à l'exécution avec ui.set, et ui.wait() renvoie la réponse de l'opérateur.
phases/inspect.py23 lines
def confirm_flags(capture_and_score, phase, ui, measurements, attach): flagged = capture_and_score.flagged if not flagged["joints"]: phase.skip("No joint flagged by the model") tiles = [] for joint in flagged["joints"]: tile = cv2.resize(cv2.imread(flagged["crops"][joint]), (320, 320)) cv2.putText(tile, joint, (8, 28), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2) tiles.append(tile) ok, png = cv2.imencode(".png", cv2.hconcat(tiles)) ui.flagged_image = "data:image/png;base64," + base64.b64encode(png.tobytes()).decode() ui.set("confirmed", options=[{"value": j, "label": j} for j in flagged["joints"]]) answer = ui.wait() confirmed = answer["confirmed"] # Nomme chaque vignette avec le verdict : les pièces jointes forment un jeu étiqueté. for joint in flagged["joints"]: verdict = "defect" if joint in confirmed else "false_positive" attach.file(flagged["crops"][joint], f"{joint}_{verdict}.png") measurements.confirmed_defects = len(confirmed)Étape 5 : déclarer la procédure
procedure.yaml59 lines
name: ESC Joint Visual Inspectionversion: 1.0.0execution: workers: 1plugs: - name: Inspection Camera key: camera python: plugs.camera:Camera scope: station with: index: 0 - name: Joint Inspector key: inspector python: plugs.inspector:JointInspector scope: station with: model_path: "models/joint_anomaly.onnx" threshold: 0.6 rois: - { joint: bat_pos, x: 410, y: 220, w: 300, h: 300 } - { joint: bat_neg, x: 760, y: 220, w: 300, h: 300 } - { joint: m1_a, x: 1210, y: 640, w: 260, h: 260 }main: - name: Capture and Score key: capture_and_score python: phases.inspect:capture_and_score measurements: - name: Max Anomaly Score key: max_anomaly_score - name: Flagged Count key: flagged_count - name: Flagged Joints key: flagged - name: Confirm Flagged Joints key: confirm_flags python: phases.inspect:confirm_flags depends_on: [capture_and_score] ui: components: - key: flagged_image type: image label: "Joints flagged by the model" width: 100% fit: contain - key: confirmed type: checklist label: "Select the joints that are real defects per IPC-A-610" description: "Leave all unselected if every flag is a false positive" required: false measurements: - name: Confirmed Defects key: confirmed_defects validators: - operator: "==" expected_value: 0Choix de conception de ce fichier :
- Seul un défaut confirmé fait échouer l'unité. Le signalement du modèle est enregistré mais n'a pas de validateur. L'opérateur, formé à l'IPC-A-610, prend la décision d'acceptation ou de rejet.
- Le seuil est un exemple. Fixez-le à partir des scores mesurés sur des unités saines et sur les défauts dont vous disposez, puis revoyez-le à mesure que les confirmations s'accumulent.
- Quand rien n'est signalé, la phase de confirmation se saute elle-même avant d'enregistrer quoi que ce soit, elle ne compte donc pas comme un échec. Son écran s'ouvre au début de la phase et se ferme au moment du saut.
scope: stationgarde la caméra ouverte et le modèle chargé d'une unité à l'autre.
Étape 6 : mesurer la performance du modèle
Chaque run enregistre désormais combien de soudures le modèle a signalées et combien l'opérateur en a confirmé. Dans le dashboard TofuPilot :
| Question | Où la lire |
|---|---|
| Le taux de faux positifs est-il acceptable | Nombre de signalements comparé aux défauts confirmés sur l'ensemble des runs |
| Le modèle dérive-t-il (éclairage, caméra, révision de carte) | Carte de contrôle du score d'anomalie maximal |
| Quelle soudure est le plus souvent confirmée défectueuse | Pareto des défaillances sur les défauts confirmés, et pièces jointes de chaque run |
| Un opérateur rejette-t-il des signalements que d'autres confirment | Défauts confirmés filtrés par opérateur |
Une hausse du score d'anomalie sur toutes les unités à la fois vient généralement du montage, pas du brasage. Une hausse sur une seule position de soudure est un signal de procédé, à analyser avec Pourquoi les soudures d'ESC cèdent en vol.
L'accord entre opérateurs compte autant que la précision du modèle. Si deux inspecteurs rendent des verdicts différents sur la même vignette, l'étiquette n'est pas fiable. L'accord entre inspecteurs est une question de système de mesure, et les principes de Qu'est-ce que la R&R et la MSA s'y appliquent. Conservez l'enregistrement d'inspection comme décrit dans Suivi des inspections IPC-A-610.
Pièges courants
Utiliser le modèle comme décision bon/mauvais. Un modèle a un taux de faux négatifs, et personne ne revoit ce qu'il laisse passer. Gardez le verdict humain comme décision et le modèle comme signalement.
Changer l'éclairage ou la caméra sans revalider. Tous les scores se décalent. Figez le montage optique et refaites la validation sur des unités saines et défectueuses connues après tout changement.
Renvoyer des images depuis un plug. Les tableaux de pixels ne sont pas sérialisables en JSON. Renvoyez un chemin de fichier.
Un prétraitement différent de l'entraînement. BGR au lieu de RGB, une autre taille d'entrée ou une autre normalisation donnent des scores plausibles qui ne signifient rien.
Attendre de la caméra qu'elle trouve les défauts cachés. Les fissures sous le ménisque, le mauvais mouillage interne et les vides sont invisibles. Associez l'inspection à une mesure 4 fils.
Jeter les verdicts de l'opérateur. Les vignettes nommées sont les images de défauts étiquetées dont le prochain modèle a besoin.
Points clés
- Une caméra et un modèle peuvent examiner chaque soudure, mais seulement sa surface visible.
- Commencez par un détecteur d'anomalies entraîné sur de bonnes soudures ; recueillez des défauts étiquetés par les confirmations de l'opérateur.
- Capturez la carte en pièce jointe, notez chaque vignette de soudure dans un plug, et enregistrez les signalements comme des mesures.
- Laissez l'opérateur confirmer chaque signalement avec
ui.wait(); seuls les défauts confirmés font échouer l'unité. - Suivez les faux positifs et la dérive des scores dans le dashboard, et revalidez après tout changement du montage optique.
- Conservez l'inspection IPC-A-610 et la mesure électrique ; l'IA oriente l'attention, elle ne les remplace pas.
