Beetje Coderen

Les 4 · Module 1 · De ESP32 als sensorknooppunt

Meten, filteren en kalibreren

Maak de metingen van het meetstation betrouwbaar: ruis begrijpen, mediaan- en voortschrijdend-gemiddeldefilters, uitschieters weggooien, kalibreren met een referentie en afgeleide waarden zoals het dauwpunt.

⏱ 70 minuten leestijd + oefenen

Illustratie: een grafiek met een golvende en een gladde lijn naast een sensor

🎯 Na deze les kun je

  • ruis en uitschieters in sensorwaarden herkennen
  • een mediaanfilter en een exponentieel voortschrijdend gemiddelde implementeren
  • een sensor kalibreren met een offset ten opzichte van een referentie
  • afgeleide waarden berekenen en afronden op zinvolle precisie

Lees je de sensor tien keer achter elkaar, dan krijg je tien net iets andere waarden. Soms zit er ook een uitschieter tussen. Op een dashboard wil je een rustige, eerlijke lijn. In deze les bouw je lib/filter.py.

Ruis bekijken

import time
from sensor import SHT31
...
for _ in range(20):
    t, rv = s.lees()
    print("%.3f" % t)
    time.sleep_ms(200)

Je ziet waarden die een paar honderdsten van een graad schommelen. De SHT31 is nauwkeurig tot ongeveer ±0,2 °C; het derde cijfer achter de komma is dus vooral ruis.

Mediaanfilter tegen uitschieters

# lib/filter.py
def mediaan(waarden):
    w = sorted(waarden)
    n = len(w)
    m = n // 2
    return w[m] if n % 2 else (w[m - 1] + w[m]) / 2

def meet_mediaan(sensor, n=5, pauze_ms=50):
    import time
    ts, rvs = [], []
    for _ in range(n):
        try:
            t, rv = sensor.lees()
            ts.append(t); rvs.append(rv)
        except Exception:
            pass
        time.sleep_ms(pauze_ms)
    if len(ts) < (n // 2 + 1):
        raise ValueError("te weinig geldige metingen")
    return mediaan(ts), mediaan(rvs)

Voorspel eerst: vijf metingen zijn 21.1, 21.2, 85.0, 21.1, 21.2. Wat geeft het gemiddelde, en wat de mediaan?

Uitkomst

Gemiddelde: 33,9, een flinke vertekening door één fout. Mediaan: 21,2: de middelste van de gesorteerde waarden, onaangetast door één uitschieter. Een mediaan van 5 metingen overleeft tot 2 uitschieters. Daarom meet je per "meting" een paar keer snel achter elkaar en neem je de mediaan.

Glad maken over de tijd: EMA

Tussen metingen (bijv. elke 10 s) wil je de lijn rustig houden zonder alles te onthouden. Een exponentieel voortschrijdend gemiddelde:

class EMA:
    def __init__(self, alfa=0.3):
        self.alfa = alfa
        self.waarde = None
    def update(self, x):
        self.waarde = x if self.waarde is None else self.alfa * x + (1 - self.alfa) * self.waarde
        return self.waarde

Een kleine alfa (0,1) geeft een rustige maar trage lijn; een grote (0,7) volgt snel maar laat meer ruis door. Eén getal geheugen: ideaal voor een microcontroller.

Uitschieters weigeren

Fysiek onmogelijke waarden gooi je weg voordat ze in het filter komen:

def plausibel(t, rv, vorige_t=None):
    if not (-40 <= t <= 85 and 0 <= rv <= 100):
        return False
    if vorige_t is not None and abs(t - vorige_t) > 5:   # 5 °C sprong in 10 s binnenshuis: verdacht
        return False
    return True

Kalibreren

Elke sensor wijkt een beetje af. Leg hem een uur naast een betrouwbare thermometer (op dezelfde plek, uit de zon) en noteer het verschil.

# config.py
KAL_T = -0.4     # sensor meet 0,4 °C te hoog
KAL_RV = 1.5
temp = temp + config.KAL_T

Een offset is meestal genoeg. Wijkt hij bij lage en hoge temperatuur verschillend af, dan meet je op twee punten en gebruik je een lineaire correctie (a * x + b).

Afgeleide waarden

Het dauwpunt zegt meer over "benauwd" of "kans op condens" dan de relatieve vochtigheid (Magnus-formule):

import math
def dauwpunt(t, rv):
    a, b = 17.62, 243.12
    g = math.log(rv / 100) + a * t / (b + t)
    return b * g / (a - g)

Rond af op wat zinvol is: temperatuur op 0,1 °C, vochtigheid op hele procenten. Meer decimalen suggereren een nauwkeurigheid die er niet is.

Typische fouten

  • ValueError: math domain error in dauwpunt: rv is 0 (log van 0). Weiger die meting.
  • EMA "blijft hangen": je maakt elke meting een nieuw EMA-object; maak het één keer buiten de lus.
  • Kalibreren in de zon of naast de laptop: de referentie en de sensor moeten dezelfde lucht meten.

Oefenen

  1. Schrijf lib/filter.py met mediaan, meet_mediaan en EMA.
  2. Test mediaan op je computer met gewone Python en de getallen uit Voorspel eerst.
  3. Vergelijk ruwe en EMA-gefilterde waarden met alfa 0,1 en 0,5 terwijl je op de sensor ademt.
  4. Kalibreer je sensor tegen een referentie en voeg het dauwpunt toe aan de uitvoer.
Modelantwoord oefening 1

Zie de codeblokken. Plaats plausibel ook in filter.py.

Modelantwoord oefening 2
python3 -c "import sys; sys.path.insert(0,'lib'); from filter import mediaan; print(mediaan([21.1,21.2,85.0,21.1,21.2]))"
# 21.2

Pure functies zonder machine kun je zo op je computer testen: sneller dan op het bordje.

Modelantwoord oefening 3

Met alfa=0.5 stijgt de gefilterde lijn binnen twee à drie metingen; met 0.1 duurt het tien metingen of meer. Voor een kamermeetstation is 0,2–0,3 een goede middenweg.

Modelantwoord oefening 4

Bij 21 °C en 50% is het dauwpunt ongeveer 10,2 °C. Toon het met "%.1f" % dauwpunt(t, rv).

Lokaal oefenen

Geen live lab. De filterfuncties kun je met gewone Python op je computer testen; de sensor heb je alleen voor het echte meten nodig.

Samenvatting

  • Meet een paar keer snel en neem de mediaan tegen uitschieters.
  • EMA maakt de lijn over de tijd rustig, met één getal geheugen.
  • Weiger onmogelijke waarden; kalibreer met een offset ten opzichte van een referentie.
  • Rond af op zinvolle precisie; bereken afgeleide waarden als het dauwpunt.
  • Volgende les: een display aansturen.

📝 Quiz: Meten, filteren en kalibreren

5 vragen · 70% nodig om te slagen

1. Wat doet een mediaanfilter goed?
2. Wat is het voordeel van een EMA op een microcontroller?
3. Wat doet een kleine alfa in een EMA?
4. Hoe kalibreer je eenvoudig?
5. Waarom niet afronden op drie decimalen?