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
@Peter: Mit Deiner Rundung wirst Du wieder 'drauf reinfallen, sobald neagtive Werte zu runden sind.
@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.
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
@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
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.