-
Also ich hab mal den Algorithmus nachgebaut (zumindest versucht).
Eigentlich müsste er so laufen, nur ich erhalte als Positionen immer 0 / 0.
Code:
#define Iterations 5
#define LCD_Width 128
#define LCD_Height 64
// Calibration values; set with defaults
ui16_t r0_x = 101;
ui16_t r1_x = 867;
ui16_t r2_x = 848;
ui16_t r3_x = 102;
ui16_t r0_y = 145;
ui16_t r1_y = 150;
ui16_t r2_y = 802;
ui16_t r3_y = 759;
void TPAD_GetPosPrecise(ui8_t *xp, ui8_t *yp)
{
ui8_t cycle;
ui16_t x_raw;
ui16_t y_raw;
ui32_t main_det;
double W = 0; // iteration parameter
double LV_x = 0; // solving vector X
double LV_y = 0; // solving vector Y
double D_x = 0; // det. x
double D_y = 0; // det. y
double factor_x = 0;
double factor_y = 0;
TPAD_GetPos(&xraw, &yraw);
// calculate constant values for iteration:
// (r1_x - r0_x) = D20 (Excel cell D20)
// (r1_y - r0_y) = E20
// (r3_x - r0_x) = D21
// (r3_y - r0_y) = E21
// D = D20 * E21 - E20 * D21
main_det = ((r1_x - r0_x) * (r3_y - r0_y)) - ((r1_y - r0_y) * (r3_y - r0_y));
// (r - r0) = (x_raw - r0_x) = D23
// (r - r0) = (y_raw - r0_y) = E23
// (r0-r1)-(r3-r2) = (r0_x - r1_x) - (r3_x - r2_x) = D24
// (r0-r1)-(r3-r2) = (r0_y - r1_y) - (r3_y - r2_y) = E24
// Iteration:
for (cycle = 0 ; cycle < Iterations ; cycle++)
{
LV_x = (x_raw - r0_x) - (W * ((r0_x - r1_x) - (r3_x - r2_x))); // = D31
LV_y = (y_raw - r0_y) - (W * ((r0_y - r1_y) - (r3_y - r2_y))); // = E31
D_x = (LV_x * (r3_y - r0_y)) - (LV_y * (r3_x - r0_x)); // = D33
D_y = (LV_y * (r1_x - r0_x)) - (LV_x * (r1_y - r0_y)); // = E34
factor_x = D_x / main_det; // = D36
factor_y = D_y / main_det; // = E37
W = factor_x * factor_y;
}
*xp = (factor_x * (LCD_Width - 1));
*yp = (factor_y * (LCD_Height - 1));
}
Werd den Fehler natürlich weiter verfolgen.
-
Jaecko,
ich hab noch zuviel Watte im Kopp um Deinen Code gründlich zu lesen, aber die Zeile
main_det = ((r1_x - r0_x) * (r3_y - r0_y)) - ((r1_y - r0_y) * (r3_y - r0_y));
müsste meiner Meinung nach
main_det = ((r1_x - r0_x) * (r3_y - r0_y)) - ((r1_y - r0_y) * (r3_x - r0_x));
heissen.
Ciao,
mare_crisium
-
Stimmt, da bin ich durcheinandergekommen.
Hab den Algorithmus jetzt mal neu aufgebaut und dabei noch einige "Fehler" im alten gefunden. U.a. hab ich zu wenig gecastet.
(ui16_t * ui16_t ist wieder ui16_t, obwohl z.B. eine ui32_t erwartet wird)
Jetzt funktionierts schon fast perfekt. Hier ist ein Video, in dem das Programm einfach gesagt nur folgendes durchführt:
- Hol die Rohwerte der Position.
- Berechne mit dem "mare_cirisum-Algorithmus" die echte Position
- Pinsel das Pixel an der entsprechenden Stelle weiss.
http://www.youtube.com/watch?v=fdpV7baRqgs
Wie man auf dem Video sieht, geht das in der linken Displayhälfte schon perfekt. Nur in der rechten ist noch ein kleiner Offset nach unten drin.
Ich vermute, dass ich da nochmal irgendwo nen Cast vergessen hab.
Lässt sich aber auch noch rausfinden.
Hier der neue Algorithmus:
Code:
// Those values are determined by a calibration procedure (touch all 4 corners)
// Here they are loaded with default values
ui16_t r0[2] = { 101, 145 };
ui16_t r1[2] = { 867, 150 };
ui16_t r2[2] = { 848, 802 };
ui16_t r3[2] = { 102, 759 };
// variables for precise algorithm; declared here to save stack memory
ui16_t r_raw[2]; // raw value vector
double W = 0; // Iteration parameter; Start value = 0
i16_t r1_r0[2]; // solution vector for r1 - r0
i16_t r3_r0[2]; // solution vector for r3 - r0
i16_t rraw_r0[2]; // solution vector for rraw - r0
i32_t D; // determinant
i32_t D_part[2]; // partial solution for determinant
double factor[2]; // solution vector factors
i16_t r01_r32[2]; // solution vector for (r0 - r1) - (r3 - r2)
double solVec[2]; // soluton vector
double D_XY[2]; // determinant D_X, D_Y
#define LCD_Width 128
#define LCD_Height 64
#define Iterations 4
#define X 0
#define Y 1
void TPAD_GetPosPrecise(ui8_t *xp, ui8_t *yp)
{
ui8_t step;
// Check if constant 'Iteration' is defined already. If not, use default value of 5.
#ifndef Iterations
#define Iterations 5
#endif
TPAD_GetPos(&r_raw[X], &r_raw[Y]); // get raw values via 'old' method (ADC-Values)
// r_raw[X] = 560; // test value
// r_raw[Y] = 200; // test value
// Constant values, needed for calculation; constant for a single iteration:
// r1-r0
r1_r0[X] = r1[X] - r0[X];
r1_r0[Y] = r1[Y] - r0[Y];
// r3-r0
r3_r0[X] = r3[X] - r0[X];
r3_r0[Y] = r3[Y] - r0[Y];
// Determinant
D_part[0] = (i32_t) r1_r0[X] * (i32_t) r3_r0[Y];
D_part[1] = (i32_t) r1_r0[Y] * (i32_t) r3_r0[X];
D = D_part[0] - D_part[1];
// r_raw - r0
rraw_r0[X] = r_raw[X] - r0[X];
rraw_r0[Y] = r_raw[Y] - r0[Y];
// (r0 - r1) - (r3 - r2)
r01_r32[X] = (r0[X] - r1[X]) - (r3[X] - r2[X]);
r01_r32[Y] = (r0[Y] - r1[Y]) - (r3[Y] - r2[Y]);
// Iteration Step 0
for (step = 0 ; step < Iterations ; step++)
{
// Solution vector
solVec[X] = (double) rraw_r0[X] - (W * (double) r01_r32[X]);
solVec[Y] = (double) rraw_r0[Y] - (W * (double) r01_r32[Y]);
// Determinant
D_XY[X] = -(((double) r3_r0[X] * solVec[Y]) - ((double) r3_r0[Y] * solVec[X]));
D_XY[Y] = ((double) r1_r0[X] * solVec[Y]) - ((double) r1_r0[Y] * solVec[X]);
// factor
factor[X] = (double) D_XY[X] / (double) D;
factor[Y] = (double) D_XY[Y] / (double) D;
// New iteration parameter:
W = factor[X] * factor[Y];
}
*xp = (ui16_t) (factor[X] * ((double) (LCD_Width - 1)));
*yp = (ui16_t) (factor[Y] * ((double) (LCD_Height - 1)));
}
-
Hi, Jaecko,
auf Deinem Video sieht das schon ziemlich gut aus :-) ! Ich hätte auch nicht gedacht, dass die Umrechnung so schnell geht.
Mit meinen beschränkten C-Kenntnissen (bin ein Pascal-Veteran) habe ich in Deinem Programm keinen Fehler gefunden. Der Sprung in der rechten Hälfte des Touchscreens könnte imho daher kommen, dass ein signed Typ als unsigned interpretiert wird oder umgekehrt... In diesem Sinne kommen mir die Zeilen
*xp = (ui16_t) (factor[X] * ((double) (LCD_Width - 1)));
*yp = (ui16_t) (factor[Y] * ((double) (LCD_Height - 1)));
verdächtig vor.
Ciao,
mare_crisium
-
Sooo... jetzt gehts. Dieser Fehler in der rechten Hälfte war ein "Fehler" in der Kombination Algorithmus + Displayansteuerung. War manchmal so, dass es ging und der Displayinhalt plötzlich als ganzes verschoben wurde. Also konnts kein Rechenfehler sein (da ja bereits gezeichnete Pixel nicht verändert werden).
Im Algorithmus können als Endergebnis Positionen rauskommen, die ausserhalb des Display-Bereichs liegen (u.a. häufiger "inf"). Da das Display ja nur 128x64 Pixel hat, scheints der Treiber nicht zu packen, wenn er grössere bzw. unendliche Werte kriegt.
Packt man in die Funktion unter die Zuweisungen von *xp und *yp noch die folgenden Zeilen, dann gehts (bis auf wenige Ausreisserpixel, die aber im Video auch zu sehen sind) fehlerfrei.
Code:
*xp = (i16_t) (factor[X] * ((double) (LCD_Width - 1)));
*yp = (i16_t) (factor[Y] * ((double) (LCD_Height - 1)));
// prevent 'tilt' of display driver by eliminating invalid values
if (*xp > (LCD_Width - 1))
{
*xp = LCD_Width - 1;
}
if (*yp > (LCD_Height - 1))
{
*yp = LCD_Height - 1;
}
(Ein neues Video folgt demnächst)
-
Na, Jaecko,
prima, dass es jetzt läuft :-) ! Was meinst Du mit "häufiger inf"? Division durch Null?
Bin gespannt auf das nächste Video. Zeichne mal einen Weihnachtsbaum!
Frohe Weihnacht
mare_crisium
-
Hier mal der Baum... keine Schönheit, aber glaub man erkennts:
http://www.youtube.com/watch?v=wPJbjVy5GlQ
Ich hab mir in der Funktion mal die Werte von xp/yp bzw. factor[n] ausgeben lassen. Stellenweise ist es so, dass dort statt dem eigentlichen Wert (der auch dann stimmt, wenns am Bildschirm falsch dargestellt ist), "inf" drinsteht. Und sobald dieses "inf" zum ersten Mal auftaucht, spinnt das Display.
Das komische: Die einzige Division ist durch die Determinante... und die ist durch die vorgegebenen Werte aber konstant != 0.
Nen Pipelineeffekt halt ich jetzt mal für unwahrscheinlich.
(also, dass die Berechnung schon ausgeführt wird, während ein vorheriges Ergebnis noch nicht fertig ist)
Ebenfalls frohe Weihnachten.
-
Hast du dir mal die ADC Werte ausgeben lassen und die dazugehörigen Ausreiserpixelpositionen? Sind das Messfehler vom ADC?
-
Die ganzen Werte stimmen. Der Fehler scheint erst bei der Division aufzutreten, und da eben auch nicht immer.
Und auch wenn andere ADC-Werte kommen, passiert rein mathematisch nichts "schlimmes". Die Zielkoordinaten sind dann einfach woanders, aber nicht unendlich.
Im grossen und ganzen funktionierts ja, und wenn man weiss, dass man unendliche Werte abfangen/ignorieren muss, dann isses eh perfekt.
-
Jaecko,
Na, das ist doch eine schöner Baum - sogar mit ein bisschen Schneegestöber (vor allem auf der rechten Seite) :-) !
Um einen pile-up-Effekt noch sicherer auszuschliessen, würde ich die Berechnung der unveränderlichen Grössen (z.B. der Determinante) nur einmal, nämlich beim Kalibrieren des Touchscreens, ausführen und sie in globalen Variablen speichern. Das verringert die Rechenzeit für TPAD_Get_PosPrecise und beugt etwelchen pile-ups vor. Statt des Wertes D der Determinante würde ich (1/D) speichern, so dass in der Routine nur multipliziert werden muss.
Mathematisch gesehen, wird die Determinante nur dann Null, wenn das Eingabeviereck echt krank verzerrt ist, z.B. so, dass zwei Seiten zusammenfallen oder dass eine Seite fast auf Null zusammengeschnurrt ist. Bei Werten, die im normalen Betrieb auftreten, sind die beiden Elemente in der Hauptdiagonalen immer grösser als Null und immer deutlich grösser als die beiden anderen - das sind optimale Bedingungen für endliche Lösungen.
Ciao,
mare_crisium