Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

projektewise1718:haligalipublic:start

Hauptdokumentation

Einleitung

Das Spiel "Halli Galli"

Halli Galli ist ein sehr weit verbreitet Kartenpiel für Kinder welches als wichtiges Spielelement ein Glocke enthält, welche geläutet werden muss. Die genaueren Spielregeln findet man z. B. hier. Zu unseren Zwecken hab wir das Spiel so abgeändert, dass es nur angedacht ist im 1-gegen-1 gegen den Roboter zu spielen. Des weiteren habn wir uns gedacht, dass es schwierig zu realisieren ist ist, dass der Roboter Karten mischen soll. Auch das übergeben von den Karten an den Spieler, der nicht die Glocke geläutet hat, haben wir weggelassen. Somit lag bei uns der Fokus, dass der Roboter einzelnen Karten ausgeben, ausliegende Karten erkennnen und die Glocke läuten kann.

Konstruktion


Diese drei Hauptaufgaben haben wir auch in unserem Roboter getrennt betrachtet:

Kartenausgabe

Seitenansicht
Abbildung 1: Seitenansicht

Unser Anspruch war, dass der Roboter ziemlich zuverlässig einzelne Karten ausgeben kann. Unsere Konstruktion besteht somit aus einer Kartenablage, in der die Karten liegen, einem Steppermoter, der über ein gummiertes Rad von unten die unterste Karte des Stapels nach vorne schiebt und einer Rampe, auf der die auszugebende Karte dann herrunterrutscht.

Frontansicht
Abbildung 2: Frontansicht

Damit wir sicherstellen konnten, dass auch nur eine Karte pro Ausgabe auch ausgegeben wird haben wir eine Art Wand noch vor den Kartenstapel eingezogen, welche von der Höhe so eingestellt war, dass nur eine Karten darunter durch passt. Da wir nicht genau bestimmen konnten, wie weit sich das Rad drehen muss, damit nur eine Karte ausgeben wird, haben wir noch eine Rückwärtsbewegung des Rades hinzugefügt, dass wenn die nächste Karte aus versehen schon mit nach vorne geschoben wird, sie wieder zurück geschoben wird.

Klingelstation

Abbildung 3: Klingelstation

Nach einigen Überlegungen,wie die Klingel von dem Roboter betätigt werden soll,kamen wir auf den Entschluss,den Roboter nicht anhand einer „Hand“ klingeln zu lassen,also von oben,sondern von unten mithilfe eines Hubmagneten. Dieser stößt den Schwingungskörper der Klingel an wodurch diese klingelt. Der große Vorteil davon ist,dass der Mensch als Gegenspieler ohne Einschränkung auf die klingel schlagen kann. Mensch und Maschine stehen sich nicht unnütz im Weg. Da nun der Mensch zwar anhand des Geräusches wahrnehmen kann, ob der Roboter geklingelt hat und der Roboter nicht diese Fähigkeit besitzt, mussten wir uns eine Lösung dafür überlegen.

Als effektivste dafür hat sich eine Verbindung des Schwingungskörpers und der Klingelinnenseite mit Kabeln ergeben. Wenn der Mensch nun klingelt wird der Stromkreis geschlossen und der Arduino kann die Spannung am einem Pin messen. Dadurch weiß jetzt auch der Roboter ob sein Gegenüber schneller war als er.

Kameraarm

Damit unser Roboter die Farben der Karten erkennen benutzen wir eine Kamera. Diese sollte nun in der Lage sein die ausgegebenen Karten des Roboters und des Menschen zu erkennen. Aufgrunddessen haben wir uns dazu entschieden sie am Ende der Rampe zu platzieren. Sie musste reichlich Fläche aufnehmen können, weshalb wir eine Vorrichtung gebaut haben, an der die Kamera kopfüber in ca. 20 cm Höhe befestigt wurde. Sie erfässt dadurch die Karten beider Spieler.

Damit es zu weniger Lichtproblemen kommt, die bei der Programmierung der Farberkennung bemerkbar wurden, haben wir neben der Kamera Lichter befestigt die den aufgenommenen Bereich so gut wie möglich beleuchten. Zu der Funktion der Farberkennung kommen wir gleich.

Hardwaresteuerung

Zur Ansteuerung der Hardwarekomponenten (Steppermotor und Hubmagnet) haben wir einen Arduino Nano benutzt. Dieser hat ausreichend Pins zum anschließen der Komponenten und eine ausreichende Rechenleistung, da der Großteil der Rechenleistung (mit der Bildverarbeitung) von einem seperaten Computer übernommen wird.

Der Arduino kommuniziert per USB über die serielle Schnittstelle mit dem Computer und empfängt Nachrichten, wann er eine Karten ausgeben oder die Klingel betätigen soll und sendet eine Nachricht, wenn die Klingel gedrückt wurde. Nähere Erklärung hier.

Schaltplan

Abbildung 4: Schaltplan
Abbildung 5: Steckbrettansicht

Materialiste

Material Menge
Baumaterialien
Holz
Schrauben
Muttern
Unterlegscheiben
Metallwinkel 5
Holzleim
Nägel
Kabelbinder
Elektronik
Arduino Nano 1
Pololu Stepper Driver A4988 1
Stepper Motor
Hubmagnet 1
Logitech Webcam 1
5V 200mA LED Panel 1
Kippschalter 1
div. Wiederstände 10Ω, 125Ω, 220Ω
47µF Kondensator 1
MOSFET 1
Diode 1
Schraubklemme 4-polig
diverse Materialien
Halli Galli Spiel 1
gummiertes Rad für Stepper 1
Breadboard 1
Jumper-Kabel 12

Pinbelegungstabelle

Arduino Pin Funktion
2 Glocke (interrupt-Pin)
3 Stepper (DIR)
4 Stepper (STEP)
6 Hubmagnet

Arduino Processing Kommunikation

Auf dem Arduino läuft ein Programm, welches immer wieder die empfangenden Bytes über der seriellen Schnittstelle überprüft. Je nach Nachricht wird eine Karten ausgegeben, die Glocke geläutet oder nichts getan.

void loop() {
  //serielle Schnittstelle abfragen
  if (Serial.available() > 0) {
    //eingehende Bytes abfangen
    int incoming = Serial.read();
 
    //Fallunterscheidung
    switch (incoming) {
      case '0':
 
        //Hier folgt dann die Kartenausgabe
 
      case '1':
 
        //Hier erfolgt dann das Glockenklingeln
 
    }
  }
}

Damit aber der Arduino auch senden kann, wenn die Glockgeläutet wird und der Stromkreis sehr kurz einmal geschlossen wird, hat der Arduino eine Interrupt Routine, die dafür sorgt, dass zu jedem Zeitpunkt die Nachricht gesendet werden kann1).

void setup() {
 
  //Funktion "interrupt_routine" an einen Interrupt Pin "binden"
  attachInterrupt(0, interrupt_routine, FALLING);
}
 
void interrupt_routine() {
 
  //Senden von Nachricht, wenn die Glockgeklingelt wurde
  Serial.write('2');
}

Bildverarbeitung

Die Bilderkennung war einer der wichtigsten Aspekte des Roboters, der ja erkennen sollte, welche Karten ausliegen. Um dies zu realisieren ist die Kamera am Kameraarm von oben auf das Spielfeld ausgerichtet, damit sie die beiden ausliegenden Karten überblicken kann. Die die Kamera wird also an den Computer angeschlossen und mithilfe von Processing (einer Entwicklungsumgebung für Java im Bereich Animation und Graphik) ein Livebild ausgegeben.

Um die Farben der ausliegenden Karten zu erkennen, werden in dem Livebild feste Bereiche definiert, in dem die Farbe erkannt wird. Von jedem Pixel in diesem Bereich wird nun der Farbwert genommen und der Median2) der Werte gebildet. Dieser neue Wert der Box wird nun mit Referenzfarbwerten verglichen und solte der Abstand von diesen zwei Farben unter einem bestimmten Schwellenwert liegen, wird angenommen, dass der gemessene Wert z. B. die Farbe Blau ist.

//Verfahren zur Bestimmung von Abständen zwischen Farben (Pseudocode)
a = 23ab5c //Farbe in Hexadezimaldarstellung und RGB-Farbsystem
b = 10a200
distanz = sqrt((rotWert(a)-rotWert(b))² + (grünWert(a)-grünWert(b))² + (blauWert(a)-blauWert(b))²)
//Damit der Abstand von zwei Farben äquivalent zu dem Abstand von zwei Punkten im 3-dimensionalen Raum

Durch ausprobieren sind wir schließlich auf folgende Referenzwerte gekommen:

Farbe Hex-Wert Darstellung
rot 8E2D1F ██████
grün 88965B ██████
lila 7848A7 ██████

Processing Programm

Das Hauptprogramm main arbeitet mit einer Instanz der Klasse Arduino und Camera

Arduino

Die Arduino-Klasse kümmert sich um die Kommunikation zwischen dem Computer und dem Arduino. Dazu baut es die Verbindung zunächst auf und kümmert sich dann darum, dass Nachrichten an den Arduino gesendet werden können, aber auch die Nachricht empfangen werden kann, dass die Klingel gerade betätigt wurde.

Camera

Die Camera-Klasse sorgt dafür, dass das Kamerabild ausgewertet wird. Somit stellt die Klasse eine Verbindung zur der angeschlossenen Kamera auf, und sorgt dafür, dass die Boxen richtig geladen werden. Im Sinne der objektorientierten Programmierung sind diese Boxen natürlich auch eigene Instanzen von Objekten, die wiederum eigene Methoden implementieren. Somit wertet die Camera-Klasse das Bild aus und bestimmt für jede Box den Farbwert.

Des weiteren bietet die Camera-Klasse die Möglichkeit sich:

  • Größe und Postion der Boxen
  • aktuell erkannte Farbe
  • Anzahl der je erkannten Farben
  • das Livebild

anzeigen zu lassen.

Box Klasse
class Box {
  int x; //x-Position
  int y; //y-Position
  int dx; //Breite
  int dy; //Hoehe
  Capture cam;
  color theColor; //Farbe der Box
  color[] colorReference; //Farbreferenzwerte
  int[] colorThreshold; //Farbspielräume
  String[] colorName; //Frabnamen
 
  //parametrisierter Konstruktor
  Box(int x, int y, int dx, int dy, Capture camera, String side) {}
 
//berechnen von Medianfarbwert
void median_color() {
  //Arrays für die verschiedenen Farbanteile
  int[] red = new int[this.dx*this.dy];
  int[] green = new int[this.dx*this.dy];
  int[] blue = new int[this.dx*this.dy];
  int m=0; //Laufindex
 
  //iterieren von allen Pixeln in der Box und aufspalten in die Farbanteile
  for (int i = this.x; i < (this.dx + this.x); i++) {
      for (int j = this.y; j < (this.dy + this.y); j++) {
        int k = i + j * width;
        color pixel = cam.pixels[k];
        red[m] = (int)red(pixel);
        green[m] = (int)green(pixel);
        blue[m] = (int)blue(pixel);
        m++;
      }
    }
 
    //Arrays sortieren
    red = sort(red);
    green = sort(green);
    blue = sort(blue);
 
    //setzten der Boxfarbe auf den Medianwert der einzelnen Farbanteile
    this.theColor = color((int)median(red), (int)median(green), (int)median(blue));
  }
 
  //Median von einem Int-Array bilden und zurückgeben
  double median(int[] k) {
    double median;
    if (k.length % 2 == 0) {
      median = ((double)k[k.length/2] + (double)k[k.length/2 - 1])/2;
    } else {
      median = (double)k[k.length/2];
    }
    return median;
  }
}

Um die Zusammenhänge der einzelnen Klassen besser darzustellen wäre hier wohl ein UML angebracht, jedoch wurde dieses noch nicht erstellt.

Bildauswertung
Abbildung 6: Bildauswertung3)

Somit kann man also jedem festgelegten Bereich eine Farbe zuordnen. Danach müssen nur noch die Anzahl der jeweiligen Farbfelder bestimmt werden, und schon kann gesagt werden, ob die Gewinnbedingung nach den Spielregeln eintrifft.

Fazit

Unser Halli-Galli Roboter ist in der Lage die Grundlagen des Spiels zu erledigen (Kartenausgabe → Kartenerkennung → Klingel ). Allerdings ist es noch nicht möglich ein richtiges Spiel gegen ihn zu spielen, weil er noch nicht erkennen kann ob sein Gegner eine neue Karte gelegt hat und folglich nicht weiß wann er am Zug ist. Jedoch haben wir ein sehr gut strukturiertes Framework geschaffen, welches auch sehr gut geeignet wäre für Gruppen in folgenden Projektlaboren weitergeführt zu werden.

Gesamter Code

1) Wenn man im normalen loop versuchen würde immer abzufragen, ob der Stromkreis geschlossen ist, dann passiert es, dass der Arduino in dem Moment, wo die Stromkreis kurz geschlossen ist gerade etwas anderes macht und das Schließen somit garnicht bemerkt.
2) Der Durchschnittsfarbwert lieferte zunächst sehr schwankende Werte, nach einer Empfehlung durch einer anderen Person im Robotiklabor wurde dann der Median-Farbwert benutzt
3) Hier sieht man aber auch, dass die Bildauswertung noch nicht optimiert wurde.
projektewise1718/haligalipublic/start.txt · Zuletzt geändert: 2018/07/10 13:47 von d.golovko