Projekt 27: Carolo Cup - Aufgabe: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
|||
(67 dazwischenliegende Versionen von 4 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
Aufgabenstellung: ''Objekt- und Spurerkennung mit Asus XTION'' | [[Kategorie:Projekte]] | ||
[[Datei:Carolo Debug.png|600px|thumb|right|Debug Anzeige zur Auswertung]]Aufgabenstellung: ''Objekt- und Spurerkennung mit Asus XTION'' | |||
Das Projekt steht im Zusammenhang mit der Aufgabenstellung des [http://carolo-cup.de Carolo Cups] und befasst sich mit dem Teil der Objekt- und Spurerkennung. Das Ziel des Projektes ist die Erstellung eines Modules, welches rechteckige Objekte vor dem Fahrzeug erkennen kann. Gleichfalls soll das Modul in der Lage sein die Spur erkennen zu können. | Das Projekt steht im Zusammenhang mit der Aufgabenstellung des [http://carolo-cup.de Carolo Cups] und befasst sich mit dem Teil der Objekt- und Spurerkennung. Das Ziel des Projektes ist die Erstellung eines Modules, welches rechteckige Objekte vor dem Fahrzeug erkennen kann. Gleichfalls soll das Modul in der Lage sein die Spur erkennen zu können. | ||
__TOC__ | |||
<br/> | |||
== Anforderungen == | |||
Das in diesem Projekt entwickelte Modul gliedert sich in eine Reihe anderer Module, welche Teil eines autonom fahrenden Fahrzeuges sind. Das in diese Projekt zu erstellende Modul hatte folgende Anforderungen zu erfüllen: | |||
;Eingang:Das Modul erhält einen Stream von der Asus XTION, durch welchen es Kameradaten erhalten kann. | |||
;Ausgang: Als Rückgabe soll das Modul gefilterte Informationen über Objekte im Spurbereich und die Spur selbst liefern. | |||
Nicht Teil dieses Projektes war die Prozessverwaltung zur Erfüllung der Aufgaben des Carolo Cups. Daraus resultiert auch die Tatsache, dass dieses Modul nur mit der beschriebenen Hardware zum Laufen gebracht werden kann. | |||
== Implementierung - Hardware == | == Implementierung - Hardware == | ||
Zeile 11: | Zeile 17: | ||
Die Hardware besteht aus folgenden Teilen: | Die Hardware besteht aus folgenden Teilen: | ||
[[Datei:Asus XTION.jpg|100px|thumb| | [[Datei:Asus XTION.jpg|100px|thumb|right|Asus XTION Kamera]] | ||
Die [http://www.asus.com/Multimedia/Xtion_PRO_LIVE/ Asus XTION Kamera] besteht aus zwei Sensoren, sowie einer USB-Schnittstelle<ref>[http://www.asus.com/Multimedia/Xtion_PRO_LIVE/#specifications] Asus XTION Spezifikationen (07.01.2014)</ref>: | |||
*; RGB-Kamera:Die RGB-Kamera hat eine Auflösung von maximal 640x480 Pixel. Die RGB-Daten werden hierbei mit 24 Bits pro Pixel gespeichert und als eindimensionales Array mit der Länge Höhe*Breite ausgegeben. | |||
*; Tiefeninformationssensor:Der Tiefeninformationssensor besitzt die gleiche Auflösung und hat einen Messbereich zwischen 0.8m und 3.5m. Die Werte werden ebenfalls als eindimensionales Array mit der Länge Höhe*Breite ausgegeben. Hierbei werden die Werte als 2-Byte-Format gespeichert. | |||
Ein [http://http://cubieboard.org/ CubieBoard] dient als zentrale Verarbeitungseinheit. Auf dem CubieBoard wurde die Bibliothek OpenNI2 installiert <ref>[http://ariandy1.wordpress.com/2013/02/27/getting-raspberry-pi-openni-and-asus-xtion-pro-live-to-work/] How to: Installieren der OpenNI-Bibliothek auf RaspberryPi (14.01.2014)</ref>.<br/><br/> | |||
== Implementierung - Software == | |||
Die Implementierung der Software<ref>[http://193.175.248.56/wiki/images/1/17/Struktur_Carolo_Cup.pdf] Logischer Aufbau der Hauptfunktion | Logischer Aufbau der Spurerkennung</ref> stützt sich auf eine sequenzielle Abarbeitung der Aufgaben. Dies ist durch die Architektur der Hardware bedingt. Das Ansprechen und Abfragen der Kameradaten wird durch die Einbindung der OpenNI2-Bibliothek<ref>[http://openni.org] OpenNI-Bibliothek (07.01.2014)</ref> realisiert. | |||
Grundlegend folgt die Software den Sequenzen ''Daten abfragen'' -> ''Daten aufarbeiten'' -> ''Auswerten'' -> ''Ausgeben''. Hierbei wurde ein modularer Aufbau des Sourcecodes verwendet, dies diente der Transparenz und Orienterung im Code. | |||
=== RGB-Kamera abfragen === | |||
:Die Abfrage der Daten wird über die OpenNI-Bibliothek abgearbeitet. Grundlage ist das Beispielprogramm ''MultiStreamRead''<ref>[https://github.com/OpenNI/OpenNI2/blob/master/Samples/MultipleStreamRead/main.cpp] OpenNI2::MultiStreamRead Beispielprogramm (07.01.2014)</ref>. Aus diesem Beispiel wurde folgender Code zur Abfrage der RGB-Kamera genutzt:<br/> | |||
<code> | |||
//Initialisierung der Bibliothek OpenNI | |||
Status rc = OpenNI::initialize(); | |||
//Aufbau des Datenstreams | |||
Device device; | |||
rc = device.open(ANY_DEVICE); | |||
VideoStream RGB; | |||
rc = RGB.create(device, SENSOR_RGB); | |||
rc = RGB.start(); | |||
VideoFrameRef frame; | |||
int changedStreamDummy; | |||
VideoStream* pStream = &RGB; | |||
rc = OpenNI::waitForAnyStream(&pStream, 1, &changedStreamDummy, SAMPLE_READ_WAIT_TIMEOUT); | |||
//Auslesen eines Frames | |||
rc = RGB.readFrame(&frame); | |||
RGB888Pixel* pRGB = (RGB888Pixel*)frame.getData(); | |||
//Zerstören des Datenstreams | |||
depth.stop(); | |||
depth.destroy(); | |||
device.close(); | |||
OpenNI::shutdown();</code> | |||
:Wie aus dem Code ersichtlich, wird zuerst die Bibliothek OpenNI initialisiert. Nach der erfolgreichen Initialisierung kann ein Datenstream aufgebaut werden. Dieser Stream stellt die Verbindung zwischen dem Zielsystem und der Asus XTION. Durch diese Verbindung können einzelne Frames ausgelesen werden, welche dann in einem OpenNI-spezifischen Datentypen gespeichert werden. | |||
=== Tiefeninformationen abfragen === | |||
:Gleich zur Abfrage der RGB-Kamera lässt sich auch eine Abfrage der Tiefeninformationen gestalten. Hierbei werden nur wenige Änderungen vollzogen, sodass folgender Code entsteht: | |||
<code> | |||
//Initialisierung der Bibliothek OpenNI | |||
Status rc = OpenNI::initialize(); | |||
//Aufbau des Datenstreams | |||
Device device; | |||
rc = device.open(ANY_DEVICE); | |||
VideoStream depth; | |||
rc = depth.create(device, SENSOR_DEPTH); | |||
rc = depth.start(); | |||
VideoFrameRef frame; | |||
int changedStreamDummy; | |||
VideoStream* pStream = &depth; | |||
rc = OpenNI::waitForAnyStream(&pStream, 1, &changedStreamDummy, SAMPLE_READ_WAIT_TIMEOUT); | |||
//Auslesen eines Frames | |||
rc = depth.readFrame(&frame); | |||
DepthPixel* pDepth = (DepthPixel*)frame.getData(); | |||
//Zerstören des Datenstreams | |||
depth.stop(); | |||
depth.destroy(); | |||
device.close(); | |||
OpenNI::shutdown();</code> | |||
:Die Abfrage der Tiefeninformation unterscheidet sich von der Abfrage der RGB-Kamera nur durch die Auswahl des Sensors ''SENSOR_DEPTH'' und die Wahl des Datentypens, in dem gespeichert wird. | |||
=== RGB zu Grayscale === | |||
:Nach dem Auslesen der RGB-Kamera müssen die Daten in eine binäre Form gebracht werden, um durch einen vereinfachten Canny-Algorithmus<ref> [https://campusapp02.hshl.de/Mathematik/2012/Optimierung_-_Statische,_dynamische,_stochastische_Verfahren_fuer_die_Anwendung.pdf] Papageorgiou, Leibold, Buss: Optimierung - Statistische, dynamische, stochastische Verfahren für die Anwendung, Springer, ISBN 978-3-540-34012-6 (S.441ff.) -> eBook-Bibliothek</ref> die Kanten extrahieren zu können. Um eine dynamische Verarbeitung zu ermöglichen, werden folgende Prozessschritte vollzogen: ''RGB'' -> ''Grayscale'' -> ''Binär''. Die grundlegenden Schritte für die Umwandlung eines RGB-Bildes zu einem Grayscale-Bild lässt sich durch folgenden Pseudo-Code beschreiben: | |||
<code> | |||
for (gesamtes Bild) | |||
Grau = (Faktor)*R+(Faktor)*G+(Faktor)*B | |||
Mittelwert aufaddieren | |||
end | |||
Mittelwert bilden | |||
</code> | |||
:Durch die Bildung des Mittelwertes kann nun die Umwandlung in ein binäres Bild erfolgen. Hierzu wird die generelle Helligkeit des Bildes bewertet und anhand dieses Wertes ein Threshold ermittelt, welches entscheidet, ob ein Bit gesetzt wird oder nicht. Dies lässt sich durch folgenden Pseudo-Code beschreiben: | |||
<code> | |||
Threshold ermitteln | |||
for (gesamtes Bild) | |||
if (Pixelwert < Threshold) | |||
Pixel setzen | |||
end | |||
end | |||
</code> | |||
=== Spur extrahieren === | |||
:Durch die Ermittlung eines binären Bildes kann eine Kantendetektion erfolgen. Dies wird durch einen vereinfachten Canny-Algorithmus erzielt. An dieser Stelle wurde auf einen kompletten Canny verzichten, da dies die Leistung des RaspberryPi´s übersteigen würde. Der vereinfachte Canny bearbeitet das binäre Bild zeilenweise und sucht nach gesetzten Bits. Anhand der gefunden Bereiche können Linien durch mathematische Modelle gefunden werden. | |||
<code> | |||
for (Bildbereiche) | |||
Suche nach gesetzten Pixeln | |||
Zusammenfügen der Pixel zu Bereichen | |||
end | |||
Abgleich der Bereiche mit math.Modell | |||
Ausgabe der Linien nach Abgleich | |||
</code> | |||
:Die Ausgabe der Linien erfolgt durch eine Matrix, welche die einzelnen Pixel der Linie pro Zeile beinhaltet. Anhand dieser Matrix kann später durch weitere Prozessschritte eine Linienführung verwirklicht werden. | |||
=== Objekte extrahieren === | |||
:Die Informationen, welche der Tiefensensor ausgibt, müssen in Objekte transformiert werden, um aus diesen Informationen zu erkennen, wann Hindernissen ausgewichen werden muss. Um eine Optimierung des Algorithmus zu ermöglichen, werden sämtliche Bildbereiche ''ausgegraut'', welche nicht interessant sind. Das Interesse an einzelnen Pixeln wurde im Vorweg anhand von Entfernung und Plausibilitäten definiert. Die gefunden interessanten Pixel werden nun anhand ihrer Nachbarpixel bewertet. Aus den Informationen über die Pixel können nun durch mathematische Modelle die Objekte extrahiert werden. | |||
=== Spur und Objekte ausgeben === | |||
:Die aus den vorhergehenden Modulen extrahierten Objekte und Spuren werden durch eine definierte Schnittstelle an weitere Module übergeben. Die übergebenen Informationen werden anhand von Arrays folgender Form ausgegeben: | |||
<code> | |||
Objekte: int [n][7] //n definiert die Anzahl der Objekte | |||
[n][0] | [n][1] | [n][2] | [n][3] | [n][4] | [n][5] | [n][6] | |||
x-Pos Tiefe x-Pos Tiefe x-Pos Tiefe y-Pos | |||
links links Mitte Mitte rechts rechts | |||
Spur: int [5][2*x] //x wird beim Kompilieren definiert und stellt | |||
die mögliche Anzahl an Punkten dar, wo eine | |||
Linie gefunden werden kann | |||
[1][1] | [1][2] | ... | [1][n] | [1][n+1] | [1][n+2] | [1][n+3] | |||
x-Pos y-Pos x-Pos y-Pos -1 -1 | |||
//n=2*(Anzahl gefundenen Linie): als Delimiter wird der Wert -1 | |||
eingetragen, um das Ende der Linie zu kennzeichnen | |||
</code> | |||
== Realisierung == | |||
Die ausführbaren Dateien inklusive der benötigten Kernel-Module sind hier<ref>[http://193.175.248.56/wiki/images/b/b6/Executable_carolo.zip] Ausführbare Projekt-Dateien (24.01.2014)</ref> verfügbar. Dabei ist zu beachten, dass das Programm für die ARM-Architektur kompiliert wurde. | |||
== Hilfestellung zur Projektaufnahme == | |||
=== Einbindung der OpenNI === | |||
In diesem Projekt wurde die Kamera ASUS XTION PRO durch die OpenSource-Bibliothek OpenNI angesprochen. Die an dieser Stelle beschriebene Anleitung basiert auf der Einbindung der OpenNI für ein System aus RaspberryPi, OpenNI2 und der AUSU XTION PRO<ref>[http://ariandy1.wordpress.com/2013/02/27/getting-raspberry-pi-openni-and-asus-xtion-pro-live-to-work/] How to: Installieren der OpenNI-Bibliothek auf RaspberryPi (14.01.2014)</ref>. Es wird für die erfolgreiche Durchführung der Anleitung auf Vorkenntnisse im Umgang mit Linux-Systemen ausgegangen. | |||
;1. Installieren der abhängigen Programme auf dem RaspberryPI:<code>sudo apt-get install git g++ python libusb-1.0-0-dev freeglut3-dev doxygen graphviz</code><br/> Hierbei ist zu beachten, dass die benötigten Dateien einen Umfang von ca. 900MB darstellen. Es ist vorher sicherzustellen, dass dieser Speicherplatz entsprechend frei ist. | |||
;2. Runterladen der Bibliothek:<code>git clone https://github.com/OpenNI/OpenNI2</code> | |||
;3. Adaptieren der Bibliothek für ARM-Architektur: Öffnen der Datei ''OpenNI2/Redist/Redist.py'' und ändern der Zeile 534:<br/> <code>compilation_cmd = "make -j" + calc_jobs_number() + " CFG=" + configuration + " PLATFORM=" + platform + " > " + outfile + " 2>&1"</code> <br/>Diese Zeile sollte kopiert und auskommentiert werden, da sie durch folgenden Code zu ersetzen ist: <br/><code>#compilation_cmd = "make -j" + calc_jobs_number() + " CFG=" + configuration + " PLATFORM=" + platform + " > " + outfile + " 2>&1"</code><br/> | |||
:<code>compilation_cmd = "make -j1" + " CFG=" + configuration + " PLATFORM=" + platform + " > " + outfile + " 2>&1"</code><br/> | |||
:Dieser Aschritt kann in Abhängigkeit der Prozessorleistung bis zu 45 Minuten dauern. | |||
;4. Erzeugen eines Builds: <code> cd OpenNI2/</code><br/> | |||
:<code>PLATFORM=Arm make</code> | |||
;5. Kopieren und installieren der kompilierten Dateien: <code>cd Final/</code> | |||
:<code>cp OpenNI-Linux-Arm-2.1.0.tar.bz2 /usr/local/src</code> | |||
:<code>cd /usr/local/src/</code> | |||
:<code>tar -xjvf OpenNI-Linux-Arm-2.1.0.tar.bz2</code> | |||
:<code>cd OpenNI-2.1.0-arm/</code> | |||
:<code>sudo ./install.sh</code> | |||
;6. Kontrolle: Bei angeschlossener Kamera sollte der Befehl <code>lsusb -vv</code> ausgeführt werden. Mit diesem Befehl werden alle angeschlossenen USB-Geräte identifiziert und aufgelistet. In dieser Auflistung sollte ein Gerät mit der ID <code>1d27:0600</code> erscheinen. | |||
;7. Hinweise: Es ist darauf zu achten, dass die Kamera über eine externe Quelle mit Strom versorgt wird, da diese Kamera nur mit PoweredUSB-Ports läuft. | |||
=== Kompilieren der Projektdateien === | |||
Die hier verfügbaren Quell-Dateien<ref>[http://193.175.248.56/wiki/images/3/30/Source_carolo.zip] Quelldateien des Projektes</ref> können mittels des Programms ''as'' in Maschinencode übersetzt werden und anschließend mittels Linker ''ld'' zu einer ausführbaren Datei zusammengefügt werden<ref>[http://www.gnu.org/software/binutils/] GNU Binutils (25.01.2014)</ref>. Dabei ist wiederum zu beachten, dass sämtliche Anweisungen auf der Ziel-Architektur (ARM) ausgeführt werden müssen oder ein Cross-Compiler verwendet werden muss. | |||
== Lessons learned == | |||
== | Da dieses Projekt einen Teil der Projektarbeit des Carolo Cup Teams LCP beinhaltete, war der reale Arbeitsaufwand zu Beginn des Semesters schwer abschätzbar. Es stellte sich heraus, dass das Projekt einen weit größeren Arbeitsaufwand darstellt als von den Projektteilnehmern angenommen wurde. Daraus lässt sich für folgende Projekte ableiten, dass der geschätzte Arbeitsaufwand eines Projektes mit einem Faktor versehen werden sollte. Dieser Faktor dient dem Projektteam dann als Puffer, falls es während des Projektes zu größerem Arbeitsaufwand (durch unvorgesehene Komplexität oder anderen Komplikationen) kommt.<br/> | ||
Neben der Erfahrung zum Abschätzen des Arbeitsaufwandes lehrte dieses Projekt, was es für einzelne Personen bedeutet in einem Projektteam zu arbeiten. Gerade die Kommunikation mit dem übergeordneten Projekt war für den Erfolg dieses Teilprojektes von enormer Bedeutung.<br/> | |||
Als größten Lernerfolg kann für dieses Projekt die Erweiterung des persönlichen Horizonts im Bereich Informatik gewertet werden. Hierbei wurde sich auf die Vorlesungen aus dem vorhergehenden Semestern gestützt. Das bereits aufgebaute Wissen konnte nun in einer realen Umgebung getestet, verfestigt und erweitert werden. Besonders die Erfahrung in der Programmierung auf einem Embedded Systems wird sich für den weiteren Verlauf des Studiums sicherlich als positiv darstellen. Des Weiteren wurde sich mit einer Linux-Distribution beschäftigt, welche einen den Umgang mit einem Betriebssystem von Grund an lehrte. | |||
== Anforderungen zur Aufnahme des Projektes == | |||
Durch die Komplexität des Projektes sollte das Projektteam folgende Vorkenntnisse erfüllen: | |||
;1. Sprachen:In diesem Projekt wurde mit folgenden Sprachen gearbeitet: <br/>C#, Assembler, GNU make <br/> Der sichere Umgang mit Programmierlogiken und -techniken wird ebenfalls als zwingend notwendig angesehen. | |||
;2. Betriebssystem:Für den Umgang sollten grundlegende Vorkenntnisse mit den Betriebssystemen von Windows<ref>[http://msdn.microsoft.com/de-de/library/ms123401.aspx] Microsoft Developer Network: Hilfestellungen zum Programmieren unter Windows</ref> und Lubuntu<ref>[http://docs.cubieboard.org/tutorials/a10-cubieboard_lubuntu_server_releases] Download der Lubuntu-Distribution (25.01.2014)</ref> (basierend auf Ubuntu) vorliegen. | |||
Weiterhin wird eine Kontaktaufnahme mit vorherigen Gruppen empfohlen. Dies erleichtert den Einstieg in das Thema und hilft für einen erfolgreichen Projektabschluss. | |||
== Zusammenfassung == | |||
Zusammenfassend kann dieses Projekt als erfolgreich gewertet werden. Die Aufgabenstellung wurde entsprechend der technischen Anforderung vollkommen erfüllt. Die Spur- und Objekterkennung liefert die angeforderten Werte, welche es jedoch durch nachgehängte Prozesse zu optimieren gilt. Auf diese Prozesse wurde in diesem Projektteil jedoch nicht eingegangen. Lediglich die formale Aufbereitung des Projektes für weitere Generationen bedarf einer Optimierung. Hierbei hat sich herausgestellt, dass es eine weit größeren Anteil des Projektaufwandes ausmacht, die Daten entsprechend aufzubereiten und festzuhalten, als vom Projektteam angenommen wurde. <br/> | |||
Da dieses Projekt eine weit größere Komplexität darstellt, als es anfangs scheint, wird an dieser Stelle auch explizit darauf hingewiesen, dass es zu einer Kontaktaufnahme zum vorherigen Projektteam kommen sollte, wenn man das Projekt aufnimmt. Dieser Schritt erleichtert den Einstieg in die Logik des Projektes und dient gleichzeitig der Sicherstellung des Erfolges des Projektes. <br/> | |||
== Projekthistorie == | |||
Dies Auflistung zeigt sämtliche Personen, welche an diesem Projekt gearbeitet haben: | |||
{| class="wikitable" width="100%" | |||
|- | |||
!width="15%"| Jahr ||colspan="2"| Gruppenmitglieder ||width="15%"| Hinweise | |||
|- | |||
|style="text-align:center"| Wintersemester 2013/14 ||style="text-align:center"| [http://193.175.248.56/wiki/index.php/Benutzer:Jan_Kifmann Jan Kifmann] ||style="text-align:center"| [http://193.175.248.56/wiki/index.php/Benutzer:Hauke_Ludwig Hauke Ludwig] ||style="text-align:center"| ([https://www.facebook.com/LippeCoastPerformance Team LCP]) | |||
|} | |||
== Einzelnachweise == | == Einzelnachweise == | ||
<references /> | <references /> |
Aktuelle Version vom 22. Dezember 2014, 11:48 Uhr
Aufgabenstellung: Objekt- und Spurerkennung mit Asus XTION
Das Projekt steht im Zusammenhang mit der Aufgabenstellung des Carolo Cups und befasst sich mit dem Teil der Objekt- und Spurerkennung. Das Ziel des Projektes ist die Erstellung eines Modules, welches rechteckige Objekte vor dem Fahrzeug erkennen kann. Gleichfalls soll das Modul in der Lage sein die Spur erkennen zu können.
Anforderungen
Das in diesem Projekt entwickelte Modul gliedert sich in eine Reihe anderer Module, welche Teil eines autonom fahrenden Fahrzeuges sind. Das in diese Projekt zu erstellende Modul hatte folgende Anforderungen zu erfüllen:
- Eingang
- Das Modul erhält einen Stream von der Asus XTION, durch welchen es Kameradaten erhalten kann.
- Ausgang
- Als Rückgabe soll das Modul gefilterte Informationen über Objekte im Spurbereich und die Spur selbst liefern.
Nicht Teil dieses Projektes war die Prozessverwaltung zur Erfüllung der Aufgaben des Carolo Cups. Daraus resultiert auch die Tatsache, dass dieses Modul nur mit der beschriebenen Hardware zum Laufen gebracht werden kann.
Implementierung - Hardware
Die Hardware besteht aus folgenden Teilen:
Die Asus XTION Kamera besteht aus zwei Sensoren, sowie einer USB-Schnittstelle[1]:
- RGB-Kamera
- Die RGB-Kamera hat eine Auflösung von maximal 640x480 Pixel. Die RGB-Daten werden hierbei mit 24 Bits pro Pixel gespeichert und als eindimensionales Array mit der Länge Höhe*Breite ausgegeben.
- Tiefeninformationssensor
- Der Tiefeninformationssensor besitzt die gleiche Auflösung und hat einen Messbereich zwischen 0.8m und 3.5m. Die Werte werden ebenfalls als eindimensionales Array mit der Länge Höhe*Breite ausgegeben. Hierbei werden die Werte als 2-Byte-Format gespeichert.
Ein CubieBoard dient als zentrale Verarbeitungseinheit. Auf dem CubieBoard wurde die Bibliothek OpenNI2 installiert [2].
Implementierung - Software
Die Implementierung der Software[3] stützt sich auf eine sequenzielle Abarbeitung der Aufgaben. Dies ist durch die Architektur der Hardware bedingt. Das Ansprechen und Abfragen der Kameradaten wird durch die Einbindung der OpenNI2-Bibliothek[4] realisiert.
Grundlegend folgt die Software den Sequenzen Daten abfragen -> Daten aufarbeiten -> Auswerten -> Ausgeben. Hierbei wurde ein modularer Aufbau des Sourcecodes verwendet, dies diente der Transparenz und Orienterung im Code.
RGB-Kamera abfragen
- Die Abfrage der Daten wird über die OpenNI-Bibliothek abgearbeitet. Grundlage ist das Beispielprogramm MultiStreamRead[5]. Aus diesem Beispiel wurde folgender Code zur Abfrage der RGB-Kamera genutzt:
//Initialisierung der Bibliothek OpenNI
Status rc = OpenNI::initialize();
//Aufbau des Datenstreams
Device device;
rc = device.open(ANY_DEVICE);
VideoStream RGB;
rc = RGB.create(device, SENSOR_RGB);
rc = RGB.start();
VideoFrameRef frame;
int changedStreamDummy;
VideoStream* pStream = &RGB;
rc = OpenNI::waitForAnyStream(&pStream, 1, &changedStreamDummy, SAMPLE_READ_WAIT_TIMEOUT);
//Auslesen eines Frames
rc = RGB.readFrame(&frame);
RGB888Pixel* pRGB = (RGB888Pixel*)frame.getData();
//Zerstören des Datenstreams
depth.stop();
depth.destroy();
device.close();
OpenNI::shutdown();
- Wie aus dem Code ersichtlich, wird zuerst die Bibliothek OpenNI initialisiert. Nach der erfolgreichen Initialisierung kann ein Datenstream aufgebaut werden. Dieser Stream stellt die Verbindung zwischen dem Zielsystem und der Asus XTION. Durch diese Verbindung können einzelne Frames ausgelesen werden, welche dann in einem OpenNI-spezifischen Datentypen gespeichert werden.
Tiefeninformationen abfragen
- Gleich zur Abfrage der RGB-Kamera lässt sich auch eine Abfrage der Tiefeninformationen gestalten. Hierbei werden nur wenige Änderungen vollzogen, sodass folgender Code entsteht:
//Initialisierung der Bibliothek OpenNI
Status rc = OpenNI::initialize();
//Aufbau des Datenstreams
Device device;
rc = device.open(ANY_DEVICE);
VideoStream depth;
rc = depth.create(device, SENSOR_DEPTH);
rc = depth.start();
VideoFrameRef frame;
int changedStreamDummy;
VideoStream* pStream = &depth;
rc = OpenNI::waitForAnyStream(&pStream, 1, &changedStreamDummy, SAMPLE_READ_WAIT_TIMEOUT);
//Auslesen eines Frames
rc = depth.readFrame(&frame);
DepthPixel* pDepth = (DepthPixel*)frame.getData();
//Zerstören des Datenstreams
depth.stop();
depth.destroy();
device.close();
OpenNI::shutdown();
- Die Abfrage der Tiefeninformation unterscheidet sich von der Abfrage der RGB-Kamera nur durch die Auswahl des Sensors SENSOR_DEPTH und die Wahl des Datentypens, in dem gespeichert wird.
RGB zu Grayscale
- Nach dem Auslesen der RGB-Kamera müssen die Daten in eine binäre Form gebracht werden, um durch einen vereinfachten Canny-Algorithmus[6] die Kanten extrahieren zu können. Um eine dynamische Verarbeitung zu ermöglichen, werden folgende Prozessschritte vollzogen: RGB -> Grayscale -> Binär. Die grundlegenden Schritte für die Umwandlung eines RGB-Bildes zu einem Grayscale-Bild lässt sich durch folgenden Pseudo-Code beschreiben:
for (gesamtes Bild)
Grau = (Faktor)*R+(Faktor)*G+(Faktor)*B
Mittelwert aufaddieren
end
Mittelwert bilden
- Durch die Bildung des Mittelwertes kann nun die Umwandlung in ein binäres Bild erfolgen. Hierzu wird die generelle Helligkeit des Bildes bewertet und anhand dieses Wertes ein Threshold ermittelt, welches entscheidet, ob ein Bit gesetzt wird oder nicht. Dies lässt sich durch folgenden Pseudo-Code beschreiben:
Threshold ermitteln
for (gesamtes Bild)
if (Pixelwert < Threshold)
Pixel setzen
end
end
Spur extrahieren
- Durch die Ermittlung eines binären Bildes kann eine Kantendetektion erfolgen. Dies wird durch einen vereinfachten Canny-Algorithmus erzielt. An dieser Stelle wurde auf einen kompletten Canny verzichten, da dies die Leistung des RaspberryPi´s übersteigen würde. Der vereinfachte Canny bearbeitet das binäre Bild zeilenweise und sucht nach gesetzten Bits. Anhand der gefunden Bereiche können Linien durch mathematische Modelle gefunden werden.
for (Bildbereiche)
Suche nach gesetzten Pixeln
Zusammenfügen der Pixel zu Bereichen
end
Abgleich der Bereiche mit math.Modell
Ausgabe der Linien nach Abgleich
- Die Ausgabe der Linien erfolgt durch eine Matrix, welche die einzelnen Pixel der Linie pro Zeile beinhaltet. Anhand dieser Matrix kann später durch weitere Prozessschritte eine Linienführung verwirklicht werden.
Objekte extrahieren
- Die Informationen, welche der Tiefensensor ausgibt, müssen in Objekte transformiert werden, um aus diesen Informationen zu erkennen, wann Hindernissen ausgewichen werden muss. Um eine Optimierung des Algorithmus zu ermöglichen, werden sämtliche Bildbereiche ausgegraut, welche nicht interessant sind. Das Interesse an einzelnen Pixeln wurde im Vorweg anhand von Entfernung und Plausibilitäten definiert. Die gefunden interessanten Pixel werden nun anhand ihrer Nachbarpixel bewertet. Aus den Informationen über die Pixel können nun durch mathematische Modelle die Objekte extrahiert werden.
Spur und Objekte ausgeben
- Die aus den vorhergehenden Modulen extrahierten Objekte und Spuren werden durch eine definierte Schnittstelle an weitere Module übergeben. Die übergebenen Informationen werden anhand von Arrays folgender Form ausgegeben:
Objekte: int [n][7] //n definiert die Anzahl der Objekte
[n][0] | [n][1] | [n][2] | [n][3] | [n][4] | [n][5] | [n][6]
x-Pos Tiefe x-Pos Tiefe x-Pos Tiefe y-Pos
links links Mitte Mitte rechts rechts
Spur: int [5][2*x] //x wird beim Kompilieren definiert und stellt
die mögliche Anzahl an Punkten dar, wo eine
Linie gefunden werden kann
[1][1] | [1][2] | ... | [1][n] | [1][n+1] | [1][n+2] | [1][n+3]
x-Pos y-Pos x-Pos y-Pos -1 -1
//n=2*(Anzahl gefundenen Linie): als Delimiter wird der Wert -1
eingetragen, um das Ende der Linie zu kennzeichnen
Realisierung
Die ausführbaren Dateien inklusive der benötigten Kernel-Module sind hier[7] verfügbar. Dabei ist zu beachten, dass das Programm für die ARM-Architektur kompiliert wurde.
Hilfestellung zur Projektaufnahme
Einbindung der OpenNI
In diesem Projekt wurde die Kamera ASUS XTION PRO durch die OpenSource-Bibliothek OpenNI angesprochen. Die an dieser Stelle beschriebene Anleitung basiert auf der Einbindung der OpenNI für ein System aus RaspberryPi, OpenNI2 und der AUSU XTION PRO[8]. Es wird für die erfolgreiche Durchführung der Anleitung auf Vorkenntnisse im Umgang mit Linux-Systemen ausgegangen.
- 1. Installieren der abhängigen Programme auf dem RaspberryPI
sudo apt-get install git g++ python libusb-1.0-0-dev freeglut3-dev doxygen graphviz
Hierbei ist zu beachten, dass die benötigten Dateien einen Umfang von ca. 900MB darstellen. Es ist vorher sicherzustellen, dass dieser Speicherplatz entsprechend frei ist.- 2. Runterladen der Bibliothek
git clone https://github.com/OpenNI/OpenNI2
- 3. Adaptieren der Bibliothek für ARM-Architektur
- Öffnen der Datei OpenNI2/Redist/Redist.py und ändern der Zeile 534:
compilation_cmd = "make -j" + calc_jobs_number() + " CFG=" + configuration + " PLATFORM=" + platform + " > " + outfile + " 2>&1"
Diese Zeile sollte kopiert und auskommentiert werden, da sie durch folgenden Code zu ersetzen ist:#compilation_cmd = "make -j" + calc_jobs_number() + " CFG=" + configuration + " PLATFORM=" + platform + " > " + outfile + " 2>&1"
compilation_cmd = "make -j1" + " CFG=" + configuration + " PLATFORM=" + platform + " > " + outfile + " 2>&1"
- Dieser Aschritt kann in Abhängigkeit der Prozessorleistung bis zu 45 Minuten dauern.
- 4. Erzeugen eines Builds
cd OpenNI2/
PLATFORM=Arm make
- 5. Kopieren und installieren der kompilierten Dateien
cd Final/
cp OpenNI-Linux-Arm-2.1.0.tar.bz2 /usr/local/src
cd /usr/local/src/
tar -xjvf OpenNI-Linux-Arm-2.1.0.tar.bz2
cd OpenNI-2.1.0-arm/
sudo ./install.sh
- 6. Kontrolle
- Bei angeschlossener Kamera sollte der Befehl
lsusb -vv
ausgeführt werden. Mit diesem Befehl werden alle angeschlossenen USB-Geräte identifiziert und aufgelistet. In dieser Auflistung sollte ein Gerät mit der ID1d27:0600
erscheinen. - 7. Hinweise
- Es ist darauf zu achten, dass die Kamera über eine externe Quelle mit Strom versorgt wird, da diese Kamera nur mit PoweredUSB-Ports läuft.
Kompilieren der Projektdateien
Die hier verfügbaren Quell-Dateien[9] können mittels des Programms as in Maschinencode übersetzt werden und anschließend mittels Linker ld zu einer ausführbaren Datei zusammengefügt werden[10]. Dabei ist wiederum zu beachten, dass sämtliche Anweisungen auf der Ziel-Architektur (ARM) ausgeführt werden müssen oder ein Cross-Compiler verwendet werden muss.
Lessons learned
Da dieses Projekt einen Teil der Projektarbeit des Carolo Cup Teams LCP beinhaltete, war der reale Arbeitsaufwand zu Beginn des Semesters schwer abschätzbar. Es stellte sich heraus, dass das Projekt einen weit größeren Arbeitsaufwand darstellt als von den Projektteilnehmern angenommen wurde. Daraus lässt sich für folgende Projekte ableiten, dass der geschätzte Arbeitsaufwand eines Projektes mit einem Faktor versehen werden sollte. Dieser Faktor dient dem Projektteam dann als Puffer, falls es während des Projektes zu größerem Arbeitsaufwand (durch unvorgesehene Komplexität oder anderen Komplikationen) kommt.
Neben der Erfahrung zum Abschätzen des Arbeitsaufwandes lehrte dieses Projekt, was es für einzelne Personen bedeutet in einem Projektteam zu arbeiten. Gerade die Kommunikation mit dem übergeordneten Projekt war für den Erfolg dieses Teilprojektes von enormer Bedeutung.
Als größten Lernerfolg kann für dieses Projekt die Erweiterung des persönlichen Horizonts im Bereich Informatik gewertet werden. Hierbei wurde sich auf die Vorlesungen aus dem vorhergehenden Semestern gestützt. Das bereits aufgebaute Wissen konnte nun in einer realen Umgebung getestet, verfestigt und erweitert werden. Besonders die Erfahrung in der Programmierung auf einem Embedded Systems wird sich für den weiteren Verlauf des Studiums sicherlich als positiv darstellen. Des Weiteren wurde sich mit einer Linux-Distribution beschäftigt, welche einen den Umgang mit einem Betriebssystem von Grund an lehrte.
Anforderungen zur Aufnahme des Projektes
Durch die Komplexität des Projektes sollte das Projektteam folgende Vorkenntnisse erfüllen:
- 1. Sprachen
- In diesem Projekt wurde mit folgenden Sprachen gearbeitet:
C#, Assembler, GNU make
Der sichere Umgang mit Programmierlogiken und -techniken wird ebenfalls als zwingend notwendig angesehen. - 2. Betriebssystem
- Für den Umgang sollten grundlegende Vorkenntnisse mit den Betriebssystemen von Windows[11] und Lubuntu[12] (basierend auf Ubuntu) vorliegen.
Weiterhin wird eine Kontaktaufnahme mit vorherigen Gruppen empfohlen. Dies erleichtert den Einstieg in das Thema und hilft für einen erfolgreichen Projektabschluss.
Zusammenfassung
Zusammenfassend kann dieses Projekt als erfolgreich gewertet werden. Die Aufgabenstellung wurde entsprechend der technischen Anforderung vollkommen erfüllt. Die Spur- und Objekterkennung liefert die angeforderten Werte, welche es jedoch durch nachgehängte Prozesse zu optimieren gilt. Auf diese Prozesse wurde in diesem Projektteil jedoch nicht eingegangen. Lediglich die formale Aufbereitung des Projektes für weitere Generationen bedarf einer Optimierung. Hierbei hat sich herausgestellt, dass es eine weit größeren Anteil des Projektaufwandes ausmacht, die Daten entsprechend aufzubereiten und festzuhalten, als vom Projektteam angenommen wurde.
Da dieses Projekt eine weit größere Komplexität darstellt, als es anfangs scheint, wird an dieser Stelle auch explizit darauf hingewiesen, dass es zu einer Kontaktaufnahme zum vorherigen Projektteam kommen sollte, wenn man das Projekt aufnimmt. Dieser Schritt erleichtert den Einstieg in die Logik des Projektes und dient gleichzeitig der Sicherstellung des Erfolges des Projektes.
Projekthistorie
Dies Auflistung zeigt sämtliche Personen, welche an diesem Projekt gearbeitet haben:
Jahr | Gruppenmitglieder | Hinweise | |
---|---|---|---|
Wintersemester 2013/14 | Jan Kifmann | Hauke Ludwig | (Team LCP) |
Einzelnachweise
- ↑ [1] Asus XTION Spezifikationen (07.01.2014)
- ↑ [2] How to: Installieren der OpenNI-Bibliothek auf RaspberryPi (14.01.2014)
- ↑ [3] Logischer Aufbau der Hauptfunktion | Logischer Aufbau der Spurerkennung
- ↑ [4] OpenNI-Bibliothek (07.01.2014)
- ↑ [5] OpenNI2::MultiStreamRead Beispielprogramm (07.01.2014)
- ↑ [6] Papageorgiou, Leibold, Buss: Optimierung - Statistische, dynamische, stochastische Verfahren für die Anwendung, Springer, ISBN 978-3-540-34012-6 (S.441ff.) -> eBook-Bibliothek
- ↑ [7] Ausführbare Projekt-Dateien (24.01.2014)
- ↑ [8] How to: Installieren der OpenNI-Bibliothek auf RaspberryPi (14.01.2014)
- ↑ [9] Quelldateien des Projektes
- ↑ [10] GNU Binutils (25.01.2014)
- ↑ [11] Microsoft Developer Network: Hilfestellungen zum Programmieren unter Windows
- ↑ [12] Download der Lubuntu-Distribution (25.01.2014)