Hallo,
ich habe auch das Problem mit den Tastern gehabt. Die Lösung mit der Modifikation der Return-Zeile in Funktion PollSwitch finde ich zwar brauchbar, sie bleibt aber meiner Meinung nach eine Quick&Dirty-Lösung. Man kann den Multiplikator variieren (*62, *63, *64, was auch immer, sogar mit Nachkommastellen) um die Werte 0, 1, 2, 4, 8, 16 und 32 zu bekommen wenn EINEN Taster gedrückt wird. Ich glaube aber, dass es sehr schwer ist die 64 Werte aller möglichen Kombinationen korrekt zu bekommen! In der Praxis kommt natürlich sehr selten vor, dass mehrere Taster z.B. 1,2,3,4 bei Kollision gleichzeitig gedrückt werden. Es macht aber trotzdem Sinn: z.B. Wenn ich feststellen will ob der Asuro an der linken oder rechten Seite mit einem Obj. kollidiert (Taster 1 und 2) dann brauche ich 48 (also 110000) und nicht 47 oder 46.
Meine Lösung des Problems heißt "lineare Regression" oder "lineare Ausgleich". Im Prinzip versucht man die Messwerte durch eine Gerade zu beschreiben, wobei die Lage (Parameter) der Gerade die Lösung des Ausgleichsproblems ist. (Anm.: Die Formel in PollSwitch enthält bereits die (Daume-mal-Pi) Lösung des Ausgleichsproblems.)
Nun habe ich folgendes gemacht:
1) Als erstes versuche ich herauszufinden, welche Werte ich zurückbekomme (so genau wie möglich, ohne Info-verlust) wenn ich einen oder mehrere Taster drücke. Dazu habe ich folgendes Prg. verwendet:
Die Messwerte (1023/y) werden sozusagen direkt aufsummiert und Durchschnitt gebildet.Code:#include "asuro.h" /* function to read out switches */ unsigned int testSwitch (void) { unsigned int i; DDRD |= SWITCHES; // Switches as Output SWITCH_ON; // Output HIGH for measurement ADMUX = (1 << REFS0) | SWITCH; // AVCC reference with external capacitor Sleep(10); ADCSRA |= (1 << ADSC); // Start conversion while (!(ADCSRA & (1 << ADIF)));// wait for conversion complete ADCSRA |= (1 << ADIF); // clear ADCIF i = ADCL + (ADCH << 8); SWITCH_OFF; Sleep(5); return (int)(1023000L/(long)i); } int main(void) { Init(); StatusLED(OFF); SerWrite("\n\rtest switch:\n",15); StatusLED(GREEN); while(1) { int j; long sum = 0, sw=0; SerWrite("\n\r",2); while (!PollSwitch()); Msleep(100); for (j = 0; j < 100; j++) sum += testSwitch(); int sw1,sw2; do { sw1 = PollSwitch(); sw2 = PollSwitch(); } while (sw1 != sw2); PrintInt((int)(sum/100.0)+0.5); Msleep(200); SerWrite(" ",2); PrintInt(sw1); } return 0; }
2) Anhand der Werte und die Formel in PollSwitch lässt sich vermuten, dass 1/y linear ist. Die Formel ist auch nichts anders als lineare Transformation. Wegen der herstellungsbedingten Ungenauigkeit der Widerstände liegen die Messwerte 1023/y nicht auf einer Gerade.
3) Als nächste habe ich Matlab angeworfen und die Ausgleichgerade berechnet:
4) Für denjenigen, der kein Matlab hat, habe ich was vorgerechnet.Code:Z = 1./y; X = [ones(64,1) (0:63)']; p = X\Z; % Lsg des Glg-Systems -> Koeff. der Gerade p Z_out = (Z-p(1))/p(2);
Die Werte 1/y aus 1) im folgenden Prg dem y zuweisen.
Das Prg spuckt zwei Werte aus: bias b (bei mir 0.995) und slope s (die Steigung der Gerade, bei mir 61.0).Code:#include "asuro.h" int main(void) { // y wird durch indiv. Messwerte ersetzt int y[] = {1000, 1015, 1031, 1047, 1062, 1079, 1095, 1111, 1125, 1142, 1157, 1173, 1190, 1206, 1222, 1238, 1258, 1274, 1290, 1307, 1322, 1339, 1355, 1369, 1384, 1401, 1417, 1433, 1449, 1466, 1480, 1498, 1525, 1541, 1557, 1574, 1589, 1606, 1621, 1637, 1653, 1669, 1685, 1699, 1716, 1734, 1749, 1764, 1785, 1801, 1817, 1833, 1850, 1867, 1881, 1898, 1912, 1930, 1945, 1960, 1975, 1994, 2010, 2026}, A[2][64] = {{ 6106, 5962, 5817, 5673, 5529, 5385, 5240, 5096, 4952, 4808, 4663, 4519, 4375, 4231, 4087, 3942, 3798, 3654, 3510, 3365, 3221, 3077, 2933, 2788, 2644, 2500, 2356, 2212, 2067, 1923, 1779, 1635, 1490, 1346, 1202, 1058, 913, 769, 625, 481, 337, 192, 48, -96, -240, -385, -529, -673, -817, -962, -1106, -1250, -1394, -1538, -1683, -1827, -1971, -2115, -2260, -2404, -2548, -2692, -2837, -2981}, { -144, -140, -135, -130, -126, -121, -117, -112, -108, -103, -98, -94, -89, -85, -80, -76, -71, -66, -62, -57, -53, -48, -43, -39, -34, -30, -25, -21, -16, -11, -7, -2, 2, 7, 11, 16, 21, 25, 30, 34, 39, 43, 48, 53, 57, 62, 66, 71, 76, 80, 85, 89, 94, 98, 103, 108, 112, 117, 121, 126, 130, 135, 140, 144}}; int i; float p1,p2,fkt=1.e5; p1 = p2 = 0; Init(); SerWrite("\n\rlin.Regression:\n\r",19); for (i = 0; i < 64; i++) { p1 += (float)A[0][i]*(float)y[i]; p2 += (float)A[1][i]*(float)y[i]; } p1 /= fkt; SerWrite("bias: 0.",8); Msleep(100); PrintInt(p1); SerWrite("\n\r",2); p2 = 1000*fkt/p2; SerWrite("slope: ",7); Msleep(100); PrintInt(p2); SerWrite(".",2); PrintInt((int)((p2-(int)p2)*10.0+0.5)); SerWrite("\n\r",2); while (1); return 0; }
5) Zum Schluß wird die Return-Zeile noch korrigiert mit:
Mit diesem Feintuning habe ich alle 64 Werte richtig. Probiert habe ich mit und ohne Motor eingeschalten. Hier ist noch das Testprg. Es gibt die Messwerte und die transf. Werte 1 bis 63 aus.Code:return ((unsigned char) ((( 1023.0/(float)i - 0.995)) * 61.0 + 0.5));
Ich hoffe, dass diese codes hilfreich sind. Falls jemand einen Fehler entdeckt, bitte melden. Wenn jemand sich interessiert, kann ich auch kurz erläutern, wie das lin. Ausgleichsproblem analytisch gelöst wird (auf die Frage, wieso sieht die 2x64-Matrix A so aus).Code:#include "asuro.h" /* function to read out switches */ unsigned int testSwitch (void) { unsigned int i; DDRD |= SWITCHES; // Switches as Output SWITCH_ON; // Output HIGH for measurement ADMUX = (1 << REFS0) | SWITCH; // AVCC reference with external capacitor Sleep(10); ADCSRA |= (1 << ADSC); // Start conversion while (!(ADCSRA & (1 << ADIF)));// wait for conversion complete ADCSRA |= (1 << ADIF); // clear ADCIF i = ADCL + (ADCH << 8); SWITCH_OFF; Sleep(5); return (int)(1023000L/(long)i); } int main(void) { Init(); StatusLED(OFF); SerWrite("\n\rtest switch:\n",15); StatusLED(GREEN); while(1) { int j; long sum = 0; SerWrite("\n\r",2); MotorDir(FWD,FWD); MotorSpeed(150,150); while (!PollSwitch()); Msleep(100); for (j = 0; j < 100; j++) sum += testSwitch(); int sw1,sw2; do { sw1 = PollSwitch(); sw2 = PollSwitch(); } while (sw1 != sw2); PrintInt((int)(sum/100.0)+0.5); SerWrite(" ",2); Msleep(200); PrintInt(sw1); if (sw1 & 0x7) MotorDir(RWD,RWD); else MotorDir(FWD,FWD); MotorSpeed(150,150); Msleep(500); } return 0; }
Felix
Lesezeichen