Forum: Mikrocontroller und Digitale Elektronik AD-Wandler Wert im AVR filtern


von Zoltan (Gast)


Angehängte Dateien:

Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Zoltan (Gast)


Lesenswert?

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!

von Peter D. (peda)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

@Peter:

Mit Deiner Rundung wirst Du wieder 'drauf reinfallen, sobald neagtive 
Werte zu runden sind.

von Peter D. (peda)


Lesenswert?

@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

von Zoltan (Gast)


Lesenswert?

>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?

von Michael (Gast)


Lesenswert?

@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.

von Reiner Patommel (Gast)


Lesenswert?

Hier gibts was Feines.

http://www-users.cs.york.ac.uk/~fisher/cgi-bin/mkfscript

Ich habe für ein ähnliches Problem ein Bessel Filter 2.Ordnung 
verwendet. Koeffizienten, Gain, Plot und C Code werden direkt berechnet.

Reiner

von Peter D. (peda)


Lesenswert?

@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

von edi (Gast)


Lesenswert?

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

von Zoltan (Gast)


Angehängte Dateien:

Lesenswert?

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..

von Michael (Gast)


Lesenswert?

@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.

von wolfram (Gast)


Lesenswert?

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

von Zoltan (Gast)


Lesenswert?

Danke für die Erläuterung und den Tipp mit dem Medianfilter.

von Uwe (Gast)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

@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

von Peter D. (peda)


Lesenswert?

@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

von Michael (Gast)


Lesenswert?

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

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
Noch kein Account? Hier anmelden.