Friday 20 October 2017

Moving Average Class C ++


Ich versuche, den gleitenden Durchschnitt eines Signals zu berechnen. Der Signalwert (a double) wird zu beliebigen Zeiten aktualisiert. Ich suche nach einem effizienten Weg, um seinen zeitgewichteten Durchschnitt über ein Zeitfenster in Echtzeit zu berechnen. Ich könnte es selbst tun, aber es ist schwieriger als ich dachte Die meisten der Ressourcen, die ich über das Internet gefunden habe, berechnen den gleitenden Durchschnitt des periodischen Signals, aber meine Updates zufällig. Kennt jemand gute Ressourcen dafür Der Trick ist der folgende: Sie erhalten Updates zufällig über void update (int time, float value). Allerdings müssen Sie auch auch verfolgen, wenn ein Update aus dem Zeitfenster fällt, also setzen Sie einen Alarm, der zur Zeit N aufruft, der das vorherige Update entfernt, von der jemals wieder in der Berechnung berücksichtigt wird. Wenn dies in Echtzeit geschieht, können Sie das Betriebssystem anfordern, um einen Anruf zu einer Methode zu machen void dropoffoldestupdate (int time) zum Zeitpunkt N aufgerufen werden. Wenn dies eine Simulation ist, können Sie keine Hilfe vom Betriebssystem erhalten und Sie müssen Mach es manuell. In einer Simulation nennst du Methoden mit der Zeit, die als Argument geliefert wird (was nicht mit der Realzeit korreliert). Eine vernünftige Annahme ist jedoch, dass die Anrufe so garantiert werden, dass die Zeitargumente zunehmen. In diesem Fall müssen Sie eine sortierte Liste von Alarmzeitwerten pflegen und für jeden Aktualisierungs - und Leseaufruf überprüfen Sie, ob das Zeitargument größer ist als der Kopf der Alarmliste. Während es größer ist, machst du die Alarmverarbeitung (Drop off die älteste Aktualisierung), entfernen Sie den Kopf und überprüfen Sie noch einmal, bis alle Alarme vor der angegebenen Zeit verarbeitet werden. Dann rufe das Update auf. Ich habe so weit davon ausgegangen, dass es offensichtlich ist, was Sie für die eigentliche Berechnung tun würden, aber ich werde nur im Fall erarbeiten. Ich nehme an, Sie haben eine Methode float lesen (int Zeit), die Sie verwenden, um die Werte zu lesen. Ziel ist es, diesen Anruf so effizient wie möglich zu machen. So berechnen Sie den gleitenden Durchschnitt nicht jedes Mal, wenn die Lesemethode aufgerufen wird. Stattdessen berechnen Sie den Wert ab dem letzten Update oder dem letzten Alarm und optimieren diesen Wert um ein paar Gleitkommaoperationen, um den Ablauf der Zeit seit dem letzten Update zu berücksichtigen. (Z. B. eine konstante Anzahl von Operationen, außer bei der Verarbeitung einer Liste von aufgestauten Alarmen). Hoffentlich ist das klar - das sollte ein ganz einfacher Algorithmus und sehr effizient sein. Weitere Optimierung. Eines der verbleibenden Probleme ist, wenn eine große Anzahl von Updates innerhalb des Zeitfensters passiert, dann gibt es eine lange Zeit, für die es weder Lesungen noch Updates gibt und dann ein Lesen oder Update kommt. In diesem Fall ist der obige Algorithmus ineffizient in der inkrementellen Aktualisierung des Wertes für jeden der Updates, die abfällt. Dies ist nicht notwendig, weil wir uns nur um die letzte Aktualisierung über das Zeitfenster kümmern, also wenn es einen Weg gibt, um alle älteren Updates effizient abzusetzen, würde es helfen. Um dies zu tun, können wir den Algorithmus ändern, um eine binäre Suche nach Updates durchzuführen, um das aktuellste Update vor dem Zeitfenster zu finden. Wenn es relativ wenige Updates gibt, die gelöscht werden müssen, dann kann man den Wert für jedes gelöschte Update inkrementell aktualisieren. Aber wenn es viele Updates gibt, die gelöscht werden müssen, dann kann man den Wert von neuem neu berechnen, nachdem er die alten Updates abgelegt hat. Anhang zur Inkrementalberechnung: Ich sollte klären, was ich mit der inkrementellen Berechnungen oben im Satz tue, um diesen Wert durch ein paar Gleitkommaoperationen zu zwingen, um den Ablauf der Zeit seit dem letzten Update zu berücksichtigen. Initiale nicht-inkrementale Berechnungen: dann iterate über relevanteupdates in der Reihenfolge der zunehmenden Zeit: moveaverage (sum lastupdate timesincelastupdate) windowlength. Nun, wenn genau ein Update aus dem Fenster fällt, aber keine neuen Updates ankommen, stelle die Summe wie folgt ein: (Notiz ist es, dass der Zeitstempel geändert wurde, um den Anfang des letzten Fensters zu starten). Und wenn genau ein Update in das Fenster eintritt, aber keine neuen Updates abfallen, stelle die Summe als: Wie sollte es offensichtlich sein, das ist eine grobe Skizze, aber hoffentlich zeigt es, wie man den Durchschnitt so beibehalten kann, dass es O (1) Operationen pro Update ist Auf amortisierter Basis. Aber bemerken Sie weitere Optimierung im vorherigen Absatz. Beachten Sie auch Stabilitätsprobleme, die in einer älteren Antwort angedeutet werden, was bedeutet, dass sich Gleitkomma-Fehler über eine große Anzahl solcher inkrementeller Operationen ansammeln können, so dass es eine Abweichung von dem Ergebnis der vollständigen Berechnung gibt, die für die Anwendung signifikant ist. Wenn eine Annäherung in Ordnung ist und theres eine minimale Zeit zwischen den Proben, könnte man Super-Sampling versuchen. Haben Sie ein Array, das gleichmäßig beabstandete Zeitintervalle repräsentiert, die kürzer als das Minimum sind, und bei jedem Zeitspanne das letzte erhaltene Sample speichern. Je kürzer das Intervall ist, desto näher wird der Durchschnitt auf den wahren Wert. Der Zeitraum sollte nicht größer als die Hälfte des Minimums sein oder es besteht die Möglichkeit, eine Probe zu fehlen. Antwortete Dec 15 11 um 18:12 antwortete Dec 15 11 um 22:38 Vielen Dank für die Antwort. Eine Verbesserung, die es nötig wäre, um den Wert des Gesamtdurchschnitts tatsächlich zu notieren, so dass wir die ganze Zeit nicht schleifen. Auch kann es ein kleiner Punkt sein, aber wäre es nicht effizienter, einen Deque oder eine Liste zu verwenden, um den Wert zu speichern, da wir davon ausgehen, dass das Update in der richtigen Reihenfolge kommen wird. Einfügung wäre schneller als in der Karte. Ndash Arthur Dec 16 11 at 8:55 Ja, du kannst den Wert der Summe zwischenspeichern. Subtrahieren Sie die Werte der Samples, die Sie löschen, fügen Sie die Werte der Proben hinzu, die Sie einfügen. Auch, ja, ein dequeltpairltSample, Dategtgt könnte effizienter sein. Ich wählte Karte für Lesbarkeit, und die Leichtigkeit der Aufruf Karte :: upperbound. Wie immer, schreiben Sie korrekten Code zuerst, dann Profil und messen Sie inkrementelle Änderungen. Ndash Rob Dez 16 11 um 15:00 Hinweis: Anscheinend ist dies nicht der Weg, um dies zu nähern. Verlassen Sie es hier als Referenz auf das, was mit diesem Ansatz falsch ist. Überprüfe die Kommentare. AKTUALISIERT - basierend auf Olis Kommentar. Nicht sicher über die Instabilität, über die er spricht. Verwenden Sie eine sortierte Karte der Ankunftszeiten gegen Werte. Bei der Ankunft eines Wertes fügen Sie die Ankunftszeit zu der sortierten Karte zusammen mit seinem Wert hinzu und aktualisieren Sie den gleitenden Durchschnitt. Warnung das ist Pseudocode: Dort. Nicht voll ausgefunden, aber du bekommst die Idee. Dinge zu beachten. Wie ich schon sagte, ist der Pseudocode. Du musst eine passende Karte auswählen. Dont entfernen Sie die Paare, wie Sie durch, wie Sie den Iterator ungültig machen und müssen wieder anfangen. Siehe Olis Kommentar unten auch. Beantwortet Dec 15 11 at 12:22 Das funktioniert nicht: Es wird nicht berücksichtigt, welcher Anteil der Fensterlänge jeder Wert existiert. Auch dieser Ansatz des Hinzufügens und dann Subtrahieren ist nur stabil für ganzzahlige Typen, nicht Floats. Ndash Oliver Charlesworth Dec 15 11 at 12:29 OliCharlesworth - Entschuldigung Ich vermisse einige wichtige Punkte in der Beschreibung (doppelt und zeitlich gewichtet). Ich werde aktualisieren Vielen Dank. Ndash Dennis Dec 15 11 at 12:33 Die Zeitgewichtung ist ein weiteres Problem. Aber das ist nicht das, worüber ich rede. Ich bezog mich auf die Tatsache, dass, wenn ein neuer Wert zuerst das Zeitfenster betritt, sein Beitrag zum Durchschnitt minimal ist. Der Beitrag steigt weiter an, bis ein neuer Wert eintritt. Ndash Oliver Charlesworth Dec 15 11 at 12: 35Ich weiß, das ist erreichbar mit Boost wie pro: Aber ich möchte wirklich vermeiden, Boost. Ich habe gegoogelt und fand keine geeigneten oder lesbaren Beispiele. Grundsätzlich möchte ich den gleitenden Durchschnitt eines laufenden Stroms eines Stroms von Gleitkommazahlen mit den aktuellsten 1000 Zahlen als Datenmuster verfolgen. Was ist der einfachste Weg, um dies zu erreichen, experimentierte ich mit einem kreisförmigen Array, einem exponentiellen gleitenden Durchschnitt und einem einfacheren gleitenden Durchschnitt und fand, dass die Ergebnisse aus dem kreisförmigen Array meinen Bedürfnissen am besten entsprechen. Gefragt am 12. Juni 12 um 4:38 Wenn Ihre Bedürfnisse einfach sind, können Sie nur versuchen, einen exponentiellen gleitenden Durchschnitt. Setzen Sie einfach, Sie machen eine Akkumulator-Variable, und wie Ihr Code bei jedem Sample sieht, aktualisiert der Code den Akkumulator mit dem neuen Wert. Sie wählen eine konstante Alpha, die zwischen 0 und 1 ist, und berechnen Sie diese: Sie müssen nur einen Wert von Alpha zu finden, wo die Wirkung einer bestimmten Probe nur für etwa 1000 Proben dauert. Hmm, Im nicht wirklich sicher, dass dies für Sie geeignet ist, jetzt, dass Ive es hier. Das Problem ist, dass 1000 ist ein ziemlich langes Fenster für einen exponentiellen gleitenden Durchschnitt Im nicht sicher, es gibt ein Alpha, die den Durchschnitt über die letzten 1000 Zahlen, ohne Unterlauf in der Gleitkomma Berechnung zu verbreiten würde. Aber wenn du einen kleineren Durchschnitt wünschst, wie 30 Zahlen oder so, das ist eine sehr einfache und schnelle Möglichkeit, es zu tun. Antwortete Jun 12 12 um 4:44 1 auf deinem Post. Der exponentielle gleitende Durchschnitt kann das Alpha variabel sein. So kann es verwendet werden, um Zeitbasis-Mittelwerte (z. B. Bytes pro Sekunde) zu berechnen. Wenn die Zeit seit dem letzten Akkumulator-Update mehr als 1 Sekunde ist, lassen Sie Alpha 1,0 sein. Andernfalls kannst du alpha sein (usecs seit letztem update1000000). Ndash jxh Grundsätzlich möchte ich den gleitenden Durchschnitt eines laufenden Stroms eines Stroms von Gleitkommazahlen mit den aktuellsten 1000 Zahlen als Datenmuster verfolgen. Beachten Sie, dass die unten genannte Gesamtsumme als Elemente als addreplaced, Vermeidung kostspieliger O (N) Traversal, um die Summe zu berechnen - benötigt für die durchschnittliche - on demand. Insgesamt wird ein anderer Parameter von T verwendet, um z. B. Mit einer langen langen, wenn insgesamt 1000 lang s, ein int für char s, oder ein doppeltes bis total float s. Dies ist ein bisschen fehlerhaft, dass Numsamples an INTMAX vorbeikommen könnten - wenn man sich vorstellt, dass man eine langjährige langjährige langwierige Zeit haben könnte. Oder verwenden Sie ein zusätzliches bool Datenelement, um aufzuzeichnen, wenn der Container zum ersten Mal gefüllt wird, während er Numsamples um das Array herumtreibt (am besten dann umbenannt etwas Unschuldiges wie Pos). Antwortete am 12. Juni 12 um 5:19 man geht davon aus, dass der Quanten-Operator (T-Stichprobe) tatsächlich quasi Operator (T-Probe) ist. Ndash oPless Jun 8 14 um 11:52 oPless ahhh. Gut beobachtet. Eigentlich habe ich gedacht, dass es nicht leer ist () (T Probe), aber natürlich könntest du auch immer Notizen verwenden, die du mochst. Werde reden, danke Wenn du andere erwähnt hast, solltest du einen Filter von IIR (unendliche Impulsantwort) als den FIR (Finite Impulse Response) Filter, den du jetzt benutzt hast, betrachten. Deutsch:. Englisch: v3.espacenet. com/textdoc? DB = EPODOC & ... PN = Es gibt mehr dazu, aber auf den ersten Blick sind FIR-Filter als explizite Windungen und IIR-Filter mit Gleichungen implementiert. Der spezielle IIR-Filter, den ich viel in den Mikrocontrollern verwende, ist ein einpoliger Tiefpassfilter. Dies ist das digitale Äquivalent eines einfachen R-C-Analogfilters. Für die meisten Anwendungen, diese haben bessere Eigenschaften als die Box-Filter, die Sie verwenden. Die meisten Verwendungen eines Kastenfilters, dem ich begegnet haben, sind ein Ergebnis von jemand, der nicht in der digitalen Signalverarbeitung Klasse auffällt, nicht als Ergebnis der Notwendigkeit ihrer besonderen Eigenschaften. Wenn du nur hohe Frequenzen abschwächen willst, die du kennst, ist Rauschen, ein einpoliger Tiefpassfilter ist besser. Der beste Weg, um ein digital in einem Mikrocontroller zu implementieren, ist in der Regel: FILT lt-- FILT FF (NEU - 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 anpasst. Schauen Sie sich diesen Algorithmus an und sehen Sie, dass für FF 0 der Filter unendlich schwer ist, da sich die Ausgabe niemals ändert. Für FF 1 ist das eigentlich kein Filter, da der Ausgang gerade dem Eingang folgt. Nützliche Werte sind dazwischen. Bei kleinen Systemen wählst du FF auf 12 N, so dass die Multiplikation mit FF als rechte Verschiebung durch N Bits erreicht werden kann. Zum Beispiel könnte FF 116 sein und die Multiplikation mit FF daher eine rechte Verschiebung von 4 Bits. Andernfalls braucht dieser Filter nur einen Subtrakt und man fügt hinzu, obwohl die Zahlen in der Regel breiter als der Eingabewert sein müssen (mehr auf numerische Genauigkeit in einem separaten Abschnitt unten). Ich nehme normalerweise AD-Lesungen 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 dBoctave über der Rolloff-Frequenz. Allerdings ist für AD-Lesungen in der Regel mehr relevant, um den Filter im Zeitbereich zu betrachten, indem man seine Schrittantwort berücksichtigt. Dies sagt Ihnen, wie schnell Ihr System eine Veränderung sehen wird, wenn das, was Sie Änderungen messen. Um das Entwerfen dieser Filter zu erleichtern (was nur bedeutet, FF zu wählen und zu entscheiden, wie viele von ihnen zu kaskaden), verwende ich mein Programm FILTBITS. Sie legen die Anzahl der Schaltbits für jede FF in der kaskadierten Filterreihe fest und berechnet die Sprungantwort und andere Werte. Eigentlich laufe ich in der Regel über mein Wrapper-Skript PLOTFILT. Dies führt FILTBITS, die eine CSV-Datei macht, dann die CSV-Datei. Zum Beispiel ist hier das Ergebnis von PLOTFILT 4 4: Die beiden Parameter zu PLOTFILT bedeuten, dass es zwei Filter gibt, die von der oben beschriebenen Art kaskadiert sind. Die Werte von 4 geben die Anzahl der Verschiebungsbits an, um die Multiplikation mit FF zu realisieren. Die beiden FF-Werte sind also in diesem Fall 116. Die rote Spur ist die Einheit Schritt Antwort, und ist die Hauptsache zu betrachten. Zum Beispiel sagt Ihnen dies, dass, wenn sich die Eingabe sofort ändert, die Ausgabe des kombinierten Filters auf 90 des neuen Wertes in 60 Iterationen absetzen wird. Wenn Sie sich um 95 Zeit entscheiden, dann müssen Sie auf 73 Iterationen warten, und für 50 Einschwingzeit nur 26 Iterationen. Die grüne Spur zeigt Ihnen die Ausgabe von einer einzigen Amplitude. Dies gibt Ihnen eine Vorstellung von der zufälligen Geräuschunterdrückung. Es sieht so aus, als würde kein einzelnes Beispiel mehr als eine 2,5-Änderung in der Ausgabe verursachen. Die blaue Spur ist, ein subjektives Gefühl zu geben, was dieser Filter mit weißem Rauschen macht. Dies ist kein rigoroser Test, da es keine Garantie gibt, was genau der Inhalt von den zufälligen Zahlen war, die als der weiße Rauschen Eingang für diesen Lauf von PLOTFILT ausgewählt wurden. Sein nur, um Ihnen ein grobes Gefühl zu geben, wie viel es zerquetscht wird und wie glatt es ist. PLOTFILT, vielleicht FILTBITS, und viele andere nützliche Sachen, vor allem für PIC Firmware-Entwicklung ist in der PIC Development Tools Software-Version auf meiner Software-Downloads-Seite verfügbar. Hinzugefügt über numerische Präzision sehe ich aus den Kommentaren und nun eine neue Antwort, dass es Interesse an der Diskussion über die Anzahl der Bits, die benötigt werden, um diesen Filter zu implementieren. Beachten Sie, dass die Multiplikation mit FF log 2 (FF) neue Bits unterhalb des Binärpunktes erzeugt. Bei kleinen Systemen wird FF gewöhnlich als 12 N gewählt, so daß diese Multiplikation tatsächlich durch eine rechte Verschiebung von N Bits realisiert wird. FILT ist also meist ein Fixpunkt ganzzahlig. Beachten Sie, dass dies nicht ändern, eine der Mathematik aus der Prozessoren Sicht. Wenn Sie z. B. 10-Bit-AD-Messwerte und N 4 (FF 116) filtern, benötigen Sie 4 Bruchbits unterhalb der 10-Bit-Integer-AD-Messwerte. Eine der meisten Prozessoren, youd machen 16-Bit-Integer-Operationen aufgrund der 10-Bit-AD-Messwerte. In diesem Fall können Sie immer noch genau die gleichen 16-Bit-Integer-Opertionen, aber beginnen mit der AD-Lesungen links um 4 Bits verschoben. Der Prozessor kennt den Unterschied nicht und muss nicht. Die Mathematik auf ganzen 16-Bit-Ganzzahlen zu machen, funktioniert man, wenn man sie als 12,4 Fixpunkt oder wahre 16-Bit-Ganzzahlen betrachtet (16,0 Fixpunkt). Im Allgemeinen müssen Sie N Bits jeden Filterpol hinzufügen, wenn Sie nicht möchten, dass Rauschen aufgrund der numerischen Darstellung hinzufügen. Im obigen Beispiel müsste der zweite Filter von zwei 1044 18 Bits haben, um keine Informationen zu verlieren. In der Praxis auf einer 8-Bit-Maschine, die bedeutet, dass Sie 24-Bit-Werte verwenden. Technisch würde nur der zweite Pol von zwei den größeren Wert benötigen, aber für die Firmware-Einfachheit verwende ich gewöhnlich dieselbe Darstellung und damit denselben Code für alle Pole eines Filters. Normalerweise schreibe ich eine Unterroutine oder ein Makro, um einen Filterpolbetrieb durchzuführen, dann das auf jeden Pol anwenden. Ob eine Subroutine oder ein Makro davon abhängt, ob Zyklen oder Programmspeicher in diesem Projekt wichtiger sind. So oder so, ich benutze einen Scratch-Zustand, um NEU in den Subroutinemacro zu übergeben, der FILT aktualisiert, aber auch Lasten, die in den gleichen Kratzer Zustand NEU war in. Dies macht es einfach, mehrere Pole anzuwenden, da die aktualisierte FILT von einem Pole die NEU ist Der nächsten. Wenn eine Subroutine, ist es nützlich, einen Zeiger auf FILT auf dem Weg in, die aktualisiert wird, um nur nach FILT auf dem Ausweg. Auf diese Weise arbeitet die Subroutine automatisch auf aufeinanderfolgenden Filtern im Speicher, wenn sie mehrmals aufgerufen wird. Mit einem Makro brauchst du keinen Zeiger, da du die Adresse passierst, um bei jeder Iteration zu operieren. Code Beispiele Hier ist ein Beispiel für ein Makro wie oben für ein PIC 18 beschrieben: Und hier ist ein ähnliches Makro für ein PIC 24 oder dsPIC 30 oder 33: Beide Beispiele werden als Makros mit meinem PIC Assembler Preprocessor implementiert. Die mehr fähig ist als die eingebauten Makroanlagen. Clabacchio: Ein anderes Problem, das ich erwähnt habe, ist die Firmware-Implementierung. Sie können einmal ein einzelnes Pole-Tiefpassfilter-Subroutine schreiben, dann mehrmals anwenden. In der Tat schreibe ich normalerweise eine solche Unterroutine, um einen Zeiger in den Speicher auf den Filterzustand zu bringen, dann schalte ihn der Zeiger vor, damit er nacheinander einfach aufgerufen werden kann, um mehrpolige Filter zu realisieren. Ndash Olin Lathrop 20. April um 15:03 1. Vielen Dank für eure Antworten - alle von ihnen. Ich habe mich entschlossen, diesen IIR Filter zu verwenden, aber dieser Filter wird nicht als Standard LowPass Filter verwendet, da ich die Zählerwerte durchschnittlich vergleichen und sie vergleichen muss, um Änderungen in einem bestimmten Bereich zu erkennen. Da diese Werte von sehr unterschiedlichen Dimensionen je nach Hardware abhängen, wollte ich einen Durchschnitt nehmen, um auf diese Hardware-spezifischen Änderungen automatisch reagieren zu können. Ndash sensslen Wenn Sie mit der Beschränkung einer Macht von zwei Anzahl von Gegenständen zu durchschnittlich (dh 2,4,8,16,32 usw.) leben können, dann kann die Kluft einfach und effizient auf einem getan werden Low-Performance-Mikro mit keine dedizierten Divide, weil es als Bit-Shift durchgeführt werden kann. Jede Schicht rechts ist eine Potenz von zwei zB: Das OP dachte, er habe zwei Probleme, die sich in einem PIC16 und Speicher für seinen Ringpuffer teilen. Diese Antwort zeigt, dass die Teilung nicht schwierig ist. Zugegeben, es adressiert nicht das Speicherproblem, aber das SE-System erlaubt teilweise Antworten, und Benutzer können etwas von jeder Antwort für sich selbst nehmen oder sogar bearbeiten und kombinieren other39s Antworten. Da einige der anderen Antworten eine Aufteilungsoperation erfordern, sind sie ähnlich unvollständig, da sie nicht zeigen, wie man dies effizient auf einem PIC16 erreichen kann. Ndash Martin Apr 20 12 at 13:01 Es gibt eine Antwort für eine echte gleitende durchschnittliche Filter (aka boxcar Filter) mit weniger Speicherbedarf, wenn Sie nicht Bedenken Downsampling. Es heißt ein kaskadierter Integrator-Kamm-Filter (CIC). Die Idee ist, dass Sie einen Integrator haben, den Sie Unterschiede über einen Zeitraum nehmen, und das Schlüssel speichersparendes 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 umzustandsmuster zu halten. Offensichtlich können Sie eine bessere Leistung erzielen, wenn Ihr stateize und decimationFactor Potenzen von 2 sind, so dass die Division und Restbetreiber durch Verschiebungen und Masken ersetzt werden. Postscript: Ich bin einverstanden mit Olin, dass Sie immer einfache IIR-Filter vor einem gleitenden durchschnittlichen Filter beachten sollten. Wenn Sie nicht die Frequenznulls eines Boxcar Filters 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 (wobei ein High-Sample-Rate-Input und Mittelwertbildung für die Verwendung durch einen Low-Rate-Prozess) dann ein CIC-Filter kann genau das, was Sie suchen. (Vor allem, wenn Sie stateize1 verwenden und den Ringpuffer insgesamt mit nur einem vorherigen Integrator-Wert vermeiden können). Hierbei handelt es sich um eine eingehende Analyse der Mathematik hinter dem ersten Auftrags-IIR-Filter, den Olin Lathrop bereits auf der Digital Signal Processing Stack Exchange beschrieben hat (Enthält viele schöne Bilder.) Die Gleichung für diesen IIR-Filter ist: Dies kann mit nur Integern und keine Teilung mit dem folgenden Code implementiert werden (könnte einige Debugging, wie ich aus dem Speicher war.) Dieser Filter nähert sich einem gleitenden Durchschnitt von Die letzten K-Samples durch Setzen des Wertes von alpha auf 1K. Führen Sie dies im vorherigen Code durch, indem Sie BITS auf LOG2 (K) definieren, dh für K 16 gesetzt BITS bis 4, für K 4 gesetzt BITS bis 2, etc. (Ill überprüfen Sie den hier aufgeführten Code, sobald ich eine Änderung bekomme und Beantworten Sie diese Antwort, wenn nötig.) Beantwortet Jun 23 12 um 4:04 Heres ein einpoliger Tiefpassfilter (gleitender Durchschnitt mit Cutoff-Frequenz CutoffFrequenz). Sehr einfach, sehr schnell, funktioniert super und fast kein Speicher oben. Hinweis: Alle Variablen haben Umfang über die Filterfunktion hinaus, mit Ausnahme der übergebenen newInput Hinweis: Dies ist ein einstufiger Filter. Mehrere Stufen können zusammenkaskadiert werden, um die Schärfe des Filters zu erhöhen. Wenn Sie mehr als eine Stufe verwenden, müssen Sie den DecayFactor (bezogen auf die Cutoff-Frequency) anpassen, um zu kompensieren. Und offensichtlich alles, was Sie brauchen, ist die beiden Linien irgendwo platziert, sie brauchen nicht ihre eigene Funktion. Dieser Filter hat eine Rampenzeit, bevor der gleitende Mittelwert des Eingangssignals entspricht. 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. (CutoffFrequencySampleRate) hat einen Bereich zwischen 0 und 0,5. DecayFactor ist ein Wert zwischen 0 und 1, in der Regel in der Nähe von 1. Single-Präzision Schwimmer sind gut genug für die meisten Dinge, ich bevorzuge einfach Doppelzimmer. Wenn du mit Ganzzahlen haften musst, kannst du DecayFactor und Amplitude Factor in gebrochene Ganzzahlen umwandeln, in denen der Zähler als Integer gespeichert ist und der Nenner eine ganzzahlige Potenz von 2 ist (also kannst du dich nach rechts verschieben Nenner anstatt sich während der Filterschleife zu teilen). Zum Beispiel, wenn DecayFactor 0,99, und du willst ganze Zahlen verwenden, kannst du DecayFactor 0,99 65536 64881 einstellen. Und dann, wenn du dich von DecayFactor in deiner Filterschleife vermehrst, verschiebst du einfach das Ergebnis 16. Für weitere Informationen dazu ein exzellentes Buch thats Online, Kapitel 19 auf rekursive Filter: dspguidech19.htm PS Für das Moving Average-Paradigma, ein anderer Ansatz zur Einstellung DecayFactor und AmplitudeFactor, die möglicherweise mehr relevant für Ihre Bedürfnisse, sagen wir, dass Sie wollen die vorherigen, etwa 6 Punkte gemittelt zusammen, tun es diskret, youd fügen Sie 6 Elemente und teilen durch 6, so Sie können den AmplitudeFactor auf 16 und DecayFactor auf (1.0 - AmplitudeFactor) einstellen. Antwortete am 14. Mai 12 um 22:55 Alle anderen haben sich gründlich über den Nutzen von IIR vs. FIR und auf Power-of-Two Division kommentiert. Id nur gerne einige Implementierungsdetails zu geben. Die unten funktioniert gut auf kleinen Mikrocontrollern ohne FPU. Theres keine Multiplikation, und wenn Sie N eine Macht von zwei halten, ist die ganze Teilung Single-Cycle-Bit-Shifting. Grundlegende FIR-Ringpuffer: Halten Sie einen laufenden Puffer der letzten N-Werte und eine laufende SUM aller Werte im Puffer. Jedes Mal, wenn ein neues Sample hereinkommt, subtrahieren Sie den ältesten Wert im Puffer von SUM, ersetzen ihn mit dem neuen Sample, fügen Sie das neue Sample zu SUM hinzu und geben Sie SUMN aus. Modifizierter IIR-Ringpuffer: Halten Sie einen laufenden SUM der letzten N-Werte. Jedes Mal, wenn ein neues Sample kommt, SUM - SUMN, fügen Sie das neue Sample hinzu und geben Sie SUMN aus. Antwortete am 28. August 13 um 13:45 Wenn ich dich gerade richtig lese, beschreibst du einen ersten IIR-Filter, der den Wert, den du subtrahierst, nicht auf den ältesten Wert, der herausfällt, sondern stattdessen der Durchschnitt der vorherigen Werte ist. Erstklassige IIR-Filter können sicherlich nützlich sein, aber ich bin mir nicht sicher, was du meinst, wenn du vorschreibst, dass die Ausgabe für alle periodischen Signale gleich ist. Bei einer Abtastrate von 10 kHz liefert die Zuführung einer 100-Hz-Rechteckwelle in einen 20-stufigen Kastenfilter ein Signal, das für 20 Proben gleichmäßig ansteigt, für 30 sitzt, für 20 Proben gleichmäßig sinkt und für 30 niedrig sitzt. Eine erste Ordnung IIR-Filter Ndash supercat 28. August 13 um 15:31 wird eine Welle, die scharf beginnt zu steigen und allmählich aus der Nähe (aber nicht auf) der Eingabe Maximum, dann scharf beginnt zu fallen und allmählich aus der Nähe (aber nicht auf) die Eingabe Minimum. Sehr unterschiedliches Verhalten Ndash supercat Eine Frage ist, dass ein einfacher gleitender Durchschnitt kann oder nicht nützlich sein. Mit einem IIR-Filter kannst du einen schönen Filter mit relativ wenigen Berechnungen bekommen. Die FIR, die Sie beschreiben, kann Ihnen nur ein Rechteck in der Zeit geben - ein sinc in freq - und Sie können die Seitenkeulen verwalten. Es lohnt sich, ein paar ganzzahlige Multiplikatoren zu werfen, um es zu einem schönen symmetrischen, abstimmbaren FIR zu machen, wenn man die Uhrzeiger ersparen kann. Ndash Scott Seidman Aug 29 13 bei 13:50 ScottSeidman: Keine Notwendigkeit für Multiplikationen, wenn man einfach jede Stufe der FIR entweder den Mittelwert der Eingabe auf diese Stufe und ihren vorherigen gespeicherten Wert ausgibt und dann die Eingabe speichern (falls man hat Der numerische Bereich, man könnte die Summe eher als Durchschnitt verwenden). Ob das besser als ein Kastenfilter ist, hängt von der Anwendung ab (die Sprungantwort eines Kastenfilters mit einer Gesamtverzögerung von 1ms, zum Beispiel, wird eine böse d2dt Spitze haben, wenn die Eingangsänderung und wieder 1ms später, aber das Minimum haben wird Möglich ddt für einen Filter mit insgesamt 1ms Verzögerung). Ndash supercat Als mikeselectricstuff sagte, wenn Sie wirklich brauchen, um Ihren Speicherbedarf zu reduzieren, und Sie dont mind Ihre Impulsantwort ist ein exponentielles (anstelle eines rechteckigen Pulses), würde ich für einen exponentiellen gleitenden durchschnittlichen Filter gehen . Ich benutze sie ausgiebig. Bei dieser Art von Filter brauchst du keinen Puffer. Sie müssen nicht n hintere Proben speichern. Nur einer. Also, Ihre Speicheranforderungen werden durch einen Faktor von N reduziert. Auch brauchst du keine Teilung dafür. Nur Multiplikationen Wenn Sie Zugriff auf Gleitkomma-Arithmetik haben, verwenden Sie Gleitkomma-Multiplikationen. Andernfalls machen ganzzahlige Multiplikationen und verschieben nach rechts. Allerdings sind wir im Jahr 2012, und ich würde Ihnen empfehlen, Compiler (und MCUs) zu verwenden, mit denen Sie mit Gleitkommazahlen arbeiten können. Abgesehen davon, dass mehr Speicher effizienter und schneller (Sie müssen nicht Elemente in jedem kreisförmigen Puffer zu aktualisieren), würde ich sagen, es ist auch natürlicher. Denn eine exponentielle Impulsantwort passt besser der Art, wie sich die Natur verhält, in den meisten Fällen. Beantwortet Apr 20 12 at 9:59 Ein Problem mit dem IIR-Filter, der fast von Olin und Supercat berührt wird, aber anscheinend von anderen nicht beachtet wird, ist, dass die Abrundung einige Ungenauigkeiten (und potenziell Biastruncation) einführt. Unter der Annahme, dass N eine Potenz von zwei ist und nur ganzzahlige Arithmetik verwendet wird, beseitigt das Verschiebungsrecht systematisch die LSBs der neuen Probe. Das heißt, wie lange die Serie jemals sein könnte, der Durchschnitt wird diese niemals berücksichtigen. Nehmen wir z. B. eine langsam abnehmende Reihe an (8,8,8, 8,7,7,7,7,7,6,6) und nehmen an, daß der Durchschnitt in der Tat 8 am Anfang ist. Die erste 7 Probe wird den Durchschnitt auf 7 bringen, was auch immer die Filterstärke. Nur für eine Probe. Gleiche Geschichte für 6, etc. Jetzt an das Gegenteil denken. Die serie geht nach oben Der Durchschnitt wird auf 7 für immer bleiben, bis die Probe groß genug ist, um es zu ändern. Natürlich können Sie für die Vorspannung korrigieren, indem Sie 12N2 hinzufügen, aber das wird nicht wirklich das Präzisionsproblem lösen. In diesem Fall bleibt die abnehmende Reihe für immer bei 8, bis die Probe 8-12 (N2) ist. Für N4 zum Beispiel wird jede Probe über Null den Durchschnitt unverändert halten. Ich glaube, eine Lösung dafür würde bedeuten, einen Akkumulator der verlorenen LSBs zu halten. Aber ich habe es nicht weit genug gemacht, um Code bereit zu haben, und ich bin nicht sicher, dass es nicht schaden würde die IIR Macht in einigen anderen Fällen von Serien (zum Beispiel, ob 7,9,7,9 würde durchschnittlich 8 dann). Olin, deine zweistufige Kaskade braucht auch eine Erklärung. Meinst du mit zwei durchschnittlichen Werten, mit dem Ergebnis der ersten in die Sekunde in jeder Iteration eingegeben. Was ist der Vorteil davon

No comments:

Post a Comment