Skip to content
Snippets Groups Projects
Commit c9987fe3 authored by Philipp Stenkamp's avatar Philipp Stenkamp
Browse files

Merge branch 'master' of https://gitlab.cvh-server.de/lf.ps/vbls

Conflicts:
	LeonardoMixerIO/doc/Dokumentation_Systemtechnik.pdf
parents 6d2ea529 f5f67c3b
Branches
No related tags found
No related merge requests found
......@@ -70,7 +70,7 @@ Systems, welches eingehende Signale aus mehreren, ggf.
unterschiedlichen Quellen erfassen und anschließend zu einem
kombinierten Ausgangssignal verarbeiten kann. Konkret sollen hierbei
die im Modellbau etablierte Protokolle Spektrum-Remote-Receiver und das Multiwii
Serial Protocol erfasst bzw. ausgegeben werden können. Die
Serial Protocol erfasst bzw.\ ausgegeben werden können. Die
Applikation soll auf Mikroprozessoren der Reihe Atmega implementiert
werden.
......@@ -107,6 +107,7 @@ Echtzeitfähigkeit beschreibt in der Informationstechnik also die Fähigkeit, je
Daten innerhalb eines fest definierten Zeitraumes verarbeiten zu können.
\subsection{Kooperatives Multitasking}
\label{multitasking}
Multitasking im Allgemeinen bezeichnet die Verarbeitung einer Vielzahl konkurrierender,
quasiparalleler Tasks. Ein Scheduler dient der Ressourcen- und Taskverwaltung.
Beim kooperativen Multitasking können Tasks garnicht oder nur dann unterbochen werden,
......@@ -171,8 +172,8 @@ oder einer Flugsteuerung dar.
% \includegraphics[page=4,trim=4cm 17cm 4cm 5cm,clip,width=0.8\textwidth]{references/900-00005-Standard-Servo-Product-Documentation-v2_2.pdf}
% \caption{Ansteuerung eines Servos mittels PWM-Signal~\cite[][4]{parallax2011}}
% \label{fig:servo-pwm}
\includegraphics[page=1,trim=11.5cm 19cm 3cm 6cm,clip,width=0.8\textwidth]{references/MTN004.pdf}
\caption{Typische Wellenform eines im Modellbau verwendeten PWM-Signals~\cite[][1]{mec2001}}
\includegraphics[page=1,trim=11.5cm 19cm 3cm 6cm,clip,width=0.6\textwidth]{references/MTN004.pdf}
\caption{Typische Wellenform eines PWM-Signals~\cite[][1]{mec2001}}
\label{fig:waveform-pwm}
\end{figure}
}
......@@ -182,23 +183,24 @@ Es findet im Modellbau beispielsweise Verwendung, um bis zu neun PWM-Signale üb
eine einzige Leitung zu senden. Dies geschieht durch Aneinanderreihung der unter PWM
beschriebenen Pulse (siehe Abb.\ \ref{fig:waveform-ppm}). Aufgrund der simplen Verdrahtung
stellt es eine beliebte Möglichkeit dar, Flugsteuerung und RC-Empfänger zu verbinden.
\begin{figure}[hbt]
\begin{figure}[hb]
\centering
\includegraphics[page=1,trim=3cm 4.5cm 7cm 20.5cm,clip,width=\textwidth]{references/MTN004.pdf}
\caption{Typische Wellenform eines im Modellbau verwendeten PPM-Summen-Signals~\cite[][1]{mec2001}}
\caption{Typische Wellenform eines PPM-Summen-Signals~\cite[][1]{mec2001}}
\label{fig:waveform-ppm}
\end{figure}
}
\end{itemize}
\subsubsection{Digital}
Bei den im Folgenden beschriebenen Protokollen handelt es sich ausshließlich um
Bei den im Folgenden beschriebenen Protokollen handelt es sich ausschließlich um
digitale, unidirektionale \& asynchrone serielle Datenströme, welche von einem
UART eines Mikrocontrollers generiert und gelesen werden können.
Jedes dieser Protokolle ist dazu geeignet, mindestens sechs Steuerkanäle zu übertragen.
\begin{itemize}
\item{Spektrum-Remote-Receiver\\
Das Spektrum-Remote-Receiver Protokoll wird von sog.\ Satelliten-Empfängern des Herstellers
Spektrum bzw. Horizon Hobby dazu kompatiblen Systemen verwendet, um mit einem Hauptempfänger
Spektrum bzw.\ Horizon Hobby und dazu kompatiblen Systemen verwendet, um mit einem Hauptempfänger
oder einem Flugcontroller zu kommunizieren.
Ursprünglich diente es lediglich der Erhöhung der Ausfall- und Übertragungssicherheit
der Funkverbingung zwischen Fernsteuerung und Empfänger durch das Vernetzen mehrerer,
......@@ -208,12 +210,12 @@ Mehrere erfolgreiche Reverse-Engineering Ansätze ermöglichten jedoch bald die
Verwendung dieser kostengünstigen Satellitenempfänger mit Flugsteuerungen.
Horizon Hobby reagierte schließlich mit dem begrüßenswerten Entschluss, eine offizielle
Spezifikation dieses Protokolls mitsamt einer rudimentären Implementierungsanleitung
zu veröffentlichen.\cite[][2]{spek2016}
zu veröffentlichen \cite[][2]{spek2016}.
}
\item{SRXL\\
Das Serial Receiver Link Protocol stellt ein offenes Protokoll dar, mit dem Ziel die Kommunikation zwischen
Modellbau-Komponenten unterschiedlicher Hersteller zu ermöglichen. Es können in
SRXL Version 2 bis zu 16 Kanäle übertragen werden.\cite[][1]{multiplex2013}
SRXL Version 2 bis zu 16 Kanäle übertragen werden \cite[][1]{multiplex2013}.
}
\item{MSP\\
Das MultiWii-Serial Protokoll ist eine offenes Protokoll zur Kommunikation unterschiedlichster
......@@ -229,15 +231,23 @@ beschreibt jedoch gut die zur Verfügung stehenden Kommandos, und in dem MultiWi
gibt es einen entsprechenden Beitrag (vgl.\ \cite{multiWii2012}) mit sporadischer Aktivität.
}
\end{itemize}
\section{Arduino Leonardo}
Der Arduino Leonardo stellt eine Entwicklungsplatine für den Atmel-Atmega-32u4 dar.
Gewählt wurde der Leonardo, da er der günstigste und kompakteste Arduino ist, welcher
\begin{figure}[ht]
\centering
\includegraphics[width=0.42\textwidth]{graphics/leonardo-olimex.jpg}
\caption{Arduino Leonardo kompatibler OLIMEXINO-32U4 -- Quelle: Olimex}
\label{fig:leonardo-olimex}
\end{figure}
\noindent{}Gewählt wurde der Leonardo, da er der günstigste und kompakteste Arduino ist, welcher
neben einer in Hardware implementierten seriellen Schnittstelle auch eine zweite
serielle Schnittstelle in Form einer virtuellen USB-CDC Schnittstelle bietet.
Diese ist essentiell für die Kommunikation des RC-Mischers mit dem Mobiltelefon.
Beide Schnittstellen sind auf dem Blockdiagramm des Atmega 32u4 (siehe Abb.\ \ref{fig:block-diag-32u4})
zu erkennen, namentlich USB2.0 und USART1.
\begin{figure}[h]
\begin{figure}[ht]
\centering
\includegraphics[page=4,trim=1cm 8.4cm 1cm 3.5cm,clip,width=\textwidth]{references/Atmel-7766-8-bit-AVR-ATmega16U4-32U4_Datasheet.pdf}
\caption{Block Diagramm des Atmega 32u4~\cite[][4]{atmel2016}}
......@@ -247,12 +257,6 @@ Verwendung fand in diesem Projekt eine Arduino-kompatible Platine (siehe Abb.\ \
Herstellers Olimex\footnote{\url{https://www.olimex.com/Products/Duino/AVR/OLIMEXINO-32U4/open-source-hardware}},
welche gegenüber dem offiziellen Arduino Leonardo einige Vorteile sowie ein
besseres Preis-Leistungs-Verhältnis bietet.
\begin{figure}[h]
\centering
\includegraphics[width=0.42\textwidth]{graphics/leonardo-olimex.jpg}
\caption{Arduino Leonardo kompatibler OLIMEXINO-32U4 -- Quelle: Olimex}
\label{fig:leonardo-olimex}
\end{figure}
\section{Quadrocopter}
Als Quadrocopter wird im Allgemeinen ein Fluggerät bezeichnet, welches über vier
......@@ -272,11 +276,10 @@ herangezogen werden \cite{wikipedia1}.
Die Regelung der Lage und Geschwindigkeit eines Quadrocopters erfolgt mittels einer
Flugsteuerung an Bord. Diese stellt ein für sich eigenständiges eingebettetes System
dar, welches über entsprechende Sensorik zur Lage- und ggf.\ Positionserkennung verfügt.
Die Steuerung eines Quadrocopters durch den Piloten erfolgt bspw.\ konventionell
mittels einer Funkfernsteuerung oder auch per App über ein Mobiltelefon.
\begin{figure}[h]
\begin{figure}[ht]
\centering
\includegraphics[width=\textwidth]{../../common/header.jpg}
\caption{Der in diesem Projekt verwendete Quadrocopter}
......@@ -424,15 +427,14 @@ struct linkedListElement {
void *next;
};
\end{lstlisting}
Der Scheduler kann Realtime-Tasks (abgekürzt mittels \glqq{}rtTask\grqq{}) und
Non-Realtime-Tasks (abgekürzt mittels \glqq{}nRtTask\grqq{}) verwalten.
Dabei besteht jeder Task grundlegend aus einer Funktion, die ihm bei der
Initialisierung zugewiesen werden kann und einem Zeitstempel, in dem seine letzte
Dabei besteht jeder Task aus einer Funktion, die ihm bei der Initialisierung zugewiesen werden kann und einem Zeitstempel, in dem seine letzte
Ausführungszeit gespeichert wird. Es ist dabei zu beachten, dass lediglich
Funktionen ohne Übergabeparameter und Rückgabe-Werte zulässig sind, da der
Scheduler eine reine Taskverwaltung und keine datenverarbeitende Instanz darstellt.
Der Datenaustausch zwischen den Tasks ist mittels globaler Variablen wie bspw.\
dem \lstinline{rcSource}-Struct realisiert.
Der Datenaustausch zwischen den Tasks ist über einen gemeinsamen Speicherbereich in Form globaler Variablen wie bspw.\ dem \lstinline{rcSource}-Struct möglich.
Die Datenstrukturen \lstinline{rtTask} und \lstinline{nRtTask} erben beide direkt von
\lstinline{task} mit dem einzigen Unterschied, dass Realtime-Tasks eine Zykluszeit
......@@ -442,13 +444,13 @@ lediglich einmal für beide Typen zu definieren (vgl.\ beispielsweise \lstinline
Weiterhin ergibt sich durch die grundsätzlich gleiche Datenstruktur von \lstinline{rtTask}
und \lstinline{nRtTask} die Möglichkeit, äquivalent auf die Attribute \lstinline{cycleTime}
und \lstinline{priority} zuzugreifen (d.\,h.\ über die Funktion \lstinline{getTaskCycleTime}
kann auf einen Non-Realtime-Task dessen Priorität erhalten werden).
kann bei einem Non-Realtime-Task dessen Priorität erhalten werden).
Die Datenstruktur \lstinline{linkedListElement} stellt einzelne Elemente einer
verknüpften Liste bereit. Diese enthalten jeweils einen Pointer auf einen Task
verketteten Liste bereit. Diese enthalten jeweils einen Pointer auf einen Task
(und sind damit zunächst unabhängig davon, ob es sich um einen Realtime- oder
einen Non-Realtime-Task handelt) und einen Pointer auf das nächste Listenelement.
Die durch die Verknüpfung der Elemtente entstehende Liste ist unidirektional,
Die durch die Verkettung der Elemtente entstehende Liste ist unidirektional,
d.\,h.\ sie kann nur von vorne nach hinten, nicht jedoch andersherum durchlaufen werden.
\begin{lstlisting}[caption=Festlegen der Threshold-Werte]
......@@ -457,22 +459,22 @@ d.\,h.\ sie kann nur von vorne nach hinten, nicht jedoch andersherum durchlaufen
#define OVERLOAD_THRESHOLD_TIMES 100
#define STARVATION_THRESHOLD 100000
\end{lstlisting}
Über die Threshold-Werte können verschiedene Eigenschaften der Taskverwaltung
beeinflusst werden. Mittels \lstinline{MAX_TASK_THRESHOLD} lässt sich die maximal
zulässige Anzahl an Realtime- und Non-Realtime-Taks (jeweils) festlegen, um einer
zulässige Anzahl an Realtime- und Non-Realtime-Taks (jeweils) festlegen. Dies dient dazu, einer
Überlast vorzubeugen und sicherzustellen zu können, dass das übergebene Task-Array
korrekterweise mit einer \lstinline{NULL} beendet wird.
\lstinline{OVERLOAD_THRESHOLD_PERCENT} und \lstinline{OVERLOAD_THRESHOLD_TIMES}
legen fest, wie häufig ein Realtime-Task bei seiner tatsächlichen Ausführung um
wieviel Prozent von seiner vorgesehenen Ausführungszeit abweichen darf, bevor der
Scheduler sich abschaltet, da er aus Überlastgründen keine Echtzeitfähigkeit mehr
gewährleisten kann.
Scheduler sich aus Überlast abschaltet. Somit wird die Echtzeitfähigkeit während des Betriebs gewährleistet.
Mittels \lstinline{STARVATION_THRESHOLD} lässt sich weiterhin eine Grenze für die
Differenz zwischen letzter Ausführungszeit und aktueller Systemzeit festlegen,
ab deren Überschreitung ein Non-Realtime-Task temporär in seiner Priorität erhöht
und somit für einen Zyklus priorisiert ausgeführt wird, um Starvation vorzubeugen.
und somit für einen Zyklus priorisiert ausgeführt wird. Durch diese Maßnahme wird Starvation (s. Kapitel \ref{multitasking}) vorzubeugen.
\subsection{Scheduler.cpp}
\label{Scheduler.cpp}
......@@ -498,6 +500,7 @@ Scheduler::Scheduler (rtTask **newRtTasks, nRtTask **newNRtTask) {
setNRtTasks(newNRtTask);
}
\end{lstlisting}
Der Scheduler wird mittels des Konstruktors initialisiert. Beim Aufruf können
zwei seperate Arrays mit Pointern auf Realtime- und Non-Realtime-Tasks übergeben
werden (das erste Array enthält alle Realtime-Tasks, das zweite Array alle
......@@ -506,12 +509,8 @@ enthalten, um das Ende zu kennzeichnen. Sollen nur Echtzeit- oder nur
Nicht-Echtzeit-Tasks verwaltet werden, oder sollen die Arrays eigenständig mittels
der Funktionen \lstinline{setRtTasks(...)} und \lstinline{setNRtTasks(...)} gesetzt werden,
so kann dies durch die Übergabe eines Nullpointers an der entsprechend anderen
Stelle beim Konstruktoraufruf signalisiert werden. Standardmäßig werden alle in
der Bibliothek deklarierten, pointerartigen Elemente mit \lstinline{NULL} initialisert;
alle (Zahlen-)Counter werden auf den Wert null gesetzt. Anschließend werden die
Funktionen \lstinline{setRtTasks(...)} und \lstinline{setNRtTasks(...)} aufgerufen.
Stelle beim Konstruktoraufruf signalisiert werden.
\pagebreak
\begin{lstlisting}[caption=setNRtTasks]
// Sorts and sets the input-array as the new non-realtime-task-
// array; the last
......@@ -540,19 +539,18 @@ void Scheduler::setNRtTasks (nRtTask **newNRtTasks) {
}
}
\end{lstlisting}
Die Funktion \lstinline{setNRtTasks(...)} verarbeitet das übergebene Array mit Zeigern
auf Non-Real"=time-Task vor und weist es der Programmintern verwendeten
Variable \lstinline{nRtTasks} zu. Zunächst werden die Tasks in dem Array gezählt.
Wie bereits erwähnt, muss das übergebene Array am Ende einen Nullpointer enthalten,
um das Ende zu kennzeichnen. Überschreitet die Anzahl der Tasks den für
\lstinline{MAX_TASK_THRESHOLD} festgelegten Wert (siehe Kapitel \ref{Scheduler.cpp}),
so gibt die Funktion, falls ein serieller Debugging-Port mittels \lstinline{setDebugger(...)}
übergeben wurde, eine Fehlermeldung aus und der Scheduler schaltet sich mittels
\lstinline{exterminate()} ab (bzw. konkret wechselt er in eine Dauerschleife).
so schaltet sich der Scheduler mittels
\lstinline{exterminate()} ab (bzw.\ konkret wechselt er in eine Dauerschleife).
Nachdem die Tasks gezählt wurden, werden sie mittels \lstinline{sortTasks(...)}
ihrer Priorität nach sortiert. Dies hat zur Folge, das das Array anschließend für
die Ausführungsreihenfolge der Tasks von vorne nach hinten durchlaufen werden kann,
ohne einen zweiten Prioritätscounter mitlaufen zu lassen (Effizienzsteigerung).
die Ausführungsreihenfolge der Tasks einfach von vorne nach hinten durchlaufen werden kann (Effizienzsteigerung).
Wurde ein Nullpointer als Parameter übergeben oder ist der erste Eintrag im Array
eine \lstinline{NULL}, so wird die Variable \lstinline{nRtTasks} ebenfalls auf
\lstinline{NULL} gesetzt, wodurch signalisiert wird, dass es keine Nicht-Echtzeit-Tasks
......@@ -586,6 +584,7 @@ void Scheduler::setRtTasks (rtTask **newRtTasks) {
}
}
\end{lstlisting}
Die Funktion \lstinline{setRtTasks(...)} dient, ebenso wie \lstinline{setNRtTasks(...)}
der Vorverarbeitung und Zuweisung des übergebenen Arrays, dieses Mal jedoch die
Echtzeit-kritischen-Tasks betreffend. Der Aufbau Funktion ist analog zu
......@@ -645,25 +644,25 @@ void Scheduler::sortTasks(task **tasks, int left, int right) {
}
}
\end{lstlisting}
Bei \lstinline{sortTasks(...)} handelt es sich um einen Heapsort-Algorithmus mit der
durchschnittlichen Ordnung von $\mathcal{O}(n\cdot\log n)$. Dieser sortiert das übergebene Array
entweder nach Zykluszeiten oder nach Prioritäten (abhängig davon, ob ein Array mit
Bei \lstinline{sortTasks(...)} handelt es sich um einen Heapsort-Algorithmus mit einer Ordnung von $\mathcal{O}(n\cdot\log n)$. Dieser sortiert das übergebene Array entweder nach Zykluszeiten oder nach Prioritäten (abhängig davon, ob ein Array mit
Pointern auf Realtime- oder auf Non-Realtime-Task übergeben wurde).
Dadurch, dass als Typ des Übergabeparameters die Grund-Datenstruktur \lstinline{task}
Dadurch, dass als Typ des Übergabeparameters die Grund-Daten"=struktur \lstinline{task}
gewählt wurde, können sowohl Echtzeit- als auch Nicht-Echtzeit-Task-Arrays verarbeitet werden.
Auf die Parameter \lstinline{cycle-time} bzw. \lstinline{priority} kann, da,
Auf die Parameter \lstinline{cycle-time} bzw.\ \lstinline{priority} kann, da,
wie bereits in Kapitel \ref{Scheduler.h} angeführt, sowohl \lstinline{rtTask} als auch
\lstinline{nRtTask} die gleiche Speicherstruktur haben, analog über die Funktion
\lstinline{getTaskCycleTime(...)} zugegriffen werden.
Der Vorteil des Heapsort-Algorithmus gegenüber dem aus Quicksort-Verfahren besteht darin,
dass ersterer iterativ abläuft, während letzterer rekursiv aufgerufen wird.
Der Vorteil des Heapsort-Algorithmus gegenüber Sortierverfahren wie bspw.\ dem Quicksort besteht darin, dass ersterer iterativ abläuft, während letzterer rekursiv aufgerufen wird.
Somit wird einem Überlaufen des Stacks des Mikrokontrollers vorgebeugt.
Die bereits erwähnte Ordnung des Heapsorts ist darüber hinaus unabhängig vom Inhalt
des zu sortierenden Arrays, sie ist konstant. Sein Verhalten ist folglich deterministisch,
weswegen er sich für die gegebene echtzeitkritische Anwendung anbietet.
des zu sortierenden Arrays, sie ist konstant. Sein Verhalten ist folglich deterministisch, was ihn für die Verwendung in echtzeitkritischen Anwendung prädestiniert. Dies ist durchaus insofern von Relevanz, da die Neusorortierung von Nicht-Echtzeit-Tasks z.\,B.\ bei Starvation eines Tasks auch während des laufenden Betriebs erfolgt.
\\
\\
Anmerkung 1: Für eine geringe Anzahl an Tasks bzw.\ für den Fall, dass Starvation kein Problem in der gegebenen Anwendung darstellen sollte, kann für eine Performanceverbesserung ein Algorithmus wie bspw.\ Bubblesort verwendet werden. Ist hingegen nicht zwangsläufig gegeben, dass die zu sortierenden Arrays weitestgehend vorsortiert sind, ist der Heapsort aufgrund seines deterministischen Verhaltens zu präferieren.
\\
Anmerkung: Für die Herleitung und Grundlagen des Heapsort-Algorithmus vergleiche
\\
Anmerkung 2: Für die Herleitung und Grundlagen des Heapsort-Algorithmus vergleiche
\cite[][ S.186-192]{sedgewick1992} und \cite[][ S.191-197]{baumann1981}.
Die hiesige Implementation orientiert sich an \cite[][ S.336-338]{numRecC1992}.
......@@ -691,14 +690,15 @@ linkedListElement * Scheduler::convertToLinkedList (task **tasks)
return firstElement;
}
\end{lstlisting}
Mittels \lstinline{convertToLinkedList(...)} wird das übergebene Array mit Pointern
auf Tasks in eine einfach verkettete Liste umgewandelt und der Zeiger auf das erste Element
der Liste zurückgegeben. Grundsätzlich ist die Implementierung der Funktion unabhängig davon,
ob Realtime- oder Non-Realtime-Tasks übergeben werden; konkret werden jedoch lediglich
ob Realtime- oder Non-Realtime-Tasks übergeben werden. Konkret werden jedoch lediglich
die Echtzeit-Tasks in Listenform verwaltet, da sich durch den Zugriffsalgorithmus an
dieser Stelle Zeiteinsparungen erzielen lassen, während sich die Effizienz bei
Nicht-Echtzeit-Tasks gegenüber der Array-Speicherform verschlechtern würde
(Quicksort ist besser für Arrays als für einfach verkettete Listen geeignet).
(Heapsort ist besser für Arrays als für einfach verkettete Listen geeignet).
Zunächst wird mit \lstinline{new_pointer} ein Zeiger auf ein mittels der Funktion
\lstinline{newLinkedList-}
......@@ -707,7 +707,7 @@ Diesem werden der Task an der aktuellen Stelle des übergebenen Task-Arrays und
ein Nullpointer als Adresse des nächsten Elements zugewiesen.
Anschließend wird überprüft, ob bereits ein vorangegangenes Listenelement existiert.
Ist dies der Fall, wird \lstinline{listElementPointer->next} auf die Adresse des neu
erzeugten Elements gesetzt; falls nicht, wird die Adresse als Einstiegspunkt der
erzeugten Elements gesetzt. Falls nicht, wird die Adresse als Einstiegspunkt der
Liste in \lstinline{firstElement} gespeichert und später zurückgegeben.
Danach wird mit \lstinline{listElementPointer} die Variable in der die Adresse des
letzten erzeugten \lstinline{linkedListElement} gespeichert ist, auf
......@@ -731,6 +731,7 @@ linkedListElement * Scheduler::newLinkedListElement (void){
}
}
\end{lstlisting}
Die Funktion \lstinline{newLinkedListElement()} gibt als Rückgabewert einen mit jedem
Aufruf fortlaufenden Zeiger auf ein \lstinline{linkedListElement} aus einem in
\lstinline{setRtTasks(...)} initialisierten Array der Größe \lstinline{MAX_TASK_THRESHOLD}
......@@ -770,6 +771,7 @@ linkedListElement * Scheduler::sortRtTasks (linkedListElement
}
}
\end{lstlisting}
Die Funktion \lstinline{sortRtTasks(...)} wird direkt nach Ausführen eines Realtime-Tasks
aufgerufen und ordnet das übergebene (standardmäßig das gerade ausgeführte) Element
der verknüpften Liste abhängig von seinem nächsten Ausführzeitpunkt in die Liste ein.
......@@ -780,11 +782,11 @@ Listen-Element existiert und ob der Zeitpunkt der nächsten Ausführung vor dem
des übergebene Elements liegt. Ist dies nicht der Fall, wird die Liste nicht modifiziert
und der Zeiger auf das aktuelle Element zurückgegeben. Ansonsten werden die Elemente
solange durchlaufen, wie die Ausführungszeit des betrachteten Tasks vor derjenigen
des übergebenen Tasks liegt oder die Liste zu Ende ist. Anschließend wird das
des übergebenen Tasks liegt oder bis die Liste zu Ende ist. Anschließend wird das
übergebene Element an der entsprechenden Stelle einsortiert und der Zeiger auf den
ehemals zweiten Task (der jetzt der als Nächstes auszuführende ist) zurückgegeben.
Somit wird im schlechtesten Fall eine Ordnung von O(n) erreicht, während der
äquivalente Vorgang in einem herkömmlichen Array eine Ordnung von O(n\^2) hätte.
Somit wird im schlechtesten Fall eine Ordnung von $O(n)$ erreicht, während der
äquivalente Vorgang in einem herkömmlichen Array eine Ordnung von $O(n^2)$ hätte.
\begin{lstlisting}[caption=schedule]
// Scheduler, called cyclical in the loop-function
......@@ -830,6 +832,7 @@ void Scheduler::schedule (void) {
}
}
\end{lstlisting}
Die Funktion \lstinline{schedule()} wird zyklisch vom Mikrocontroller aufgerufen und
bildet das Herzstück der Taskverwaltung. Grundsätzlich werden drei möglich Fälle unterschieden:
\begin{itemize}
......@@ -842,16 +845,16 @@ bildet das Herzstück der Taskverwaltung. Grundsätzlich werden drei möglich F
die für die Verwaltung der Nicht-Echtzeit-Tasks zuständig ist.}
\item{Es sind Realtime-Tasks vorhanden. In diesem Fall überprüft der Scheduler zunächst,
ob innerhalb des aktuellen Zyklus noch Zeit für die Ausführung von Non-Realtime-Tasks ist.
Hierzu wird die vorraussichtliche Ausführungszeit des nächsten Realitme-Tasks
mit der verbleibenden Zykluszeit verglichen. Ist noch Zeit übrig und sind Non-Realtime-Tasks
Hierzu wird der vorraussichtliche Ausführungszeitpunkt des nächsten Realitme-Tasks
mit der aktuellen Zykluszeit verglichen. Ist noch Zeit übrig und sind Non-Realtime-Tasks
vorhanden, so wird \lstinline{perform()} aufgerufen.
Ansonsten wird der Realtime-Task abgearbeitet, sein Timestamp wird aktualisiert
und er wird abhängig von seinem nächsten vorgesehenen Ausführungszeitpunkt mittels
\lstinline{sortRtTasks(...)} in die einfach verkettete Liste der Echtzeit-Tasks einsortiert.
Existieren weiterhin zu verwaltende Non-Realtime-Tasks, so wird abschließend die
\lstinline{sortRtTasks(...)} in die einfach verkettete Liste der Echtzeit-Tasks einsortiert. Existieren weiterhin zu verwaltende Non-Realtime-Tasks, so wird abschließend die
Funktion \lstinline{setPriorities()} aufgerufen, um gegebenenfalls mittels dynamischer
Prioritätenvergabe Starvation zu verhindern. Abschließend wird der Zeiger auf den
aktuellen Nicht-Echtzeit-Task zurückgesetzt.}
aktuellen Nicht-Echtzeit-Task zurückgesetzt. \\
Es handelt sich bei dieser Art der Taskverwaltung um einen reaktive Ansatz (auf den Ausführungszeitpunkt eines Echtzeit-Tasks wird nach dessen Eintreten reagiert).}
\end{itemize}
\begin{lstlisting}[caption=perform]
......@@ -867,6 +870,7 @@ void Scheduler::perform (void) {
}
}
\end{lstlisting}
Die Funktion \lstinline{perform()} verwaltet die Ausführung der Non-Realtime-Tasks.
\lstinline{taskCounter} zeigt dabei immer auf die Speicheradresse
des aktuellen Tasks. Bei jedem Aufruf der Funktion wird dieser abgearbeitet, sein
......@@ -894,6 +898,7 @@ void Scheduler::setPriorities (void) {
}
}
\end{lstlisting}
Mittels der Funktion \lstinline{setPriorities()} lassen sich während der Laufzeit
der Taskverwaltung durch Verschiebung der Non-Realtime-Tasks innerhalb des Arrays
temporär neue Prioritäten vergeben um so Starvation vorzubeugen.
......@@ -917,12 +922,15 @@ void Scheduler::exterminate (void){
while (true);
}
\end{lstlisting}
Mit Hilfe der Funktion \lstinline{exterminate()} kann der Scheduler beim Auftreten
eines Fehlers in einen definierten Zustand überführt werden. Erreicht wird dies
mittels des Eintretens in eine Dauerschleife. Somit wird verhindert, dass
unvorhersehbares Verhalten auftritt.
\section{Erstellung eines allgemeinen Mischer-Frameworks}
\chapter{Erstellung eines allgemeinen Mischer-Frameworks}
\section{Implementierung der Mischerfunktionalität}
In Anbetracht der großen Anzahl an bereits existierenden Protokollen zur Kommunikation
mit Modellbau-Empfängern und in Hinblick auf die Erweiterbarkeit und Portierbarkeit
dieses Projektes erschien es als angemessen und sinnvoll, den RC-Mischer modular
......@@ -933,15 +941,13 @@ ein möglichst allgemein gehaltenes Mischer-Framework, welches im Modellbau
\subsection{Implementierung der Inputs und Outputs}
Der hier vorgestellte Mischer soll mit Daten von unterschiedlichen Funk-Fernsteuerungen
aus dem Modellbau-Bereich umgehen können.
Wie in den Grundlagen bereits dargestellt, hat sich im Modellbau ein Wertebereich
von 1000µs-2000µs Pulsweite für die Ansteuerung von Servo-Motoren und Drehzahlstellern etabliert.
Obwohl dieser Wertebereich für das Mischen von Daten aus mehreren Quellen nicht ideal ist,
wurde sich aus Gründen der Kompatibilität zu etablierten Komponenten, für eine Beibehaltung
und konsequente Verwendung dieses Wertebereiches entschieden.
Die einzelnen Software-Komponenten tauschen die Kanaldaten über globale Variablen in Form eines Structs aus.
Dieses bietet Speicherplatz für bis zu 16 Kanäle sowie einen Zeitstempel zur Ermittlung der Aktualiät der gespeicherten Daten.
Der Datentyp des data[]-Arrays wurde gemäß dem abbzubildenden Wertebereich von 1000-2000
als uint16 gewählt.
Im Modellbau ist ein Wertebereich von 1000µs-2000µs Pulsweite für die Ansteuerung
von Servo-Motoren und Drehzahlstellern etabliert. Dieser Wertebereich ist für
das Mischen von Daten aus mehreren Quellen nicht ideal. Um die Kompatiblität zu
etablierten Komponenten zu wahren wurde sich dennoch für eine konsequente Verwendung
dieses Wertebereiches entschieden.
Die einzelnen Software-Komponenten tauschen die Kanaldaten über global definierte Structs aus.
Diese bietet Speicherplatz für bis zu 16 Kanäle sowie einen Zeitstempel.
\begin{lstlisting}[caption=RC.h]
#ifndef RC_h
......@@ -957,7 +963,6 @@ struct rcSource
#endif
\end{lstlisting}
\subsection{Realisierung des Mischerverhaltens}
Gehen wir nun davon aus, dass wir Daten aus unterschiedlichen Quellen in Form des
soeben vorgestellten Structs vorliegen haben. Diese sollen nun in einer Anwendungs-spezifischen
......@@ -979,6 +984,7 @@ class Mixer
rcSource *_out, *_in0, *_in1;
};
\end{lstlisting}
Wie in Mixer.h zu erkennen, liegt die einfachste Form eines Mischers vor. Daten
aus lediglich zwei Quellen werden eingelesen und zu einem einzigen ausgehenden
Datenstrom verarbeitet. Der Datenaustausch findet gemäß dem vorangegangenen Abschnitt
......@@ -1008,11 +1014,11 @@ void Mixer::mix()
_out->timestamp = micros();
}
\end{lstlisting}
Die Verarbeitung bzw. Vermischung der eingehenden Daten findet in der Funktion \lstinline{mix()} statt.
Die Verarbeitung bzw.\ Vermischung der eingehenden Daten findet in der Funktion \lstinline{mix()} statt.
Sicherheit im Sinne von Wahrung der manuellen Kontrolle über das System zu jedem
Zeitpunkt ist hier das Hauptaugenmerk. So befindet sich der Mischer zunächst in
einem inaktiven Zustand, in welchem er lediglich die an \lstinline{in0} vorliegenden Daten in
out kopiert. \lstinline{in0} stellt hierbei die von der Fernsteuerung empfangengen Daten dar.
einem inaktiven Zustand, in welchem er lediglich die an \lstinline{in0} vorliegenden Daten in \lstinline{out} kopiert. \lstinline{in0} stellt hierbei die von der Fernsteuerung empfangengen Daten dar.
Erst wenn ein frei definierbarer Kanal der \lstinline{in0} Quelle, also der Fernsteuerung mit
welcher das Fluggerät manuell geflogen wird, einen ebenso frei definierbaren Wert
überschreitet, werden die Daten aus der Sekundär-Quelle \lstinline{in1} mit den Daten aus der
......@@ -1026,12 +1032,15 @@ Andere Konfigurationen sind selbstverständlich möglich, sollte dies die konkre
Anwendung erfordern.
\section{Vernetzung der Systemkomponenten}
\begin{figure}[hb]
\centering
\includegraphics[width=\textwidth, trim = 0cm 1cm 0cm 1cm, clip]{graphics/diag-LeonardoMixerIO.pdf}
\caption{Blockdiagramm des Systems}
\label{fig:diag-LeonardoMixerIO}
\end{figure}Wie in Abbildung \ref{fig:diag-LeonardoMixerIO} zu erkennen, ist im Rahmen dieses Projektes die Kommunikation
\end{figure}
Wie in Abbildung \ref{fig:diag-LeonardoMixerIO} zu erkennen, ist im Rahmen dieses Projektes die Kommunikation
zwischen drei wesentlichen Komponenten herzustellen und aufrecht zu erhalten.
Im konventionellen Betrieb eines Flugmodells oder Quadrocopters besteht zwischen
Flugsteuerung und Fernsteuerungs-Empfänger eine direkte Verbindung.
......@@ -1044,9 +1053,9 @@ die Anbindung an die beiden verwendeten Quellen, und im Anschluss die Anbindung
die verwendete Flugsteuerung erläutert werden.
\subsection{Anbindung an die Android-Applikation}
Der Datenaustausch mit der Android-Applikation erfolgt uni-direktional, in Form
eines simplen byte-Streams über eine serielle Schnittstelle. Diese serielle Schnittstelle
ist im Falle des Arduino Leonardo bzw. des Atmega 32u4 als virtueller CDC-USB-ComPort
Der Datenaustausch mit der Android-Applikation erfolgt unidirektional, in Form
eines simplen Byte-Streams über eine serielle Schnittstelle. Diese serielle Schnittstelle
ist im Falle des Arduino Leonardo bzw.\ des Atmega 32u4 als virtueller CDC-USB-ComPort
realisiert.
Auf eine bi-direktionale Kommunikation wurde verzichtet, da ein erneutes Anfordern
potentiell ungültiger oder falsch übertragender Daten allenfalls zu einer Neuübertragung
......@@ -1082,9 +1091,10 @@ class RawSerial
uint16_t channelData[SERIAL_MAX_SUPPORTED_CHANNEL_COUNT];
};
\end{lstlisting}
Die Klasse \lstinline{RawSerial} ist bei Instanzierung mit einem Zeiger auf ein rcSource Objekt
im Konstruktor zu versehen. Dieses dient dem Datenaustausch mit Funktionen anderer Klassen.
Die Methode \lstinline{init(Serial_ *raw_Serial)} bzw. \lstinline{init(HardwareSerial *raw_Serial)}
Die Methode \lstinline{init(Serial_ *raw_Serial)} bzw.\ \lstinline{init(HardwareSerial *raw_Serial)}
dient der Übergabe und Initialisierung der zu verwendenen seriellen Schnittstelle.
Hier ist derzeit je nach verwendeter Art der seriellen Schnittstelle eine manuelle
Anpassung im Programmcode der Klasse notwendig, da im Falle des Arduino Leonardos
......@@ -1126,6 +1136,7 @@ void RawSerial::getData()
_source->timestamp = micros();
}
\end{lstlisting}
Die abgebildete Funktion \lstinline{getData()} skaliert den Wertebereich der empfangenden Daten
um den Faktor 4 auf 0-1024 und verschiebt diesen darüber hinaus um 1024, so dass
sich ein resultierender Wertebereich von 1024-2048 ergibt, welcher dem angestrebten
......@@ -1145,7 +1156,7 @@ und dem Mischer zur Verfügung zu stellen.
Die Wahl des verwendeten Funkempfängers fiel aus mehreren Gründen auf einen kompakten
und leichten Spektrum-kompatiblen Empfänger, einen sog.\ Satelliten-Empfänger.
Ausschlaggebend für diese Wahl war die Bereitstellung einer Implementierungs-Anleitung
von Seiten Spektrums bzw. Horizon Hobbys (vgl.\ \cite[][]{spek2016}). Darüber hinaus
von Seiten Spektrums bzw.\ Horizon Hobbys (vgl.\ \cite[][]{spek2016}). Darüber hinaus
findet das Spektrum DSMX Protokoll im europäischen und amerikanischen Raum große
Verbreitung und es stehen zahlreiche kompatible und preisgünstige Fernsteuerungen
zur Verfügung, welche dieses Protokoll unterstützen. Dies unterstützt das Ziel dieser
......@@ -1181,6 +1192,7 @@ class Spektrum
uint16_t channelData[SPEKTRUM_MAX_SUPPORTED_CHANNEL_COUNT];
};
\end{lstlisting}
Der Spektrum-Remote-Receiver überträgt die empfangenen Daten ebenfalls uni-direktional
mittels einer einfachen seriellen Schnittstelle gemäß der untenstehenden Spezifikation:
\begin{quote}
......@@ -1190,7 +1202,7 @@ For those UARTs which are not capable of that speed, they will also work at the
115200bps.
(vgl.\ \cite[][2]{spek2016})
\end{quote}Die Formatierung dieser empfangenen Daten unterscheidet sich grundlegend von dem
simplen Protokoll, welches im vorrangegangenen Abschnitt vorgestellt wurde.
simplen Protokoll, welches im vorangegangenen Abschnitt vorgestellt wurde.
Sie ist ausführlich in dem von Horizon Hobby, LLC herausgegebenen Dokument beschrieben:
\begin{quote}
[...][T]he first two bytes (fieldname “fades” in Section 8.4 below) indicate the fade
......@@ -1201,6 +1213,9 @@ Sie ist ausführlich in dem von Horizon Hobby, LLC herausgegebenen Dokument besc
is the msb.
(vgl.\ \cite[][3,4]{spek2016})
\end{quote}
\newpage
\begin{lstlisting}[caption=Spektrum::dataReceive()]
void Spektrum::dataReceive()
{
......@@ -1221,6 +1236,7 @@ void Spektrum::dataReceive()
}
}
\end{lstlisting}
Die obenstehende Methode \lstinline{dataReceive()} liest zunächst die vom Empfänger
geschriebenen Daten aus dem Puffer der seriellen Schnittstelle. Anschließend werden
diese nach folgendem Schema ausgelesen und klassenintern in \lstinline{channelData[]} gespeichert:
......@@ -1259,6 +1275,8 @@ Eintrag in dem MultiWii-Wiki (vgl.\ \cite[][]{multiWii2015}) lediglich eine Übe
des MSP in Cleanflight zurückgegriffen, um sich anhand dieser ein Verständnis der
Funktionsweise der Datenübertragung mittels MSP zu erarbeiten.
\newpage
\begin{lstlisting}[caption=MultiWiiSerial.h - Auszug]
#define MSP_MAX_SUPPORTED_CHANNEL_COUNT 16
#define MSP_FRAME_SIZE 16*2 // two bytes per channel
......@@ -1281,6 +1299,7 @@ class MultiWiiSerial
uint16_t channelData[MSP_MAX_SUPPORTED_CHANNEL_COUNT];
};
\end{lstlisting}
Die Klasse \lstinline{MultiWiiSerial} wird analog zu den bereits vorgestellten
Empfangs-Klassen instanziiert. Der \lstinline{void init(HardwareSerial *msp_Serial)}
Methode wird die Referenz auf eine serielle Schnittstelle übergeben.
......@@ -1304,12 +1323,11 @@ crc & = XOR of <size>, <command> and each data byte into a zero'ed sum \\
\end{tabular}
\cite[][]{multiWii2015}
\end{quote}
Die Implementierung dieses Protokolls gestaltet sich als verhältnismäßig einfach,
Die Implementierung dieses Protokolls gestaltet sich als verhältnismäßig einfach;
die folgende Methode \lstinline{SerialEncode()} ist speziell und ausschließlich
für das Bilden und Senden sogenannter \glqq{}MSP\_SET\_RAW\_RC\grqq{}-Pakete
(message\_id = 200, vgl.\ \cite[][]{multiWii2015}) geschrieben.
\pagebreak
\begin{lstlisting}[caption=MultiWiiSerial::SerialEncode()]
void MultiWiiSerial::SerialEncode()
{
......@@ -1352,9 +1370,8 @@ des Mischerverhaltens, genauer der Gewichtung der vom Mobiltelefon empfangenen K
kann zur Laufzeit und aus der Ferne über einen freien Proportional-Kanal der verwendeten
Fernsteuerung erfolgen. Dies erwies sich während der Testflüge als ausgesprochen hilfreich.
Die Parametrisierung des Schedulers ist nicht zur Laufzeit möglich, dies wäre für
dieses Projekt jedoch auch nicht zielführend.
Sie erfolgt innerhalb der \lstinline{setup()} Methode der LeonardoMixerIO.cpp:
Die Parametrisierung des Schedulers erfolgt innerhalb der \lstinline{setup()} Methode der LeonardoMixerIO.cpp:
\begin{lstlisting}[caption=Parametrisierung des Schedulers]
nRtTask42.priority = 3;
nRtTask42.activity = doSomething_Function;
......@@ -1367,10 +1384,12 @@ Sie erfolgt innerhalb der \lstinline{setup()} Methode der LeonardoMixerIO.cpp:
scheduler = new Scheduler(rtTasks, nRtTasks);
//scheduler->setDebugger(&Serial);
\end{lstlisting}
Die hierbei hervorzuhebenden Parameter sind die \lstinline{cycleTime} der
echtzeitkritischen Tasks sowie die \lstinline{priority} der nicht-echtzeitkritischen
Tasks.
Diese wurden für den Flugbetrieb wie folgt gewählt:
\begin{lstlisting}
readRawSerial.priority = 1;
readSpektrum.priority = 1;
......@@ -1379,6 +1398,7 @@ Diese wurden für den Flugbetrieb wie folgt gewählt:
send.cycleTime = 20000;
\end{lstlisting}
Mit den Funktionen, welche Daten empfangen und das lokale Daten-Abbild aktualisieren
als den höchstprioren nicht-echtzeitkritischen Tasks, dem eigentlichen Mischer als
nächst-priorem und der LCD-Ausgabe (nur im stationären Laborbetrieb im Rahmen des Debugging)
......@@ -1387,7 +1407,7 @@ Das Senden der Daten an die Flugsteuerung ist echtzeit-kritisch und erfolgt mit
einer vorgegebenen Zykluszeit von 20ms.
\section{Testbedingungen}
Getestet wurde der Mischer auf der im Rahmen des Schwesterprojektes VBLS genutzten
Getestet wurde der Mischer auf der im Rahmen des Parallelprojekts \glqq{}VBLS\grqq{} \footnote{VBLS - \url{https://gitlab.cvh-server.de/lf.ps/vbls/tree/master/Visual-Based-Landing-System}} genutzten
Flugplattform, einem Quadrocopter in X-Anordung.
In Abbildung \ref{fig:pic-quad-detail-sexy} gut zu erkennen ist der schichtweise Aufbau des
......@@ -1443,25 +1463,14 @@ Nicht nur konnte eine Vernetzung der Einzelkomponenten RC-Empfänger, Mobiltelef
und Flugsteuerung hergestellt und im Testbetrieb aufrecht erhalten werden, diese
konnte sich auch in mehreren Testflügen über einen Zeitraum von insgesamt mehr als
60 Minuten bewähren.
\section{Persönliches Fazit}
Ergänzend zu dem vorangegangenen inhaltlichen Fazit möchten wir zum Ende dieser
Dokumentation noch kurz unser persönliches Fazit ziehen.
Die Entwicklung dieses Systems hat uns als Autoren der hierfür nötigen Software
und dieser Dokumentation einiges an Zeit gekostet. Zusammenfassend lässt sich jedoch
sagen, dass diese Zeit ausgesprochen sinnvoll investiert ist und die gewonnenen
Erkenntnisse und Erfahrungen den nötigen Aufwand mehr als rechtfertigen. Auch konnten
wir als Team von einem regen Wissens- und Erfahrungsaustausch untereinander profitieren.
So konnte jeder von uns sein eigenes Wissen und seine persönlichen Fähigkeiten voll
in dieses Projekt einbringen. Dies freut uns sehr.
\todo{Bezug auf gemessene Zykluszeiten nehmen}
\chapter{Ausblick}
Wie in der Zusammenfassung bereits verdeutlich, konnten alle Projektziele erreicht
werden. Der modulare Aufbau dieses Projektes bzw. des diesem zugrunde liegenden Programms
werden. Der modulare Aufbau dieses Projektes bzw.\ des diesem zugrunde liegenden Programms
lädt jedoch zu Erweiterungen und Modifikationen ein:
\begin{itemize}
\item{Implementierung eines präventiven (im Gegensatz zum vorhandenen reaktiven) Ansatz der Taskverwaltung, d.\,h.\ lediglich Ausführen eines Nicht-Echtzeit-Tasks, wenn dessen durchschnittlich benötigte Ausführungszeit geringer ist, als die Differenz zwischen aktuellem Zeitpunkt und dem Ausführungszeitpunkt des nächsten Echtzeit-Tasks. Somit könnte künstlich nahezu harte Echtzeit geschaffen werden}
\item{Erweiterung um weitere Protokolle}
\item{Erweiterung des eigentlichen Mischer-Codes, beispielsweise um eine Möglichkeit
der Parametrisierung zur Laufzeit, etwa mittels eines Kommandozeileninterfaces.
......@@ -1477,6 +1486,21 @@ bereitgestellt werden können.}
\printbibliography[heading = bib]
\chapter{Anhang}
\section{Persönliches Fazit}
Ergänzend zu dem vorangegangenen inhaltlichen Fazit möchten wir zum Ende dieser
Dokumentation noch kurz unser persönliches Fazit ziehen.
\\
\\
Die Entwicklung dieses Systems hat uns als Autoren der hierfür nötigen Software
und dieser Dokumentation einiges an Zeit gekostet. Zusammenfassend lässt sich jedoch
sagen, dass diese Zeit ausgesprochen sinnvoll investiert ist und die gewonnenen
Erkenntnisse und Erfahrungen den nötigen Aufwand mehr als rechtfertigen. Auch konnten
wir als Team von einem regen Wissens- und Erfahrungsaustausch untereinander profitieren.
So konnte jeder von uns sein eigenes Wissen und seine persönlichen Fähigkeiten voll
in dieses Projekt einbringen. Dies freut uns sehr.
\newpage
\section{Lizenzen}
Der im Rahmen dieses Projektes geschaffene Programmcode steht ebenso wie diese Dokumentation
unter der Modifizierten BSD Lizenz (\url{https://gitlab.cvh-server.de/lf.ps/vbls/blob/master/common/BSD-MODIFIED.txt}).
......@@ -1523,7 +1547,9 @@ Freeware \\
Proprietär \& Kommerziell \\
\url{http://labs.adobe.com/technologies/eula/lightroom.html}}
\end{itemize}
\newpage
\section{gitlab-Repository}
Der im Rahmen dieses Projektes entstandene Quellcode sowie die hier vorliegende
Dokumentation können über den gitlab-Server des Campus Velbert-Heiligenhaus
......
......@@ -4,7 +4,7 @@
%% Page layout and dimensions
\RequirePackage[a4paper,top=32mm,bottom=3cm,left=3cm,right=3cm,marginparwidth=1.75cm,headheight=12mm,footskip=12mm]{geometry}
%% Language and font encodings
\RequirePackage[rm, light, sfdefault]{roboto}
\RequirePackage[rm, light, sfdefault]{roboto} %%TODO:
\RequirePackage[T1]{fontenc}
%% Packages
......@@ -91,10 +91,10 @@
{0pt}{-12pt}{12pt}
\titleformat{\section}
{\fontfamily{\sfdefault}\mdseries\fontsize{12}{15}\color{BO}}{\thesection}{1em}{#1} % without #1 the section title disappears, since [explicit]{titlesec} is used
{\fontfamily{\sfdefault}\bfseries\fontsize{13}{15}\color{BO}}{}{0em}{\thesection\hspace{1em}#1} % without #1 the section title disappears, since [explicit]{titlesec} is used
\titleformat{\subsection}
{\fontfamily{\sfdefault}\mdseries\color{BO}}{\thesubsection}{1em}{#1}
{\fontfamily{\sfdefault}\fontseries{m}\selectfont\color{BO}}{}{0em}{\thesubsection\hspace{1em}#1}
%% Custom titlepage (redefines \maketitle, might not be up to LaTeX standards!)
\renewcommand{\maketitle}{
......
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{layout/BOmodern}[2016/11/02 BOmodern Design Package - Philipp Stenkamp]
%% Page layout and dimensions
\RequirePackage[a4paper,top=32mm,bottom=3cm,left=3cm,right=3cm,marginparwidth=1.75cm,headheight=12mm,footskip=12mm]{geometry}
%% Language and font encodings
\RequirePackage[rm, sfdefault]{roboto}
\RequirePackage[T1]{fontenc}
%% Packages
\RequirePackage{hyperref}
\RequirePackage{fancyhdr}
\RequirePackage{xcolor}
\RequirePackage[explicit]{titlesec} %customize chapters and sections
\RequirePackage[absolute]{textpos}
%% Copy author and title to use them in the header and footer
\makeatletter
\let\runauthor\@author
\let\runtitle\@title
\makeatother
%% Defines the colors used for the design elements
\definecolor{pagenum}{rgb}{1,0.75,0}
\definecolor{BO}{RGB}{227,27,0}
%% Redefining pagestyle, headers and footers
\pagestyle{empty}
\pagestyle{fancy}
\fancypagestyle{plain}{} % makes headers and footers appear also on pages containing chapter headlines
\renewcommand{\familydefault}{\rmdefault}
\fancyhead{} % clear all header fields
\fancyhead[RO]{\fontfamily{\sfdefault}\mdseries\textbf{\runcourse}\\\runcategory}
\fancyhead[LE]{\includegraphics[height=10mm]{layout/logo-hochschule-bochum-text.pdf}\hspace{3mm}\includegraphics[height=10mm]{layout/logo-hochschule-bochum-bo.pdf}}
\fancyfoot{} % clear all footer fields
\fancyfoot[LO]{\parbox{\textwidth}{\fontfamily{\sfdefault}\mdseries\MakeUppercase{\runtitle}}}
\fancyfoot[RE]{\parbox{\textwidth}{\fontfamily{\sfdefault}\mdseries\MakeUppercase{\runauthor}}}
\fancyfoot[LE,RO]{\colorbox{pagenum}{\color{white}\parbox[c][10mm-4\fboxsep][c]{10mm-4\fboxsep}{\fontfamily{\sfdefault}\centering\bf\thepage}}} % page number in orange square
\fancyfootoffset[LE,RO]{12mm} % offsets the page number 12 mm to the side
\renewcommand{\headrulewidth}{0pt} % removes the seperating line between header and page
\renewcommand{\footrulewidth}{0pt} % removes the seperating line between footer and page
% Redefinition of ToC heading (Workaround, would be cleaner to change the formatting of the \contentsname, not its value
\addto\captionsgerman{
\renewcommand{\contentsname}%
{INHALTSVERZEICHNIS}%
}
% Redefinition of LoF heading; anaolog to the redefinition of the ToC-heading
\addto\captionsgerman{
\renewcommand{\listfigurename}%
{ABBILDUNGSVERZEICHNIS}%
}
% Redefinition of LoL heading; anaolog to the redefinition of the ToC-heading
\addto\captionsgerman{
\renewcommand{\lstlistlistingname}%
{LISTINGS}%
}
%% Custom chapters and sections
\newlength\widestnum
\setlength\widestnum{1.5em}
\titleformat{\chapter}[hang]
{\fontfamily{\sfdefault}\fontseries{m}\color{white}\fontsize{12}{12}\uppercase}
{}
{0em}
{%
\makebox[\linewidth]{%
\colorbox{BO}{%
\parbox{\textwidth+2\fboxsep}{%
\ifnum\thechapter=0
\hspace{\fboxsep}%
\parbox[t]{\dimexpr\textwidth-\widestnum\relax}{\strut#1\strut}%
\else
\hspace{\fboxsep}\parbox[t]{\widestnum}{\strut\thechapter\strut}%
\parbox[t]{\dimexpr\textwidth-\widestnum\relax}{\strut#1\strut}%
\fi
}%
}%
}%
}
\titlespacing*{\chapter}
{0pt}{-12pt}{12pt}
\titleformat{\section}
{\fontfamily{\sfdefault}\fontseries{m}\fontsize{12}{15}\color{BO}}{\thesection}{1em}{#1} % without #1 the section title disappears, since [explicit]{titlesec} is used
\titleformat{\subsection}
{\fontfamily{\sfdefault}\fontseries{m}\color{BO}}{\thesubsection}{1em}{#1}
%% Custom titlepage (redefines \maketitle, might not be up to LaTeX standards!)
\renewcommand{\maketitle}{
\begin{titlepage}
\vspace*{-20mm}
\includegraphics[height=15mm]{layout/logo-hochschule-bochum-text.pdf}\hspace{3mm}\includegraphics[height=15mm]{layout/logo-hochschule-bochum-bo.pdf}
\vspace*{-21mm} % crude way to vertically align the text with the logo, alternatives using tabu and tabularx did not work as desired
\begin{flushright}
\LARGE
\MakeUppercase\today\\
\vfill % stretches the vertical space between date and title to the maximum
\huge
\fontseries{l}
\MakeUppercase\runtitle
\LARGE
\vskip\baselineskip
\MakeUppercase\runcategory
\normalsize
\fontseries{\seriesdefault}
\vskip3\baselineskip
HOCHSCHULE BOCHUM\\
Campus Velbert/Heiligenhaus\\
Höseler Platz 2\\
42579 Heiligenhaus
\end{flushright}
\begin{textblock*}{5mm}(\paperwidth-25mm,15mm)
{\color{BO}\rule{5mm}{\paperheight-40mm}}
\end{textblock*}
\begin{textblock*}{5mm}(\paperwidth-25mm,\paperheight-20mm)
{\color{pagenum}\rule{5mm}{5mm}}
\end{textblock*}
\end{titlepage}
}
No preview for this file type
......@@ -12,6 +12,11 @@
\def\runcourse{Eingebettete Systeme}
\def\runcategory{Projektdokumentation}
%% Allow for a greater part of the page to be occupied by figures
\renewcommand{\topfraction}{0.7}
\renewcommand{\bottomfraction}{0.7}
\renewcommand{\textfraction}{0.3}
%% Packages
\usepackage{layout/BOmodern} % modern HS Bochum themed document template designed for assignments and documentations
\usepackage{amsmath}
......@@ -53,13 +58,12 @@
\lstlistoflistings
\chapter{Einführung}
\pagenumbering{arabic}
\section{Motivation}
Längst hat der Trend der zunehmenden Automatisierung nicht nur in der Industrie sondern auch im normalen Alltag Einzug gehalten. In Fabriken übernehmen Roboter verschiedenste Aufgaben von der Montage bis hin zu Qualitätskontrolle, auf den Straßen sind Autos in der Lage, Hindernisse selbstständig zu erkennen und eine Notbremsung einzuleiten und in Gebäuden passt sich die Beleuchtung automatisch dem aktuellen Betriebszustand an. Doch um diese Funktionen umsetzen zu können, ist eine geeignete Sensorik bzw. Datenerfassung von essentieller Bedeutung.
Längst hat der Trend der zunehmenden Automatisierung nicht nur in der Industrie sondern auch im normalen Alltag Einzug gehalten. In Fabriken übernehmen Roboter verschiedenste Aufgaben von der Montage bis hin zu Qualitätskontrolle, auf den Straßen sind Autos in der Lage, Hindernisse selbstständig zu erkennen und eine Notbremsung einzuleiten und in Gebäuden passt sich die Beleuchtung automatisch dem aktuellen Betriebszustand an. Doch um diese Funktionen umsetzen zu können, ist eine geeignete Sensorik bzw.\ Datenerfassung von essentieller Bedeutung.
\\
Einen Teil dieses Gebiets, der zunehmend an Bedeutung gewinnt, stellt die Bildverarbeitung dar. Für viele Problemstellungen mit Variablen erweist sich diese Art der Datenerfassung als geeigneter als die klassische Sensorik oder gar als einzig mögliche Lösung. In der Industrie kann somit beispielsweise ein Produkt bei der Qualitätskontrolle auf ein bestimmtes Merkmal überprüft werden oder der korrekte Bestückungszustand eines Förderbands kann aus Sicherheitszwecken validiert werden, bevor ein Roboter die Werkstücke greift. Im Alltag kann diese Technik unter anderem bei der erwähnten Hinderniserfassung durch ein Auto zur Verwendung kommen.
Gerade im Bereich der Robotik, wo man bei der Entwicklung mobiler Roboter häufig das Problem unbekannter Einsatzumgebungen hat, bieten sich viele Einsatzmöglichkeiten für die Bildverarbeitung, wie zum Beispiel ein Multikopter, welcher von alleine Ladestationen lokalisieren und ansteuern kann, um so über längere Zeit hinweg autonom agieren zu können \cite[vgl.\ ][S.1]{vblsArticle2015}.
......@@ -69,10 +73,10 @@ Mit dieser Motivation behandelt diese Arbeit einen einfach zugänglichen, flexib
\section{Zielsetzung}
\label{ziel}
Ziel dieser Arbeit ist die Entwicklung einer Optical-Flow-basierten Applikation, welche eingehende Bilddaten einer Kamera verarbeiten und definierte Umgebungsmerkmale erfassen und herausstellen kann. Anhand dieser soll anschließend ein Ausgangssignal abhängig von der aktuellen Position des Merkmals im Bild erzeugt werden, welches die Steuerung einer gekoppelten Anwendung übernehmen oder beeinflussen kann (vgl.\ Spurhalteassistenzsysteme in einem Auto).
Ziel dieser Arbeit ist die Entwicklung einer Optical-Flow-basierten Applikation, welche eingehende Bilddaten einer Kamera verarbeiten und definierte Umgebungsmerkmale erfassen und herausstellen kann. Anhand dieser soll anschließend ein Ausgangssignal abhängig von der aktuellen Position des Merkmals im Bild erzeugt werden, welches bspw.\ die Steuerung einer gekoppelten Anwendung übernehmen oder beeinflussen kann (vgl.\ Spurhalteassistenzsysteme in einem Auto).
Konkret soll als Zielplattform ein Smartphone auf Android-Basis sowie die Open Source Bibliothek OpenCV verwendet werden. Als Interface soll die USB-Schnittstelle dienen, da die Umwandlung von diesem Format in die meisten etablierten seriellen Datenprotokolle wie beispielsweise UART sehr einfach und kostengünstig umsetzbar ist und ein Großteil der gängigen Entwicklungsplattformen den Standard bereits von sich aus integriert.
\\
Zu Demonstrationszwecken soll neben einem allgemeinen Framework zur Entwicklung von OpenCV gestützten Bildverarbeitungsapplikationen für Android-Plattformen eine Landeplatzlokalisierung eines UAVs (Abk., engl. für Unmanned Aerial Vehicle) realisiert werden. Als zu erfassendes Merkmal fungiert zu diesem Zweck ein Kreis (vgl.\ Landeplatzsymbol eines Helikopters) und als Ausgabesignal eines sich unter dem Flugvehikel befindenden Smartphones soll die Stellgröße eines in die Applikation zu integrierenden Reglers dienen. Ziel ist es, dass das UAV sich autonom und ohne weitere Kenntnis über seine Umgebung über einem Landeplatz zu zentriert.
Zu Demonstrationszwecken soll neben einem allgemeinen Framework zur Entwicklung von OpenCV-gestützten Bildverarbeitungsapplikationen für Android-Plattformen eine Landeplatzlokalisierung eines UAVs (Abk., engl.\ für Unmanned Aerial Vehicle) realisiert werden. Als zu erfassendes Merkmal fungiert zu diesem Zweck ein Kreis (vgl.\ Landeplatzsymbol eines Helikopters). Als Ausgabesignal eines sich unter dem Flugvehikel befindenden Smartphones soll die Stellgröße eines in die Applikation zu integrierenden Reglers dienen. Ziel ist es, dass das UAV sich autonom und ohne weitere Kenntnis über seine Umgebung über einem Landeplatz zu zentriert.
Hierbei muss aus Sicherheitsaspekten ein manuelles Eingreifen durch den Menschen jederzeit möglich sein, weshalb eine signalverarbeitende Instanz in Form eines Mischers, welcher die Signale der Fernbedienung und der Applikation überlagert, zu integrieren ist.
\chapter{Grundlagen}
......@@ -83,7 +87,7 @@ Hierbei muss aus Sicherheitsaspekten ein manuelles Eingreifen durch den Menschen
\label{introductionAndroid}
Android Inc. wurde 2003 von Andy Rubin gegründet, ursprünglich mit der Bestrebung, Software für Digitalkameras zu entwickeln. Bald stellte sich jedoch heraus, dass der Markt auf diesem Gebiet nicht groß genug war, so dass Rubin die Zielstellung zur Entwicklung eines für jeden Entwickler frei zugänglichen Betriebssystems für mobile Plattformen verallgemeinerte \cite[vgl.\ ][]{foundingAndroidNYTimes2007}. Diese Idee stieß weitgehend auf große Begeisterung, bis schließlich im Juli 2005 Google Android Inc. für 50 Millionene US-Dollar aufkaufte, um seine mobile Sparte zu erweitern \cite[vgl.\ ][]{googleBuysAndroidNYTimes2015}. Dem ursprünglichen Ziel von Rubin folgend, gründete sich am 5. November 2007 die Open Handset Alliance bestehend aus Unternehmen wie Sony, Samsung, HTC, T-Mobile, Qualcomm, Texas Instruments und Google selbst mit dem Ziel, einen offenen Standard für Mobilgeräte zu entwickeln - mit Android als Grundlage \cite[vgl.\ ][]{openPlatformMobileDevicesOpenhandsetalliance2007}. Daraufhin erschien am 22. Oktober 2008 das HTC Dream als erstes kommerzielles Mobilgerät auf Android-Basis.
Seit diesem Zeitpunkt hat sich Android als weltweit \cite[vgl.\ ][]{aboutAndroid} am Weitesten verbreitete Betriebssystem für mobile Systeme durchgesetzt mit inzwischen mehr als 1,4 Millarden aktiven Nutzern \cite[vgl.\ ][ Stand\ September\ 2015]{monthlyAndroidUsersAndroidcentral2015}. Seit der Veröffentlichung von Android Base, dem ersten offiziellen Release, hat das Betriebssystem eine Vielzahl von Varianten durchlaufen, bis hin zur derzeit (03.11.2016) aktuellsten Version 7.1.1 mit dem Namen Nougat. Neben der offiziellen Version von Google existieren weiterhin eine Vielzahl von modifizierten Distributionen des Betriebssystem wie beispielsweise CyanogenMod.
Seit diesem Zeitpunkt hat sich Android als das weltweit am Weitesten verbreitete Betriebssystem für mobile Systeme \cite[vgl.\ ][]{aboutAndroid} durchgesetzt mit inzwischen mehr als 1,4 Millarden aktiven Nutzern \cite[vgl.\ ][ Stand\ September\ 2015]{monthlyAndroidUsersAndroidcentral2015}. Seit der Veröffentlichung von Android Base, dem ersten offiziellen Release, hat das Betriebssystem eine Vielzahl von Varianten durchlaufen, bis hin zur derzeit (03.11.2016) aktuellsten Version 7.1.1 mit dem Namen Nougat. Neben der offiziellen Version von Google existieren weiterhin eine Vielzahl von modifizierten Distributionen des Betriebssystem wie beispielsweise CyanogenMod.
\begin{figure}[htbp]
\centering
......@@ -94,11 +98,23 @@ Seit diesem Zeitpunkt hat sich Android als weltweit \cite[vgl.\ ][]{aboutAndroid
Einen großen Anteil an der erfolgreichen Verbreitung von Android hatte (und hat) das frei zugängliche und gut dokumentierte von Google zur Verfügung gestellte Interface zur Erstellung von Applikationen in Kombination mit dem Google Play Store als zentraler Distributionsplattform.
Das Interface kann in Form des Android SDK (Abk., engl. für \glqq{}Software Development Kit\grqq{}) (und ggf.\ des NDK (Abk., engl. für \glqq{}Native Development Kit\grqq{})) direkt von Google bezogen und in viele Entwicklungsumgebungen wie beispielsweise Eclipse direkt integriert werden. Seit Mai 2013 stellt Google mit Android Studio eine eigene IDE (Abk., engl. für \glqq{}Integrated Development Enviroment\grqq{}) zur Verfügung \cite[vgl.\ ][]{androidStudioAndroid2016}.
Das Interface kann in Form des Android SDK (Abk., engl.\ für \glqq{}Software Development Kit\grqq{}) (und ggf.\ des NDK (Abk., engl.\ für \glqq{}Native Development Kit\grqq{})) direkt von Google bezogen und in viele Entwicklungsumgebungen wie beispielsweise Eclipse direkt integriert werden. Seit Mai 2013 stellt Google mit Android Studio eine eigene IDE (Abk., engl.\ für \glqq{}Integrated Development Enviroment\grqq{}) zur Verfügung \cite[vgl.\ ][]{androidStudioAndroid2016}.
Android trennt bei der Entwicklung sehr stark zwischen dem funktionalen Quellcode und der GUI. Letztere wird in XML-Dateien beschrieben und anschließend mit Funktionen belegt. Zentrale, für die Funktionsfähigkeit der Applikation essentielle Informationen wie beispielsweise Berechtigungen werden dem System mittels des Android-Manifests (ebenfalls in XML) zur Verfügung gestellt \cite[vgl.\ ][]{manifestAndroid2016}.
\\
Der funktionale Anteil der Anwendung wird in Java geschrieben und implementiert die durch das SDK bereitgestellte Grundstruktur in Form des Activity Lifecycles (vgl.\ Abbildung \ref{fig:activity_lifecycle}). Activities stellen das Herzstück einer Anwendung dar und repräsentieren voneinander unabhängige Benutzerschnittstellen, die in Kombination die Anwendung bilden. Den Activity Lifecycle bildet ein Satz von Callback-Methoden, bestehend aus \lstinline{onCreate()} (Erstmaliges Starten der Activity), \lstinline{onStart()} (Initiierung der Activity), \lstinline{onPause()} (eine andere Applikation kommt in den Vordergrund), onResume() (pausierte Activity läuft weiter), \lstinline{onStop()} (Activity ist nicht länger sichtbar), \lstinline{onDestroy()} (Activity wird beendet oder vom System zerstört) und \lstinline{onRestart()} (Activity wird nach dem Stoppen erneut aufgerufen) und verwaltet den Lebenszyklus der Activity \cite[vgl.\ ][]{activityAndroid2016}. Weiterhin können aus einer Activity Services (über längere Zeit im Hintergrund fungierende Funktionen, bspw. Kommunikation mit einem USB-Gerät), Content Providers (Funktionen, um von mehreren Instanzen verwendete Datenquellen wie bspw. SQLite Datenbanken zu verwalten), Broadcast-Receiver und Handler (Funktionen, die systemweite Nachrichten (sogenannte Intents für Anfragen er Messages für reine Mitteilungen) filtern und je nach Konfiguration bei gewissen Nachrichten Aktionen auslösen) oder andere Activities aufgerufen werden.
Der funktionale Anteil der Anwendung wird in Java geschrieben und implementiert die durch das SDK bereitgestellte Grundstruktur in Form des Activity Lifecycles (vgl.\ Abbildung \ref{fig:activity_lifecycle}). Activities stellen das Herzstück einer Anwendung dar und repräsentieren voneinander unabhängige Benutzerschnittstellen, die in Kombination die Anwendung bilden. Den Activity Lifecycle bildet ein Satz von Callback-Methoden, bestehend aus
\begin{itemize}
\item{\lstinline{onCreate()} (Erstmaliges Starten der Activity)}
\item{\lstinline{onStart()} (Initiierung der Activity)}
\item{\lstinline{onPause()} (eine andere Applikation kommt in den Vordergrund)}
\item{\lstinline{onResume()} (pausierte Activity läuft weiter)}
\item{\lstinline{onStop()} (Activity ist nicht länger sichtbar)}
\item{\lstinline{onDestroy()} (Activity wird beendet oder vom System zerstört)}
\item{\lstinline{onRestart()} (Activity wird nach dem Stoppen erneut aufgerufen)}
\end{itemize}
Der Activity Lifecycle verwaltet den Lebenszyklus der Activity \cite[vgl.\ ][]{activityAndroid2016}. Weiterhin können aus einer Activity Services (über längere Zeit im Hintergrund agierende Funktionen, bspw.\ Kommunikation mit einem USB-Gerät), Content Providers (Funktionen, um von mehreren Instanzen verwendete Datenquellen wie bspw.\ SQLite Datenbanken zu verwalten), Broadcast-Receiver und Handler (Funktionen, die systemweite Nachrichten (sogenannte Intents für Anfragen er Messages für reine Mitteilungen) filtern und je nach Konfiguration bei gewissen Nachrichten Aktionen auslösen) oder andere Activities aufgerufen werden.
\\
\\
Neben der klassischen Programmierung in Java bietet das NDK weiterhin die Option, Elemente nativen Codes mittels des Java Native Interfaces zu implementieren (siehe Kapitel \ref{jni}).
......@@ -108,13 +124,13 @@ Die eingängige Struktur des Interface erlaubt es auch weniger erfahrenen Progra
\subsection{Warum Android?}
Wie bereits in der Einleitung dargestellt, ist das Ziel der Arbeit, einen einfach zugänglichen, flexibel erweiterbaren und kostengünstigen Ansatz mit variablen Einsatzgebieten zu erstellen, welcher auch Privatpersonen ohne ausgeprägte Programmier-Vorkenntnisse die Verwendung von Bildverarbeitungssoftware ermöglichen soll. Während die Einrichtung von Alternativplattformen wie beispielsweise eines Raspberry Pi eine relativ große Herausforderung für Personen ohne Vorkenntnisse darstellen kann, ist es verhältnismäßig einfach, mittels der Tutorials von OpenCV, Android Studio oder Eclipse und dem im Rahmen dieses Projekts erstellten Frameworks eine lauffähige Anwendung zu erstellen, da sich der eigentliche Programmieraufwand lediglich auf den reinen Bildverarbeitungsanteil beschränkt. Einen weiteren Vorteil bietet das Smartphone als Zielgerät an sich, da es im Normalfall ein Kameramodul, einen Bildschirm, diverse Kommunikationsschnittstellen und ein fertiges internes Bussystem mit sich bringt und sehr kompakt und leicht ist. Weiterhin wird durch die weite Verbreitung von Android mit mehr als 1,4 Millarden Geräten weltweit \cite[vgl.\ ][ Stand\ September\ 2015]{monthlyAndroidUsersAndroidcentral2015} die einfache Zugänglichkeit für jeden Nutzer sichergestellt.
Wie bereits in der Einleitung dargestellt, ist das Ziel der Arbeit, einen Ansatz mit variablen Einsatzgebieten zu erarbeiten, der auch Privatpersonen ohne ausgeprägte Programmier-Vorkenntnisse die Verwendung von Bildverarbeitungssoftware ermöglichen soll. Die Einrichtung von Alternativplattformen wie bspw.\ eines Raspberry Pi kann eine relativ große Herausforderung für Personen ohne Vorkenntnisse darstellen. Dem hingegen ist es verhältnismäßig einfach, mittels der Tutorials von OpenCV, Android Studio oder Eclipse und des im Rahmen dieses Projekts erstellten Frameworks eine lauffähige Android-Applikation zu erstellen, da sich der eigentliche Programmieraufwand lediglich auf den reinen Bildverarbeitungsanteil beschränkt. Einen weiteren Vorteil bietet das Smartphone als Zielgerät an sich, da es im Normalfall ein Kameramodul, einen Bildschirm, diverse Kommunikationsschnittstellen und ein fertiges internes Bussystem mit sich bringt und sehr kompakt und leicht ist. Weiterhin wird durch die weite Verbreitung von Android mit mehr als 1,4 Millarden Geräten weltweit \cite[vgl.\ ][ Stand\ September\ 2015]{monthlyAndroidUsersAndroidcentral2015} die einfache Zugänglichkeit für jeden Nutzer sichergestellt.
\section{OpenCV}
\subsection{Einführung}
OpenCV (Abk. für \glqq{}Open Source Computer Vision Library\grqq{}) wurde ursprünglich 1999 als Forschungsprojekt von Intel im Zuge der Entwicklung kommerziellen Bildverarbeitungsanwendungen ins Leben gerufen \cite[vgl.\ ][]{aboutOpenCVWillowGarage}. Im Laufe der Zeit wechselte das Projekt mehrfach den Entwickler, bis es schließlich im August 2012 von der nicht-profitablen Organisation OpenCV.org übernommen wurde, unter Anderem mit itseez, welches erst im Mai 2016 von Intel für sein Know-How im Bereich \glqq{}Computer Vision for IOT\grqq{} aquiriert wurde, als treibende Kraft im Hintergrund \cite[vgl.\ ][]{intelBuysItseezIntel2016}.
OpenCV (Abk. für \glqq{}Open Source Computer Vision Library\grqq{}) wurde ursprünglich 1999 als Forschungsprojekt von Intel im Zuge der Entwicklung kommerziellen Bildverarbeitungsanwendungen ins Leben gerufen \cite[vgl.\ ][]{aboutOpenCVWillowGarage}. Im Laufe der Zeit wechselte das Projekt mehrfach den Entwickler, bis es schließlich im August 2012 von der nicht-profitablen Organisation OpenCV.org übernommen wurde, u.\,a.\ mit itseez, welches erst im Mai 2016 von Intel für sein Know-How im Bereich \glqq{}Computer Vision for IOT\grqq{} aquiriert wurde \cite[vgl.\ ][]{intelBuysItseezIntel2016}, als treibende Kraft im Hintergrund.
Seit August 2014 steht mit OpenCV3.0 die dritte Version des unter der modifizierten BSD-Lizenz veröffentlichten Projekts für maschinelles Lernen und Bildverarbeitung zur öffentlichen Verfügung \cite[vgl.\ ][]{changeLogOpenCVGitHub2016}. Die Bibliothek stellt über 2500 Algorithmen zur Verfügung, welche genutzt werden können, um:
\begin{itemize}
\item{Gesichter zu detektieren und zu erkennen}
......@@ -133,7 +149,7 @@ Seit August 2014 steht mit OpenCV3.0 die dritte Version des unter der modifizier
\end{itemize}
Ferner beinhaltet das Projekt eine Bibliothek für Maschinelles Lernen zur Implementierung von künstlichen neuronalen Netzen, EM-Algorithmen, Bayes-Klassifikation, Boosting, etc..
OpenCV bietet dabei C++, C, Python, Java und MATLAB Schnittstellen und unterstützt mit Windows, Linux, Android und Mac OS sämtliche großen Betriebssysteme \cite[vgl.\ ][]{aboutOpenCV}.
Genutzt wird die Bibliothek sowohl in Forschungs- und Regierungsprojekten, als auch von renommierten Unternehmen wie Google, Yahoo, Microsoft, Intel, IBM, Sony, Honda und Toyota für Anwendungen wie das Zusammenschneiden von Streetview-Bildern, Detektierung von Einbrüchen in Überwachungssystemen, Navigieren von Roboterbewegungen, Verkehrsüberwachung, Inspektion von Labeln in der Industrie, Rapid Face Detection und Vielem mehr \cite[vgl.\ ][]{aboutOpenCV}.
Genutzt wird die Bibliothek sowohl in Forschungs- und Regierungsprojekten, als auch von renommierten Unternehmen wie Google, Yahoo, Microsoft, Intel, IBM, Sony, Honda und Toyota für Anwendungen wie das Zusammenschneiden von Streetview-Bildern, Detektierung von Einbrüchen in Überwachungssystemen, Navigieren von Roboterbewegungen, Verkehrsüberwachung, Inspektion von Labeln in der Industrie, Rapid Face Detection u.\,v.\,m.\ \cite[vgl.\ ][]{aboutOpenCV}.
Für weitere Informationen bzgl.\ der Programmierung von Bildverarbeitungsprogrammen mit OpenCV sind die Dokumentation des Projekts sowie ausführliche Tutorials zu finden unter \url{http://opencv.org/documentation.html}.
......@@ -146,11 +162,11 @@ Weiterhin steht die Bibliothek unter der modifizierten BSD-Lizens und trägt som
\section{Java Native Interface}
\label{jni}
Das JNI (Abk., engl. für \glqq{}Java Native Interface\grqq{}) dient dazu, nativen Code in eine Java-Appli"=kation zu integrieren oder andersherum. Man spricht von einem sogenannten \glqq{}Two-Way-Interface\grqq{}. Das JNI unterstützt dabei sowohl \lstinline{native Bibliotheken} als auch \lstinline{native Appli-}
Das JNI (Abk., engl.\ für \glqq{}Java Native Interface\grqq{}) dient dazu, nativen Code in eine Java-Appli"=kation zu integrieren oder andersherum. Man spricht von einem sogenannten \glqq{}Two-Way-Interface\grqq{}. Das JNI unterstützt dabei sowohl \lstinline{native Bibliotheken} als auch \lstinline{native Appli-}
\lstinline{kationen} \cite[vgl.\ ][S.5]{liang1999}.
\begin{itemize}
\item{Das JNI erlaubt es auf Java-Seite, \lstinline{native Methoden} aufzurufen. Der Aufruf innerhalb der Java-Applikation erfolgt nach den gleichen Prinzipien, wie auch eine Java-Methode aufgerufen wird. Im Hintergrund können diese jedoch in jeder nativen Programmiersprache wie beispielsweise C oder C++ geschrieben sein.}
\item{Das JNI erlaubt es auf Java-Seite, \lstinline{native Methoden} äquivalent zu Java-Methoden aufzurufen. Im Hintergrund können diese jedoch in jeder nativen Programmiersprache wie beispielsweise C oder C++ geschrieben sein.}
\item{Auf nativer Seite bietet das JNI ein \lstinline{Interface} an, welches die Implementierung einer \lstinline{virtuellen Java-Maschine} in nativen Code erlaubt (siehe Abbildung \ref{fig:diag-jni}). Native Applikationen können Bibliotheken, welche die virtuelle Maschine implementieren, einbinden und somit über das Interface in Java programmierte Elemente aufrufen. So kann beispielsweise eine C++-Instanz von OpenCV den Bildstrom einer mittels Java gesteuerten Kamera abrufen.}
\end{itemize}
......@@ -166,7 +182,7 @@ Es ist jedoch zu beachten, dass eine Java-Applikation, welche nativen Code mitte
\begin{itemize}
\item{Die Applikation kann nicht mehr ohne weiteres in andere Host-Plattformen portiert werden, da der native Anteil jedes mal umgebungsspezifisch neu kompiliert werden muss.}
\item{Während Java architekturgemäß durch die zugrundeliegende JVM (Abk., engl. für \glqq{}Java Virtual Machine\grqq{}) eine gewisse Sicherheit mit sich bringt, ist dies für native Sprachen nicht zwangsläufig der Fall und in erster Linie von der Programmierung selbst abhängig.}
\item{Während Java architekturgemäß durch die zugrundeliegende JVM (Abk., engl.\ für \glqq{}Java Virtual Machine\grqq{}) eine gewisse Sicherheit mit sich bringt, ist dies für native Sprachen nicht zwangsläufig der Fall und in erster Linie von der Programmierung selbst abhängig.}
\end{itemize}
Ausserdem ist das Java Native Interface lediglich dazu in der Lage, native Datentypen (d.\,h.\ Integer, Double, Char, etc., jedoch z.\,B.\ keine Matrizen) zu übertragen.
......@@ -177,15 +193,15 @@ Für weitere Informationen bzgl.\ der einzuhaltenden Form und Semantik bei der V
\section{Auswahl der Entwicklungsumgebung}
Für die Entwicklung der Applikation wurde aus persönlichen Präferenzen Eclipse (Download: \url{https://eclipse.org/downloads/}) als Entwicklungsumgebung verwendet. Alternativ kann ebenfalls Android Studio (Download: \url{https://developer.android.com/studio/index.html}) gewählt werden. Es ist jedoch zu erwähnen, dass Google seit der Veröffentlichung von Android Studio das ADT (Android Developer Tools) Plugin für Eclipse nicht länger weiterentwickelt \cite[vgl.\ ][]{adtAndroid2015}. Dementsprechend ist es zu empfehlen, bei zukünftigen Entwicklungsarbeiten zu Android Studio zu migrieren.
Für die Entwicklung der Applikation wurde aus persönlichen Präferenzen Eclipse (Download: \url{https://eclipse.org/downloads/}) als Entwicklungsumgebung verwendet. Alternativ kann ebenfalls Android Studio (Download: \url{https://developer.android.com/studio/index.html} gewählt werden. Es ist jedoch zu erwähnen, dass Google seit der Veröffentlichung von Android Studio das ADT (Android Developer Tools) Plugin für Eclipse nicht länger weiterentwickelt \cite[vgl.\ ][]{adtAndroid2015}. Dementsprechend ist es zu empfehlen, bei zukünftigen Entwicklungsarbeiten zu Android Studio zu migrieren.
\section{Einrichtung der Entwicklungsumgebung}
Bei der eigentlichen Einrichtung der IDE (Abk., engl. für \glqq{}Integrated Development Enviroment\grqq{}) wurde sich an dem offiziellen Tutorial von OpenCV orientiert, daher wird an dieser Stelle nicht näher darauf eingegangen.
Bei der eigentlichen Einrichtung der IDE (Abk., engl.\ für \glqq{}Integrated Development Enviroment\grqq{}) wurde sich an dem offiziellen Tutorial von OpenCV orientiert, daher wird an dieser Stelle nicht näher darauf eingegangen.
Links zu den offiziellen Tutorials:
\begin{itemize}
\item{Installation von Java und Einrichtung des SDK (Abk, engl. für \glqq{}Software Development Kit\grqq{}) und NDK (Abk., engl. für \glqq{}Native Development Kit\grqq{}) für Android:
\item{Installation von Java und Einrichtung des SDK (Abk, engl.\ für \glqq{}Software Development Kit\grqq{}) und NDK (Abk., engl.\ für \glqq{}Native Development Kit\grqq{}) für Android:
\\
\url{http://docs.opencv.org/2.4/doc/tutorials/introduction/android_binary_package/android_dev_intro.html#android-dev-intro}}
\item{Einbindung des OpenCV4Android-SDKs:
......@@ -199,7 +215,7 @@ Links zu den offiziellen Tutorials:
Es wird empfohlen, eine Testapplikation zu schreiben und zu überprüfen, ob diese sowie die Beispielanwendungen kompiliert und auf einem Testgerät zum Laufen gebracht werden können, um die richtige Einrichtung der IDE zu validieren.
\\
\\
Anmerkung: Beim Kompilieren der VBLS-Applikation (Abk., engl. für \glqq{}Visual Based Landing System\grqq{}) ist zu beachten, dass Elemente der verwendeten Bibliothek zur USB-Kommuni"=kation erst ab SDK-Version 12 oder höher verfügbar sind; dementsprechend muss die Zielplattform unter \lstinline{Preferences->Android} und in \lstinline{AndroidManifest.xml} angepasst werden. Des weiteren kann es vorkommen, dass Fehler beim Kompilieren des Layouts auftreten. Dies kann behoben werden, indem die Android Appcompat v7 oder höher als Bibliothek hinzugefügt wird (zu finden im Android SDK).
Anmerkung: Beim Kompilieren der VBLS-Applikation (Abk., engl.\ für \glqq{}Visual Based Landing System\grqq{}) ist zu beachten, dass Elemente der verwendeten Bibliothek zur USB-Kommuni"=kation erst ab SDK-Version 12 oder höher verfügbar sind. Dementsprechend muss die Zielplattform unter \lstinline{Preferences->Android} und in \lstinline{AndroidManifest.xml} angepasst werden. Des weiteren kann es vorkommen, dass Fehler beim Kompilieren des Layouts auftreten. Dies kann behoben werden, indem die Android Appcompat v7 oder höher als Bibliothek hinzugefügt wird (zu finden im Android SDK).
\chapter{Entwicklung der Applikation: Allgemeines OpenCV-Android-Framework}
\label{framework}
......@@ -213,13 +229,13 @@ Anmerkung: Beim Kompilieren der VBLS-Applikation (Abk., engl. für \glqq{}Visual
\label{fig:diag-framework}
\end{figure}
In diesem Kapitel soll zunächst das im Rahmen des Projekts erstellte allgemeine Framework zur Entwicklung von OpenCV-basierten Bildverarbeitungsapplikationen für Android-Systeme betrachtet werden. Wie in dem Architekturdiagramm (\ref{fig:diag-framework}) dargestellt, ist das Grundgerüst in drei Bestandteile unterteilt, welche im Folgenden nacheinander voneinander differenziert betrachtet werden:
In diesem Kapitel soll zunächst das im Rahmen des Projekts erstellte allgemeine Framework zur Entwicklung von OpenCV-basierten Bildverarbeitungsapplikationen für Android-Systeme betrachtet werden. Wie in dem Architekturdiagramm (\ref{fig:diag-framework}) dargestellt, ist das Grundgerüst in drei Bestandteile unterteilt, welche im Folgenden nacheinander betrachtet werden:
\begin{itemize}
\item{die Android-Schnittstelle}
\item{die Bildverarbeitung auf OpenCV-Basis}
\item{die USB-Bibliothek}
\end{itemize}
Neben den jeweiligen Implementierungen der genannten Kernelemente sollen weiterhin die zwischen den einzelnen Abschnitten kommunizierenden Schnittstellen betrachtet werden. Dabei wird für eine gute Übersichtlichkeit nach verwendeten Programmiersprachen geordnet vorgegangen, beginnend mit Java, gefolgt von C++ und XML. Zentrale Funktionalitäten werden detailliert betrachtet, für nebensächliche oder rein organisatorische Aspekte wird die Kommentierung des Programms als ausreichend erachtet. Der vollständige Quellcode findet sich im Anhang.
Neben den jeweiligen Implementierungen der genannten Kernelemente sollen weiterhin die zwischen den einzelnen Abschnitten kommunizierenden Schnittstellen betrachtet werden. Dabei wird für eine gute Übersichtlichkeit nach verwendeten Programmiersprachen geordnet vorgegangen, beginnend mit Java, gefolgt von C++ und XML. Zentrale Funktionalitäten werden detailliert betrachtet, für nebensächliche oder rein organisatorische Aspekte wird die Kommentierung des Programms als ausreichend erachtet. Der vollständige Quellcode findet sich unter dem in Kapitel \ref{repository} angeführten Link.
\section{Teil 1: Java}
\lstset{language=JAVA}
......@@ -227,7 +243,7 @@ Neben den jeweiligen Implementierungen der genannten Kernelemente sollen weiterh
\subsection{openCVActivity.java}
\label{openCVActivity.java}
Die Klasse \lstinline{openCVActivity.java} bildet das Herzstück der Applikation. Sie implementiert mit dem Activiy-Lifecycle (s. Abbildung \ref{fig:activity_lifecycle}) die Android-Schnittstelle und verwaltet die USB-Konfiguration. Gleichzeitig leitet sie die aufgenommenen Bilder über \lstinline{openCVFramework.java} an \lstinline{openCVFramework.cpp} (Bildverarbeitung) weiter.
Die Klasse \lstinline{openCVActivity.java} bildet das Herzstück der Applikation. Sie implementiert mit dem Activiy-Lifecycle (s.\ Abbildung \ref{fig:activity_lifecycle}) die Android-Schnittstelle und verwaltet die USB-Konfiguration. Gleichzeitig leitet sie die aufgenommenen Bilder über \lstinline{openCVFramework.java} an \lstinline{openCVFramework.cpp} (Bildverarbeitung) weiter.
\pagebreak
\begin{lstlisting}[caption=Set Up]
......@@ -245,7 +261,7 @@ Die Klasse \lstinline{openCVActivity.java} bildet das Herzstück der Applikation
/*-------------------------------------------------*/
\end{lstlisting}
Die Klasse ermöglicht ein nutzerspezifisches Set Up. An dieser Stelle kann die gewünschte Kameraauflösung eingestellt und ausgewählt werden, ob mit Farb- oder Graustufen-Bildern gearbeitet werden soll. Je weniger Informationen ein Bild enthält, desto höher ist die Performanz der Applikation; es ist daher zu empfehlen, die Auflösung nicht höher einzustellen als notwendig und falls möglich mit Graustufen zu arbeiten.
Die Klasse ermöglicht ein nutzerspezifisches Set Up. An dieser Stelle kann die gewünschte Kameraauflösung eingestellt und ausgewählt werden, ob mit Farb- oder Graustufen-Bildern gearbeitet werden soll. Je weniger Informationen ein Bild enthält, desto höher ist die Performanz der Applikation. Es ist daher zu empfehlen, die Auflösung nicht höher einzustellen als notwendig und falls möglich mit Graustufen zu arbeiten.
\begin{lstlisting}[caption=OpenCV Loader]
// Implementation of the LoaderCallbackInterface; tries
......@@ -295,7 +311,7 @@ Die Klasse ermöglicht ein nutzerspezifisches Set Up. An dieser Stelle kann die
}
\end{lstlisting}
Die Funktion \lstinline{onCreate()} ist Bestandteil des von Android bereitgestellten Interfaces und wird bei erstmaligem Öffnen der Applikation aufgerufen. Zunächst wird versucht, gespeicherte Einstellungen aufzurufen; danach wird die Verbindung zum User Interface der Applikation mittels \lstinline{setContentView(...)} herzustellen (s. Kapitel \ref{layout}). Anschließend wird die Kamera initialisiert und mit \lstinline{setMaxFrameSize(...)} die Auflösung auf die im Set Up festgelegten Werte gesetzt. Die Funktion \lstinline{setVisibility(...)} bewirkt die Anzeige der Bilder auf dem Display und kann bei Bedarf deaktiviert werden.
Die Funktion \lstinline{onCreate()} ist Bestandteil des von Android bereitgestellten Interfaces und wird bei erstmaligem Öffnen der Applikation aufgerufen. Zunächst wird versucht, gespeicherte Einstellungen aufzurufen; danach wird die Verbindung zum User Interface der Applikation mittels \lstinline{setContentView(...)} herzustellen (s.\ Kapitel \ref{layout}). Anschließend wird die Kamera initialisiert und mit \lstinline{setMaxFrameSize(...)} die Auflösung auf die im Set Up festgelegten Werte gesetzt. Die Funktion \lstinline{setVisibility(...)} bewirkt die Anzeige der Bilder auf dem Display und kann bei Bedarf deaktiviert werden.
\begin{lstlisting}[caption=onPause]
// Called if the application is moved to the background
......@@ -308,7 +324,7 @@ Die Funktion \lstinline{onCreate()} ist Bestandteil des von Android bereitgestel
}
\end{lstlisting}
Da es je nach Art und Verwendung der Applikation kritisch sein kann, wenn bei Pausieren der Anwendung beispielsweise die USB-Kommunikation (s. Beispielanwendung Visual Based Landing System) unterbrochen wird, ist \lstinline{onPause()} standardmäßig derart konfiguriert, dass lediglich die Kamera deaktiviert wird.
Da es je nach Art und Verwendung der Applikation kritisch sein kann, wenn bei Pausieren der Anwendung beispielsweise die USB-Kommunikation (s.\ Beispielanwendung Visual Based Landing System) unterbrochen wird, ist \lstinline{onPause()} standardmäßig derart konfiguriert, dass lediglich die Kamera deaktiviert wird.
\begin{lstlisting}[caption=onResume]
// Called if the application is resumed from the background
......@@ -353,11 +369,11 @@ Da es je nach Art und Verwendung der Applikation kritisch sein kann, wenn bei Pa
\end{lstlisting}
Die Funktion \lstinline{onResume()} ist ebenfalls Teil des von Android zur Verfügung gestellten Interfaces und wird jedes Mal aufgerufen, wenn die Applikation aus dem Hintergrund (pausiert) aufgerufen wird und somit wieder in den Vordergrund rückt, sowie nach dem erstmaligen Starten des Programms (siehe Abbildung \ref{fig:activity_lifecycle}).
Zunächst wird \lstinline{super.onResume()} aufgerufen, wodurch unter anderem versucht wird, die in onPause() deaktivierte Kamera wieder zu reaktivieren; es ist kein expliziter Aufruf von \lstinline{mOpenCvCameraView.enableView()} notwendig.
Zunächst wird \lstinline{super.onResume()} aufgerufen, wodurch u.\,a.\ versucht wird, die in \lstinline{onPause()} deaktivierte Kamera wieder zu reaktivieren; es ist kein expliziter Aufruf von \lstinline{mOpenCvCameraView.enableView()} notwendig.
Seit Version 6.0 arbeitet Android jedoch mit sogenannten Runtime Permissions, d.\,h.\ es ist möglich, einer Anwendung bestimmte Berechtigungen zu geben und zu nehmen, während diese läuft. Daher muss an dieser Stelle zunächst das Level der Distribution der Plattform überprüft werden und, falls dieses größer als 6.0 ist, ob die benötigte Nutzungsberechtigung für die Kamera aktuell immer noch vorliegt. Dies geschieht mittels der ersten if-Abfrage (\lstinline{android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M}) und \lstinline{check-}
Seit Version 6.0 arbeitet Android jedoch mit sogenannten Runtime Permissions, d.\,h.\ es ist möglich, einer Anwendung bestimmte Berechtigungen zu geben und zu nehmen, während diese läuft. Daher muss an dieser Stelle zunächst das Level der Distribution der Plattform überprüft werden und, falls dieses größer als 6.0 ist, ob die benötigte Nutzungsberechtigung für die Kamera aktuell immer noch vorliegt. Dies geschieht mittels der ersten if-Abfrage (\lstinline{android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M}) und
\\
\lstinline{SelfPermission(...)}. Liegt keine Berechtigung vor, so muss diese aktiv beim Nutzer mittels \lstinline{requestPermissions(...)} (siehe unten) angefragt werden. Da nach jeder Anfrage einer Berechtigung erneut \lstinline{onResume()} aufgerufen wird, kann mittels der Einführung der Einführung der Counter-Variablen \lstinline{counter_denied} eine Fallunterscheidung, ob bereits eine Abfrage stattgefunden hat, implementiert werden. So wird eine rekursive Endlosschleife vermieden, falls der Nutzer die Berechtigung beim Aufruf nicht geben möchte.
\lstinline{checkSelfPermission(...)}. Liegt keine Berechtigung vor, so muss diese aktiv beim Nutzer mittels \lstinline{requestPermissions(...)} (siehe unten) angefragt werden. Da nach jeder Anfrage einer Berechtigung erneut \lstinline{onResume()} aufgerufen wird, kann mittels der Einführung der Einführung der Counter-Variablen \lstinline{counter_denied} eine Fallunterscheidung, ob bereits eine Abfrage stattgefunden hat, implementiert werden. So wird eine rekursive Endlosschleife vermieden, falls der Nutzer die Berechtigung beim Aufruf nicht geben möchte.
Die zweite if-Abfrage (\lstinline{if (!OpenCVLoader.initDebug()}) führt eine Unterscheidung nach der Art der Implementierung der OpenCV-Bibliothek (vorinstalliert mittels des OpenCV-Managers oder mit der Applikation zusammen installiert) durch und versucht, diese zu reinitialisieren.
\begin{lstlisting}[caption=Überprüfen der Kamera-Berechtigung]
......@@ -387,9 +403,9 @@ Die zweite if-Abfrage (\lstinline{if (!OpenCVLoader.initDebug()}) führt eine Un
Die Funktion \lstinline{onRequestPermissionsResult(...)} wird nach Anfragen einer Berechtigung mittels \lstinline{requestPermissions(...)} aufgerufen und kann verwendet werden, um Reaktionen abhängig vom zurückgelieferten Ergebnis zu definieren. Da diese Funktion jedoch bei jeder Anfrage einer beliebigen Berechtigung aktiviert wird, ist zunächst mittels \lstinline{switch(request-}
\\
\lstinline{Code)} eine Fallunterscheidung bzgl.\ der Art der angefragten Berechtigung durchzuführen; im Falle der in dieser Arbeit behandelten Applikation ist konkret die Nutzungsberechtigung für die Kamera von Bedeutung, welche mittels \lstinline{case REQUEST_CODE_CAMERA_PERMISSION} abgefragt wird. Der erste Eintrag des übergebenen Arrays grantResults entspricht nun dem Ergebnis der Anfrage. Ist die Berechtigung nun also erteilt worden (\lstinline{grantResults[0] ==}
\lstinline{Code)} eine Fallunterscheidung bzgl.\ der Art der angefragten Berechtigung durchzuführen; im konkreten Fall ist die Nutzungsberechtigung für die Kamera von Bedeutung, welche mittels \lstinline{case REQUEST_CODE_CAMERA_PERMISSION} abgefragt wird. Der erste Eintrag des übergebenen Arrays \lstinline{grantResults} entspricht dem Ergebnis der Anfrage. Ist die Berechtigung nun also erteilt worden (\lstinline{grantResults[0] == PackageManager.PERMISSION_GRANTED}), so wird \lstinline{counter}
\\
\lstinline{PackageManager.PERMISSION_GRANTED}), so wird \lstinline{counter_denied} zurückgesetzt und die Funktion wird ohne weitere Aktionen verlassen (return). Als Reaktion auf die Anfrage wird erneut \lstinline{onResume()} aufgerufen; da die Berechtigung jedoch erteilt wurde, wechselt die Funktion dieses Mal jedoch direkt zur Initialisierung der OpenCV-Bibliothek. Ist das Ergebnis der Anfrage dagegen negativ, so wird \lstinline{counter_denied} erhöht. Dadurch wird beim Aufruf von \lstinline{onResume()} nach Verlassen von \lstinline{onRequestPermissionsResult(...)} anstatt einer erneuten Abfrage die Applikation mittels \lstinline{onDestroy()} terminiert.
\lstinline{_denied} zurückgesetzt und die Funktion wird ohne weitere Aktionen verlassen (return). Als Reaktion auf die Anfrage wird erneut \lstinline{onResume()} aufgerufen; da die Berechtigung jedoch erteilt wurde, wechselt die Funktion dieses Mal direkt zur Initialisierung der OpenCV-Bibliothek. Ist das Ergebnis der Anfrage dagegen negativ, so wird \lstinline{counter_denied} erhöht. Dadurch wird beim Aufruf von \lstinline{onResume()} nach Verlassen von \lstinline{onRequestPermissionsResult(...)} anstatt einer erneuten Abfrage die Applikation mittels \lstinline{onDestroy()} terminiert.
\begin{lstlisting}[caption=onDestroy]
// Called if the application is terminated
......@@ -400,7 +416,7 @@ Die Funktion \lstinline{onRequestPermissionsResult(...)} wird nach Anfragen eine
}
\end{lstlisting}
Die Funktion \lstinline{onDestroy()} terminiert wie zu erwarten die Activity. Standardmäßig wird dabei lediglich \lstinline{disableView()} zusätzlich zu \lstinline{super.onDestroy()} aufgerufen um die Kamera zu deaktivieren. Die Funktion wird an dieser Stelle nur angeführt, um zu erwähnen, dass je nach Art der aus dem Framework abstrahierten Applikation alle weiteren mit der Activity zusammenhängenden Prozesse (wie beispielsweise weitere Views) beendet werden können/sollten.
Die Funktion \lstinline{onDestroy()} terminiert wie zu erwarten die Activity. Standardmäßig wird dabei lediglich \lstinline{disableView()} zusätzlich zu \lstinline{super.onDestroy()} aufgerufen um die Kamera zu deaktivieren. An dieser Stelle können/sollten in aus dem Framework abstrahierten Applikationen alle weiteren mit der Activity zusammenhängenden Prozesse (wie beispielsweise weitere Views) beendet werden.
\begin{lstlisting}[caption=onCameraFrame]
// Called on every frame received from the input - stream
......@@ -461,22 +477,22 @@ Die Funktion \lstinline{onDestroy()} terminiert wie zu erwarten die Activity. St
}
\end{lstlisting}
Die Funktion \lstinline{onCameraFrame(...)} ist eine weitere Callback-Methode des von Android bereitgestellten Interfaces. Sie wird jedes Mal aufgerufen, wenn die Kamera ein Bild aufnimmt und bekommt dieses als Übergabeargument in Form eines CvCameraViewFrames übergeben. Zunächst muss auch an dieser Stelle wieder ausgewählt werden, ob im weiteren Verlauf mit einem Farb- oder Graustufenbild gearbeitet werden soll; je nachdem wird das Bild mittels \lstinline{rgba()} oder \lstinline{gray()} von CvCameraViewFrames als Datentyp zu Mat (Abk., kurz für Matrix), einem Datentyp bestehend aus einem Array von Vektoren, welche wiederum die Informationen jedes Pixels des Bilds enthalten (d.\,h.\ für Farbbilder die RGB- sowie den Alpha-Anteil und für Graustufenbilder den Weißanteil), konvertiert.
Die Funktion \lstinline{onCameraFrame(...)} ist eine weitere Callback-Methode des von Android bereitgestellten Interfaces. Sie wird jedes Mal aufgerufen, wenn die Kamera ein Bild aufnimmt und bekommt dieses als Übergabeargument in Form eines CvCameraViewFrames übergeben. Zunächst muss auch an dieser Stelle wieder ausgewählt werden, ob im weiteren Verlauf mit einem Farb- oder Graustufenbild gearbeitet werden soll. Je nachdem wird das Bild mittels \lstinline{rgba()} oder \lstinline{gray()} von CvCameraViewFrames zu Mat (Abk., kurz für Matrix), einem Datentyp bestehend aus einem Array von Vektoren, welche wiederum die Informationen jedes Pixels des Bilds enthalten (d.\,h.\ für Farbbilder die RGB- sowie den Alpha-Anteil und für Graustufenbilder den Weißanteil), konvertiert.
Da die eigentliche Bildverarbeitung jedoch in \lstinline{openCVFramework.cpp} stattfindet und das JNI, welches als Kommunikationsschnittstelle zwischen Java und C++ fungiert, lediglich native Datentypen unterstützt, muss weiterhin die Matrix zu einem Array aus Bytes umgewandelt werden. Dies geschieht mit \lstinline{jMatToArray(...)} (s. Kapitel \ref{openCVFramework.java}). Das so erhaltene Array wird in jImageDate gespeichert. Schlägt die Umwandlung fehl, z.\,B.\ wenn die Bilddaten beschädigt sein sollten, so wirft \lstinline{jMatToArray(...)} eine \lstinline{IOException}, die an dieser Stelle abgefangen wird und mit einem entsprechenden Log-Eintrage zur Terminierung der Applikation führt. Anschließend wird mit \lstinline{process(...)} die eigentliche Bildverarbeitung initiiert. Das in Form des Byte-Arrays übergebene Bild wird per JNI an openCVFramework.cpp weitergeleitet und dort verarbeitet. Standardmäßig ist \lstinline{process(...)} so eingerichtet, dass die Rückgabe in Form eines Double-Arrays erfolgt, so dass beispielsweise Positionen oder Größen von detektierten Objekten übergeben werden können (s. Kapitel \ref{openCVFramework.cpp}). Alternativ ist es auch möglich ein bearbeitetes Bild ebenfalls wieder in Byte-Form zurückzugeben und anschließend anzeigen zu lassen.
Da die eigentliche Bildverarbeitung jedoch in \lstinline{openCVFramework.cpp} stattfindet und das JNI, welches als Kommunikationsschnittstelle zwischen Java und C++ fungiert, lediglich native Datentypen unterstützt, muss weiterhin die Matrix zu einem Array aus Bytes umgewandelt werden. Dies geschieht mit \lstinline{jMatToArray(...)} (s.\ Kapitel \ref{openCVFramework.java}). Das so erhaltene Array wird in jImageDate gespeichert. Schlägt die Umwandlung fehl, z.\,B.\ wenn die Bilddaten beschädigt sein sollten, so wirft \lstinline{jMatToArray(...)} eine \lstinline{IOException}, die an dieser Stelle abgefangen wird und mit einem entsprechenden Log-Eintrage zur Terminierung der Applikation führt. Anschließend wird mit \lstinline{process(...)} die eigentliche Bildverarbeitung initiiert. Das in Form des Byte-Arrays übergebene Bild wird per JNI an openCVFramework.cpp weitergeleitet und dort verarbeitet. Standardmäßig ist \lstinline{process(...)} so eingerichtet, dass die Rückgabe in Form eines Double-Arrays erfolgt, so dass beispielsweise Positionen oder Größen von detektierten Objekten übergeben werden können (s.\ Kapitel \ref{openCVFramework.cpp}). Alternativ ist es auch möglich ein bearbeitetes Bild (ebenfalls wieder in Byte-Form) zurückzugeben und anschließend anzeigen zu lassen.
Treten bereits vor der Kommunikation mittels JNI Komplikationen auf, so wirft auch \lstinline{process(...)} eine Fehlermeldung, die ebenso wie bei \lstinline{jMatToArray(...)} zu einer Beendigung der Applikation führt; kommt es zu einem Fehler während der eigentlichen Bildverarbeitung, so liefert \lstinline{openCVFramework.cpp} einen Nullpointer als Rückgabewert zurück, der in einer if-Abfrage abgefangen wird. So kann im Fehlerfall beim Debugging leichter zwischen den Ursachen differenziert werden.
Anschließend an die Verarbeitung besteht die Möglichkeit, weitere Aktionen durchzuführen. So können beispielsweise die durch \lstinline{process(...)} erhaltenen Daten per USB ausgegeben oder ereignisbasierte Nachrichten dargestellt werden. Dadurch erhält der Nutzer bei Erstellung seiner eigenen Applikation neben der eigentlichen Bildverarbeitung mittels OpenCV eine weitere Schnittstelle auf Android-Seite, wodurch die Anzahl der Möglichkeiten vervielfältigt wird.
Anschließend an die Verarbeitung besteht die Möglichkeit, weitere Aktionen durchzuführen. So können die durch \lstinline{process(...)} erhaltenen Daten bspw.\ per USB ausgegeben oder ereignisbasierte Nachrichten dargestellt werden. Durch den Zugriff auf die Java-Bibliotheken auf Android-Seite vervielfältigen sich die Möglichkeiten.
Abschließend wird mittels \lstinline{return} das Bild, welches auf dem Display angezeigt werden soll, in Matrix-Darstellung zurückgegeben.
\\
\\
Standardmäßig ist die Applikation derart eingerichtet, dass einfach das aufgenommene Bild in Graustufen-Form dargestellt wird. Wurde das Bild im Rahmen der Anwendung jedoch verändert (z.\,B.\ indem bestimmte detektierte Objekte eingerahmt wurden), so kann an dieser Stelle auch die modifizierte Variante übergeben werden. Es ist jedoch anzumerken, dass es rechentechnisch sehr aufwendig ist, das an die Bildverarbeitung übergebene Bild dort zu verändern, innerhalb von \lstinline{openCVFramework.cpp} wieder von einer Matrix in ein Byte-Array umzuwandeln, anschließend per JNI zurückzugeben und erneut zu einer Matrix zu konvertieren. Daher ist es aus Performanz-Gründen zu empfehlen, falls möglich lediglich die Detektion der Objekte im Bild in der Verarbeitung auszuführen, deren Positionen anschließend zurückzugeben und auf Java-Seite direkt in das Bild einzuzeichnen (auch wenn dort ohne die Einbindung einer weiteren externen Bibliothek lediglich triviale Aktionen zur Verfügung stehen).
Standardmäßig ist die Applikation derart eingerichtet, dass das aufgenommene Bild in Grau"=stufen-Form dargestellt wird. Wurde das Bild im Rahmen der Anwendung jedoch verändert (z.\,B.\ indem bestimmte detektierte Objekte eingerahmt wurden), so kann an dieser Stelle auch die modifizierte Variante übergeben werden. Es ist jedoch anzumerken, dass es rechentechnisch sehr aufwendig ist, das an die Bildverarbeitung übergebene Bild dort zu verändern, innerhalb von \lstinline{openCVFramework.cpp} wieder von einer Matrix in ein Byte-Array umzuwandeln, anschließend per JNI zurückzugeben und erneut zu einer Matrix zu konvertieren. Daher ist es aus Performanz-Gründen zu empfehlen, falls möglich lediglich die Detektion der Objekte im Bild in der Verarbeitung auszuführen, deren Positionen anschließend zurückzugeben und auf Java-Seite direkt in das Bild einzuzeichnen (auch wenn dort ohne die Einbindung einer weiteren externen Bibliothek lediglich triviale Aktionen zur Verfügung stehen).
\subsection{openCVFramework.java}
\label{openCVFramework.java}
Die Klasse \lstinline{openCVFramework.java} stellt die Schnittstelle zwischen \lstinline{openCVActivity.java} (Android) und \lstinline{openCVFramework.cpp} (OpenCV) dar und wird von \lstinline{openCVActivity.java} implementiert und instanziiert.
Die Klasse \lstinline{openCVFramework.java} stellt die Schnittstelle zwischen \lstinline{openCVActivity.java} (Android) und \lstinline{openCVFramework.cpp} (OpenCV) dar und wird von \lstinline{openCVActivity.java} eingebunden.
\begin{lstlisting}[caption=Unterscheidung des Bildformats]
// Color - image
......@@ -486,7 +502,7 @@ Die Klasse \lstinline{openCVFramework.java} stellt die Schnittstelle zwischen \l
private static final int ch = 1;
\end{lstlisting}
Wie auch in der übergeordneten Activity kann unterschieden werden, ob ein Farbbild oder ein Graustufen-Bild verarbeitet wird. Je nachdem sind jedem Pixel des Bildes unterschiedlich viele Informationen zugeordnet; im Falle des Farbbilds sind dies jeweils die RGB-Anteile und zusätzlich der Alpha-Anteil, bei einem Graustufen-Bild wird lediglich der Weißanteil des Pixels angegeben (0 = Schwarz, 255 = Weiß). Standardmäßig werden Graustufen-Bilder verwendet, da dies den Verarbeitungsaufwand senkt und eine höhere Performanz bewirkt.
Wie auch in der übergeordneten Activity kann unterschieden werden, ob ein Farbbild oder ein Graustufen-Bild verarbeitet wird. Je nachdem sind jedem Pixel des Bildes unterschiedlich viele Informationen zugeordnet; im Falle des Farbbilds sind dies jeweils die RGB-Anteile und zusätzlich der Alpha-Anteil, bei einem Graustufen-Bild wird lediglich der Weißanteil des Pixels angegeben.
\begin{lstlisting}[caption=Laden der vorkompilierten Bibliothek]
public openCVFramework() {
......@@ -523,8 +539,8 @@ Mittels des dargestellten Quellcodes wird die Bilbiothek \lstinline{openCVFramew
}
\end{lstlisting}
Wie bereits in Kapitel \ref{jni} erwähnt, können über das JNI lediglich native Datentypen kommuniziert werden. Da Android jedoch mit dem Datenformat Mat (s. Kapitel \ref{openCVActivity.java}) arbeitet, muss zunächst eine Konvertierung stattfinden, bevor die Bilder an \lstinline{openCVFramework.cpp} weitergeleitet werden können. Diese Funktionalität bietet die Funktion \lstinline{jMatToArray(...)}. Zunächst werden dabei die Parameter des Bildes eingelesen (Höhe, Breite, Kanäle und Format der Matrix) und anschließend in der if-Bedingung überprüft. Somit wird vermieden, dass ein beschädigtes Bild verarbeitet wird und einen Systemabsturz verursacht.
Ist sind die Parameter in Ordnung wird anschließend ein Byte-Array erzeugt und der Inhalt der Matrix wird mittels \lstinline{img.get(...)} in das Array übertragen. Ist die if-Bedingung nicht erfüllt, wirft die Funktion eine Exception, welche von \lstinline{openCVActivity.java} abgefangen wird und zu einer geregelten Beendigung der Applikation mittels \lstinline{onDestroy()} führt.
Wie bereits in Kapitel \ref{jni} erwähnt, können über das JNI lediglich native Datentypen kommuniziert werden. Da Android jedoch mit dem Datenformat Mat (s.\ Kapitel \ref{openCVActivity.java}) arbeitet, muss zunächst eine Konvertierung stattfinden, bevor die Bilder an \lstinline{openCVFramework.cpp} weitergeleitet werden können. Diese Funktionalität bietet die Funktion \lstinline{jMatToArray(...)}. Dabei werden zunächst die Parameter des Bildes (Höhe, Breite, Kanäle und Format der Matrix) eingelesen und überprüft. Somit wird vermieden, dass ein beschädigtes Bild verarbeitet wird und einen Systemabsturz verursacht.
Sind die Parameter in Ordnung wird anschließend ein Byte-Array erzeugt und der Inhalt der Matrix wird mittels \lstinline{img.get(...)} in das Array übertragen. Ist die if-Bedingung nicht erfüllt, wirft die Funktion eine Exception.
\begin{lstlisting}[caption=Implementation des JNIs auf Java-Seite]
// Calls the native method containing the image processing
......@@ -549,9 +565,10 @@ Ist sind die Parameter in Ordnung wird anschließend ein Byte-Array erzeugt und
image, int r, int c, int ch, int d);
\end{lstlisting}
Die Funktionen \lstinline{process(...)} und \lstinline{nativeImageProcession(...)} stellen die Implementierung des JNIs auf Java-Seite dar. Erstere Funktion überprüft zunächst, ob ob eine valide (d.\,h.\ ungleich \lstinline{null}) Datenstruktur übergeben wird und ruft, falls nicht, \lstinline{nativeImageProcession }
\lstinline{(...)} mit dem übergebenen Bild und den zugehörigen Eigenschaften auf, wodurch der Aufwand zur Verwendung der Funktion reduziert wird. Das Schlüsselwort \lstinline{native} in der Deklaration von \lstinline{nativeImageProcession(...)} signalisiert, dass es sich um eine über das JNI aufgerufene Funktion handelt und erlaubt es dem Interface, die Verknüpfung mit der Bibliothek \lstinline{openCVFramework.h} herzustellen.
Standardmäßig wird von der in openCVFramework.cpp implementierten Funktion ein Double-Array über das JNI zurückgeliefert, wodurch Positionen oder Größen von detektierten Gegenständen übergeben werden können. Kommt es während der Bildverarbeitung jedoch zu einem Fehler, so wird ein Nullpointer übergeben.
Die Funktionen \lstinline{process(...)} und \lstinline{nativeImageProcession(...)} stellen die Implementierung des JNIs auf Java-Seite dar. Erstere Funktion ruft \lstinline{nativeImageProcession(...)} mit dem übergebenen Bild und den zugehörigen Eigenschaften auf, wodurch der Aufwand zur Verwendung der Funktion reduziert wird. Das Schlüsselwort \lstinline{native} in der Deklaration von \lstinline{nativeImageProcession(...)} signalisiert, dass es sich um eine über das JNI aufgerufene Funktion handelt und erlaubt es dem Interface, die Verknüpfung mit der Bibliothek \lstinline{openCV-}
\\
\lstinline{Framework.h} herzustellen.
Standardmäßig wird von der in \lstinline{openCVFramework.cpp} implementierten Funktion ein Double-Array über das JNI zurückgeliefert, wodurch Positionen oder Größen von detektierten Gegenständen übergeben werden können. Kommt es während der Bildverarbeitung jedoch zu einem Fehler, wird stattdessen ein Nullpointer übergeben.
\section{Teil 2: C++}
\lstset{language=C++}
......@@ -559,7 +576,7 @@ Standardmäßig wird von der in openCVFramework.cpp implementierten Funktion ein
\subsection{openCVFramework.h}
\label{vorgehenJNI}
In diesem Kapitel soll beschrieben werden, wie aus einer eine oder mehrere native Funktionen implementierenden Java-Klasse, in diesem Falle openCVFramework.java, die für die Verknüpfung mittels JNI benötigte native Bibliothek erstellt werden kann.
In diesem Kapitel soll beschrieben werden, wie aus einer eine oder mehrere native Funktionen implementierenden Java-Klasse, in diesem Falle \lstinline{openCVFramework.java}, die für die Verknüpfung mittels JNI benötigte native Bibliothek erstellt werden kann.
Zunächst muss dafür aus dafür aus der betreffenden .java Datei aus ihrem Verzeichnis heraus mittels
\begin{itemize}
......@@ -616,13 +633,14 @@ Die resultierende Bibliothek \lstinline{org_opencv_openCVFramework.h} hat anschl
#endif
\end{lstlisting}
Es ist darauf zu achten, dass die so erstellte .h Datei sich in dem jni-Ordner der Applikation befinden muss, damit das Java Native Interface diese bei Aufruf der nativen Funktion auf Java-Seite findet. Bei der Kompilierung der Applikation wird aus dem nativen Anteil der Anwendung schließlich eine .so Datei erstellt, die in \lstinline{openCVFramework.java} (s. Kapitel \ref{openCVFramework.java}) aufgerufen wird. Dieser Vorgang kann in der \lstinline{Android.mk} und in der \lstinline{Application.mk} (beide ebenfalls im JNI-Verzeichnis) definiert werden. Da beide Dateien als Makefiles jedoch selbsterklärend sind, wird an dieser Stelle nicht weiter darauf eingegangen.
Es ist darauf zu achten, dass die so erstellte .h Datei sich in dem jni-Ordner der Applikation befinden muss, damit das Java Native Interface diese bei Aufruf der nativen Funktion auf Java-Seite findet. Bei der Kompilierung der Applikation wird aus dem nativen Anteil der Anwendung schließlich eine .so Datei erstellt, die in \lstinline{openCVFramework.java} (s.\ Kapitel \ref{openCVFramework.java}) aufgerufen wird. Dieser Vorgang kann in der \lstinline{Android.mk} und in der \lstinline{Application.mk} (beide ebenfalls im JNI-Verzeichnis) definiert werden.
\subsection{openCVFramework.cpp}
\label{openCVFramework.cpp}
Die Klasse \lstinline{openCVFramework.cpp} bildet beeinhaltet die eigentliche Bildverarbeitung der Applikation und implementiert die in \lstinline{openCVFramework.java} definierte native Funktion \lstinline{native-}
\lstinline{ImageProcession(...)} (s. Kapitel \ref{openCVFramework.java}).
Die Klasse \lstinline{openCVFramework.cpp} beeinhaltet die eigentliche Bildverarbeitung der Applikation und implementiert die in \lstinline{openCVFramework.java} definierte native Funktion \lstinline{nativeImage-}
\\
\lstinline{Procession(...)} (s.\ Kapitel \ref{openCVFramework.java}).
\begin{lstlisting}[caption=Deklaration der nativeImageProcession-Funktion]
#include "org_opencv_openCVFramework.h"
......@@ -635,7 +653,9 @@ Die Klasse \lstinline{openCVFramework.cpp} bildet beeinhaltet die eigentliche Bi
jrows, jint jcolumns, jint jchannels, jint jdepth){
\end{lstlisting}
Damit das JNI den Bezug von der der nativen Funktion zu der Bibliothek und dadrüber zu deren Implementation in Java herstellen kann, ist es nötig, dass die native Klasse den selben Namen hat, wie die Java-Klasse, in der die Funktion aufgerufen wird, d.\,h.\ wenn die Java-Klasse \lstinline{openCVFramework.java} heißt, muss der Name des nativen Äquivalents \lstinline{openCVFramework.cpp} sein. Der Funktionsaufruf der nativen Funktion selbst muss nun demjenigen aus \lstinline{org_opencv_openCVFramework.h} gleichen, damit die Referenz zu der Bibliothek aufgelöst werden kann. Das Schlüsselwort \lstinline{JNIEXPORT} zeigt an, dass der nachfolgende Datentyp (in diesem Fall jdoubleArray) zurückgegeben wird; \lstinline{JNICALL} bedeutet, dass die Funktion über das JNI aufgerufen wird.
Damit das JNI den Bezug von der nativen Funktion zu der Bibliothek und dadrüber zu deren Einbindung in Java herstellen kann, ist es nötig, dass die native Klasse den selben Namen hat, wie die Java-Klasse, in der die Funktion aufgerufen wird, d.\,h.\ wenn die Java-Klasse \lstinline{openCVFramework.java} heißt, muss der Name des nativen Äquivalents \lstinline{openCVFramework.cpp} sein. Der Funktionsaufruf der nativen Funktion selbst muss demjenigen aus \lstinline{org_opencv_o-}
\\
\lstinline{penCVFramework.h} gleichen, damit die Referenz zu der Bibliothek aufgelöst werden kann. Das Schlüsselwort \lstinline{JNIEXPORT} zeigt an, dass der nachfolgende Datentyp (in diesem Fall jdoubleArray) zurückgegeben wird; \lstinline{JNICALL} bedeutet, dass die Funktion über das JNI aufgerufen wird.
\begin{lstlisting}[caption=Konvertierung der Java-Datentypen zu nativen Datentypen]
Mat src, src_gray;
......@@ -658,7 +678,7 @@ Damit das JNI den Bezug von der der nativen Funktion zu der Bibliothek und dadr
}
\end{lstlisting}
Da die Speicherstruktur der Datentypen in Java sich teilweise von derjenigen in C++ unterscheidet, muss zunächst eine Konvertierung vorgenommen werden, um anschließend die Daten verwenden zu können. Das JNI stellt dafür die Datentypen jint, jbyteArray, etc.\ für die von Java übergebenen Argumente sowie Funktionen zur Umwandlung wie \lstinline{GetByte-}
Da die Speicherstruktur der Datentypen in Java sich teilweise von derjenigen in C++ unterscheidet, muss zunächst eine Konvertierung vorgenommen werden, um anschließend die Daten verwenden zu können. Das JNI stellt dafür die Datentypen \lstinline{jint}, \lstinline{jbyteArray}, etc.\ für die von Java übergebenen Argumente sowie Funktionen zur Umwandlung wie \lstinline{GetByte-}
\lstinline{ArrayElements(...)} zur Verfügung.
\begin{lstlisting}[caption=Wiederherstellung der Bildmatrix aus dem übergebenen Array]
......@@ -703,7 +723,7 @@ Da die Speicherstruktur der Datentypen in Java sich teilweise von derjenigen in
}
\end{lstlisting}
Vor der Verarbeitung muss aus dem übergebenen Bild-Array erneut eine Matrix erzeug werden, da die OpenCV-Funktionalitäten den Datentyp Mat als Übergabewert erwarten. Dafür werden die die Informationen eines Bildpixels wiederspiegelnden Einträge des Arrays (s. Kapitel \ref{openCVActivity.java}) jeweils in den dem Pixel zugeordneten Vektor geschrieben. Erneut hat der Nutzer die Auswahl zwischen einem Farb- und einem Graustufenbild. Entsprechen die Bildparameter nicht den diesen Bildformaten zugehörigen oder wird die Konvertierung nicht durchgeführt, so gibt die Funktion einen Nullpointer zurück, der in \lstinline{openCVActivity.java} abgefangen wird.
Vor der Verarbeitung muss aus dem übergebenen Bild-Array erneut eine Matrix erzeug werden, da die OpenCV-Funktionalitäten den Datentyp Mat als Übergabewert erwarten. Dafür werden die die Informationen eines Bildpixels wiederspiegelnden Einträge des Arrays (s.\ Kapitel \ref{openCVActivity.java}) jeweils in den dem Pixel zugeordneten Vektor geschrieben. Entsprechen die Bildparameter nicht den dem gewählten Bildformat zugehörigen oder wird die Konvertierung nicht korrekt durchgeführt, so gibt die Funktion einen Nullpointer zurück.
\begin{lstlisting}[caption=Bildverarbeitung]
// Color - image
......@@ -749,7 +769,7 @@ Für den Fall, dass ein Farbbild übergeben wurde, besteht auch an dieser Stelle
}
\end{lstlisting}
Abschließend besteht die Möglichkeit, Argumente, die über das JNI an Java zurückgegeben werden sollen, festzulegen. Standardmäßig ist die Rückgabe eines Double-Arrays implementiert. Dabei muss jedoch auch an dieser Stelle zunächst eine Konvertierung von einem C++-Array in die von Java verwendete Arraystruktur erfolgen. Dies geschieht, indem entsprechender Speicherplatz per \lstinline{malloc(...)} reserviert, der Zeiger des Ausgabearrays (in diesem Fall vom Typ jdoubleArray) auf diesen Speicherbereich gesetzt wird und anschließend der Inhalt des C++-Arrays per \lstinline{SetDoubleArrayRegion(...)} in das Java-Array geschrieben wird. Misslingt die Speicherbelegung, so wird ein Nullpointer zurückgegeben. Ansonsten wird nach Freigabe des durch das C++-Array reservierten Speicherplatzes das jdoubleArray zurückgegeben.
Abschließend besteht die Möglichkeit, Argumente, die über das JNI an Java zurückgegeben werden sollen, festzulegen. Standardmäßig ist die Rückgabe eines Double-Arrays implementiert. Dabei muss jedoch auch an dieser Stelle zunächst eine Konvertierung von einem C++-Array in die von Java verwendete Arraystruktur erfolgen. Dies geschieht, indem entsprechender Speicherplatz per \lstinline{malloc(...)} reserviert, der Zeiger des Ausgabearrays (in diesem Fall vom Typ \lstinline{jdoubleArray}) auf diesen Speicherbereich gesetzt wird und anschließend der Inhalt des C++-Arrays per \lstinline{SetDoubleArrayRegion(...)} in das Java-Array geschrieben wird. Misslingt die Speicherbelegung, so wird ein Nullpointer zurückgegeben.
\section{Teil 3: XML}
\lstset{language=XML}
......@@ -760,14 +780,16 @@ Jede Android-Applikation benötigt ein Android-Manifest. In diesem werden dem Sy
\begin{itemize}
\item{Das Java-Paket, in dem sich die Applikation befindet sowie die mindestens auf dem ausführenden Gerät benötigte Android-Version:
\begin{lstlisting}[caption=Java-Paket und min. Android-Version]
package="org.opencv"
android:versionCode="301"
android:versionName="3.01"
\end{lstlisting}
Die mindestens benötigte Android-Version orientiert sich dabei an den in der Applikation verwendeten Features.
}
\item{Die Grundeigenschaften der Applikation wie Name und Icon sowie die einzelnen Bestandteile wie Activities, Services, Intent-Filter, etc.\ und ordnet diese einander zu:
\item{Die Grundeigenschaften der Applikation wie Name und Icon sowie die einzelnen Bestandteile wie Activities, Services, Intent-Filter, etc.:
\begin{lstlisting}[caption=Aufbau der Applikation]
<application
......@@ -787,7 +809,7 @@ Die mindestens benötigte Android-Version orientiert sich dabei an den in der Ap
</application>
\end{lstlisting}
Im Falle des Frameworks existiert standardmäßig auf Android-Seite lediglich eine Activity. Besteht jedoch im Rahmen der Implementierung des Frameworks je nach abzudeckender Anwendung die Notwendigkeit, weitere Android-Komponenten hinzuzufügen, so sind diese im Manifest nach folgendem Muster zu ergänzen:
Im Falle des Frameworks existiert auf Android-Seite standardmäßig lediglich eine Activity. Besteht jedoch im Rahmen der Implementierung des Frameworks je nach abzudeckender Anwendung die Notwendigkeit, weitere Android-Komponenten hinzuzufügen, so sind diese im Manifest nach folgendem Muster zu ergänzen:
\begin{lstlisting}[caption=Ergänzen der Applikation um weitere Android-Komponenten]
<application
......@@ -806,7 +828,7 @@ Im Falle des Frameworks existiert standardmäßig auf Android-Seite lediglich ei
</application>
\end{lstlisting}
Mittels des \lstinline{<application>} Blocks werden innerhalb des Betriebssystem bei starten der Applikation die Beziehung zwischen den einzelnen Teilkomponenten hergestellt.
Mittels des \lstinline{<application>} Blocks werden innerhalb des Betriebssystem beim Starten der Applikation die Beziehung zwischen den einzelnen Teilkomponenten hergestellt.
}
\item{Die mindestens zum Kompilieren der Applikation benötigte SDK-Version:
......@@ -829,10 +851,11 @@ Ebenso wie die mindestens benötigte Android-Version orientiert sich die SDK-Ver
android:required="true"/>
\end{lstlisting}
Standardmäßig wird für die Applikation lediglich die Kamera des Mobilgeräts sowie die zugehörige Berechtigung benötigt; werden in der nutzerspezifisch erstellten Anwendung weitere Features verwendet, sind diese an dieser Stelle im Manifest zu ergänzen.
Standardmäßig wird für die Applikation lediglich die Kamera des Mobilgeräts sowie die zugehörige Berechtigung benötigt. Werden in der nutzerspezifisch erstellten Anwendung weitere Features verwendet, sind diese an dieser Stelle im Manifest zu ergänzen.
}
\end{itemize}
Des weiteren können im Android-Manifest bei komplexeren Applikationen festgelegt werden, mit welcher Activity die Anwendung startet und gegen welche Bibliotheken sie gelinkt werden muss \cite[vgl.\ ][]{manifestAndroid2016}.
Des Weiteren können im Android-Manifest bei komplexeren Applikationen festgelegt werden, mit welcher Activity die Anwendung startet und gegen welche Bibliotheken sie gelinkt werden muss \cite[vgl.\ ][]{manifestAndroid2016}.
\subsection{Layout}
\label{layout}
......@@ -844,7 +867,7 @@ Ein Layout definiert die visuelle Strukturierung eines User-Interfaces wie beisp
\item{Alternativ können die visuellen Elemente bei Ausführung der Anwendung initialisiert werden. Dazu können in der Applikation selbst sogenannte Views erstellt werden.}
\end{itemize}
Wichtig ist dabei, dass neben einem übergeordneten Layout, in dem sozusagen globale (d.\,h.\ für die ganze Applikation gültige) Parameter deklariert werden, ein Layout für jedes erstellte User Interface angelegt werden muss. Im Falle des Frameworks sind dies die Dateien \lstinline{activity_openCV.xml} und \lstinline{openCVFramework_surface_view.xml}. Die Dateien sind grundsätzlich selbsterklärend, da für die Erstellung des Frameworks auf weitere Anpassungen des Layouts verzichtet und stattdessen die Parameter automatisch von der nächsten übergeordneten Instanz, d.\,h.\ vom Betriebssystem bezieht und sich ansonsten an der Standard-Displayauflösung des Mobilgeräts orientiert.
Wichtig ist dabei, dass neben einem übergeordneten Layout, in dem \glqq{}globale\grqq{} (d.\,h.\ für die ganze Applikation gültige) Parameter deklariert werden, ein Layout für jedes erstellte User Interface angelegt werden muss. Im Falle des Frameworks sind dies die Dateien \lstinline{activity_openCV.xml} und \lstinline{openCVFramework_surface_view.xml}. Die Dateien sind grundsätzlich selbsterklärend, da die Parameter automatisch von der nächsten übergeordneten Instanz, d.\,h.\ vom Betriebssystem bezogen werden und sich ansonsten an der Standard-Display"=auflösung des Mobilgeräts orientiert wird.
Weiterhin ist zu erwähnen, dass, falls im Android-Manifest ein Icon definiert wurde, die zugehörige Bilddatei ebenfalls im \lstinline{res}-Ordner in den \lstinline{drawable-XXX}-Verzeichnissen abgelegt werden muss.
......@@ -860,20 +883,20 @@ Weiterhin ist zu erwähnen, dass, falls im Android-Manifest ein Icon definiert w
\label{fig:diag-vbls}
\end{figure}
Auf Basis der in Kapitel \ref{framework} entwickelten Vorlage wurde mit Visual Based Landing System (VBLS) zu Demonstrationszwecken eine Applikation für die automatische Landeplatzlokalisierung von UAVs (Abk., engl. für Unmanned Aerial Vehicle) entwickelt. Mögliche Anwendungsfälle wären beispielsweise die Ermöglichung der selbstständigen Detektion von Ladestationen als Bestandteil eines autonomen Betriebs \cite[vgl.\ ][S.1]{vblsArticle2015} oder das Auffinden eines vordefinierten Landeplatzes in ansonsten unbekanntem oder unwegsamem Gelände. Als Kennzeichnung des Landeplatzes wurde in Anlehnung an die Markierung eines Helikopter-Landeplatzes ein Kreis gewählt.
Als Grundlage implementiert die Anwendung das OpenCV-Android-Framework; um die Unterscheidbarkeit von den generalisierten Klassen des Frameworks zu gewährleisten wurden die für diese Applikation spezialisierten Klassen entsprechend eindeutig benannt. Die Namenskonvention lässt sich dabei wie folgt interpretieren:
Auf Basis der in Kapitel \ref{framework} entwickelten Vorlage wurde mit Visual Based Landing System (VBLS) zu Demonstrationszwecken eine Applikation für die automatische Landeplatzlokalisierung von UAVs (Abk., engl.\ für Unmanned Aerial Vehicle) entwickelt. Mögliche Anwendungsfälle wären beispielsweise die Ermöglichung der selbstständigen Detektion von Ladestationen als Bestandteil eines autonomen Betriebs \cite[vgl.\ ][S.1]{vblsArticle2015} oder das Auffinden eines vordefinierten Landeplatzes in ansonsten unbekanntem oder unwegsamem Gelände. Als Kennzeichnung des Landeplatzes wurde in Anlehnung an die Markierung eines Helikopter-Landeplatzes ein Kreis gewählt.
Als Grundlage implementiert die Anwendung das OpenCV-Android-Framework. Um die Unterscheidbarkeit von den generalisierten Klassen des Frameworks zu gewährleisten wurden die für diese Applikation spezialisierten Klassen entsprechend eindeutig benannt. Die Namenskonvention lässt sich dabei wie folgt interpretieren:
\begin{itemize}
\item{\lstinline{openCVActivity.java} -> \lstinline{VBLSActivity.java}}
\item{\lstinline{openCVFramework.xxx} -> \lstinline{HoughCircleTransformation.xxx} (Hintergrund: Hough-Circle-Transformation ist die zur Detektion der Kreise genutzte Bildverarbeitungsfunktion)}
\end{itemize}
Weiterhin wurden neben der Spezialisierung der Bildverarbeitung die Möglichkeit, dass das Mobilgerät als USB-Host gegenüber verbreiteten Mikrocontrollern wie dem Arduino fungieren und über diese Schnittstelle die erhaltenen Daten kommuniziert werden können, sowie ein PID-Regler zur Regelung der Ausgabewerte in Abhängigkeit von der Position des Landeplatzes im Kamerabild (für die Anpassung der Positionierung des UAVs) implementiert.
Weiterhin wurden die Möglichkeit, dass das Mobilgerät als USB-Host gegenüber verbreiteten Mikrocontrollern wie dem Arduino fungieren und über diese Schnittstelle die erhaltenen Daten kommuniziert werden können, sowie ein PID-Regler zur Regelung der Ausgabewerte in Abhängigkeit von der Position des Landeplatzes im Kamerabild, implementiert.
\section{VBLSActivity.java}
\lstset{language=JAVA}
Die Klasses \lstinline{VBLSActivity.java} wurde im Gegensatz zu \lstinline{openCVActivity.java} um die Verarbeitung der in \lstinline{HoughCircleTransformation.cpp} aus dem übergebenen extrahierten Daten sowie die Implementierung eines Reglers in Form der Klasse \lstinline{PID.java}, welcher die Zentrierung des UAVs über dem detektierten Landeplatz bewirkt, erweitert. Weiterhin wurde die USB-Kommunikation in dieser Activity realisiert.
Die Klasses \lstinline{VBLSActivity.java} wurde im Gegensatz zu \lstinline{openCVActivity.java} um die Verarbeitung der in \lstinline{HoughCircleTransformation.cpp} aus dem übergebenen Bild extrahierten Daten erweitert. Weiterhin wurde ein Regler in Form der Klasse \lstinline{PID.java}, welcher die Zentrierung des UAVs über dem detektierten Landeplatz bewirkt, implementiert und die USB-Kommunikation realisiert.
Anmerkung: Da die USB-Kommunikation in Kapitel \ref{usb} seperat betrachtet wird, wird an dieser Stelle nicht weiter darauf eingegangen.
......@@ -888,7 +911,7 @@ Anmerkung: Da die USB-Kommunikation in Kapitel \ref{usb} seperat betrachtet wird
final static IMG_HEIGHT = 240;
\end{lstlisting}
Wie auch in dem zugrunde liegenden Framework wurde sowohl die Möglichkeit, Farbbilder als auch Graustufenbilder zu verarbeiten implementiert. Da die Natur der Anwendung jedoch eine möglichst hohe Echtzeitfähigkeit (d.\,h.\ in diesem Falle einfach möglichst viele Bildverarbeitungszyklen pro Zeitintervall) fordert, wurde die Priorität an dieser Stelle klar auf möglichst geringen Rechenaufwand gelegt und standardmäßig Graustufen als Format gewählt. Die Auflösung sollte für die beste Performanz möglichst so gering eingestellt werden, dass der als Landeplatz dienende Kreis aus der durchschnittlichen Flughöhe gerade noch so detektiert wird.
Da die Natur der Anwendung eine möglichst hohe Echtzeitfähigkeit (d.\,h.\ in diesem Falle einfach möglichst viele Bildverarbeitungszyklen pro Zeitintervall) fordert, wurde die Priorität an dieser Stelle klar auf möglichst geringen Rechenaufwand gelegt und standardmäßig Graustufen als Format gewählt. Die Auflösung sollte für die beste Performanz möglichst so gering eingestellt werden, dass der als Landeplatz dienende Kreis aus der durchschnittlichen Flughöhe gerade noch so detektiert wird.
\begin{lstlisting}[caption=Implementierung von onCameraFrame]
// Called on every frame received from the input - stream of
......@@ -982,9 +1005,10 @@ Wie auch in dem zugrunde liegenden Framework wurde sowohl die Möglichkeit, Farb
}
\end{lstlisting}
Die konkrete Implementation von \lstinline{onCameraFrame(...)} sieht vor, dass nach der eigentlichen und Kreisdetektion durch \lstinline{HoughCircleTransformation.cpp} (vgl.\ Kapitel \ref{implementationKreisdetektion}) zunächst überprüft wird, ob überhaupt Kreise und somit potentielle Landeplätze gefunden wurden (\lstinline{jImageDataCircels.length > 0}). Ist dies der Fall, so wird mit Hilfe der Funktion \lstinline{calculate-}
\\
\lstinline{NearestCircle(...)} (vgl.\ Kapitel \ref{implementationHoughCircleTransformationJava}) der Kreis, welcher sich am nächsten zum Bildmittelpunkt befindet, ermittelt und zurückgegeben. Somit wird verhindert, dass bei mehreren Einträgen des Arrays \lstinline{jImageDataCircles} immer nur der erste und damit bedingt durch die Natur des Suchalgorithmus der am weitesten links oben im Bild angeordnete Kreis verwendet wird. Die derart erhaltenen Koordinaten werden im Anschluss an den PID-Regler (vgl.\ Kapitel \ref{pid}) übergeben, der wiederum auf dieser Basis die Stellgrößen anpasst. Diese werden mittels \lstinline{get_actuating_variables()} ausgelesen, auf einen Wertebereich von 0 bis 255 umgerechnet (Wertebereich eines Bytes; notwendig, damit bei der Konvertierung von Double zu Byte keine Informationen verloren gehen), und per \lstinline{write(...)} über die USB-Schnittstelle ausgegeben. Wichtig ist, dass sowohl in $x$- als auch in $y$-Richtung ein fester Offset von 127 auf die Ausgabegrößen beaufschlagt werden muss, da diese sich um 0 herum bewegen, der Flugcontroller jedoch Werte um 127 herum erwartet.
Die konkrete Implementation von \lstinline{onCameraFrame(...)} sieht vor, dass nach der eigentlichen Kreisdetektion durch \lstinline{HoughCircleTransformation.cpp} (vgl.\ Kapitel \ref{implementationKreisdetektion}) zunächst überprüft wird, ob überhaupt Kreise (und somit potentielle Landeplätze) gefunden wurden (\lstinline{jImage-}
\lstinline{DataCircels.length > 0}). Ist dies der Fall, so wird mit Hilfe der Funktion \lstinline{calculateNearest-}
\lstinline{Circle(...)} (vgl.\ Kapitel \ref{implementationHoughCircleTransformationJava}) der Kreis, der sich am Nächsten zum Bildmittelpunkt befindet, ermittelt und zurückgegeben. Somit wird verhindert, dass bei mehreren Einträgen des Arrays \lstinline{jImageDataCircles} immer nur der erste und damit bedingt durch die Natur des Suchalgorithmus der am weitesten links oben im Bild angeordnete Kreis verwendet wird. Die derart erhaltenen Koordinaten werden an den PID-Regler (vgl.\ Kapitel \ref{pid}) übergeben, der wiederum auf dieser Basis die Stellgrößen anpasst. Diese Stellgrößen werden mittels \lstinline{get_actuating_va-}
\lstinline{riables()} ausgelesen, auf einen Wertebereich von 0 bis 255 umgerechnet (Wertebereich eines Bytes; notwendig, damit bei der Konvertierung von Double zu Byte keine Informationen verloren gehen) und per \lstinline{write(...)} über die USB-Schnittstelle ausgegeben. Wichtig ist, dass sowohl in $x$- als auch in $y$-Richtung ein fester Offset von 127 auf die Ausgabegrößen beaufschlagt werden muss, da diese sich um 0 herum bewegen, der Flugcontroller jedoch Werte um 127 herum erwartet.
\\
\\
Anmerkung: Der PID-Regler arbeitet threadbasiert. Um Nebenläufigkeitskomplikationen auszuschließen, ist es daher notwendig, beim Zugriff auf die Stellgrößen eine einmalige lokale Kopie dieser anzulegen und im Anschluss mit dieser Kopie zu arbeiten, anstatt sowohl für $x$- als auch für $y$-Richtung jeweils einzeln \lstinline{get_actuating_variables()} aufzurufen. Sonst läuft man Gefahr, dass die Stellgrößen zwischen dem ersten und dem zweiten Zugriff aktualisiert wurden und die beiden ausgelesenen Werte nicht mehr miteinander korrelieren.
......@@ -1017,15 +1041,17 @@ Anmerkung: Der PID-Regler arbeitet threadbasiert. Um Nebenläufigkeitskomplikati
}
\end{lstlisting}
Die Funktion \lstinline{calculateNearestCircle(...)} überprüft anhand der Länge des die Kreiskoordinaten enthaltenden Arrays, ob bei der Bildverarbeitung mehrere Kreise und somit potentielle Landeplätze detektiert wurden. Falls dies der Fall sein sollte, wird der dem Bildmittelpunkt am Nächsten gelegene Kreis ermittelt und zurückgegeben. Dazu wird zunächst der Abstand des ersten Positions-Eintrags des Arrays als minimaler Abstand festgelegt und anschließend für alle weiteren Kreiskoordinaten überprüft, ob deren Abstand zum Bildmittelpunkt geringer ist. Ist dies der Fall, wird der entsprechende Eintrag als neue Referenz gespeichert. Somit bleibt am Ende lediglich derjenige Kreis mit der geringsten Entfernung zur Bildmitte übrig.
Die Funktion \lstinline{calculateNearestCircle(...)} überprüft anhand der Länge des die Kreiskoordinaten enthaltenden Arrays, ob bei der Bildverarbeitung mehrere Kreise detektiert wurden. Falls dies der Fall sein sollte, wird der dem Bildmittelpunkt am Nächsten gelegene Kreis ermittelt und zurückgegeben. Dazu wird zunächst der Abstand des ersten Positions-Eintrags des Arrays als minimaler Abstand festgelegt und anschließend für alle weiteren Kreiskoordinaten überprüft, ob deren Abstand zum Bildmittelpunkt geringer ist. Ist dies der Fall, wird der entsprechende Eintrag als neue Referenz gespeichert. Somit bleibt am Ende derjenige Kreis mit der geringsten Entfernung zur Bildmitte übrig.
Dies ist notwendig, da ansonsten immer der am weitesten links oben angeordnete Kreis als Ziel gewählt würde, da die Bildverarbeitung das Bild von der linken oberen Ecke aus durchläuft und somit dessen Koordinaten an erster Stelle im Array stehen würden.
Anmerkung: Der Offset von \lstinline{0.5*img_width} bzw. \lstinline{0.5*img_height} ergibt sich dadurch, dass der Nullpunkt des Bildes standardmäßig in der oberen linken Ecke liegt.
\\
\\
Anmerkung: Der Offset von \lstinline{0.5*img_width} bzw.\ \lstinline{0.5*img_height} ergibt sich dadurch, dass der Nullpunkt des Bildes standardmäßig in der oberen linken Ecke liegt.
\section{HoughCircleTransformation.cpp}
\label{implementationKreisdetektion}
\lstset{language=C++}
\lstinline{HoughCircleTransformation.cpp} basiert auf der Klasse \lstinline{openCVFramework.cpp} des Frameworks und implementiert die OpenCV-Methoden Lokalisierung von Kreisen und damit potentiellen Landeplätzen im per JNI übergebenen Bild.
\lstinline{HoughCircleTransformation.cpp} basiert auf der Klasse \lstinline{openCVFramework.cpp} des Frameworks und implementiert die OpenCV-Methoden zur Lokalisierung von Kreisen und damit potentiellen Landeplätzen.
\begin{lstlisting}[caption=Kreisdetektion]
// Reduce the noise so false circle detection is avoided (or
......@@ -1037,7 +1063,7 @@ Anmerkung: Der Offset von \lstinline{0.5*img_width} bzw. \lstinline{0.5*img_heig
(double) src_gray.rows/8, 150.0, 75.0, 0, 0);
\end{lstlisting}
Nachdem das in Byte-Array-Form per JNI übergebene Bild erneut zurück in eine Matrix umgewandelt wurde (s. Kapitel \ref{openCVFramework.cpp}, wird zunächst mittels \lstinline{GaussianBlur(...)} eine Reduzierung des Rauschens im Bild durchgeführt. Dafür werden jeweils die Pixel in direkter Nachbarschaft zum aktuell betrachteten Bildpunkt untersucht und deren Farb- oder Helligkeitswert (je nachdem, ob Farb- oder Graustufenbilder verwendet werden) in Relation gesetzt. Ist also beispielsweise das aktuelle Pixel schwarz, alle benachbarten Bildpunkte jedoch weiß, so wird der Wert des Pixels ebenfalls auf weiß gesetzt. Diese Verminderung des Rauschens ist notwendig, um bei nachfolgender Objektlokalisierung gute Ergebnisse zu erzielen.
Nachdem das in Byte-Array-Form per JNI übergebene Bild erneut zurück in eine Matrix umgewandelt wurde (s.\ Kapitel \ref{openCVFramework.cpp}, wird zunächst mittels \lstinline{GaussianBlur(...)} eine Reduzierung des Rauschens im Bild durchgeführt. Dafür werden jeweils die Pixel in direkter Nachbarschaft zum aktuell betrachteten Bildpunkt untersucht und deren Farb- oder Helligkeitswert (je nachdem, ob Farb- oder Graustufenbilder verwendet werden) in Relation gesetzt. Ist also beispielsweise das aktuelle Pixel schwarz, alle benachbarten Bildpunkte jedoch weiß, so wird der Wert des Pixels ebenfalls auf weiß gesetzt. Diese Verminderung des Rauschens ist notwendig, um bei der nachfolgenden Objektlokalisierung bessere Ergebnisse erzielen zu können.
Anschließend wird mittels \lstinline{HoughCircles(...)} die eigentliche Kreisdetektion durchgeführt. Diese basiert ebenfalls darauf, dass Cluster von benachbarten, gleichfarbigen Pixeln betrachtet werden und zu jedem dieser Cluster ein zugehöriger Kreisradius und -mittelpunkt interpoliert werden. Liegen genügend Mittelpunkte von Clustern mit gleichem Radius in einem gewissen Bereich, so wird angenommen, dass diese Pixel zu einem Kreis gehören. Die Attribute der gefundenen Kreise (bestehend aus den Koordinaten des Mittelpunkts und dem Radius) werden in einem einem Array von Vektoren, \lstinline{circles}, gespeichert. Die letzten fünf Übergabeparameter der Funktion stellen dabei konfigurierbare Parameter wie beispielsweise minimale und maximale Radien oder Thresholds für die Kantendetektion dar.
......@@ -1051,7 +1077,6 @@ konsultiert werden.
vec_size = circles.size();
sub_vec_size = 3;
double *outCArray;
//LOGD("%d, %d", vec_size, sub_vec_size);
outCArray = (double *) malloc((vec_size*sub_vec_size)*sizeof(
double)); // Malloc to prevent the application from using
// new memory space on every call of this method
......@@ -1059,21 +1084,20 @@ konsultiert werden.
for (int j = 0; j < sub_vec_size; j++) {
outCArray[i*sub_vec_size+j] = cvRound((double)circles[i][
j]);
//LOGD("%d, %d", i, j);
}
}
\end{lstlisting}
Um die extrahierten Daten anschließend wieder über das JNI an \lstinline{VBLSActivity.java} zurückgeben zu können, muss die Vektordatenstruktur von \lstinline{circles} in ein Array eines nativen Datentyps, in diesem Falle Double, konvertiert werden. Dafür wird zunächst die Größe des benötigten Feldes berechnet und der entsprechende Speicherplatz per \lstinline{malloc(...)} reserviert. Anschließend werden die einzelnen den detektierten Kreisen zugehörigen Vektoren nacheinander ausgelesen und deren Inhalt in das Array geschrieben. Die \lstinline{sub_vec_size} von drei kommt daher, dass jedem Kreis sowohl der Radius als auch die Koordinaten des Mittelpunkts zugeordnet werden. Anschließend wird das Array wie in \lstinline{openCVFramework.cpp} (s. Kapitel \ref{openCVFramework.cpp}) per JNI zurückgegeben.
Um die extrahierten Daten anschließend wieder über das JNI an \lstinline{VBLSActivity.java} zurückgeben zu können, muss die Vektordatenstruktur von \lstinline{circles} in ein Array eines nativen Datentyps, in diesem Falle Double, konvertiert werden. Dafür wird zunächst die Größe des benötigten Feldes berechnet und der entsprechende Speicherplatz per \lstinline{malloc(...)} reserviert. Anschließend werden die einzelnen den detektierten Kreisen zugehörigen Vektoren nacheinander ausgelesen und deren Inhalt in das Array geschrieben. Die \lstinline{sub_vec_size} von drei kommt daher, dass jedem Kreis sowohl der Radius als auch die Koordinaten des Mittelpunkts zugeordnet werden. Anschließend wird das Array wie in \lstinline{openCVFramework.cpp} (s.\ Kapitel \ref{openCVFramework.cpp}) per JNI zurückgegeben.
\section{USB-Kommunikation}
\label{usb}
\subsection{Anforderungen an die zu verwendende Bibliothek}
Wie bereits in Kapitel \ref{vblsAufbau} beschrieben, verfügen Android-Mobilgeräte standardmäßig nicht über die Möglichkeit, als Host gegenüber anderen USB-Geräten zu fungieren. Für diesen Zweck stellt das Betriebssystem das sogenannte Open Accessory zur Verfügung \cite[vgl.\ ][S.199ff.]{schwark2016}. Dabei simuliert das Android dem angeschlossenen USB-Gerät, der Host zu sein, obwohl in Realität letzteres die Host-Funktion übernimmt. Da diese Funktionalität jedoch bisher nur in wenigen Chips von FTDI integriert und damit sehr unflexibel hinsichtlich der Gerätekompabilität.
Wie bereits in Kapitel \ref{vblsAufbau} beschrieben, verfügen Android-Mobilgeräte standardmäßig nicht über die Möglichkeit, als Host gegenüber anderen USB-Geräten zu fungieren. Für diesen Zweck stellt das Betriebssystem das sogenannte Open Accessory zur Verfügung \cite[vgl.\ ][S.199ff.]{schwark2016}. Dabei simuliert das Android dem angeschlossenen USB-Gerät, der Host zu sein, obwohl in Realität letzteres die Host-Funktion übernimmt. Diese Funktionalität ist bisher jedoch nur in wenigen Chips von FTDI integriert und somit sehr unflexibel hinsichtlich der Gerätekompabilität.
Alternativ zu Open Accessory besteht die Möglichkeit, eine von mehreren zur Verfügung stehenden Bibliotheken inklusive Treibern für die anzusprechenden Endgeräte in die Applikation zu integrieren. Von zentraler Wichtigkeit bei der Auswahl der passenden Bibliothek ist deren Lizenz. Um der in der Zielsetzungen des Projekts (s. Kapitel \ref{ziel}) geforderte möglichst einfache Zugänglichkeit zu gewährleisten, ist es wichtig, ein Projekt unter einer Open Source Lizenz auszuwählen. Von technischer Seite aus sollte besonderes Augenmerk auf die Flexibilität bzgl.\ der USB-Chips, die jede der Bibliotheken unterstützt, gelegt werden, um somit eine möglichst große Anzahl an Endgeräten unterstützen zu können. Weiterhin ist es wichtig, dass asynchrone Kommunikation unterstützt wird, um die Applikation nicht unnötig zu verlangsamen (die Bildverarbeitung im UI-Thread kann weiter stattfinden, während der USB-Thread kommuniziert). Zuletzt ist eine gute Dokumentation und Verständlichkeit der Bibliothek wichtig, um die Implementierung einfach und korrekt durchführen zu können.
Alternativ zu Open Accessory besteht die Möglichkeit, eine von mehreren zur Verfügung stehenden Bibliotheken inklusive Treibern für die anzusprechenden Endgeräte in die Applikation zu integrieren. Von zentraler Wichtigkeit bei der Auswahl der passenden Bibliothek ist deren Lizenz. Um der in der Zielsetzungen des Projekts (s.\ Kapitel \ref{ziel}) geforderte möglichst einfache Zugänglichkeit zu gewährleisten, ist es wichtig, ein Projekt unter einer Open Source-Lizenz auszuwählen. Von technischer Seite aus sollte besonderes Augenmerk auf die Flexibilität bzgl.\ der USB-Chips, die jede der Bibliotheken unterstützt, gelegt werden, um eine möglichst große Anzahl an Endgeräten unterstützen zu können. Weiterhin ist es wichtig, dass asynchrone Kommunikation unterstützt wird, um die Applikation nicht unnötig zu verlangsamen (die Bildverarbeitung im UI-Thread kann weiter stattfinden, während der USB-Thread kommuniziert). Zuletzt ist eine gute Dokumentation und Verständlichkeit der Bibliothek wichtig, um die Implementierung einfach und korrekt durchführen zu können.
\\
\\
Auf Grundlage dieser Kriterien wurde sich für die USB-Serial-Bibliothek von Felipe Herranz, zu finden unter \url{https://github.com/felHR85/UsbSerial}, unter der MIT-Lizens ausgewählt.
......@@ -1088,8 +1112,9 @@ Bei der Entwicklung von Android-Applikationen wird bzgl.\ des funktionalen Codes
\item{Einbindung in die Activity}
\end{itemize}
Die der Implemetierung des Services dienliche Klasse \lstinline{USB_Service.java} orientiert sich stark an der der Bibliothek beiliegenden Beispiel-Implementation (ebenfalls zu finden unter \url{https://github.com/felHR85/UsbSerial}) und ist an dieser Stelle gut dokumentiert; dementsprechend soll an dieser Stelle nicht weiter darauf eingegangen, sondern sich lediglich auf die Integration in \lstinline{VBLSActivity.java} fokussiert werden. Einzig erwähnenswert an dieser Stelle ist, dass mittels der zu Beginn des Services definierten Variable \lstinline{BAUD_RATE} die für die Kommunikation zu verwendende Baud-Rate eingestellt werden kann.
Die der Implemetierung des Services dienliche Klasse \lstinline{USB_Service.java} orientiert sich stark an der der Bibliothek beiliegenden Beispiel-Implementation (ebenfalls zu finden unter \url{https://github.com/felHR85/UsbSerial}) und ist an dieser Stelle gut dokumentiert. Dementsprechend soll an dieser Stelle nicht weiter darauf eingegangen, sondern sich lediglich auf die Integration in \lstinline{VBLSActivity.java} fokussiert werden. Einzig erwähnenswert an dieser Stelle ist, dass mittels der zu Beginn des Services definierten Variable \lstinline{BAUD_RATE} die für die Kommunikation zu verwendende Baud-Rate eingestellt werden kann.
\\
\\
Anmerkung: Für Nutzer, die neu in der Android-Entwicklung sind, ist es grundsätzlich empfehlenswert, sich die offizielle Dokumentation zu Services unter \url{https://developer.android.com/guide/components/services.html} durchzulesen.
\begin{lstlisting}[caption=Initialisierung des USB-Daten-Arrays]
......@@ -1140,7 +1165,7 @@ Zunächst wird ein Daten-Array initialisiert, in das die zu versendenden Daten g
}
\end{lstlisting}
Mittels der Service-Connnection sowie der Funktion \lstinline{startService} wird der Service aus der Activity heraus gestartet und an die Activity gebunden. Die Bindeanfrage wird dabei mittels des Intents \lstinline{bindingIntent} über dei Funktion \lstinline{bindService} kommuniziert \lstinline{startService} wird bei sowohl bei erstmaligem Erstellen der Applikation als auch, wenn die Anwendung aus gestopptem Zustand heraus fortgesetzt wird mittels \lstinline{onResume()} aufgerufen. Bei Pausieren (\lstinline{onPause()} wird der Service gestoppt und der zugehörige Broadcast-Receiver mittels \lstinline{unregisterReceiver(...)} entfernt.
Mittels der Service-Connnection sowie der Funktion \lstinline{startService} wird der Service aus der Activity heraus gestartet und an die Activity gebunden. Die Bindeanfrage wird dabei mittels des Intents \lstinline{bindingIntent} über die Funktion \lstinline{bindService} kommuniziert. \lstinline{startService} wird sowohl bei erstmaligem Erstellen der Applikation als auch, wenn die Anwendung aus gestopptem Zustand heraus fortgesetzt wird mittels \lstinline{onResume()} aufgerufen. Bei Pausieren (\lstinline{onPause()}) wird der Service gestoppt und der zugehörige Broadcast-Receiver mittels \lstinline{unregisterReceiver(...)} entfernt.
\begin{lstlisting}[caption=Einrichtung des Broadcast-Receivers]
private final BroadcastReceiver mUsbReceiver = new
......@@ -1193,7 +1218,7 @@ Mittels der Service-Connnection sowie der Funktion \lstinline{startService} wird
}
\end{lstlisting}
Um miteinander zu kommunizieren, nutzen Activities und Services die Broadcast-Funk"=tionalität. Dabei kannn jede Komponente sogennante Intents oder Messages über das Broad"=cast-System versenden. Andere Komponenten können Broadcast-Receiver (s. Kapitel \ref{introductionAndroid}) einrichten, um allen auf diese Art im System gesendeten Nachrichten zu lauschen und Aktionen für bestimmte Inhalte definieren. Mittels \lstinline{setFilters()} können die zu empfangenden Intents oder Messages festgelegt werden; die Komponente \lstinline{abboniert} sie. Anschließend wird der Receiver mittels \lstinline{registerReceiver(...)} registriert. Im konkreten Fall werden lediglich Nachrichten des Services empfangen.
Um miteinander zu kommunizieren, nutzen Activities und Services die Broadcast-Funk"=tionalität. Dabei kann jede Komponente sogennante Intents oder Messages über das Broad"=cast-System versenden. Andere Komponenten können Broadcast-Receiver (s.\ Kapitel \ref{introductionAndroid}) einrichten, um allen auf diese Art im System gesendeten Nachrichten zu lauschen und Aktionen für bestimmte Inhalte definieren. Mittels \lstinline{setFilters()} können die zu empfangenden Intents oder Messages festgelegt werden; die Komponente \lstinline{abboniert} sie. Anschließend wird der Receiver mittels \lstinline{registerReceiver(...)} registriert. Im konkreten Fall werden lediglich Nachrichten des Services empfangen.
\begin{lstlisting}[caption=Einrichten und Weiterreichen des USB-Handlers]
/*
......@@ -1239,12 +1264,12 @@ Um miteinander zu kommunizieren, nutzen Activities und Services die Broadcast-Fu
usbService.setHandler(usbHandler);
\end{lstlisting}
Eine weitere Möglichkeit der Kommunikation zwischen Activity und Service besteht neben dem Versenden von Intents über das Broadcast-System im Überliefern von Messages mittels Handlern. Wie bereits in Kapitel \ref{introductionAndroid} beschrieben, dienen Intents dazu, Anfragen zu übermitteln, während Messages reine Mitteilungen darstellen. Die Klasse \lstinline{usbHandler} dient zur Verarbeitung der zwischen beiden Komponenten versendeten Messages. In ihr werden verschiedene Aktionen für bestimmte Nachrichten wie beispielsweise das Ausgeben der vom Endgerät empfangenen Daten bei \lstinline{USB_Service.MESSAGE_FROM_SERIAL_PORT} (für die konkrete Anwendung uninteressant, da nur Daten versendet und nicht empfangen werden) definiert. Anschließend wird das in der Aktivity erzeugte und mittels \lstinline{WeakReference} gebundene Objekt per \lstinline{setHandler(...)} an den Service übergeben, wodurch es diesem ermöglicht wird, Messages über den Handler zu versenden.
Eine weitere Möglichkeit der Kommunikation zwischen Activity und Service besteht neben dem Versenden von Intents über das Broadcast-System im Überliefern von Messages mittels Handlern. Wie bereits in Kapitel \ref{introductionAndroid} beschrieben, besteht die Aufgabe von Intents darin, Anfragen zu übermitteln, während Messages reine Mitteilungen darstellen. Die Klasse \lstinline{usbHandler} dient zur Verarbeitung der zwischen beiden Komponenten versendeten Messages. In ihr werden verschiedene Aktionen für bestimmte Nachrichten wie bspw.\ das Ausgeben der vom Endgerät empfangenen Daten bei \lstinline{USB_Service.MESSAGE_FROM_SERIAL_PORT} (für die konkrete Anwendung uninteressant, da nur Daten versendet und nicht empfangen werden) definiert. Anschließend wird das in der Aktivity erzeugte und mittels \lstinline{WeakReference} gebundene Objekt per \lstinline{setHandler(...)} an den Service übergeben, wodurch es diesem ermöglicht wird, Messages über den Handler zu versenden.
\section{PID.java}
\label{pid}
Wie bereits in der Zielsetzung (vgl.\ Kapitel \ref{ziel}) definiert, soll die zu Demonstrationszwecken dienende Applikation zuverlässig einen Landeplatz zu lokalisieren und es dem UAV ermöglichen, sich über diesem zu zentrieren. Da es sich jedoch bei einem UAV im Normalfall um ein träges, nicht mechanisch geführtes System handelt, das weiterhin von Einflüssen umwelttechnischer oder mechanischer Art wie beispielsweise Windböen oder einer ungenauen Kalibrierung der Sensorik betroffen sein kann, ist es für eine sichere Positionierung über dem Mittelpunkt des detektierten Landeplatzes unerlässlich, eine Positionsregelung zu implementieren, um die Fluggeschwindigkeit zu regulieren und starke Überschwinger zu vermeiden. Dies geschieht mittels der Klasse \lstinline{PID.java} in Form eines klassischen PID-Reglers.
Wie bereits in der Zielsetzung (vgl.\ Kapitel \ref{ziel}) definiert, soll die zu Demonstrationszwecken dienende Applikation zuverlässig einen Landeplatz zu lokalisieren und es dem UAV ermöglichen, sich über diesem zu zentrieren. Da es sich jedoch bei einem UAV im Normalfall um ein träges, nicht mechanisch geführtes System handelt, das weiterhin von Einflüssen umwelttechnischer oder mechanischer Art wie bspw.\ Windböen oder einer ungenauen Kalibrierung der Sensorik betroffen sein kann, ist es für eine sichere Positionierung über einem detektierten Landeplatzes unerlässlich, eine Positionsregelung zu implementieren, um die Fluggeschwindigkeit zu regulieren und starke Überschwinger zu vermeiden. Dies geschieht mittels der Klasse \lstinline{PID.java} in Form eines klassischen PID-Reglers.
\begin{lstlisting}[caption=Hilfsklasse für Koordinatentupel]
// Immutable class to represent a coordinate tuple
......@@ -1267,7 +1292,7 @@ Wie bereits in der Zielsetzung (vgl.\ Kapitel \ref{ziel}) definiert, soll die zu
}
\end{lstlisting}
Da für die Realisierung des Reglers ein threadbasierter Ansatz gewählt wurde und im Bezug auf die Stellgrößen auf der Basis von Referenzen gearbeitet wird (s.u.), wird eine Klasse benötigt, deren Objekte es erlauben, ein Koordinatenpaar zu übergeben und auf diese Werte zuzugreifen. Diesen Zweck erfüllt \lstinline{Coordinates_Immutable}, da Java standardmäßig keine Tupel unterstützt. Die Klasse ist aus Gründen der Zugriffssicherheit nach dem Entwurfsmuster \glqq{}Immutable\grqq{} (einmalige schreibende Zuweisung der Werte, ansonsten lediglich lesender Zugriff) gestaltet.
Da für die Realisierung des Reglers ein threadbasierter Ansatz gewählt wurde und im Bezug auf die Stellgrößen auf der Basis von Referenzen gearbeitet wird (s.\,u.), wird eine Klasse benötigt, deren Objekte es erlauben, ein Koordinatenpaar zu übergeben und auf diese Werte zuzugreifen. Diesen Zweck erfüllt \lstinline{Coordinates_Immutable}, da Java standardmäßig keine Tupel unterstützt. Die Klasse ist aus Gründen der Zugriffssicherheit nach dem Entwurfsmuster \glqq{}Immutable\grqq{} (einmalige schreibende Zuweisung der Werte, ansonsten lediglich lesender Zugriff) gestaltet.
\begin{lstlisting}[caption=Erläuterung der Parameter]
// Constructor
......@@ -1296,10 +1321,9 @@ Da für die Realisierung des Reglers ein threadbasierter Ansatz gewählt wurde u
\end{lstlisting}
Wie bereits zu Beginn der Sektion angeführt, weist der implementierte Regler in seiner Grundform eine standardmäßige PID-Charakteristik auf. Folglich entsprechen die übergebenen Werte für \lstinline{Kp}, \lstinline{Ki} und \lstinline{Kd} den Koeffizienten für den Proportional-, Integral- und Differentialanteil, sowie \lstinline{set_point_x} und \lstinline{set_point_y} dem Arbeitspunkt.
Da sowohl der I-, als auch der D-Anteil eines PID-Reglers zeitabhängig sind, wurde zur Realisierung dieser Funktioalität ein threadbasierter Ansatz gewählt. Die Run-Methode des Threads führt dabei jeweils die Berechnung aus; anschließend schläft der Thread bis zu seinem nächsten Aufruf und gibt belegte Ressourcen frei (\lstinline{sleep(...)}). Mittels \lstinline{dt} kann dabei die Schrittweite bzw. das Zeitintervall, in dem der Regler aufgerufen wird, festgelegt werden (in Millisekunden).
Da sowohl der I-, als auch der D-Anteil eines PID-Reglers zeitabhängig sind, wurde zur Realisierung dieser Funktioalität ein threadbasierter Ansatz gewählt. Die Run-Methode des Threads führt dabei jeweils die Berechnung aus. Anschließend schläft der Thread bis zu seinem nächsten Aufruf und gibt belegte Ressourcen frei (\lstinline{sleep(...)}). Mittels \lstinline{dt} kann die Schrittweite bzw.\ das Zeitintervall, in dem der Regler aufgerufen wird, festgelegt werden (in Millisekunden).
Da nun jedoch sowohl schreibender (auf die Eingangsgrößen) als auch lesender (auf die Stellgrößen) Zugriff aus einem anderen Thread (dem Main-Thread) auf Elemente des Regler-Threads erfolgen soll, ist es notwendig, die betroffenen Objekte threadsicher zu implementieren. Ansonsten kann es zu Nebenläufigkeitsproblematiken kommen, z.\,B.\ erster Wert der Stellgröße wird ausgelesen -> Aktualisierung der Stellgröße von Seiten des Reglers -> zweiter Wert wird ausgelesen -> ausgelesene Werte korrelieren nicht miteinander. Zu diesem Zweck werden die betroffenen Elemente als threadsichere Referenzen, sogenannten \lstinline{AtomicRefe-}
\lstinline{rences}, auf Objekte der Klasse \lstinline{Coordinates_Immutable} implementiert. Somit erfolgt der Zugriff auf die Objekte selbst threadsicher und es wird immer auf beide betroffenen Werte ($x$- und $y$-Richtung) gleichhzeitig zugegriffen.
Da nun sowohl schreibender (auf die Eingangsgrößen) als auch lesender (auf die Stellgrößen) Zugriff aus einem anderen Thread (dem Main-Thread) auf Elemente des Regler-Threads erfolgen soll, ist es notwendig, die betroffenen Objekte threadsicher zu implementieren. Ansonsten kann es zu Nebenläufigkeitsproblematiken kommen, z.\,B.\ erster Wert der Stellgröße wird ausgelesen -> Aktualisierung der Stellgröße von Seiten des Reglers -> zweiter Wert wird ausgelesen -> ausgelesene Werte korrelieren nicht miteinander. Zu diesem Zweck werden die betroffenen Elemente als threadsichere Referenzen, sogenannten \lstinline{AtomicReferences}, auf Objekte der Klasse \lstinline{Coordinates_Immutable} implementiert. Somit erfolgt der Zugriff auf die Objekte selbst threadsicher und es wird immer auf beide betroffenen Werte ($x$- und $y$-Richtung) gleichzeitig zugegriffen.
\begin{lstlisting}[caption=Lesender und schreibender Zugriff auf threadsichere Objekte]
// Gets the actual actuating variable
......@@ -1323,10 +1347,10 @@ Da nun jedoch sowohl schreibender (auf die Eingangsgrößen) als auch lesender (
}
\end{lstlisting}
Lesender Zugriff auf die durch threadsichere Referenzen gekapselten Elemente erfolgt, indem zunächst mittels \lstinline{get()} das Objekt selbst und anschließend dessen Inhalt zurückgegeben wird. Dabei ist auch an dieser Stelle darauf zu achten, dass um Nebenläufigkeitsproblematiken zu vermeiden, \lstinline{get()} genau einmal aufgerufen und anschließend mit einer Kopie der Objekte gearbeitet werden sollte. Eine flache Kopie (lediglich die Referenz) ist ausreichend, da bei einer Aktualisierung der Stellgrößen nicht die Werte der Inhalt des Objekts verändert wird(nicht möglich, da immutable), sondern ein komplett neues Objekt erzeugt wird (genau ein schreibender Aufruf und nicht zwei und damit erneut Anfälligkeit für Nebenläufigkeitsproblematiken).
Lesender Zugriff auf die durch threadsichere Referenzen gekapselten Elemente erfolgt, indem zunächst mittels \lstinline{get()} das Objekt selbst und anschließend dessen Inhalt zurückgegeben wird. Dabei ist auch an dieser Stelle darauf zu achten, dass um Nebenläufigkeitsproblematiken zu vermeiden, \lstinline{get()} genau einmal aufgerufen und anschließend mit einer Kopie der Objekte gearbeitet werden sollte. Eine flache Kopie (lediglich die Referenz) ist ausreichend, da bei einer Aktualisierung der Stellgrößen nicht die Werte der Inhalt des Objekts verändert werden (nicht möglich, da immutable), sondern ein komplett neues Objekt erzeugt wird (genau ein schreibender Aufruf und nicht zwei und damit erneut Anfälligkeit für Nebenläufigkeitsproblematiken).
Dieses Vorgehen kann man ebenfalls bei der Funktion \lstinline{set_values(...)} sehen, mittels der aus dem Main-Thread bei jedem verarbeitetem Bild die Eingangsgrößen geschrieben werden. Auch an dieser Stelle werden nicht die Werte verändert, sondern die \lstinline{AtomicRefe-}
\lstinline{rence} auf ein neues Objekt der Klasse \lstinline{Coordinates_Immutable} mit den neuen Eingangsgrößen gesetzt. Das \glqq{}alte\grqq{} Element wird dann nicht mehr verwendet und somit vom Garbage Collector der JVM freigegeben.
Dieses Vorgehen kann man ebenfalls bei der Funktion \lstinline{set_values(...)} sehen, mittels der aus dem Main-Thread bei jedem verarbeiteten Bild die Eingangsgrößen geschrieben werden. Auch an dieser Stelle werden nicht die Werte verändert, sondern die \lstinline{AtomicRefe-}
\lstinline{rence} auf ein neues Objekt der Klasse \lstinline{Coordinates_Immutable} mit den neuen Eingangsgrößen gesetzt. Das \glqq{}alte\grqq{} Element wird somit nicht länger verwendet und vom Garbage Collector der JVM freigegeben.
\begin{lstlisting}[caption=Berechnung der Stellgrößen]
// Thread to calculate the actuating variables for a given
......@@ -1409,7 +1433,7 @@ Durch das Hinzukommen der Kommunikation über die USB-Schnittstelle des Mobilger
</application>
\end{lstlisting}
Zusätzlich zu der eigentlichen Activity implementiert die Anwendung einen Service, um die USB-Schnittstelle handzuhaben (s. Kapitel \ref{usb}). Weiterhin wird in \lstinline{VBLSActivity.java} selbst ein Intent-Filter eingebettet, um zwischen Service und Activity kommunizieren zu können. Dies spiegelt sich in dieser Form auch in der in \lstinline{<application>} eingebetteten Struktur der Anwendung wieder.
Zusätzlich zu der eigentlichen Activity implementiert die Anwendung einen Service, um die USB-Schnittstelle handzuhaben (s.\ Kapitel \ref{usb}). Weiterhin wird in \lstinline{VBLSActivity.java} ein Intent-Filter eingebunden, um zwischen Service und Activity kommunizieren zu können. Dies spiegelt sich in dieser Form auch in der in \lstinline{<application>} eingebetteten Struktur der Anwendung wieder.
\begin{lstlisting}[caption=Verwendete Features]
<uses-feature android:name="android.hardware.usb.host"
......@@ -1420,15 +1444,15 @@ Zusätzlich zu der eigentlichen Activity implementiert die Anwendung einen Servi
autofocus" android:required="true"/>
\end{lstlisting}
Des weiteren muss der Liste der verwendeten Features die USB-Schnittstelle hinzugefügt werden.
Des Weiteren muss der Liste der verwendeten Features die USB-Schnittstelle hinzugefügt werden.
\chapter{Versuchsaufbau und Tests}
\section{Versuchsaufbau}
\label{aufbau}
Dieses Projekt teilt sich mit seinem Schwesterprojekt, der Entwicklung eines echtzeitfähigen RC-Mischers auf Arduino-Basis \footnote{LeonardoMixerIO - \url{https://gitlab.cvh-server.de/lf.ps/vbls/tree/master/LeonardoMixerIO}}, einen gemeinsamen Versuchsaufbau.
Im Folgenden soll ein Überblick über die Anordnung der Komponenten, insbesondere des Android-Mobil"=telefones, gegeben werden.
Dieses Projekt teilt sich einen gemeinsamen Versuchsaufbau mit einem Parallelprojekt, der Entwicklung eines echtzeitfähigen RC-Mischers auf Arduino-Basis \footnote{LeonardoMixerIO - \url{https://gitlab.cvh-server.de/lf.ps/vbls/tree/master/LeonardoMixerIO}}.
Im Folgenden soll ein Überblick über die Anordnung der Komponenten gegeben werden.
\\
\\
Der Versuchsaufbau besteht aus einem Quadrocopter in X-Anordung.
......@@ -1449,7 +1473,7 @@ Die Hauptdiagonale zwischen zwei Rotoren beträgt 415mm, der Rotordurchmesser be
Die Masse des Aufbaus beträgt 375g. Zu dieser kommen weitere 98g für den verwendeten
zwei-Zellen-LiPo vom Typ \glqq{}Gens ace 1800mAh-20C-7.4V-2S1P\grqq{}, sowie 142g
für das verwendete Mobiltelefon vom Typ \glqq{}HTC One S\grqq{}. Die Gesamtmasse
für das verwendete Mobiltelefon vom Typ \glqq{}HTC One S\grqq{} hinzu. Für die Gesamtmasse
des flugbereiten Aufbaus ergibt sich folglich ein Gewicht von 515g. Die maximale Tragfähigkeit des
Quadrocopters ist nicht bekannt, das Flugverhalten bei diesem Gewicht suggeriert
jedoch eine Auslastung von etwa 70\%-80\%.
......@@ -1493,18 +1517,18 @@ Um das UAV außerhalb der autonomen Landeplatzsuche steuern, sowie bei unerwarte
\section{Einstellung des Reglers}
Um ein sicheres autonomes Flugverhalten während der Landeplatzdetektion realisieren zu können, ist eine korrekte Parametrierung des Reglers notwendig (vgl.\ Kapitel \ref{pid}). Zu diesem Zweck muss zunächst die Art des betrachteten Systems bestimmt werden. Dies kann beispielsweise durch die Betrachtung der Übergangsfunktion als Antwort auf eine Beaufschlagung des Systems mit einem Sprung des Eingangssignals (in diesem Fall z.\,B.\ das Erscheinen eines Kreises am äußeren Rand des betrachteten Bildes) geschehen. Mittels dieser Methode ergibt sich, dass es sich bei dem vorliegenden System, um ein IT1-System handelt. D.\,h.\ die Ausgangsgröße, in diesem Fall der zurückgelegte Weg des Quadrocopters in $x$- oder $y$-Richtung, verhält sich mit einer gewissen Anlaufverzögerung integral zur Eingangsgröße, dem Steuersignal in diesen Dimensionen. Dies entspricht dem erwarteten Verhalten, dass das UAV bei einem Sprung des Steuersignals nach einer Beschleunigungsphase (bedingt durch seine Massenträgheit) eine lineare Änderung der Trajektorie aufweist.
Um ein sicheres autonomes Flugverhalten während der Landeplatzdetektion realisieren zu können, ist eine korrekte Parametrierung des Reglers notwendig (vgl.\ Kapitel \ref{pid}). Zu diesem Zweck muss zunächst die Art des betrachteten Systems bestimmt werden. Dies kann beispielsweise durch die Betrachtung der Übergangsfunktion als Antwort auf eine Beaufschlagung des Systems mit einem Sprung des Eingangssignals (in diesem Fall z.\,B.\ das Erscheinen eines Kreises am äußeren Rand des betrachteten Bildes) geschehen. Mittels dieser Methode ergibt sich, dass es sich bei dem vorliegenden System, um ein IT1-System handelt. D.\,h.\ die Ausgangsgröße des Systems, in diesem Fall der zurückgelegte Weg des Quadrocopters in $x$- oder $y$-Richtung, verhält sich mit einer gewissen Anlaufverzögerung integral zur Eingangsgröße, dem Steuersignal in der entsprechenden Dimension. Dies entspricht dem erwarteten Verhalten, dass das UAV bei einem Sprung des Steuersignals nach einer Beschleunigungsphase (bedingt durch seine Massenträgheit) eine lineare Änderung der Trajektorie aufweist.
Um nun dieses Systemverhalten stabilisieren zu können, muss ein PD-Regler verwendet werden. Somit lässt sich bereits an dieser Stelle schlussfolgern, dass der Integral-Anteil des auf Applikationsseite implementierten PID-Reglers wegfallen muss, da dieser ansonsten zu instabilem Verhalten führen würde. Für die passende Parametrierung des Proportional- und Differential-Anteils sind hingegen Tests notwendig.
Um nun dieses Systemverhalten stabilisieren zu können, muss ein PD-Regler verwendet werden. Somit lässt sich bereits an dieser Stelle schlussfolgern, dass der Integral-Anteil des implementierten PID-Reglers wegfallen muss, da dieser ansonsten zu instabilem Verhalten führen würde. Für die passende Parametrierung des Proportional- und Differential-Anteils sind hingegen Tests notwendig.
Zu diesem Zweck wurde im konkreten Fall in der Flugsteuerung des in Kapitel \ref{aufbau} beschriebene Test-Quadrokopter der sog.\ Angle-Mode (automatische Stabilisierung innerhalb der Ebene) aktiviert und als Landeplatzmarker ein schwarzer Kreis im DIN A4-Format auf weißem Papier gewählt und auf ebenem Untergrund befestigt. Anschließend wurden die Ausgabewerte der Mobilapplikation auf feste Startwerte gesetzt, indem der Versuchaufbau (zunächst nicht im Betrieb) mittig über dem Ziel positioniert, die Kamera abgedeckt und der Kreis entfernt wurde.
Zu diesem Zweck wurde in der Flugsteuerung des in Kapitel \ref{aufbau} beschriebene Test-Quadro"=kopters der sog.\ Angle-Mode (automatische Stabilisierung innerhalb der Ebene) aktiviert. Als Landeplatzmarker diente ein auf ebenem Untergrund befestigter schwarzer Kreis im DIN A4-Format auf weißem Papier. Anschließend wurden die Ausgabewerte der Mobilapplikation auf feste Startwerte gesetzt, indem der Versuchaufbau (zunächst nicht im Betrieb) mittig über dem Ziel positioniert, die Kamera abgedeckt und der Kreis entfernt wurde.
Der konkrete Testablauf gestaltete sich derart, dass das UAV manuell in einer erfahrungsgemäß geeigneten Höhe für eine zuverlässige Erfassung der vorgesehenen geometrischen Marker gehalten wurde. Sobald das Ziel im Erfassungsbereich des Quadrokopters erschien, wurde der autonome Betrieb über den Mischer mittels eines per Potentiometer auf Seiten der Fernsteuerung einstellbaren Faktors hinzugeschaltet. Somit war es möglich, weiterhin in das Flugverhalten einzugreifen, um beispielsweise auf unvorhergesehene Situationen oder unerwünschtes Reglerverhalten zu reagieren.
Der konkrete Testablauf gestaltete sich derart, dass das UAV manuell in einer erfahrungsgemäß geeigneten Höhe für eine zuverlässige Erfassung des geometrischen Markers gehalten wurde. Sobald das Ziel im Erfassungsbereich des Quadrokopters erschien, wurde der autonome Betrieb über den Mischer mittels eines per Potentiometer auf Seiten der Fernsteuerung einstellbaren Faktors hinzugeschaltet. Somit war es möglich, weiterhin in das Flugverhalten einzugreifen, um beispielsweise auf unvorhergesehene Situationen oder unerwünschtes Reglerverhalten zu reagieren.
Der gesamte Testprozess gestaltete sich iterativ, d.\,h.\ nach jedem Test wurden die Ergebnisse evaluiert, die Regel-Parameter angepasst und anschließend erneut getestet.
\\
Wichtig: Für möglichst präzise Testergebnisse sollte von Anfang an darauf geachtet werden, möglichst alle störenden Umwelteinflüsse während den Tests zu eliminieren. So sollte z.\,B.\ als Testumgebung ein möglichst großer geschlossener Raum gewählt werden, um den Einfluss der durch das UAV entstehenden Luftverwirblungen zu reduzieren. Ebenfalls sollten Fenster nicht geöffnet werden, um Störungen durch Windböen vorzubeugen.
Weiterhin ist bei Flugbetrieb des UAVs auf die Einhaltung von Sicherheitsmaßnahmen (vgl.\ beispielsweise \cite[][]{quadrocopterSafety}) zu achten, um Verletzungen und Sachbeschädigungen zu vermeiden. Gerade zu diesem Zweck empfiehlt sich vor allem für ungeübte Piloten auch die Verwendung des Angle-Modes.
Weiterhin ist bei Flugbetrieb des UAVs auf die Einhaltung von Sicherheitsmaßnahmen (vgl.\ beispielsweise \cite[][]{quadrocopterSafety}) zu achten, um Verletzungen und Sachbeschädigungen zu vermeiden. Gerade zu diesem Zweck empfiehlt sich vor Allem für ungeübte Piloten auch die Verwendung des Angle-Modes.
\section{Ergebnisse}
\label{ergebnisse}
......@@ -1512,16 +1536,16 @@ Weiterhin ist bei Flugbetrieb des UAVs auf die Einhaltung von Sicherheitsmaßnah
Während des iterativen Testverfahrens ließen sich diverse Faktoren feststellen, welche sich kompromittierend auf die Ergebnisse auswirkten:
\begin{itemize}
\item{Eine statische Befestigung des Mobilgeräts unter dem UAV resultiert in einer Verschiebung des Erfassungsbereichs bei Roll- und Nickbewegungen. Dies hat zur Folge, dass der Kreis z.T. das Bild verlässt (vor allem, wenn er sich im Randbereich befindet oder gerade von außen in den Erfassungsbereich kommt und der Ausschlag des Steuerimpulses als Reaktion darauf hoch ist). \\
Eine mögliche Lösung für dieses Problem bestünde in der Bestigung des Mobilgeräts mittels eines Gimbals (Schwenk-Neige-Vorrichtung) oder in der Einberechnung der aus dem Neigewinkel des UAVs resultierenden Verschiebung des Erfassungsbereichs (hierzu wäre einen Erweiterung der bestehende Sensorik um eine absolute Höhenmessung (z.\,B.\ per Ultraschall) notwendig; ein Gyroskop bringen sowohl das Mobilgerät als auch der Flugcontroller im Normalfall mit sich).}
\item{Eine statische Befestigung des Mobilgeräts unter dem UAV resultiert in einer Verschiebung des Erfassungsbereichs bei Roll- und Nickbewegungen. Dies hat zur Folge, dass der Kreis z.T. das Bild verlässt (vor Allem, wenn er sich im Randbereich befindet oder gerade von außen in den Erfassungsbereich kommt und der Ausschlag des Steuerimpulses als Reaktion darauf hoch ist). \\
Eine mögliche Lösung für dieses Problem bestünde in der Bestigung des Mobilgeräts mittels eines Gimbals (Schwenk-Neige-Vorrichtung). Alternativ könnte die aus dem Neigewinkel des UAVs resultierenden Verschiebung des Erfassungsbereichs einberechnet werden (hierzu wäre einen Erweiterung der bestehende Sensorik um eine absolute Höhenmessung (z.\,B.\ per Ultraschall) notwendig; ein Gyroskop bringen sowohl das Mobilgerät als auch der Flugcontroller im Normalfall mit sich).}
\item{Zuweilen kommt es zu Problemen bzgl.\ der Stromversorgung der Komponenten, was zur Folge hat, dass der Flugcontroller nicht korrekt initialisiert werden kann und es anschließend im Betrieb zu Fehlern bei der Kalibrierung der Sensorik oder dem Verarbeiten der Eingangsdaten des Mischers kommen kann (z.\,B.\ alternierender Wert für Throttle (Gas)). Dieses Fehlerbild begründet sich darin, dass das Mobilgerät bedingt durch den verwendeten USB-Treiber (vgl.\ Kapitel \ref{usb}) als Client und nicht als Host der USB-Verbindung agiert und daher versucht, sich über diese aufzuladen. \\
Behoben werden kann dies durch das Einhalten der richtigen Reihenfolge des Zuschalten der Stromversorgung für die einzelnen Elemente. Zunächst muss der Flugcontroller ohne angeschlossenes Mobilgerät initialisiert werden (per USB-Verbindung oder direkt über die mit dem Akku verbundenen BECs (Abk., engl. für \glqq{}Battery Eliminator Circuit\grqq{}) der ESCs (Abk., engl. für \glqq{}Electronic Speed Control\grqq{}, regelt die Geschwindigkeit der Motoren des UAVs)). Anschließend kann das Mobilgerät angeschlossen werden. Alternativ könnte diese Problematik behoben werden, indem die unterschiedlichen Funktionalitäten zusammengefasst würden oder der USB-Treiber derartig modifiziert würde, dass das Mobilgerät als Host der USB-Verbindung agiert.}
\item{Teilweise interferieren die standardmäßigen Regler des Flugkontrollers mit den Steuersignalen des Reglers des Mobilgeräts. Gibt die Applikation beispielsweise einen starken Nick-Impuls aus, erzeugen die Regler des Controllers einen entgegengesetzten Impuls, um das UAV in Waage zu halten. Diesem Verhalten könnte Abhilfe geschaffen werden, indem die Regler (möglicherweise im Rahmen einer Plattformzusammenfas"=sung/-integration) kombiniert bzw. zusammenfasst werden.}
Behoben werden kann dies durch das Einhalten der richtigen Reihenfolge des Zuschalten der Stromversorgung für die einzelnen Elemente. Zunächst muss der Flugcontroller ohne angeschlossenes Mobilgerät initialisiert werden (per USB-Verbindung oder direkt über die mit dem Akku verbundenen BECs (Abk., engl.\ für \glqq{}Battery Eliminator Circuit\grqq{}) der ESCs (Abk., engl.\ für \glqq{}Electronic Speed Control\grqq{}, regelt die Geschwindigkeit der Motoren des UAVs)). Anschließend kann das Mobilgerät angeschlossen werden. Alternativ könnte diese Problematik behoben werden, indem die unterschiedlichen Funktionalitäten zusammengefasst würden oder der USB-Treiber derartig modifiziert würde, dass das Mobilgerät als Host der USB-Verbindung agiert.}
\item{Teilweise interferieren die standardmäßigen Regler des Flugkontrollers mit den Steuersignalen des Reglers des Mobilgeräts. Gibt die Applikation beispielsweise einen starken Nick-Impuls aus, erzeugen die Regler des Controllers einen entgegengesetzten Impuls, um das UAV in Waage zu halten. Diesem Verhalten könnte Abhilfe geschaffen werden, indem die Regler (möglicherweise im Rahmen einer Zusammenfassung der einzelnen Plattformen) kombiniert werden.}
\item{Schlussendlich ist es sehr kompliziert, den Quadrokopter manuell stabil genug in einer halbwegs stationären Höhe über dem Ziel zu positionieren, um die Effekte der Paramtrierung des Reglers evaluieren zu können, solange dieser noch nicht korrekt eingestellt ist. Dieser Prozess erfordert sehr viel Geschick und Übung von Seiten des Piloten und beeinflusst die Güte der Ergebnisse sehr stark, da es gerade bei sanfteren Parametrierungen (geringe Werte für den Proportional-Anteil) schwierig abzuschätzen ist, welche Reaktionen des Fluggeräts dem Regler und welche manuellen Eingriffen des Piloten entstammen.}
\end{itemize}
Unter Berücksichtigung der angeführten beeinflussenden Faktoren war es nicht möglich, eine stabile Regelung für die autonome Zentrierung des UAVs über einem gegebenen Landeplatz zu erreichen. Jedoch konnte mittels der erzielten Ergebnisse ein anschaulicher Machbarkeitsbeweis erbracht und die Funktionsfähigkeit des Gesamtsystems nachgewiesen werden.
Für weitere Optimierung im Rahmen von Folgeprojekten können folgende Werte als Grundlage bzw. Orientierung für die Größenordnung des stabilen Bereichs der Regel-Parameter verwendet werden:\\
Für weitere Optimierung im Rahmen von Folgeprojekten können folgende Werte als Grundlage bzw.\ Orientierung für die Größenordnung des stabilen Bereichs der Regel-Parameter verwendet werden:\\
\\
\begin{tabular}{ l l }
Proportional-Anteil: & 0.15 \\
......@@ -1533,28 +1557,29 @@ Diese Parameter-Werte bewirken in Kombination mit dem verwendeten RC-Mischer und
\chapter{Vergleich Zielsetzung-Endergebnis}
Resümierend lässt sich festhalten, dass die Zielsetzung der Erstellung eines frei zugänglichen und einfach zu verwendenden Frameworks zur Entwicklung von OpenCV gestützten Bildverarbeitungsapplikationen für Android-Plattformen erfüllt wurde. In Kombination mit dem im parallelen Schwesterprojekt entwickelten echtzeitfähigen RC-Mischer auf Arduino-Basis ergibt sich ein modularer Aufbau sämtlicher Hardware- und Software-Komponenten, welcher die geforderte einfache Verwendung unterstützt und potentiellen Nutzern die Erweiterung um bzw. Verwendung von bereits bestehenden Komponenten ermöglicht. Darüber hinaus, war es möglich, beide Aufgabenstellung auf Anwendungsebene (d.\,h.\ zur Erstellung der Applikationen benötigte Bibliotheken, SDKs und Programme, sowie Bootloader (vgl.\ Kapitel \ref{licenses})) ausschließlich unter Verwendung von Open Source-Software umzusetzen, wodurch die freie Zugänglichkeit zu allen Elementen des Projekts bestmöglichst gewährleistet wird.
Ebenfalls konnte mittels der Kombination der angeführten Projekte zu einem Demonstrator in Form eines Quadrokopters mit integrierter autonomer Landeplatzerkennung ein Machbarkeitsbeweis hinsichtlich der Funktionalität der einzelnen Komponenten bzw. potentieller praktischer Anwendungsmöglichkeiten erbracht werden. Im Rahmen des iterativen Testverfahrens zur Parametrierung des Reglers offenbarten sich jedoch die Grenzen des verwendeten Versuchsaufbaus (starre Kamerabefestigung, Regler-Interferenzen, etc.), wodurch sich der Vorgang komplizierter als erwartet gestaltete. In Anbetracht der in Kapitel \ref{ergebnisse} angeführten Herausforderungen besteht daher weiterhin Optimierungspotential hinsichtlich der Realisierung einer stabilen Regelung zur autonome Zentrierung über einem gegebenen Landeplatz.
Resümierend lässt sich festhalten, dass die Zielsetzung der Erstellung eines frei zugänglichen und einfach zu verwendenden Frameworks zur Entwicklung von OpenCV-gestützten Bildverarbeitungsapplikationen für Android-Plattformen erfüllt wurde. In Kombination mit dem im Rahmen eines Parallelprojekts entwickelten echtzeitfähigen RC-Mischer auf Arduino-Basis ergibt sich ein modularer Aufbau sämtlicher Hardware- und Software-Komponenten. Dies unterstützt die geforderte einfache Verwendung und ermöglichht potentiellen Nutzern die Erweiterung um bzw.\ Verwendung von bereits bestehenden Komponenten. Darüber hinaus war es möglich, beide Aufgabenstellung auf Anwendungsebene (d.\,h.\ zur Erstellung der Applikationen benötigte Bibliotheken, SDKs und Programme, sowie Bootloader (vgl.\ Kapitel \ref{licenses})) ausschließlich unter Verwendung von Open Source-Software umzusetzen, wodurch die freie Zugänglichkeit zu allen Elementen des Projekts bestmöglichst gewährleistet wird.
\\
\\
Ebenfalls konnte ein Machbarkeitsbeweis hinsichtlich der Funktionalität der einzelnen Komponenten erbracht und potentieller praktischer Anwendungsmöglichkeiten aufgezeigt werden. Im Rahmen des iterativen Testverfahrens zur Parametrierung des Reglers offenbarten sich jedoch die Grenzen des verwendeten Versuchsaufbaus (starre Kamerabefestigung, Regler-Interferenzen, etc.), wodurch sich der Vorgang komplizierter als erwartet gestaltete. In Anbetracht der in Kapitel \ref{ergebnisse} angeführten Herausforderungen besteht daher weiterhin Optimierungspotential hinsichtlich der Realisierung einer stabilen Regelung zur autonome Zentrierung über einem gegebenen Landeplatz.
\\
\\
Zusammenfassend lässt sich sagen, dass die definierten Zielstellungen in weitesten Teilen erfüllt werden konnten. Trotzdem besteht teilweise weiterhin Optimierungspotential (vgl.\ Kapitel \ref{ausblick}). Unter diesem Gesichtspunkt stellt das Projekt eine gute Grundlage für weiterführende oder eigenständige Projekte in Form der Bildverarbeitung mittels OpenCV auf Android-Plattformen, potentiell kombiniert mit einer Datenausgabe zu angeschlossenen Peripheriegeräten, dar.
Zusammenfassend lässt sich sagen, dass die definierten Zielstellungen in weitesten Teilen erfüllt werden konnten. Trotzdem besteht teilweise weiterhin Optimierungspotential (vgl.\ Kapitel \ref{ausblick}). Unter diesem Gesichtspunkt stellt das Projekt eine gute Grundlage für weiterführende oder eigenständige Projekte in Form der Bildverarbeitung mittels OpenCV auf Android-Plattformen, potentiell kombinierbar mit einer Datenausgabe zu angeschlossenen Peripheriegeräten, dar.
\chapter{Ausblick}
\label{ausblick}
Hinsichtlich der Optimierung der autonomen Landeplatzlokalisierung auf Grundlage des im Rahmen des dokumentierten Projekts erstellten Frameworks zur Entwicklung von OpenCV gestützten Bildverarbeitungsapplikationen für Android-Plattformen sind im Verlauf der Arbeiten zusätzlich zu den bereits realisierten Funktionalitäten folgende Punkte aufgekommen:
Hinsichtlich der Optimierung der autonomen Landeplatzlokalisierung sind im Verlauf der Arbeiten zusätzlich zu den bereits realisierten Funktionalitäten folgende weiter Aspekte aufgekommen:
\begin{enumerate}
\item{Grundsätzlich besteht weiterhin Potential hinsichtlich der Parametrierung des Reglers auf Seite der Applikation, um eine stabile Zentrierung über dem Landeplatz mit möglichst geringen Überschwingern zu realisieren bzw. zu optimieren. Sollten mechanische Änderungen an dem Versuchsaufbau vorgenommen werden, so ist eine Rekalibrierung der Parameter empfehlenswert um ein gutes Ergebnis zu erzielen. \\
Durch geeignete Modifikationen kann der Anspruch an den Regler weiterhin reduziert werden (beispielsweise durch Verwendung eines Gimbals (s. Punkt drei) oder der Optimierung in den Randbereichen des Erfassungsgebiets (s. Punkt zwei)).}
\item{Grundsätzlich besteht weiterhin Potential hinsichtlich der Parametrierung des Reglers auf Seite der Applikation, um eine stabile Zentrierung über dem Landeplatz mit möglichst geringen Überschwingern zu realisieren. Sollten mechanische Änderungen an dem Versuchsaufbau vorgenommen werden, so ist eine (komplette) Rekalibrierung der Parameter empfehlenswert um ein gutes Ergebnis zu erzielen. \\
Durch geeignete Modifikationen kann der Anspruch an den Regler weiterhin reduziert werden (beispielsweise durch Verwendung eines Gimbals (s.\ Punkt drei) oder der Optimierung in den Randbereichen des Erfassungsgebiets (s.\ Punkt zwei)).}
\item{Wie bereits in Kapitel \ref{ergebnisse} angeführt, kann zur Optimierung der Erfassung des Landeplatzes im Randbereich des Bilds die bestehende Sensorik um eine absolute Höhenmessung (beispielsweise mittels Ultraschall) ergänzt werden. \\
Somit könnte in Kombination mit dem bereits auf Seiten der meisten Mobilgeräte und Flugcontroller existierenden Gyroskop aus dem gemessenen Neigungswinkel gegenüber der Ebene und der Höhe die Verschiebung des Erfassungsbereichs bei Neigung des UAVs als Reaktion auf Sprünge der Steuersignale (wenn das Ziel am Rande des Bilds erscheint) ermittelt und mit dem von der Bildverarbeitung ausgegebenen Wert verrechnet werden, um den Regler in diesen Randbereichen zu optimieren und die Störgröße der Neigung zu eliminieren.}
\item{Die derzeitige mechanische Konstruktion des Testgeräts (dargestellt in Kapitel \ref{aufbau}) ist derzeit noch insofern suboptimal, dass das kapazitive Display des Mobilgeräts zuweilen durch den darüber liegenden Akku angesprochen und so beispielsweise die Applikation beendet wird. Weiterhin ist dementsprechend das Display an sich nur sehr begrenzt zugänglich, wodurch es erschwert wird, Statusmeldungen zu quittieren. Die Lösung der Befestigung des Geräts am Versuchsaufbau mittels Kabelbindern ist ebenfalls sicherheitstechnisch suboptimal. \\
Insofern wäre eine mechanische Umkonstruktion mit dem Fokus der Neupositionierung des Akkus und des Mobilgeräts, so dass der Bildschirm frei zugänglich ist (potentiell direkt in Kombination mit der Verwendung eines Gimbals zur Bildstabilisierung als Alternative zu zweiterem Punkt), denkbar.}
\item{Derzeit existiert keine Möglichkeit, um den Test-Quadrokopter bei einem potentiellen Versagen des Flugcontrollers oder des RC-Mischers abzuschalten, außer am laufenden System die Stromversorgung zu unterbrechen. Da dies jedoch mit Sicherheits- und Verletzungsrisiken einhergeht, wäre es denkbar, einen drahtlosen Notaus als zusätzliche Sicherheitsinstanz zu implementieren, mittels dem das System \glqq{}remote\grqq{} (aus der Ferne) abgeschaltet werden kann. Wichtig wäre bzgl.\ der Umsetzung dieser Funktionalität insbesondere der Fokus auf die Echtzeitfähigkeit im Sinne von Rechtzeitigkeit und die Stabilität.}
\item{Um eine Interferenz der standardmäßigen Regler des Flugcontrollers und der vorgestellten Applikation zu vermeiden, könnten diese zusammengefasst werden, so dass beispielsweise die Steuersignale des Mobilgeräts diejenigen des Controllers während des autonomen Betriebs zu einem gewissen Grad überlagern. Somit könnte diese Störgröße eliminiert und eine einfachere Parametrierung des Reglers auf Applikations-Seite ermöglicht werden. Potentiell wäre es möglich, dies mit einer Fusion der verwendeten Hardware- und Software-Komponenten (s. folgender Punkt) zu verbinden.}
\item{Wie bereits zu Beginn in der Zielsetzung (vgl.\ Kapitel \ref{ziel}) dargestellt, lag der Fokus dieses Projektes darauf, möglichst einfach verwendbare und frei zugängliche Software zu entwickeln. Dies inkludiert bzw. induziert zwangsläufig einen modularen Aufbau des Systems, so dass potentielle Nutzer möglichst einfach auf ihren bestehenden Komponenten aufbauen können. \\
Zu Optimierungszwecken wäre es jedoch zielführender, die einzelnen Software- und Hardware-Module zusammenzufassen und auf einer einzigen Platine zu bündeln. Somit könnte das Gewicht und der Stromverbrauch deutlich reduziert und die Kommunikation sowie die mechanische Anbringung am UAV stark vereinfacht werden. Ebenfalls könnten teilweise aufgrunde des modularen Aufbaus redundante Code-Elemente eliminiert werden. Anbieten würde sich für diese Zwecke beispielsweise ein Einplattinencomputer wie der BeagleBone Black, wobei ein Kern rein für die Flugsteuerung und ein weiterer Kern für die restliche Software verwendet werden könnten, um die Echtzeitfähigkeit zu erhalten.}
Somit könnte in Kombination mit dem bereits auf Seiten der meisten Mobilgeräte und Flugcontroller existierenden Gyroskop aus dem gemessenen Neigungswinkel gegenüber der Ebene die Verschiebung des Erfassungsbereichs bei Neigung des UAVs ermittelt werden. Durch die Verrechnung des so erhaltenen Wertes mit dem von der Bildverarbeitung ausgegebenen Wert kann die Funktionsfähigkeit des Reglers in diesen Randbereichen verbessert und die Störgröße der Neigung weitestmöglich eliminiert werden.}
\item{Die mechanische Konstruktion des Testgeräts (dargestellt in Kapitel \ref{aufbau}) ist derzeit noch insofern suboptimal, dass das kapazitive Display des Mobilgeräts zuweilen auf den darüber liegenden Akku reagiert und so beispielsweise die Applikation beendet wird. Weiterhin ist das Display an sich nur sehr begrenzt zugänglich, wodurch es erschwert wird, Statusmeldungen zu quittieren. Die Lösung der Befestigung des Geräts am Versuchsaufbau mittels Kabelbindern ist ebenfalls sicherheitstechnisch suboptimal. \\
Insofern wäre eine mechanische Umkonstruktion mit dem Fokus der Neupositionierung des Akkus und des Mobilgeräts, so dass der Bildschirm frei zugänglich ist, denkbar. Dies könnte potentiell in Kombination mit der Verwendung eines Gimbals zur Bildstabilisierung geschehen.}
\item{Derzeit existiert keine Möglichkeit, um den Test-Quadrokopter bei einem potentiellen Versagen des Flugcontrollers oder des RC-Mischers abzuschalten, außer am laufenden System die Stromversorgung zu unterbrechen. Da dies jedoch mit Sicherheits- und Verletzungsrisiken einhergeht, wäre es denkbar, einen drahtlosen Notaus als zusätzliche Sicherheitsinstanz zu implementieren, mittels dem das System \glqq{}remote\grqq{} (aus der Ferne) abgeschaltet werden kann. Wichtig wäre bzgl.\ der Umsetzung dieser Funktionalität insbesondere der Fokus auf die Echtzeitfähigkeit und die Stabilität.}
\item{Um eine Interferenz der standardmäßigen Regler des Flugcontrollers und der vorgestellten Applikation zu vermeiden, könnten diese zusammengefasst werden, so dass bspw.\ die Steuersignale des Mobilgeräts diejenigen des Controllers während des autonomen Betriebs zu einem gewissen Grad überlagern. Somit könnte diese Störgröße eliminiert und eine einfachere Parametrierung des Reglers auf Applikations-Seite ermöglicht werden. Potentiell wäre es möglich, dies mit einer Fusion der verwendeten Hardware- und Software-Komponenten (s.\ Punkt sechs) zu verbinden.}
\item{Wie bereits zu Beginn in der Zielsetzung (vgl.\ Kapitel \ref{ziel}) dargestellt, lag der Fokus dieses Projektes darauf, möglichst einfach verwendbare und frei zugängliche Software zu entwickeln. Dies inkludiert bzw.\ induziert zwangsläufig einen modularen Aufbau des Systems, so dass potentielle Nutzer möglichst einfach auf ihren bestehenden Komponenten aufbauen können. \\
Zu Optimierungszwecken wäre es jedoch zielführender, die einzelnen Software- und Hardware-Module zusammenzufassen und auf einer einzigen Platine zu bündeln. Somit könnte das Gewicht und der Stromverbrauch deutlich reduziert und die Kommunikation zwischen den einzelnen Komponenten sowie die mechanische Anbringung am UAV stark vereinfacht werden. Ebenfalls könnten teilweise aufgrunde des modularen Aufbaus redundante Code-Elemente eliminiert werden. Anbieten würde sich für diese Zwecke beispielsweise ein Einplatinencomputer wie der BeagleBone Black, wobei ein Kern rein für die Flugsteuerung und ein weiterer Kern für die restliche Software verwendet werden könnten, um die Echtzeitfähigkeit zu erhalten.}
\end{enumerate}
Die angeführten Aspekte sollen als Anregung für eigenständige oder auf diesem Projekt aufbauende Folgeprojekte dienen und sind nicht als zwangsläufige Voraussetzung zu sehen, um das erstellte Framework oder die Demoapplikation in ihrer bestehenden Funktion nutzen zu können. Vielmehr dienen sie wie zu Beginn der Sektion angeführt der Optimierung.
......@@ -1569,7 +1594,7 @@ Was haben wir als Entwickler nun persönlich aus dem Projekt mitgenommen? Neben
Weiterhin haben wir gelernt, dass eine stabile Stromversorgung maßgeblich für die Funktionalität der angeschlossenen Komponenten ist und Instabilitäten zu schwierig nachvollziehbaren Fehlerbildern führen können.
\\
\\
Im Hinblick auf die vermittelten Soft Skills konnten, bedingt durch die Durchführung des Projekts als Gruppenarbeit, die eignenen Kompetenzen in den Bereichen Planung, Teamarbeit und Kommunikation verbessert werden. Vor allem letztere beiden Aspekten wurden durch die kooperative Arbeit mit Versionskontrollsystemen wie dem über den campuseigenen Server zur Verfügung stehenden Gitlab unterstützt bzw. gefördert.
Im Hinblick auf die vermittelten Soft Skills konnten, bedingt durch die Durchführung des Projekts als Gruppenarbeit, die eignenen Kompetenzen in den Bereichen Planung, Teamarbeit und Kommunikation verbessert werden. Vor Allem letztere beiden Aspekten wurden durch die kooperative Arbeit mit Versionskontrollsystemen wie dem über den campuseigenen Server zur Verfügung stehenden Gitlab unterstützt bzw.\ gefördert.
\\
\\
Der jedoch wahrscheinlich entscheidenste Aspekt, den wir im Verlauf dieser Projektarbeit lernen konnten, ist, wie man im Allgemeinen planungstechnisch an größere Projekte hertritt, diese sinnvoll aufteilt (\glqq{}Top-Down-Ansatz\grqq{} der Softwareentwicklung) und anschließend mit einem agilen Entwicklungsansatz bearbeit.
......@@ -1629,6 +1654,7 @@ Proprietär \& Kommerziell \\
\newpage
\section{gitlab-Repository}
\label{repository}
Der im Rahmen dieses Projektes entstandene Quellcode sowie die hier vorliegende
Dokumentation können über den gitlab-Server des Campus Velbert-Heiligenhaus
......
......@@ -4,7 +4,7 @@
%% Page layout and dimensions
\RequirePackage[a4paper,top=32mm,bottom=3cm,left=3cm,right=3cm,marginparwidth=1.75cm,headheight=12mm,footskip=12mm]{geometry}
%% Language and font encodings
\RequirePackage[rm, light, sfdefault]{roboto}
\RequirePackage[rm, light, sfdefault]{roboto} %%TODO:
\RequirePackage[T1]{fontenc}
%% Packages
......@@ -91,10 +91,10 @@
{0pt}{-12pt}{12pt}
\titleformat{\section}
{\fontfamily{\sfdefault}\mdseries\fontsize{12}{15}\color{BO}}{\thesection}{1em}{#1} % without #1 the section title disappears, since [explicit]{titlesec} is used
{\fontfamily{\sfdefault}\bfseries\fontsize{13}{15}\color{BO}}{}{0em}{\thesection\hspace{1em}#1} % without #1 the section title disappears, since [explicit]{titlesec} is used
\titleformat{\subsection}
{\fontfamily{\sfdefault}\mdseries\color{BO}}{\thesubsection}{1em}{#1}
{\fontfamily{\sfdefault}\fontseries{m}\selectfont\color{BO}}{}{0em}{\thesubsection\hspace{1em}#1}
%% Custom titlepage (redefines \maketitle, might not be up to LaTeX standards!)
\renewcommand{\maketitle}{
......
File added
File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment