IoT und KI

Ausreißererkennung in Sensordaten mit Z-Score, IQR und EWMA

Drei einfache, erklärbare Methoden, um Ausreißer in Sensordatenströmen zu finden – mit Python-Implementierungen, die Sie auf einen Live-MQTT-Temperaturstrom anwenden können, und Hinweisen, wann welche Methode versagt.

Diagramm zur Ausreißererkennung in Sensordaten, das Z-Score-, IQR- und EWMA-Bänder auf einem Temperaturstrom mit markierten Ausreißern vergleicht
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

Ausreißererkennung in Sensordaten braucht kein Machine Learning, um nützlich zu sein. Drei klassische Verfahren – der Z-Score, der Interquartilsabstand (IQR) und der exponentiell gewichtete gleitende Mittelwert (EWMA) – erfassen die meisten Spitzen, Aussetzer und Driften in Temperaturströmen. Dieser Artikel erklärt, wie jedes Verfahren funktioniert und wo es an seine Grenzen stößt, und liefert Python-Code, der alle drei auf einen Live-MQTT-Datenstrom anwendet.

Ausreißer und Anomalien in Sensordatenströmen

Ein Ausreißer ist ein Messwert, der weit von den übrigen entfernt liegt. Eine Anomalie ist ein Messwert oder Muster, das darauf hinweist, dass in der realen Welt etwas Ungewöhnliches geschieht. Beides überschneidet sich, ist aber nicht identisch. Ein einzelner wilder Messwert durch elektrisches Rauschen ist ein Ausreißer, aber keine Prozessanomalie; ein langsamer Anstieg um zwei Grad innerhalb einer Stunde erzeugt womöglich nie einen Ausreißer und kündigt dennoch einen ausfallenden Kühllüfter an.

Die Ausreißererkennung in Sensordaten hat daher zwei Aufgaben:

  1. Bereinigung. Spitzen und unmögliche Werte entfernen oder markieren, bevor sie Dashboards, Mittelwerte und Modelle erreichen.
  2. Frühwarnung. Messwerte hervorheben, die Aufmerksamkeit verdienen, weil sie das jüngste Muster durchbrechen.

Die folgenden Methoden decken beides ab, mit unterschiedlichen Stärken. Die übergreifende Architektur einschließlich Machine-Learning-Detektoren beschreibt Methoden der IoT-Anomalieerkennung.

Z-Score: Abstand in Standardabweichungen

Der Z-Score misst, wie viele Standardabweichungen ein Wert vom Mittelwert entfernt ist:

z = (x - mean) / std

Ein Messwert mit einem absoluten Z-Score über 3 gilt nach einer verbreiteten Regel als Ausreißer. Im Streaming-Kontext berechnen Sie Mittelwert und Standardabweichung über ein gleitendes Fenster aktueller Messwerte statt über die gesamte Historie, sodass die Basislinie langsamen Änderungen folgt.

Stärken: leicht zu erklären, schnell, funktioniert gut, wenn das Signal annähernd normalverteilt um ein stabiles Niveau schwankt.

Schwächen: Mittelwert und Standardabweichung werden selbst durch Ausreißer verzerrt. Eine Häufung von Spitzen bläht die Standardabweichung auf und verdeckt die nächste Spitze. Zudem setzt das Verfahren symmetrisches Rauschen voraus, das viele Sensoren nicht haben.

Eine robuste Variante ersetzt den Mittelwert durch den Median und die Standardabweichung durch die mittlere absolute Abweichung vom Median (MAD). Diese Version kommt im Tutorial Echtzeit-Anomalieerkennung in Python zum Einsatz.

IQR: Quartilsgrenzen

Der Interquartilsabstand verwendet Perzentile statt Mittelwerte:

Q1 = 25th percentile, Q3 = 75th percentile, IQR = Q3 - Q1
lower fence = Q1 - 1.5 * IQR
upper fence = Q3 + 1.5 * IQR

Alles außerhalb dieser Grenzen ist ein Ausreißer. Verwenden Sie 3 statt 1,5 für „extreme“ Ausreißer.

Stärken: robust gegenüber genau den Ausreißern, nach denen gesucht wird, keine Normalverteilungsannahme, kommt mit schiefen Daten wie Schwingungsamplituden oder Stromaufnahme zurecht.

Schwächen: benötigt ein ausreichend großes Fenster für stabile Quartile, und bei stark quantisierten Sensoren (zum Beispiel einem Temperatursensor mit 0,5 Grad Auflösung) kann der IQR auf null schrumpfen und jede kleine Änderung markieren. Legen Sie eine Mindestbreite der Grenzen fest, die der Auflösung des Sensors entspricht.

EWMA: geglättete Basislinie mit Kontrollgrenzen

EWMA führt einen laufend geglätteten Wert, bei dem aktuelle Messwerte stärker gewichtet werden:

ewma = alpha * x + (1 - alpha) * ewma_previous

Parallel dazu verfolgen Sie eine exponentiell gewichtete Varianz und markieren Messwerte außerhalb von ewma ± k * ewm_std. Ein kleines Alpha (0,01 bis 0,05) ergibt eine langsame, stabile Basislinie; ein größeres Alpha folgt Änderungen schnell.

Stärken: konstanter Speicherbedarf (zwei Zahlen pro Signal), ideal für Mikrocontroller, passt sich von selbst langsamer Drift an und reagiert als Regelkarte empfindlich auf kleine anhaltende Verschiebungen.

Schwächen: Weil sich das Verfahren anpasst, kann eine langsame Störung in die Basislinie aufgenommen werden. Kombinieren Sie es mit einer absoluten Grenze für Werte, die nie überschritten werden dürfen.

Die Methoden im Vergleich

Methode Speicher pro Signal Robust gegenüber Ausreißern Kommt mit Schiefe zurecht Erkennt langsame Drift Passt auf Mikrocontroller
Gleitender Z-Score Fenster mit N Werten Nein Nein Schlecht Ja, bei kleinem N
Robuster Z-Score (Median/MAD) Fenster mit N Werten Ja Teilweise Schlecht Ja, bei kleinem N
IQR-Grenzen Fenster mit N Werten Ja Ja Schlecht Ja, bei kleinem N
EWMA-Kontrollgrenzen Zwei Zahlen Teilweise Nein Ja, bei kleinem Alpha Problemlos

Keine Methode gewinnt allein. Eine praxistaugliche Kombination für Temperaturen lautet: harte physikalische Grenzen, IQR für Spitzen und EWMA mit kleinem Alpha für Drift.

Ausreißererkennung in Sensordaten mit Python

Der folgende Code implementiert alle drei Detektoren als kleine Klassen mit einer update-Methode, sodass Sie sie parallel auf denselben Datenstrom anwenden können.

# detectors.py
import math
import statistics
from collections import deque


class RollingZ:
    def __init__(self, window=60, k=3.0):
        self.buf, self.k = deque(maxlen=window), k

    def update(self, x):
        flagged = False
        if len(self.buf) >= 20:
            mean = statistics.fmean(self.buf)
            std = statistics.pstdev(self.buf) or 1e-9
            flagged = abs(x - mean) / std > self.k
        if not flagged:
            self.buf.append(x)
        return flagged


class IQRFence:
    def __init__(self, window=60, k=1.5, min_width=0.2):
        self.buf, self.k, self.min_width = deque(maxlen=window), k, min_width

    def update(self, x):
        flagged = False
        if len(self.buf) >= 20:
            q1, _, q3 = statistics.quantiles(self.buf, n=4)
            iqr = max(q3 - q1, self.min_width)
            flagged = x < q1 - self.k * iqr or x > q3 + self.k * iqr
        if not flagged:
            self.buf.append(x)
        return flagged


class EWMA:
    def __init__(self, alpha=0.05, k=4.0, warmup=20):
        self.alpha, self.k, self.warmup = alpha, k, warmup
        self.mean, self.var, self.n = None, 0.0, 0

    def update(self, x):
        self.n += 1
        if self.mean is None:
            self.mean = x
            return False
        diff = x - self.mean
        std = math.sqrt(self.var) or 1e-9
        flagged = self.n > self.warmup and abs(diff) > self.k * std
        if not flagged:
            self.mean += self.alpha * diff
            self.var = (1 - self.alpha) * (self.var + self.alpha * diff * diff)
        return flagged

Jeder Detektor lernt nicht aus den Werten, die er markiert. So verunreinigt eine Spitze die Basislinie nicht.

Anwendung auf einen Live-MQTT-Temperaturstrom

Um das durchgängig auszuprobieren, veröffentlichen Sie simulierte Temperaturen an den öffentlichen MQTT-Broker unter broker.connectmq.com und führen die Detektoren in einem Subscriber aus. Ersetzen Sie t9x4q2 durch Ihre eigene zufällige ID; der öffentliche MQTT-Broker ist für alle lesbar und nur für Tests gedacht, verwenden Sie daher nur synthetische Daten.

# temp_sim.py
import json, random, time, uuid
import paho.mqtt.client as mqtt

TOPIC = "connectmq/demo/t9x4q2/lab/room1/temperature"
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"tsim-{uuid.uuid4().hex[:6]}")
c.connect("broker.connectmq.com", 1883, keepalive=60)
c.loop_start()
base = 21.0
while True:
    base += random.gauss(0, 0.01)                 # langsames Wandern
    value = base + random.gauss(0, 0.15)
    if random.random() < 0.02:
        value += random.choice([-6, 7])           # gelegentliche Spitze
    c.publish(TOPIC, json.dumps({"tempC": round(value, 2), "ts": time.time()}))
    time.sleep(1)
# temp_outliers.py
import json, uuid
import paho.mqtt.client as mqtt
from detectors import RollingZ, IQRFence, EWMA

TOPIC = "connectmq/demo/t9x4q2/lab/room1/temperature"
detectors = {"z": RollingZ(), "iqr": IQRFence(), "ewma": EWMA()}


def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(TOPIC)


def on_message(client, userdata, msg):
    x = json.loads(msg.payload)["tempC"]
    flags = {name: d.update(x) for name, d in detectors.items()}
    if any(flags.values()):
        print(f"{x:6.2f}  flagged by: {[n for n, f in flags.items() if f]}")
        client.publish(TOPIC.rsplit("/", 1)[0] + "/outliers",
                       json.dumps({"tempC": x, "flags": flags}), qos=1)


c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"tout-{uuid.uuid4().hex[:6]}")
c.on_connect = on_connect
c.on_message = on_message
c.connect("broker.connectmq.com", 1883, keepalive=60)
c.loop_forever()

Starten Sie beide Skripte und beobachten Sie, welche Detektoren übereinstimmen. Typischerweise erfassen alle drei die großen Spitzen, während der Z-Score gelegentlich eine Spitze übersieht, die kurz auf eine andere folgt – genau die oben beschriebene Schwäche.

Erkennung auf dem Gerät oder nach dem Broker

Sie können diese Detektoren an zwei Stellen betreiben, und die Wahl beeinflusst, was Sie veröffentlichen.

Läuft die Erkennung auf dem Sensorknoten, etwa einem ESP32, kann das Gerät jeder Nachricht ein Ausreißer-Flag anhängen oder nur dann veröffentlichen, wenn etwas Interessantes passiert. Das spart Bandbreite und funktioniert auch bei Netzausfall, doch die Schwellenwerte sind fest in der Firmware verankert und schwerer zu ändern.

Läuft die Erkennung nach dem Broker in einem Subscriber wie dem obigen, bleiben die Geräte einfach, und Sie können Methoden und Schwellenwerte jederzeit ändern, mehrere Detektoren parallel vergleichen und die Historie neu verarbeiten. Der Preis dafür ist, dass jeder Rohwert zum Broker übertragen werden muss.

Ein verbreiteter Kompromiss ist eine harte Grenze und eine Prüfung auf eingefrorene Werte auf dem Gerät, die defekte Sensoren sofort erkennt, kombiniert mit den statistischen Detektoren in einem Subscriber-Dienst, wo sie sich leicht abstimmen lassen. Mit einem ConnectMQ-Broker in der Mitte landen beide Ausgaben auf Topics, die dieselben Dashboards lesen können. In einer Fabrik ordnen Sie diese Topics am besten in einen Unified Namespace (UNS) nach ISA-95 ein, etwa acme/werk-muenchen/spritzguss/linie-4/maschine-2/temperature und daneben .../outliers, damit Rohwerte und Markierungen derselben Maschine zusammen auffindbar sind.

Praktische Checkliste

  1. Wenden Sie zuerst harte physikalische Grenzen an (ein Raumsensor mit 150 Grad hat eine Störung, keinen Ausreißer).
  2. Erkennen Sie eingefrorene Werte separat, indem Sie über einen Zeitraum auf identische Messwerte prüfen.
  3. Wählen Sie Fenstergrößen als Zeitspanne statt als Anzahl Messwerte, wenn die Veröffentlichungsintervalle schwanken.
  4. Legen Sie Mindestbreiten für Bänder entsprechend der Sensorauflösung fest.
  5. Markieren Sie Ausreißer, statt sie zu löschen, und bewahren Sie den Rohwert für spätere Analysen auf.
  6. Verlangen Sie zwei oder drei aufeinanderfolgende Markierungen, bevor Sie eine Person alarmieren.
  7. Überprüfen Sie die Schwellenwerte nach einer Woche mit echten Daten.

Wenn Signale zusammenwirken, zum Beispiel Strom und Durchfluss an einer Pumpe, stoßen diese Methoden pro Signal an ihre Grenzen, und ein multivariates Modell wie Isolation Forest für Sensordaten wird lohnenswert.

Welche Rolle der Broker spielt

Jeder der oben gezeigten Bausteine ist ein gewöhnlicher MQTT-Client: Der Sensor veröffentlicht, der Detektor abonniert und veröffentlicht erneut. Der MQTT-Broker leitet die Nachrichten lediglich weiter, sodass Sie Detektoren hinzufügen, entfernen oder ersetzen können, ohne die Geräte anzufassen. Für echte Einsätze bietet ein privater Cloud-MQTT-Broker wie ConnectMQ zusätzlich TLS, Zugangsdaten und einen Live-Topic-Explorer, in dem Sie Rohwerte und markierte Ausreißer nebeneinander verfolgen können. Weitere Möglichkeiten, Analytik mit Geräten zu kombinieren, zeigt der Leitfaden zu KI und IoT.

Mit ConnectMQ starten

Erstellen Sie unter app.connectmq.com einen Broker im kostenlosen Serverless-Tarif, ganz ohne Kreditkarte, verbinden Sie Ihre Sensoren damit und lassen Sie den Ausreißerdetektor auf Ihre eigenen Daten statt auf den Simulator los.

Häufig gestellte Fragen

Welche Methode zur Ausreißererkennung eignet sich am besten für Temperatursensoren?

Eine Kombination funktioniert am besten: physikalische Grenzen, IQR-Grenzen für Spitzen und EWMA mit kleinem Glättungsfaktor für langsame Drift. Da sich Temperaturen langsam ändern, ist EWMA besonders wirksam.

Welche Fenstergröße sollte ich für gleitende Statistiken verwenden?

Das Fenster sollte genug Zeit abdecken, um normales Rauschen zu erfassen, aber nicht so viel, dass legitime Änderungen ewig brauchen, bis sie übernommen werden. Bei einem Messwert pro Sekunde sind eine bis fünf Minuten ein vernünftiger Ausgangspunkt.

Sollten Ausreißer aus den Daten entfernt werden?

Markieren Sie sie, statt sie zu löschen. Bewahren Sie den Rohwert mit einem Ausreißer-Flag auf, damit Sie Entscheidungen nachvollziehen und Modelle später neu trainieren können.

Können diese Methoden auf einem ESP32 laufen?

Ja. EWMA benötigt nur zwei Gleitkommazahlen pro Signal, und ein gleitendes Fenster mit 30 bis 60 Werten passt problemlos in den Speicher eines ESP32, sodass Sie Ausreißer vor dem Veröffentlichen markieren können.

Verwandte Artikel

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.