Les tests matériels produisent des formes d'onde, pas seulement des nombres isolés. Un test d'ondulation d'alimentation capture des milliers d'échantillons de tension dans le temps. Un test de vibration enregistre des spectres d'accélération sur différentes bandes de fréquence. Les stocker nativement permet de suivre et comparer les formes d'onde sur l'ensemble de votre production.
Valeurs uniques vs. séries temporelles
La plupart des systèmes de test stockent un seul nombre par mesure : « tension de sortie = 3,31 V ». Mais l'histoire complète se trouve dans la forme d'onde. Ce 3,31 V peut être un signal DC propre ou un signal bruité qui se moyenne simplement à 3,31 V.
| Type de données | Exemple | Ce que cela révèle |
|---|---|---|
| Valeur unique | Vout = 3,31 V | Niveau moyen de sortie |
| Série temporelle | 1000 échantillons sur 10 ms | Ondulation, bruit, comportement transitoire |
| Spectre fréquentiel | FFT de la tension de sortie | Contenu fréquentiel du bruit de commutation |
| Multi-axes | Accélération X/Y/Z | Vibration dans toutes les directions |
Les mesures dimensionnées gèrent tous ces types.
Capture de données en séries temporelles
Avec OpenHTF
Utilisez des mesures dimensionnées pour stocker les données en séries temporelles.
ripple_test_openhtf.py35 lines
import openhtf as htffrom tofupilot.openhtf import upload@htf.measures( htf.Measurement("output_ripple_mv") .with_dimensions("time_us") .with_units("mV"), htf.Measurement("ripple_pk_pk_mv") .in_range(0, 50) .with_units("mV"),)def ripple_test(test): # Capturer 1000 échantillons à 100 kHz (espacement de 10 us) waveform = capture_oscilloscope(channel=1, samples=1000, rate_hz=100000) for i, sample in enumerate(waveform): test.measurements.output_ripple_mv[i * 10] = sample # temps en microsecondes # Stocker aussi le résumé scalaire test.measurements.ripple_pk_pk_mv = max(waveform) - min(waveform)def main(): test = htf.Test( ripple_test, procedure_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", # UUID de procédure du dashboard part_number="PSU-100", ) test.add_output_callbacks(upload()) test.execute(lambda: "PSU-2025-0099")if __name__ == "__main__": main()Installez-le avec pip install "tofupilot[openhtf]". Le callback upload() lit TOFUPILOT_API_KEY dans l'environnement.
Notez que la limite de conformité porte sur le scalaire (ripple_pk_pk_mv), pas sur la forme d'onde. Cette répartition est délibérée et vaut la peine d'être conservée : le scalaire décide du verdict et alimente les tendances, tandis que la forme d'onde est ce qu'on ouvre quand le scalaire signale un problème.
Avec le SDK Python
Si vous n'utilisez pas OpenHTF, créez l'exécution directement :
ripple_test_client.py36 lines
import osfrom datetime import datetime, timedelta, timezoneimport numpy as npfrom tofupilot.v2 import TofuPilotstarted = datetime.now(timezone.utc) - timedelta(seconds=5)ended = datetime.now(timezone.utc)waveform = capture_oscilloscope(channel=1, samples=1000, rate_hz=100000)pk_pk = float(np.max(waveform) - np.min(waveform))outcome = "PASS" if pk_pk < 50 else "FAIL"with TofuPilot(api_key=os.getenv("TOFUPILOT_API_KEY")) as client: client.runs.create( procedure_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", serial_number="PSU-2025-0099", part_number="PSU-100", outcome=outcome, started_at=started, ended_at=ended, phases=[{ "name": "Output Ripple", "outcome": outcome, "started_at": started, "ended_at": ended, "measurements": [{ "name": "Ripple Peak to Peak", "measured_value": pk_pk, "units": "mV", "validators": [ {"operator": "<=", "expected_value": 50}, ], }], }], )Ceci envoie le scalaire extrait plutôt que les 1000 échantillons. Si vous devez conserver la capture brute à côté du résultat, joignez-la comme fichier plutôt que de l'étendre en mesures.
Types de données de test en séries temporelles
Formes d'onde tension/courant
Capturées depuis des oscilloscopes ou des DAQ lors de séquences de mise sous tension, tests d'ondulation ou tests de réponse transitoire. Stockez la forme d'onde brute plus les résumés scalaires (crête-à-crête, RMS, temps de montée).
Profils de température
Enregistrés pendant les cycles thermiques, le déverminage ou les tests de dissipation thermique. Plusieurs capteurs produisent des données multi-canaux sur des minutes ou des heures.
# Test thermique avec 4 capteurs échantillonnés chaque seconde pendant 30 minutessensors = ["junction", "ambient", "heatsink", "case"]duration_s = 1800samples_per_sensor = duration_s # 1 échantillon/secondefor sensor_name in sensors: readings = read_thermal_sensor(sensor_name, samples=samples_per_sensor) measurements.append({ "name": f"temp_{sensor_name}", "measured_value": max(readings), # la température de pic est la valeur qui décide "units": "degC", "validators": [ {"operator": "<=", "expected_value": 85}, ], })Quatre capteurs à 1800 échantillons chacun font 7200 points par unité. En volume de production, stockez les caractéristiques qui décident du résultat (pic, temps au-dessus du seuil, régime établi final) et gardez le profil complet en pièce jointe.
Spectres de vibration
Données FFT d'accéléromètres lors de tests de vibration. La fréquence sur un axe, l'amplitude sur l'autre.
Courbes pression/débit
Mesures de pression et de débit en séries temporelles lors de tests d'étanchéité, tests pneumatiques ou validation hydraulique.
Analyse des séries temporelles sur l'ensemble de la production
La puissance du stockage de formes d'onde plutôt que de simples scalaires réside dans la comparaison entre unités.
Superposition de formes d'onde
Comparez les formes d'onde de 100 unités. Si 99 se ressemblent et qu'une a un pic supplémentaire, cette unité a un problème que la mesure crête-à-crête seule n'aurait peut-être pas détecté.
Bornes statistiques
Calculez la moyenne et l'écart type de votre forme d'onde à chaque point temporel sur toutes les unités. Cela donne une enveloppe de comportement normal. Toute unité qui en sort est signalée.
import numpy as np# Toutes les formes d'onde de la production (chacune est une liste de 1000 échantillons)all_waveforms = np.array(waveforms_from_production) # forme : (N_unités, 1000)mean_waveform = np.mean(all_waveforms, axis=0)std_waveform = np.std(all_waveforms, axis=0)upper_bound = mean_waveform + 3 * std_waveformlower_bound = mean_waveform - 3 * std_waveform# Vérifier si la forme d'onde d'une nouvelle unité est dans les bornesnew_waveform = np.array(new_unit_data)is_anomalous = np.any(new_waveform > upper_bound) or np.any(new_waveform < lower_bound)Deux réserves avant d'en faire un critère de rejet. Avec 1000 points et une bande à 3 sigma, environ 3 points par unité sortiront par simple hasard, donc np.any signalera une large part des bonnes unités. Exigez une série de points consécutifs hors bande, ou comparez à une bande de tolérance plutôt qu'à un sigma par point. Et l'enveloppe doit être construite sur une période où le process était stable, sinon vous encodez une dérive comme normale.
Analyse de tendance sur les caractéristiques
Extrayez des caractéristiques de chaque forme d'onde (temps de montée, temps d'établissement, dépassement, RMS) et suivez leur tendance. Une augmentation progressive du temps de montée suggère une dérive de composant ou de processus.
C'est généralement l'analyse la plus rentable des trois, et la moins coûteuse à stocker : quelques nombres par unité, qui alimentent tendances et alertes comme toute autre mesure.
Bonnes pratiques
| Pratique | Pourquoi |
|---|---|
| Stocker à la fois la forme d'onde brute et les résumés scalaires | Les formes d'onde pour l'analyse approfondie, les scalaires pour les tableaux de bord et le suivi |
| Utiliser des taux d'échantillonnage cohérents | Comparer des formes d'onde nécessite le même nombre de points et le même timing |
| Inclure les métadonnées de l'axe temporel | Taux d'échantillonnage ou horodatages pour reconstruire la forme d'onde |
| Définir des limites sur les résumés scalaires | Les scalaires déterminent la conformité, les formes d'onde servent à l'analyse des causes |
| Limiter la taille des formes d'onde | Garder les tableaux sous 10 000 points par mesure |
| Capturer les traces brutes sur échec ou par échantillonnage | Chaque unité à pleine cadence dépasse vite son utilité |
