Moin! Ich bekomme von einem externen Kompassmodul einen fertig berechneten Kompass-Wert (0.0 bis 359.9 Grad) über eine serielle Schnittstelle (als ASCII mit einer Dezimalstelle und einem Zeilenende als Abschluss). Der Wert ist allerdings mit einem Rauschen von etwa 2 Grad Spitze-Spitze überlagert und taugt noch nicht zur direkten Anzeige - das wackelt zu viel. Da die Messfrequenz ungefähr 30 Hertz beträgt und mein Messobjekt nicht besonders dynamisch ist, könnte ich über ungefähr 20 bis 40 Werte tiefpassfiltern. Das habe ich auch schon umgesetzt und es funktioniert auch ganz gut, die Anzeige schwankt nur um 0.1 Grad. Allerdings gibt es an einer Stelle (logischerweise) ein Problem: Wenn der Wert bei Nordkurs zwischen 359 bis 1 Grad schwankt, erhalte ich als Ergebnis aus dem Tiefpaßfilter 180 Grad als Anzeige. Das ist nun ganz und gar ein unerwünschtes Verhalten! Wie kann man diese Filterung eleganter lösen, so dass ein stabiler Kompasswert herauskommt, insbesondere wenn sich das Messobjekt langsam dreht? Sprich - wie filtert man einen Messwert, der eine Diskontinuität im Bereich hat?
Moinsen! Rechne intern einfach die Umdrehunen mit. Anschließend kannst du den Winkel z.B. durch eine Modulo Operation wieder berechnen. Etwas so:
1 | if((letzterWert<90)&&(wert>180)) umdrehungen--; |
2 | else |
3 | if((letzterWert>270)&&(wert<180))umdrehungen++; |
4 | |
5 | wert += umdrehungen * 360; |
6 | wert = tiefpass(wert); |
7 | wert = wert%360; |
... der lieber mit diesen Parametern:
1 | if((letzterWert<90)&&(wert>270)) umdrehungen--; |
2 | else |
3 | if((letzterWert>270)&&(wert<90))umdrehungen++; |
4 | |
5 | wert += umdrehungen * 360; |
6 | wert = tiefpass(wert); |
7 | wert = wert%360; |
Gerhard schrieb: > Sprich - wie filtert man einen Messwert, der eine Diskontinuität > im Bereich hat? Rechne mit cos(KpK) und sin(KpK). Da tritt keine Diskontinuität auf.
Scheint mir auch die eleganteste Lösung zu sein. Falls du sin & cos einigermaßen brauchbar berechnen kannst. Ansonsten könntest du deinen Wertebereich um 180° drehen, sobald es in die Nähe der 0° geht, damit die Diskontinuität dann woanders liegt. Schwierig wird es, da den Grenzwert einzustellen, denn genau da tritt wieder ein Schalten auf. Vermutlich möchte man das glatte, gefilterte Signal als Entscheidungsgrundlage nehmen und etwas Hysterese einbauen. Das wird vermutlich schwieriger, als es klingt... sollte aber auch machbar sein.
Ein Median-Filter wäre auch noch eine Möglichkeit. Da kommt immer ein sinnvoller Wert heraus. https://de.wikipedia.org/wiki/Rangordnungsfilter
Joe F. schrieb: > Ein Median-Filter wäre auch noch eine Möglichkeit. > Da kommt immer ein sinnvoller Wert heraus. Das ist doch Unfug. Es kommt zwar kein um rund 180° falscher Wert raus, aber bei Anwendung auf Kompasskurse im Bereich 360/0° werden gerade die richtigen Werte (nahe 0/360°) durch die Medianberechung rausgeschmissen. Als Ergebnis aus dem Median kommt nämlich in dem Fall einer raus, der gerade von 0/360° weit weg liegt :-(
Wolfgang schrieb: > Als Ergebnis > aus dem Median kommt nämlich in dem Fall einer raus, der gerade von > 0/360° weit weg liegt :-( Nein, denn man nimmt natürlich den Wert, der in der Mitte der Liste liegt. Nachtrag: Achso, du hast natürlich recht. Sorry. Medianfilter ist Quatsch. Was Sinn machen würde, ist den am häufigsten auftretenden Wert zu nehmen.
Andere Idee: Du berechnest für jeden eingehenden Wert die Abweichung vom aktuell angezeigten Kurs (kann dann auch negativ sein). Wenn die Abweichung des Messwertes >180° ist, nimmst du einen Überlauf über 0° an, und ziehst 360° ab. Wenn die Abweichung <-180° ist, addierst du 360°. Diese Abweichungen mittelst du, rechnest dieses Mittel zum aktuellen Kurs dazu, und bringst das Ergebnis wieder in den Wertebereich 0.0-359.9 (<0 -> +360.0 , >=360 -> -360.0) Das ist dann der neue Kurs.
Das trifft sich gerade prima, mache nämlich just in diesem Moment etwas Ähnliches. N Kompass ist aber nicht dabei :> Wolfgang schrieb: > Rechne mit cos(KpK) und sin(KpK). Da tritt keine Diskontinuität auf. Hättest du Lust und Zeit das zu erläutern? EDIT: Nach etwas googlen habe ich das hier gefunden: sum_i_from_1_to_N sin(a[i]) a = arctangent --------------------------- sum_i_from_1_to_N cos(a[i])
Mann muss übrigens Atan2 benutzen, sonst weiß man nicht in welchem Quadranten sich der Mittelwert befindet (für alle Non-Mathematiker wie mich :>)
Hier die reine Integer-Lösung (mit 0.1° Auflösung)
1 | #define COMPASS_VALUES 30
|
2 | #define TEST_DIRECTION 3599 // 359.9°
|
3 | |
4 | int compass[COMPASS_VALUES]; |
5 | int i; |
6 | int d; |
7 | int d_sum; |
8 | int course; |
9 | |
10 | // create random input values
|
11 | for (i=0; i<COMPASS_VALUES; i++) |
12 | {
|
13 | compass[i] = TEST_DIRECTION + rand()%5 - 2; // main direction +/- 0.2° |
14 | if (compass[i] >= 3600) compass[i] -= 3600; |
15 | if (compass[i] < 0) compass[i] += 3600; |
16 | printf("%2d: %3d.%1d\n", i, compass[i]/10, compass[i]%10); |
17 | }
|
18 | |
19 | // calculate average course derivation
|
20 | course = compass[0]; |
21 | d_sum = 0; |
22 | for (i=1; i<COMPASS_VALUES; i++) |
23 | {
|
24 | d = compass[i] - course; |
25 | if (d > 1800) d -= 3600; |
26 | if (d < -1800) d += 3600; |
27 | d_sum += d; |
28 | }
|
29 | d_sum += COMPASS_VALUES / 2; // round (add 0.5) |
30 | d_sum /= (COMPASS_VALUES); |
31 | course += d_sum; |
32 | |
33 | // normalize
|
34 | if (course >= 3600) course -= 3600; |
35 | if (course < 0) course += 3600; |
36 | |
37 | // output
|
38 | printf("Course: %3d.%1d°\n", course/10, course%10); |
Ergebnis:
1 | 0: 359.7 |
2 | 1: 0.1 |
3 | 2: 0.0 |
4 | 3: 359.9 |
5 | 4: 0.0 |
6 | 5: 359.9 |
7 | 6: 359.8 |
8 | 7: 0.0 |
9 | 8: 359.7 |
10 | 9: 359.8 |
11 | 10: 359.7 |
12 | 11: 359.8 |
13 | 12: 0.1 |
14 | 13: 359.7 |
15 | 14: 0.0 |
16 | 15: 359.9 |
17 | 16: 359.7 |
18 | 17: 0.1 |
19 | 18: 359.9 |
20 | 19: 359.9 |
21 | 20: 359.8 |
22 | 21: 0.1 |
23 | 22: 0.1 |
24 | 23: 359.9 |
25 | 24: 0.1 |
26 | 25: 0.1 |
27 | 26: 359.7 |
28 | 27: 359.8 |
29 | 28: 359.9 |
30 | 29: 0.1 |
31 | Course: 359.9° |
Reginald L. schrieb: > Wolfgang schrieb: >> Rechne mit cos(KpK) und sin(KpK). Da tritt keine Diskontinuität auf. > Hättest du Lust und Zeit das zu erläutern? Guck dir den Kurvenverlauf von sin(x) und cos(x) in der Nähe von 0°/360° an. Da ist alles wunderschön stetig. So ganz unproblematisch ist eine Mittelung bei den Umkehrpunkten aber auch nicht (z.B. cos(x) für x nahe 0). Die Idee von Joe F. ist aber viel praktischer, weil keiner wegen einer simplen Mittelung trigonometrische Funktionen hoch und runter rechnen möchte. Joe F. schrieb: > Andere Idee: ...
Wolfgang schrieb: > Die Idee von Joe F. ist aber viel praktischer, weil keiner wegen einer > simplen Mittelung trigonometrische Funktionen hoch und runter rechnen > möchte. Deshalb überlasse ich das meinem Rechner ;)
Reginald L. schrieb: > Deshalb überlasse ich das meinem Rechner ;) Mit dieser Einstellung wird es auch weiterhin gelingen, Fortschritte in der Rechnerleistung sinnlos durch Faulheit bei der Programmierung und Entwicklung von Algorithmen zu verpulvern.
Wolfgang schrieb: > Mit dieser Einstellung wird es auch weiterhin gelingen, Fortschritte in > der Rechnerleistung sinnlos durch Faulheit bei der Programmierung und > Entwicklung von Algorithmen zu verpulvern. Du kannst ruhig weiterhin stundenlang an der Entwicklung eines Algorithmus sitzen um deinen 4x3GHz Rechner dann mit dem kleinen EinMalEins zu malträtieren :) Es dient letztendlich als Mittel zum Zweck. Möchte man sowas in einen kleinen langsamen 8-Bitter implementieren stellt sich die Frage inwieweit die "Faulheit" noch taugt.
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.
