PCBA & Functional Testing

Détecter les contacts intermittents

Détectez les soudures, sertissages et connecteurs intermittents en mesurant la résistance sous flexion et en validant la dispersion, pas seulement la moyenne.

JJulien Buteau
intermediate9 min de lecture27 octobre 2026

Un contact intermittent mesure normalement au repos et ne s'ouvre que lorsque la pièce bouge. Ce guide montre comment le détecter en production en mesurant la résistance en continu pendant que la liaison est fléchie, en enregistrant le résultat comme une mesure temporelle dans TofuPilot, et en validant la dispersion des lectures, pas seulement leur moyenne.

Pourquoi une mesure statique ne suffit pas

Une soudure fissurée, un sertissage qui n'a pas capturé tous les brins ou un contact de connecteur à force de contact trop faible peuvent tous conduire parfaitement tant que rien ne bouge. Les faces de la fissure se touchent, ou les brins appuient contre le fût, et une lecture de résistance unique tombe dans les limites. Sous vibration ou traction sur le fil, la surface de contact change d'un instant à l'autre et la résistance saute.

Lectures d'exemple sur une soudure de fil de forte section d'un ESC, en 4 fils, courant de test de 4 A :

SoudureLecture statiqueSous flexion : minSous flexion : maxSous flexion : moyenne
Bonne0,50 mΩ0,49 mΩ0,52 mΩ0,50 mΩ
Fissurée0,62 mΩ0,60 mΩ3,80 mΩ0,75 mΩ

Ces valeurs sont illustratives. C'est le schéma qui compte : la soudure fissurée passe une limite statique de 0,8 mΩ et passe aussi une limite sur la moyenne sous flexion, parce que les lectures élevées sont brèves. Seuls le maximum et la dispersion (max moins min) la distinguent de la bonne soudure.

La méthode s'applique à tout contact qui transporte du courant à travers une interface mécanique :

ContactComment le solliciter pendant la mesure
Soudure fil sur pastilleFléchir le fil d'un angle fixe à la sortie de la soudure
SertissageFléchir le fil à l'arrière du fût de sertissage
ConnecteurBasculer le connecteur accouplé, ou faire vibrer l'ensemble
Soudure traversante sur un composant lourdTapoter ou faire vibrer la carte

Principe de la mesure

Le gabarit fait trois choses simultanément pendant une durée fixe :

  1. Il impose un courant constant dans le contact par des contacts de force Kelvin.
  2. Il sollicite le contact avec un actionneur : un bras de servomoteur, un solénoïde ou un petit pot vibrant, à amplitude et fréquence fixes.
  3. Il échantillonne la tension de mesure et le courant réel à intervalle fixe.

Chaque échantillon donne une résistance R = V / I. Avec la tension en microvolts et le courant en milliampères, le rapport est directement en milliohms. La série d'échantillons est enregistrée comme une seule mesure multidimensionnelle : le temps en abscisse, la résistance en ordonnée.

L'actionneur doit solliciter la pièce de la même façon sur chaque unité. Un technicien qui fléchit un fil à la main applique une force différente à chaque fois, et le résultat ne peut pas être comparé d'une unité à l'autre ni entre opérateurs.

Prérequis

  • Le CLI TofuPilot installé sur le poste de test
  • Un gabarit Kelvin tel que décrit dans Contrôler les soudures sans les détruire, complété d'un actionneur
  • Un firmware de gabarit qui capture une rafale d'échantillons pendant l'actionnement et les renvoie en une seule réponse

Étape 1 : définir la commande de capture

Dans cet exemple, le microcontrôleur du gabarit accepte une commande par capture :

fixture-protocol.txt
FLEX <channel> <amps> <samples> <period_ms> <flex_hz>

Le firmware sélectionne le canal, mesure l'offset à courant nul, active le courant, démarre l'actionneur, prend samples lectures toutes les period_ms, arrête l'actionneur et le courant, puis répond par une ligne JSON :

fixture-reply.json
{"sense_uv": [2010.4, 2012.1, 2009.8], "current_ma": [4001.2, 4000.8, 4001.0]}

L'offset est déjà soustrait de sense_uv. La temporisation reste dans le firmware, parce qu'un microcontrôleur échantillonne à intervalle régulier, ce que ne fait pas une boucle Python qui envoie une requête série par échantillon.

Étape 2 : écrire le plug du gabarit

plugs/flex_fixture.py
21 lines
import jsonimport serialclass FlexFixture:    def __init__(self, port, baudrate=115200):        self._ser = serial.Serial(port, baudrate, timeout=10)    def capture_under_flex(self, channel, amps, samples, period_ms, flex_hz):        command = f"FLEX {channel} {amps:.3f} {samples} {period_ms} {flex_hz:.2f}"        self._ser.write(command.encode())        reply = self._ser.readline().decode().strip()        if not reply or reply.startswith("ERR"):            raise RuntimeError(f"Flex capture on channel {channel} failed: {reply!r}")        # Des listes de flottants traversent la frontière du plug, qui n'accepte que du JSON.        return json.loads(reply)    def __del__(self):        self._ser.close()

Le timeout série couvre toute la capture : 500 échantillons à 2 ms prennent 1 s, plus le démarrage et l'arrêt de l'actionneur.

Étape 3 : écrire la phase de mesure

phases/flex.py
49 lines
import csvimport io# Enregistré à la place d'un échantillon où le courant s'est effondré (contact ouvert).R_CEILING_MOHM = 100.0def measure_under_flex(    flex_fixture,    measurements,    attach,    log,    channel,    current_a=4.0,    samples=500,    period_ms=2,    flex_hz=5.0,):    capture = flex_fixture.capture_under_flex(channel, current_a, samples, period_ms, flex_hz)    times_ms = [i * period_ms for i in range(len(capture["sense_uv"]))]    resistance = []    open_samples = 0    for uv, ma in zip(capture["sense_uv"], capture["current_ma"]):        # Un courant très inférieur à la consigne signifie que la source a atteint        # sa tension de compliance : le contact était ouvert pendant cet échantillon.        if ma < 0.5 * current_a * 1000:            open_samples += 1            resistance.append(R_CEILING_MOHM)        else:            resistance.append(uv / ma)  # µV / mA = mΩ    m = measurements.flex_resistance    m.x_axis = times_ms    m.y_axis.resistance = resistance    aggs = m.y_axis.resistance.aggregations    aggs.mean = sum(resistance) / len(resistance)    aggs.max = max(resistance)    aggs.spread = max(resistance) - min(resistance)    aggs.open_samples = open_samples    buf = io.StringIO()    writer = csv.writer(buf)    writer.writerow(["time_ms", "sense_uv", "current_ma", "resistance_mohm"])    writer.writerows(zip(times_ms, capture["sense_uv"], capture["current_ma"], resistance))    attach.data(buf.getvalue().encode(), f"flex_channel_{channel}.csv")    log.info(f"Channel {channel}: spread {aggs.spread:.3f} mΩ, {open_samples} open samples")

Le graphique du run montre la courbe, les agrégations portent la décision bon/mauvais, et le CSV conserve la tension et le courant bruts pour l'analyse des défaillances.

Étape 4 : déclarer la mesure et ses limites

Les validateurs d'axe d'une mesure multidimensionnelle n'acceptent que == et != contre un tableau complet, les limites de plage se placent donc sur des agrégations. Le type d'une agrégation est une chaîne libre, ce qui permet spread et open_samples à côté de mean et max.

procedure.yaml
49 lines
name: ESC Power Joint Flex Screeningversion: 1.0.0execution:  workers: 1plugs:  - name: Flex Fixture    key: flex_fixture    python: plugs.flex_fixture:FlexFixture    scope: station    with:      port: "COM5"main:  - name: Battery Positive Joint Under Flex    key: flex_bat_pos    python: phases.flex:measure_under_flex    with:      channel: 0    measurements:      - name: Resistance Under Flex        key: flex_resistance        title: Battery positive joint, 5 Hz flex        x_axis:          legend: Time          key: time          unit: ms        y_axis:          - legend: Resistance            key: resistance            unit: mΩ            aggregations:              - type: mean                validators:                  - operator: "<="                    expected_value: 0.8              - type: max                validators:                  - operator: "<="                    expected_value: 1.2              - type: spread                validators:                  - operator: "<="                    expected_value: 0.3              - type: open_samples                validators:                  - operator: "=="                    expected_value: 0

Les limites sont des exemples pour une soudure de fil de forte section à 4 A. Dérivez les vôtres d'unités réputées bonnes, comme pour la mesure statique : mesurez la dispersion sur 30 unités saines ou plus sous le même actionnement, et placez la limite au-dessus de cette population avec une marge pour le bruit du gabarit. Une étude R&R sur la dispersion indique si le bruit du gabarit est assez faible pour la limite visée.

AgrégationCe qu'elle détecte
meanUne soudure résistive en permanence, déjà visible en statique
maxDe brefs pics de résistance pendant l'ouverture de la fissure
spreadUn contact dont la résistance suit l'actionneur
open_samplesDes échantillons où le contact s'est complètement ouvert

Étape 5 : choisir la cadence et la durée

Deux paramètres déterminent ce que la mesure peut voir :

  • Intervalle d'échantillonnage. Une ouverture plus courte que l'intervalle entre deux échantillons peut passer entre eux. À 2 ms par échantillon, une ouverture de 0,5 ms n'est détectée que si un échantillon tombe dedans. Des intervalles plus courts réduisent le moyennage par échantillon et augmentent le bruit, la limite de dispersion doit en tenir compte. Il existe des détecteurs d'événements dédiés aux discontinuités inférieures à la milliseconde sur les connecteurs ; une mesure de résistance échantillonnée ne les remplace pas.
  • Durée et cycles de flexion. Enregistrez plusieurs cycles complets d'actionneur : 500 échantillons à 2 ms couvrent 1 s, soit 5 cycles à 5 Hz. Un seul cycle peut ne pas atteindre la position où la fissure s'ouvre.

Gardez un nombre de points modéré par mesure : de quelques centaines à quelques milliers. La pièce jointe CSV contient la capture complète si une acquisition plus longue est nécessaire pour l'analyse.

Étape 6 : analyser les résultats

Dans le dashboard TofuPilot, chaque run affiche le graphique de résistance de chaque soudure à côté de ses agrégations. Sur l'ensemble des runs :

VueUsage
Pareto des défaillances par mesureMontre la soudure ou le connecteur qui échoue le plus souvent sous flexion
Carte de contrôle et Cpk de chaque agrégationMontre une dispersion qui augmente sur plusieurs semaines, souvent un actionneur ou un gabarit usé
Rendement par opérateur et par posteDésigne l'étape de montage ou le poste de test en cause

Pour les causes de procédé des soudures fissurées et les actions correctives, voir Pourquoi les soudures d'ESC cèdent en vol.

Pièges courants

Valider uniquement la moyenne. Une fissure ouverte quelques pour cent du temps déplace à peine la moyenne. Mettez des limites sur max, spread et open_samples.

Fléchir à la main. La force et l'angle changent avec chaque opérateur. Utilisez un actionneur à amplitude et fréquence fixes.

Échantillonner depuis une boucle Python sur la liaison série. L'intervalle varie avec la latence USB et celle du système d'exploitation. Laissez le firmware cadencer les échantillons et renvoyer la rafale.

Calculer la résistance avec la consigne de courant. Pendant une ouverture, la source ne peut pas maintenir son courant. Utilisez le courant mesuré à chaque échantillon, et comptez ceux où il s'est effondré.

Mesurer en 2 fils. Les contacts des pointes bougent aussi sous flexion, et leur résistance varie. Les contacts Kelvin les retirent de la lecture.

Oublier workers: 1. Les phases sans depends_on démarrent en parallèle. Deux phases qui pilotent en même temps un seul actionneur et un seul multiplexeur produisent des données sans signification.

Fléchir trop fort. Le test doit révéler les défauts, pas les créer. Réglez l'amplitude bien en dessous de ce que la soudure subit en service, et vérifiez sur des unités saines que des tests répétés ne modifient pas leurs lectures.

Points clés

  • Un contact intermittent passe une mesure de résistance statique et ne s'ouvre qu'en mouvement.
  • Mesurez la résistance en continu pendant qu'un actionneur à amplitude fixe fléchit la soudure, le sertissage ou le connecteur.
  • Enregistrez la série comme une mesure multidimensionnelle, la résistance en milliohms en fonction du temps.
  • Validez les agrégations max, spread et open_samples, pas seulement la moyenne.
  • Laissez le firmware du gabarit cadencer les échantillons, et joignez la capture brute en CSV.
  • Fixez les limites à partir d'unités saines sous le même actionnement, et vérifiez le bruit du gabarit par une étude R&R.

Plus de guides

Mettez ce guide en pratique