Saturday 21 January 2017

Efficient Moving Average Filter

Ich habe vor kurzem über Fortschritte in der Antwort auf diesen Beitrag gelernt. Und frage mich, wie ich sie verwenden, um einen gleitenden Durchschnitt Filter effizienter als das, was ich in diesem Beitrag vorgeschlagen (mit Faltungs-Filter) zu berechnen. Dies ist, was ich bisher habe. Es nimmt eine Ansicht des ursprünglichen Arrays dann rollt es um die notwendige Menge und summiert die Kernel-Werte, um den Durchschnitt zu berechnen. Ich bin mir bewusst, dass die Kanten nicht richtig gehandhabt werden, aber danach kann ich mich darum kümmern. Gibt es einen besseren und schnelleren Weg Das Ziel ist es, große Gleitkomma-Arrays bis zu 5000x5000 x 16 Schichten in der Größe zu filtern, eine Aufgabe, die scipy. ndimage. filters. convolve ist ziemlich langsam. Beachten Sie, dass ich nach 8-Nachbar-Konnektivität suche, dh ein 3x3-Filter nimmt den Durchschnitt von 9 Pixeln (8 um das Brennpixel) und weist diesen Wert dem Pixel im neuen Bild zu. EDIT Klarstellung, wie ich diese Arbeit sehen: Verwenden Sie stridetricks, um ein Array wie 0,1,2,1,2,3,2,3,4 generieren. Die der oberen Zeile des Filterkerns entspricht. Rollen Sie entlang der vertikalen Achse, um die mittlere Reihe des Kerns 10,11,12,11,12,13,13,14,15 zu erhalten. Und fügen Sie es auf das Array ich bekam in 1) Wiederholen, um die untere Zeile des Kernels 20,21,22,21,22,23,22,23,24 erhalten. . An diesem Punkt nehme ich die Summe jeder Zeile und dividiere sie durch die Anzahl der Elemente im Filter, wobei mir der Durchschnitt für jedes Pixel, (um 1 Zeile und 1 col verschoben, und mit einigen Merkwürdigkeiten um die Kanten, aber ich kann Kümmere dich darum später). Was ich erhoffte, ist eine bessere Verwendung von stridetricks, um die 9 Werte oder die Summe der Kernel-Elemente direkt, für das gesamte Array zu erhalten, oder dass jemand mich von einer anderen effizienteren Methode überzeugen kann. Fragte am 8. Februar um 18:05 für was sein Wert, heres, wie youd es mit fancy schreitenden Tricks tun. Ich wollte diese Post gestern, sondern wurde von der tatsächlichen Arbeit abgelenkt. ) Paul amp essen beide haben schöne Implementierungen mit verschiedenen anderen Möglichkeiten, dies zu tun. Nur um die Dinge aus der früheren Frage fortzusetzen, dachte ich Id post das N-dimensionale Äquivalent. Sie werden nicht in der Lage, scipy. ndimage Funktionen für 1D-Arrays deutlich zu schlagen. (Scipy. ndimage. uniformfilter sollte schlagen scipy. ndimage. convolve. though) Darüber hinaus, wenn youre versuchen, ein multidimensionales bewegliches Fenster zu erhalten, riskieren Sie, Speicherverwendung zu sprengen, wenn Sie versehentlich eine Kopie Ihres Arrays zu machen. Während das anfängliche rollende Array gerade eine Ansicht in den Gedächtnis Ihres ursprünglichen Arrays ist, bilden alle Zwischenschritte, die das Array kopieren, eine Kopie, die Größenordnungen größer als Ihr ursprüngliches Array ist (dh Lets sagen, dass youre, das mit einem 100x100 ursprünglichen Array arbeitet (Für eine Filtergröße von (3,3)) wird 98x98x3x3, aber verwenden Sie den gleichen Speicher wie das Original. Allerdings werden alle Kopien die Menge an Speicher verwenden, dass eine vollständige 98x98x3x3 Array würde) Grundsätzlich mit verrückt Schreitenden Tricks ist ideal, wenn Sie verschieben Fenster Operationen auf einer einzigen Achse eines ndarray vektorisieren möchten. Es macht es wirklich einfach, Dinge wie eine bewegte Standardabweichung, etc. mit sehr wenig Overhead zu berechnen. Wenn Sie anfangen, dies zu tun entlang mehrerer Achsen, seine mögliche, aber youre in der Regel besser mit mehr spezialisierte Funktionen. (Wie scipy. ndimage etc.) Auf jeden Fall ist heres, wie Sie es tun: Also, was wir bekommen, wenn wir b Rollingwindow (a, filtsize) ist ein 8x8x3x3 Array, das ist eigentlich eine Ansicht in den gleichen Speicher wie das Original 10x10 Array. Wir könnten genauso leicht verschiedene Filtergrößen entlang verschiedener Achsen verwenden oder nur entlang ausgewählter Achsen eines N-dimensionalen Arrays betrieben werden (dh Filzgröße (0,3,0,3) auf einem 4-dimensionalen Array würde uns eine 6-dimensionale Ansicht geben ). Wir können dann eine beliebige Funktion auf die letzte Achse anwenden, um die Dinge in einem sich bewegenden Fenster effektiv zu berechnen. Jedoch, weil waren die Speicherung von temporären Arrays, die viel größer als unsere ursprüngliche Array auf jedem Schritt der Mittelwert (oder std oder was auch immer), das ist überhaupt nicht Speicher effizient Sein auch nicht zu sein, furchtbar schnell, entweder. Das Äquivalent für ndimage ist nur: Dies wird eine Vielzahl von Randbedingungen behandeln, das Unschärfen an Ort und Stelle durchführen, ohne eine temporäre Kopie des Arrays zu benötigen, und sehr schnell sein. Schiebe-Tricks sind eine gute Möglichkeit, eine Funktion auf ein bewegtes Fenster entlang einer Achse anzuwenden, aber theyre nicht ein guter Weg, um es entlang mehrerer Achsen zu tun, in der Regel. Nur meine 0.02, jedenfalls. Sehr gut: Schiebe-Tricks sind eine gute Möglichkeit, eine Funktion auf ein bewegtes Fenster entlang einer Achse anzuwenden, aber sie sind nicht ein guter Weg, um es entlang mehrerer Achsen zu tun, in der Regel. Und natürlich ist Ihre Erklärung der Erinnerung 39blow up39 wichtig. Art der Zusammenfassung aus Ihrer Antwort (zumindest für mich) ist: 39don39t gehen zu weit Angeln, ist der Quarterteed Fang allready in scipy39. Thanks ndash eat Feb 9 11 at 16:37 Vielen Dank, Joe, für diese Antwort. In rollendem Fenster sollte das, wenn nicht hasattr (.): Rollingwindowlastaxis (.) Statt Rolling zurückgeben. Ndash unutbu Ich bin nicht vertraut genug mit Python, um Code für das zu schreiben, aber die beiden besten Möglichkeiten zur Beschleunigung von Windungen ist entweder trennen Sie den Filter oder die Fourier-Transformation verwenden. Getrennter Filter. Faltung ist O (MN), wobei M und N die Anzahl der Pixel in dem Bild bzw. dem Filter sind. Da die durchschnittliche Filterung mit einem 3-mal-3-Kernel dem Filtern zuerst mit einem 3-by-1-Kernel und dann einem 1-by-3-Kernel gleichkommt, können Sie durch eine konsekutive Faltung eine Geschwindigkeit von 33 erhalten Mit zwei 1-d-Kerneln (dies wird offensichtlich besser, da der Kernel größer wird). Sie können immer noch in der Lage, Schritt-Tricks hier zu verwenden, natürlich. Fourier-Transformation . Conv (A, B) äquivalent zu ifft (fft (A) fft (B)) ist. D. h. eine Faltung im direkten Raum wird eine Multiplikation im Fourierraum, wobei A dein Bild und B dein Filter ist. Da die (elementweise) Multiplikation der Fourier-Transformationen A und B gleich groß ist, ist B ein Array der Größe (A) mit dem Kernel im Zentrum des Bildes und Nullen überall. Um einen 3-mal-3-Kernel in der Mitte eines Arrays zu platzieren, müssen Sie A bis ungerade Größe auffüllen. Abhängig von der Implementierung der Fourier-Transformation kann dies viel schneller sein als die Faltung (und wenn Sie denselben Filter mehrmals anwenden, können Sie fft (B) vorberechnen und weitere 30 Rechenzeit sparen). Antwortete am 9. Februar um 15:27 Für was es wert ist, in python, sind diese in scipy. ndimage. uniformfilter und scipy. signal. fftconvolve implementiert. beziehungsweise. Ndash Joe Kington Jonas: Cool Der seperate Filter Ansatz funktioniert gut, wie Sie sagen, es spart mehr Zeit als die Kernel-Größe erhöht. Für ein 5000x5000-Array, bei einer 11x11-Kernelgröße, bekomme ich 7.7s für 2d Convolution mit ndimage. convolve und 2.0s für zwei 1d-Convolutions mit ndimage. convolve1d. Für Ihre zweite Lösung, was ist B ndash Benjamin Eine Sache, die ich bin zuversichtlich, muss behoben werden, ist Ihr View-Array b. Es hat ein paar Elemente aus nicht zugeordneten Speicher, so youll erhalten Abstürze. Angesichts Ihrer neuen Beschreibung Ihres Algorithmus, ist die erste Sache, die Fixierung ist die Tatsache, dass Sie außerhalb der Zuweisung von a: Weil Im noch nicht ganz erfassen die Methode und es scheint einfacher Wege, um das Problem zu lösen, Im nur gehen Um das hier zu sagen: das scheint nur der einfache Ansatz zu sein. Der einzige äußere Vorgang ist, dass er B nur einmal zugewiesen und bevölkert hat. Der Zusatz, die Teilung und die Indexierung müssen unbedingt durchgeführt werden. Wenn Sie 16 Bands machen, müssen Sie B nur noch einmal zuweisen, wenn Sie die Absicht haben, ein Bild zu speichern. Auch wenn dies keine Hilfe ist, könnte es klären, warum ich nicht verstehen, das Problem, oder zumindest dienen als ein Maßstab für die Zeit der Beschleunigungen von anderen Methoden. Dies läuft in 2,6 Sekunden auf meinem Laptop auf einem 5k x 5k-Array von float64s, 0,5 von denen die Schaffung von B beantwortet ist nicht so klar, Ihre Frage, aber Im, dass jetzt, dass youll wie zu verbessern Deutlich diese Art der Mittelung. Nun, welche Art von Leistungsverbesserungen würden Sie eigentlich erwarten Update: Zunächst einmal eine Warnung: der Code in seinem aktuellen Zustand passt sich nicht richtig an die Kernform an. Aber das ist nicht meine primäre Sorge jetzt (sowieso ist die Idee gibt es schon, wie man richtig anpassen). Ich habe gerade gewählt, die neue Form eines 4D A intuitiv, für mich ist es wirklich sinnvoll, darüber nachzudenken, ein 2D-Kernel zentriert werden, um jede Rasterposition des ursprünglichen 2D A. Aber die 4D-Gestaltung kann nicht wirklich die beste sein. Ich denke, das eigentliche Problem hier ist die Leistung der Summierung. Man sollte in der Lage, die beste Ordnung (der 4D A) zu finden, um voll zu nutzen Ihre Maschinen Cache-Architektur. Allerdings kann diese Reihenfolge nicht die gleichen für kleine Arrays, die Art der Zusammenarbeit mit Ihrem Maschinen-Cache und die größeren, die nicht (zumindest nicht so einfach). Update 2: Hier ist eine leicht modifizierte Version von mf. Offensichtlich ist es besser, wieder zu einem 3D-Array zuerst und dann anstelle von Summierung einfach tun Punktprodukt (dies hat den Vorteil, alle so, dass Kernel willkürlich sein kann). Jedoch seine noch etwas 3x langsamer (auf meiner Maschine) als Pauls aktualisiert Funktion. Beantwortet am 8. Februar um 19: 33Die Moving Average als Filter Der gleitende Durchschnitt wird oft für die Glättung von Daten in Anwesenheit von Rauschen verwendet. Der einfache gleitende Durchschnitt wird nicht immer als der Finite Impulse Response (FIR) - Filter erkannt, der es ist, während er tatsächlich einer der gebräuchlichsten Filter in der Signalverarbeitung ist. Wenn man sie als Filter betrachtet, kann man sie beispielsweise mit gefensterten Filtern vergleichen (siehe Artikel zu Tiefpaß-, Hochpass - und Bandpass - und Bandsperrfiltern für Beispiele). Der Hauptunterschied zu diesen Filtern besteht darin, daß der gleitende Durchschnitt für Signale geeignet ist, für die die Nutzinformation im Zeitbereich enthalten ist. Von denen Glättungsmessungen durch Mittelung ein Paradebeispiel sind. Window-sinc-Filter, auf der anderen Seite, sind starke Künstler im Frequenzbereich. Mit Ausgleich in der Audioverarbeitung als typisches Beispiel. Es gibt einen detaillierteren Vergleich beider Arten von Filtern in Time Domain vs. Frequency Domain Performance von Filtern. Wenn Sie Daten haben, für die sowohl die Zeit als auch die Frequenzdomäne wichtig sind, dann möchten Sie vielleicht einen Blick auf Variationen auf den Moving Average werfen. Die eine Anzahl gewichteter Versionen des gleitenden Durchschnitts zeigt, die besser sind. Der gleitende Durchschnitt der Länge (N) kann so definiert werden, wie er üblicherweise implementiert ist, wobei der aktuelle Ausgabeabtastwert der Durchschnitt der vorhergehenden (N) Abtastwerte ist. Als Filter betrachtet, führt der gleitende Durchschnitt eine Faltung der Eingangsfolge (xn) mit einem rechteckigen Puls der Länge (N) und der Höhe (1 / N) durch (um den Bereich des Pulses und damit die Verstärkung von Der Filter, ein). In der Praxis ist es am besten, (N) ungerade zu nehmen. Obwohl ein gleitender Durchschnitt auch unter Verwendung einer geraden Anzahl von Abtastwerten berechnet werden kann, hat die Verwendung eines ungeradzahligen Wertes für (N) den Vorteil, daß die Verzögerung des Filters eine ganzzahlige Anzahl von Abtastwerten ist, da die Verzögerung eines Filters mit (N) Proben genau ist ((N-1) / 2). Der gleitende Durchschnitt kann dann exakt mit den ursprünglichen Daten ausgerichtet werden, indem er um eine ganze Zahl von Abtastwerten verschoben wird. Zeitdomäne Da der gleitende Durchschnitt eine Faltung mit einem rechteckigen Puls ist, ist sein Frequenzgang eine sinc-Funktion. Dies macht es ähnlich dem Dual des Fenstersynchronfilters, da es sich hierbei um eine Faltung mit einem Sinc-Puls handelt, der zu einem rechteckigen Frequenzgang führt. Es ist diese sinc Frequenzantwort, die den gleitenden Durchschnitt ein schlechter Darsteller im Frequenzbereich macht. Allerdings führt es sehr gut im Zeitbereich. Daher ist es perfekt, um Daten zu löschen, um Rauschen zu entfernen, während gleichzeitig eine schnelle Sprungantwort beibehalten wird (1). Für das typische Additiv-Weiß-Gauß-Rauschen (AWGN), das oft angenommen wird, hat die Mittelung (N) - Proben den Effekt, das SNR um einen Faktor von (sqrt N) zu erhöhen. Da das Rauschen für die einzelnen Proben unkorreliert ist, gibt es keinen Grund, jede Probe unterschiedlich zu behandeln. Daher wird der gleitende Durchschnitt, der jeder Probe das gleiche Gewicht gibt, die maximale Menge an Rauschen für eine gegebene Sprungantwortschärfe beseitigen. Implementierung Da es sich um ein FIR-Filter handelt, kann der gleitende Durchschnitt durch Faltung implementiert werden. Es hat dann die gleiche Effizienz (oder das Fehlen davon) wie jedes andere FIR-Filter. Sie kann aber auch rekursiv und effizient umgesetzt werden. Es folgt unmittelbar aus der Definition, daß diese Formel das Ergebnis der Ausdrücke für (yn) und (yn1) ist, dh, daß die Veränderung zwischen (yn1) und (yn) einen zusätzlichen Term (xn1 / N) Erscheint am Ende, während der Term (xn-N1 / N) vom Anfang entfernt wird. In praktischen Anwendungen ist es oft möglich, die Division durch (N) für jeden Term auszulassen, indem die resultierende Verstärkung von (N) an einer anderen Stelle kompensiert wird. Diese rekursive Umsetzung wird viel schneller als Faltung. Jeder neue Wert von (y) kann mit nur zwei Additionen anstelle der (N) Additionen berechnet werden, die für eine einfache Implementierung der Definition erforderlich wären. Eine Sache, mit der Sie nach einer rekursiven Implementierung Ausschau halten, ist, dass Rundungsfehler akkumulieren. Dies kann ein Problem für Ihre Anwendung sein oder auch nicht, aber es bedeutet auch, dass diese rekursive Implementierung tatsächlich mit einer Integer-Implementierung besser funktionieren wird als mit Gleitkommazahlen. Dies ist sehr ungewöhnlich, da eine Gleitkomma-Implementierung gewöhnlich einfacher ist. Der Schluss davon muss sein, dass Sie die Nützlichkeit des einfachen gleitenden Durchschnittsfilters in Signalverarbeitungsanwendungen nie unterschätzen sollten. Filter Design Tool Dieser Artikel wird mit einem Filter Design Tool ergänzt. Experimentiere mit verschiedenen Werten für (N) und visualisiere die resultierenden Filter. Versuchen Sie es jetztAls andere schon erwähnt haben, sollten Sie ein IIR (Endlosimpulsantwort) Filter anstelle der FIR (Finite Impulse Response) Filter, die Sie jetzt verwenden. Es gibt mehr dazu, aber auf den ersten Blick werden FIR-Filter als explizite Windungen und IIR-Filter mit Gleichungen implementiert. Das besondere IIR-Filter, das ich viel in Mikrocontrollern verwende, ist ein einpoliges Tiefpaßfilter. Dies ist das digitale Äquivalent eines einfachen R-C-Analogfilters. Für die meisten Anwendungen haben diese bessere Eigenschaften als der Kastenfilter, den Sie verwenden. Die meisten Verwendungen eines Box-Filter, die ich begegnet bin, sind ein Ergebnis von jemand nicht Aufmerksamkeit in der digitalen Signalverarbeitung Klasse, nicht als Ergebnis der Notwendigkeit ihrer besonderen Eigenschaften. Wenn Sie nur wollen, um hohe Frequenzen zu dämpfen, dass Sie wissen, Rauschen sind, ist ein einpoliges Tiefpassfilter besser. Der beste Weg, um ein digitales in einem Mikrocontroller zu implementieren, ist in der Regel: FILT lt - FILT FF (NEW - FILT) FILT ist ein Stück persistenten Zustand. Dies ist die einzige persistente Variable, die Sie benötigen, um diesen Filter zu berechnen. NEU ist der neue Wert, den der Filter mit dieser Iteration aktualisiert. FF ist die Filterfraktion. Die die Schwere des Filters einstellt. Betrachten Sie diesen Algorithmus und sehen Sie, dass für FF 0 der Filter unendlich schwer ist, da sich der Ausgang nie ändert. Für FF 1 ist das eigentlich gar kein Filter, da der Ausgang nur dem Eingang folgt. Nützliche Werte sind dazwischen. Auf kleinen Systemen wählen Sie FF auf 1/2 N, so dass die Multiplikation mit FF als Rechtsverschiebung um N Bits erreicht werden kann. Beispielsweise kann FF 1/16 betragen und das Multiplizieren mit FF daher eine Rechtsverschiebung von 4 Bits. Andernfalls benötigt dieses Filter nur eine Subtraktion und eine Addition, obwohl die Zahlen in der Regel größer als der Eingangswert sein müssen (mehr über die numerische Genauigkeit in einem separaten Abschnitt weiter unten). Ich normalerweise nehmen A / D-Messwerte deutlich schneller als sie benötigt werden und wenden Sie zwei dieser Filter kaskadiert. Dies ist das digitale Äquivalent von zwei R-C-Filtern in Serie und dämpft um 12 dB / Oktave über der Rolloff-Frequenz. Für A / D-Messungen ist es jedoch gewöhnlich relevanter, den Filter im Zeitbereich zu betrachten, indem er seine Sprungantwort betrachtet. Dies zeigt Ihnen, wie schnell Ihr System eine Änderung sehen wird, wenn die Sache, die Sie messen, ändert. Zur Erleichterung der Gestaltung dieser Filter (was nur bedeutet Kommissionierung FF und entscheiden, wie viele von ihnen zu kaskadieren), benutze ich mein Programm FILTBITS. Sie legen die Anzahl der Schaltbits für jede FF in der kaskadierten Filterreihe fest und berechnen die Schrittantwort und andere Werte. Eigentlich habe ich in der Regel laufen diese über mein Wrapper-Skript PLOTFILT. Dies führt FILTBITS, die eine CSV-Datei macht, dann die CSV-Datei. Beispielsweise ist hier das Ergebnis von PLOTFILT 4 4: Die beiden Parameter zu PLOTFILT bedeuten, dass es zwei Filter gibt, die von dem oben beschriebenen Typ kaskadiert sind. Die Werte von 4 geben die Anzahl der Schaltbits an, um die Multiplikation mit FF zu realisieren. Die beiden FF-Werte sind in diesem Fall 1/16. Die rote Spur ist die Einheit Schritt Antwort, und ist die Hauptsache zu betrachten. Dies bedeutet beispielsweise, dass sich der Ausgang des kombinierten Filters auf 90 des neuen Wertes in 60 Iterationen niederschlägt, falls sich der Eingang sofort ändert. Wenn Sie ca. 95 Einschwingzeit kümmern, dann müssen Sie etwa 73 Iterationen warten, und für 50 Einschwingzeit nur 26 Iterationen. Die grüne Kurve zeigt Ihnen den Ausgang einer einzelnen Amplitude. Dies gibt Ihnen eine Vorstellung von der zufälligen Rauschunterdrückung. Es sieht aus wie keine einzelne Probe wird mehr als eine 2,5 Änderung in der Ausgabe verursachen. Die blaue Spur soll ein subjektives Gefühl geben, was dieser Filter mit weißem Rauschen macht. Dies ist kein strenger Test, da es keine Garantie gibt, was genau der Inhalt der Zufallszahlen war, die als der weiße Rauscheneingang für diesen Durchlauf von PLOTFILT ausgewählt wurden. Seine nur, um Ihnen ein grobes Gefühl, wie viel es gequetscht werden und wie glatt es ist. PLOTFILT, vielleicht FILTBITS, und viele andere nützliche Dinge, vor allem für PIC-Firmware-Entwicklung ist verfügbar in der PIC Development Tools-Software-Release auf meiner Software-Downloads-Seite. Hinzugefügt über numerische Genauigkeit Ich sehe aus den Kommentaren und nun eine neue Antwort, dass es Interesse an der Diskussion der Anzahl der Bits benötigt, um diesen Filter zu implementieren. Beachten Sie, dass das Multiplizieren mit FF Log 2 (FF) neue Bits unterhalb des Binärpunkts erzeugt. Bei kleinen Systemen wird FF gewöhnlich mit 1/2 N gewählt, so daß diese Multiplikation tatsächlich durch eine Rechtsverschiebung von N Bits realisiert wird. FILT ist daher meist eine feste Ganzzahl. Beachten Sie, dass dies ändert keine der Mathematik aus der Prozessoren Sicht. Wenn Sie beispielsweise 10-Bit A / D-Messwerte und N 4 (FF 1/16) filtern, benötigen Sie 4 Fraktionsbits unter den 10-Bit-Integer-A / D-Messwerten. Einer der meisten Prozessoren, youd tun 16-Bit-Integer-Operationen aufgrund der 10-Bit-A / D-Lesungen. In diesem Fall können Sie immer noch genau die gleichen 16-Bit-Integer-Opertions, aber beginnen mit der A / D-Lesungen um 4 Bits verschoben verschoben. Der Prozessor kennt den Unterschied nicht und muss nicht. Das Durchführen der Mathematik auf ganzen 16-Bit-Ganzzahlen funktioniert, ob Sie sie als 12,4 feste oder wahre 16-Bit-Ganzzahlen (16,0 Fixpunkt) betrachten. Im Allgemeinen müssen Sie jedem Filterpole N Bits hinzufügen, wenn Sie aufgrund der numerischen Darstellung kein Rauschen hinzufügen möchten. Im obigen Beispiel müsste das zweite Filter von zwei 1044 18 Bits haben, um keine Informationen zu verlieren. In der Praxis auf einer 8-Bit-Maschine bedeutet, dass youd 24-Bit-Werte verwenden. Technisch nur den zweiten Pol von zwei würde den größeren Wert benötigen, aber für Firmware Einfachheit ich in der Regel die gleiche Darstellung, und damit der gleiche Code, für alle Pole eines Filters. Normalerweise schreibe ich eine Unterroutine oder Makro, um eine Filterpol-Operation durchzuführen, dann gelten, dass für jeden Pol. Ob eine Unterroutine oder ein Makro davon abhängt, ob Zyklen oder Programmspeicher in diesem Projekt wichtiger sind. So oder so, ich benutze einige Scratch-Zustand, um NEU in die Subroutine / Makro, die FILT Updates, sondern auch lädt, dass in den gleichen Kratzer NEU war in. Dies macht es einfach, mehrere Pole anzuwenden, da die aktualisierte FILT von einem Pol ist Die NEUE der nächsten. Wenn ein Unterprogramm, ist es sinnvoll, einen Zeiger auf FILT auf dem Weg in, die auf nur nach FILT auf dem Weg nach draußen aktualisiert wird. Auf diese Weise arbeitet das Unterprogramm automatisch auf aufeinanderfolgenden Filtern im Speicher, wenn es mehrmals aufgerufen wird. Mit einem Makro benötigen Sie nicht einen Zeiger, da Sie in der Adresse passieren, um auf jeder Iteration zu arbeiten. Code-Beispiele Hier ist ein Beispiel für ein Makro wie oben für eine PIC 18 beschrieben: Und hier ist ein ähnliches Makro für eine PIC 24 oder dsPIC 30 oder 33: Beide Beispiele sind als Makros mit meinem PIC-Assembler-Präprozessor implementiert. Die mehr fähig ist als eine der eingebauten Makroanlagen. Clabacchio: Ein weiteres Thema, das ich erwähnen sollte, ist die Firmware-Implementierung. Sie können eine einpolige Tiefpassfilter-Subroutine einmal schreiben, dann mehrfach anwenden. Tatsächlich schreibe ich normalerweise solch ein Unterprogramm, um einen Zeiger im Gedächtnis in den Filterzustand zu nehmen, dann ihn voranbringen den Zeiger, so daß er nacheinander leicht aufgerufen werden kann, um mehrpolige Filter zu verwirklichen. Ndash Olin Lathrop Apr 20 12 at 15:03 1. Dank sehr viel für Ihre Antworten - alle von ihnen. Ich beschloss, dieses IIR-Filter zu verwenden, aber dieser Filter wird nicht als Standard-Tiefpaßfilter verwendet, da ich die Zählerwerte berechnen und sie vergleichen muss, um Änderungen in einem bestimmten Bereich zu erkennen. Da diese Werte von sehr unterschiedlichen Dimensionen abhängig von Hardware Ich wollte einen Durchschnitt nehmen, um in der Lage sein, auf diese Hardware spezifischen Änderungen automatisch reagieren. Wenn Sie mit der Beschränkung einer Macht von zwei Anzahl von Elementen zu durchschnittlich leben können (dh 2,4,8,16,32 etc), dann kann die Teilung einfach und effizient auf einem getan werden Low-Performance-Mikro ohne dedizierte Division, weil es als Bit-Shift durchgeführt werden kann. Jede Schicht rechts ist eine Macht von zwei zB: Die OP dachte, er hatte zwei Probleme, die Teilung in einem PIC16 und Speicher für seine Ringpuffer. Diese Antwort zeigt, dass die Teilung nicht schwierig ist. Zwar adressiert es nicht das Gedächtnisproblem, aber das SE-System erlaubt Teilantworten, und Benutzer können etwas von jeder Antwort für selbst nehmen, oder sogar redigieren und kombinieren andere39s Antworten. Da einige der anderen Antworten eine Divisionsoperation erfordern, sind sie ähnlich unvollständig, da sie nicht zeigen, wie dies auf einem PIC16 effizient erreicht werden kann. Ndash Martin Apr 20 12 at 13:01 Es gibt eine Antwort für einen echten gleitenden Durchschnitt Filter (auch bekannt als Boxcar-Filter) mit weniger Speicher Anforderungen, wenn Sie dont mind Downsampling. Es heißt ein kaskadiertes Integrator-Kamm-Filter (CIC). Die Idee ist, dass Sie einen Integrator, die Sie nehmen Differenzen über einen Zeitraum, und die wichtigsten Speicher-sparende Gerät ist, dass durch Downsampling, müssen Sie nicht jeden Wert des Integrators zu speichern. Es kann mit dem folgenden Pseudocode implementiert werden: Ihre effektive gleitende durchschnittliche Länge ist decimationFactorstatesize, aber Sie müssen nur um Stateize Proben zu halten. Offensichtlich können Sie bessere Leistung erzielen, wenn Ihr stateize und decimationFactor Potenzen von 2 sind, so dass die Divisions - und Restoperatoren durch Shifts und Masken ersetzt werden. Postscript: Ich stimme mit Olin, dass Sie immer sollten einfache IIR-Filter vor einem gleitenden durchschnittlichen Filter. Wenn Sie die Frequenz-Nullen eines Boxcar-Filters nicht benötigen, wird ein 1-poliger oder 2-poliger Tiefpassfilter wahrscheinlich gut funktionieren. Auf der anderen Seite, wenn Sie für die Zwecke der Dezimierung filtern (mit einer hohen Sample-Rate-Eingang und Mittelung es für die Verwendung durch einen Low-Rate-Prozess), dann kann ein CIC-Filter genau das, was Sie suchen. (Vor allem, wenn Sie stateize1 verwenden und den Ringbuffer insgesamt mit nur einem einzigen vorherigen Integrator-Wert zu vermeiden) Theres einige eingehende Analyse der Mathematik hinter der Verwendung der ersten Ordnung IIR-Filter, Olin Lathrop bereits beschrieben hat auf der Digital Signal Processing Stack-Austausch (Enthält viele schöne Bilder.) Die Gleichung für diese IIR-Filter ist: Dies kann mit nur Ganzzahlen und keine Division mit dem folgenden Code implementiert werden (möglicherweise benötigen einige Debugging, wie ich aus dem Speicher wurde.) Dieser Filter approximiert einen gleitenden Durchschnitt von Die letzten K Proben durch Setzen des Wertes von alpha auf 1 / K. Führen Sie dies im vorherigen Code durch die Definition von BITS auf LOG2 (K), dh für K 16 gesetzt BITS auf 4, für K 4 gesetzt BITS auf 2, etc. (Ill Überprüfung der Code hier aufgelistet, sobald ich eine Änderung und Bearbeiten Sie diese Antwort, wenn nötig.) Antwort # 1 am: Juni 23, 2010, um 4:04 Uhr Heres ein einpoliges Tiefpassfilter (gleitender Durchschnitt, mit Cutoff-Frequenz CutoffFrequency). Sehr einfach, sehr schnell, funktioniert super, und fast kein Speicher Overhead. Hinweis: Alle Variablen haben einen Bereich über die Filterfunktion hinaus, mit Ausnahme des übergebenen newInput Hinweis: Dies ist ein einstufiger Filter. Mehrere Stufen können zusammen kaskadiert werden, um die Schärfe des Filters zu erhöhen. Wenn Sie mehr als eine Stufe verwenden, müssen Sie DecayFactor anpassen (was die Cutoff-Frequenz betrifft), um sie zu kompensieren. Und natürlich alles, was Sie brauchen, ist die beiden Zeilen überall platziert, brauchen sie nicht ihre eigene Funktion. Dieser Filter hat eine Rampenzeit, bevor der gleitende Durchschnitt diejenige des Eingangssignals darstellt. Wenn Sie diese Rampenzeit umgehen müssen, können Sie MovingAverage einfach auf den ersten Wert von newInput anstelle von 0 initialisieren und hoffen, dass der erste newInput kein Ausreißer ist. (CutoffFrequency / SampleRate) einen Bereich zwischen 0 und 0,5 aufweist. DecayFactor ist ein Wert zwischen 0 und 1, in der Regel in der Nähe von 1. Single-precision Schwimmer sind gut genug für die meisten Dinge, ich bevorzuge nur Doppel. Wenn Sie mit ganzen Zahlen bleiben müssen, können Sie DecayFactor und Amplitude Factor in Fractional Integers umwandeln, in denen der Zähler als Integer gespeichert wird und der Nenner eine Ganzzahl von 2 ist (so können Sie Bit-Shift nach rechts als die Nenner, anstatt sich während der Filterschleife teilen zu müssen). Zum Beispiel, wenn DecayFactor 0.99, und Sie Ganzzahlen verwenden möchten, können Sie DecayFactor 0.99 65536 64881. Und dann immer wenn Sie multiplizieren mit DecayFactor in Ihrer Filterschleife, nur verschieben Sie das Ergebnis 16. Für weitere Informationen über dieses, ein ausgezeichnetes Buch thats Online, Kapitel 19 auf rekursive Filter: dspguide / ch19.htm PS Für das Moving Average-Paradigma, einen anderen Ansatz für die Einstellung DecayFactor und AmplitudeFactor, die möglicherweise mehr relevant für Ihre Bedürfnisse, können Sie sagen, dass Sie wollen, dass die vorherigen, etwa 6 Artikeln gemittelt, es diskret tun, fügen Sie 6 Elemente und teilen durch 6, so Können Sie den AmplitudeFactor auf 1/6 und DecayFactor auf (1.0 - AmplitudeFactor) einstellen. Antwortete May 14 12 at 22:55 Jeder andere hat kommentiert gründlich über den Nutzen der IIR vs FIR, und auf Power-of-two-Division. Id nur, um einige Implementierungsdetails zu geben. Das unten genannte funktioniert gut auf kleinen Mikrocontrollern ohne FPU. Es gibt keine Multiplikation, und wenn Sie N eine Potenz von zwei halten, ist die gesamte Division ein-Zyklus-Bit-Verschiebung. Basic FIR-Ringpuffer: Halten Sie einen laufenden Puffer der letzten N-Werte und einen laufenden SUM aller Werte im Puffer. Jedes Mal, wenn eine neue Probe kommt, subtrahieren Sie den ältesten Wert im Puffer von SUM, ersetzen Sie ihn durch das neue Sample, fügen Sie das neue SUM zu SUM hinzu und geben Sie SUM / N aus. Modifizierter IIR-Ringpuffer: Halten Sie einen laufenden SUM der letzten N-Werte. Jedes Mal, wenn ein neues Sample eingeht, SUM - SUM / N, fügen Sie das neue Sample hinzu und geben SUM / N aus. Antwort # 1 am: August 28, 2008, um 13:45 Uhr Wenn Sie 399m lesen Sie Recht, you39re beschreiben einen First-Order IIR-Filter der Wert you39re Subtraktion isn39t der älteste Wert, der herausfällt, sondern ist stattdessen der Durchschnitt der vorherigen Werte. Erstklassige IIR-Filter können sicherlich nützlich sein, aber I39m nicht sicher, was du meinst, wenn Sie vorschlagen, dass der Ausgang ist der gleiche für alle periodischen Signale. Bei einer Abtastrate von 10 kHz liefert das Einspeisen einer 100 Hz-Rechteckwelle in ein 20-stufiges Kastenfilter ein Signal, das für 20 Abtastungen gleichmäßig ansteigt, für 30 sitzt, für 20 Abtastungen gleichmäßig sinkt und für 30 sitzt. Ein erster Ordnung IIR-Filter. Ndash Supercat Aug 28 13 am 15:31 wird eine Welle, die scharf anfängt zu steigen und allmählich Niveaus in der Nähe (aber nicht auf) das Eingabe-Maximum, dann scharf beginnt zu fallen und allmählich in der Nähe (aber nicht auf) der Eingabe Minimum. Sehr unterschiedliches Verhalten. Ndash Supercat Ein Problem ist, dass ein einfacher gleitender Durchschnitt kann oder auch nicht nützlich sein. Mit einem IIR-Filter können Sie einen schönen Filter mit relativ wenigen Calcs erhalten. Die FIR Sie beschreiben kann Ihnen nur ein Rechteck in der Zeit - ein sinc in freq - und Sie können nicht die Seitenkeulen zu verwalten. Es kann lohnt sich, in ein paar ganzzahlige Multiplikatoren zu werfen, um es eine schöne symmetrische abstimmbare FIR, wenn Sie die Zeitschaltuhren ersparen können. Ndash ScottSeidman: Keine Notwendigkeit für Multiplikatoren, wenn man einfach jede Stufe der FIR entweder den Durchschnitt der Eingabe auf diese Stufe und ihre vorherigen gespeicherten Wert, und dann speichern Sie die Eingabe (wenn man hat Der numerische Bereich, man könnte die Summe anstatt den Durchschnitt verwenden). Ob das besser ist als ein Box-Filter, hängt von der Anwendung ab (die Sprungantwort eines Boxfilters mit einer Gesamtverzögerung von 1ms wird zum Beispiel eine böse d2 / dt-Spitze aufweisen, wenn der Eingang geändert wird, und wieder 1ms später, wird aber haben Die minimal mögliche d / dt für einen Filter mit einer Gesamtverzögerung von 1ms). Ndash supercat Wie mikeselectricstuff sagte, wenn Sie wirklich brauchen, um Ihren Speicherbedarf zu reduzieren, und Sie dont dagegen Ihre Impulsantwort ist eine exponentielle (anstelle eines rechteckigen Puls), würde ich für einen exponentiellen gleitenden durchschnittlichen Filter gehen . Ich nutze sie ausgiebig. Mit dieser Art von Filter, brauchen Sie nicht jeden Puffer. Sie brauchen nicht zu speichern N Vergangenheit Proben. Nur einer. So werden Ihre Speicheranforderungen um einen Faktor von N reduziert. Auch brauchen Sie keine Division für das. Nur Multiplikationen. Wenn Sie Zugriff auf Gleitpunktarithmetik haben, verwenden Sie Fließkomma-Multiplikationen. Andernfalls können ganzzahlige Multiplikationen und Verschiebungen nach rechts erfolgen. Allerdings sind wir im Jahr 2012, und ich würde Ihnen empfehlen, Compiler (und MCUs), mit denen Sie mit Gleitkommazahlen arbeiten können. Abgesehen davon, dass mehr Speicher effizienter und schneller (Sie dont haben, um Elemente in jedem kreisförmigen Puffer zu aktualisieren), würde ich sagen, es ist auch natürlich. Weil eine exponentielle Impulsantwort besser auf die Art und Weise reagiert, wie sich die Natur verhält, in den meisten Fällen. Ein Problem mit dem IIR-Filter fast berührt von Olin und Supercat, aber anscheinend von anderen ignoriert ist, dass die Rundung nach unten führt einige Ungenauigkeiten (und möglicherweise Bias / Trunkierung). Unter der Annahme, dass N eine Potenz von zwei ist und nur ganzzahlige Arithmetik verwendet wird, beseitigt das Shift-Recht systematisch die LSBs des neuen Samples. Das bedeutet, dass, wie lange die Serie jemals sein könnte, wird der Durchschnitt nie berücksichtigen. Nehmen wir z. B. eine langsam abnehmende Reihe (8,8,8,8,7,7,7,7,6,6) an und nehmen an, daß der Durchschnitt tatsächlich 8 ist. Die Faust 7 Probe bringt den Durchschnitt auf 7, unabhängig von der Filterstärke. Nur für eine Probe. Gleiche Geschichte für 6, usw. Jetzt denke an das Gegenteil. Die serie geht auf. Der Durchschnitt bleibt auf 7 für immer, bis die Probe groß genug ist, um es zu ändern. Natürlich können Sie für die Bias korrigieren, indem Sie 1 / 2N / 2, aber das nicht wirklich lösen, die Präzision Problem. In diesem Fall wird die abnehmende Reihe für immer bei 8 bleiben, bis die Probe 8-1 / 2 (N / 2) ist. Für N4 zum Beispiel, wird jede Probe über Null halten den Durchschnitt unverändert. I believe a solution for that would imply to hold an accumulator of the lost LSBs. But I didnt make it far enough to have code ready, and Im not sure it would not harm the IIR power in some other cases of series (for example whether 7,9,7,9 would average to 8 then). Olin, your two-stage cascade also would need some explanation. Do you mean holding two average values with the result of the first fed into the second in each iteration. Whats the benefit of this


No comments:

Post a Comment