Pool-Billard Assistenz: Unterschied zwischen den Versionen

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen
 
(480 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
[[Kategorie:Projekte]]
[[Kategorie:Projekte]]
 
[[Datei:Billardausgabe.png|550px|thumb|right|Abbildung 1: Pool-Billard Assistenz]]
Autoren: [[Benutzer:Christo Tsibadze|Christo Tsibadze]], [[Benutzer:Kevin Penner|Kevin Penner]]<br/>
Autoren: [[Benutzer:Christo Tsibadze|Christo Tsibadze]], [[Benutzer:Kevin Penner|Kevin Penner]]<br/>
Betreuer: [[Benutzer:Ulrich_Schneider| Prof. Schneider]]
Betreuer: [[Benutzer:Ulrich_Schneider| Prof. Schneider]]
Zeile 8: Zeile 8:
= Aufgabenstellung =
= Aufgabenstellung =
'''Realisierung einer Billard-Assistenz-Software, mit Hilfe von Matlab und dessen Bildverarbeitungs-Tools:'''
'''Realisierung einer Billard-Assistenz-Software, mit Hilfe von Matlab und dessen Bildverarbeitungs-Tools:'''
Es soll ein "Billard-Assistenz-Software" entwickelt werden, die aus einem Video (oder Live-Cam) Billard Kugel- und Queue -Positionen erkennt und mit einer Algorithmus die Abprallrichtung einer Zielkugel "vorhersagt" (berechnet). Vor dem Schlag soll anhand der Queue- und Kugel-Positionen erkannt werden in welcher Richtung die weiße Kugel geschlagen wird und falls die weiße Kugel einen anderen Kugel trifft, dann in welcher Richtung diese abprallen wird. Die berechnete Richtungen sollen auf dem Video eingeblendet werden.
Es soll ein "Billard-Assistenz-Software" entwickelt werden, welche aus einem Video (oder Live-Cam) Billardkugeln- und Queue -Positionen erkennt und anschließend mit einem Algorithmus die Abprallrichtung einer Zielkugel "vorhersagt" (berechnet). Vor dem Schlag soll anhand der Queue- und Kugelpositionen erkannt werden, in welche Richtung die weiße Kugel geschlagen wird. Falls die weiße Kugel eine andere Kugel auf dem Billardtisch trifft, soll im Voraus die Abprallrichtung dieser ermittelt werden. Die berechnete Richtungen soll auf dem Video eingeblendet werden.
Für die Bildverarbeitung sowie Programmierung wurde MATLAB R2016a verwendet.
Für die Bildverarbeitung sowie Programmierung wurde die Matlabversion R2016a verwendet.
Eine CAD-Software wurde während der Entwicklung des Algorithmus zur Prüfung der Ergebnisse eingesetzt.
Eine CAD-Software wurde während der Entwicklung des Algorithmus zur Überprüfung der Ergebnisse eingesetzt.
 
Die Gesamtaufgabe wurde wie folgt aufgeteilt:
* Kevin Penner:
** Automatische Erkennung von Queue- und Kugelpositionen in einer Videoquelle
** sowie Unterscheidung der Kugeln, weiße- oder Zielkugel
* Christo Tsibadze:
** Ein Algorithmus programmieren, der die Schlagrichtung der weißen Kugel berechnet
** Erkennung ob die weiße Kugel nach dem Schlag einen anderen Kugel trifft oder nicht
** Falls die weiße Kugel eine andere Kugel trifft, Berechnung in welcher Richtung die andere Kugel abprallt.




== Erwartungen an die Projektlösung ==
== Erwartungen an die Projektlösung ==
* Sammeln von weiteren Erfahrungen in Bildverarbeitung mit MATLAB
* Sammeln von weiteren Erfahrungen in der Bildverarbeitung mit MATLAB  
* Umsetzung der in die Vorlesung gewonnenen Kenntnisse in die Praxis
* Umsetzung der in der Vorlesung gewonnenen Erkenntnisse in die Praxis
* Tieferer Einblick in die geometrische Physik
* Tieferer Einblick in die geometrische Physik
* Sammeln von Erkenntnissen durch Problemstellungen
* Lösen von Problemstellungen durch ingenieurmäßige Herangehensweisen
* Erstellung eines spektakulären Videos, welches die Software demonstriert
* Erstellung eines benutzerfreundlichen Videos, welches die Software visualisiert
<br />


= Plannung =
= Plannung =
== Projektplan ==
== Projektplan ==


Folgende Meilensteine wurden von uns bearbeiten:
* Automatische Erkennung von Queue- und Kugelpositionen in einem Videoframe
* sowie Unterscheidung der Kugeln in weiß- und Zielkugeln
* ein Algorithmus implementieren, der die Schlagrichtung der weißen Kugel berechnet
* Erkennung, ob die weiße Kugel nach dem Schlag eine andere Kugel trifft
* falls die weiße Kugel eine andere Kugel trifft, soll die Abprallrichtung der getroffenen Zielkugel berechnet werden
* Visualisierung von Hilfslinien im Video
* Dokumentation


Zunächst wurde ein Projektplan erstellt mit vier Phasen:
Die Aufgabe ist freiwillig. Zeitlich wurde für das Projekt im 6. Semester je ca. 3 Stunden pro Woche investiert.
Zur Ideenfindung oder Besprechung der Problemlösungsstrategien wurden regelmäßige Wochen-Meetings gehalten.
<br /> <br />


* Planung
= Bildverarbeitung: Erkennung von Queue- und Kugelpositionen =
* Konzept/Entwurf
[[Datei:Ablaufdiagramm Pool-Billard Assistenz.png|350px|thumb|right|Abbildung 2: Ablaufdiagramm zur Erkennung von Billardkugeln und Queue]]
* Realisierung
 
* Dokumentation
In Abbildung 2 ist ein Ablaufplan zu sehen, welcher den Algorithmus zur Erkennung von Billardkugeln und dem Queue grob veranschaulicht. Im Folgenden wird auf jeden dieser Punkte eingegangen:
<br />
<br />
 
== Frame einlesen und Zuschneiden des Bildes ==
 
Damit das Testen der Algorithmen stattfinden kann, wurden öffentliche Videos von Billardwettkämpfen genutzt. Diese können über die VideoReader Funktion eingelesen werden. Außerdem sind der Anfangsframe ('''Startframe''') und die Schrittweite ('''Frameschritte''') der zu analysierenden Frames einstellbar.
 
<source lang="matlab">
video=VideoReader('001.mp4');
nframes = video.NumberOfFrames;
Frameschritte = 1;
Startframe = 1;
</source>
<br />


und zu geschätzten Arbeitsstunden zugeordnet.
Da die Videos überflüssige Bildbereiche besitzen, wird zunächst der Billardtisch extrahiert, indem ein konstant definierter Bildbereich ausgewählt wird (ROI). Das zu analysierende Bild besitzt nun also einen homogenen Hintergrund (Farbe des Billardtisches), wodurch das Erkennen bzw. Segmentieren der Objekte vereinfacht wird.
<br /><br />


== Binarisierung ==


Zeitliche Ressource insgesamt ist mit 46 Arbeitsstunden geplant.
Für die Erkennung der Objekte im Bild (Kugeln und Queue) wird das RGB-Bild in ein Binärbild umgewandelt. Da die Objekte jeweils einen unterschiedlichen Farbwert besitzen, muss für die Binarisierung jeder einzelne Farbkern mit einem Schwellwertverfahren umgewandelt werden. Zur Unterstützung wurde hier das Matlabtool '''Color Thresholder''' eingesetzt, welches aus einem vom Benutzer ausgewählten Bildbereiches automatische eine Funktion generiert. Dieser wird im Matlab Code ein RGB-Bild als Parameter übergeben, woraufhin ein Binärbild zurückgegeben wird.
<br />
<br />
Diese Vorgehensweise muss jedoch für jeden spezifischen Billardtisch durchgeführt werden, falls ein anderer Tisch mit einer anderen Farbe angewendet wird.
<br />
<br />
In Abbildung 3 ist die Ausgabe des Originalbildes (links) und des Binärbildes (rechts) zu sehen. An einigen Stellen treten jedoch einzelne Artefakte auf, welche im Nachhinein noch entfernt werden müssen.
[[Datei:Unbereinigtes Binärbild Billard.png|800px|thumb|left|Abbildung 3: Unbereinigtes Binärbild Billard]]
<br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
== Nachverarbeitung ==
Um später zu Verhindern das Artefakte als Objekte wahrgenommen werden, wird das Binärbild zusätzlich noch bereinigt. Matlab bietet hier eine große Anzahl an Funktionen und morphologischen Operationen an. Folgende zwei Nachverarbeitungsschritte werden durchgeführt:
# Schwarze Pixel auf weißen Hintergrund werden gelöscht (Funktion: '''imfill'''). Diese Funktion füllt die Löcher in Objekten, damit diese, homogene Flächen besitzen.
# Mithilfe der Funktionen '''imopen''' und '''bwareaopen''' werden kleine weiße Pixelregionen im Bild gelöscht
In Abbildung 4 (rechts) ist nun eine deutliche Veränderung im Gegensatz zu Abbildung 3 (rechts) zu erkennen. Dieses Binärbild kann nun als Grundlage für die Erkennung der Objekte herangezogen werden.
<br />
<br />
<source lang="matlab">
%Binärbild bereinigen
imClean = imfill(BW,'holes');
se = strel('disk',1);
imClean = imopen(imClean,se);
imClean = bwareaopen(imClean, 80);
</source>
<br />
<br />


== Materialbeschaffung ==
[[Datei:Bereinigtes Binärbild Billard.png|800px|thumb|left|Abbildung 4: Bereinigtes Binärbild Billard]]
<br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
 
== Kugelerkennung ==


Für die Erkennung von Kreisen bietet Matlab die Funktion '''imfindcircles''' an, welche folgende Parameter übergeben bekommt:
# Bereinigtes Binärbild ('''imClean''')
# Maximalen und minimalen Radius (in Pixeleinheiten) eines erkannten Kreises
# Suche nach hellen Kreisen auf einem dunklen Hintergrund
# Kreisformabweichung (wurde experimentell ermittelt)
Als Rückgabewert erhält man eine Liste von Bildkoordinaten der erkannten Kreise und deren Radien.
<br />


<source lang="matlab">
%Parameter für die Größe und "Genauigkeit" der Billardkugeln
RadiusMin = 6;
RadiusMax = 12;
Circle_Sensitivity = 0.9;
</source>
<source lang="matlab">
%Kreise im bereinigten Binärbild suchen
[Centers, Radii] = imfindcircles(imClean,[RadiusMin RadiusMax],'ObjectPolarity','bright','Sensitivity',Circle_Sensitivity);
AnzahlKreise = size(Centers);
</source>
<br /> <br />


=== Erkennung der weißen Kugel ===


Einige Materialien konnten nicht bei vorgegebenen Händlern bestellt werden, deshalb wurden diese im Baumarkt erworben:
Die Erkennung der weißen Kugel erfolgt über eine selbstgeschriebene Funktion:
<source lang="matlab">
function[WhiteCenter, WhiteRadius, WhiteIndex] = Detect_White_Sphere(Centers, Radii, Image)
</source>


* ein Paar Holzplatten (Holzzuschnitt-Reste)
* sowie eine weiße Tapete (1 Meter), der das Licht diffus durchlässt
* günstiger Tapetenkleister
* Sekundenkleber


Dieser werden die Bildkoordinaten und Radien aller erkannten Kreise übergeben, sowie das zugeschnittene RGB-Bild. Als Rückgabewerte erhält man die Koordinaten, den Radius und den Index der weißen Kugel aus dem übergebenen Kreiskoordinatenvektor. <br />
In dieser Funktion werden alle erkannten Kreise in einer Schleife auf deren Helligkeit (vorher muss das RGB-Bild in ein Grauwertbild umwandelt werden) überprüft, indem der Mittelwert über alle Pixelwerte in einem Kreis gebildet wird und so der höchste Wert der weißen Kugel zugeordnet werden kann. Ob sich ein Pixel in einem Kreis befindet, kann mit folgender Formel berechnet werden:


Gesamtkosten (Holz und Tapete) weniger als 10€.
[[Datei:PunktInKreis.png|800px|thumb|right|Abbildung 5: Punkt im Kreis]]


= Konzeption/Entwurf =
<br /><br /><math>\mathtt{
PunktInKreis = (x-x_\text{center})^2 + (y-y_\text{center})^2 - r^2
}</math><br />


== CAD - Entwurf ==
<br /><math>\mathtt{
[[Datei: CAD 1.jpg|200px|mini|links|LED-Streifen_CAD]]
PunktInKreis < 0 \Rightarrow Koordinaten\ liegen\ im\ Kreis
[[Datei: CAD 2.jpg|300px|mini|rechts|LED-Streifen geklebt auf Gehäuse]]
}</math><br />
[[Datei: CAD 3.jpg|200px|mini|links|Gehäuse inkl Arduino]]
 
[[Datei: CAD 4.jpg|300px|mini|rechts|Konsole mit Gamepad ohne Tapete]]
<br /><math>\mathtt{
PunktInKreis = 0 \Rightarrow Koordinaten\ liegen\ auf\ Kreisbahn
}</math><br />
 
<br /><math>\mathtt{
PunktInKreis > 0 \Rightarrow Koordinaten\ liegen\ ausserhalb\ des\ Kreises
}</math><br />
 
<br /><br />Der Abscannbereich eines jeden Kreises wird wie in Abbildung 5 realisiert, indem ein Quadrat um den Kreis gebildet wird, in welchem anschließend nur die Pixel zum Mittelwert gezählt werden, welche mithilfe der Kreisgleichung zum Kreis zugeordnet werden können.
<br />Der Matlabcode sieht wie folgt aus:
<source lang="matlab">
for j=1:AnzahlKreise
  %Boundingbox um Kugel ermitteln
  xmin = round(Centers(j,1)- Radii(j));
  xmax = round(Centers(j,1)+ Radii(j));
  ymin = round(Centers(j,2)- Radii(j));
  ymax = round(Centers(j,2)+ Radii(j));
  %Boundingbox "abscannen". Wenn Punkt im Kreis liegt, dann soll eine
  %Pixelsumme aller Helligkeitswerte gebildet werden. Aus diesen wird
  %dann der Mittelwert gebildet
  PxSum = 0;
  PxInd = 0;
  for m=ymin:ymax
      for n=xmin:xmax
        %PunktinKreis < 0 --> Koordinate liegt im Kreis
        %PunktinKreis = 0 --> Koordinate liegt auf Kreisbahn
        %PunktinKreis > 0 --> Koordinate liegt außerhalb des Kreises
        PunktinKreis = (n-Centers(j,1))^2 + (m-Centers(j,2))^2 - Radii(j)^2;
        if PunktinKreis <= 0 && n <= width && m <= height && n>0 && m>0
            PxSum = PxSum + double(GrayImage(m,n));
            PxInd = PxInd + 1;
        end
      end
  end
  PxMean(j) = PxSum/PxInd;
end
</source>
<br />
<br />
== Queueerkennung ==
Nachdem die weiße Kugel erkannt ist, wird der Queue in der unmittelbaren Umgebung zur weißen Kugel gesucht. Hierzu wurde folgende Funktion implementiert:
<source lang="matlab">
[Queue] = Detect_Queue_01(WhiteCenter, DetectionRadius, imClean);
</source> <br />
[[Datei:Queueerkennung.png|300px|thumb|right|Abbildung 6: Queueerkennung]]
Der Funktion werden die Koordinaten der weißen Kugel, der Absuchradius zur Detektion des Queues und das bereinigte Binärbild übergeben. Als Rückgabewert wird die Position des Queues geliefert. <br />
In der Funktion wird auf der Kreisbahn mit einem Absuchradius um die weiße Kugel nach weißen Pixelwerten gesucht (ob ein Pixel auf der Kreisbahn liegt, wird mit derselben Kreisgleichung, wie bei der Erkennung der weißen Kugel, überprüft). Die Koordinaten der Pixelwerte, die zu dem Queue zugeordnet werden, werden in einen Vektor geschrieben. Um den Mittelpunkt des Queues zu erhalten, wird folgende Berechnung im Anschluss an die Erfassung der Pixelwerte durchgeführt:
<br /><br /><math>\mathtt{
Queueposition_X = \frac{Min(Queuevektor_X) + Max(Queuevektor_X)}{2}
}</math><br />
<br /><br /><math>\mathtt{
Queueposition_Y = \frac{Min(Queuevektor_Y) + Max(Queuevektor_Y)}{2}
}</math><br /> <br />
In der Testphase des Algorithmus zur Erkennung hat sich jedoch herausgestellt, dass aufgrund der ungenauen Binarisierung und damit verbundenen schlechten Erkennung des Queues, eine Abweichung zwischen wahrer mittleren Position und erkannter mittleren Position des Queues resultierte. Diese Abweichung konnte durch das Erfassen von mehreren Punkten auf dem Queue minimiert werden (siehe Abbildung 6). Die Funktion wird nun in einer Schleife ausgeführt, in welcher der Absuchradius stetig erhöht wird:
<source lang="matlab">
%Absuchradius um weiße Kugel zum detektieren des Queue
DetectionRadius = 15;
DetectionSteps = 5;
Iterationen = 10;
</source>
<source lang="matlab">
for j=1:Iterationen
  [Queue] = Detect_Queue_01(WhiteCenter, DetectionRadius + (j-1)*DetectionSteps, imClean);
end
</source> <br />
Die detektierten Punkte werden in einen Vektor geschrieben. Für das Erhalten eines eindeutigen Punktes auf dem Queue, wird nun noch der Mittelwert aller x- und y-Koordinaten gebildet.
<br />
<br />
In der Konzeptions- bzw. Entwurfsphase wurde die Retro-Game-Station zu erst im CAD entworfen.


=== Eingeschränkte Durchführung des Algorithmus ===
Da die Ermittlung der Queueposition nur erfolgen muss, wenn sich die weiße Kugel im Stillstand befindet, wird vor der Ausführung der Funktion zur Queueerkennung die Positionsveränderung der weißen Kugel kontrolliert. Hierfür wird die Differenz der alten Position der weißen Kugel mit der neuen Position gebildet und mit einem geringem Schwellwert verglichen:
<source lang="matlab">
Position_of_rest = ((abs(WhiteCenter(1)-WhiteCenterOld(1))<5) && (abs(WhiteCenter(2)-WhiteCenterOld(2))<5))
if Position_of_rest
.
.
.
end
</source> <br />
= Billardprognose =


die WS2812B LED-Streifen, sowie das Arduino Uno Mikrokontroller-Platine und diverse Schalter, Knöpfe und Potentiometer wurden aus der Webseite https://grabcad.com verwendet.
Nach der Bildverarbeitung jedes Videoframes wird die Vorhersagefunktion "'''Billard_Prediction'''" mit folgenden Parametern und Rückgabewerten aufgerufen:  
Die Form, Größe und das Material wurden für restliche Einzelteile während der CAD-Konstruktion festgelegt.
<br />
<br />
Es wurden folgende Teile entworfen:
* seitliche dreieckige Stütz-Teile
* hintere Deckel aus dünner Holzplatte
* dünne Holzplatte auf dem die LED-Streifen geklebt werden
* dicke Holzplatte mit relativ großen Bohrungen mit der gleichen Abstand wie die LEDs zueinander
* Gamepad


<source lang="matlab">
function [ RICHTUNG_Koordinaten, ZIEL_KUGEL, RICHTUNG_Koord_Weiss ] = Billard_Prediction( W,Z,Q,R )
</source> <br />


<br />
* '''W''': X- und Y-Koordinate der weißen Kugel
<br />
Format:<source lang="matlab">[double x;double y]</source> <br />
<br />
* '''Z''': X- und Y-Koordinaten aller Zielkugeln in einer Matrix
<br />
Format:<source lang="matlab">[[double x1;double y1],[double x2;double y2],...,[double xn;double yn]];</source> <br />
<br />
* '''Q''': X- und Y-Koordinate der Queuespitze
<br />
Format:<source lang="matlab">[double x;double y]</source> <br />
<br />
* '''R''': Kugelradius (Annahme: alle Kugeln sind gleich groß)
<br />
Format:<source lang="matlab">[double r]</source>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />


== Technische Zeichnungen ==
Als Rückgabe liefert die Funktion folgende Werte:


* '''RICHTUNG_Koordinaten''': ein Vektor mit der Länge von 50 Pixeln, dessen Startpunkt die anvisierte Zielkugel ist und dessen Richtung die vorhersagte Abprallrichtung der Zielkugel beschreibt.
Format:<source lang="matlab">[double x;double y]</source> <br />
* '''ZIEL_KUGEL''': Gibt die Koordinate der anvisierten Zielkugel zurück
Format:<source lang="matlab">[double x;double y]</source> <br />
* '''RICHTUNG_Koord_Weiss''': Gibt eine Hilfskoordinate zurück, um die "Anvisierlinie" der weißen Kugel zu visualisieren
Format:<source lang="matlab">[double x;double y]</source> <br />


Falls keine Zielkugel anvisiert wird, werden alle Rückgabewerte mit 0 initialisiert.


[[Datei: Gamepad_2D.JPG|200px|mini|links|Technische Zeichnung Gamepad]]
== Vorbereitung ==
[[Datei: Dicke_Platte_2D.JPG|300px|mini|rechts|Technische Zeichnung Dicke_Platte]]
Da die Y-Achse der Bildkoordinaten bei der Videoverarbeitung von oben nach unten verläuft (positive Y-Werte) und im Vorhersagealgorithmus von unten nach oben (negative Y-Werte), werden alle Y-Koordinaten vor und nach der Berechnung umgekehrt.
<source lang="matlab">%% Y-Achse Umkehren 
Z(2,:) = Z(2,:).*-1;
Q(2) = Q(2).*-1;
W(2) = W(2).*-1;</source>


Nach der Fertigstellung der 3D-CAD-Modelle wurden daraus die technische Zeichnungen für die Fertigung abgeleitet.
Es wurden alle wichtigen Abmaße eingetragen und im Maßstab 1:1 ausgedruckt.


Einige Zeichnungen wurden direkt auf den Holzplatten mit Klebefilm befestigt und somit die Positionierung der Bohrungen erleichtert.
=== Sortierung nach Entfernung ===
<br />
Wenn in der Schlagrichtung der weißen Kugel mehrere Zielkugeln positioniert sind, ist es erforderlich zu unterscheiden, welche Kugel den kürzesten Abstand zur weißen Kugel besitzt bzw. welche Zielkugel zuerst getroffen wird.
<br />
<source lang="matlab">%% Zielkugel mit kleinsten Abstand finden (und sortieren)
<br />
Anzahl_Zielkugeln = size(Z,2);
<br />
B(3,Anzahl_Zielkugeln)=zeros; %Neue Matrix vorbereiten
<br />
B(2,:,:)=Z(1,:); % in die zweite Zeile alle X-Koordinaten der Zielkugeln
<br />
B(3,:,:)=Z(2,:); % in die dritte Zeile alle Y-Koordinaten der Zielkugeln
<br />
for i = 1: Anzahl_Zielkugeln
<br />
    B(1,i,:) = norm(Z(:,i)-W); % in die erste Zeile --> Abstände aller Zielkugeln zur weißen Kugel
<br />
end
<br />
B = sortrows(B')'; % Matrix transponieren, nach Abstände zur weißen Kugel ordnen und zurücktransponieren</source> <br />
<br />
Die Anzahl der Spalten ist gleich der erkannten bzw. übrig gebliebenen Zielkugeln.
<br />
Für die Sortierung nach der Entfernung wird eine neue Matrix B mit drei Zeilen erzeugt. Die X- und Y-Koordinaten aus der Z-Matrix werden in  die zweite und dritte Zeile der B-Matrix eingetragen (1. --> 2 und 2. --> 3.).
<br />
In der ersten Zeile wird für jede Zielkugel die Entfernung zur weißen Kugel berechnet und eingetragen.


== Leiterplatten Entwurf ==
Die Entfernung einer Zielkugel zur weißen Kugel wird wie folgt ermittelt:
[[Datei: Elektronik.JPG|200px|mini|rechts|Fritzing Projekt]]
* Vektorbestimmung anhand der Koordinaten von Ziel- und weißer Kugel <source lang="matlab">[x1-x1;y1-y2]</source> <br />
* Entfernung: Länge des Vektors bestimmten <source lang="matlab">sqrt(x^2 + y^2)</source>


Nachdem alle Entfernungen der Zielkugeln in die erste Zeile der B-Matrix eingetragen wurden, wird mithilfe der MATLAB-Funktion '''sortrows''' die Matrix aufsteigend (nach Entfernungen) sortiert.


Der elektrische Schaltplan wurde mit Hilfe von einem Freeware-Software (Fritzing; http://fritzing.org/home/) realisiert.
Da die Funktion<source lang="matlab">sortrows</source> den Inhalt der ersten Matrizenspalte aufsteigend ordnet, jedoch die Entfernungen in unserem Fall in der ersten Zeile stehen, wird die Matrix vor und nach dem Sortieren transponiert. Beim Transponieren werden die Zeilen und Spalten vertauscht bzw. Zeilen in Spalten und Spalten in Zeilen umgewandelt.
Dort ist es möglich die einzelne elektrische Bauteile virtuell miteinander zu verschalten und zu parametrisieren.
Anschließend ist es möglich daraus einen elektrischen Schaltplan zu erzeugen und sogar auch Leiterplatten-Design ist hiermit möglich.
Die verschalteten elektrische Bauteile, der Schaltplan, die Leiterplatten-Layout und auch das Arduino Code zusammen lassen sich in einem Fritzing-Projekt als eine Datei speichern.


<br />
Nach der Sortierung besteht die B-Matrix aus drei Zeilen:
<br />
*1. Zeile: Abstände zur weißen Kugel (aufsteigend geordnet)
<br />
*2. Zeile: X-Koordinaten der Zielkugeln
<br />
*3. Zeile: Y-Koordinaten der Zielkugeln
<br />
Die Spaltenanzahl entspricht der Anzahl der Zielkugeln.
<br />
<br />
<br />


= Realisierung =
== Schlagrichtung weiße Kugel ==
== Hardware-Design ==
[[Datei:Schlagrichtung.jpg|250px|thumb|left|Abbildung 7: Schlagrichtung]]
=== Bildschirm ===
[[Datei: Tapeten_Test.JPG|200px|mini|links|Licht-Test mit Tapete]]
[[Datei: LED-Schablone.JPG|200px|mini|rechts|LED-Schablone für exakte Positionierung]]
[[Datei: LED-Streifen_anbringen.JPG|200px|mini|links|LED-Streifen anbringen]]
[[Datei: Dicke_Platte.JPG|200px|mini|rechts|Platte bohren]]
<br />
<br />
<br />
<br />
<br />
Für die "Haut" der Konsole wurde eine weiße undurchsichtige Tapete mit bestimmtem Muster im Baumarkt gekauft. Die Tapete ist nicht transparent aber wie im Bild (links) zu sehen ist, kommen die Farben durch.


Eine Zeichnung von einer CAD-Baugruppe (Holzplatte mit positionierten LED-Streifen) wurde auf ein Brett geklebt und anschließend mit einem sehr scharfem Messer vorsichtig rechteckige Streifen ausgeschnitten. In die ausgeschnittene Bereiche wurden die LED-Streifen positioniert und verklebt. Zuvor wurden die LED-Streifen (60-LEDs pro Meter) in kleinere Streifen (18-LEDs pro Streifen) zugeschnitten. Aus dem Verschnitt wurde die letzte 18-LED-Streifen zusammengelötet. Die Verkabelung der LED-Streifen wurde durch kleine Bohrungen seitlich durch das Brett geführt.
Um herauszufinden in welche Richtung die weiße Kugel geschlagen bzw. anvisiert wird, wird mithilfe der Koordinaten des Queues '''Q(x;y)''' und der weißen Kugel '''W(x;y)''' ein Schlagrichtungsvektor berechnet und anschließend normiert.
<source lang="matlab">%% Richtung Direkt
Q_R=(W-Q);% Richtung QW Queue-Weiss
Q_R_Norm = Q_R./norm(Q_R); %Vektor normieren</source><br />
Die Vektornormierung wird wie folgt berechnet: Der Vektor wird durch seine eigene Länge geteilt.
Einfachheitshalber wird angenommen, dass mit der Queuespitze immer auf die Mitte der weißen Kugel gezielt wird, da das Projekt sonst im verfügbaren Zeitrahmen nicht machbar gewesen wäre.
<br /><br /> <br /><br /> <br /><br /> <br /><br /><br />


<br />
== Treffer oder kein Treffer ==
<br />
[[Datei:Max Winkel fuer Treffer.jpg|250px|thumb|right|Abbildung 8: Phi_max fuer Treffer]]
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Nachdem die dünne Platte mit den LED-Streifen fertiggestellt war, wurde mit der Bearbeitung einer dickeren Platte gestartet. Diese Platte soll auf die dünnere Platte draufkommen und mit 234 Bohrungen die Lichter der einzelnen LEDs von einander trennen. Zuerst wurden breite Bohrungen gebohrt und anschließend von beiden Seiten gesenkt.  


<br />
Um herauszufinden, ob die weiße Kugel in der Schlagrichtung eine Zielkugel trifft, werden folgende Berechnungen durchgeführt:<br /> Es wird ein imaginäres gleichschenkliges Dreieck aufgestellt mit folgenden gegebenen Seitenlängen (siehe Abbildung 8):
<br />
* Länge der kleinen Seite: 2 x Kugelradius (bzw. 1 x Kugeldurchmesser)
<br />
* Länge der langen Seite: Entfernung der weißen Kugel zur Zielkugel (direkte Linie: '''D_L''')
<br />
Als Länge der kleinen Seite wurde der Kugeldurchmesser gewählt, da sich die Kugeln bei einem Kontakt berühren.
<br />
Zu bestimmen ist der Winkel '''"Phi_max"''' (siehe Abbildung 8). Dieser Winkel ist zwischen der Schlagrichtung der weißen Kugel '''Q_R''' und der direkten Richtung '''D_L''' eingeschlossen und ist abhängig von der Entfernung der Zielkugel zur weißen Kugel. Die Zielkugel kann nur dann getroffen werden, wenn der aktuelle Winkel ('''Alpha''') kleiner ist als '''Phi_max'''.
<br />
<br />
<br />
<br />
<br />
----


=== Gamepads ===
[[Datei:Max Winkel fuer Treffer 2.jpg|250px|thumb|right|Abbildung 9: Phi_max fuer Treffer Zwischenrechnung]]
[[Datei: Elektronik_Gamepad.JPG|200px|mini|links|Gamepad-Elektronik]]
[[Datei: Gamepad_Bohrschablone.JPG|200px|mini|rechts|Gamepad Bohrungen, zuvor gefräst]]
[[Datei: Gamepad Gebohrt.JPG|200px|mini|links|Hardware und Elektronik]]
[[Datei: Gamepad Verklebt.JPG|200px|mini|rechts|verklebt]]
<br />
<br />
<br />
<br />
<br />
<br />
Eine Lochrasterplatine wurde in kleinere Rechtecke geschnitten. Darauf wurden Knöpfe platziert und verlötet inklusive Verkabelung. Am ende der Kabelstrangbündel wurde ein VGA-Anschluss verlötet.
<br />
<br />
<br />
<br />
Aus den Rechteckigen Holzklötzen mit den Maßen des Gamepads wurde die untere Seite ausgefräst. Anschließend eine Schablone draufgeklebt und die Bohrungen für die Knöpfe in vorher bestimmten Positionen gebohrt.
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Die Gamepad-Elektronik und Gamepad-Gehäuse wurden zusammengefügt. Die Leiterplatten wurden mit einem Klebstoff fest positioniert und verklebt. Für gleichmäßigen Abstand der Knöpfe wurden an jeder Ecke der Leiterplatten gleichgroße Schrauben als Abstandshalter verwendet.
Zur Kabelzugsentlastung wurde an dem Kabelbündel ein Doppelknoten mit einem Leitungsstück angebracht. Anschließend wurde eine dünne Platte als Deckel drauf geschraubt.


<br />
Für die Bestimmung von '''Phi_max''' von jeder Zielkugel wird zunächst nur die Hälfte des gleichschenkligen Dreiecks betrachtet --> rechtwinkliges Dreieck (siehe Abbildung 9)
<br />
* '''D_L''': Direkte Linie zwischen der Ziel- und weißen Kugel entspricht der Hypotenuse
<br />
** Anfangsposition der '''D_L''': Koordinaten der weißen Kugel
<br />
** Endposition der '''D_L''' : Koordinaten der Zielkugel
<br />
** Länge der '''D_L''': Betrag des Vektors, welcher zwischen Ziel- und weißer Kugel aufgespannt wird
<br />
* Länge der Gegenkathete: Kugelradius
<br />
* Winkel zwischen Gegen- und Ankathete: 90°
<br />
<br />
----


=== Leiterplatte und Verkabelung ===
Da die Hypotenuse, die Gegenkathete und ein Winkel bekannt sind, ist der gesuchte Winkel mithilfe des Sinus ermittelbar:<br />
--> '''Phi_max''' = 2 * arcsin(Gegenkathete/Hypotenuse)
<source lang="matlab">Phi_max=2*asind((R)/B(1,i,:)); %maximaler Winkel für Treffer</source> <br />


[[Datei: Arduino_PCB_Draufsicht.JPG|200px|mini|links|Aufsteckplatine und Arduino]]
Anschließend wird der aktuelle Winkel '''"Alpha"''' zwischen den '''D_L'''- und '''Q_R''' Vektoren wie folgt berechnet:
[[Datei: Arduino_PCB_Unten.JPG|200px|mini|rechts|Aufsteckplatine und Arduino]]
* [https://de.wikipedia.org/wiki/Skalarprodukt Winkel zwischen Vektoren Skalarprodukt]
[[Datei: Gesamtverkabelung.JPG|200px|mini|links|Verkabelung und Arduino auf die dünne Platte]]
[[Datei: Zusatzkondensator.JPG|200px|mini|rechts|Kondensator]]
<br />
<br />
    <source lang="matlab">D_L=(B(2:3,i,:)-W);% direkte Richtung, zwischen Ziel- und weißer Kugel
%% aktueller Winkel Schlagrichtung bzw. direkte Richtung
Alpha = acosd((dot(Q_R,D_L))/((norm(Q_R) * norm(D_L)))); % Winkel zwischen Q_R und D_L</source>
<br />
<br />
Ist der Winkel '''Alpha''' kleiner als '''Phi_max''' --> Treffer, sonst kein Treffer.
<br /> <br />
== Falls Treffer: Abprallrichtung ==
Mithilfe des Algorithmus wird zunächst geprüft, ob die Zielkugel mit dem kleinsten Abstand zur weißen Kugel in aktueller Queuerichtung getroffen wird. Falls dies nicht der Fall ist, wird die Zielkugel mit dem nächstkleineren Abstand geprüft, bis eine Zielkugel gefunden wird, die getroffen wird. Ist eine Zielkugel gefunden, die getroffen wird, wird die Abprallrichtung der Zielkugel berechnet.
<br />
<br />
[[Datei:Kosinus Satz.jpg|250px|thumb|right|Abbildung 10: Kosinus Satz]]
<br />
<br />
Aus einer Lochrasterplatine wurde wieder ein Rechteck im passender Größe zur Arduino Uno zugeschnitten. Auf dieser Platine wurden Pins darangelötet mit dem sie auf das Arduino Board dadrauf gesteckt wurde.
Gegeben ist ein allgemeines Dreieck mit:
Auf dieser Aufsteckplatine wurden Widerstände für die Knöpfe, ein Piezo-Speaker, ein linearer Potentiometer und Leitungen von den Anschlüssen für Gamepads sowie Datenleitung und Stromversorgung für die LED-Matrix verlötet.
* '''D_L''': die Direkte Linie zwischen der weißen und der Zielkugel
Für die Stromversorgung wurde ein AC/DC Wandler (5V mit 3000mA) verwendet. Der Stecker wurde abgeschnitten und direkt auf die Aufsteckplatine gelötet, als Stromversorgung für Arduino und auch für die LEDs. Zwischen der Plus- und Minus Leitung wurde ein Kondensator mit 1000µF (bis 50V) verbaut.
** Länge, Richtung und Position
* '''Q_R''': die Schlagrichtung der weißen Kugel
** Startpunkt (weiße Kugel), Richtung, keine Länge
* '''Alpha''': Winkel Alpha ist ermittelbar durch die zwei Vektoren '''D_L''' und '''Q_R'''
* 2 x '''R''': Richtung zwischen der Zielkugel und der weißen Kugel beim Aufprall (Richtung unbekannt, nur Länge bekannt)
** Länge = 2 x Kugelradius
Gesucht: Der Winkel '''Beta''', die Position des Überschneidungspunktes '''W' ''', sowie der Winkel im Punkt '''Z''' --> '''Gamma'''.
 
Da zwei Seiten des Dreiecks und der Winkel zwischen diesen Seiten bekannt ist, sind mithilfe des Kosinus die restlichen Parameter zu berechnen.
<br />
<br />
<br />
Siehe folgende Quelle: [https://de.wikipedia.org/wiki/Dreieck Berechnung eines beliebigen Dreiecks]
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Das Arduino-Board mit der Aufsteckplatine wurde auf der Rückseite der dünnen Platte (mit LED-Streifen auf anderer Seite) mit Kabelbinder und Klebepads für Kabelbinder befestigt.
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
----


=== Konsole ===
<source lang="matlab">    Beta=180-asind((B(1,j,:)/(2*R))*sind(Alpha)); %
[[Datei: Zusammenbau_1.JPG|200px|mini|links|Seitenstützen-Montage]]
    Gamma=180-Beta-Alpha;</source>
[[Datei: Zusammenbau_2.JPG|200px|mini|rechts|Lochplatten-Montage (Dicke-Platte)]]
[[Datei: Zusammenbau_3.JPG|200px|mini|links|Montage hintere Platte]]
[[Datei: Tapezieren.JPG|200px|mini|rechts|Tapezieren]]
<br />
<br />
<br />
Die dünne Platte mit LED-Streifen und die Dicke Platte mit 234 Bohrungen wurden miteinander verschraubt. Aus der dickeren Holzplatte wurden zwei dreieckige Stützen nach technischen Zeichnungen zugeschnitten und auf die dünne Holzplatte hinten drangeschraubt. In den dreieckigen Stützen wurden auch die Anschluss-Stecker (male) montiert.
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Anschließend wurde aus einer noch dünneren Platte die Deckel für Hinten inkl. Aussparungen für Potentiometer und Ein/Aus-Schalter gefertigt und an die Spielkonsole geschraubt.
Zum Schluss der Hardware-Fertigung wurde die Tapete zugeschnitten und damit die Konsole vorne und seitlich tapeziert.
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
----


== Software-Design ==


Es wurde mit Hilfe von Arduino IDE programmiert.
Sind alle Winkel bekannt, ist es anhand des Winkels '''Gamma''' möglich, die Kugelabprallrichtung zu bestimmen. Denn genau um diesen Winkel muss der Vektor '''D_L''' rotiert werden, um den Kugelabprallrichtung zu erhalten.<br />
Die LED-Streifen wurden mit Hilfe der Bibliotheken "FastLED" und NEOPixel" angesteuert.
Die Quellcodes wurden sowie die Fertigungspläne (technische Zeichnungen) wurden im SVN-Projektordner hochgeladen.
----
=== Steuerung ===
Zunächst wurde ein TEST-Programm für Hardware entwickelt, welches die Funktionalität der Tasten und Potentiometer prüft.
Bei Betätigung der Tasten wird auf dem seriellen Monitor die Bezeichnung der gedrückten Taste ausgegeben. Auch der Wert der Potentiometer ist ablesbar
----


=== Coole visuelle Effekte ===
Die Rotation einer Geraden bzw. eines Vektors ist durch Multiplikation mit einer [https://de.wikipedia.org/wiki/Drehmatrix Rotationsmatrix] festzustellen.
In den Bibliotheken "FastLED" und "NEOPixel" sind mehrere Beispielprogramme für schöne visuelle Effekte.
<br />
<div class="tright" style="clear:none">[[Datei: Effekt_4.JPG|350px|thumb|Effekt_4]]</div>
<div class="tright" style="clear:none">[[Datei: Effekt_3.JPG|350px|mini|thumb|Effekt_3]]</div>
<div class="tright" style="clear:none">[[Datei: Effekt_2.JPG|350px|mini|thumb|Effekt_2]]</div>
<div class="tright" style="clear:none">[[Datei: Effekt_1.JPG|350px|mini|thumb|Effekt_1]]</div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
----


=== Sounds ===


=== Fallunterscheidungen ===
[[Datei:Fallunterscheidungen.png|280px|thumb|right|Abbildung 11: Fallunterscheidungen]]


Es wurden Soundausschnitte aus Super Mario-Melody und Star Wars-Melody mit Hilfe des Summers und Arduino Beispiel-Programms realisiert.
Um die korrekte Abprallrichtung der Zielkugel zu berechnen, ist es notwendig verschiedene Fälle zu unterscheiden.
<br />
Als "Game Over"-Sound wurde ein Teil des Star Wars Melodie verwendet. Und als Anfangssequenz ein Teil aus Super Mario.
Bei jedem "Frucht" der Snake gegessen hat wird auch ein Peepton abgespielt.
<br />
<br />
Super Mario:
<br />
Video: https://youtu.be/-kkxs_fekWM
<br />
HowTo: http://www.princetronics.com/supermariothemesong/
<br />
<br />
Star Wars Melody:
<br />
Video: https://youtu.be/cye_wiaqybk
<br />
HowTo: http://www.instructables.com/id/How-to-easily-play-music-with-buzzer-on-arduino-Th/
<br />
<br />
Die Zielkugel wird von:
* links nach rechts
* rechts nach links
* oben nach unten
* unten nach oben
geschlagen:
<source lang="matlab">    %% Fallunterscheidung für Winkel Gamma, Anordnung W und Z
    if((W(1)<B(2,j,:))&&(W(2)>B(3,j,:)))
        Fall = 1; %Schlag von links oben
    end
    if((W(1)<B(2,j,:))&&(W(2)<B(3,j,:)))
        Fall = 2; %Schlag von links unten
    end
    if((W(1)>B(2,j,:))&&(W(2)>B(3,j,:)))
        Fall = 3; %Schlag von rechts oben
        Gamma=(180-Gamma);
    end
    if((W(1)>B(2,j,:))&&(W(2)<B(3,j,:)))
        Fall = 4; %Schlag von rechts unten
        Gamma=(180-Gamma);
    end</source> <br />
Außerdem muss überprüft werden, ob die Zielkugel auf der rechten oder linken Seite getroffen wird:


<source lang="matlab">    %% Winkelberechnung für Orientierung im 2D-Raum
    Winkel_1 = acosd((dot(RefH,Q_R))/((norm(RefH) * norm(Q_R))));
    Winkel_2 = acosd((dot(RefH,D_L))/((norm(RefH) * norm(D_L))));
    if(Winkel_1 == Winkel_2)
            Gamma=0;
    else
        switch Fall
            case 1
                if(Winkel_1 > Winkel_2)
                    Gamma=-Gamma;
                end
            case 2
                if(Winkel_1 < Winkel_2)
                    Gamma=-Gamma;
                end
            case 3
                if(Winkel_1 < Winkel_2)
                    Gamma=-Gamma;
                end
            case 4
                if(Winkel_1 > Winkel_2)
                    Gamma=-Gamma;
                end
        end
    end</source> <br />


Je nach Kugelanordnung wird in der Rotationsmatrix der Winkel '''Gamma''' entweder als negativ, positiv oder 180°-'''Gamma''' angenommen.


----
Für die Ermittlung der richtigen Abprallrichtung sind mehrere Rotationen um 90° und Vektorumkehrungen (Multiplikation mit -1) notwendig. Um die Implementierungen bzw. das Debuggen zu vereinfachen, wurden selbst erstellte CAD-Skizzen und eine MATLAB-GUI herangezogen. Somit wurden alle möglichen Fälle va­li­die­rt und die Software dementsprechend angepasst.


=== Snake ===
<source lang="matlab">    %% Rotation von D_L auf Abprallrichtung
    RICHTUNG_NEU=rot90(W-B(2:3,j,:))*[cosd(Gama) -sind(Gama); sind(Gama) cosd(Gama)];
    RICHTUNG_NEU=rot90(rot90(rot90((RICHTUNG_NEU)))).*-1;
    if(Fall==4 || Fall==3)
        RICHTUNG_NEU=RICHTUNG_NEU.*-1;1
    end;</source>


Nein, das Rad wurde nicht neu erfunden ;).
<br />
Die Snake-Software als .cpp und .h-Datei wurde aus GitHub-Webseite heruntergeladen, angepasst und auf Arduino geflasht.


Snake-Quelldaten GitHup: https://github.com/emanuelk/Snake-Game-Library-for-Arduino.git
=== Rückgabewerte ===


<source lang="matlab">if(Q(1)~= 0 && Q(2) ~= 0) % Rückgabewerte nur wenn Queueposition bekannt ist
        RICHTUNG_NEU = RICHTUNG_NEU./norm(RICHTUNG_NEU); %normieren
        RICHTUNG_NEU = RICHTUNG_NEU.*50; %feste Vektorlänge von 50 Pixel
        RICHTUNG_Koordinaten(1) = B(2,j,:)+RICHTUNG_NEU(1); % X-Komponente der Rückgabevektor für Abprallrichtung mit Startpunkt (Zielkugel)
        RICHTUNG_Koordinaten(2) =  B(3,j,:).*-1+RICHTUNG_NEU(2).*-1; % Y-Komponente der Rückgabevektor für Abprallrichtung mit Startpunkt (Zielkugel),
        %Richtung angepasst                           
        ZIEL_KUGEL = [B(2,j,:) ; B(3,j,:).*-1]; % Koordinaten der aktuell anvisierten Zielkugel, Y-Koodrinate angepasst
        RICHTUNG_Koord_Weiss = B(1,j,:).*Q_R_Norm+W; % Hilfslinie Startpunkt Weiße Linie X-Komponente
        RICHTUNG_Koord_Weiss(2) = RICHTUNG_Koord_Weiss(2).*(-1);% Hilfslinie Startpunkt Weiße Linie Y-Komponente angepasst
end</source>
   


Zusätzlich wurde im Spiel ein farbiger Highscore-Balken eingebaut welches den aktuellen Fortschritt/Schwierigkeitsgrad anzeigt.
Die Rückgabewerte werden so angepasst, dass die Y-Komponenten wieder den Bildkoordinaten entsprechen.
<br /><br />


== MATLAB GUI ==


----
Für Testzwecke wurde eine MATLAB GUI erstellt, womit jedes mögliche Szenario (Kugelanordnung und Schlagrichtung) simuliert wurde. Mithilfe dieser wurde die Software optimiert und von diversen kleineren Bugs befreit.
[[Datei:Aktueller Stand GUI Billard.PNG|1000px|thumb|center|Abbildung 12: GUI Billard]]
<br /><br />
Die Positionen der Kugeln können entweder per Schieberegler oder manuell eingegeben werden.
Durch das Drücken auf die "GO"-Taste, berechnet die Software die Abprallrichtung und gibt diese Linie aus.
<br /> <br />
 
= Ausgabe =


=== Präsentationssoftware für die Messe ===
Als Ausgabe erfolgt die Darstellung des zugeschnittenen Originalbildes (siehe Abbildung 12) inklusive folgender Geraden, Punkte und Kreise:


[[Datei: Gesamtkonsole.jpg|400px|mini|links|fertige Retro-Game-Station]]
*Gerade zwischen Queueposition und des Mittelpunktes der weißen Linie (rote Linie)
<br />
*Gerade zwischen Mittelpunkt der weißen Kugel und Aufprallpunkt an der Zielkugel (grüne Linie)
<br />
*Gerade zwischen Mittelpunkt der Zielkugel und Richtung der Zielkugel (gelbe Linie)
<br />
*Mittelpunkt der weißen Kugel (blauer Punkt)
Für die Messe wurde ein Quellcode zusammengestellt mit:
*Umrandung aller Kugeln auf dem Billardtisch (rote Kreise)


* einem Spielablauf (main-programm)
[[Datei:Billardausgabe.png|1000px|thumb|center|Abbildung 13: Ausgabe]]
* Intro mit HSHL-Logo (wenn die Konsole angeschaltet oder neugestartet wird) mit SuperMario-Sound
* Selbstgeschriebener auf Zufall basierter Algorithmus für Lichtfarbeneffekt der nach dem GameOver erscheint bis wieder eine Taste betätigt wird zum Spielneustart
* Steuerung: Eingabe Gamepads und Potentiometer und Ausgabe: LED-Matrix
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />


----
= Resultat =


= Dokumentation =
== Probleme ==
== Präsentationsmaterial für die Messe ==
[[Datei: Plakat.JPG|400px|mini|links|Plakat für die Messe]]
[[Datei: RetroGameStation HSHL Messe.jpg|400px|mini|rechts|RetroGameStation in Action]]


In der Endlösung sind nun noch folgende Probleme vorhanden, für welche schon theoretische Optimierungsansätze aufgestellt wurden:
<br /><br />
'''Problem:''' Ungenaue Schlagrichtung der weißen Kugel
<br />
<br />
'''Ursache:''' Aufgrund einer ungenauen Ermittlung der Queueposition und des Mittelpunktes der weißen Kugel ergibt sich eine geringe Abweichung im Stoßwinkel. Umso größer die Distanz zu einer Zielkugel ist, desto größer wird hier die Abweichung des Aufprallpunktes an der Zielkugel.
<br />
<br />
'''Lösungsansätze:'''
*Auswahl eines Videos mit höherer Auflösung, wodurch der Queue besser segmentiert werden könnte
*Auswahl eines kontrastreicheren Queues, welcher besser vom Hintergrund segmenetiert werden könnte
*Detektion des Queues mit anderen Methoden (z.B. Liniendetektion)
<br /><br />
'''Problem:''' Fehlerhafte Queueerkennung, wenn sich eine Zielkugel im Absuchradius der weißen Kugel befindet
<br />
<br />
Für die Messe wurden vier DIN-A3 "Plakate" mit Infomaterial erstellt. Sowie ein Projektplan und Fotos von Realisierung.
'''Ursache:''' Aufgrund dessen, dass im Absuchradius der weißen Kugel nach weißen Pixel gesucht wird, welche als Queue identifiziert werden, wird die Queueposition falsch ermittelt
<br />
<br />
'''Lösungsansätze:'''
*Der Queue wird nur als solcher erkannt, wenn der weiße Pixelbereich im Absuchradius einer gewissen Breite entspricht (die des Queues)
*Absuchradius um die weiße Kugel in einem gesonderten Binärbild durchsuchen, welches nur die Segmentierung des Queues beinhaltet. Hierzu müsste der Queue jedoch ein Alleinstellungsmerkmal in seiner Farbzusammensetzung besitzen, welches keiner anderen Kugel entspricht (ist in den Beispielvideos nicht der Fall)
<br /><br />
'''Problem:''' Langsame Berechnung
<br />
<br />
'''Ursache:''' Matlab ist nicht für Echtzeitanforderungen gedacht
<br />
<br />
'''Lösungsansätze:'''
*Umwandeln aller Funktionen in MEX Funktionen
*Implementierung der Algorithmen in anderen Programmiersprachen
<br />
<br />
<br />
 
<br />
== Potential ==
<br />
 
<br />
Die Software bietet noch einiges an Potential, welches aufgrund des gegebenen Zeitraums noch nicht implementiert wurde:
<br />
*Erkennung von Banden und Einbeziehen dieser bei der Schlagrichtung der weißen Kugel. So könnten auch potentielle Zielkugeln ermittelt werden, welche über die Bande angespielt werden sollen.
<br />
*Erkennung von Löchern und Ausgabe, ob die Zielkugel in eines dieser Löcher hineinfällt
<br />
*Automatisches Punktesystem und die davon Ausgehende Ermittlung von halben und ganzen Kugeln
<br />
*Echtzeitfähigkeit und Liveanwendung mit einem Embedded System (z.B. Raspberry Pi mit Webcam und einem Projektor, welcher die Hilflinien auf den Billardtisch projeziert)
<br />
*Geschwindigkeitsermittlung der weißen Kugel nach dem Stoß und damit die genaue Position der Zielkugel/n nach einem Treffer
<br />
*Berechnung der Aufprallpunkte weiterer Kugeln, welche von der Zielkugel getroffen werden (bis alle Kugeln zum Stillstand kommen)
<br />
<br />
----


== Video ==
== Video ==


<br />
<br />
Hier wird ein Link zum Youtube bereitstehen wenn das Video von Professoren hochgeladen wird.
[http://www.youtube.com/watch?v=Nycr1tyazxY Youtube Video Pool-Billard Assistenz]
<br />
<br />
<br />
<br />
----
----
= Fazit =
= Fazit =
[[Datei: DerPatriot Presse Foto.JPG|400px|mini|rechts|Foto in der Zeitung]]
 
Die von uns angestrebte Ziele wurden erreicht.
Die Bildverarbeitung sowie der anspruchsvolle Billardalgorithmus haben uns mit vielen nützlichen Erfahrungen bereichert und auch sehr viel Spaß gemacht.
Mit mehr zeitlichen Ressource wären in diesem Projekt weitere Ziele denkbar (siehe Potential). Das angeeignete Wissen lässt sich nun einfacher auf andere Problemstellungen reflektieren. Außerdem ist das große Potential der Bildverarbeitung nun deutlicher für uns geworden, jedoch auch die Schwierigkeiten, welche sich daraus ergeben.
<br /> <br />
Die Software befindet sich im folgenden SVN Ordner:
[https://svn.hshl.de/svn/DSB/trunk/User/SoSe2016/Christo_Tsibadze/Abgabe_Billard_Projekt/ SVN Link zur Abgabe]
<br />
<br />
Hier muss die folgende Maindatei ausgeführt werden: Pool_Billard_Assistenz_Main.m
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Die von uns angestrebte Ziele wurden erreicht!
Die Entwicklung der Spielkonsole hat sehr viel Spaß gemacht. Es wurde ein komplettes mechatronisches System entwickelt, Hardware und Software von Entwurf bis zur Realisierung und Tests.
Die kreative Idee mit der Tapete und die Funktionalität der Tapete in diesem Projekt war verblüffend. Ist die Konsole ausgeschaltet, sieht es aus wie ein schönes Designer-Möbelstück. Erst wenn die Konsole angeschaltet ist werden die LEDs bzw. die einzelnen Pixeln sichtbar. Durch die spezielle Tapetenoberflächenstruktur sehen die einzelnen runden Pixeln wie 3-dimensionale Kugeln aus was einen schönen Effekt erzeugt.
<br />
Eigenkritik: Das Isolierband an den Gamepad-Kabelenden könnte noch durch Steckergehäuse ersetzt werden.
<br />
Medien: Das Projekt ist bei der Messe auch bei der Presse sehr gut angekommen, so das wir auf der Titelseite der Lippstädter-Zeitung derPatriot gekommen sind. Wir wurden sogar wörtlich zittert.
[http://www.derpatriot.de/Spiele-fuer-Konsolenkinder-und-die-Mission-on-Mars-52c3275c-a7c4-41ee-91c9-9762fd7fd65d-ds]
<br />
Auch die Professoren waren begeistert und zufrieden.
[https://www.facebook.com/NeuigkeitenAusForschungUndLehre/photos/a.323245041114657.64680.212332285539267/760212820751208/?type=3&theater]


Ende gut, alles gut :)


<br />
 
<br />
 
<br />
 
<br />
<br />
<br />
----
----
→ zurück zum Hauptartikel: [[DSB SoSe2016|DSB SoSe2016]]
→ zurück zum Hauptartikel: [[DSB SoSe2016|DSB SoSe2016]]

Aktuelle Version vom 15. Juni 2016, 17:10 Uhr

Abbildung 1: Pool-Billard Assistenz

Autoren: Christo Tsibadze, Kevin Penner
Betreuer: Prof. Schneider

Aufgabenstellung

Realisierung einer Billard-Assistenz-Software, mit Hilfe von Matlab und dessen Bildverarbeitungs-Tools: Es soll ein "Billard-Assistenz-Software" entwickelt werden, welche aus einem Video (oder Live-Cam) Billardkugeln- und Queue -Positionen erkennt und anschließend mit einem Algorithmus die Abprallrichtung einer Zielkugel "vorhersagt" (berechnet). Vor dem Schlag soll anhand der Queue- und Kugelpositionen erkannt werden, in welche Richtung die weiße Kugel geschlagen wird. Falls die weiße Kugel eine andere Kugel auf dem Billardtisch trifft, soll im Voraus die Abprallrichtung dieser ermittelt werden. Die berechnete Richtungen soll auf dem Video eingeblendet werden. Für die Bildverarbeitung sowie Programmierung wurde die Matlabversion R2016a verwendet. Eine CAD-Software wurde während der Entwicklung des Algorithmus zur Überprüfung der Ergebnisse eingesetzt.


Erwartungen an die Projektlösung

  • Sammeln von weiteren Erfahrungen in der Bildverarbeitung mit MATLAB
  • Umsetzung der in der Vorlesung gewonnenen Erkenntnisse in die Praxis
  • Tieferer Einblick in die geometrische Physik
  • Lösen von Problemstellungen durch ingenieurmäßige Herangehensweisen
  • Erstellung eines benutzerfreundlichen Videos, welches die Software visualisiert


Plannung

Projektplan

Folgende Meilensteine wurden von uns bearbeiten:

  • Automatische Erkennung von Queue- und Kugelpositionen in einem Videoframe
  • sowie Unterscheidung der Kugeln in weiß- und Zielkugeln
  • ein Algorithmus implementieren, der die Schlagrichtung der weißen Kugel berechnet
  • Erkennung, ob die weiße Kugel nach dem Schlag eine andere Kugel trifft
  • falls die weiße Kugel eine andere Kugel trifft, soll die Abprallrichtung der getroffenen Zielkugel berechnet werden
  • Visualisierung von Hilfslinien im Video
  • Dokumentation

Die Aufgabe ist freiwillig. Zeitlich wurde für das Projekt im 6. Semester je ca. 3 Stunden pro Woche investiert. Zur Ideenfindung oder Besprechung der Problemlösungsstrategien wurden regelmäßige Wochen-Meetings gehalten.

Bildverarbeitung: Erkennung von Queue- und Kugelpositionen

Abbildung 2: Ablaufdiagramm zur Erkennung von Billardkugeln und Queue

In Abbildung 2 ist ein Ablaufplan zu sehen, welcher den Algorithmus zur Erkennung von Billardkugeln und dem Queue grob veranschaulicht. Im Folgenden wird auf jeden dieser Punkte eingegangen:

Frame einlesen und Zuschneiden des Bildes

Damit das Testen der Algorithmen stattfinden kann, wurden öffentliche Videos von Billardwettkämpfen genutzt. Diese können über die VideoReader Funktion eingelesen werden. Außerdem sind der Anfangsframe (Startframe) und die Schrittweite (Frameschritte) der zu analysierenden Frames einstellbar.

video=VideoReader('001.mp4');
nframes = video.NumberOfFrames;
Frameschritte = 1;
Startframe = 1;


Da die Videos überflüssige Bildbereiche besitzen, wird zunächst der Billardtisch extrahiert, indem ein konstant definierter Bildbereich ausgewählt wird (ROI). Das zu analysierende Bild besitzt nun also einen homogenen Hintergrund (Farbe des Billardtisches), wodurch das Erkennen bzw. Segmentieren der Objekte vereinfacht wird.

Binarisierung

Für die Erkennung der Objekte im Bild (Kugeln und Queue) wird das RGB-Bild in ein Binärbild umgewandelt. Da die Objekte jeweils einen unterschiedlichen Farbwert besitzen, muss für die Binarisierung jeder einzelne Farbkern mit einem Schwellwertverfahren umgewandelt werden. Zur Unterstützung wurde hier das Matlabtool Color Thresholder eingesetzt, welches aus einem vom Benutzer ausgewählten Bildbereiches automatische eine Funktion generiert. Dieser wird im Matlab Code ein RGB-Bild als Parameter übergeben, woraufhin ein Binärbild zurückgegeben wird.
Diese Vorgehensweise muss jedoch für jeden spezifischen Billardtisch durchgeführt werden, falls ein anderer Tisch mit einer anderen Farbe angewendet wird.
In Abbildung 3 ist die Ausgabe des Originalbildes (links) und des Binärbildes (rechts) zu sehen. An einigen Stellen treten jedoch einzelne Artefakte auf, welche im Nachhinein noch entfernt werden müssen.

Abbildung 3: Unbereinigtes Binärbild Billard


















Nachverarbeitung

Um später zu Verhindern das Artefakte als Objekte wahrgenommen werden, wird das Binärbild zusätzlich noch bereinigt. Matlab bietet hier eine große Anzahl an Funktionen und morphologischen Operationen an. Folgende zwei Nachverarbeitungsschritte werden durchgeführt:

  1. Schwarze Pixel auf weißen Hintergrund werden gelöscht (Funktion: imfill). Diese Funktion füllt die Löcher in Objekten, damit diese, homogene Flächen besitzen.
  2. Mithilfe der Funktionen imopen und bwareaopen werden kleine weiße Pixelregionen im Bild gelöscht

In Abbildung 4 (rechts) ist nun eine deutliche Veränderung im Gegensatz zu Abbildung 3 (rechts) zu erkennen. Dieses Binärbild kann nun als Grundlage für die Erkennung der Objekte herangezogen werden.

%Binärbild bereinigen
imClean = imfill(BW,'holes');
se = strel('disk',1);
imClean = imopen(imClean,se);
imClean = bwareaopen(imClean, 80);


Abbildung 4: Bereinigtes Binärbild Billard















Kugelerkennung

Für die Erkennung von Kreisen bietet Matlab die Funktion imfindcircles an, welche folgende Parameter übergeben bekommt:

  1. Bereinigtes Binärbild (imClean)
  2. Maximalen und minimalen Radius (in Pixeleinheiten) eines erkannten Kreises
  3. Suche nach hellen Kreisen auf einem dunklen Hintergrund
  4. Kreisformabweichung (wurde experimentell ermittelt)

Als Rückgabewert erhält man eine Liste von Bildkoordinaten der erkannten Kreise und deren Radien.

%Parameter für die Größe und "Genauigkeit" der Billardkugeln
RadiusMin = 6;
RadiusMax = 12;
Circle_Sensitivity = 0.9;
%Kreise im bereinigten Binärbild suchen
[Centers, Radii] = imfindcircles(imClean,[RadiusMin RadiusMax],'ObjectPolarity','bright','Sensitivity',Circle_Sensitivity);
AnzahlKreise = size(Centers);



Erkennung der weißen Kugel

Die Erkennung der weißen Kugel erfolgt über eine selbstgeschriebene Funktion:

function[WhiteCenter, WhiteRadius, WhiteIndex] = Detect_White_Sphere(Centers, Radii, Image)


Dieser werden die Bildkoordinaten und Radien aller erkannten Kreise übergeben, sowie das zugeschnittene RGB-Bild. Als Rückgabewerte erhält man die Koordinaten, den Radius und den Index der weißen Kugel aus dem übergebenen Kreiskoordinatenvektor.
In dieser Funktion werden alle erkannten Kreise in einer Schleife auf deren Helligkeit (vorher muss das RGB-Bild in ein Grauwertbild umwandelt werden) überprüft, indem der Mittelwert über alle Pixelwerte in einem Kreis gebildet wird und so der höchste Wert der weißen Kugel zugeordnet werden kann. Ob sich ein Pixel in einem Kreis befindet, kann mit folgender Formel berechnet werden:

Abbildung 5: Punkt im Kreis












Der Abscannbereich eines jeden Kreises wird wie in Abbildung 5 realisiert, indem ein Quadrat um den Kreis gebildet wird, in welchem anschließend nur die Pixel zum Mittelwert gezählt werden, welche mithilfe der Kreisgleichung zum Kreis zugeordnet werden können.
Der Matlabcode sieht wie folgt aus:

for j=1:AnzahlKreise
   %Boundingbox um Kugel ermitteln
   xmin = round(Centers(j,1)- Radii(j));
   xmax = round(Centers(j,1)+ Radii(j));
   ymin = round(Centers(j,2)- Radii(j));
   ymax = round(Centers(j,2)+ Radii(j));
   %Boundingbox "abscannen". Wenn Punkt im Kreis liegt, dann soll eine
   %Pixelsumme aller Helligkeitswerte gebildet werden. Aus diesen wird
   %dann der Mittelwert gebildet
   PxSum = 0;
   PxInd = 0;
   for m=ymin:ymax
      for n=xmin:xmax
         %PunktinKreis < 0 --> Koordinate liegt im Kreis
         %PunktinKreis = 0 --> Koordinate liegt auf Kreisbahn
         %PunktinKreis > 0 --> Koordinate liegt außerhalb des Kreises
         PunktinKreis = (n-Centers(j,1))^2 + (m-Centers(j,2))^2 - Radii(j)^2;
         if PunktinKreis <= 0 && n <= width && m <= height && n>0 && m>0
            PxSum = PxSum + double(GrayImage(m,n));
            PxInd = PxInd + 1;
         end
      end
   end
   PxMean(j) = PxSum/PxInd;
end


Queueerkennung

Nachdem die weiße Kugel erkannt ist, wird der Queue in der unmittelbaren Umgebung zur weißen Kugel gesucht. Hierzu wurde folgende Funktion implementiert:

[Queue] = Detect_Queue_01(WhiteCenter, DetectionRadius, imClean);


Abbildung 6: Queueerkennung

Der Funktion werden die Koordinaten der weißen Kugel, der Absuchradius zur Detektion des Queues und das bereinigte Binärbild übergeben. Als Rückgabewert wird die Position des Queues geliefert.
In der Funktion wird auf der Kreisbahn mit einem Absuchradius um die weiße Kugel nach weißen Pixelwerten gesucht (ob ein Pixel auf der Kreisbahn liegt, wird mit derselben Kreisgleichung, wie bei der Erkennung der weißen Kugel, überprüft). Die Koordinaten der Pixelwerte, die zu dem Queue zugeordnet werden, werden in einen Vektor geschrieben. Um den Mittelpunkt des Queues zu erhalten, wird folgende Berechnung im Anschluss an die Erfassung der Pixelwerte durchgeführt:








In der Testphase des Algorithmus zur Erkennung hat sich jedoch herausgestellt, dass aufgrund der ungenauen Binarisierung und damit verbundenen schlechten Erkennung des Queues, eine Abweichung zwischen wahrer mittleren Position und erkannter mittleren Position des Queues resultierte. Diese Abweichung konnte durch das Erfassen von mehreren Punkten auf dem Queue minimiert werden (siehe Abbildung 6). Die Funktion wird nun in einer Schleife ausgeführt, in welcher der Absuchradius stetig erhöht wird:

%Absuchradius um weiße Kugel zum detektieren des Queue
DetectionRadius = 15;
DetectionSteps = 5;
Iterationen = 10;
for j=1:Iterationen
   [Queue] = Detect_Queue_01(WhiteCenter, DetectionRadius + (j-1)*DetectionSteps, imClean);
end


Die detektierten Punkte werden in einen Vektor geschrieben. Für das Erhalten eines eindeutigen Punktes auf dem Queue, wird nun noch der Mittelwert aller x- und y-Koordinaten gebildet.


Eingeschränkte Durchführung des Algorithmus

Da die Ermittlung der Queueposition nur erfolgen muss, wenn sich die weiße Kugel im Stillstand befindet, wird vor der Ausführung der Funktion zur Queueerkennung die Positionsveränderung der weißen Kugel kontrolliert. Hierfür wird die Differenz der alten Position der weißen Kugel mit der neuen Position gebildet und mit einem geringem Schwellwert verglichen:

Position_of_rest = ((abs(WhiteCenter(1)-WhiteCenterOld(1))<5) && (abs(WhiteCenter(2)-WhiteCenterOld(2))<5)) 
if Position_of_rest
.
.
.
end


Billardprognose

Nach der Bildverarbeitung jedes Videoframes wird die Vorhersagefunktion "Billard_Prediction" mit folgenden Parametern und Rückgabewerten aufgerufen:

function [ RICHTUNG_Koordinaten, ZIEL_KUGEL, RICHTUNG_Koord_Weiss ] = Billard_Prediction( W,Z,Q,R )


  • W: X- und Y-Koordinate der weißen Kugel

Format:

[double x;double y]


  • Z: X- und Y-Koordinaten aller Zielkugeln in einer Matrix

Format:

[[double x1;double y1],[double x2;double y2],...,[double xn;double yn]];


  • Q: X- und Y-Koordinate der Queuespitze

Format:

[double x;double y]


  • R: Kugelradius (Annahme: alle Kugeln sind gleich groß)

Format:

[double r]


Als Rückgabe liefert die Funktion folgende Werte:

  • RICHTUNG_Koordinaten: ein Vektor mit der Länge von 50 Pixeln, dessen Startpunkt die anvisierte Zielkugel ist und dessen Richtung die vorhersagte Abprallrichtung der Zielkugel beschreibt.

Format:

[double x;double y]


  • ZIEL_KUGEL: Gibt die Koordinate der anvisierten Zielkugel zurück

Format:

[double x;double y]


  • RICHTUNG_Koord_Weiss: Gibt eine Hilfskoordinate zurück, um die "Anvisierlinie" der weißen Kugel zu visualisieren

Format:

[double x;double y]


Falls keine Zielkugel anvisiert wird, werden alle Rückgabewerte mit 0 initialisiert.

Vorbereitung

Da die Y-Achse der Bildkoordinaten bei der Videoverarbeitung von oben nach unten verläuft (positive Y-Werte) und im Vorhersagealgorithmus von unten nach oben (negative Y-Werte), werden alle Y-Koordinaten vor und nach der Berechnung umgekehrt.

%% Y-Achse Umkehren  
Z(2,:) = Z(2,:).*-1;
Q(2) = Q(2).*-1;
W(2) = W(2).*-1;


Sortierung nach Entfernung

Wenn in der Schlagrichtung der weißen Kugel mehrere Zielkugeln positioniert sind, ist es erforderlich zu unterscheiden, welche Kugel den kürzesten Abstand zur weißen Kugel besitzt bzw. welche Zielkugel zuerst getroffen wird.

%% Zielkugel mit kleinsten Abstand finden (und sortieren)
Anzahl_Zielkugeln = size(Z,2);
B(3,Anzahl_Zielkugeln)=zeros; %Neue Matrix vorbereiten
B(2,:,:)=Z(1,:); % in die zweite Zeile alle X-Koordinaten der Zielkugeln
B(3,:,:)=Z(2,:); % in die dritte Zeile alle Y-Koordinaten der Zielkugeln
for i = 1: Anzahl_Zielkugeln
    B(1,i,:) = norm(Z(:,i)-W); % in die erste Zeile --> Abstände aller Zielkugeln zur weißen Kugel 
end
B = sortrows(B')'; % Matrix transponieren, nach Abstände zur weißen Kugel ordnen und zurücktransponieren


Die Anzahl der Spalten ist gleich der erkannten bzw. übrig gebliebenen Zielkugeln. Für die Sortierung nach der Entfernung wird eine neue Matrix B mit drei Zeilen erzeugt. Die X- und Y-Koordinaten aus der Z-Matrix werden in die zweite und dritte Zeile der B-Matrix eingetragen (1. --> 2 und 2. --> 3.). In der ersten Zeile wird für jede Zielkugel die Entfernung zur weißen Kugel berechnet und eingetragen.

Die Entfernung einer Zielkugel zur weißen Kugel wird wie folgt ermittelt:

  • Vektorbestimmung anhand der Koordinaten von Ziel- und weißer Kugel
    [x1-x1;y1-y2]
    

  • Entfernung: Länge des Vektors bestimmten
    sqrt(x^2 + y^2)
    

Nachdem alle Entfernungen der Zielkugeln in die erste Zeile der B-Matrix eingetragen wurden, wird mithilfe der MATLAB-Funktion sortrows die Matrix aufsteigend (nach Entfernungen) sortiert.

Da die Funktion

sortrows

den Inhalt der ersten Matrizenspalte aufsteigend ordnet, jedoch die Entfernungen in unserem Fall in der ersten Zeile stehen, wird die Matrix vor und nach dem Sortieren transponiert. Beim Transponieren werden die Zeilen und Spalten vertauscht bzw. Zeilen in Spalten und Spalten in Zeilen umgewandelt.

Nach der Sortierung besteht die B-Matrix aus drei Zeilen:

  • 1. Zeile: Abstände zur weißen Kugel (aufsteigend geordnet)
  • 2. Zeile: X-Koordinaten der Zielkugeln
  • 3. Zeile: Y-Koordinaten der Zielkugeln

Die Spaltenanzahl entspricht der Anzahl der Zielkugeln.

Schlagrichtung weiße Kugel

Abbildung 7: Schlagrichtung

Um herauszufinden in welche Richtung die weiße Kugel geschlagen bzw. anvisiert wird, wird mithilfe der Koordinaten des Queues Q(x;y) und der weißen Kugel W(x;y) ein Schlagrichtungsvektor berechnet und anschließend normiert.

%% Richtung Direkt
Q_R=(W-Q);% Richtung QW Queue-Weiss
Q_R_Norm = Q_R./norm(Q_R); %Vektor normieren


Die Vektornormierung wird wie folgt berechnet: Der Vektor wird durch seine eigene Länge geteilt. Einfachheitshalber wird angenommen, dass mit der Queuespitze immer auf die Mitte der weißen Kugel gezielt wird, da das Projekt sonst im verfügbaren Zeitrahmen nicht machbar gewesen wäre.








Treffer oder kein Treffer

Abbildung 8: Phi_max fuer Treffer

Um herauszufinden, ob die weiße Kugel in der Schlagrichtung eine Zielkugel trifft, werden folgende Berechnungen durchgeführt:
Es wird ein imaginäres gleichschenkliges Dreieck aufgestellt mit folgenden gegebenen Seitenlängen (siehe Abbildung 8):

  • Länge der kleinen Seite: 2 x Kugelradius (bzw. 1 x Kugeldurchmesser)
  • Länge der langen Seite: Entfernung der weißen Kugel zur Zielkugel (direkte Linie: D_L)

Als Länge der kleinen Seite wurde der Kugeldurchmesser gewählt, da sich die Kugeln bei einem Kontakt berühren. Zu bestimmen ist der Winkel "Phi_max" (siehe Abbildung 8). Dieser Winkel ist zwischen der Schlagrichtung der weißen Kugel Q_R und der direkten Richtung D_L eingeschlossen und ist abhängig von der Entfernung der Zielkugel zur weißen Kugel. Die Zielkugel kann nur dann getroffen werden, wenn der aktuelle Winkel (Alpha) kleiner ist als Phi_max.

Abbildung 9: Phi_max fuer Treffer Zwischenrechnung

Für die Bestimmung von Phi_max von jeder Zielkugel wird zunächst nur die Hälfte des gleichschenkligen Dreiecks betrachtet --> rechtwinkliges Dreieck (siehe Abbildung 9)

  • D_L: Direkte Linie zwischen der Ziel- und weißen Kugel entspricht der Hypotenuse
    • Anfangsposition der D_L: Koordinaten der weißen Kugel
    • Endposition der D_L : Koordinaten der Zielkugel
    • Länge der D_L: Betrag des Vektors, welcher zwischen Ziel- und weißer Kugel aufgespannt wird
  • Länge der Gegenkathete: Kugelradius
  • Winkel zwischen Gegen- und Ankathete: 90°

Da die Hypotenuse, die Gegenkathete und ein Winkel bekannt sind, ist der gesuchte Winkel mithilfe des Sinus ermittelbar:
--> Phi_max = 2 * arcsin(Gegenkathete/Hypotenuse)

Phi_max=2*asind((R)/B(1,i,:)); %maximaler Winkel für Treffer


Anschließend wird der aktuelle Winkel "Alpha" zwischen den D_L- und Q_R Vektoren wie folgt berechnet:


D_L=(B(2:3,i,:)-W);% direkte Richtung, zwischen Ziel- und weißer Kugel
%% aktueller Winkel Schlagrichtung bzw. direkte Richtung
Alpha = acosd((dot(Q_R,D_L))/((norm(Q_R) * norm(D_L)))); % Winkel zwischen Q_R und D_L


Ist der Winkel Alpha kleiner als Phi_max --> Treffer, sonst kein Treffer.

Falls Treffer: Abprallrichtung

Mithilfe des Algorithmus wird zunächst geprüft, ob die Zielkugel mit dem kleinsten Abstand zur weißen Kugel in aktueller Queuerichtung getroffen wird. Falls dies nicht der Fall ist, wird die Zielkugel mit dem nächstkleineren Abstand geprüft, bis eine Zielkugel gefunden wird, die getroffen wird. Ist eine Zielkugel gefunden, die getroffen wird, wird die Abprallrichtung der Zielkugel berechnet.

Abbildung 10: Kosinus Satz


Gegeben ist ein allgemeines Dreieck mit:

  • D_L: die Direkte Linie zwischen der weißen und der Zielkugel
    • Länge, Richtung und Position
  • Q_R: die Schlagrichtung der weißen Kugel
    • Startpunkt (weiße Kugel), Richtung, keine Länge
  • Alpha: Winkel Alpha ist ermittelbar durch die zwei Vektoren D_L und Q_R
  • 2 x R: Richtung zwischen der Zielkugel und der weißen Kugel beim Aufprall (Richtung unbekannt, nur Länge bekannt)
    • Länge = 2 x Kugelradius

Gesucht: Der Winkel Beta, die Position des Überschneidungspunktes W' , sowie der Winkel im Punkt Z --> Gamma.

Da zwei Seiten des Dreiecks und der Winkel zwischen diesen Seiten bekannt ist, sind mithilfe des Kosinus die restlichen Parameter zu berechnen.
Siehe folgende Quelle: Berechnung eines beliebigen Dreiecks

    Beta=180-asind((B(1,j,:)/(2*R))*sind(Alpha)); %
    Gamma=180-Beta-Alpha;


Sind alle Winkel bekannt, ist es anhand des Winkels Gamma möglich, die Kugelabprallrichtung zu bestimmen. Denn genau um diesen Winkel muss der Vektor D_L rotiert werden, um den Kugelabprallrichtung zu erhalten.

Die Rotation einer Geraden bzw. eines Vektors ist durch Multiplikation mit einer Rotationsmatrix festzustellen.


Fallunterscheidungen

Abbildung 11: Fallunterscheidungen

Um die korrekte Abprallrichtung der Zielkugel zu berechnen, ist es notwendig verschiedene Fälle zu unterscheiden.
Die Zielkugel wird von:

  • links nach rechts
  • rechts nach links
  • oben nach unten
  • unten nach oben

geschlagen:

    %% Fallunterscheidung für Winkel Gamma, Anordnung W und Z
    if((W(1)<B(2,j,:))&&(W(2)>B(3,j,:)))
        Fall = 1; %Schlag von links oben
    end
    if((W(1)<B(2,j,:))&&(W(2)<B(3,j,:)))
        Fall = 2; %Schlag von links unten
    end
    if((W(1)>B(2,j,:))&&(W(2)>B(3,j,:)))
        Fall = 3; %Schlag von rechts oben
        Gamma=(180-Gamma);
    end
    if((W(1)>B(2,j,:))&&(W(2)<B(3,j,:)))
        Fall = 4; %Schlag von rechts unten
        Gamma=(180-Gamma);
    end


Außerdem muss überprüft werden, ob die Zielkugel auf der rechten oder linken Seite getroffen wird:

    %% Winkelberechnung für Orientierung im 2D-Raum
    Winkel_1 = acosd((dot(RefH,Q_R))/((norm(RefH) * norm(Q_R))));
    Winkel_2 = acosd((dot(RefH,D_L))/((norm(RefH) * norm(D_L))));
    if(Winkel_1 == Winkel_2)
            Gamma=0;
    else
        switch Fall
            case 1
                if(Winkel_1 > Winkel_2)
                    Gamma=-Gamma;
                end
            case 2
                if(Winkel_1 < Winkel_2)
                    Gamma=-Gamma;
                end
            case 3
                if(Winkel_1 < Winkel_2)
                    Gamma=-Gamma;
                end
            case 4
                if(Winkel_1 > Winkel_2)
                    Gamma=-Gamma;
                end
        end
    end


Je nach Kugelanordnung wird in der Rotationsmatrix der Winkel Gamma entweder als negativ, positiv oder 180°-Gamma angenommen.

Für die Ermittlung der richtigen Abprallrichtung sind mehrere Rotationen um 90° und Vektorumkehrungen (Multiplikation mit -1) notwendig. Um die Implementierungen bzw. das Debuggen zu vereinfachen, wurden selbst erstellte CAD-Skizzen und eine MATLAB-GUI herangezogen. Somit wurden alle möglichen Fälle va­li­die­rt und die Software dementsprechend angepasst.

    %% Rotation von D_L auf Abprallrichtung
    RICHTUNG_NEU=rot90(W-B(2:3,j,:))*[cosd(Gama) -sind(Gama); sind(Gama) cosd(Gama)];
    RICHTUNG_NEU=rot90(rot90(rot90((RICHTUNG_NEU)))).*-1;
    if(Fall==4 || Fall==3)
        RICHTUNG_NEU=RICHTUNG_NEU.*-1;1
    end;


Rückgabewerte

if(Q(1)~= 0 && Q(2) ~= 0) % Rückgabewerte nur wenn Queueposition bekannt ist
        RICHTUNG_NEU = RICHTUNG_NEU./norm(RICHTUNG_NEU); %normieren
        RICHTUNG_NEU = RICHTUNG_NEU.*50; %feste Vektorlänge von 50 Pixel
        RICHTUNG_Koordinaten(1) = B(2,j,:)+RICHTUNG_NEU(1); % X-Komponente der Rückgabevektor für Abprallrichtung mit Startpunkt (Zielkugel)
        RICHTUNG_Koordinaten(2) =  B(3,j,:).*-1+RICHTUNG_NEU(2).*-1; % Y-Komponente der Rückgabevektor für Abprallrichtung mit Startpunkt (Zielkugel),
        %Richtung angepasst                            
        ZIEL_KUGEL = [B(2,j,:) ; B(3,j,:).*-1]; % Koordinaten der aktuell anvisierten Zielkugel, Y-Koodrinate angepasst
        RICHTUNG_Koord_Weiss = B(1,j,:).*Q_R_Norm+W; % Hilfslinie Startpunkt Weiße Linie X-Komponente
        RICHTUNG_Koord_Weiss(2) = RICHTUNG_Koord_Weiss(2).*(-1);% Hilfslinie Startpunkt Weiße Linie Y-Komponente angepasst
end


Die Rückgabewerte werden so angepasst, dass die Y-Komponenten wieder den Bildkoordinaten entsprechen.

MATLAB GUI

Für Testzwecke wurde eine MATLAB GUI erstellt, womit jedes mögliche Szenario (Kugelanordnung und Schlagrichtung) simuliert wurde. Mithilfe dieser wurde die Software optimiert und von diversen kleineren Bugs befreit.

Abbildung 12: GUI Billard



Die Positionen der Kugeln können entweder per Schieberegler oder manuell eingegeben werden. Durch das Drücken auf die "GO"-Taste, berechnet die Software die Abprallrichtung und gibt diese Linie aus.

Ausgabe

Als Ausgabe erfolgt die Darstellung des zugeschnittenen Originalbildes (siehe Abbildung 12) inklusive folgender Geraden, Punkte und Kreise:

  • Gerade zwischen Queueposition und des Mittelpunktes der weißen Linie (rote Linie)
  • Gerade zwischen Mittelpunkt der weißen Kugel und Aufprallpunkt an der Zielkugel (grüne Linie)
  • Gerade zwischen Mittelpunkt der Zielkugel und Richtung der Zielkugel (gelbe Linie)
  • Mittelpunkt der weißen Kugel (blauer Punkt)
  • Umrandung aller Kugeln auf dem Billardtisch (rote Kreise)
Abbildung 13: Ausgabe


Resultat

Probleme

In der Endlösung sind nun noch folgende Probleme vorhanden, für welche schon theoretische Optimierungsansätze aufgestellt wurden:

Problem: Ungenaue Schlagrichtung der weißen Kugel
Ursache: Aufgrund einer ungenauen Ermittlung der Queueposition und des Mittelpunktes der weißen Kugel ergibt sich eine geringe Abweichung im Stoßwinkel. Umso größer die Distanz zu einer Zielkugel ist, desto größer wird hier die Abweichung des Aufprallpunktes an der Zielkugel.
Lösungsansätze:

  • Auswahl eines Videos mit höherer Auflösung, wodurch der Queue besser segmentiert werden könnte
  • Auswahl eines kontrastreicheren Queues, welcher besser vom Hintergrund segmenetiert werden könnte
  • Detektion des Queues mit anderen Methoden (z.B. Liniendetektion)



Problem: Fehlerhafte Queueerkennung, wenn sich eine Zielkugel im Absuchradius der weißen Kugel befindet
Ursache: Aufgrund dessen, dass im Absuchradius der weißen Kugel nach weißen Pixel gesucht wird, welche als Queue identifiziert werden, wird die Queueposition falsch ermittelt
Lösungsansätze:

  • Der Queue wird nur als solcher erkannt, wenn der weiße Pixelbereich im Absuchradius einer gewissen Breite entspricht (die des Queues)
  • Absuchradius um die weiße Kugel in einem gesonderten Binärbild durchsuchen, welches nur die Segmentierung des Queues beinhaltet. Hierzu müsste der Queue jedoch ein Alleinstellungsmerkmal in seiner Farbzusammensetzung besitzen, welches keiner anderen Kugel entspricht (ist in den Beispielvideos nicht der Fall)



Problem: Langsame Berechnung
Ursache: Matlab ist nicht für Echtzeitanforderungen gedacht
Lösungsansätze:

  • Umwandeln aller Funktionen in MEX Funktionen
  • Implementierung der Algorithmen in anderen Programmiersprachen


Potential

Die Software bietet noch einiges an Potential, welches aufgrund des gegebenen Zeitraums noch nicht implementiert wurde:

  • Erkennung von Banden und Einbeziehen dieser bei der Schlagrichtung der weißen Kugel. So könnten auch potentielle Zielkugeln ermittelt werden, welche über die Bande angespielt werden sollen.
  • Erkennung von Löchern und Ausgabe, ob die Zielkugel in eines dieser Löcher hineinfällt
  • Automatisches Punktesystem und die davon Ausgehende Ermittlung von halben und ganzen Kugeln
  • Echtzeitfähigkeit und Liveanwendung mit einem Embedded System (z.B. Raspberry Pi mit Webcam und einem Projektor, welcher die Hilflinien auf den Billardtisch projeziert)
  • Geschwindigkeitsermittlung der weißen Kugel nach dem Stoß und damit die genaue Position der Zielkugel/n nach einem Treffer
  • Berechnung der Aufprallpunkte weiterer Kugeln, welche von der Zielkugel getroffen werden (bis alle Kugeln zum Stillstand kommen)

Video


Youtube Video Pool-Billard Assistenz


Fazit

Die von uns angestrebte Ziele wurden erreicht. Die Bildverarbeitung sowie der anspruchsvolle Billardalgorithmus haben uns mit vielen nützlichen Erfahrungen bereichert und auch sehr viel Spaß gemacht. Mit mehr zeitlichen Ressource wären in diesem Projekt weitere Ziele denkbar (siehe Potential). Das angeeignete Wissen lässt sich nun einfacher auf andere Problemstellungen reflektieren. Außerdem ist das große Potential der Bildverarbeitung nun deutlicher für uns geworden, jedoch auch die Schwierigkeiten, welche sich daraus ergeben.

Die Software befindet sich im folgenden SVN Ordner: SVN Link zur Abgabe
Hier muss die folgende Maindatei ausgeführt werden: Pool_Billard_Assistenz_Main.m




→ zurück zum Hauptartikel: DSB SoSe2016