ich habe einen display, der zeigt 3 Messwerte (Spannung. Strom1, Strom2) es geht um einen Laderegler (Akku-spannung, vervraucher-strom, Ladestrom) die werte sind nicht Stabil, es gibt immer einen Rauschen, und versuche das Softwaremässig zu lösen, ich bin auf die Idee gekommen gleitender mittelwert zu realieseren, hat jemand eine Idee wie das geht? ich habe hier einen Vorschlag aber noch nicht ganz richtig #difine N 16 uint16_t adc_read(uint8_t mux) { uint8_t i; uint16_t result; static Messwert [3]={0,0,0}; ADMUX = mux | (1<<REFS0) ; // Kanal waehlen ADCSRA = (1<<ADEN) | (1<<ADPS0) | (1<<ADPS1) ; // Frequenzvorteiler // setzen auf 8 (1) und ADC aktivieren (1) /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen"*/ ADCSRA |= (1<<ADSC); // eine ADC-Wandlung while ( ADCSRA & (1<<ADSC) ) { ; // auf Abschluss der Konvertierung warten } result = ADCW; Messwert[mux] = ((N-1)*Messwert[mux]+result)/N; ADCSRA &= ~(1<<ADEN); // ADC deaktivieren (2) return (Messwert[mux]); }
1. Es heisst #Define nicht #Difine 2. Du addierst ja gar nichts auf um es anschließend wieder zu teilen um einen Mittelwert zu erhalten. 3. Im AVR-GCC Tutorial gibt es ein gutes Beispiel zum ADC, musst du dir mal angucken.
es geht aber hier um einen gleitenden Mittelwert?? hat jemand einen Vorschlag, wie der C-Code aussehen soll?
>2. Du addierst ja gar nichts auf um es anschließend wieder zu teilen um >einen Mittelwert zu erhalten. > Messwert[mux] = ((N-1)*Messwert[mux]+result)/N; Passt doch.
mcl024 wrote:
> 1. Es heisst #Define nicht #Difine
Weder noch: Es heißt #define (C ist Case-sensitiv!)
mcl024 wrote:
> 1. Es heisst #Define nicht #Difine
Weder noch: Es heißt #define (C ist Case-sensitiv!)
Also leute es geht nicht darum define oder difine!!!!!!!! es geht um den
gleitenden Mittelwert???
weiss vielleicht jemand was ich noch in mein Programm einfügen?? oder
ist das richtig so? (siehe oben)
> Also leute es geht nicht darum define oder difine!!!!!!!! [...] ???
Tja, du kannst dir nicht aussuchen worüber die Leute reden.
Du kannst es nur beeinflussen, indem du keine solchen Vorlagen lieferst.
Also an die eigene Nase fassen.
1.) Was heißt "aber noch nicht ganz richtig"? 2.) Vor der Rechnung ist es sinnvoll, result und messwert ein paar bit nach links shiften, um den Wertebereich auszunutzen und Rundungsfehler zu verringern. Hier ein Beispiel mit 8bit-Wert, der um 8bit nach links geshiftet wird, um die 16bit auszunutzen. Dein 10bit-Wert darf natürlich nur um 6 geshiftet werden.
1 | #define LOW_PASS_VAL 3 |
2 | |
3 | void low_pass(uint8_t *p_speed) |
4 | { |
5 | // tiefpass nach http://www.ibrtses.com/embedded/exponential.html |
6 | |
7 | // startwert bei programmstart. |
8 | static uint16_t speed_filtered = (127 * 256); |
9 | |
10 | // in 16bit umwandeln (aufloesung) |
11 | uint16_t speed_local = ((uint16_t) *p_speed) << 8; |
12 | |
13 | // gleitender mittelwert |
14 | // zuerst subtrahieren, damit nichts ueberlaeuft. |
15 | speed_filtered -= (speed_filtered >> LOW_PASS_VAL); |
16 | speed_filtered += (speed_local >> LOW_PASS_VAL); |
17 | |
18 | *p_speed = (uint8_t) (speed_filtered >> 8); |
19 | } |
20 | |
21 | |
22 | void main(void) |
23 | { |
24 | ... |
25 | foo = get_speed(); |
26 | low_pass(&foo); |
27 | do_something(foo); |
28 | ... |
29 | } |
@ markus Die Formel für gleitenden Mittelwert (exponentielle Glättung): Die Meßwerte einer (Zeit-)Reihe seien M(i) Für den Glättungsfaktor k gilt: 0 <= k <= 1 Das geglättete Ergebnis heiße Mmittel(i). Es gilt: Mmittel(i) = Mmittel(i-1) * (1 - k) + M(i) * k Bei dieser Formel ist die Gleitkomma-Rechnung für AVR-8-Bitter unschön. Das Ganze läßt sich umformen zu Mmittel(i) = Mmittel(i-1) + (M(i) - Mmittel(i-1)) * k Wenn k jetzt geschickt gewählt ist - 1/(2**n) - reduziert sich die gesamte Rechnung auf ein bißchen Shiften und einmal addieren. Bernhard
>> speed_filtered += (speed_local >> LOW_PASS_VAL);
damit wird die Genauigkeit des Meßwertes herabgesetzt, unötigerweise.
Lieber in die andere Richtung shiften, etwa so (Wertebereich beachten!):
#define FILT_KOEFF 5 // = 32 values to filter
...
avg = ((avg << FILT_KOEFF) - avg + adc_data) >> FILT_KOEFF;
Ist verkürzt so: avg(neu) = (31 * avg(alt) + adc_data) / 32
Blackbird
@Blackbird: Das ist schon der richtige Ansatz, aber Rundungsfehler machen das Ergebnis auch wieder unbrauchbar: Nachachrechnen: avg(alt)=250, adc_data=255 : Dein filter wird nie auf die 255 kommen! pseudocode: uint8 avg(uint8 neu) { static uint16 speicher=0; speicher=speicher - speicher/32 + neu; return speicher/32; } oder warscheinlich etwas schneller, liefert dafür aber ein um einen tick "älteres" sample uint8 avg(uint8 neu) { static uint16 speicher=0; uint8 temp; temp=speicher/32;//shiften nur einmal durchführen speicher=speicher - temp + neu; return temp; } Der Trick ist, sich das Zwischenergebnis(bei mir "speicher" bei dir avg selbst) mit höherer Auflösung zu merken.
@Andreas R. (rebirama)
Ja, Du hast Recht. Rundungsfehler ist bei meinem Ansatz immer 1 Digit,
um das das Ergebnis kleiner ist.
Bei uint8-Werten doch schon ein deutlicher Meßfehler, den man sich nicht
auch noch einhandeln muß.
>>Der Trick ist, sich das Zwischenergebnis(bei mir "speicher" bei dir avg
selbst) mit höherer Auflösung zu merken.
Dann bleibt nur noch der Rundungsfehler, hervorgerufen durch die
Division (shiften) mit 32.
Blackbird
Den kann man mit: temp=(speicher + 16) / 32; umgehen. Analog zu Dezimalsystem mit Wert + 0,5 (und dann die Nachkommastellen abschneiden). Blackbird
@ Blackbird: Der Rundungsfehler in deinem Ansatz ist im schlimmsten Fall nicht nur 1 digit, sondern 15 Digits, Beispiel: avg=240, adc=255: da kommt als Ergebniss nach dem Filtern 240,468... wieder selbst bei korrekter Rundung 240 ergibt. Der Filter bleibt praktisch dort stehen. Aber mit dem Runden hast du Recht. Bringt im Mittel ein halbes Digit und lässt sich effizient implementieren. Wieder was dazu gelernt ;-)
bei mir tritt das Rauschen ab 14V (AKKU-Spannung), also unetr 14V sind die Messwerte Stabil. hat jemand eine Idee woran das liegen kann??
Welcher Akkutyp ist das und was macht der, wenn du 14,x V Akkuspannung drauf gibst? Gast der schon? Gasung bei 12 V Bleiakku: 6 Zellen je 2,4 V => 14,4V http://de.wikipedia.org/wiki/Gasung
Korinthenkackerei: Wenn Du wirklich einen gleitenden Mittelwert und keine exponentielle Glättung willst, kommst Du um einen Ringpuffer o.ä. nicht herum, über den Du dann ganz normal den arithmetischen Mittelwert berechnest. Der Unterschied ist bei vielen Anwendungen nicht bedeutend, aber z.B. ein perfektes Rechtecksignal durch einen gleitenden Mittelwert geschickt ergibt ein "Trapezsignal", d.h. symmetrische, linear an und absteigende Flanken, bei der exponentiellen Glättung eben die "Kondensatorlade-entladekurven" (Haifischflossen), die Flanken sind nicht symmetrisch, eine unschöne Verzerrung also. Dazu kommt noch, beim gleitenden Mittelwert über N Schritte, haben Ereignisse, die vor N Schritten passiert sind, garantiert keinen Einfluss mehr auf das Ergebnis, bei der Exponentiellen Glättung dagegen schon, bei manchen Anwendungen ist dieses lange Nachhinken unerwünscht. Beispiel: Wenn man z.B. wie hier mit N=16 rechnet und kriegt nach langer 0 Phase eine Flanke von 0 auf 255 rein, dann ist nach 16 Messwerten der exponentielle geglättete Wert (erster Ordnung) erst auf ~164 geklettert, wenn ich mich nicht verrechnet habe, und erst nach ca. 60 Schritten ist er auf über 250. (Und wenn man noch mehr Aufwand betreiben will, dann kann man auch den Median der im Ringpuffer gespeicherten Werte. Man behält schöne, steile Flanken und Aussetzer verschwinden trotzdem. Aber das ist fast immer Overkill und für manche Anwendungen auch ungeeignet).
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.