AD-Wandler Wert im AVR filtern

Gast #22104
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
Angehängte Dateien:
Gast #22106
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!
Gast #22111
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.
#22113
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
Gast #22115
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..
Angehängte Dateien:
Gast #22116
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.
Gast #22117
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
Gast #22119
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
Gast #22120
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
#22121
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
Gast #22122
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren