IRKamera

Aus Kicker
Wechseln zu: Navigation, Suche

Inhaltsverzeichnis

IRKamera

Ballerkennung mit Kamera OHNE IR-Filter (also Kamera nimmt sichtbares Licht sowie Licht im IR-Bereich auf) in Kombination mit IR-LED-Beleuchtung (sofern noetig) und einem Kickerball, der die entsprechende Wellenlängen reflektiert

Uebersicht

28.August 2008

Die Fa. VRmagic VRMagic stellt Framegrabber-lose Kameras her, die über USB2.0 an einen PC angeschlossen werden können und unter Windows XP/Vista aber auch unter Linux betrieben werden können. Der Vertrieb erfolgt über Stemmer-Elektronik Puchheim.

Preislich bewegen sich hier die Kameras zwischen 200 und 1200 Euro inkl. Software wohlgemerkt.

Herr Menken/VRMagic hat vorgeschlagen die Ballerkennung so wie bei dem Wii Experiment über reflektieres Infrarotlicht zu machen.

Die Kamera, die wir leihweise bekommen werden, hat die Bezeichnung FC12 COB M12 mit 90 Grad Oeffnungswinkel. Dazu benötigen wir noch IR Leds die von der Kamera getriggert und hinter der Kamera (Ring um das Objektiv) aufgestellt den mit 3M IR-retroreflekierendem Material beschichteten Ball anblitzen, der dann das IR-Licht hell reflektiert. Das lässt sich dann gut vorverarbeiten in einem quasi binären Bildmuster.

IR Leds:

Datei:Hsdl4220.pdf

Datei:Sfh484 5 1.pdf

Datei:Tsha440.pdf

Vorhandene Hardware

Geliefert wurde die Kamera VRmFC-12/BW COB M12 mit Objektiv (allerdings ohne IR-Filter), USB-Kabel und DevelopmentKit for Windows: http://www.vrmagic.com/no_cache/de/vrmfc_12_bw_cob_m12/?camcolor=monochrome&camres=640x480&camfps=69

   * [752x480]  69 frames/s
   * [640x480]  80 frames/s
  • CMOS-Sensor: 1/3Zoll (= 8,47mm)
  • Monochrom
  • Auflösung: 752x480
  • Pixelgröße: 6x6 um
  • FPS (bei max. Auflösung): 69
  • Pixeltakt: 5-26.6 MHz
  • Shutter: global
  • Platinenmaße (mm): 42x38x25
  • interner RAM: 64MB
  • Beleuchtungsring optional möglich

SDK fuer Linux

http://www.vrmagic.com/de/downloads_img/

Schematischer Aufbau

Aufbauskizze.png Jede kamera hat einen Öffnungswinkel von etwa 87°. Sie werden jeweils in Verlängerung der Torauslinie seitlich am Kicker befestigt, so dass der Ball an jeder Position von beiden Kameras erfasst werden kann (durch einen 3mm-hohen Spalt in der Kicker-Außenwand), und aus dem aufgenommenen Bild der Winkel von der Kamera aus zum Ball extrahiert werden kann.

Vorversuch der Ballberechnung

Um zu prüfen, ob dieses Prinzip verwirklichbar ist, wurden verschiedene Ballpositionen simuliert und aus den daraus erhaltenen Winkel die Ballposition ermittelt. Um erste Einblicke ueber die Fehleranfaelligkeit zu erhalten, wurde die Genauigkeit der Winkel nach und nach verringert (eine Nachkommastelle, gar keine Nachkommastelle).

001-ball-rechts-unten.png 002-ball-rechts-oben.png
003-ball-mitte-unten.png
004-ball-mitte-oben.png
005-ball-links-unten.png
006-ball-links-oben.png
007-ball-frei.png
008-ball-frei.png
009-ball-frei.png

Sourcecode:

#include <iostream>
#include <iomanip> // for setw()
#include <fstream>
#include <string>
#include <math.h>
using namespace std;
 
#define LINKS 0
#define RECHTS 1
 
//const double PI = 3.1415926535897932384626433832795; // besser M_PI benutzen
 
double Round(double Zahl, int Stellen)
{
    Zahl *= pow( 10, Stellen);
    Zahl = floor(Zahl + 0.5);
    Zahl *= pow(10, -Stellen);
    return Zahl;
} 
 
struct cam
{
    string name;
    int x;
    int y;
    float angleCorrection;  //Winkelkorrektur, rechte Kamera +90°, linke Kamera 3,33847° (== tan^-1(7/120) )
};
 
struct ballpos
{
    unsigned int nr;
    float winkel1;
    float winkel2;
    unsigned int x;
    unsigned int y;
 
    float mBer1;    //Berechnete Steigung, speaeter gleichsetzen der Geradengleichung y=m*x+t
    float mBer2;    //Berechnete Steigung Gerade von rechter Kamera durch den Ball
    float xBer;     //Berechnete x-Koordinate des Balls
    float yBer;     //Berechnete y-Koordinate des Balls
    float fehler;   //Fehler der berechneten Ballkoordinate (Pythagoras)
};
 
int main()
{
    ifstream file("data.txt");
    cam cam[2];
    ballpos ballpos[9];
    string buffer, tmp;
//Werte aus Datei einlesen:
    getline(file,buffer);   // erste Zeile unwichtig
    for(int i=0; i<2; i++)
    {
	file >> cam[i].name;
	file >> cam[i].x;
	file >> cam[i].y;
	file >> cam[i].angleCorrection;
	getline(file,buffer);
    }
    getline(file,buffer);   // Zeile unwichtig
    for(int i=0; i<9; i++)
    {
	file >> ballpos[i].nr;
	file >> ballpos[i].winkel1;
	file >> ballpos[i].winkel2;
	// Opt. Winkel auf EINE Nachkommastelle runden, genauer wird man die Winkel nicht bestimmen koennen!
	ballpos[i].winkel1 = Round(ballpos[i].winkel1,1);
	ballpos[i].winkel2 = Round(ballpos[i].winkel2,1);
	file >> ballpos[i].x;
	file >> ballpos[i].y;
	getline(file,buffer);
    }
    file.close();
 
    // Ueberpruefen, ob das Einlesen der Datei "data.txt" erfolgreich war:
    cout << "Programm zur Positionsberechnung des Balles aus den Winkeln" << endl;
    for(int i=0; i<2; i++)
	cout << cam[i].name << ",\tx=" << cam[i].x << ",\ty=" << cam[i].y << ",\tWinkelkorrektur: " << cam[i].angleCorrection << endl;
    for(int i=0; i<9; i++)
	cout << ballpos[i].nr << ",\tw1=" << ballpos[i].winkel1 << ",\tw2=" << ballpos[i].winkel2 << ",\tx=" << ballpos[i].x << ",\ty=" << ballpos[i].y << endl;
    cout << "Starten der Berechnung:" << endl;
 
    for(int i=0; i<9; i++)
    {
	ballpos[i].mBer1 = tan((ballpos[i].winkel1+cam[LINKS].angleCorrection )*M_PI/180);  // Steigung von der linken  Kamera aus
	ballpos[i].mBer2 = tan((ballpos[i].winkel2+cam[RECHTS].angleCorrection)*M_PI/180);  // Steigung von der rechten Kamera aus
	ballpos[i].xBer  = ( (-120*ballpos[i].mBer2)/(ballpos[i].mBer1-ballpos[i].mBer2) );
	ballpos[i].yBer  = ballpos[i].mBer1 * ballpos[i].xBer -7;
	ballpos[i].fehler = sqrt( pow(ballpos[i].xBer - ballpos[i].x, 2) + pow(ballpos[i].yBer - ballpos[i].y, 2) );
	cout << ballpos[i].nr << ", m1: " << setw(10) << ballpos[i].mBer1 << ", m2: " << setw(10) <<  ballpos[i].mBer2;
	cout << ", x: " << setw(10) << ballpos[i].xBer << ", y: " << setw(10) << ballpos[i].yBer;
	cout << ", Fehler: " << setw(13) << ballpos[i].fehler*10 << "mm" << endl;
    }
 
    return 0;
}

Dateiinhalt aus data.txt sowie erhaltene Resultate:

Koordinaten Kameras (x y) + Korrektur in Grad
LeftCam 0 -7 3.33847
RightCam 120 -7 90
Winkel von den Kameras (Leftcam Rightcam) und Ballkoordinaten (x y)
001 1.0231  12.5288 118 2
002 28.7541 1.54816 118 67
003 5.1923  81.4692 60  2
004 47.626  39.0355 60  67
005 74.1327 85.6384 2   2
006 85.1134  57.9074 2   67
007 74.8591 57.704  14   60
008 40.2976 61.9661 43  34
009 2.93261 70.9744 91  3
 
Ausgewertet (genaue Winkel):
1, m1:  0.0762711, m2:       -4.5, x:        118, y:    1.99999, Fehler:   5.60284e-05mm
2, m1:   0.627118, m2:   -36.9999, x:        118, y:    66.9999, Fehler:   0.000534058mm
3, m1:       0.15, m2:  -0.150001, x:    60.0001, y:    2.00002, Fehler:    0.00108807mm
4, m1:    1.23333, m2:   -1.23333, x:         60, y:         67, Fehler:   0.000351698mm
5, m1:    4.49999, m2: -0.0762717, x:    2.00002, y:    2.00006, Fehler:    0.00063763mm
6, m1:    37.0007, m2:  -0.627119, x:    1.99996, y:    67.0001, Fehler:   0.000659088mm
7, m1:    4.78572, m2:  -0.632076, x:         14, y:    60.0001, Fehler:   0.000615101mm
8, m1:   0.953488, m2:  -0.532469, x:    43.0001, y:         34, Fehler:   0.000757196mm
9, m1:    0.10989, m2:  -0.344827, x:         91, y:          3, Fehler:   0.000229489mm
 
Ausgewertet (Winkel auf EINE Nachkommastelle gerundet):
1, m1:  0.0758656, m2:   -4.51071, x:    118.015, y:    1.95329, Fehler:      0.490912mm
2, m1:   0.628235, m2:   -38.1885, x:    118.058, y:     67.168, Fehler:        1.7772mm
3, m1:   0.150137, m2:  -0.149451, x:    59.8625, y:    1.98761, Fehler:       1.38047mm
4, m1:    1.23219, m2:    -1.2349, x:    60.0659, y:    67.0125, Fehler:      0.670359mm
5, m1:    4.48789, m2: -0.0769458, x:    2.02274, y:    2.07786, Fehler:       0.81112mm
6, m1:     36.683, m2:  -0.627299, x:    2.01756, y:    67.0102, Fehler:       0.20331mm
7, m1:    4.80284, m2:  -0.632174, x:    13.9578, y:    60.0371, Fehler:      0.561781mm
8, m1:   0.953568, m2:  -0.531709, x:    42.9584, y:    33.9638, Fehler:      0.551872mm
9, m1:   0.109314, m2:  -0.344328, x:    91.0836, y:    2.95672, Fehler:      0.941122mm
 
Ausgewertet (Winkel auf GANZE Winkel gerundet):
1, m1:  0.0758656, m2:   -4.33148, x:    117.934, y:    1.94717, Fehler:      0.842399mm
2, m1:   0.633114, m2:   -28.6363, x:    117.404, y:    67.3303, Fehler:       6.81127mm
3, m1:    0.14657, m2:  -0.158384, x:    62.3245, y:     2.1349, Fehler:       23.2841mm
4, m1:    1.24992, m2:    -1.2349, x:    59.6372, y:    67.5419, Fehler:       6.52129mm
5, m1:    4.45128, m2: -0.0699268, x:    1.85597, y:    1.26144, Fehler:       7.52477mm
6, m1:    34.4741, m2:  -0.624869, x:    2.13637, y:    66.6494, Fehler:       3.76217mm
7, m1:     4.8452, m2:  -0.624869, x:    13.7081, y:    59.4185, Fehler:       6.50611mm
8, m1:   0.943621, m2:  -0.531709, x:     43.248, y:    33.8097, Fehler:       3.12599mm
9, m1:   0.111081, m2:  -0.344328, x:    90.7303, y:    3.07838, Fehler:       2.80901mm

erste Aufnahmen

Mit der Entwicklungsumgebung kann die Kamera schnell in Betrieb genommen und nach Belieben konfiguriert werden. Da die Kameras nur einen kleinen Spalt aufnehmen müssen, können sie auch mittels ROI (Region of Interrest) konfiguriert werden. Momentan nehmen sie beispielsweise ein Bild von 752x20px auf. Durch die dadurch veringerte Größe der Bildinformation kann die Bildrate auf über 100fps gesteigert werden (abhöngig von der Exposuretime). Mit einer exp. von 10ms können so 100fps realisiert werden.

Desweiteren kann mit diversen Filtereinstellungen (Gamma: 0.00, Luminance: 0, Contrast: 10, Blacklevel: je nach Lichtverhältnis angepasst) schon auf der Kamera eine Vorauswertung stattfinden (Ballpixel: weiß (0xff), sonst: schwart (0x00)). Dazu muss die Bande schwarz sein, um ein möglichst hohen Kontrast zu haben.
Durch diese Vorauswertung bietet es sich außerdem an, mithilfe des auf der Kamera montierten FPGAs das Bild gleich in das RLE-Format umzuwandeln. Dabei werden die Bildinformationen in jeweils einem Byte-Päärchen gespeichert. Das erste Byte gibt die Farbe an (hier nur noch weiß oder schwarz), das zweite Byte die Anzahl der mit dieser Farbe folgenden Pixel (max. 255). Auf diese Weise reduziert sich zum einen die Datenmenge, die von der Kamera zum Hostrechner übertragen werden muss extrem, zum anderen wird so der erste Teil der Segmentierung (der einzelnen Zeilen) schon auf der Kamera durchgeführt und dadurch das Hostsystem entlastet.

Inbetriebnahme zweier Kameras

Die von VRmagic mitgelieferten Demoprogramme liefern einen guten Einstiegspunkt bei der Inbetriebnahme einer Kamera. Um die aufgenommenen Bilder live betrachten zu können wurde bei dem Democode "SDL-Viewer" die freie Multimedia-Bibliothek SDL (Simple DirectMedia Layer) verwendet. Dieser Beispielcode wurde nun soweit erweitert, dass zwei Kameras parallel verwendet werden können und verschiedene Parametereinstellungen über den Code durchgeführt werden können. Das Fenster zeigt neben den beiden Bildausschnitten der 2 Kameras auch ein Spielfeld, auf dem der Ball in der aktuell errechneten Position angezeigt wird (TODO: noch in Arbeit).

Meine Werkzeuge