Bases · 15 min de lecture

Filtrer les données d'un capteur Arduino : moyenne, médiane, EMA

Filtrer les données d'un capteur Arduino : moyenne glissante, filtre médian, EMA, hystérésis, échantillonnage, traceur série et introduction au Kalman.

Filtrer les données d'un capteur Arduino, c'est transformer les valeurs tremblantes et sautillantes venant d'analogRead() ou d'un capteur de distance en un nombre calme et fiable que ton projet peut utiliser. Voir 511, 513, 510, 514 dans le moniteur série alors que tu ne touches pas le potentiomètre, un HC-SR04 qui mesure de temps en temps 3 cm ou 400 cm sans raison, une veilleuse qui clignote à la limite entre jour et nuit : ce sont les différentes facettes d'un même problème. La plupart du temps, le capteur n'est pas en panne ; du bruit électrique, des erreurs de pas du CAN ou quelques faux échos se sont glissés dans la mesure. Bonne nouvelle : un filtre de quelques lignes règle la plupart de ces problèmes.

Dans ce guide, tu verras d'abord les types de bruit et les premières mesures à prendre côté circuit. Ensuite, on codera cinq méthodes, en commençant par la plus simple : la moyenne de plusieurs échantillons, la moyenne glissante (moving average), le filtre médian qui supprime les pics, le filtre exponentiel (EMA) très économe en mémoire et l'hystérésis, qui calme les seuils instables. On les tracera côte à côte dans le traceur série (Serial Plotter) de l'Arduino IDE, on verra comment choisir la fréquence d'échantillonnage et on fera une courte introduction au filtre de Kalman. Les dernières parties traitent du CAN bruité de l'ESP32 et du code MicroPython pour le Raspberry Pi Pico.

Capteur de distance à ultrasons HC-SR04
Des capteurs comme le HC-SR04 donnent parfois une mesure erronée ; le filtre médian supprime ces pics.

Pourquoi les données d'un capteur sont-elles bruitées ?

Voir l'erreur de mesure comme une seule chose est trompeur. Chaque type de bruit demande un filtre différent, il faut donc d'abord savoir quel est ton problème. Pour cela, il suffit d'afficher un moment les valeurs brutes dans le moniteur série et de regarder les nombres. Si les valeurs tremblent sans arrêt autour d'un nombre, c'est du bruit aléatoire ; si la plupart des mesures sont justes mais qu'une valeur absurde arrive de temps en temps, ce sont des pics (spikes) ; si la valeur glisse lentement, le problème n'est pas du bruit mais une dérive.

Type de bruitÀ quoi il ressembleCause typiqueSolution adaptée
Bruit aléatoireLa valeur tremble de ±2 à 10 pointsAlimentation instable, fils longs, bruit propre du CANMoyenne, moyenne glissante, EMA
Pic (spike)De temps en temps une valeur très différenteÉcho manqué sur le HC-SR04, faux contact, parasites de moteurFiltre médian
Erreur de pas (quantification)La valeur oscille entre deux nombres voisinsRésolution du CAN (5 V / 1024 = 4,9 mV sur l'Uno)Moyenne, hystérésis
Bruit périodiqueOndulation régulièreScintillement à 100 Hz des lampes, secteur 50 HzMoyenne sur des multiples de 20 ms
Dérive (drift)La valeur change lentement en quelques minutesTempérature, tension de batterie qui baisseÉtalonnage et référence stable plutôt qu'un filtre

D'abord le circuit : réduire le bruit à la source

Un filtre logiciel ne peut pas retrouver une information que le circuit n'a jamais captée ; la première étape consiste donc à compliquer la vie du bruit. Garde des fils courts pour les capteurs analogiques et fais-les passer loin des fils des composants gourmands comme les moteurs et les relais. Un condensateur céramique de 100 nF entre l'entrée analogique et le GND, près du capteur, absorbe une grande partie des parasites rapides. La fréquence de coupure d'un filtre passe-bas RC fait d'une résistance et d'un condensateur se calcule avec f = 1 / (2πRC) : 10 kΩ et 1 µF donnent environ 16 Hz, les variations plus rapides sont donc atténuées. Tu peux calculer les valeurs avec l'outil constante de temps RC. Dans les projets avec moteurs, il aide aussi de ramener le GND des capteurs vers la carte par un chemin séparé de celui du pilote moteur et d'alimenter la carte avec une source stable. On explique le fonctionnement du CAN et l'effet de la tension de référence dans Qu'est-ce qu'un CAN ?.

Montage de test

On testera le code de cet article avec trois capteurs simples. Le potentiomètre est idéal pour voir le bruit aléatoire, la LDR pour le bruit périodique et les décisions à seuil, le HC-SR04 pour les pics. Branche la LDR entre le 5 V et la broche analogique, et la résistance de 10 kΩ entre la broche analogique et le GND ; ainsi la valeur lue augmente avec la lumière. Les détails sont dans les articles sur le capteur de lumière LDR et le potentiomètre.

LiaisonArduino UnoESP32Raspberry Pi Pico
Curseur du potentiomètreA0GPIO 34GP26 (ADC0)
Point milieu LDR + pont de 10 kΩA1GPIO 35GP27 (ADC1)
HC-SR04 TRIGD9GPIO 5GP2
HC-SR04 ECHOD10GPIO 18 (avec pont)GP3 (avec pont)
LED ou relais (exemple d'hystérésis)D8--
AlimentationPot et LDR 5V, HC-SR04 5VPot et LDR 3V3, HC-SR04 5VPot et LDR 3V3, HC-SR04 VBUS
Sur l'ESP32 et le Pico, branche le potentiomètre et le pont de la LDR sur 3V3 ; les broches analogiques ne doivent jamais recevoir plus de 3,3 V. Le HC-SR04 fonctionne en 5 V et sa sortie ECHO donne 5 V ; sur l'ESP32 et le Pico, ramène donc la broche ECHO à 3,3 V avec un pont diviseur de 1 kΩ et 2 kΩ. Tu peux vérifier les valeurs du pont avec l'outil Pont diviseur.

1. Moyenne de plusieurs échantillons

Le filtre le plus simple consiste à prendre plusieurs mesures d'affilée et à utiliser leur moyenne. Le bruit aléatoire pousse chaque mesure dans une direction différente, il s'annule donc dans la somme. La statistique a une règle pour cela : dans la moyenne de N mesures indépendantes, le bruit diminue d'environ √N. 4 échantillons divisent le bruit par deux, 16 par quatre ; 64 le divisent par huit mais prennent quatre fois plus de temps. Sur l'Arduino Uno, un analogRead() prend environ 112 µs, donc une moyenne de 16 échantillons est faite en moins de 2 ms. Ses seuls défauts : elle occupe brièvement le processeur à chaque mesure et résiste mal aux pics, car si l'un des 16 échantillons est absurde, il fausse aussi la moyenne.

C++ (Arduino IDE)
1const int POT_PIN = A0;
2
3// Art arda "adet" ölçüm al, ortalamasını döndür / take "adet" readings in a row, return the average
4int ortalamaOku(int pin, int adet) {
5  long toplam = 0;                  // taşmasın diye long / long to avoid overflow
6  for (int i = 0; i < adet; i++) {
7    toplam += analogRead(pin);
8    delayMicroseconds(100);         // ölçümler arasında kısa ara / short gap between readings
9  }
10  return toplam / adet;
11}
12
13void setup() {
14  Serial.begin(115200);
15}
16
17void loop() {
18  int ham = analogRead(POT_PIN);
19  int ort4 = ortalamaOku(POT_PIN, 4);
20  int ort16 = ortalamaOku(POT_PIN, 16);
21  // Serial Plotter için etiket:değer biçimi / label:value format for the Serial Plotter
22  Serial.print("Ham:");
23  Serial.print(ham);
24  Serial.print(",Ort4:");
25  Serial.print(ort4);
26  Serial.print(",Ort16:");
27  Serial.println(ort16);
28  delay(20);
29}

2. Moyenne glissante (moving average)

La moyenne glissante garde la moyenne des N dernières mesures et, à chaque nouvelle mesure, retire la plus ancienne et ajoute la nouvelle. Tu ne fais ainsi qu'une mesure par tour, tout en profitant d'une moyenne sur N mesures. Au lieu d'additionner N nombres à chaque fois, le code tient une variable somme : il retranche la valeur qui sort et ajoute celle qui entre. Le calcul est donc aussi rapide quelle que soit la taille de la fenêtre. Le prix à payer est le retard : la sortie a environ (N−1)/2 mesures de retard sur la valeur réelle. Une fenêtre de 10 avec une mesure toutes les 10 ms retarde d'environ 45 ms ; pour un potentiomètre ou une température c'est négligeable, pour un robot rapide ça se sent.

C++ (Arduino IDE)
1const int POT_PIN = A0;
2const int PENCERE = 10;            // son 10 ölçüm / last 10 readings
3
4int tampon[PENCERE];               // dairesel tampon / circular buffer
5int sira = 0;
6long toplam = 0;
7bool dolu = false;
8
9int hareketliOrtalama(int yeni) {
10  toplam -= tampon[sira];          // en eski değeri çıkar / remove the oldest value
11  tampon[sira] = yeni;
12  toplam += yeni;                  // yeni değeri ekle / add the new value
13  sira = (sira + 1) % PENCERE;
14  if (sira == 0) dolu = true;
15  return dolu ? toplam / PENCERE : toplam / sira;   // ilk turda bölen küçük / smaller divisor at start
16}
17
18unsigned long sonOlcum = 0;
19
20void setup() {
21  Serial.begin(115200);
22}
23
24void loop() {
25  if (millis() - sonOlcum >= 10) { // 100 Hz örnekleme / 100 Hz sampling
26    sonOlcum = millis();
27    int ham = analogRead(POT_PIN);
28    int filtreli = hareketliOrtalama(ham);
29    Serial.print("Ham:");
30    Serial.print(ham);
31    Serial.print(",Hareketli:");
32    Serial.println(filtreli);
33  }
34}
Fenêtre (N)Réduction du bruitRetard (une mesure toutes les 10 ms)Usage adapté
4Environ 2 fois15 msRégulation qui doit réagir vite
10Environ 3 fois45 msPotentiomètre, joystick, LDR
32Environ 5,7 fois155 msTempérature, humidité, tension de batterie
10010 fois495 msValeurs très lentes, affichage calme

3. Filtre médian : supprimer les pics

La moyenne résiste mal aux pics. Si le HC-SR04 mesure quatre fois 50 cm puis rate l'écho une fois et mesure 0, la moyenne des cinq vaut 40 cm ; cette valeur n'est ni juste ni révélatrice de l'erreur. Le filtre médian, lui, trie les N dernières mesures de la plus petite à la plus grande et prend celle du milieu. Dans le même exemple, la liste triée donne 0, 50, 50, 50, 50 et la valeur centrale est 50 ; la mesure erronée n'atteint jamais le résultat. Un filtre médian sur 5 éléments supprime complètement jusqu'à 2 mesures erronées consécutives. Second atout de la médiane : elle n'étale pas un changement brusque et réel comme le fait la moyenne. Quand un obstacle s'approche vraiment, la valeur passe au nouveau niveau par une marche nette en quelques mesures au lieu d'une courbe douce. Le code ci-dessous mesure avec le HC-SR04 toutes les 60 ms et affiche la médiane des 5 dernières mesures. Pour les détails, vois l'article sur le capteur de distance HC-SR04.

C++ (Arduino IDE)
1const int TRIG_PIN = 9;
2const int ECHO_PIN = 10;
3const int N = 5;                   // medyan penceresi, tek sayı olmalı / median window, must be odd
4
5int olcumler[N];
6int sira = 0;
7
8int mesafeOlc() {
9  digitalWrite(TRIG_PIN, LOW);
10  delayMicroseconds(2);
11  digitalWrite(TRIG_PIN, HIGH);
12  delayMicroseconds(10);
13  digitalWrite(TRIG_PIN, LOW);
14  unsigned long sure = pulseIn(ECHO_PIN, HIGH, 25000);   // ~4 m zaman aşımı / ~4 m timeout
15  return sure / 58;                                       // yankı yoksa 0 / 0 if no echo
16}
17
18int medyan() {
19  int kopya[N];
20  for (int i = 0; i < N; i++) kopya[i] = olcumler[i];
21  for (int i = 1; i < N; i++) {    // eklemeli sıralama / insertion sort
22    int deger = kopya[i];
23    int j = i - 1;
24    while (j >= 0 && kopya[j] > deger) {
25      kopya[j + 1] = kopya[j];
26      j--;
27    }
28    kopya[j + 1] = deger;
29  }
30  return kopya[N / 2];             // ortadaki eleman / middle element
31}
32
33void setup() {
34  Serial.begin(115200);
35  pinMode(TRIG_PIN, OUTPUT);
36  pinMode(ECHO_PIN, INPUT);
37  for (int i = 0; i < N; i++) {    // tamponu ilk ölçümlerle doldur / fill the buffer first
38    olcumler[i] = mesafeOlc();
39    delay(60);
40  }
41}
42
43void loop() {
44  int ham = mesafeOlc();
45  olcumler[sira] = ham;
46  sira = (sira + 1) % N;
47  Serial.print("Ham:");
48  Serial.print(ham);
49  Serial.print(",Medyan:");
50  Serial.println(medyan());
51  delay(60);                       // HC-SR04 için en az 60 ms / at least 60 ms for HC-SR04
52}
Tu peux aussi combiner médiane et moyenne : supprime d'abord les pics avec une médiane sur 5, puis passe le résultat dans une moyenne glissante ou une EMA pour lisser le tremblement restant. Ce duo est la solution la plus courante pour les mesures qui ont à la fois des pics et du tremblement, comme celles des capteurs de distance et de débit. Ne fais pas une fenêtre médiane trop grande ; 3 ou 5 suffisent pour presque tout, 9 et plus ajoutent du retard pour rien.

4. Filtre exponentiel (EMA) : lisser avec peu de mémoire

La moyenne mobile exponentielle (Exponential Moving Average, EMA) n'utilise aucun tampon ; elle ne retient que le résultat précédent. À chaque nouvelle mesure, le résultat se rapproche de la mesure d'une petite fraction de l'écart entre les deux : y = y + α × (x − y). Ici x est la nouvelle mesure, y la valeur filtrée et α (alpha) un coefficient entre 0 et 1. Plus α est petit, plus le filtre est calme mais lent. On l'appelle exponentiel parce que l'influence de chaque mesure s'efface de façon exponentielle avec le temps. C'est l'équivalent logiciel du filtre passe-bas RC en électronique. La constante de temps de l'EMA vaut environ τ ≈ T / α (T est l'intervalle d'échantillonnage) : avec une mesure toutes les 10 ms et α = 0,1, le filtre atteint 63 pour cent d'un changement brusque en environ 100 ms et 95 pour cent en environ 300 ms. Le code propose une version en virgule flottante et une version entière plus rapide sur les petites cartes comme l'Uno ; la version entière utilise α = 1/16 et un décalage (>>) au lieu d'une division.

C++ (Arduino IDE)
1const int LDR_PIN = A1;
2const float ALFA = 0.1;            // 0-1 arası; küçük = daha sakin / 0-1; smaller = smoother
3
4float ema = -1;                    // henüz başlamadı / not started yet
5long emaX16 = -1;                  // tamsayılı EMA x16 / integer EMA times 16
6
7unsigned long sonOlcum = 0;
8
9void setup() {
10  Serial.begin(115200);
11}
12
13void loop() {
14  if (millis() - sonOlcum >= 10) {
15    sonOlcum = millis();
16    int ham = analogRead(LDR_PIN);
17
18    if (ema < 0) ema = ham;        // ilk değerle başlat / start from the first reading
19    ema += ALFA * (ham - ema);     // y = y + a(x - y)
20
21    if (emaX16 < 0) emaX16 = (long)ham << 4;
22    emaX16 += ham - (emaX16 >> 4); // alfa = 1/16, float yok / alpha = 1/16, no float
23
24    Serial.print("Ham:");
25    Serial.print(ham);
26    Serial.print(",EMA:");
27    Serial.print(ema, 1);
28    Serial.print(",EMA_int:");
29    Serial.println(emaX16 >> 4);
30  }
31}
α (alpha)Fenêtre équivalente approximativeTemps de réponse à 63 % (échantillonnage 10 ms)Caractère
0,5310–20 msTrès rapide, peu de lissage
0,2950 msRapide, lissage léger
0,119100 msÉquilibré, bon point de départ pour la plupart des capteurs
0,0539200 msCalme, lent
0,011991 sTrès calme, seulement pour des valeurs lentes

La « fenêtre équivalente » du tableau est la taille approximative d'une moyenne glissante qui donne le même lissage ; elle se calcule par N ≈ 2/α − 1. Le grand avantage de l'EMA est la mémoire : une moyenne glissante de 100 éléments demande 200 octets de RAM, l'EMA une seule variable. Si tu dois filtrer plusieurs capteurs dans les 2 Ko de RAM de l'Arduino Uno, la différence compte. Son défaut : elle résiste aussi mal que la moyenne aux pics ; un gros pic tire le résultat pendant plusieurs mesures.

Comparer les filtres dans le traceur série

La différence entre les filtres se voit mal dans les nombres, mais saute aux yeux sur un graphique. Le code ci-dessous lit le potentiomètre à 100 Hz, fait passer la même mesure par quatre chemins différents et affiche le tout au format étiquette:valeur. Pour rendre la différence plus nette, le code ajoute un pic artificiel à environ 3 pour cent des mesures ; dans un vrai projet, supprime cette ligne. Sur le graphique, tu verras que la médiane efface complètement les pics, tandis que la moyenne glissante et l'EMA les transforment en petites bosses.

  1. Téléverse le code et ferme le moniteur série s'il est ouvert ; le moniteur série et le traceur série ne peuvent pas être ouverts en même temps.
  2. Dans l'Arduino IDE, ouvre Outils > Traceur série ou clique sur l'icône de graphique en haut à droite.
  3. Règle la vitesse en bas à droite sur 115200 bauds.
  4. Tourne le potentiomètre lentement puis vite, et observe la réaction de chaque courbe.
  5. Active et désactive les courbes avec les cases à côté des étiquettes pour comparer directement deux filtres.
C++ (Arduino IDE)
1const int POT_PIN = A0;
2
3// Hareketli ortalama / moving average
4const int PENCERE = 10;
5int maTampon[PENCERE];
6int maSira = 0;
7long maToplam = 0;
8
9int hareketliOrtalama(int yeni) {
10  maToplam += yeni - maTampon[maSira];
11  maTampon[maSira] = yeni;
12  maSira = (maSira + 1) % PENCERE;
13  return maToplam / PENCERE;
14}
15
16// 5'lik medyan / median of 5
17int mdTampon[5];
18int mdSira = 0;
19
20int medyan5(int yeni) {
21  mdTampon[mdSira] = yeni;
22  mdSira = (mdSira + 1) % 5;
23  int a[5];
24  for (int i = 0; i < 5; i++) a[i] = mdTampon[i];
25  for (int i = 1; i < 5; i++) {    // eklemeli sıralama / insertion sort
26    int d = a[i], j = i - 1;
27    while (j >= 0 && a[j] > d) { a[j + 1] = a[j]; j--; }
28    a[j + 1] = d;
29  }
30  return a[2];
31}
32
33// Üstel filtre / exponential filter
34float ema = 0;
35const float ALFA = 0.1;
36
37unsigned long sonOlcum = 0;
38
39void setup() {
40  Serial.begin(115200);
41  int ilk = analogRead(POT_PIN);
42  for (int i = 0; i < PENCERE; i++) maTampon[i] = ilk;   // tamponları doldur / prefill buffers
43  maToplam = (long)ilk * PENCERE;
44  for (int i = 0; i < 5; i++) mdTampon[i] = ilk;
45  ema = ilk;
46  randomSeed(analogRead(A5));
47}
48
49void loop() {
50  if (millis() - sonOlcum < 10) return;   // 100 Hz
51  sonOlcum = millis();
52
53  int ham = analogRead(POT_PIN);
54  if (random(100) < 3) {                  // TEST: %3 yapay sıçrama, projede sil / TEST: 3% fake spikes, remove in a project
55    ham = constrain(ham + random(-400, 400), 0, 1023);
56  }
57  ema += ALFA * (ham - ema);
58
59  Serial.print("Ham:");
60  Serial.print(ham);
61  Serial.print(",Hareketli:");
62  Serial.print(hareketliOrtalama(ham));
63  Serial.print(",Medyan:");
64  Serial.print(medyan5(ham));
65  Serial.print(",EMA:");
66  Serial.println(ema, 1);
67}

Sur le graphique, les étiquettes apparaissent en couleur en haut à droite. Sans virgule entre les étiquettes et sans deux-points entre étiquette et valeur, le traceur série ne peut pas séparer correctement les courbes. L'ancien traceur de l'Arduino IDE 1.8 gère mal les étiquettes ; en cas de souci, envoie seulement des nombres séparés par des virgules. Pour les problèmes de port série et de vitesse en bauds, jette un œil à l'article sur le moniteur série Arduino.

5. Hystérésis : stopper les décisions qui clignotent au seuil

Le filtre calme la valeur, mais il ne règle pas à lui seul le problème de décision. Une veilleuse qui dit « allume la lampe quand la lumière passe sous 350 » s'allume et s'éteint plusieurs fois par seconde au crépuscule, tandis que la valeur oscille entre 349 et 351. Cela use vite les contacts d'un relais. La solution est l'hystérésis : on utilise deux seuils distincts pour allumer et pour éteindre. Par exemple, la lampe s'allume quand la valeur passe sous 300, mais pour s'éteindre elle doit remonter au-dessus de 400. La « zone morte » de 100 points entre les deux empêche les petites fluctuations de changer la décision. Le code ci-dessous associe l'hystérésis à une EMA et à un court délai de confirmation : la condition doit être remplie sans interruption pendant 1 seconde, de sorte qu'une ombre qui passe ou un éclat de phares ne change pas non plus la décision.

C++ (Arduino IDE)
1const int LDR_PIN = A1;
2const int LAMBA_PIN = 8;           // LED ya da röle modülü / LED or relay module
3const int YAK_ESIK = 300;          // bunun altı karanlık / below this is dark
4const int SONDUR_ESIK = 400;       // bunun üstü aydınlık / above this is bright
5const unsigned long ONAY_MS = 1000;   // koşul 1 sn sürmeli / condition must last 1 s
6
7float ema = -1;
8bool lambaAcik = false;
9unsigned long kosulBasladi = 0;
10bool kosulVar = false;
11
12void setup() {
13  Serial.begin(115200);
14  pinMode(LAMBA_PIN, OUTPUT);
15}
16
17void loop() {
18  int ham = analogRead(LDR_PIN);
19  if (ema < 0) ema = ham;
20  ema += 0.05 * (ham - ema);       // yavaş EMA / slow EMA
21
22  // Durum değiştirmek için hangi koşul gerekiyor? / which condition flips the state?
23  bool degistir = lambaAcik ? (ema > SONDUR_ESIK) : (ema < YAK_ESIK);
24
25  if (degistir) {
26    if (!kosulVar) {
27      kosulVar = true;
28      kosulBasladi = millis();     // koşul şimdi başladı / condition just started
29    } else if (millis() - kosulBasladi >= ONAY_MS) {
30      lambaAcik = !lambaAcik;      // 1 sn sürdü, kararı değiştir / lasted 1 s, flip the decision
31      digitalWrite(LAMBA_PIN, lambaAcik ? HIGH : LOW);
32      kosulVar = false;
33    }
34  } else {
35    kosulVar = false;              // koşul bozuldu, sayacı sıfırla / condition broke, reset the timer
36  }
37
38  Serial.print("EMA:");
39  Serial.print(ema, 0);
40  Serial.print(",Yak:");
41  Serial.print(YAK_ESIK);
42  Serial.print(",Sondur:");
43  Serial.print(SONDUR_ESIK);
44  Serial.print(",Lamba:");
45  Serial.println(lambaAcik ? 600 : 100);   // grafikte görünsün / visible on the plot
46  delay(10);
47}

Avant de fixer les seuils, observe les valeurs de la pièce au fil de la journée dans le traceur série. La zone morte doit être au moins deux à trois fois plus large que le tremblement de la valeur filtrée. La même méthode sert dans les thermostats (par exemple couper le chauffage à 22 °C et le rallumer à 20 °C), les pompes de réservoir d'eau et les circuits de protection de batterie. L'anti-rebond des boutons est d'ailleurs une forme de délai de confirmation ; les détails sont dans Comment brancher un bouton.

Comment choisir la fréquence d'échantillonnage ?

Pour qu'un filtre fonctionne bien, il faut lire le capteur au bon rythme. Trop lent, tu rates les changements rapides ; trop rapide, le capteur renvoie sans cesse la même valeur ou n'est même pas prêt. La règle de base est le critère de Nyquist : échantillonne au moins deux fois plus vite que le changement le plus rapide que tu veux suivre ; en pratique, 5 à 10 fois est plus sûr. Certains capteurs ont en plus leur propre limite, et les lire plus vite ne sert à rien. Attention à l'éclairage : sur un réseau 50 Hz, les lampes scintillent 100 fois par seconde. Le moyen le plus simple d'annuler ce scintillement sur une LDR ou un autre capteur analogique est de prendre des mesures régulièrement espacées pendant une durée multiple de 20 ms et d'en faire la moyenne ; comme la fenêtre couvre des périodes entières du secteur, le scintillement s'annule de lui-même.

CapteurIntervalle de lecture conseilléRemarque
DHT11 / DHT221 s / 2 sPlus souvent, tu obtiens la même valeur ou une erreur
DS18B20 (12 bits)750 msC'est le temps de conversion
HC-SR04Au moins 60 msIl faut attendre que l'écho précédent s'éteigne
Potentiomètre, joystick10–20 ms50–100 Hz suffisent largement pour des mouvements humains
LDR (contre le scintillement des lampes)Toutes les 1 ms pendant 20 ms, puis moyenneLe scintillement du secteur 50 Hz s'annule
MPU60505–10 msPlage courante pour mesurer un mouvement à 100–200 Hz

Courte introduction au filtre de Kalman

Le filtre de Kalman tient compte du bruit de la mesure et de la vitesse à laquelle la vraie valeur peut changer, et règle lui-même son gain. Dans sa forme unidimensionnelle la plus simple, il a deux réglages : R est la variance du bruit de mesure (à quel point le capteur tremble) ; Q est le bruit de processus (à quelle vitesse tu t'attends à voir changer la vraie valeur). À chaque étape, le filtre augmente d'abord son incertitude de Q, calcule ensuite le gain K = P / (P + R) et déplace son estimation vers la mesure dans la proportion K. Si la mesure est bruitée (R grand), K est petit et le filtre se fie peu à la mesure ; si l'on s'attend à un changement rapide (Q grand), K augmente et le filtre réagit vite. Résultat frappant : quand Q et R sont fixes, K se stabilise au bout d'un moment sur une valeur constante et le Kalman unidimensionnel devient en pratique une EMA. La vraie force de Kalman apparaît quand il estime plusieurs grandeurs à la fois, par exemple en combinant accéléromètre et gyroscope pour trouver un angle d'inclinaison. Tu trouveras son cousin plus simple, le filtre complémentaire, dans l'article sur le MPU6050.

C++ (Arduino IDE)
1const int POT_PIN = A0;
2
3struct Kalman1D {
4  float q;      // süreç gürültüsü / process noise
5  float r;      // ölçüm gürültüsü (varyans) / measurement noise (variance)
6  float p;      // tahminin belirsizliği / estimate uncertainty
7  float x;      // tahmin / estimate
8  bool ilk;
9};
10
11Kalman1D kf = {0.05, 9.0, 1.0, 0.0, true};   // r = 9: yaklaşık ±3 basamak titreşim / about ±3 counts of jitter
12
13float kalmanGuncelle(Kalman1D &k, float olcum) {
14  if (k.ilk) { k.x = olcum; k.ilk = false; return k.x; }
15  k.p += k.q;                          // 1. tahmin: belirsizlik artar / predict: uncertainty grows
16  float kazanc = k.p / (k.p + k.r);    // 2. Kalman kazancı / Kalman gain
17  k.x += kazanc * (olcum - k.x);       // 3. ölçüme doğru düzelt / correct toward the measurement
18  k.p *= (1.0 - kazanc);               // 4. belirsizlik azalır / uncertainty shrinks
19  return k.x;
20}
21
22void setup() {
23  Serial.begin(115200);
24}
25
26void loop() {
27  int ham = analogRead(POT_PIN);
28  float tahmin = kalmanGuncelle(kf, ham);
29  Serial.print("Ham:");
30  Serial.print(ham);
31  Serial.print(",Kalman:");
32  Serial.println(tahmin, 1);
33  delay(10);
34}

R est facile à estimer : prends 100 mesures brutes sans toucher le potentiomètre et regarde leur dispersion. Si elles varient d'environ ±3 points autour de la moyenne, l'écart type vaut environ 3 et la variance environ 9. Q se trouve par essais : un petit Q (0,001) donne une sortie très calme mais lente, un grand Q (1) une sortie rapide mais plus tremblante. Pour commencer, choisis Q entre 1 et 10 pour cent de R et affine-le dans le traceur série en tournant le potentiomètre.

Bruit du CAN et filtrage sur l'ESP32

Le CAN 12 bits de l'ESP32 mesure plus finement que celui de l'Arduino Uno, mais il est plus bruité et non linéaire, surtout aux extrémités 0–0,1 V et 3,1–3,3 V. Avec le Wi-Fi actif, le bruit augmente encore. Deux choses te simplifient la vie : utiliser analogReadMilliVolts(), qui applique l'étalonnage d'usine de la puce au lieu de renvoyer une valeur brute, et moyenner 16 à 64 échantillons par mesure avant d'appliquer une EMA. Le code ci-dessous lit le potentiomètre selon ces deux étapes ; dans les projets avec Wi-Fi, branche toujours le capteur sur les broches ADC1, GPIO 32–39.

C++ (Arduino IDE)
1const int POT_PIN = 34;            // ADC1 pini, Wi-Fi ile çalışır / ADC1 pin, works with Wi-Fi
2const int ORNEK = 32;              // her ölçümde 32 örnek / 32 samples per reading
3const float ALFA = 0.15;
4
5float ema = -1;
6
7int millivoltOku() {
8  uint32_t toplam = 0;
9  for (int i = 0; i < ORNEK; i++) {
10    toplam += analogReadMilliVolts(POT_PIN);   // kalibre mV / calibrated mV
11  }
12  return toplam / ORNEK;
13}
14
15void setup() {
16  Serial.begin(115200);
17  analogReadResolution(12);        // 0-4095
18}
19
20void loop() {
21  int ham = analogRead(POT_PIN);   // karşılaştırma için tek ham ölçüm / single raw reading for comparison
22  int mv = millivoltOku();
23  if (ema < 0) ema = mv;
24  ema += ALFA * (mv - ema);
25  Serial.printf("Ham:%d,mV:%d,EMA_mV:%.1f\n", ham, mv, ema);
26  delay(20);
27}

Filtrage avec le Raspberry Pi Pico et MicroPython

Sur le Raspberry Pi Pico, ADC.read_u16() renvoie une valeur entre 0 et 65535, mais la vraie résolution du CAN est de 12 bits et les bits de poids faible sont bruités. En MicroPython, la fonction sorted() rend la médiane très facile, et pour la moyenne glissante une liste et une somme courante suffisent. Le code ci-dessous lit le potentiomètre à 100 Hz et affiche la sortie de trois filtres dans un format que Thonny peut tracer dans sa fenêtre Affichage > Traceur (Plotter).

MicroPython
1from machine import ADC
2import time
3
4pot = ADC(26)                     # GP26 = ADC0
5
6PENCERE = 10
7tampon = [pot.read_u16()] * PENCERE   # hareketli ortalama tamponu / moving average buffer
8toplam = sum(tampon)
9sira = 0
10son5 = tampon[:5]                 # medyan için son 5 değer / last 5 values for the median
11ema = float(tampon[0])
12ALFA = 0.1
13
14while True:
15    ham = pot.read_u16()
16
17    toplam += ham - tampon[sira]  # hareketli ortalama / moving average
18    tampon[sira] = ham
19    sira = (sira + 1) % PENCERE
20    hareketli = toplam // PENCERE
21
22    son5.pop(0)                   # medyan / median
23    son5.append(ham)
24    medyan = sorted(son5)[2]
25
26    ema += ALFA * (ham - ema)     # üstel filtre / exponential filter
27
28    print("Ham:", ham, "Hareketli:", hareketli, "Medyan:", medyan, "EMA:", int(ema))
29    time.sleep_ms(10)

Le traceur de Thonny affiche les nombres de chaque ligne comme des courbes séparées et utilise le texte placé devant les nombres comme étiquette. MicroPython est plus lent qu'Arduino, ne pousse donc pas les filtres à des fréquences trop élevées ; environ 100 Hz avec de petites fenêtres tournent sans peine sur le Pico. Si tu débutes en MicroPython, commence par l'article bases de MicroPython.

Quel filtre choisir ?

SituationFiltre conseilléPourquoi ?
Un potentiomètre, un joystick ou un capteur analogique trembleMoyenne glissante ou EMA (α = 0,1–0,2)Simple, rapide, lissage suffisant
Une valeur absurde arrive de temps en temps (HC-SR04, débitmètre)Médiane sur 3 ou 5Supprime complètement les erreurs isolées
Pics et tremblement à la foisMédiane puis EMARègle les deux problèmes l'un après l'autre
Très peu de RAM, beaucoup de capteursEMA entièreUne variable par capteur
Une décision marche-arrêt clignote au seuilHystérésis et délai de confirmationRègle le problème de décision ; un filtre seul ne suffit pas
Ondulation due aux lampes ou au secteurMoyenne régulièrement espacée sur 20 msCouvre une période complète de 50 Hz
Combiner plusieurs capteurs (inclinaison, position)Filtre de Kalman ou complémentaireRéunit les points forts de chaque capteur

Erreurs fréquentes et solutions

SymptômeCause probableSolution
La valeur filtrée réagit beaucoup trop tardFenêtre trop grande ou α trop petitRéduis la fenêtre ou augmente α ; calcule le retard avec les tableaux
La moyenne glissante monte lentement depuis zéro au démarrageLe tampon démarre rempli de zérosPréremplis le tampon avec la première mesure ou divise par le nombre de valeurs jusqu'à ce qu'il soit plein
La moyenne devient folle à 64 échantillonsLa somme int a débordé (64 × 1023 = 65 472 > 32 767)Déclare la variable somme en long
Le filtre médian ne change rienFenêtre de 2 ou paire, ou pics consécutifsUtilise une fenêtre impaire (3, 5, 7) plus grande que le nombre de pics consécutifs
Les courbes du traceur série sont fusionnées ou d'une seule couleurMauvais format d'étiquettesUtilise étiquette:valeur séparés par des virgules et termine la ligne par println
La valeur de l'EMA ne bouge jamaisDivision entière : 1/10 écrit au lieu de 0.1Écris le coefficient en float (0.1) ou utilise la formule d'EMA entière
Les valeurs fluctuent malgré le filtreLe problème n'est pas le bruit mais une dérive ou une alimentation faibleVérifie l'alimentation, ajoute un condensateur de 100 nF et étalonne
Regarde toujours les données brutes avant de choisir un filtre. Observer la valeur brute quelques minutes dans le traceur série montre tout de suite si le problème vient du tremblement, des pics, d'une ondulation périodique ou d'une dérive. Commence ensuite par le filtre le plus simple et ne passe au suivant qu'en cas de besoin. Dans la plupart des projets, une médiane sur 5 suivie d'une EMA avec α = 0,1 marche mieux qu'un Kalman compliqué et se comprend bien plus facilement.

Filtrer les données des capteurs est le moyen le moins cher d'obtenir des mesures fiables : aucune pièce en plus, et quelques lignes de code rendent tes projets bien plus stables. Maintenant que tu sais combiner moyenne, moyenne glissante, médiane, EMA et hystérésis, les affichages qui tremblent, les fausses alarmes et les relais qui claquent sont derrière toi. Dans l'application BlueGrays, tu peux explorer des projets comme la mesure de lumière avec une LDR, une veilleuse, la mesure de distance avec le HC-SR04, un radar de recul et une toise électronique, avec des schémas pas à pas et du code prêt à l'emploi, puis constater toi-même le résultat en y ajoutant ces filtres.

Questions fréquentes

Pourquoi les valeurs d'un capteur Arduino changent-elles sans arrêt ?

Dans les mesures analogiques, les variations de l'alimentation, les fils longs, les moteurs proches et le bruit propre du CAN font bouger la valeur de quelques points. C'est normal et ne veut pas dire que le capteur est en panne. Un condensateur de 100 nF sur l'entrée analogique et un filtre de moyenne ou EMA dans le logiciel calment les valeurs.

Qu'est-ce qu'une moyenne glissante ?

Une moyenne glissante est la moyenne des N dernières mesures ; à chaque nouvelle mesure, la plus ancienne est retirée et la nouvelle ajoutée. Elle réduit le bruit d'environ √N mais retarde la sortie de (N−1)/2 mesures. Sur Arduino, elle s'écrit en quelques lignes avec un tampon circulaire et une seule variable de somme.

Quelle différence entre un filtre médian et une moyenne ?

La moyenne tient compte de toutes les valeurs, donc une seule mesure absurde fausse le résultat. Le filtre médian trie les valeurs et prend celle du milieu, il ignore donc complètement les mesures erronées isolées. C'est pourquoi la médiane convient mieux aux capteurs comme le HC-SR04, qui se trompent parfois, et la moyenne aux valeurs analogiques qui tremblent.

Comment choisir le coefficient d'un filtre exponentiel (EMA) ?

Le coefficient α est compris entre 0 et 1 ; un petit α donne une sortie plus calme mais plus lente, un grand α une sortie plus rapide mais plus tremblante. Avec une mesure toutes les 10 ms, α = 0,1 donne une constante de temps d'environ 100 ms et constitue un bon point de départ pour la plupart des capteurs. Tu peux calculer le temps de réponse avec τ ≈ T / α.

Comment tracer plusieurs valeurs dans le traceur série Arduino ?

Affiche sur chaque ligne les valeurs au format étiquette:valeur, séparées par des virgules, et termine la ligne par println, par exemple Ham:512,EMA:509. Le traceur série affiche chaque étiquette comme une courbe de couleur différente. Ferme d'abord le moniteur série s'il est ouvert et règle la même vitesse en bauds que dans le code.

Peut-on utiliser un filtre de Kalman sur Arduino ?

Oui, un filtre de Kalman unidimensionnel simple tient en quelques lignes, même sur un Arduino Uno. Il se règle avec le bruit de mesure R et le bruit de processus Q ; pour un seul capteur, ses résultats sont souvent très proches d'une EMA bien réglée. Son vrai intérêt apparaît quand on combine plusieurs capteurs, comme un accéléromètre et un gyroscope.

Voir tous les projets
Mesurer l'intensité lumineuse avec une LDR
#11Débutant

Mesurer l'intensité lumineuse avec une LDR

Envie de voir en chiffres à quel point ta chambre est lumineuse ? Le LDR, une petite pièce sensible à la lumière, mesure la lumière ambiante, et l'Arduino écrit la valeur sur l'écran de ton ordinateur

4.6(81)
140
HC-SR04 Mesure de la distance (affichage en série)
#27Débutant

HC-SR04 Mesure de la distance (affichage en série)

Le capteur HC-SR04 fonctionne comme une chauve-souris qui trouve son chemin dans le noir : il envoie un son que tu ne peux pas entendre, écoute l'écho et mesure à quelle distance se trouve l'objet dev

4.6(47)
74
Veilleuse avec LDR
#12Débutant

Veilleuse avec LDR

Une petite veilleuse qui s'allume toute seule quand il fait nuit et s'éteint au matin. Un capteur de lumière mesure la clarté de la pièce, et l'Arduino allume la LED quand il fait assez sombre puis l'

4.8(103)
210
Capteur de stationnement avec HC-SR04
#28Intermédiaire
Premium

Capteur de stationnement avec HC-SR04

Une version miniature du radar de recul des voitures : plus un objet s'approche, plus les bips s'accélèrent. Le capteur de distance mesure l'obstacle devant lui ; au-delà de 20 centimètres, la LED ver

4.8(64)
85
Projet de mesure de la taille
#33Intermédiaire
Premium

Projet de mesure de la taille

Une toise qui se fixe au cadre de la porte et te dit en centimètres la taille de la personne qui se tient dessous. D'abord, tu appuies sur le premier bouton sans personne en dessous et le capteur de d

4.5(25)
34
Utilisation d'un servomoteur avec un potentiomètre
#36Débutant
Premium

Utilisation d'un servomoteur avec un potentiomètre

Quand tu tournes le bouton du potentiomètre, le bras du servomoteur tourne avec lui : un bout du bouton correspond à zéro degré, l'autre à un demi-tour. L'Arduino lit sans arrêt la position du bouton,

4.6(37)
53
Mesure de l'intensité lumineuse avec le module LDR ESP32
#113Débutant

Mesure de l'intensité lumineuse avec le module LDR ESP32

Suivi en temps réel sur le moniteur série de la valeur brute de l'ADC du module LDR et du pourcentage relatif de luminosité calculé à partir de références dans l'obscurité et à la lumière. Ce pourcentage n'est pas une mesure en lux. Permet de comprendre le principe de calibrage du module. Il est préférable de réaliser cet exercice avant le projet 10, car il sert à déterminer la valeur seuil.

4.6(36)
55
ESP32 HC-SR04 Télémètre à ultrasons
#116Intermédiaire

ESP32 HC-SR04 Télémètre à ultrasons

Le capteur à ultrasons HC-SR04 calcule la distance en mesurant le temps de vol (Time of Flight) de l'onde sonore qu'il émet lorsqu'elle rebondit sur un objet. Dans ce projet, nous enverrons une impulsion de 10 microsecondes depuis l'ESP32 vers la broche TRIG du capteur et nous lirons la durée de l'écho provenant de la broche ECHO à l'aide de la fonction pulseIn(). Nous afficherons la distance calculée en cm sur un écran LCD I2C 16x2. Le capteur HC-SR04 fonctionnant en 5 V, un diviseur de ten

4.9(28)
46