Wie bereits erwähnt, sollten Sie einen IIR-Filter (Infinite Impulse Response) anstelle des FIR-Filters (Finite Impulse Response) in Betracht ziehen, den Sie derzeit verwenden. Es gibt noch mehr, aber auf den ersten Blick sind FIR-Filter als explizite Faltungen und IIR-Filter mit Gleichungen implementiert.
Das spezielle IIR-Filter, das ich häufig in Mikrocontrollern verwende, ist ein einpoliges Tiefpassfilter. Dies ist das digitale Äquivalent eines einfachen RC-Analogfilters. Für die meisten Anwendungen weisen diese bessere Eigenschaften auf als der von Ihnen verwendete Boxfilter. Die meisten Anwendungen eines Boxfilters, auf die ich gestoßen bin, sind das Ergebnis von jemandem, der in der Klasse der digitalen Signalverarbeitung nicht aufpasst, nicht weil er seine besonderen Eigenschaften benötigt. Wenn Sie nur hohe Frequenzen dämpfen möchten, von denen Sie wissen, dass sie Rauschen sind, ist ein einpoliger Tiefpassfilter besser. Der beste Weg, einen digital in einem Mikrocontroller zu implementieren, ist normalerweise:
FILT <- FILT + FF (NEU - FILT)
FILT ist ein hartnäckiger Zustand. Dies ist die einzige persistente Variable, die Sie zur Berechnung dieses Filters benötigen. NEU ist der neue Wert, den der Filter mit dieser Iteration aktualisiert. FF ist die Filterfraktion , die die "Schwere" des Filters einstellt. Schauen Sie sich diesen Algorithmus an und sehen Sie, dass der Filter für FF = 0 unendlich schwer ist, da sich die Ausgabe nie ändert. Für FF = 1 ist es wirklich überhaupt kein Filter, da die Ausgabe nur der Eingabe folgt. Nützliche Werte liegen dazwischen. Bei kleinen Systemen wählen Sie FF mit 1/2 Nso dass die Multiplikation mit FF als Rechtsverschiebung um N Bits ausgeführt werden kann. Beispielsweise könnte FF 1/16 sein und das Multiplizieren mit FF daher eine Rechtsverschiebung von 4 Bits. Andernfalls benötigt dieser Filter nur ein Subtrahieren und ein Addieren, obwohl die Zahlen normalerweise breiter als der Eingabewert sein müssen (mehr zur numerischen Genauigkeit in einem separaten Abschnitt weiter unten).
Normalerweise nehme ich A / D-Messungen deutlich schneller vor, als sie benötigt werden, und wende zwei dieser Filter kaskadiert an. Dies ist das digitale Äquivalent von zwei in Reihe geschalteten RC-Filtern und dämpft um 12 dB / Oktave über der Rolloff-Frequenz. Bei A / D-Messungen ist es jedoch in der Regel relevanter, den Filter im Zeitbereich unter Berücksichtigung seiner Sprungantwort zu betrachten. Hier erfahren Sie, wie schnell Ihr System eine Änderung erkennt, wenn sich das Messobjekt ändert.
Um das Entwerfen dieser Filter zu erleichtern (was nur bedeutet, FF auszuwählen und zu entscheiden, wie viele davon kaskadiert werden sollen), verwende ich mein Programm FILTBITS. Sie geben die Anzahl der Schiebebits für jeden FF in der kaskadierten Reihe von Filtern an und errechnen die Sprungantwort und andere Werte. Eigentlich starte ich das normalerweise über mein Wrapper-Skript PLOTFILT. Dadurch wird FILTBITS ausgeführt, wodurch eine CSV-Datei erstellt und anschließend die CSV-Datei geplottet wird. Zum Beispiel ist hier das Ergebnis von "PLOTFILT 4 4":
Die beiden Parameter für PLOTFILT bedeuten, dass zwei Filter des oben beschriebenen Typs kaskadiert werden. Die Werte von 4 geben die Anzahl der Schiebebits an, um die Multiplikation mit FF zu realisieren. Die beiden FF-Werte betragen in diesem Fall also 1/16.
Die rote Kurve ist die Einheitsschrittantwort und die Hauptsache, auf die Sie achten müssen. Dies zeigt Ihnen beispielsweise an, dass bei einer sofortigen Änderung der Eingabe die Ausgabe des kombinierten Filters in 60 Iterationen auf 90% des neuen Werts eingestellt wird. Wenn Sie 95% der Einschwingzeit benötigen, müssen Sie 73 Iterationen abwarten, und für 50% der Einschwingzeit nur 26 Iterationen.
Die grüne Kurve zeigt Ihnen die Ausgabe einer einzelnen Spitze mit voller Amplitude. Dies gibt Ihnen eine Vorstellung von der Unterdrückung von zufälligem Rauschen. Es sieht so aus, als würde kein einziges Sample mehr als 2,5% Veränderung in der Ausgabe verursachen.
Die blaue Spur soll ein subjektives Gefühl dafür vermitteln, wie dieser Filter mit weißem Rauschen umgeht. Dies ist kein strenger Test, da keine Garantie dafür besteht, wie genau der Inhalt der Zufallszahlen lautete, die als Eingabe für das weiße Rauschen für diesen Durchlauf von PLOTFILT ausgewählt wurden. Es ist nur, um Ihnen ein grobes Gefühl dafür zu geben, wie stark es zerdrückt wird und wie glatt es ist.
PLOTFILT, möglicherweise FILTBITS und viele andere nützliche Dinge, insbesondere für die PIC-Firmware-Entwicklung, finden Sie in der Softwareversion der PIC Development Tools auf meiner Seite zum Herunterladen von Software .
Hinzugefügt über numerische Präzision
Ich sehe aus den Kommentaren und nun aus einer neuen Antwort, dass Interesse besteht, die Anzahl der Bits zu diskutieren, die zur Implementierung dieses Filters benötigt werden. Beachten Sie, dass das Multiplizieren mit FF neue Log 2 (FF) -Bits unterhalb des Binärpunkts erzeugt. Bei kleinen Systemen wird FF normalerweise mit 1/2 N gewählt, so dass diese Multiplikation tatsächlich durch eine Verschiebung von N Bits nach rechts realisiert wird.
FILT ist daher normalerweise eine Festkommazahl. Beachten Sie, dass dies aus Sicht des Prozessors nichts an der Mathematik ändert. Wenn Sie beispielsweise 10-Bit-A / D-Messwerte und N = 4 (FF = 1/16) filtern, benötigen Sie 4 Bruchbits unterhalb der 10-Bit-Ganzzahl-A / D-Messwerte. Bei den meisten Prozessoren würden Sie aufgrund der 10-Bit-A / D-Messwerte 16-Bit-Ganzzahloperationen ausführen. In diesem Fall können Sie immer noch genau die gleichen 16-Bit-Ganzzahloperationen ausführen, aber mit den A / D-Messwerten beginnen, die um 4 Bit nach links verschoben sind. Der Prozessor kennt den Unterschied nicht und muss es auch nicht. Die Berechnung ganzer 16-Bit-Ganzzahlen funktioniert unabhängig davon, ob Sie sie als 12,4-Fixpunkt- oder echte 16-Bit-Ganzzahlen (16,0-Fixpunkt) betrachten.
Im Allgemeinen müssen Sie für jeden Filterpol N Bits hinzufügen, wenn Sie aufgrund der numerischen Darstellung kein Rauschen hinzufügen möchten. Im obigen Beispiel müsste der zweite von zwei Filtern 10 + 4 + 4 = 18 Bits haben, um keine Informationen zu verlieren. In der Praxis bedeutet dies auf einem 8-Bit-Computer, dass Sie 24-Bit-Werte verwenden. Technisch würde nur der zweite von zwei Polen den breiteren Wert benötigen, aber zur Vereinfachung der Firmware verwende ich normalerweise die gleiche Darstellung und damit den gleichen Code für alle Pole eines Filters.
Normalerweise schreibe ich eine Subroutine oder ein Makro, um eine Filterpoloperation durchzuführen, und wende diese dann auf jeden Pol an. Ob ein Unterprogramm oder ein Makro vorhanden ist, hängt davon ab, ob Zyklen oder der Programmspeicher in diesem bestimmten Projekt wichtiger sind. In beiden Fällen verwende ich einen Scratch-Status, um NEW an die Subroutine / das Makro zu übergeben, die / das FILT aktualisiert, aber auch in denselben Scratch-Status lädt, in dem NEW sich befand. Dies erleichtert das Anwenden mehrerer Pole, da der aktualisierte FILT eines Pols ist das NEUE vom nächsten. Bei einer Unterroutine ist es nützlich, einen Zeiger auf FILT auf dem Weg nach innen zu haben, der beim Verlassen auf FILT aktualisiert wird. Auf diese Weise bearbeitet das Unterprogramm automatisch aufeinanderfolgende Filter im Speicher, wenn es mehrmals aufgerufen wird. Mit einem Makro benötigen Sie keinen Zeiger, da Sie die Adresse übergeben, um mit jeder Iteration zu arbeiten.
Codebeispiele
Hier ist ein Beispiel eines Makros wie oben für einen PIC 18 beschrieben:
//////////////////////////////////////////////////// ////////////////////////////////
//
// Makrofilter filt
//
// Aktualisiere einen Filterpol mit dem neuen Wert in NEWVAL. NEWVAL wird aktualisiert auf
// Enthält den neuen gefilterten Wert.
//
// FILT ist der Name der Filterstatusvariablen. Es werden 24 Bit angenommen
// breit und in der lokalen Bank.
//
// Die Formel zum Aktualisieren des Filters lautet:
//
// FILT <- FILT + FF (NEWVAL - FILT)
//
// Das Multiplizieren mit FF wird durch eine Verschiebung der FILTBITS-Bits nach rechts erreicht.
//
/ Makrofilter
/schreiben
dbankif lbankadr
movf [arg 1] +0, w; NEWVAL <- NEWVAL - FILT
subwf newval + 0
movf [arg 1] +1, w
subwfb newval + 1
movf [arg 1] +2, w
subwfb newval + 2
/schreiben
/ loop n Filtbits, einmal für jedes Bit, um NEWVAL nach rechts zu verschieben
rlcf newval + 2, w; NEWVAL um ein Bit nach rechts verschieben
rrcf newval + 2
rrcf newval + 1
rrcf newval + 0
/ endloop
/schreiben
movf newval + 0, w; addiere den verschobenen Wert in den Filter und speichere in NEWVAL
addwf [arg 1] +0, w
movwf [arg 1] +0
movwf newval + 0
movf newval + 1, w
addwfc [arg 1] +1, w
movwf [arg 1] +1
movwf newval + 1
movf newval + 2, w
addwfc [arg 1] +2, w
movwf [arg 1] +2
movwf newval + 2
/ endmac
Und hier ist ein ähnliches Makro für ein PIC 24 oder dsPIC 30 oder 33:
//////////////////////////////////////////////////// ////////////////////////////////
//
// Makro FILTER ffbits
//
// Aktualisiere den Zustand eines Tiefpassfilters. Der neue Eingabewert steht in W1: W0
// und der zu aktualisierende Filterstatus wird von W2 angezeigt.
//
// Der aktualisierte Filterwert wird auch in W1 zurückgegeben: W0 und W2 zeigen
// zum ersten Speicher nach dem Filterstatus. Dieses Makro kann also sein
// wird nacheinander aufgerufen, um eine Reihe von kaskadierten Tiefpassfiltern zu aktualisieren.
//
// Die Filterformel lautet:
//
// FILT <- FILT + FF (NEU - FILT)
//
// wobei die Multiplikation mit FF durch eine arithmetische Rechtsverschiebung von durchgeführt wird
// FFBITS.
//
// WARNUNG: W3 wird in den Papierkorb geworfen.
//
/ Makrofilter
/ var new ffbits integer = [arg 1]; Anzahl der zu verschiebenden Bits abrufen
/schreiben
/ write "; Führe eine einpolige Tiefpassfilterung durch, verschiebe Bits =" ffbits
/schreiben " ;"
Unter w0, [w2 ++], w0; NEW - FILT -> W1: W0
subb w1, [w2--], w1
lsr w0, # [v ffbits], w0; verschiebe das Ergebnis in W1: W0 nach rechts
sl w1, # [- 16 ffbits], w3
für w0, w3, w0
asr w1, # [v ffbits], w1
addiere w0, [w2 ++], w0; addiere FILT, um das Endergebnis in W1: W0 zu erhalten
addc w1, [w2--], w1
mov w0, [w2 ++]; Ergebnis in den Filterstatus schreiben, Zeiger vorrücken
mov w1, [w2 ++]
/schreiben
/ endmac
Diese beiden Beispiele werden als Makros mit meinem PIC-Assembler-Präprozessor implementiert , der leistungsfähiger ist als jede der integrierten Makrofunktionen.