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
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.
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
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?
16MHz(!!!) abtastrate erscheinen mir für diesen zweck vollkommen übertrieben. 16Khz vielleicht?
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:-)
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)
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.
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?
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.
>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
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.
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).
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
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
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"
Naja. div 16 wird als shift-right-4 ausgefuehrt. Und mal 15 als shift left 4 minus den Wert selbst
:
Bearbeitet durch User
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
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
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.
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 :-)
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
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.
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 ;-)
Ich fand das mal ganz gut http://www.dspguide.com/pdfbook.htm In deinem Fall währen dann Kapitel 14, 15, 16 interessant.
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.
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)
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).
> 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).
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.
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
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.
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.
Hier zum Spielen eine Berechnung in Excel. Man kann beliebige Zeitkonstanten einstellen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.