Forum: Mikrocontroller und Digitale Elektronik Wie baue ich ein digitales Filter auf?


von LeiderNichtVerfügbar A. (Firma: Keine) (strgfn)


Lesenswert?

Digitales Filter für ADC-Werte eines Potentiometers.

Hey Leute, versuche jetzt schon seit längeren ein digitales filter für 
die Steuerung eines DC Motors mittels Potentiometer aufzubauen.

ich versuche derzeit einen gewöhnlichen DC-Motor mit eingebauten Encoder 
als Servomotor zu benutzen.

Dabei lese ich den Potiwert sowie Encoder wert ein und vergleiche diese 
beiden mit einander. Abhängig davon wie weit die Motorposition von der 
Poti Position entfernt ist wird der Motor mit einer Geschwindigkeit an 
die Position des Potis gebracht.

ist der Potiwert weit weg dreht sich der motor also schnell und wird 
gegen ende wenn er näher an den Potentiometerwert kommt langsamer.

Im schnellen versuch, also wenn ich das Potentiometer schnell bewege 
(180 Grad in 2sekundne) funktioniert das schon ganz gut...
auch wenn ich es langsam in eine Richtung bewege folgt der Motor dem 
Potiwert sehr zufriedenstellend.

Bewege ich das Potentiometer jedoch schnell hin und her tut der Motor 
dies natürlich auch und ruckelt bei solchen Bewegungen natürlich enorm.
Bei meiner Anwendung ist es jedoch relativ wichtig das der Motor in so 
einem Fall einfach nichts oder möglichst wenig tut...


Es gibt doch bestimmt eine Möglichkeit wie ich diese ADC-Werte des 
Potentiometers so weiterverarbeiten kann das so ein rauf und 
runterspringen des ADC-Wertes ignoriert wird...

Kenne mich mit digitalen Filtern leider kaum aus, und hoffe das ich hier 
eine gute Erklärung finde

LG

von Sascha (Gast)


Lesenswert?

Mach n gleitendes Mittelwertfilter, da schiebst du immer den neuesten 
ADC Wert in ein Array und berechnest dann den Mittelwert aus den letzten 
n Werten im Array. n sollte eine Zweierpotenz sein, dann kommen nur 
Additionen und Bitshifts vor.

Ist im Grunde ein FIR wo alle Koeffizienten 1 sind.

Gibt auch bessere Filter, aber für deine Anforderungen tuts.

von LeiderNichtVerfügbar A. (Firma: Keine) (strgfn)


Lesenswert?

Aber brauche ich für so ein gleitendes Mittelwertfilter keine konstanten 
bzw. irgendeine Dimensionierung?

Desweiteren frage ich den Wert des Potentiometers mit ca 16MHz ab, wäre 
ein Array in dem ich alle werte speichere dann nicht in kürzester Zeit 
riesig?

im Mikrokontroller Forum habe ich dazu dies gefunden;
Beitrag "gleitender Mittelwert"


und darin diesen kurzen Ausdruck:

new = (old *(n-1) + messwert) / n

könnte das für meine Anwendung funktionieren? bzw. ist n die Anzahl der 
gemessenen Werte?


Ich kann mit irgendwie nicht vorstellen das das so einfach geht, 
speziell weil ich nicht wei wie schnell das Poti hin und her schwingen 
wird (ist immer unterschiedlich) und mit welcher Amplitude es das tut.
Wenn ich das Potentiometer jedoch etwas langsamer hin und her drehe soll 
das immer noch funktionieren.

Wie gesagt, bin Neuling also entschuldige ich mich im vorherein für 
mögliche dumme Fragen bzw. falsche Denkweise.

LG

von LeiderNichtVerfügbar A. (Firma: Keine) (strgfn)


Lesenswert?

Vielleicht liege ich ja völlig falsch, aber nach erneuter Betrachtung 
benötige ich doch lediglich einen Digitalen Tiefpassfilter (also im 
Prinzip eh was du meintest oder?) oder?

Im regulären Fall geht das Potentiometer in 2s 180 Grad in die eine 
Richtung und dann auf die gleich weise wieder zurück... Also steigt der 
ADC wert die ganze Zeit und fällt dann wieder ab.
Lediglich an manchen stellen geht das Potentiometer eben viel schneller 
hin und her und diese Bewegung muss ich verhindern.

Wie könnte ich das machen?

von Joe F. (easylife)


Lesenswert?

16MHz(!!!) abtastrate erscheinen mir für diesen zweck vollkommen 
übertrieben. 16Khz vielleicht?

von Irgendwer (Gast)


Lesenswert?

LeiderNichtVerfügbar A. schrieb:
> Aber brauche ich für so ein gleitendes Mittelwertfilter keine konstanten
> bzw. irgendeine Dimensionierung?
>
> Desweiteren frage ich den Wert des Potentiometers mit ca 16MHz ab, wäre
> ein Array in dem ich alle werte speichere dann nicht in kürzester Zeit
> riesig?

Deine Dimensionierung besteht darin festzulegen wie groß das Array ist.
Mit einem kleine Array z.B. 16 Werte hast ein einzelner Messwert einen 
recht großen Anteil an der Veränderung und das ganze reagiert 
entsprechend "Schnell" ggf aber mit dem Nachteil das dass ganze heftig 
schwingt. Ein Ausreißer in den Messwerten hat aber auch eine 
entsprechend große Auswirkung.
Macht man das Array entsprechend größer z.B. 1024 Werte hat ein 
einzelner Messwert entsprechend weniger Einfluss und das ganze System 
wird "Langsamer" und damit oftmals "Stabiler". Welcher Wert sinnvoll ist 
-> einfach mal verschiedenen Varianten ausprobieren und selbst 
beobachten und lernen... Das ganze ist sehr stark abhängig von der 
konkreten Mechanik.

Insgesamt erscheinen mit deine 16MHz für den ADC generell etwas arg weit 
außerhalb von dem was hier überhaupt sinnvoll ist. Messen in 
nanosekundenbereich für eine System das zig millisekunden benötigt um 
überhaupt zu reagieren (so ein E-Moter ist auf µC Sicht schnarch 
langsam) ist in der Regel eher kontraproduktiv.
Man könnte jetzt natürlich ein Mittelwert-Array mit 8Mio Elementen 
nehmen um deinen derzeitigen "viel hilft viel" Ansatz wieder in 
sinnvollere Bahnen zu lenken:-)

von Michael W. (Gast)


Lesenswert?

Bei 16MHz Abtastung müssen wir aber noch etwas Dezimieren oder ein 
gewaltiges Filter bauen. Wie wäre es mit einem IIR?

filterwert(t+1) = 0,9 * filterwert(t)  +  0,1 * Eingangswert(t)

von Wolfgang (Gast)


Lesenswert?

Markus W. schrieb:
> Bei 16MHz Abtastung müssen wir aber noch etwas Dezimieren oder ein
> gewaltiges Filter bauen. Wie wäre es mit einem IIR?
>
> filterwert(t+1) = 0,9 * filterwert(t)  +  0,1 * Eingangswert(t)

Was soll so ein Filter. Bei 16MSa/s liegt mit solchen 
Filterkoeffizienten die Grenzfrequenz im MHz-Bereich. Außerdem sind 
solch krumme Zahlen für die Koeffizienten ohne FPU mühselig zu 
berechnen.

Die Rechnung ohne FPU ist für Koeffizienten
1
 2^n - 1           1
2
---------  bzw.  -----
3
   2^n            2^n

erheblich schneller, insbesondere wenn man (2^n - 1) in 2^n und -1 
zerlegt.

von LeiderNichtVerfügbar A. (Firma: Keine) (strgfn)


Lesenswert?

die 16 MHz kamen einfach von der Frequenz meines Mikrokontrollers...
Es wäre also besser den ADC-Wert mittels Timer in größeren Abständen zu 
messen? Was wäre eine angemessene Frequenz mit der ich dieses Signal 
einlesen sollte?

von waldi (Gast)


Lesenswert?

Wenn die 16MHz für den ADC stimmen: Kann der ADC so schnell samplen? Je 
nach Controller muss man den ADC runtertakten, damit die 
Sample-And-Hold-Mimik korrekt arbeiten kann.

von Pandur S. (jetztnicht)


Lesenswert?

>Was wäre eine angemessene Frequenz mit der ich dieses Signal einlesen sollte?

Wie schnell aendert sich der Wert ? 2Hz ? Dann sample mit 20Hz, dh alle 
50ms, und als filter den tiefpass nach :
http://www.ibrtses.com/embedded/exponential.html

: Bearbeitet durch User
von Hannäs (Gast)


Lesenswert?

Zunächst mußt Du Dir erstmal über die Frequenzen im Klaren sein, mit 
denen Du es zu tun hast:

Eine sinnvolle Abtastrate für ein Poti liegt nicht im MHz-Bereich. Auch 
nicht bei 16kHz.

Mach Dir mal klar, wie oft Du es überhaupt pro Sekunde schaffst, das 
Poti hin und her zu bewegen. Nähmen wir an, Du würdest es 10 Mal pro 
Sekunde schaffen, dann würde die abgetastete Bewegung als 
Positionsverlauf etwa dreieck-artig mit Abbremsen und Beschleunigen an 
den Scheitelpunkten, fast schon sinus-artig aussehen.

Würdest Du diese Bewegung 20 Mal pro Sekunde abtasten, dann würde man 
einen kleinen Fehler im Verlauf akzeptieren, weil man einen reinen 
Sinusverlauf unterstellt.

Deswegen wurden bei 80er-Jahre Computern Mäuse oft nur mit 50Hz 
abgetastet.

Heutzutage werden Profi-Spieler-Mäuse 200 bis 1000 Mal pro Sekunde 
abgetastet und viele sagen, daß die 1kHz Samplerate absoluter overkill 
sind.

Unabhängig von dieser Diskussion, bist Du mit 200Hz bis 1kHz auf der 
absolut sicheren Seite. Vergiß alles darüber.

Und bei dieser Abtastrate kannst Du dann auch mit 1 - 1/2^n und 1/2^n 
wirkungsvolle Koffizienten berechnen, so daß Du überhaupt etwas von der 
Filterung merkst und trotzdem die Wortlänge nicht übertrieben groß wird.

Versuch z.B. bei 1kHz mal n=6, also 63/64 und 1/64. Das wirst Du spüren 
und dann kannst Du durch Probieren zu Deinem optimalen n kommen.

von Hannäs (Gast)


Lesenswert?

Mach Dir auch noch klar, daß Du ein Wortlängen-Problem hast:

Nehmen wir an, der Wandler liefert 12 Bit, also Integer-Werte zwischen 0 
und 4095 (was übrigens für die Schleiferposition eines Potis auch als 
overkill bezeichnet werden könnte).

Wenn Du nun rechnen würdest:

short adcout; // aktueller Abtastwert vom Wandler

gefiltert = ( 63 * gefiltert + adcout ) / 64;

dann würde bei 16 Bit short ein overflow statt finden, da sie nur ein 
Maximum von 32767 erlauben, Du aber als Maximum in der Rechnung 
63*gefiltert 63*4095= 257985 erhalten kannst.

Und Du hast einen Rundungsfehler.

Tatsächlich müßtest Du also folgendes rechnen (wenn int 32 Bit hat):
gefiltert = (short)( (63 * (int)gefiltert + adcout + 32) / 64 );

wobei 32 die Hälfte von 64 ist (wenn Du also statt 64 mal 32 probierst, 
mußt Du hier dann entsprechend 16 setzen).

von LeiderNichtVerfügbar A. (Firma: Keine) (strgfn)


Lesenswert?

Danke erstmal, hab das ganze jetzt mit einem Timer gelöst und bin sogar 
auf 50Hz runter gegangen...funktioniert auf jedenfall jetzt schon 
wesentlich besser als vorher ;)

Mein Wandle hat liefert 10 Bit, also zwischen 0 und 1024.
das Potentiometer fährt maximal in einer Sekunde ca 200 Grad, also 
relativ langsam...
Das der motor so arg mitgeht wenn ich das Potentiometer schnell hin und 
her drehe ist jetzt schon kaum noch der Fall, werde den Filter aber 
trotzdem einbauen...zur Sicherheit.

gefiltert = (short)( (63 * (int)gefiltert + adcout + 32) / 64 );

ist das Ergebnis dieser Rechnung denn immer eine ganze Zahl?

und was ist der wert "gefiltert", den ich ja zur Berechnung von 
"gefiltert" nehme zum Zeitpunkt null?

LG

von Hannäs (Gast)


Lesenswert?

gefiltert ist immer eine ganze Zahl, deswegen ja die Rundung. Und zu 
Beginn tastest Du einmal das Poti ab und nimmst den Wert als 
Initialisierung. Dann hast Du kein unschönes Einschwingen.

Bei 50Hz ist 64 sicher zuviel. Starte mal mit 16, dann brauchst auch 
kein int-Gecaste, weil es bei max 1023 vom Wandler erst bei über 32 zum 
Overflow kommen kann:

gefiltert = (15 * gefiltert + adcout + 8) / 16;

Gefiltert ist natürlich short

von m.n. (Gast)


Lesenswert?

Hannäs schrieb:
> gefiltert = (15 * gefiltert + adcout + 8) / 16;

Ich bin ja gespannt, wann die ersten Leute (sogenannte 'Profis') 
auftauchen, die eine Multiplikation für 'schädlich' und eine Division 
für total verboten halten ;-)
Beitrag "[AVR] Mittelwertbildung"

von Pandur S. (jetztnicht)


Lesenswert?

Naja. div 16 wird als shift-right-4 ausgefuehrt.
Und mal 15 als shift left 4 minus den Wert selbst

: Bearbeitet durch User
von LeiderNichtVerfügbar A. (Firma: Keine) (strgfn)


Lesenswert?

Vielen Dank, werde ich gleich ausprobieren

kann ich gefiltert auch long machen? Würde für meinen weiteren Code viel 
besser Passen.

Da ich den Potentiometerwert mit dem Encoderzählstand vergleiche müssen 
die beiden ja den gleichen Wertebereich haben...

Mein Potentiometer geht bei 180 Grad Drehung vom ADC von 97 - 887

Der Encoder, bei dem ich die impulse zähle und je nachdem ob er sich 
nach rechts oder links dreht einen Impuls abziehe gehen die Werte von 0 
- 18.000 bei 180 Grad...

ich skaliere also den Potentiometerwert auf den Encoder Wertebereich um, 
also mit der "map" Funktion...

sollte ich den ADC wert filtern bevor ich ihn um skaliere oder besser 
erst danach?

LG

von Pandur S. (jetztnicht)


Lesenswert?

Eine Skalierung skaliert optiomalerweise eh mit 2^N, also links oder 
rechts schieben.
Auf Grad zu skalieren ist etwas sinnlos. Wenn es also um eine 
positionsregelung het, wuerde ich fuer die hoehere Aufloesung in 
Encodereinheiten rechnen. Also ein click des Encoders entspricht einem 
bit beim Rechnen

von Hannäs (Gast)


Lesenswert?

Nun noch den 3. Zahn ziehen (1. war absurd hohe Samplerate, 2. war 
Wortlänge und Auflösung): Optimierung

Es ist absoluter Quatsch bei 50Hz Samplerate, also 16.000.000 Taktzyklen 
der CPU / 50 = 320.000 Taktzyklen pro Abtastwert hier mit explizitem 
Bitshift oder sogar "shift left 4 minus den Wert selbst" 
herumzufuhrwerken.

Mach Dir klar, daß das "/ 16" in der Zeile von mir natürlich vier Bits 
wegwirft, die man genauso gut mitverarbeiten könnte und die beim 
Eliminieren des Zitterns als Abtastartefakt helfen werden.

Ich würde nicht bei so einem Überangebot an Rechenleistung hier geizig 
werden.

FAZIT: Rechne in float, um Fehler durch versehentlich falsches 
Runden/Skalieren zu vermeiden (Du kannst es Dir leisten und machst es 
offenbar später eh):

float gefiltert, adcout,
      coef = 1/16.;      // Der Punkt muß sein

...
gefiltert = (1-coef) * gefiltert + coef * adcout;
...

Dieser Zahn ist am schwersten zu ziehen, da des Deutschen Lieblingskind 
das Knausern und Pingeln ist, und wenn es auch nur um Taktzyklen geht.

Vernünftig ist aber, das Ganze erst einmal exakt gerechnet zum Laufen zu 
bringen. Und wenn es dann real zu CPU-Leistungsproblemen kommen sollte, 
kann man optimieren, weiß aber, wie sich das exakte System verhält.

von m.n. (Gast)


Lesenswert?

Hannäs schrieb:
> ...
> gefiltert = (1-coef) * gefiltert + coef * adcout;
> ...
>
> Dieser Zahn ist am schwersten zu ziehen, da des Deutschen Lieblingskind
> das Knausern und Pingeln ist, und wenn es auch nur um Taktzyklen geht.

Manno, Du bist ja genauso blöd wie ich :-)

von LeiderNichtVerfügbar A. (Firma: Keine) (strgfn)


Lesenswert?

Ich muss zugeben ich hab jetzt nicht alles verstanden:D (also bei den 
shift um 4 bla bla bin ich ausgestiegen...)

also nehme ich weder short noch long, sondern float?

Dann ist denke ich alles klar...der filter ist ein mooving average 
filter oder?

wie bestimmt man diesen koeffizienten? warum ist der genau 1/16?
Muss das für die Doku wissen...

Danke

LG

von Sascha (Gast)


Lesenswert?

Joe F. schrieb:
> 16MHz(!!!) abtastrate erscheinen mir für diesen zweck vollkommen
> übertrieben. 16Khz vielleicht?

Kann auch gar nicht sein, CPU Clock /13 ist die schnellste ADC 
Samplingrate die überhaupt möglich ist.

Bei 16MHz sind das 1,23 MS/s.

Allerdings sind nur ADC Takte bis 4 MHz einigermaßen sinnvoll: 300kS/s.

Und nur bis 1MHz hat man Fehler von unter 1 LSB: 76kS/s. Und die Zahl 
findet man auch im Datenblatt.

Die Koeffizienten eines Moving Average Filter sind alle 1. Das hatte ich 
auch schon geschrieben.
Das ist ein FIR wo alle Koeffizienten 1 sind.

Schulregel zum arithmetischen Mittelwert: "Summe aller Werte, geteilt 
durch die Anzahl der Werte". Das solltest du in Software umsetzen 
können, oder?

Am besten schiebst du die ADC Werte in einen Ringpuffer:
(Achtung, Prinzipmäßiger Pseudocode weil ich mein Projekt dazu grad 
nicht rauskramen will)
1
const uint8_t coefficientCount = 4; //4 Taps hat der Filter
2
uint16_t ADCValues;
3
uint8_t ADCValuesPointer = coefficientCount;//Enthält den Index des aktuellen Abtastwerts
4
5
ISR(ADCfertigVector)
6
{
7
   ADCValues[ADCValuesPointer] = ADC;
8
   ADCValuesPointer++;
9
10
   uint16_t FilteredValue = //Hier arithmetischer Mittelwert über die letzten 4 Werte
11
12
/TODO: Über Wrap-Around von ADCValuesPointer und der Array-Index Arithmetik für FilteredValue nachdenken
13
}

Ich hab das jetzt mit Absicht nicht so gemacht dass man es einfach 
Copypasten kann.

von Hannäs (Gast)


Lesenswert?

Sascha hat jetzt mal was völlig anderes gemacht als, als in den gefühlt 
20 letzten Posts. Das geht tatsächlich in Richtung eines MA-Filters 
(auch FIR genannt), aber die Koeffizienten, das Rotieren des 
Ringbuffers, ...

Vergiß das sofort, es hat gegenüber der jetzigen Lösung nur Nachteile.

Was wir hier besprochen haben ist ein AR-Filter für autoregressiv (oder 
auch IIR). Für die Doku googlest Du mal, das kau ich nicht vor, und dann 
findest Du auch, wie man den Koeffizienten tatsächlich berechnet oder 
auch aufgrund eines gegebenen Koeffizienten auf die Eckfrequenz kommt.

Es ist hier aber nicht sinnvoll, mit Eckfrequenzen zu hantieren, da
1) ein Filter 1. Ordnung eine Flankensteilheit von nur -6dB/Oktave hat
2) weder die reale noch benötigte Bandbreite des Signals bekannt ist
3) kein Analogfilter vor dem AD-Wandler sitzt und damit das Abtastteorem 
sowieso verletzt ist
4) die Punkte 1)-3) sowieso bei dieser Art von Anwendung irrelevant 
werden, da Dir "irgendeine" Art von Glättung auch gereicht hätte.

Also, viel Spaß beim Googlen und schließlich irgendwann Verstehen von 
allem, was hier steht.


m.n. schrieb:
> Manno, Du bist ja genauso blöd wie ich :-)

Mir macht es einfach Spaß, den ganzen Krempel ernsthaft zu durchdenken. 
Muß ja nicht jeder Anfänger immer alle Fehler machen (aber vielleicht 
auch doch).

PS.: Es gibt auch ARMA-Filter ;-)

von Jasson J. (jasson)


Lesenswert?

Ich fand das mal ganz gut
http://www.dspguide.com/pdfbook.htm
In deinem Fall währen dann Kapitel 14, 15, 16 interessant.

von Michael W. (Gast)


Lesenswert?

Wolfgang schrieb:
> Was soll so ein Filter. Bei 16MSa/s liegt mit solchen
> Filterkoeffizienten die Grenzfrequenz im MHz-Bereich.
Im kHz-Bereich, probiere es aus.

> Außerdem sind
> solch krumme Zahlen für die Koeffizienten ohne FPU mühselig zu
> berechnen

Man darf gerne eine Binärzahl verwenden.

von Wolfgang (Gast)


Lesenswert?

Markus W. schrieb:
> Im kHz-Bereich, probiere es aus.

Ohne jetzt genau nachgerechnet zu haben, landet man mit der 
Grenzfrequenz damit um 300kHz. Was anderes erwartet man auch kaum, wenn 
die Sprungantwort nach weniger als 1.4µs auf 90% ist.

Markus W. schrieb:
> Bei 16MHz Abtastung müssen wir aber noch etwas Dezimieren oder ein
> gewaltiges Filter bauen. Wie wäre es mit einem IIR?
>
> filterwert(t+1) = 0,9 * filterwert(t)  +  0,1 * Eingangswert(t)

von Sascha (Gast)


Lesenswert?

Hannäs schrieb:
> Sascha hat jetzt mal was völlig anderes gemacht als, als in den
> gefühlt
> 20 letzten Posts. Das geht tatsächlich in Richtung eines MA-Filters
> (auch FIR genannt)

Ein Moving Average Filter ist ein FIR, aber ein MA wird nicht FIR 
genannt.
Aber ne Implementierung als MA hatte ich ja nur vorgeschlagen, ich 
hindere keinen daran noch Koeffizienten ungleich 1 vor die Werte zu 
setzen.

> aber die Koeffizienten, das Rotieren des
> Ringbuffers, ...
>
> Vergiß das sofort, es hat gegenüber der jetzigen Lösung nur Nachteile.

Welche? Das Ringing im Sperrbereich interessiert hier wohl kaum, dafür 
hat ein FIR keine Stabilitätsprobleme. Für nen Anfänger in DSV imho die 
sichere Alternative.
Und der rotierende Ringbuffer ist wunderbar skalierbar und performant.

> Was wir hier besprochen haben ist ein AR-Filter für autoregressiv (oder
> auch IIR). Für die Doku googlest Du mal, das kau ich nicht vor, und dann
> findest Du auch, wie man den Koeffizienten tatsächlich berechnet oder
> auch aufgrund eines gegebenen Koeffizienten auf die Eckfrequenz kommt.

Beim händischen Drehen eines Potis ist Eckfrequenz praktisch 
Gleichspannung. Der sinnvollere Wert hier ist wohl die Anstiegszeit (wie 
in der Regelungstechnik, 63% Regel und so).

von eProfi (Gast)


Lesenswert?

> Ich bin ja gespannt, wann die ersten Leute (sogenannte 'Profis')
> auftauchen, die eine Multiplikation für 'schädlich' und eine
> Division für total verboten halten ;-)
Bin schon da, du meintest doch mich, oder ?-)

> gefiltert = (short)( (63 * (int)gefiltert + adcout + 32) / 64 );
Selbst wenn ADC 1023  und  gefiltert 1022 ist,
(63*1022 + 1023 + 32) / 64 = 1022,515625 (was zu 1022 abgerundet wird)
kommt man nie auf 1023.

besser:
adcSum += adc - (adcSum / 64);  //64 oder anderes Vielfaches von 2

Nur diese Schreibweise bewirkt, dass der Ausgangswert gleich dem 
Eingangswert werden kann.

Die Summe wächst bis 64*adc an, vergleichbar mit 6 Nachkommabits, wenn 
man mit Fixedpoint-Arithmetik weiterrechnet.
Der TO strgfn wollte doch Werte um 18000 haben, also doch 16 statt 64 
verwenden.
> Der Encoder, bei dem ich die impulse zähle und je nachdem ob er
> sich nach rechts oder links dreht einen Impuls abziehe gehen die
> Werte von 0 - 18.000 bei 180 Grad...


Man kann mit uint16_t bei 10-bit-ADC  ohne Überlauf bis 64 (6 bit) 
gehen, da adc nur 1023 werden kann (1023*64=65472).

von W.S. (Gast)


Lesenswert?

LeiderNichtVerfügbar A. schrieb:
> Kenne mich mit digitalen Filtern leider kaum aus, und hoffe das ich hier
> eine gute Erklärung finde

OK, du hast also ein Poti, das du per Hand drehen kannst und damit 
willst du die Position deines Motors vorgeben. Damit man bei schnellem 
Wackeln am Poti den Motor nicht auch herumschüttelt, soll die 
tatsächliche Positionsvorgabe gefiltert, sprich tiefpaßgefiltert sein. 
Richtig so? Und nun weißt du nicht, wie man einen Tiefpaß per Programm 
macht.

Kein Problem, benutze einen FIR-Filter, das ist ein Filter, wo nur die 
letzten N ADC-Werte als Argumente eingehen. Wieviel N bei dir sein muß, 
kannst du ja mal ausprobieren, das hängt ab von: Abtastfrequenz, Rechen- 
und Speicherkapazität deines µC.

Im allersimpelsten Falle geht so eine Filterberechnung so:

ADCWerte: Array[0..9] OF REAL; // hier für 10 ADC-Werte
i: integer;
Kernel: Array[0..9] OF REAL;
Summe: REAL; // oder int oder double oder was dir paßt

IF NeuerADCWertDa THEN
BEGIN
  // neuen ADC Wert einstapeln
  FOR i=9 downto 1 DO Array[i]=Array[i-1];
  Array[0]=NeuerADCWert;

  // Filtern
  Summe:= 0;
  FOR i:= 0 to 9 DO
    Summe=Summe+(ADCWerte[i]*{Kernel[i]);

  GibResultatAus(Summe / 10);
END

So. Das ist ganz grob das Prinzip. Reale Implementationen arbeiten etwas 
anders, um unnütze Datenbewegungen zu vermeiden und Rechenzeit zu 
sparen. Aber das Grundprinzip ist gleich.

Für einen simplen gleitenden Mittelwert sind alle Zahlen in "Kernel" 1.0 
und für andere Filterformen sehen sie anders aus. Ich geb dir mal ein 
Beispiel für einen Tiefpaß. Die Cutoff-Frequenz ist was relatives, 
nämlich Grenzfrequenz/Abtastfrequenz und sie muß im Bereich kleiner als 
1/2 Abtastfrequenz sein. Kleiner als Abtastfrequenz/Filtergröße ist auch 
albern.
1
// Filterkoeffizienten nach sin(x)/x * Blackmann erzeugen
2
procedure TFiForm.DoGenerate(Cutoff : double);
3
var
4
  D : double;
5
  P, Q, R : double;
6
  i : integer;
7
  M : integer;
8
  M2: integer;
9
begin
10
  D:= Cutoff;
11
  M:= FilterTapnum;
12
  M2:= M div 2;
13
  for i:= 0 to M do
14
  begin
15
    if i = M2
16
    then
17
      P:= D
18
    else
19
      P:= (sin(D * (i - M2))) / (i - M2);
20
21
    // Blackman-Fenster
22
    Q:= 0.42 - 0.5 * cos((2 * PI * i)/M) + 0.08 * cos((4 * PI * i)/M);
23
    R:= P * Q;
24
    FilterTaps[i]:= R;
25
  end;
26
end;

Hoffe, das hilft.

W.S.

von aSma>> (Gast)


Lesenswert?

Servus,
folgende Punkte beachten:
-Spannungsversorgung für Aktoren und Sensoren trennen!
-EMV beachten, d.h. Digitale und Analoge Kabel getrennt verlegen.
-Analoge Kabel verzwirbeln, zusätzlich ein Ferritkern spendieren
-Ferritkern beim messenden Signal.

Dann brauchst du kaum irgendwelche loops zu erstellen.

Ich kenne jetzt nicht deinen µC, aber bei ARM muss der ADC Kanal auf den 
messenden Widerstand eingestellt werden, um ein super Ergebnis zu 
erzielen...

Es gibt verschiedene PWM arten um den Motor anzusteuern. Wenn deine 
H-Brücker nur 2 Zustände kennt, dann könntest du ja einen dritten ihr 
beibringen.

Ich will jetzt nicht weiter ausholen.

mfg

von Michael W. (Gast)


Lesenswert?

eProfi schrieb:
>> gefiltert = (short)( (63 * (int)gefiltert + adcout + 32) / 64 );
> Selbst wenn ADC 1023  und  gefiltert 1022 ist,
> (63*1022 + 1023 + 32) / 64 = 1022,515625 (was zu 1022 abgerundet wird)
> kommt man nie auf 1023.

Eben ein Bit mehr nehmen.


> besser:
> adcSum += adc - (adcSum / 64);  //64 oder anderes Vielfaches von 2
Das ist was anderes und kein Filter! Dem Konstrukt fehlt die Dämpfung.

von Hermann (Gast)


Lesenswert?

Ich habe jetzt nicht alles gelesen, aber ein normaler Tiefpass ist auf 
jeden Fall das Richtige.
Markus W. hat schon den richtigen Ansatz genannt. Man kann es aber auch 
gleich richtig machen.
Man wählt eine Abtastzeit Ta (=1/Frequenz in der man rechnet). Und die 
gewünschte Zeitkonstante Ti des Tiefpasses.
Und jetzt rechnet man in jeden Takt mit Gleitkommarechnung die einfache 
Gleichung:
X[t]=X[t-1]*(1-Ta/Ti)+Y[t]*Ta/Ti
dabei ist:
X[t] ist der aktuelle gefilterte Wert und X[t-1] der vorhergehende
Y[t] ist der aktuell gemessene Messwert

Das ist jetzt ein digitaler Tiefpass, wie er im Buche steht. Jetzt kann 
man sich beliebige Rechenfrequenzen und Filterzeitkonstanten aussuchen. 
Es gilt natürlich Ta<<Ti. Aber Faktor 10 sollte für den Fall reichen. 
Für Ti=1sec reicht also Ta=0,1 also eine Zyklusfrquenz von 10Hz.

von Hermann (Gast)


Angehängte Dateien:

Lesenswert?

Hier zum Spielen eine Berechnung in Excel. Man kann beliebige 
Zeitkonstanten einstellen.

von J. S. (engineer) Benutzerseite


Angehängte Dateien:

Lesenswert?

Markus W. schrieb:
> Eben ein Bit mehr nehmen.

Das wird noch nicht reichen, oder man muss massiv dithern, um die 
Rundungsfehler am Kummulieren zu hindern. Zweckmäßig ist bei einfachem 
Runden eine Auflösung von mindestens einem Faktor 10 = 4 Bit und bei 
Rechnungen wie Quadrierung und Potenzieren (und das IST das hier!) ein 
Faktor 100 oder 7-8 Bit.

Hermann schrieb:

> X[t]=X[t-1]*(1-Ta/Ti)+Y[t]*Ta/Ti

Auch das muss mit erhöhter Auflösung berechnet werden, da das Excel im 
Beispiel mit Real arbeitet (und nur deshalb stimmt!).

Sonst konvergiert der Filterwert nicht.

Wenn man sich entschließt, Dithering zu verwenden, kann man allerdings 
mehrere solcher Rechenblöcken zusammenschalten und bei geringeren 
Auflösungen bleiben, was dann Implementierungsfläche in FPGAs sparen 
hilft.

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.