PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Messwert durch arithm. Mittel stabilisieren



Florian.
21.08.2009, 00:54
Hallo,

ich überlege jetzt schon seit Stunden wie ich es am einfachsten machen könnte eine schwankende Messung stabil zu bekommen. Also z.B. über einige Sekunden die Messungen aufaddieren und dann durch die Anzahl der Messungen zu teilen. Habe mir hierzu auch schon einen Sekundentakt gebastelt, wollte die Messwerte in einem Array speichern und per Laufvariable die Anzahl der Messungen mitzählen. Mir scheint das aber alles viel zu kompliziert zu sein. Ich komme jedoch einfach nicht auf eine einfache Lösung.

Ich möchte einfach einen stabilen Wert für die Displayausgabe haben. Es handelt sich um eine Füllstandsanzeige einer Zisterne. Es ist also völlig unkritisch. Da können ruhig einige Sekunden, eigentlich sogar Minuten verstreichen bis die Messung auf dem Display aktualisiert wird. Es stört nur eben wenn die Zahlen ständig "hin und herwackeln".

Wäre für einen Vorschlag dankbar


Gruss Florian.

Felix G
21.08.2009, 06:47
Das was du beschrieben hast ist eine durchaus übliche Vorgehensweise bei derart unkritischen Sachen (natürlich kann man auch einen Tiefpass als FIR oder IIR Filter implementieren, aber in diesm Fall wäre das übertrieben).


Du musst nur aufpassen daß du bei der Summe keinen Überlauf kriegst, ansonsten gibts eigentlich nichts was man falsch machen könnte.


und falls dich die Geschichte mit dem Timer stört, nimm doch stattdessen einfach immer eine feste Anzahl an Werten, dann brauchst du nur mitzählen.

Michael
21.08.2009, 06:52
Hallo Florian.,
richtig, mit einem Array die Werte aufsummieren und durch die Anzahl teilen beruhigt den Meßwert.
Da gibt es verschiedene Ansätze.
Ich würde mit jeder Messung einen (Array-)Zeiger inkrementieren. Der neue Wert kommt dann in die entsprechende Arrayvariable. Dann wird das Array aufsummiert und durch die Anzahl dividiert.

Schwankende Meßwerte entstehen aber auch gerne durch unsaubere Spannungsversorgung bzw. Referenzspannung.

Gruß, Michael

Manf
21.08.2009, 06:54
Bei der gleitenden Mittelung multipliziert man den bestehenden Mittelwert mit 1-w und addiert den mit w multiplizierten aktuellen Messwert.
Dabei ist w beispielsweise 0,1 oder 0,001.
So wird die Verwaltung der Messwerte bei der Mittelung recht einfach.

Besserwessi
21.08.2009, 09:30
Wenn man die Werte nur aufsummieren will, braucht man die Werte gar nicht einzeln speichern. Es reicht, einfach nur den neuen Wert zur Summe zu addieren und mitzuzählen wie viele Werte man schon hat. Das Spart einiges an knappen SRAM.

Man muß am Ende auch nicht genau durch die Zahl der Messerte zu teilen. Es reicht wenn man den dann geänderten Wertebereich berücksichtigt. Wenn man also z.B. 256 Werte Aufsummiert kann man so tun als wäre die Summe das Ergebis eine 18 Bit AD Wandlers, also Werte bis 2^18-1. Die Tatsächliche Auflösung wird natürlich nicht so gut, aber kann duchaus besser als die ursprünglichen 10 Bit werden.

oberallgeier
21.08.2009, 09:41
... möchte ... stabilen Wert für die Displayausgabe ... eine Füllstandsanzeige einer Zisterne ...Ich weiß nicht, wieviel Zu- und Abflüsse Deine Zisterne hat bzw. welche Mengen da bilanziert werden müssen. Aber so ganz allgemein denke ich, dass sich der Pegelstand einer Zisterne nur selten schneller als 1 mm pro Sekunde, eher pro Minute?, ändert.

... können ruhig ... sogar Minuten verstreichen bis ... Display aktualisiert wird ...Also würde ich so etwa im Sekundentakt messen, nach 8 oder 16 Messwerten dividieren (Überlauf war ja schon angesprochen) und das Ergebnis mit dem alten Wert vergleichen. WENN das Ergebnis anders ist als der alte Wert - dann die Anzeige um eins verstellen - sonst nix machen. Wenn das immer noch zu stark schwankt, könnte man die Verstellung nochmal durch einen Counter bremsen - nach mehreren Verstellwünschen in die gleiche Richtung wird verstellt. Diese Beschreibung ist jetzt nicht die blanke theoretische Herleitung, aber so stelle ich mir eben einen möglichen Lösungsweg vor.

Besserwessi
21.08.2009, 10:59
Wenn man das Display etwa jede Sekunde aktualisiert, kann man den Zahlen in der Regel noch folgen. Es stört dann auch nicht mehr wenn die letzte Ziffer doch zwischen 2 Werten schwankt.

Ein begrenzen auf nur kleine Schrittte ist eher hinderlich, besonders für Tests.

Vitis
21.08.2009, 16:12
oder nen ringpuffer ... hat den charme, dass man sich nen trend
errechnen kann

Florian.
23.08.2009, 11:35
Hallo,

entschuldigt bitte, dass ich mich erst jetzt melde. Ich musste spontan weg und bin erst jetzt wieder zu Hause. Ich werde mir jetzt eure zahlreichen Antworten genauer durchlesen und versuchen das in Bascom umzusetzen. Sollte ich hierbei noch Probleme haben würde ich mich nochmal melden.

Vielen Dank

Florian.

Florian.
23.08.2009, 16:20
Hallo,

habe mich jetzt heute nachmittag mit der Bascomumsetzung auseinandergesetzt, aber noch kein zufriedenstellendes Ergebnis erhalten.
Wenn ich aufsummiere dann rennt der Avr ja sehr schnell durch die Schleife mit seinen 16Mhz und ich habe wieder nur eine Momentaufnahme. Mit waitms zu arbeiten ist ja nicht sonderlich elegant, weil der ganze Controller währenddessen nichts macht.
Als ich noch ein paar Single oder Double Variablen zusätzlich deklariert habe kam auch schon die Fehlermeldung, dass ich an die Grenzen der Bascom Testversion hinsichtlich Codeumfang gestoßen bin. Kann das sein mit so einem kleinen Progrämmchen?

Ich würde nämlich noch gerne zusätzlich einen 1wire Temperaturfühler (Ds1820) implementieren. Das ganze soll später auf einem Mega8 laufen. Nur zum programmieren benutze ich der Schnittstelle wegen das RNControl mit Mega32.

Mein vermutlich höchst ineffizienter Code lautet bisher wie folgt:


$regfile = "m32def.dat"
$crystal = 16000000 'Quarzfrequenz
$baud = 9600
$hwstack = 64
$swstack = 64



Dim Aw As Integer 'Analogwert
Dim Men As Integer 'Menüvariable
Dim Volumen As Single
Dim Volint As Integer 'Volumen als Integer
Dim Hoehe As Single
Dim Prozent As Integer '
Dim Balken As Single 'Balkenanzeige LCD
Dim U As Integer 'Konvertierung Single Integer
Dim I As Integer
Dim X As Integer 'Zählvariable For Schleife





'Variable initialisieren:
Men = 1




'LCD:
Config Lcdpin = Pin , Rs = Portb.7 , E = Portb.6 , Db4 = Portb.5 , Db5 = Portb.4 , Db6 = Portb.3 , Db7 = Portb.2
Config Lcd = 16 * 2
Initlcd
Cursor Off
Cls




'ADC:
Config Adc = Single , Prescaler = Auto 'Für Tastenabfrage und Spannungsmessung
Config Pina.7 = Input 'Für Tastenabfrage
Porta.7 = 1 'Pullup Widerstand ein
Start Adc





Cls
Lcd "****Zisterne****"
Wait 2
Cls

Portd.2 = 0
Portd.3 = 0





Config Pind.2 = Output
Config Pind.3 = Input
Config Debounce = 10

Do
Gosub Berechnung
Debounce Pind.3 , 1 , Menuvar , Sub
Gosub Menu

Loop

End




'Volumenberechnung aus Analogwert
Berechnung:

Aw = Getadc(7)
Hoehe = Aw / 240
Hoehe = Hoehe * 23
Volumen = 176.714 * Hoehe
Volint = Volumen
Return


'Menü
Menuvar:

If Pind.3 = 1 Then
Waitms 500
If Pind.3 = 1 Then
Locate 2 , 1
Lcd " "
Incr Men
End If
End If
Return



'Menüauswahl
Menu:

Select Case Men

Case 1 : Lcd " "
Locate 1 , 1
lcd "Fuellstand:"
Locate 2 , 6
Lcd Volint ; " l"


Case 2 : Lcd " "
Locate 1 , 1
lcd "Fuellstand:"
Balken = 0.004 * Volumen
Balken = Round(balken)
U = Balken
For I = 0 To U Step 1
Locate 2 , I
Lcd Chr(0)
Next U


Case 3 : Lcd " "
Locate 1 , 1
lcd "Fuellstand:"
Locate 2 , 6
Prozent = Volumen / 40
Lcd Prozent ; " %"

Case 4 : Men = 0
Cls
Locate 1 , 1
Lcd "Temperatur"


End Select
Return

Explizit geht es um die Berechnungsroutine. Das Volint würde ich gerne auf einem LCD ausgeben und würde gerne verhindern, dass die Anzeige zu sehr schwankt. Aber ich bekomme wie schon gesagt keinen kompakten Code für dieses Problem auf die Reihe.
Vielleicht kann mir jemand den entscheidenden Tipp geben.

Besten Dank

Florian.

Besserwessi
23.08.2009, 17:19
Die Berechnungen mit Fließkommazahlen brauchen relativ viel Platz. Man kann da schon noch einiges zusammenfassen, allerdings wird das Programm dann auch etwas unübersichtlicher. Die Umrechnung von AD-Wert nach Volumen wäre z.B. mit nur einer Multiplication möglich indem man die Konstanten vorab zusammenfaßt.

Es sollte auch möglich sein ganz ohne fließkomma Division auszukommen. z.B. sollte man statt "Prozent = Volumen / 40" besser "Prozent = Volumen *0.025" schreiben.

Edit:
Auf Double sollte man mit der Testversion wohl verzichten. Vor allem nicht beides (single und double nutzen). Irgendwann ist halt die Grenze der Testversion erreicht.


Zum Mittelen:
Der AD wandler liefert immer nur einen Wert für einen Zeitpunkt. Im Idealfall sollte vor den AD wandler ein analoger Filter sein (Anti aliasing Filter). Dadurch werden die hohen Frequenzen Unterdrückt und der AD Wandler kriegt nur noch ein so langsames Signal, dass er das auch richtig erfassen kann. Für ein langsames Eingangssignal wie die Füllstandsmessung werden dann mehrere Menge Werte Zusammengefaßt. Sinnvoll ist es dabei wenigstens über 20 ms (= 1 Periode von 50Hz) zu mitteln.

Für eine Effektive umsetzung sollte man mit 32 Bit integers (bzw. besser noch mit der vorzeichenlosen Version ?) arbeiten. Bei einer festen zahl an AD werten braicht man danach micht teilen, sondern kann die gleich mit der Summe Weiterarbeiten. Nur die Konstante zum Umrechenen ändert sich dann. Am einchsten ist es ohne Warten, denn eine Schleife mit mehr durchläufen braucht ja auch nicht mehr Code. Wenn man will den AD mit der Mittelung im Hintergrund laufen lassen will, könnte man den Interrupts vom AD nutzen und den AD durchlaufen lassen. Das letzte Ergebnis könnte man dann jeweils aus einer Variable abrufen.

recycle
23.08.2009, 17:30
Prinzipiell ghabe ich eigentlich immer dasselbe Problem wie du. Die Werte einfach in einer Schleife mitteln bring nicht viel, wel die Schleife wie du ja selber schreibts zu schnell durchläuft.
speicher kannst du allerdings sparen, wenn du die Werte nicht in Arrays abspeicherst um da den mittelwert draus zu bilden, sondern einfach X-Werte addierst und dann durch X teilst.
Der ADC lieffert ja nur Werte bis maximal 1023. Davon passen 32 in eine Integer-Variable und 64 in eine Word Variable.

Anstatt die Werte in einer Schleiffe aufzunehmen und zu addieren, kannst du auch in einem Interrupt einen Zähler mitlaufen lassen und die Messwerte in der Interruptroutine aufaddieren. Wenn der Zaäühler dann bei X ankommt, teilst du die Summe durch X, gibst den Mittelwert aufs Display oder an eine andere Variable aus und setzt den Zähler zurück.

Wenn deine Werte nicht zu sehr verrauscht sind, kannst du aber auch einfach ein bischen fuschen,indem du den Wert auf dem display einfach nur in grossen abständen, z.B. einmal pro Minute refreshst.

Wenn dein Timer z.B. alle 10 mS einen interrupt auslöst, kannst du darin ja einen Zähler hochlaufen lassen.
In deiner Hauptschleife fragst du dann den Zähler ab, wenn der grösser 6000 ist, ist eine Minute um. Dann refreshst du dein Display und setzt den Zähler zurück.

Florian.
23.08.2009, 23:52
Wäre es also das einfachste wenn ich vor den AD Wandler Eingang einfach einen Tiefpass mit der Grenzfrequenz von ca 50Hz schalte?
Oder lässt sich das unproblematisch softwareseitig umsetzen?

recycle
24.08.2009, 00:41
Wäre es also das einfachste wenn ich vor den AD Wandler Eingang einfach einen Tiefpass mit der Grenzfrequenz von ca 50Hz schalte?

Soweit ich Besserwessi verstehe hilft das bei sehr schnellen Eingangssignalen oder verrauschten Eingangssignalen.

Der eigentliche Messwert fürfte sich bei einer Zisterne nicht so schnell ändern, dass ein Tiefpass zur Mittelung nötig ist.
Im Kehrschluss kann er bei langsam veränderlichen Messwerten aber eigentlich auch nicht viel schaden/verfälschen.

Den Digitalisierungsfehler des AD-Eingangs kann ein Tiefpass aber eigentlich auch nicht verhindern und der reicht vermutlich schon um die letzte stelle in deiner Anzeige "schwanken" zu lassen.

Wenn du den per Software "rausfiltern" musst und es nicht so sehr auf Genauigkeit ankommt, ist es vermutlich am einfachsten das Rauschen auch per Software rauszurechnen.

Deine Idee die Messwerte zu mitteln war doch gar nicht verkehrt. Das das zuviel speicher braucht, liegt daran, dass du die Messwerte in einem Array gespeichert hast, anstatt sie direkt aufzuaddieren.

Wenn du die Messwerte in einem Array speicherst , brauchst du für jeden Messwert 2 Byte, d.h. bei 20 Wertten schon 40 Byte.
Wenn du sie stattdessen direkt in einer Word-Variablen aufsummierst brauchst du für bis zu 64 Messwerte nur 2 Byte.

Die Flackernde Anzeige am Display kannst du auch dadurch wegbekommen, dass du den neuen Messwert mit dem vorherigen vergleichst und nur dann anzeigts, wenn er um x grösser oder kleiner ist als der vorherige, sprich wenn sich der Füllstand der Zisterne tatsächlich nennenswert geändert hat.

Florian.
24.08.2009, 02:39
Den Digitalisierungsfehler des AD-Eingangs kann ein Tiefpass aber eigentlich auch nicht verhindern und der reicht vermutlich schon um die letzte stelle in deiner Anzeige "schwanken" zu lassen.

Stimmt natürlich, das hatte ich nicht bedacht.


Deine Idee die Messwerte zu mitteln war doch gar nicht verkehrt. Das das zuviel speicher braucht, liegt daran, dass du die Messwerte in einem Array gespeichert hast, anstatt sie direkt aufzuaddieren.

Ja, allerdings ist das grundlegende Problem daran, dass die Schleife so schnell durchlaufen wird, dass der Mittelwert wieder nur eine Momentaufnahme zeigt.



Die Flackernde Anzeige am Display kannst du auch dadurch wegbekommen, dass du den neuen Messwert mit dem vorherigen vergleichst und nur dann anzeigts, wenn er um x grösser oder kleiner ist als der vorherige, sprich wenn sich der Füllstand der Zisterne tatsächlich nennenswert geändert hat.

Gute Idee. Ich habe in letzter Zeit studiumsbedingt viel C programmiert, mich aber noch nicht an Winavr GCC herangetraut.

Eine Abfrage wie


if Volumen < Volumen_vorher + 50 then
Volumen = Volumen_vorher
end if


erzeugt immer einen Fehler. Wie muss der korrekte Syntax lauten? Überhaupt wenn ich in Bascom versuche mehrere (arithmetische) Operationen wie z.B. x=5*7+15 auszuführen funktioniert das nicht.

Nochmals Danke an alle, die trotz meiner Fragerei noch nicht aufgegeben haben.

recycle
24.08.2009, 03:40
Ja, allerdings ist das grundlegende Problem daran, dass die Schleife so schnell durchlaufen wird, dass der Mittelwert wieder nur eine Momentaufnahme zeigt.

Stimmt, aber du musst die Schleife ja nicht unbedingt als Schleife ausführen sondern kannst die Messwerte über einen Timer aufnehmen und addieren. In dem timer kannst du dann einen Zähler mitlaufen lassen, der z.B bis 50 hochzählt. Da angekommen setzt du dann den Zähler auf Null zurück und teilst die Summe der Messwerte durch 50.



Wie muss der korrekte Syntax lauten?

Die Syntax von C kenne ich auch nicht, dass was du da gepostet hast sieht aber allgemein mehr nach Basic als nach C aus.

In C würde es glaube ich eher in Richtung If ( Volumen > (Volumen + 50)) {...} aussehen.


Überhaupt wenn ich in Bascom versuche mehrere (arithmetische) Operationen wie z.B. x=5*7+15 auszuführen funktioniert das nicht.
Stimmt, verkettete Operationen werden in Bascom nicht unterstützt.
Darüber ob das gut oder schlecht ist gab es hier schon lange Diskusionen.

Ich finde es manchmal zwar etwas lästig aber eigentlich ganz OK.
Als Programmierlaie gefällt mir vor allem, dass es Programmierer davon abhält gleich soviele Operationen zu verknüpfen, dass anschliessend kein ormaler Mensch den Code nachvollziehen kann ,-)

Statt x = 5*7+15 kannst du in Bascom x= 5*7 : x=x+15 schreiben. Dass verhindert hier auf jeden Fall schon mal, dass man über die gute alte Regel "Punkrechnung vor Strichrechnung" stolpert ;-)

Manf
24.08.2009, 06:01
Die Berechnung bei der gleitenden Mittelung kommt mir irgendwie recht einfach und speicherplatz-schonend vor, deshalb noch einmal kurz zu dem Prinzip.
Zur gleitenden Mittelung mit einer Zeitkonstanten von 1000 Abtastwerten teilt man das bisherige Ergebnis durch 1000 und zieht diesen Wert vom bisherigen Ergebnis ab. Dann hat man noch den 0,999 fachen Mittelwert.
Zu dem Wert addiert man den durch 1000 geteilten aktuellen Messwert.
Das wär's,
Als Anfangsbedingung kann man den ersten Meswert nehmen, und dann auch noch mit einer steigenden Zeitkonstanten in die Mittelung einsteigen.
(Man wird die Zeitkonstante im allgemeinen als runden Binärwert wählen.)

recycle
24.08.2009, 11:45
Zur gleitenden Mittelung mit einer Zeitkonstanten von 1000 Abtastwerten teilt man das bisherige Ergebnis durch 1000 und zieht diesen Wert vom bisherigen Ergebnis ab. Dann hat man noch den 0,999 fachen Mittelwert.
Zu dem Wert addiert man den durch 1000 geteilten aktuellen Messwert.
Das wär's,

Soweit klingt das einfach, einleuchtend und effektiv. Da werde ich wohl mal die Mittelwertbildung in meinen Programmen umstellen :-)
Allgemein macht das aber glaube ich nur Sinn, wenn man mit Fließkommatypen rechnet und stößt Florian dann schnell wieder auf das Problem, dass die 4k seiner Demo-Version überschritten werden.

Ich habe die Integer Messwerte bisher immer in einem Integer aufaddiert und durch die Integer-Anzahl der Messwerte dividiert.
Sprich ganz ohne Fliesskommazahlen. Dachte eigentlich auch das ist genau genug.



Als Anfangsbedingung kann man den ersten Meswert nehmen, und dann auch noch mit einer steigenden Zeitkonstanten in die Mittelung einsteigen.
(Man wird die Zeitkonstante im allgemeinen als runden Binärwert wählen.)

Hier wird es dann schon zu kompliziert für mich.
Dass der Mittelwert nur in Mittelwert ist, wenn man die Messwerte in konstanten Zeitabständen aufnimmt, leuchtet mir noch ein.
Aber in der Berechnung selber geht die Zeit doch gar nicht ein, warum soll es dann möglichst ein runder Binärwert sein?

Oder soll dieses "Manöver" dafür da sein, um den Mittelwert auch am Anfang richtig zu berechnen, wenn man noch gar keine 1000 Messwerte hat und die Division durch 1000 einen falschen Wert liefern würde?
Aber selbst da leuchtet mir nicht ein, wöfür man mit einer Zeitkonstanten und nicht einfach mit der Anzahl der aktuell vorhandenen Messwerte rechnet.

Edit: OK, ich glaube ich habe es. Deine Zeitkonstante ist dasselbe was ich mit "Anzahl der Messwerte" meine.

Manf
24.08.2009, 17:03
Ja, die Zeitkonstante liefert die Analogie zum RC Tiefpass. Dort ist einem die Funktion e^-t/tau vertraut, sie liefert die gleitende Mittelung.
Die Funkton gilt hier auch, wenn die Messwerte in gleichen Zeitabständen kommen, bzw aufgenommen werden.

Besserwessi
24.08.2009, 17:13
Die analoge Filterung sollte vor den AD wandler, um die Lücken zwischen den Wandlungen zu überbrücken. Wo man da die Grenzfrequenz hinlegt ist hier weniger wichtig irgendwas zwischen etwa 10 Hz und 1 kHz sollte passen. Da sollte auch ein einfacher passives RC gleid reichen.

Soweit ich weiss wartet getadc(..) auf 2 Wandlungen und dauert damit Größenordnungsmäßig 0,2 ms. Die Schleife ist damit gar nicht so schnell. Einfach 5000 Werte Aufsummieren und man hat auch einen Sekunde.

Wenn man statt der Testversion von BASCOM GCC nimmt, hat man die Begrenzung auf 4 K nicht und der Code wird meistens auch noch etwas kürzer. Allerdings hat man dann kein Double und einige der vordefinierten Funktionen (z.B. LCD, servo etc.) fehlen.

Die Idee mit dem fließenden Mittel ist auch nicht schlecht, braucht aber in den meisten Ausfürungen eine division und Fließkommazahlen. Es geht ohne, wenn über diesen Trick:
Mittel := (1023*Mittel+ ADwerrt) / 1024

Wobei man die Division bei Integer (32 Bit) auch durch shifts ersetzen kann.

Florian.
24.08.2009, 23:00
Habe ich das mit der gleitenden Mittelung so richtig verstanden?

Ich nehme 3000 als Startwert und mein aktueller Messwert ist 3100

3000-(3000/1000)+(3100/1000)=3000,1

Im nächsten Schritt mit aktuellem Messwert von z.B. 3200:
3000,1-(3000,1/1000)+(3200/1000)=3000,3

Aber 3000,3 ist ja nicht im entferntesten der Mittelwert von 3000 und 3200. Ich habe auch schon mittels Excel Tabelle einige Schritte durchgeführt, aber den gewünschten Effekt bringt das so nicht.

Habe ich Manfs Erklärung falsch aufgefasst, oder das richtige Ergebnis und verstehe es nur nicht es zu deuten?

Gruss

Florian.

recycle
25.08.2009, 00:20
Habe ich Manfs Erklärung falsch aufgefasst, oder das richtige Ergebnis und verstehe es nur nicht es zu deuten?

Die Division durch 1000 in Manfs Beispiel gilt nur, wenn man auch über 1000 Messwerte mittelt.
Du rechest aber mit nur 2 Messwerten, bzw. mit 998 Messwerten die = 0 sind.

Wenn du eine Mittelung über x Werte machst, musst du entweder in deinem Programm berücksichtigen, dass du Anfangs noch keine X-Messwerte hast und entsprechende nur durch die Anzahl der vorhandenen Messwerte teilen, oder dich einfach damit abfinden, dass das Programm nach dem Start erst mal eine ganze Zeit lang falsche Messwerte liefert.

Ich habe mal ein bischen in Bascom mit der fliessenden Mittelung rumgespielt. Dan Code füge ich unten ein, wenn du Bascom hast, kannst du ihn dir anpassen und ihn im Simulator von Bascom laufen lassen.

Der erste Teil des Programms geht von einer Mittelung über 50 Messwerte aus, die Messwerte sind immer genau 512, das Programm ignoriert aber so wie du, dass es am Anfang ja noch gar keine 50 Messwerte gibt.

An der Ausgabe kannst du dann sehen, wie sich das Program langsam auf den korrekten Mittelwert "hinarbeitet". Ganz korrekt wird der Mittelwert gar nicht, dass liegt an der Fliesskommaarithmetik.


Anschliessend kannst du dann selber neue Messwerte eingeben und sehen, wie wenig sich ein einzelner Messwert auf das fliessende Mittel auswirkt.




$sim
$crystal = 16000000
$regfile = "m16def.dat"
$baud = 38400

Dim Messwert As Integer : Messwert = 512
Const Anzahl = 50 ' Anzahl Messwerte

Dim Mittelwert As Single
Dim Mittelwert_int As Integer
Dim Temp As Single

Dim Zaehler As Word : Zaehler = 0

While Mittelwert < 511
Incr Zaehler
Temp = Mittelwert / Anzahl
Mittelwert = Mittelwert - Temp
Temp = Messwert / Anzahl
Mittelwert = Mittelwert + Temp
Print Zaehler ; ": " ; Mittelwert
Wend

'Print Zaehler ; ": " ; Mittelwert


Do
Input "Neuer Messwert: " , Messwert
' For Zaehler = 1 To Anzahl
Temp = Mittelwert / Anzahl
Mittelwert = Mittelwert - Temp
Temp = Messwert / Anzahl
Mittelwert = Mittelwert + Temp
Mittelwert_int = Mittelwert
' Next Zaehler
Print "Neuer Mittelwert: " ; Mittelwert_int

Loop
End

Manf
25.08.2009, 06:01
Bei dem gleitenden Mittelwert über z.B. 1000 Werte stützt sich der vorhandene Mittelwert auf ein Gewicht von tausend Werten. Nun kommen zwei neue Werte und es wid abgestimmt wie groß der Mittelwert aktuell ist.
Da wirken die beiden neuen Werte als kleine Störung.

Wenn der Wert 3100 über 1000 Messungen anliegt dann beeinflußt er das Ergbnis erheblich und nach weiteren 2000 Messungen ist er praktisch übernommen. Die Anzahl 1000 ist etwas unpraktisch zum nachrechen, in EXCEL sollte es aber gehen.

Besserwessi
25.08.2009, 15:57
Das "gleitenden" Mittel ohne ein speichern der Werte, ist kein echtes gleitendes Mittel, sondern mehr ein digitale Version einen Tiefpasses 1. ter Ordnung. Die Zeitkonstante entspricht halt der Zahl durch die geteilt wird (mal Ziet zwischen den Punkten). Bei 1000 als Teiler muss man da schon etwa 5000 Werte abwarten bis man sich wirklich dem neuen Wert genähert hat. Bei einer Rate von rund 5000 Werten pro Sekunde ist das aber nicht so schlimm.

Manf
25.08.2009, 19:02
Genau, es ist ein Tiefpass erster Ordnung, der durch diese Funktion realisiert wird.
Die Filterfunktion dieses Tiefpasses ist eine Mittelung über die vorangegangenen Werte, die in ihrer Gewichtung exponentiell (über der Zeit) abklingen.
Diese Mittelungsfunktion gleitet über die die eingehenden Werte und es ist schon etwas besonderes, dass diese Funktion ohne ein Speichern aller Einzelwerte bestimmt werden kann. Die abklingende Gewichtung wird in jedem Schritt durch die Bewertung des neuen Wertes gegenüber dem bisherigen gewichteten Mittelwert für alle zurückliegenden Werte bestimmt.

Ich wollte das nur noch einmal deutlich machen. Man kann dann ja zur Filterfunktion und ihrer Implementierung stehen wie man will.
Wenn der Tiefpass erster Ordung immer optimal wäre, dann gäbe es ja die ganzen anderen Filterfunktionen nicht.

Florian.
25.08.2009, 22:24
Danke für die ausführlichen Erläuterungen. In der Theorie habe ich die Sache mittlerweile verstanden. Die softwareseitige Umsetzung bzw Anpassung werde ich bestimmt mit Hilfe des Beispiels auf der 1. Seite schaffen.

Gruss

Florian.

recycle
26.08.2009, 03:11
Bei 1000 als Teiler muss man da schon etwa 5000 Werte abwarten bis man sich wirklich dem neuen Wert genähert hat. Bei einer Rate von rund 5000 Werten pro Sekunde ist das aber nicht so schlimm.

Nö, aber bei einer Rate von ca. 1 Messwert pro Sekunde im Bascom Simulator wurde das doch schnell langweilig ;-)

Besserwessi
26.08.2009, 16:54
Im Simulator muß man dann halt von 1000 auf 10 ändern.

recycle
26.08.2009, 16:59
Im Simulator muß man dann halt von 1000 auf 10 ändern.

Naja, auf die Idee bin ich auch gekommen, habe es allerdings auf 50 geändert um den Eindruck wie wenig einzelne Werte das Mittel beeinflussen etwas stärker zu halten.

Gibt es einen speziellen Grund für die 10, weil der Simulator z.B. um Faktor 100 langsamer ist oder ist die 10 einfach nur ein Beispiel?

Besserwessi
26.08.2009, 17:12
Die 10 Waren einfach nur ein Beispiel, so das man die 5*10 Werte zum einschwingen noch sinnvoll abwarten kann.

Vitis
26.08.2009, 20:41
sinnvoll sind auch Stückelungen im 2^x Bereich, sprich
2, 4, 8, 16, 32, 64 etc.
Das erspart einem u.U. ne menge Rechenzeit, man kann sich das
Summe/2 sparen indem man einfach shift Summe, right,1 (2, 3, 4, etc.) macht.

Ceos
26.08.2009, 20:58
man könnte ja auch den Mittelwert errechnen (nicht den arrithmetischen) der ist sehr unempfindlich gegen extremwerte ... einfach 100 werte aufzeichnen und den mittelwert bestimmen

mit nem array kann man das sogar sehr recheneffizient machen wenn der ewrtebereich und die auflösung nicht zu groß gewählt werden ...

recycle
27.08.2009, 02:48
man könnte ja auch den Mittelwert errechnen (nicht den arrithmetischen) der ist sehr unempfindlich gegen extremwerte ... einfach 100 werte aufzeichnen und den mittelwert bestimmen

Du meinst so wie das am Anfang des Threads schon mal Beschrieben wurde? ;-)



mit nem array kann man das sogar sehr recheneffizient machen wenn der ewrtebereich und die auflösung nicht zu groß gewählt werden ...

Womit wir dann wohl endgültig wieder zum Ausgangspunkt des Threads zurückkommen ;-)

Ceos
27.08.2009, 10:39
naja nicht ganz, ich habs so verstanden, dass er werte aufzeichnet und dann aufaddiert und durch die anzahl teilen will ?! DAS ist natürlich unperformant

mir geht es um den echten mittelwert und nicht um das arithmetische mittel wegen der extremwerte .. problematsich ist aber dass der übliche weg zur mittelwertbestimmung über nen sortieralgo geht und das frisst zeit

ihc hab ne lösung die die auflösung auf den verfügbaren speicher begrenzt

was ich meine ist z.B. ein array mit 100mal 1byte

und über dann den index zu arbeiten mit einer einfachen addition um zeit zu sparen, anschließend noch das array durchzählen und man hat seinen mittelwert

ich erkläre es nochmal mit code, der letze satz geht nur mit viel fantasie

ein 100er-array für eine angabe von 0-100 Liter ohne komma


unsigned char maray[100];

while(messen)
{
marray[getFüllstandInLiter()]++;
count++;
if(ausgabe) {
unsigned in mid = count/2;
for(int i = 0; i < 100; i++) {
count-=marray[i];
if(count < mid) {
print(mid);
clearArrayAndCounter();
}
}
}
}

Besserwessi
27.08.2009, 17:35
Für die Anwendung im µC ist das eher unpraktisch, da man oft wenig SRAM hat. Vor allem wenn man mehr als 100 mögliche Werte hat.

Der was hier "echter Mittelwert" genannt wurde ist der Median. Für einige Fälle mit größeren Ausreißern hat das wirklich vorteile, ist aber halt Rechenzeitintensiv und ggf. braicht oft auch viel Speicherplatz.

Eine Alternative wäre es echte Ausreißer durch einen Plausibilitätstest rauszuschmeißen, und dann den Rest zu mitteln. Wenn die Zahl der Messwerte groß ist, stört es auch kaum, wenn kleinere Ausreißer drin bleiben.