Hallo,
mir ist durch Zufall aufgefallen, dass beim Filtern (Summe durch
Anzahl=Gleitender Durchschnitt) des AD-Wandlers immer nur die jeneigen
Werte gefiltert werden, welche über dem Duchschnitt liegen. Siehe
Anhang. Die Erklärung hierfür ist, dass beim Teilen der Summe manchmal
kommastelen entstehen. Wenn eine Kommasstelle auftaucht, dann wird
dieser gekürzt. (Na ja, gekürzt wird er nicht wirklich ,denn es gibt
diese Kommastelle in Wirklichkeit garnicht. Wenn z.B. 3/2=1 , oder binär
das Selbe nochmal 11>>1 = 1).
Bsp. Wenn ich einen super Sensor am AD-Wandler habe, welcher 999 mal den
korrekten Wert 10 liefert, und der 1000-ste Wert wird aber 9, dann war
die ganze Filterung(1000x Aufsummierung und Division) umsonst, denn das
Eregebniss ist 9 (,999). Das geht doch nicht!!!!
Wäre die 9 eine Zahl über dem Durchschnitt, ich habs mir mal für den
Beispiel ausgerechnet, zwischen 10 und 1009 so würden diese "im Filter"
verschwinden.
Z.B. mit der 1000-sten Zahl als 15 --> 10005/1000=10 (,005) der Wert ist
OK.
Ich hoffe, dass ich habe mein Problem deutlich beschrieben habe. Nun ist
meine Frage, wie ich das erreichen kann, dass der Filter auch Werte
unter dem Durchschnitt filtert, ohne float verwenden zu müssen.
Danke
Zoltan
Ja, da bin ich auch mal drauf reingefallen.
In Integer mußt Du es so machen:
Summe = Summe - Summe / Anzahl + NeuerWert
Ausgabewert = (Summe + Anzahl / 2) / Anzahl
Peter
Hi Peter,
wie ich sehe verschiebst Du den Durchschnitt um 0,5 nach oben, so dass
jetzt "oberhalb" UND "unterhalb" des verschobenen Durchschnitts Platz
für den Filter gibt.
Mit meinen Werten von oben als Beispiel:
Ausgabewert = (Summe + Anzahl / 2) / Anzahl
Ausgabewert = (9999 + 1000 / 2) / 1000 == 10 (,490)
Warum brauche ich aber die folgende Gleichung (ich habe noch die Klammer
gesezt, sind die korrekt?)
Summe = Summe - (Summe / Anzahl) + NeuerWert
Ich speichere immer die letzten 16 Werte und rechne daraus die Summe:
uint16_t damping_temperature[16];
.
.
.
summe=0;
for(dea=15;dea>0;dea--) //alle um 1 nach links shiften
{
damping_temperature[dea]=damping_temperature[(dea-1)];
}
damping_temperature[0]=(uint16_t)temperature; //an die "0-te" stelle
Neuen Wert schreiben
for(dea=0;dea<16;dea++) //aufsummieren
{
summe+=damping_temperature[dea];
}
temperature=summe/16; //durchschnitt ausrechnen
und vielen Dank für Deine Hilfe!
Meine Methode entspricht einem RC-Filter.
Ich speichere nur die Summe und nicht alle 16 Meßwerte.
Deine Methode entspricht einem Integrator.
Wenn man öfter dividiert, kann man für Division mit Runden eine extra
Funktion schreiben.
Peter
@Michael,
da hast Du recht.
Bei 16 Meßwerten müßte es so aber auch für negative Werte klappen:
int summe, ausgabe;
ausgabe = (summe + 8) >> 4;
Der Trick ist aritmetisches Schieben statt Division.
Peter
>Der Trick ist aritmetisches Schieben statt Division.
Ich sehe nicht wo der Unterschied zwischen /16 oder >> 4 liegen soll? MC
entlasten??
Wofür speziell verwendet man einen RC Filter bzw. einen Integrator? Oder
wann nimmt man einen RC Filter und wann einen Integrator?
@Peter:
Es gibt bessere 'Zufallszahlengeneratoren'. Ich warne davor, Programme
zusammenzutricksen. Wenn der Compiler eine /16 Operation optimiert, dann
soll er das selber machen.
Ich warne auch davor, bei Mittelwertbildungen 'summe' nur als 'int' zu
deklarieren. Mit 'long' ist man auf der sicheren Seite; wird 'mal ein
externer AD-Wandler mit 12 oder mehr Bits verwendet, dann wird die Summe
sehr schnell überlaufen können.
@Michael,
Es ist kein Zufallsgenerator.
der Witz ist, es ist eben keine Division und deshalb darf der Compiler
bei vorzeichenbehafteten Zahlen eine Divsion /16 nicht zu 4* Shift
optimieren !
Man erspart sich durch das Shift die Fallunterscheidung für negative
Zahlen.
Das man den Wertebereich von Variablen überprüfen muß, gilt doch für
jede Variable und hat nichts mit dem Runden zu tun.
Auch eine simple Addition zweier Werte kann schon einen Überlauf
bewirken.
Peter
zoltan,
wenn die messwerte vom A/D wandler 'so gut' sind,
(999mal 10 und nur einmal 9)dann wuerde ich mich
zufrieden geben.
ist bei einer digitalen anzeige nicht +/- 1 digit
ueblich?
ich nehme an du willst kein fliesskomma.
ed
hi edi,
das mit dem 999 war nur ein Beispiel. In Wirklichkeit sieht das ganze so
aus (Anhang). Das Signal ist von einem ADXL202 Beschläunigungssensor,
den ich zur Neigungsmessung benutzen will. Auf dem Bild bedeutet ein
Sprung einen Winkel von 0.03°! Blau ist der unsaubere Eingang vom
AD-Wandler und rot ist der gefliterte. Durch die Filterung wird das
Signal etwas träge, ob das stört muss die Anwendung zeigen(Modell
Uboot).
>ist bei einer digitalen anzeige nicht +/- 1 digit
ueblich?
umso genauer desto besser
>ich nehme an du willst kein fliesskomma.
Nein..
@Zoltan:
>Ich sehe nicht wo der Unterschied zwischen /16 oder >> 4 liegen soll?
Probiere 'mal: printf("%3d %3d", (-1>>4), -1/16);
Der 1.Ausdruck liefert -1, der 2. 0; das ist der Unterschied !
@Peter:
Was Du da machst ist kein Witz, sondern ein gutes Beispiel für schlechte
Programmierung. So macht man es, wenn man nicht problemorientiert
programmiert, sondern einem vermutlich leistungsschwachen Prozessor auf
die Beine helfen will. Gespart wird so garnichts, das Gegenteil ist der
Fall.
Viele Compiler erzeugen fürs Schieben deutlich mehr Inline-Code, als für
den Aufruf einer div-Routine, die zumeist sowieso schon mit gelinkt
wird.
Hi
shiften geht nun mal schneller weil es schon vorhanden ist,das heisst du
kannst schneller abtasten...
vom AD bekommst 10bit+/-1bit genauigkeit das flattert sowieso dh.
0..1023 unsigned int's wenn du 16 davon aufaddierst reicht ein 16 bit
word als summe vollkommen aus
vor dem shiften nimmst du die unteren 4 bit und merkst sie dir ,das sind
deine nachkommastellen mit einer genauigkeit von 1/16 dh. du musst diese
4 bit nur noch mit 1/16 multiplizieren und hast die stellen hinter dem
komma,also 8dezimal oder 1000binaer wuerde 0,5 entsprechen.
dann shiftest du deine summe und hast den vorkommaanteil.
das ganze klappt natürlich nur wenn du vorzeichenlos arbeitest!
zum filtern von störungen ist der medianfilter aber wesentlich besser
z.B. du nimmst 7 werte und sortierst sie aufsteigend der mittlere ist
dein wert also der 4. in dem beispiel.Vorteil es entstehen nur Werte die
auch in der ausgangsmenge vorhanden sind.Nachteil grösserer
Rechenaufwand aber wenn man ausnutzt das 6 davon schon sortiert sind,
muss der 7 nur richtig eingefügt werden.
Viel spass beim programmieren
Hi!
AD-Werte sind von haus aus nicht negativ, sie können nur so
interpretiert werden. Bei der Mittelwertbildung sollte man das Runden
nicht vergessen. Das kann in ASM dann so aussehen:
ldi R16,4 ;4 x schieben
weiter:
clc ;kann eigentl. entfallen, aber immer drann denken!
ror R30 ;angenommener High-Teil
ror R31 ;angenommener Low-Teil
ror R17 ;Rundungsspeicher
dec R16 ;Zähler -1
brne weiter ;wenn nicht 0 springe nach weiter
tst R17 ;Rundungsspeicher auf >="0,5" testen
brpl fertig ;wenn nicht negativ -> fertig
adiw R30,1 ;sonst aufrunden
fertig:
viel Spass Uwe
hoffentlich bleibt das Format erhalten
@Wolfram:
Dir ist sicher bekannt, daß die Auswahl des richtigen statistischen
Verfahrens ausschlaggebend für das Ergebnis einer Versuchsreihe ist. Da
es sich bei den wechselnden AD-Werten um Rauschen und nicht um Störungen
handelt, bringt ein Medianfilter nur Rechenaufwand, aber kein 'besseres'
Ergebnis. Daß Zoltan einen 'nur' 10 Bit AD-Wandler verwendet, steht hier
nirgends, hat aber wohl jeder vermutet.
Man darf nicht glauben, mit einem 10-Bit Wandler bessere Ergebnisse als
10 Bit zu erhalten, mit welcher Zahlenspielerei auch immer. Eine krumme
Kennlinie bleibt auch durch vermeintlich bessere Auflösung krumm.
Ich würde vorschlagen, bei Bedarf einen Wandler mit höherer Auflösung zu
verwenden. Daß AD-Werte von Hause aus immer positiv und immer
rechtsbündig sind, dieser Aussage widerspreche ich. Es kann z.B.
durchaus sinnvoll sein, AD-Werte intern linksbündig zu verarbeiten. Egal
ob 10, 12 oder 16 Bit, die Routinen zur Verarbeitung (Skalierung)
bleiben gleich; die Auflösung wird jedoch verbessert.
Michael
@Michael
"...gutes Beispiel für schlechte Programmierung..."
Das darf doch nicht Dein Ernst sein !
Ich soll also das >>4, welches mir das richtige Ergebnis liefert durch
/16 ersetzen, welches falsch ist, nur um "gut" zu programmieren ???
Das werde ich aber in keinem Fall tun !
Der Funktion steht immer im Vordergrund. D.h. das Programm muß in jedem
Fall logisch (Ergebnis) und zeitlich (Geschwindigkeit) richtig sein.
Erst danach kommt die "Schönheit".
Problemorientiert bedeutet doch nicht, ich mache mir Probleme, indem ich
die falsche Berechnung nehme.
Ich habe auch genug Zeit für 1000-de Divisionen, aber ich brauche das
richtige Ergebnis !
Ich kann natürlich zeigen, daß ich die binäre Zahlenrechnung nicht
verstehe und umständlich das Vorzeichen abtrennen, runden, dividieren
und das Vorzeichen wieder dazumixen, aber das bringt doch nichts.
Der Compiler kennt auch den Unterschied zwischen >> und / ganz genau.
Bei vorzeichen behafteten Zahlen nimmt er auch brav die Division, wenn
ich das so hingeschrieben habe. Nur bei "unsigned" optimiert er /16
durch Verwendung der SWAP-Operation.
Das Wort "Witz" meinte ich in der Bedeutung "Clou" oder "geniale Idee".
Peter
Hallo Peter,
ich meine, man sollte die Schweinereien, wie man sie vor 10-20 Jahren
bei der Assemblerprogrammierung gemacht hat, heutzutage auf C-Ebene
nicht wiederholen - wie genial diese auch immer gewesen sein mögen.
Wie schiebst Du denn die Bits, wenn z.B. der Mittelwert aus 17 oder 19
Einzelwerten gebildet werden soll ? Wie soll denn das aussehen, wenn
mehrere Kanäle gefiltert werden sollen - mit unterschiedlichen Divisoren
? Würgi, würgi, Du verstehn ?
Michael