Hallo, ich muß einen Mittelwert von m/s berechnen. Die Werte kommen 40 mal pro Sekunde und werden dann jede Sekunde aufs LCD geschrieben. Momentan werden die 40 Strecken und die 40 Zeiten in ein Array geschrieben dann jeweils addiert und dann Strecke durch Zeit ergibt den Wert. Gibt es einere schnellere Lösung vielleicht mit einem Filter? Gruß Michael
Hallo - d.h. du bildest den Mittelwert quasi immer über eine Messreihe von 40 Messungen? Sicher hast du ein festes Abtastraster - dann kannst du dir die Zeit sparen. Aber egal was du machst... auch ein Filter braucht "Altwerte". Was du machst scheint so was wie die gleitende Mittelung >> Tiefpass/Integrator zu sein.
PS - Warum brauchst du soviele Messpunkte - ist die Wegerfassung so ungenau bzw. schwankt das Signal so stark? Evtl. kannst du was an der Auflösung machen und damit den Rechenaufwand verringern.
Hi, als erstes fällt mir auf, dass deine Mittelwertbildung nicht korrekt ist wenn die Zeitabstände nicht identisch sind. Du müsstest jede Strecke jeweils durch ihre Zeit teilen und dann diese Werte mitteln. Meine Empfehlung: Wenn möglich die Hardware an die Anforderung anpassen. Das heißt: - Gleiche Zeitabstände zwischen Messungen. Dann muss man die Zeiten nicht in die Rechnung einbringen. Stattdessen ist die Geschwindigkeit dann Strecke mal (1/T). (1/T) stellt dabei eine Konstante dar. Optimal wäre natürlich eine Zahl, die als Bit-Shift ausgeführt werden kann. Die Strecken werden dann einfach aufaddiert, am Ende einmal mit (1/T) multipliziert und dann durch die Zahl der Messungen geteilt. - Für die Zahl der Messungen möglichst eine Potenz von 2 (z.B 32 oder 64) als Zahl der Messungen wählen, dann kann man die Division als Bit-Shift ausführen. - Diese Operation kann man auch mit der (1/T) Multiplikation kombinieren. Man muss also nur die Strecken addieren und am Ende einmal mit irgendeinem Wert multiplizieren bzw Shiften. Mehr fällt mir nicht ein. Gruß, Detlev
Hallo ein gleitender Mittelwert ist hier einfacher. Für den ersten Wert ist der Mittelwert gleich dem Messwert. Beim 2ten die Summe aus dem letzten Mittelwert gewichtet mit der bisherigen Anzahl der Messungen geteilt durch die neue Anzahl + dem neuen Wert geteilt durch der neuen Anzahl der Messungen. Für 40 Messungen also immer alter Mittelwert geteilt durch 40 mal 39 + neuer Messwert/40. Damit spart man das Array und man hat nach jeder Messung einen neuen Wert.
Deine Ausführungen und die Formel passen nicht zueinander. So wies beschrieben war würde ihrgendwann der Zähler für die Messungen überlaufen. Die Formel ansich passt dann schon eher. Aber auch anders, ist das ganze nicht sehr brauchbar, wenn man bedenkt, dass hier mit teilen und multipliziern gearbeitet wird. Beides sehr unpraktisch wenn man keinen µC mit Multiplikator in Hardware hat. Mit shift-befehlen könnte das womöglich so aussehen: mittelwert - (mittelwert >> 5) + (messwert >> 5) (für 32 Messungen) Dürfte dann doch erheblich schneller als der Ursprünglich code sein. Allerdings kostet das immernoch einiges an Abarbeitungszeit für die shifts. Das Effizienteste dürfte es sein, ein Variable der doppelten breite der Messwerte(16 für 8bit) aufzuaddieren und diese dann nach 2^n Messunge mit n shifts zu teilen.
oder so:
1 | ;================================================================== |
2 | ; Routine für Mittelwert über 256 Messungen (z.B. ADC) |
3 | ;------------------------------------------------------------------ |
4 | ; in 'wert_h' und 'wert_l' steht der Mittelwert, |
5 | ; in 'wert_n' sind die Nachkommastellen, |
6 | ; wl und wh sind Temp-Register |
7 | ; das Register 'null' enthält immer 0 |
8 | |
9 | ;für 16-Bit-Werte: |
10 | in wl,adcl ;ADC-Wert |
11 | in wh,adch ;einlesen |
12 | sub wert_n,wert_l ;1/256 vom Wert |
13 | sbc wert_l,wert_h ;mit Übertrag |
14 | sbc wert_h,null ;subtrahieren, |
15 | add wert_n,wl ;1/256 des neuen |
16 | adc wert_l,wh ;Wertes mit Übertrag |
17 | adc wert_h,null ;addieren |
18 | ;fertig... |
19 | |
20 | ;für 8-Bit-Wert: |
21 | in wl,adch ;linksbündig justierten ADC-Wert einlesen |
22 | sub wert_n,wert ;1/256 vom alten Wert |
23 | sbc wert,null ;mit Übertrag subtrahieren |
24 | add wert_n,wl ;1/256 vom neuen Wert |
25 | adc wert,null ,mit Übertrag addieren |
26 | ;fertig... |
...
@,-,-,-,- Sorry die Beschreibung ist nicht vollständig. Das gilt nur bis der 40ste Wert gemessen wurde, danach wird immer durch 40 geteilt. 40 ist natürlich kein Wert der für einen uC optimal ist. Ich habe diesen Mittelwert in der Labordatenerfassung oft eingesetzt und er funktioniert auf einem PC hervorragend. Für einen uC muß man das halt optimieren. Der Vorteil ist eben das nur der letzte Mittelwert gespeichert werden muß, nachdem die Anzahl der Werte die Sollanzahl erreicht hat.
Ihr müsst aber unterscheiden zwischen Mittelwertbildung über eine definierte Anzahl von Messungen und einem gleitenden Mittelwert, der die einfachste Variante eines IIR-Filters ist. Um die Rechenoperationen bei hoher Genauigkeit möglichst gering zu halten, habe ich immer den skalierten (z.B. *32) Mittelwert gespeichert, dann davon 1/32 abgezogen und den Messwert addiert. Der Mittelwert pendelt sich dann auf dem 32-fachen des Messwertes ein. Mittelwert-=Mittelwert>>5-Messwert oder Mittelwert+=Messwert-Mittelwert>>5 Oft kann man mit dem skalierten Mittelwert einfacher weiterrechnen. Übrigens ist ein gleitendes Filter mit 32 schon ein sehr starkes Filter. Die andere Möglichkeit ist ein echtes IIR-Filter. Die haben den Vorteil, dass sie Störer gut wegfiltern und auf Änderungen schnell reagieren. Allerdings ist die Wahl der benötigten Faktoren für die Filtergleichung eine diffizile Angelegenheit. Für den Digitalfilter-Anfänger ist es nämlich recht schwer, festzustellen, ob der Filter wirklich filtert, oder eher nur Mittelwerte berechnet (aperiodischer Fall). Ein Problem dabei ist auch, dass in der Literatur diese Faktoren unterschiedlich beziffert und deren Vorzeichen unterschiedlich bezeichnet werden.
@Hannes ich habe Deine "Routine für Mittelwert über 256 Messungen (z.B. ADC)" ausprobiert. Mir ist aber noch etwas unklar, wenn ich 256 mal den Wert 100 (wl) einlese, dann erhalte ich als Ergebnis bei der 8-Bit-Variante immer "63". Ist das korrekt und auch so beabsichtigt? Ich dachte, dass nach 256 Messungen und wl=100 auch als Ergebnis 100 errechnet wird? Danke Bernhard
Dein Test ist korrekt. Ein gleitender Mittelwert nähert sich nur sehr langsam an den endgültigen Wert an, vergleiche Aufladung eines Kondensators über einen Widerstand. Der Wert, den du erhältst ist daher (näherungsweise) 100 * (1 - 1/e), und das ist eben 63, wenn du mit 0 startest.
> Ist das korrekt und auch so beabsichtigt?
Wenn ich mir in Excel mal diesen 'gleitenden
Mittelwert' nachbilde, dann sehe ich: Der
Mittelwert hinkt ziemlich hinterher und steigt
nur sehr langsam an. Nach 256 Iterationen ist
tatsächlich erst ein 'Mittelwert' von 63,2
erreicht (erster 'Mittelwert' auf 0 gesetzt
und danach immer 100 hineingerechnet). Selbst
wenn ich den Startwert auf 90 setze, wird nach
256 Iterationen erst ein Wert von 96,3 erreicht.
Meiner Meinung nach sollte der Mittelwert der Geschwindigkeit aus Gesamtstrecke / Gesamtzeit errechnet werden, oder irre ich mich da? Arno
Hallo den Nachteil des "nachhinkens" kann man umgehen wenn man zu Anfang den Mittelwert über die wirklich schon gemessenen Werte berechnet. Wie ich schon geschrieben habe: 1 Wert=1Mittelwert. Bei der 2ten Messung Mittelwert = alter Mittelwert/2 + neuer Wert/2 Bei der 3ten Messung Mittelwert = alter Mittelwert/3*2 + neuer Wert/3 bei der nten Messung Mittelwert = alter Mittelwert/n*(n-1)+neuer Wert/n mit n=Anzahl der Messung wobei n mitläuft bis die Blockgröße erreicht ist. z.B. 32 oder 64 evtl. kann man auch statt der exakten Anzahlen Stufen in 2er Potenzen verwenden. Diese Routine läuft seit Jahren in Messsystemen auf PCs bei vielen Kunden von mir.
Ein kleines Beispiel für eine Mittelwertbildung eines ADC-Wertes. Ein ADC-Wert wird 256 mal eingelesen und die Werte miteinander addiert anschließend erfolgt eine Prüfung (wie beim kaufmannischen Runden) der Nachkommastelle (temp4). Diese Prüfung entscheidet, ob das Ergebnis aufgerundet werden soll oder nicht. Bernhard ;############################################################# ; ADC-WERT 256 mal einlesen und daraus den Mittelwert bilden ; ; OUT: TEMP1 (low) ; TEMP2 MESSEN: push temp3 ; HILFSREGISTER sichern push temp4 push temp5 push temp6 push temp8 ;------------------------------------------------------------- clr temp3 ; HILFSREGISTER CLEAR clr temp4 clr temp5 clr temp6 clr temp8 ;------------------------------------------------------------- ; neuen ADC-Wert lesen Wert verwerfen, ; da meist vom vorherigem ADC-Kanal sbi ADCSRA, ADIF ; logisch "1" löscht ADIF flag ! MESSEN_w: sbis ADCSRA, ADIF ; warten bis ADIF flag gesetzt rjmp MESSEN_w ;------------------------------------------------------------- ; neuen ADC-Wert lesen (Schleife - 256 mal) MESSEN_SCHLEIFE: sbi ADCSRA, ADIF ; logisch "1" löscht ADIF flag ! MESSEN_SCHLEIFE_w: sbis ADCSR, ADIF ; warten bis ADIF flag gesetzt rjmp MESSEN_SCHLEIFE_w ;------------------------------------------------------------- ; ADC einlesen: in temp1, ADCL ; immer zuerst LOW Byte lesen in temp2, ADCH ; danach das mittlerweile gesperrte High Byte ;------------------------------------------------------------- ; alle 256 ADC-Werte addieren add temp4,temp1 ; addieren adc temp5,temp2 ; addieren über Carry adc temp6,temp3 ; addieren über Carry dec temp8 ; Schleifenzähler MINUS 1 brne MESSEN_SCHLEIFE ; ==> SCHLEIFE ;------------------------------------------------------------- ; MITTELWERT-BERECHNUNG (Prüfung der "Kommastelle" temp4) cpi temp4,128 ; "Kommastelle" kleiner als 128 ? brlo MESSEN_MITTELWERT_w ; ist kleiner ==> Sprung ;------------------------------------------------------------- ; Aufrunden ldi temp1,1 ; =1 add temp5,temp1 ; addieren von 1 adc temp6,temp3 ; addieren über Carry MESSEN_MITTELWERT_w: ; Ergebnis nach temp1 und temp2 kopieren mov temp1,temp5 mov temp2,temp6 ;------------------------------------------------------------- pop temp8 ; Hilfsregister wieder herstellen pop temp6 pop temp5 pop temp4 pop temp3 ret ;#################################################################
Genausogut hättest Du temp4 mit 128 initialisieren können und die ganze Aufrundungsgeschichte am Ende wäre überflüssig.
;Genausogut hättest Du temp4 mit 128 initialisieren können Stimmt!!! Sehr gute Idee. Danke
Mal was anderes: der Mittelwert (arithmetisches Mittel) ist schlecht geeignet, um Meßwerte zu glätten. Viel besser ist der Median (Zentralwert), weil damit Ausreißer viel weniger stark gewichtet werden. Allerdings ist die Berechnung etwas aufwendiger (Sortieren einer Tabelle).
Ob median oder oder arithmetisches mittel ist eine frage des zwecks des "Filters": um die wirkung von ausreißern(=messFEHLER) zu verhindern eignet sich der median optimal. Er "glättet" (=tiefpass) allerdings nicht, unter ungünstigsten umständen bekommt man sogar einen sehr "nicht-repräsentativen" Wert. z.b. 9,8,1,9,0 : median= 8 a.mittel =5,4 -> 5 zum reinen glätten eignet sich dann besser das a. mittel. ich hab bei sensoren an einem roboter (die billigen sharp IR enfernungs-teile) mit einer kombination aus median und a. mittel das beste (=zuverlässigste) ergebnis gehabt: 32 messungen, die sortiert, aus den mittleren 16 dann das arithmetische mittel gebildet. zurück zum thema: wenn der controller wenig anderes zu tun hat, als die 40 werte/s zu mitteln, und das LCD zu bedienen; und die aktuelle lösung zufriedenstellende ergebnisse liefert, wär ich glücklich damit. wobei natürlich die rechenzeit in diesem fall auch für eine exzentrische akademiker-lösung reicht ;-)
@Bernhard: Sorry, aus irgendeinem Grund bekam ich keine Mailbanachrichtigung mehr, wenn hier neue Beiträge verfasst wurden. Daher fiel mir der Thread erst jetzt zufällig wieder auf. Aber Deine Frage wurde ja inzwischen bereits so umfassend beantwortet, dass ich auch noch etwas daraus lernen konnte. Danke... Ich benutze diese Routine für sich langsam ändernde Werte, wie z.B. die Batteriespannung. Dabei kommt es mir nicht unbedingt auf das extrem starke Integrieren dieser Routine an, sondern auf ihre Einfachheit und den extrem geringen Bedarf an Rechenzeit. Meist wird der erste Messwert nach dem Reset als Startwert genommen, manchmal (wenn ein Grenzwert zum Auslösen eines Alarms oder einer Notabschaltung überwacht wird) aber auch auf ein Fixwert. Die Idee zu dieser Routine kam mir, als hier jemand jemand Anderem (ich finde den Thread jetzt leider nicht) den Algorithmus erklärt hatte. Demnach sollte vor jeder Messung 1/10 (oder war's 1/100?) des Mittelwertes vom Mittelwert subtrahiert werden und 1/10 (1/100) des neuen Messwertes zum Mittelwert addiert werden. Da mir die (relativ aufwendige) Berechnung des Zehntels bzw. Hundertstels für diesen Zweck unangemessen erschien, stellten ich das auf 1/256 um, womit die Division entfällt, da sie durch Byteshiften ersetzt werden kann. Und seitdem hat sich diese Routine bei mir bestens bewährt. ...
Ich habe die hervorragende Funktion von Hannes Lux genauer in Excel untersucht. Ändert sich das ADC Ergebnis von 0 auf 256, dann dauert es weitere 1030 Messungen, bis der "Mittelwert über 256 Messungen" den Wert 255 erreicht hat. Bei 5 Messungen je Sekunde dauert das ca. 3 Minuten 26 Sekunden. Für viele Zwecke ist das zu langsam. Deshalb als Anlage die Auswertung für den gleitenden Mittelwert über 128/64/32/16/8 und 4 Werte. Hierfür ist lediglich ein zusätzliches Shiften erforderlich, was die Effiziens nur unwesentlich verschlchtert. Ein "Mittelwert über 64 Messungen" eines 10Bit ADC passt noch in ein 16 Bit Register und gleicht sich nach 326 Messungen aus.
@Dai Du sagst es, dass ist der Algorithmus von Hannes wie ich ihn in Excel formuliert habe. Ich hatte die erforderliche Anzahl der Iterationsschritte gesucht bis zum Ausgleich gesucht. Hast Du dafür eine geschlossene Lösung? In meinem Fall geht es um die Auswertung eines PIR-Sensors. Ich erfasse die Pegeländerungen gegenüber dem gleitenden Mittelwert (Auswertung ca. 10Hz). Hier noch der angepasste Code für schnelle Filterung:
1 | ;=============================================================================== |
2 | ; Routine für gleitenden Mittelwert über 2 bis 64 Messungen eines 10Bit-ADC (AVR) |
3 | ;------------------------------------------------------------------------------- |
4 | ; 10 Bit ADC |
5 | ; vereinfacht wird der 2^n-fache Mittelwert fotgeschrieben, mit n=2...64 |
6 | ; im Registerpaar R19:R18 (16Bit) steht der 2^n-fache Mittelwert des ADC |
7 | ; R17:R16 und R20 sind Temp-Register |
8 | |
9 | |
10 | MOV R17, R19 ;Kopieren des alten MW |
11 | MOV R16, R18 |
12 | |
13 | LDI R20, 6 ;2^n (6->64) |
14 | Div_n: |
15 | LSR R17 ;Division des MW durch 2^n |
16 | ROR R16 |
17 | DEC R20 |
18 | BRNE Div_n |
19 | SUB R18, R16 ;MW_neu = MW_alt*(1-1/2^n) |
20 | SBC R19, R17 |
21 | |
22 | IN R16,adcl ;ADC-Wert |
23 | IN R17,adch ;einlesen |
24 | |
25 | ADD R18,R16 ;MW_neu=MW_alt*(1-1/2^n)+ADC |
26 | ADC R19,R17 ;mit Übertrag addieren |
27 | |
28 | ;fertig... |
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.