Noise

Filter in Arduino Teil 1

Ich habe mich nun einige Zeit mit dem Thema Filter in Arduino beschäftigt. Da ich jedoch, gelinde gesagt, eher begrenzte Kenntnisse in Mathematik habe, möchte ich in diesem Beitrag versuchen die Grundlagen aufzuarbeiten und am Ende eine praxistaugliche Lösung anbieten, welche ganz bewusst nicht allzu sehr in die theoretische Tiefe geht. Aus dem einfachen Grund, weil ich es nicht kann 😉

Was ist überhaupt ein Filter und wozu brauche ich das?

Viele werden beim Arduino oder anderen Mikrocontrollern an einen Punkt angelangen, wo man einen Sensor z. B. über einen analogen Eingang ausliest und feststellt das die Werte zu unruhig sind, um Sie z. B. von einem Display abzulesen.

Oder man stellt fest, dass die Werte am Eingang schwanken, obwohl sich am Sensor nichts ändert.

Dieses Phänomen nennt sich Sensorrauschen oder einfach nur Rauschen. Dieses Rauschen kann früher oder später, bzw. stärker oder schwächer auftreten, kommt jedoch generell bei jedem Sensor und somit auch an jedem analogen Eingang eines Mikrokontrollers vor.

Bei der Übertragung von Nachrichtensignalen ist das Rauschen meistens die größte Störquelle. Die Rauschquellen treten dabei im gesamten Übertragungssystem, also im Sender, im Empfänger und auf dem Übertragungsweg auf. 1)https://de.wikipedia.org/wiki/Rauschen_(Physik

Es gibt allerdings programmiertechnische Möglichkeiten das Rauschen abzuschwächen oder es sogar ganz auszufiltern.

Filter:

Ein Filter wird unter anderem definiert als:

…eine elektrische Schaltung, die bestimmte Frequenzen aus einem Signalspektrum abschwächt. 2)Wikipedia

Frequenzen? Was hat das denn nun alles mit Tönen zu tun?

Zugegebenermaßen wenig. Wenn wir aber bedenken, dass die Änderungen an unserem Sensor auch eine gewisse Frequenz haben (Änderungen/Zeit) macht das schon wieder mehr Sinn. Da die sehr schnellen Änderungen an unserem Sensor höchstwahrscheinlich nicht durch unsere Messung entstehen, sondern durch das Rauschen, wollen wir diese Messungen mit sehr schnellen Änderungen => hohen Frequenzen filtern. Also brauchen wir softwaretechnisch im Arduino einen Filter. Ein Tiefpassfilter meint ein Filter, der nur die langsamen, und somit wahrscheinlich „echten“ Messungen durchlässt und die anderen nicht beachtet.

Verschiedene Arten der Filterung:

  1. Mittelwert bilden

Ja, auch eine einfache Mittelwertbildung kann man schon als eine Art Filter sehen. Um genau zu sein, wird hierbei der arithmetische Mittelwert verwendet.
Dabei wird der Sensor einfach mehrfach ausgelesen und das Ergebnis in eine Variable addiert.

/*deklariere und initialisiere Zählvariable varMittelwert, setze Anzahl der 
Schleifendurchläufe = 10, addiere 1 bei jedem Durchlauf */
for(int varMittelwert=0, varMittelwert= 10, varMittelwert++)
    {
        variable += Sensor.read();     //Sensorwert wird zu variable addierd
        delay(10); //warte 10 Millisekunden
    }

Dieses Ergebnis wird nun durch die Anzahl der addierten Messwerte geteilt:



variable = variable/varMittelwert;


Dadurch löschen sich nun – theoretisch zumindest – „Ausreisser“ aus.

Beispiel:

Nehmen wir an, der Sensor soll den Wert = 0 messen. Nun wird während der 10 Messungen ein Wert plötzlich mit +5 gemessen. So verfälscht dieser Wert das Ergebnis nur noch um 1/10tel also um 0,5 Einheiten.
Mehr noch! Denn man kann davon ausgehen, dass das Sensorrauschen sowohl in positiver als auch in negativer Richtung auftritt. Sprich: auf unseren Fehler von +5 folgt in der Theorie auch ein Fehler von -5 wodurch unsere Ergebnis = 0 ist.

Lösung?

Moment! Das ist doch genau der Wert, den wir erwarten! Problem also gelöst? Für manche Fälle in der Praxis ist diese Art der Filterung sicher ausreichend.
Doch so einfach diese Methode ist, so hat sie doch einige

Nachteile:

Gewichtung

Die 10 Sensorwerte aus dem Beispiel haben alle den gleichen Einfluss auf das Ergebnis. Auch die, die zeitlich weiter zurückliegen. Mag das für dieses Beispiel mit 10 Durchläufen noch zu vernachlässigen sein, so wird es bei einer größeren Menge an Werten und somit auch längerer Dauer der „Aufzeichnung“ schon problematisch. Was ist, wenn sich die „echten“ Daten des Sensors während der Durchschnittsbildung ändern? Genau genommen verfälschen dann die „alten“ Werte die Messung und es dauert einen ganzen Schleifendurchlauf, bis dies nicht mehr der Fall ist.

Laufzeit

Je genauer der Mittelwert sein soll, umso mehr Werte müssen gemessen werden und umso länger läuft die Schleife. In dieser Zeit ist der Arduino „belegt“ und kann keine andere Aufgabe wahrnehmen. Für zeitkritische Anwendungen ist dies ein nicht zu verachtender Nachteil.

  1. Gleitender Mittelwert

Wie man feststellt, ist der arithmetische Mittelwert schon mal eine passable Methode. Es werden immer „n“ Proben genommen, der Mittelwert gebildet und danach das Ergebnis ausgegeben. Anschließend beginnt die Mittelwertbildung von Neuem.

Wenn die Schleife, in der die Messungen vorgenommen werden, nun nicht beendet wird, sondern bei jedem Durchlauf der Mittelwert aller bisher gemessenen Proben berechnet und ausgeben werden würde? Bingo! Wir haben den sogenannten „gleitenden Mittelwert“.

Da mit dieser Methode allerdings die Speicher mit der Zeit überlaufen würden, muss die Liste der Werte, aus denen der Mittelwert gebildet wird, begrenzt werden.

In der Signaltheorie wird der gleitende Mittelwert als Tiefpassfilter mit endlicher Impulsantwort (FIR-Tiefpass) beschrieben. In der gleichgewichteten Form stellt der gleitende Mittelwert das einfachste FIR-Tiefpassfilter dar. 3)Wikipedia

Vergleich verschiedener Filter in Arduino
Vergleich Mittelwert 4)Gut informiert, MittelwertSignalvergleich3, marked as public domain, more details on Wikimedia Commons

Wie in der Darstellung erkennbar, sind die Messungen schon enigermaßen geglättet. Allerdings bleiben weiterhin einige Nachteile.

Nachteile:

Gewichtung:

Die länger zurückliegenden Werte gehen immer noch gleichwertig in den Mittelwert ein.

Speicher:

Um einen aussagekräftigen Mittelwert zu erhalten, muss einiges an Daten vorgehalten werden. Dadurch ist diese Art der Filterung sehr speicherintensiv.

 

  1. Exponentiell geglätteter Mittelwert (Exponential moving average EMA)

Ein gern genutztes Filter in der Praxis ist das EMA-Filter. Es vereint die oben genannten Methoden der Filterung und vermeidet dabei die genannten Nachteile. Die zeitlich weiter zurückliegenden Werte fließen mit exponentiell abnehmender Gewichtung in den Mittelwert ein. Weiterhin ist das Filter rekursiv. Das bedeutet, die bereits geglätteten Werte fließen wieder in die Mittelwertsbildung mit ein.

EMA Gewichtung
EMA Gewichtung 5)Kevin Ryde (https://commons.wikimedia.org/wiki/File:Exponential_moving_average_weights_N=15.png), „Exponential moving average weights N=15“, https://creativecommons.org/licenses/by-sa/3.0/legalcode

Da der exponentielle Mittelwert nicht nur Werte aus der Zeitreihe, sondern auch vorangegangene Mittelwerte miteinbezieht, stellt er ein Filter mit unendlicher Impulsantwort dar. Ein entscheidender Vorteil ist seine wesentlich kürzere Verzögerung bei gleicher Glättung. 6)https://de.wikipedia.org/wiki/Gleitender_Mittelwert

Die Implementierung dieses Filter ist nun nicht mehr ganz so trivial. Deswegen habe ich recherchiert ob ich nicht eine vorhande Library finde.

Der User ahseeshr hat auf Github eine Library veröffentlicht, welche, neben dem EMA-Filter, auch noch weitere Filtertypen beinhaltet:

Microsmooth

Der eigentliche Algorithmus besteht aus wenigen Zeilen:

int ema_filter(int current_value, void * ptr)
{ 
    static uint16_t exponential_average=current_value;
    
    exponential_average=(EMA_ALPHA*(uint32_t)current_value + (100 - EMA_ALPHA)*(uint32_t)exponential_average)/100;
    return exponential_average;
}

Wobei EMA_ALPHA in der Headerdatei definiert wird. Laut Angabe des Autors ist das EMA-Filter mit dem Alpha von 0.10 Filter das, dass am besten performt.

The best performing filter at present is EMA with alpha parameter 0.10.7)https://github.com/asheeshr/Microsmooth/blob/master/README.md

Die ersten Implementierungen sehen vielversprechend aus.

Im nächsten Teil werde ich die Implementierung der verschiedenen Filter in Arduino genauer beschreiben und versuchen einige Graphen zur Funktion der Filter zu erstellen.
Weiter gehts in in Teil 2

Quellen[+]

3 Kommentare

    1. Hm komisch.
      Wird bei dir kein horizontaler Scrollbalken angezeigt ?
      Die ganze Zeile lautet:
      exponential_average=(EMA_ALPHA*(uint32_t)current_value + (100 – EMA_ALPHA)*(uint32_t)exponential_average)/100;

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert