universit - institut für informatikfsjaetzo/archive/art.pdf · hester institute of t ec hnology,...
TRANSCRIPT
UNIVERSIT�AT OSNABR�UCK
Fachbereich Mathematik/Informatik
Diplomstudiengang Mathematik, Vertiefungsfach Informatik
Diplomarbeit
Objekte, Threads und Events
f�ur Roboter{ Ein Toolkit zur hardware-unabh�angigen Robotersteuerung in Java {
Betreuer: Verfasser:
Prof. Dr. AXEL T. SCHREINER STEPHAN J�ATZOLD
Vorgelegt im Februar 2002
iii
Danksagungen
Ich danke Herrn Prof. Dr. Axel-Tobias Schreiner f�ur seine Unterst�utzung und
die Betreung dieser Diplomarbeit. Seine Bereitschaft neue Inhalte und Ideen in
die Lehre an der Universit�at zu bringen ist bemerkenswert.
F�ur die �nanzielle Unterst�utzung bei zwei Forschungsaufenthalten in den
USA, am Rochester Institute of Technology, im Zusammenhang mit dieser Ar-
beit, bin ich der Universit�atsgesellschaft Osnabr�uck und dem Fachbereich Ma-
thematik/Informatik der Universit�at Osnabr�uck zu Dank verp ichtet.
Ganz besonderer Dank gilt meinen Eltern, die mir f�ur meine Ausbildung
jede m�ogliche Freiheit lassen und dabei gro�e Geduld beweisen. Ich ho�e ich
kann ihnen vermitteln, da� es sich lohnt.
F�ur die Durchsicht des Textes und inhaltliche Diskussionen danke ich Bernd
K�uhl, Eva Ebenh�oh und Helga J�atzold.
Eva Ebenh�oh danke ich f�ur ihre Geduld und ihre Unterst�utzung, sie versteht
mich wie sonst niemand.
Hilfsmittel
F�ur die Erstellung dieser Arbeit wurden die folgenden Programme verwendet:
nedit zum editieren der Texte
LATEX in Verbindung mit einer Menge Erweiterungen zum Setzen dieser Arbeit
x�g zur Erstellung der Gra�ken
linux und viele der dort �ublicherweise installierten Tools wie make, convert
usw.
jikes als haupts�achlich verwendeter Java-Compiler
� verschiedene Java Development Kits zur Java-Entwicklung
� verschiedene Programme und Ger�ate zur Erstellung und Bearbeitung der
Fotos
Erkl�arung
Hiermit erkl�are ich, diese Diplomarbeit selbst�andig verfasst und keine anderen
als die angegebenen Quellen und Hilfsmittel verwendet zu haben.
Stephan J�atzold
iv
Inhaltsverzeichnis
1 Einleitung 1
1.1 Vorgeschichte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Begri�skl�arungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Zielgruppe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Inhalts�ubersicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 Ariadne { a Li(ght)Se(arching) Trusty 7
2.1 Trusty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 LiSe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Ariadne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4 Hervorzuhebende Eigenschaften . . . . . . . . . . . . . . . . . . . 9
3 Programmiersysteme f�ur Roboter 13
3.1 Gra�sche Programmiersysteme . . . . . . . . . . . . . . . . . . . 13
3.1.1 LLWin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.1.2 RCX-Code . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.1.3 Robolab . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.2 Textbasierte Programmiersysteme . . . . . . . . . . . . . . . . . 22
3.2.1 Fernsteuerungen . . . . . . . . . . . . . . . . . . . . . . . 22
3.2.2 Sprachen f�ur die Lego-Firmware . . . . . . . . . . . . . . 24
3.2.3 Systeme mit eigener Firmware . . . . . . . . . . . . . . . 25
3.2.4 leJOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.3 Vorl�au�ges Fazit . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4 Die Idee des Abstract Robot Toolkit 35
4.1 Tutebot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2 Fischertechnik und Lego Mindstorms . . . . . . . . . . . . . . . . 39
4.3 Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4.4 Design-Pattern und das AWT . . . . . . . . . . . . . . . . . . . . 41
4.5 Das Abstract Robot Toolkit . . . . . . . . . . . . . . . . . . . . . 43
4.6 Die Sensor-Implementierungen in ART . . . . . . . . . . . . . . 50
4.7 Model, View, Controller . . . . . . . . . . . . . . . . . . . . . . . 56
4.8 Subsumption-Architektur . . . . . . . . . . . . . . . . . . . . . . 57
v
vi INHALTSVERZEICHNIS
5 Ariadne & ART - Ein Tutorial 59
5.1 "Hello, Robot!" . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
5.2 DriveTrain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
5.3 Und wie benutzt man das nun? . . . . . . . . . . . . . . . . . . . 73
5.4 Trusty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.5 Kon�gurierte Sensoren . . . . . . . . . . . . . . . . . . . . . . . . 86
5.5.1 SubsumptionTrustyMSDemoRobot . . . . . . . . . . . . . . 86
5.5.2 Der Lego-Rotationssensor . . . . . . . . . . . . . . . . . . 89
5.5.3 Rotationsmessung mit den Fischertechnik-Impulsr�adern . 91
5.6 LiSe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.7 Ariadne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6 Implementierung eines RobotInterface 107
6.1 Ein virtuelles RobotInterface . . . . . . . . . . . . . . . . . . . 107
6.2 Die Peers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
6.3 Die Ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
6.4 Die Methoden von RobotInterface . . . . . . . . . . . . . . . . 117
6.5 Die Einbindung in ART: RobotInterfaceFactory . . . . . . . . 120
7 Zusammenfassung 125
7.1 Ergebnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
7.2 Geschwindigkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
7.3 Flexibilit�at . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
7.4 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
7.5 Sicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
7.6 Andere Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
7.7 Subsumption . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
7.8 JavaBeans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
7.9 XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
7.10 Persistenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
A Installation des Abstract Robot Toolkit 135
A.1 Installation der Beispiele . . . . . . . . . . . . . . . . . . . . . . . 136
A.2 Klassendokumentation . . . . . . . . . . . . . . . . . . . . . . . . 137
B Troubleshooting 139
B.1 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
B.2 Geschwindigkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
B.3 Automatische Erkennung . . . . . . . . . . . . . . . . . . . . . . 140
Abbildungsverzeichnis 141
Literaturverzeichnis 143
1
Einleitung
Moderne Programmiersprachen, wie z.B. Java, stellen inzwischen f�ur viele Be-
reiche ein recht weit entwickeltes API (Application Program Interface) zur
Verf�ugung. F�ur die Programmierung von Robotern scheint es aber bisher zu-
mindest kein frei verf�ugbares API zu geben, das einen objektorientierten, von
der konkreten Hardware abstrahierenden Zugang zu Robotern bereitstellt. F�ur
die Ansteuerung von Robotern aus Java heraus ist man in vielen F�allen auf die
Besch�aftigung mit wenig intuitiven Byte-Str�omen angewiesen.
Die vorliegende Arbeit will diese L�ucke f�ullen. Es wird eine Schnittstelle ent-
wickelt, die eine objektorientierte und event-basierte Sicht auf die Komponenten
eines Roboters erm�oglicht. Unter Verwendung dieser Schnittstelle kann man Al-
gorithmen zur Robotersteuerung entwickeln, die weitgehend wiederverwendbar
sind { nicht nur f�ur den gleichen Roboter, sondern f�ur verschiedene Roboter,
von m�oglicherweise sogar unterschiedlicher Hardware-Architektur. Wo immer es
m�oglich ist, werden die gleichen Klassen f�ur unterschiedliche Roboter-Hardware
verwendet, womit eine weitgehende Unabh�angigkeit von der verwendeten Platt-
form erreicht wird.
Implementiert wurde diese Schnittstelle exemplarisch f�ur Roboter-Hardware
von Lego und Fischertechnik; ein Beispiel f�ur eine Implementierung f�ur weitere
Plattformen wird gegeben.
1.1 Vorgeschichte
Lego Mindstorms kam Ende 1998 in den USA auf den Markt und ein Jahr sp�ater
auch in Deutschland. Seitdem hat sich eine gro�e Fan-Gemeinde entwickelt und
im Internet organisiert. Die von Lego zur Verf�ugung gestellten M�oglichkeiten
zur Programmierung des Brick, wie der gro�e gelbe Legostein mit Batterien
und einem Computer darin h�au�g genannt wird1, waren damals die gra�sche
Programmierumgebung RCX-Code und das ActiveX-Control Spirit.ocx.
1In diesem Dokument soll mit Brick die Hardware des Computer-Bausteins im Lego Mind-
storms Robotics-Invention-System (RIS) bezeichnet werden. Der Begri� RCX (so wird der
Brick auch manchmal genannt) soll hier hingegen die Original-Firmware, das von Lego aus-
gelieferte Betriebssystem des Brick bezeichnen. Es existieren verschiedene RCX-Versionen f�ur
den Brick und weitere Lego-Produkte wie den Cybermaster und den Scout.
1
2 1. EINLEITUNG
Beide Systeme laufen auch heute nur unter Windows und vor allem RCX-
Code ist au�erdem f�ur Menschen ohne Programmiererfahrung konzipiert wor-
den, aber: Der Brick kann sein Betriebssystem �uber Infrarot laden, es ist nicht
auf dem Chip eingebrannt. Vielleicht war es aber auch schlicht der Drang, wis-
sen zu wollen, wie "Es" funktioniert, auf jeden Fall knackte Kekoa Proudfoot
(Proudfoot, 1998) noch im selben Jahr den Brick (im wahrsten Sinne des Wor-
tes) und Dave Baum entwickelte NQC (Baum, 2001), eine alternative Program-
miersprache zur Programmierung von RCX, dem Betriebssystem des Brick.�Uber das Internet wurden und werden die Erkenntnisse und die Arbeiten
einer immer gr�o�er werdenden Gruppe von Enthusiasten zug�anglich gemacht,
so da� gute eineinhalb Jahre sp�ater, im Fr�uhjahr 2000, der Brick in vielen
Sprachen, darunter C, C++, Forth und Java, programmiert werden kann. Ei-
nige dieser Systeme setzen daf�ur nicht auf der Standard-Firmware auf, sondern
beinhalten gleich ein eigenes Betriebssystem f�ur den Brick, welches die M�oglich-
keiten, z.B. zur Ansteuerung des integrierten LCD betr�achtlich erweitert.
Inzwischen hat auch Lego das Betriebssystem erweitert und die textbasierte
Programmiersprache Mindscript entwickelt, sowie die Bytecodes des RCX unter
dem Namen Lego-Assembler ver�o�entlicht.
Doch Konstruktionsbauk�asten mit Computern zu verbinden ist eine Idee, die
f�ur Fischertechnik bereits 1984 in ein Produkt umgesetzt wurde. Das Universal-
Interface kann an die parallele Schnittstelle eines IBM-PC, Commodore C64
oder Atari ST angeschlossen werden. Das Interface hat keinen eigenen Mikro-
controller, sondern vermittelt im Wesentlichen nur zwischen den elektrischen
Signalen des angeschlossenen Computers und den an das Interface angeschlosse-
nen Motoren und Sensoren. Erst das 1997 herausgebrachte Intelligent-Interface
kann auf der Basis der Windows-Software LLWin entwickelte Programme auch
autonom ausf�uhren. LLWin ist ein gra�sches Programmiersystem, das an das
industriell verwendete iCon-L der Firma Pro-Sign Process Design GmbH ange-
lehnt ist.
Bereits von Anfang an lag dem Universal-Interface eine gedruckte Doku-
mentation bei, welche die Signale an der parallelen Schnittstelle beschreibt, die
zur Ansteuerung des Interface n�otig sind. Au�erdem verf�ugt das Interface �uber
mehr Ein- und Ausg�ange als der Brick. Trotzdem ist die Fan-Gemeinde und die
frei verf�ugbare Software f�ur die Fischertechnik-Interfaces wesentlich weniger
weit entwickelt, als das bei Lego Mindstorms der Fall ist. F�ur das Intelligent-
Interface, welches eine serielle Schnittstelle hat, ist von Fischertechnik ein Kom-
munikationsprotokoll ver�o�entlicht worden, mit welchem die gleichen M�oglich-
keiten wie mit dem Universal-Interface er�o�net werden. Die F�ahigkeit, Pro-
gramme auf das Interface zu laden und dort dann lokal, vom PC getrennt
ausf�uhren zu lassen, bleibt leider der Fischertechnik-Software LLWin vorbe-
halten. Es scheint keine Quelle zu geben, die diesen Vorgang dokumentiert und
eine Analyse der Daten die von LLWin an das Interface gesendet werden ist
nicht so einfach, da die Datenmenge bereits bei den einfachsten Programmen
recht gro� ist.
1.2. BEGRIFFSKL�ARUNGEN 3
1.2 Begri�skl�arungen
Die meisten der betrachteten Programmiersysteme f�ur Roboter bieten Program-
mierern nur sehr eingeschr�ankte M�oglichkeiten zur Anwendung �ublicher Tech-
niken und Paradigmen in der Softwareentwicklung. Im Folgenden wird auf ein
paar Aspekte eingegangen, die in dieser Arbeit immer wieder zur Bewertung
der einzelnen Programmiersysteme herangezogen werden.
Wiederverwendung von Code: Damit ist der Umstand gemeint, da�
ein Programm, oder Teile davon, in einem neuen Programm erneut verwendet
werden. Eine Form von Wiederverwendung ist das Kopieren (von Teilen) eines
Quelltextes, entweder durch "Cut&Paste" oder durch blo�es Abschreiben aus
Beispielprogrammen. Diese Vorgehensweise kann zum Lernen von neuen Tech-
niken durchaus als geeignet angesehen werden. Zum Aufbau einer Bibliothek
von h�au�g benutzten Bausteinen gibt es aber besseres. Solche Sofwarebausteine
k�onnen heute �ublicherweise Funktionen oder Objekte sein. Sind diese Bausteine
fertig, getestet und gen�ugend abstrakt, bzw. modular gehalten (sowie nat�urlich
deren Funktionsweise hinreichend bekannt), mu� nur noch der Code geschrieben
werden, der diese miteinander verbindet, und sie k�onnen somit eine erhebliche
Arbeitserleichterung darstellen.
Threads: Unter Threads versteht man den aus logischer Sicht gleichzeiti-
gen Ablauf verschiedener Programmteile nebeneinander. Ein Scheduler verteilt
diese Abl�aufe auf die verf�ugbaren Prozessoren. Preemptive Scheduling liegt vor,
wenn der Scheduler zu beliebiger Zeit einen Thread zugunsten eines anderen
vor�ubergehend stilllegen kann.
Interrupts vs. Polling: Wenn Software auf Zustands�anderungen in der
Hardware reagieren soll, stehen im Wesentlichen zwei Techniken zur Verf�ugung:
Interrupts und Polling. Polling bedeutet, da� der Zustand von der Seite der
Software aus st�andig aktiv �uberpr�uft wird. Umgekehrt wird bei Interrupts die
Software erst dann aktiv, wenn eine �Anderung eingetreten ist. Die �Anderung
wird in diesem Fall von der Hardware �uber den Interrupt signalisiert. Wenn
man Polling verwenden mu�, weil die Hardware keine (geeigneten) Interrupts
zur Verf�ugung stellt, ist es mit Threads m�oglich, die ben�otigten Interrupts in
der Software zu simulieren. So programmiert man oberhalb dieser Simulation,
als w�aren Interrupts in der Hardware vorhanden. Zudem ist es mit mehreren
Threads m�oglich, die Notwendigkeit des Polling vollends zu kapseln, also in
einem Modul zu verstecken. Ein Modul besitzt mit Threads die M�oglichkeit,
v�ollig unabh�angig von anderen Modulen aktiv zu werden. Ohne Threads w�are
es notwendig, da� der einzige vorhandene Programmz�ahler h�au�g genug in dem,
dann als passiv zu bezeichnenden, Polling-Modul "vorbeikommt".
Events: Events sind im Zusammenhang mit Threads und Modularisierung
wichtig. Ein Thread gibt anderen Threads (in anderen Modulen) �uber Events
Nachrichten. Ein Thread kann auf einen Event warten, bzw. ein Modul wird
von einem Thread ausgef�uhrt, wenn ein bestimmter Event eingetreten ist. Da-
bei sollte ein auf einen Event wartender Thread keine, bzw. wenig Rechenzeit
in Anspruch nehmen m�ussen., also kein sogenanntes busy-wait durchf�uhren.
Im Optimalfall wird ein Event z.B. f�ur die �Anderung eines Sensorwertes von
der Hardware durch einen Interrupt ausgel�ost, der dann die Ausf�uhrung eines
4 1. EINLEITUNG
Threads veranla�t.
Subsumption: Subsumption ist ein von Rodney A. Brooks (Brooks, 1985)
erdachtes Konzept zur Steuerung von Robotern. Es basiert auf der Idee, bzw.
der Beobachtung aus der Biologie, da� komplexe Verhaltensweisen h�au�g das
Ergebnis von dem Zusammenspiel vieler einzelner, einfacher Verhaltensweisen
(Re exe) sind. Es eignet sich besonders gut f�ur die Wiederverwendung, da
die einzelenen Verhaltensweisen unterschiedlich kombiniert werden k�onnen. Der
"Original-Trusty" von Knudsen ist z.B. mit Subsumption programmiert worden
(Knudsen, 1999, S. 179�).
Objektorientierung: Diese Technik zur Modularisierung mittels Klassen
und zur Abstraktion mittels Interfaces2 ist sehr n�utzlich, da sie in weiten Teilen
an die menschliche Vorstellung von Dingen angelehnt ist. Das Entwickeln einer
vern�unftigen Modularisierung, deren Funktionsweise auch verst�andlich ist, wird
damit erleichtert.
Java: Java (Gosling et al., 2000) ist eine Sprache in der diese Techniken
relativ leicht und elegant einsetzbar sind. Au�erdem bietet Java die M�oglich-
keit der plattform�ubergreifenden Programmierung (Lindholm und Yellin, 1999),
mit dem (leider nicht ganz perfekt eingel�osten) Versprechen "Write Once, Run
Anywhere". So soll der Programmierer eigentlich nicht einmal darauf achten
m�ussen, auf welcher Plattform die Software am Ende laufen soll. Salopp gesagt,
k�onnte man die Motivation f�ur diese Arbeit auch folgenderma�en zusammen-
fassen: "Das, was Java f�ur normale Computer ist, will ich f�ur Roboter auch!"
1.3 Zielgruppe
Diese Arbeit ist weitgehend unter Verwendung zweier Produktreihen entstan-
den, die als Spielzeug vermarktet werden: Lego Mindstorms und Fischertechnik
Computing. Sowohl Lego als auch Fischertechnik wird au�erdem im Forschungs-
und Bildungsbereich eingesetzt und auf der Basis von Fischertechnik werden
Modelle zur Simulation von industriellen Fertigungsanlagen entwickelt. In die-
sem, eigentlich sehr breiten, Anwendungsbereich bewegt sich auch diese Arbeit.
Die Ideen, die hinter dem in dieser Arbeit entwickelten Programmiersystem
stecken, sind von der konkret verwendeten Hardware zwar unabh�angig, wurden
aber bislang nur f�ur Hardware aus den Lego Mindstorms und Fischertechnik
Computing Produktreihen realisiert. Daraus ergibt sich aber bereits ein Impuls
f�ur den Forschungsbereich, denn eine Portierung, bzw. Erweiterung auf ande-
re, neue Roboter-Hardware w�are sozusagen eine �Uberpr�ufung des entwickelten
Modells an der Realit�at.
Wer sich mit Robotern als Hobby besch�aftigt, will dabei (auch) Spa� haben.
Nun kann auch Java-Programmieren Spa� machen, warum also nicht beides
miteinander verbinden?Macht Java programmieren keinen Spa�, bzw. mu� man
es erst lernen, so kann man sich vielleicht mit einem Roboter als Anwendung
motivieren.
2Der Begri� Interface wird hier im gleichen Sinne wie in Java verwendet. Ein Interface be-
schreibt die Schnittstelle und bis zu einem gewissen Grad auch das Ergebnis der Funktionalit�at
von Objekten.
1.4. INHALTS�UBERSICHT 5
F�ur Lehrende und Lernende ist Motivation sehr hilfreich. Durch die Ver-
bindung von Mechanik und Informatik im Roboterbau kann man das eine als
Vehikel benutzen, um die damit verbundene Motivation auf das andere zu �uber-
tragen. In diesem Fall, wie auch beim Hobby, kommt es weniger auf perfekte
Pr�azision und hohe Geschwindigkeit des Roboters an, als darauf, da� man re-
lativ leicht und schnell zu greifbaren Ergebnissen kommt. Spielzeug ist daf�ur
eigentlich die ideale Plattform (Nievergelt, 1999), zumal die notwendige Hard-
ware im Vergleich mit professionelleren Systemen deutlich billiger zu bekommen
ist.
Bei der Entwicklung von Simulationsmodellen in der Industrie ist zwar der
Spielraum f�ur den Hardware-Aufwand gr�o�er, aber daf�ur die Entwicklungszeit
oft von entscheidender Bedeutung. Je mehr dabei auf bereits vorhandene Mo-
dule zur�uckgegri�en werden kann desto besser. Kann die gleiche Software so-
wohl die Modelle als auch die "gro�en" Anlagen steuern, hat man nicht nur
Zeit gespart, sondern bekommt auch die unsch�atzbare M�oglichkeit, die Softwa-
re vorher, am Modell, ohne gr�o�ere Gefahr, zu testen. Eine Implementierung
f�ur diesen speziellen Verwendungszweck ist zwar im Rahmen dieser Arbeit nicht
geschehen, wurde aber weitgehend o�engehalten. F�ur die Programmierung der
reinen Simulationsmodelle, z.B. aus Fischertechnik, n�utzen die gleichen Din-
ge wie auch schon bei Hobby & Lehre: Es macht Spa� und produziert schnell
Ergebnisse.
1.4 Inhalts�ubersicht
Das folgende Kapitel beschreibt den "Beispielroboter" Ariadne. Ariadne kann
auf ein Licht zufahren und dabei Hindernissen ausweichen. Der Roboter ist
daf�ur aus zwei unabh�angigen Modulen aufgebaut, von denen eines ein Licht
�nden (LiSe) und das andere fahren und Hindernissen ausweichen kann (Trus-
ty).
In Kapitel 3 werden bereits bestehende Programmiersysteme untersucht.
Durch die Programmierung des in Kapitel 2 beschriebenen Robotermodells mit
unterschiedlichen Systemen werden deren Besonderheiten und Unterschiede ver-
deutlicht. Am Ende dieses �Uberblicks werden die dadurch erlangten Erkennt-
nisse zusammengefasst und erste Schlussfolgerungen daraus gezogen, welche vor
allem einen Ausgangspunkt f�ur das darauf folgende Kapitel bilden sollen.
Kapitel 4 ist die Beschreibung des im Rahmen dieser Arbeit entwickelten
Programmiersystems, das Abstract Robot Toolkit (ART). Es wird zuerst der
Grundgedanke erl�autert, welcher hinter dem System steckt, und welche Ziele
mit dem System erreicht werden sollen. Zudem werden einige wichtige Konzepte
wie MVC und Subsumption in diesem Zusammenhang n�aher betrachtet.
In Kapitel 5 wird eine praktische Einf�uhrung in das System gegeben, bei der
gezeigt wird, wie man damit Roboter programmieren kann. Hier wird, neben
einigen zus�atzlichen Erl�auterungen der F�ahigkeiten von ART, der rote Faden
aus Kapitel 3 wieder aufgenommen und das von dort bereits bekannte Robo-
termodell programmiert.
In Kapitel 6 wird erkl�art, wie das System f�ur andere Roboter-Hardware er-
6 1. EINLEITUNG
weitert werden kann. Daf�ur wird als Beispiel ein "Treiber" erl�autert, welcher
Textfelder des AWT als Ein- und Ausg�ange f�ur Sensoren und Aktuatoren ver-
wendet.
Das letzte Kapitel baut auf die Schlussfolgerungen aus Abschnitt 3.3 auf.
Es wird ein Vergleich zwischen den Systemen aus Kapitel 3 und dem eige-
nen System, das in den Kapiteln 4 bis 6 beschrieben wurde, durchgef�uhrt. Die
wesentlichen Vor- und Nachteile, sowie die Unterschiede, die in Kapitel 5 zu-
tage getreten sind, werden noch einmal zusammengefasst. Es wird bewertet,
wie vollst�andig die gesetzten Ziele erreicht wurden und wo o�enbar prinzipiel-
le Schwierigkeiten stecken. Au�erdem werden Ideen f�ur eine Weiterentwicklung
aufgezeigt.
2
Ariadne {
a Li(ght)Se(arching) Trusty
Als Anwendungsbeispiel f�ur die verschiedenen Programmiersysteme soll ein Ro-
botermodell dienen, welches auf ein Licht zufahren und dabei Hindernissen aus-
weichen kann. Dieses Modell soll (auf der Harware-Seite), mit m�oglichst ver-
gleichbarer Funktionalit�at, einerseits aus Fischertechnik- und andererseits aus
Lego-Bauteilen realisiert werden. Zudem soll es aus zwei weitgehend unabh�angi-
gen Modulen bestehen: Eines ist f�ur das Chassis, das fahren und Hindernissen
ausweichen kann (Trusty). Das andere Modul ermittelt die relative Richtung,
in der sich eine Lichtquelle be�ndet (LiSe). Die Verbindung der beiden Module
kann dann einen Roboter ergeben, der auf ein Licht zufahren und dabei Hinder-
nissen ausweichen kann (Ariadne). Jeder lichtsuchende Trusty-Roboter besteht
also aus zwei Robotern mit geringerem Funktionsumfang, die von Ariadne mit-
einander verbunden werden.1
2.1 Trusty
Robotermodelle, die herumfahren und dabei im Weg stehenden Dingen wie
Cola aschen, St�uhlen oder auch Menschen ausweichen, sind beliebt. Der Name
Trusty stammt aus Knudsen (1999) und hat sich im Rahmen der Vorlesung zur
Roboterprogrammierung an der Universit�at Osnabr�uck (Schreiner, 2000b) als
Name f�ur solche ausweichenden Roboter eingeb�urgert.
Die hier verwendete Variante besitzt einen Antrieb, der aus zwei Motoren
besteht, die jeweils ein Rad antreiben. Ein drittes Rad ist leicht drehbar um die
senkrechte Achse gelagert, um einerseits einen sicheren Stand des Roboters zu
erm�oglichen, aber andererseits der durch die R�ader mit den Motoren vorgege-
benen Bewegung weitgehend zu folgen. Dieser Antrieb ist typisch f�ur einfache
1www.webster.com wei� �uber Ariadne folgendes zu berichten:
Etymology: Latin, from Greek Ariadne
: a daughter of Minos who helps Theseus escape from the labyrinth
Nun gut, hier ist es Trusty, der die Hilfe bekommt und da unsere Ariadne von LiSe unterst�utzt
wird, braucht sie auch keinen Faden . . .
7
8 2. ARIADNE { A LI(GHT)SE(ARCHING) TRUSTY
Abbildung 2.1: Das Trusty-Chassis als Schema. Zwei voneinader unabh�angige An-triebsr�ader, sowie vorne zwei Bumper-Sensoren.
mobile Roboter.2
Vorne sind zwei bewegliche Querstangen angebracht, die jeweils einen Touch-
sensor bet�atigen, wenn der Roboter gegen ein Hindernis f�ahrt. Der Roboter ist
also nur in der Lage, gen�ugend fest stehende Hindernisse, die sich zudem auch
noch in der richtigen H�ohe be�nden, zu erkennen. Von Tischkanten zum Beispiel
w�urde er einfach herunterfallen.
2.2 LiSe
Oben auf dem Roboter be�nden sich zwei um eine senkrechte Achse drehbar
montierte Lichtsensoren. Mit zwei Sensoren, die V-f�ormig montiert sind, ist es
relativ einfach herauszu�nden, ob links oder rechts mehr Licht ist. Die Dreh-
achse wird von einem Motor, stark untersetzt (1:243 bei Lego und 1:6 { bzw.
1:1448,7 wenn man das am Motor montierte Getriebe mit einbezieht { bei Fi-
schertechnik), angetrieben. Au�erdem kann die Position der Drehachse �uber
einen Rotationssensor gemessen werden (Lego 48, Fischertechnik 48 Abstufun-
gen pro Umdrehung der Achse mit dem Lichtsensor; da� diese Werte gleich
sind, ist nur ein Zufall).
Da die Lichtsensoren an einem Kabel h�angen, k�onnen sie au�erdem nicht
um ganze 360Æ gedreht werden. Es gibt also eine Art Endabschalter, d.h. einen
Touchsensor, der bei Erreichen des einen Randes des Bewegungsbereiches be-
t�atigt wird. Das Erreichen des anderen Randes kann dann �uber den Rotations-
sensor festgestellt werden. Damit ist es au�erdem m�oglich, die zum Unterbau
relative Ausrichtung der Drehachse festzustellen, ohne da� sich die Achse beim
Start in einer de�nierten Position be�nden mu�.
Mit diesem Aufbau ist es m�oglich, die Lichtsensoren auf eine Lichtquelle aus-
zurichten und diese Ausrichtung bei einer relativen Bewegung der Lichtquelle
daran anzupassen. Die Lichtquelle kann innerhalb des Bewegungsbereiches ver-
folgt werden und die Position der Ausrichtung ist �uber den Rotationssensor
feststellbar. Der Name LiSe steht f�ur "Light-Search".
2Das beschriebene Rad wird im englischen auch als idler wheel bezeichnet.
2.3. ARIADNE 9
S
Abbildung 2.2: LiSe als Schema. Zwei Lichtsensoren, V-f�ormig zueinander positioniert,ein Antrieb mit Rotationssensor (S) und Endabschalter.
2.3 Ariadne
Beide Teile, das Trusty-Chassis und der drehbare Turm mit den Lichtsensoren
von LiSe, sind f�ur sich genommen schon als eigenst�andige Roboter anzusehen.
Trusty kann herumfahren und Hindernissen ausweichen und LiSe kann eine sich
bewegende Lichtquelle verfolgen. F�ugt man diese beiden Module zusammen hat
man einen Roboter der eine Lichtquelle verfolgen, darauf zufahren, und dabei
Hindernissen ausweichen kann: Ariadne. Dieses Zusammenf�ugen geschieht auf
der Ebene der Lego- bzw. Fischertechnik-Bausteine, indem der Turm auf dem
Chassis in nat�urlicher Weise montiert wird.
Dieses Zusammenf�ugen der voneinander unabh�angigen Module zu einem
komplexeren Ganzen soll so weit wie m�oglich auch auf der Steuerungsebene in
der Software nachvollzogen werden. Die Steuerung f�ur die jeweiligen Module
soll daher unabh�angig voneinander programmiert werden. Trotzdem wird ver-
sucht, die dabei entstandene Software mit m�oglichst wenig Aufwand f�ur Ariadne
wiederverzuverwenden.
W�urde man die Lichtsensoren z.B. durch eine Kamera ersetzen, so k�onnten
nat�urlich auch Objekte wie beispielsweise ein Fu�ball verfolgt werden { schon
k�onnte der Roboter beim Robocup mitspielen!
2.4 Hervorzuhebende Eigenschaften
Das Robotermodell Ariadne ist aus verschiedenen Bed�urfnissen heraus entstan-
den.
typische Sensorik: Obwohl man an die Eing�ange des Brick und des Intelli-
gent Interface im Grunde beliebige Strom- bzw. Widerstandsquellen an-
schlie�en kann, so sind doch die Schalter (Tastsensoren) am h�au�gsten
anzutre�en. F�ur analoge Sensoren ist ein Lichtsensor das beste Beispiel,
da er in den Grundbauk�asten "Robotics Invention System" (1.0 - 2.0)
und "Mobile Robots" bereits vorhanden ist. Ein Rotationssensor geh�ort
bei Lego zwar nicht zum "Standard", ist aber auch ein sehr wichtiger
10 2. ARIADNE { A LI(GHT)SE(ARCHING) TRUSTY
Abbildung 2.3: Die Lichtsensoren bei Lego sind die beiden blauen Steine mit dem rotleuchtenden Punkt.Bei Fischertechnik sind die Lichtsensoren die kleinen gelben Steine, die wie Lampenaussehen. Das selbstgebaute Getriebe f�ur die �Ubersetzung nimmt viel Platz ein.
Sensor, da nur mit ihm eine pr�azise Steuerung der Motoren m�oglich ist.
Die Rotationssensoren von Lego und Fischertechnik unterscheiden sich
von den anderen Sensoren zudem dadurch, da� sie eine kontinuierliche
Abfrage ihres Zustands ben�otigen, um einen sinnvollen Wert liefern zu
k�onnen.
modularer Aufbau: Da ein wesentlicher der betrachteten Aspekte die Modu-
larisierung und Wiederverwendung von Software ist, mu� der Beispielro-
boter selbst aus Modulen bestehen, die unabh�angig voneinander betrach-
tet werden k�onnen.
Standardmodell: Wenigstens eines der Modelle sollte eine Art "Standardmo-
dell" sein. Ein Beispiel, das in vielen B�uchern zu dem Thema verwendet
wird, ist ein Roboter wie Trusty. Damit ist es auch einem Leser ohne Er-
fahrung in der Konstruktion mit Lego und/oder Fischertechnik m�oglich,
zumindest Trusty nachzubauen und eigene Programmierversuche durch-
zuf�uhren. Selbst die Anleitungen zu den Grundbauk�asten enthalten Bei-
spiele f�ur einen solchen Roboter (er hei�t dort nur nicht Trusty).
viele Sensoren und Motoren: Die Anschlussm�oglichkeiten der verwendeten
Hardware sollten weitgehend ausgereizt werden, um zu sehen, wie sich die
Systeme bei aufw�andigeren Konstruktionen verhalten. Der Brick bietet
im Grunde nur drei Anschl�usse f�ur Sensoren, f�ur das komplette Ariadne-
Beispiel werden aber sechs Sensoren verwendet. Da aber vor allem die
Tastsensoren des Brick nicht unbedingt eine spezielle Konstruktion be-
n�otigen, kann man diese durchaus parallel zu einem anderen Sensor ver-
2.4. HERVORZUHEBENDE EIGENSCHAFTEN 11
wenden (Ferrari und Ferrari, 2002). Es ist dann aber notwendig, da� die
verwendete Software einem weitgehende Freiheit darin l�asst, wie man den
Eingang kon�guriert und seinen Wert ausliest.
Lego und Fischertechnik Die Interface-Hardware von Lego und Fischertech-
nik ist sehr verschieden. So wird ein g�anzlich anderes serielles Protokoll
verwendet, die elektrischen Eigenschaften der Sensoren unterscheiden sich
weitgehend und die Ansteuerung der Motoren ist auch nicht auf die gleiche
Weise umgesetzt. Zudem sind am Intelligent Interface sogar noch ein paar
Anschl�usse frei. Trotzdem k�onnen mit beiden Systemen Roboter gebaut
werden, die sich im Wesentlichen gleichen. Damit k�onnen, anhand eines
gemeinsamen Steuerungsalgorithmus f�ur beide Varianten, die Abstrakti-
onsf�ahigkeiten des Abstract Robot Toolkit (ART) auf die Probe gestellt
werden.
Das Abstract Robot Toolkit ist aber keineswegs nur f�ur dieses Roboter-
modell entwickelt worden. Es soll vielmehr jeder Roboter mit ART gesteuert
werden k�onnen, zumindest vom Prinzip her. Die Verwendung von Lego und
Fischertechnik hat rein praktische Gr�unde. Die Hardware ist billig und der Zu-
sammenbau eines Roboters erfordert keine tiefgehenden ingenieurwisenschaft-
lichen Kenntnisse. Der Beispielroboter Ariadne ist nur das Testobjekt mit dem
das Prinzip erkl�art und �uberpr�uft wird.
12 2. ARIADNE { A LI(GHT)SE(ARCHING) TRUSTY
3
Programmiersysteme f�ur
Roboter
In diesem Kapitel werden verschiedene Programmiersysteme f�ur LEGO Mind-
storms und Fischertechnik Computing vorgestellt. Besonderer Wert wird dabei
auf die M�oglichkeiten zur Modularisierung undWiederverwendung gelegt, sowie
auf die Verwendung von Threads und Events.
Im Abschnitt 3.1 werden gra�sche Programmierumgebungen vorgestellt.
Der Abschnitt 3.2 auf Seite 22 besch�aftigt sich mit den verschiedenen Program-
miersystemen auf Textbasis, von denen einige gleich ein eigenes Betriebssystem
mitbringen.
Es gibt noch wesentlich mehr, frei verf�ugbare Systeme, als hier beschrieben
werden. Als Einstieg im Zusammenhang mit dem Brick bietet sich das Lugnet-
Forum im Internet an (http://news.lugnet.com/robotics/rcx/). Von Fi-
schertechnik gibt es auch eine Web-Seite von welcher aus weitere Program-
miersysteme erreichbar sind, die sich aber meistens auf eine Art Treiber, f�ur
die Ansteuerung aus bestimmten Programmiersprachen heraus, beschr�anken
(http://www.fischertechnik.de/ft-Computing.html).
Alle bekannten verf�ugbaren Systeme zu betrachten w�urde im Rahmen dieser
Arbeit zu weit gehen. Aus den Bereichen der gra�schen Programmiersysteme
wird LLWin besonders ausf�uhrlich behandelt, bei den textbasierten wurde le-
JOS der meiste Raum einger�aumt. Ansonsten werden einige der wichtigsten Sy-
steme kurz vorgestellt und die im Zusammenhang mit dieser Arbeit wichtigen
Eigenschaften erl�autert. F�ur einige der vorgestellten Systeme existieren Bei-
spielprogramme f�ur LiSe, Trusty und Ariadne, deren Quellcode auf der CD zu
dieser Arbeit enthalten ist, sowie aus dem Internet geladen werden kann (siehe
Anhang, Abschnitt A.1 auf Seite 136). Hin und wieder werden, um Besonder-
heiten am realen Beispiel zu verdeutlichen, Ausschnitte aus den Programmen
in der Beschreibung der jeweiligen Systeme gezeigt.
3.1 Gra�sche Programmiersysteme
F�ur die Computerinterfaces von Lego und Fischertechnik sind vor allem drei
gra�sche Programmierumgebungen von Bedeutung, die vom Hersteller der je-
13
14 3. PROGRAMMIERSYSTEME F�UR ROBOTER
weiligen Hardware angeboten werden:
� LLWin (Lucky Logic f�ur Windows) f�ur Fischertechnik
� RCX-Code f�ur Lego
� Robolab f�ur Lego
RCX-Code ist speziell f�ur das Robotics Invention System entwickelt wor-
den. Robolab und LLWin sind an die jeweilige Hardware angepasste Versionen
einer Software, die auch in der Industrie eingesetzt wird. Robolab stammt von
LabVIEW ab und der gro�e Bruder von LLWin ist iCon-L. LabVIEW wird
von National Instruments verkauft und ist vor allem f�ur den Einsatz im La-
bor konzipiert. iCon-L wird von der Firma Pro-Sign Process Design GmbH als
Programmiersystem f�ur Prozessautomation verkauft.
Bei den gra�schen Systemen wird ein Schwerpunkt auf LLWin gelegt. LL-
Win ist einerseits m�achtiger als RCX-Code, allein schon deswegen, weil man
Variablen verwenden kann, andererseits ist es �uberschaubarer als Robolab, da
weniger Programmierbausteine zur Verf�ugung stehen.
3.1.1 LLWin
LLWin wird von Fischertechnik verkauft und l�auft nur unter Windows. �Ahn-
lich wie bei den anderen gra�schen Programmiersystemen werden Bausteine auf
einem Arbeitsblatt plaziert und durch "Dr�ahte", die den Kontroll uss symbo-
lisieren, miteinander verbunden. Programme k�onnen in das Interface geladen
werden. Das Interface f�uhrt sie dann unabh�angig von einem angeschlossenen
PC aus. Werden die Programme hingegen auf dem PC betrieben, ist sogar eine
Ablaufverfolgung in der gra�schen Programmansicht m�oglich.
Es gibt 115 Speicherpl�atze f�ur Variablen1 und innerhalb von Zuweisungen
und Vergleichen k�onnen, zumindest ab Version 3.0, einfache arithmetische Ope-
rationen wie +��= und Klammern eingesetzt werden. Ohne diese Operationen
war bisher etwa eine sinnvolle Kalibrierung von Sensoren kaum zu bewerkstelli-
gen, da man beispielsweise ein arithmetisches Mittel nur auf dem Umweg �uber
die Operationen INC, DEC, einen Vergleich und eine Schleife (also mit entspre-
chend gro�em Aufwand) berechnen konnte.
Die LLWin-Version von Trusty wurde mit Subsumption (siehe 1.2 auf Sei-
te 4) programmiert, LiSe hingegen nicht. Bei Trusty ist Subsumption besonders
hilfreich im Hinblick auf die sp�atere Erweiterung zu Ariadne, da dadurch in
Trusty's Verhalten leichter eingegri�en werden kann.
LiSe k�onnte, von der Aufgabenstellung her, zwar auch sehr gut mit Sub-
sumption programmiert werden (siehe z.B. Abschnitt 3.2.4 auf Seite 27 oder
auch Abschnitt 5.6 auf Seite 99), eine solche Implementierung in LLWin w�urde
jedoch 3-4 Threads mehr ben�otigen. Die Steuerung von Ariadne ist aber bereits
in ihrer jetzigen Form { ohne Subsumption bei LiSe { schon zu komplex, um
199 normale sowie 16 Z�ahlvariablen, die auch von dem Baustein POSITION benutzt werden.
Alle sind 16 Bit signed.
3.1. GRAFISCHE PROGRAMMIERSYSTEME 15
auf das Interface heruntergeladen zu werden. Die Ausf�uhrung ist dort schlicht
zu langsam. Bei Ausf�uhrung in LLWin auf einem angeschlossenen PC (Penti-
um III, 650Mhz) ist die Geschwindigkeit zwar noch ausreichend, die Reaktions-
zeiten "f�uhlen" sich aber bereits etwas langsam an. Aus diesem Grund wurde
die Steuerung von LiSe in LLWin auf konventionellem Weg, mit einer gro�en
Schleife, realisiert.
Bei der Programmierung mu�ten Kompromisse eingegangen werden, da die
komplette Fassung von Ariadne anfangs zu langsam und zu ungenau lief. Zum
Beispiel war die Positionsmessung mit so gro�en Fehlern behaftet, da� sie un-
brauchbar war. Zur L�osung wird nun immer die letzte gewollte Drehrichtung
gespeichert und zwischen einer Umkehrung der Drehrichtung eine kurze Pause
eingelegt, in der der Motor steht. Damit wird die Positionsmessung zwar noch
langsamer, aber wieder ausreichend genau.
Ein Grund f�ur die geringe Ausf�uhrungsgeschwindigkeit sind wahrscheinlich
die vielen Threads. Sie sind jedoch f�ur eine elegante und �ubersichtliche Program-
mierung unabdingbar. Zudem sollten ja auch die M�oglichkeiten von LLWin aus-
gelotet werden. Es ist bestimmt m�oglich, mit LLWin eine Steuerung f�ur Ariadne
zu entwickeln, die schneller l�auft und den Roboter mindestens genausogut steu-
ert, das w�are dann wahrscheinlich aber kein geeignetes Beispiel mehr, weil man
wohl auf den Versuch der Modularisierung und Wiederverwendbarkeit, elegante
Beispiele f�ur Thread-Synchronisation sowie Subsumption verzichten m�u�te.
Threads
In LLWin ist das eigentliche Erzeugen von verschiedenen Threas sehr einfach.
Dazu braucht man nur mehrere Startsymbole auf das Arbeitsblatt zu setzen. Ei-
ne M�oglichkeit zu deren Synchronisierung ist jedoch nicht vorgesehen. Es bleibt
nur der Ausweg dieses �uber Variablen selbst umzusetzen. Threads k�onnen sich
nicht gegenseitig aufwecken oder unterbrechen, eine Synchronisierung verlangt
daher die wiederholte Abfrage eines Zustands.
VAR81 1
VAR97 1025
VAR96 1025
VAR98 EX
Obergrenzen für den Analogwert
Signal an Main-Thread, daß dieInitialisierung abgeschlossen ist.
Kopierthread für Analogwerte.
VAR81=1
1
0
VAR5 VAR96
VAR6 VAR97
Kopierthread für die Analogwerteerst einmal durchlaufen lassen.
Eigene Variablen initialisieren
Obere Schranke der Lichtsensoren.
Main
...
...
Abbildung 3.1: Der linke Thread braucht f�ur die Initialisierung seiner Variablen diebereits initialisierten Variablen VAR96/97 aus dem rechten Thread. Der Linke wartetzu Beginn daher bis der Rechte einen bestimmten Punkt in seiner Ausf�uhrung erreichthat (in diesem Fall bis die Schleife einmal durchgelaufen ist).
Im Beispielprogramm werden Threads auf zwei Arten synchronisiert. Im
16 3. PROGRAMMIERSYSTEME F�UR ROBOTER
einen Fall soll ein Thread warten, bzw. einen bestimmten Ausf�uhrungsweg erst
dann nehmen, wenn ihm ein anderer explizit dazu die Erlaubnis gibt. Das wird
schlicht �uber das Setzen und Abfragen von jeweils einer Variable pro solch einer
Kommunikation geregelt. Im einfachsten Fall wird dies { im Algorithmus von
LiSe { dazu verwendet, zwei Threads ihre Initialisierung in einer bestimmten
Reihenfolge ausf�uhren zu lassen (siehe Abbildung 3.1 auf der vorherigen Seite).
Im Algorithmus von Trusty sollen die Threads, die f�ur die Implementie-
rung der jeweiligen Verhaltensweisen zust�andig sind, nur dann ihr Verhalten
ausf�uhren, wenn der Thread, der die Priorit�aten der Verhaltensweisen regelt,
ihnen das Signal dazu gibt. Der Thread f�ur die Priorit�atenvergabe hingegen
mu� dann warten, bis die jeweils angesto�ene Verhaltensweise "fertig" ist, damit
nicht die n�achste beginnt, solange die vorherige noch l�auft. An dieser Stelle wird
der Baustein POSITION verwendet, um Threads m�oglichst ressourcenschonend
zu synchronisieren (siehe Abschnitt 3.1.1 und Abbildung 3.3 auf der n�achsten
Seite).
Etwas schwieriger wird es, wenn es nicht darum geht, zwei Threads immer an
der gleichen Stelle aufeinander warten zu lassen, sondern wenn erreicht werden
soll, da� bestimmte, kritische Bereiche nicht von mehreren Threads gleichzeitig
ausgef�uhrt werden k�onnen. In Java gibt es daf�ur eine eigene Kontrollstruktur {
synchronized. Ohne Java l�ost man solch ein Problem z.B. mit einer Semaphore.
Aus
0.5 s
VAR82
VAR82
VAR82<0
0
1
Ein
Aus
VAR82
Ein
Abbildung 3.2: Implementierung einer Semaphore in LLWin. Das UnterprogrammAQUIRE z�ahlt VAR82 um eins herunter, wird aber nur verlassen, wenn VAR82 dabei nichtkleiner 0 wird. RELEASE z�ahlt VAR82 wieder um eins hoch.
Abbildung 3.2 zeigt die Implementierung einer Art Semaphore (Tanenbaum,
1990; Dijkstra, 1965) als Unterprogramm in LLWin. Die korrekte Funktions-
weise dieser Implementierung ist davon abh�angig, da� die einzelnen Bausteine
unteilbar abgewickelt werden. Davon kann man aber ausgehen, da es sich bei
LLWin um einen Interpreter handelt.
Mit dieser Implementierung ist ein gesch�utzter Bereich m�oglich. F�ur mehre-
re m�u�ten auch mehrere solcher Unterprogramm-Paare implementiert werden.
Mit dem Unterprogramm AQUIREwird Zugri� auf einen gesch�utzten Bereich ver-
langt, mit RELEASE wird er wieder freigegeben. Dabei mu� man sich nat�urlich
an die Spielregeln halten und darf z.B. nicht RELEASE aufrufen, wenn man noch
gar kein AQUIRE aufgerufen hat. Direkte Ver�anderungen an der von den Unter-
programmen benutzten Variable sind in diesem Sinne (nat�urlich) genausowenig
gestattet.
Eine Ausnahme ist die Initialisierung der Z�ahlvariable von AQUIRE/RELEASE.
Das Programm beginnt mit einem Zustand, in dem kein Aufruf von AQUIRE zu
3.1. GRAFISCHE PROGRAMMIERSYSTEME 17
Ende gehen w�urde, d.h. ein Thread sollte mindestens einmal RELEASE ohne
ein vorausgegangenes AQUIRE aufrufen. Es gibt also zu jedem Zeitpunkt nur
h�ochstens genausoviele bereits durchlaufene AQUIRE-Aufrufe wie bereits durch-
laufene RELEASE-Aufrufe.
Events
In LLWin gibt es etwas, das man mit Events vergleichen kann, in zwei Bau-
steinen: POSITION und FLANKE. Kommt ein Thread zu solch einem Baustein,
verharrt er dort solange, bis die angegebene Anzahl von Flanken (POSITION),
bzw. der angegebene Typ Flanke (fallend oder steigend bei FLANKE) an dem
angegebenen Eingang registriert wurde. In Bezug auf die analogen Eing�ange
mu� man auf Events verzichten.
VAR22 0
VAR32=1
1
0
VAR22 0
VAR22 1
E 1
1
0
VAR32 0
E6 VAR34 0
E6 VAR35 0
E6 VAR31 0
VAR25=1
0
1
VAR35 1
VAR31 1
VAR34 1
VAR21=1
0
1
VAR24=1
0
1
Scheduler für Subsumption
AusweichenMitte
Ins LichtDrehen
ImmerGeradeaus
.....
.
...
VerhaltensweiseAusweichen Links
Linker Bumper Sensor
Darf nicht
Soll
Ausweichen ...
Nachricht an Scheduler,daß fertig
Will
Will nicht
Abbildung 3.3: Die Variablen VAR3x werden von dem Scheduler (links) auf den Wert 1
gesetzt, als Signal an den jeweiligen Verhaltensweisen-Thread (rechts). Umgekehrt setztein Verhaltensweisen-Thread, nachdem er sein Verhalten ausgef�uhrt hat, diese Variablewieder auf 0. Die gleiche Variable kann hier f�ur zwei verschiedene Kommunikationengenutzt werden, da die Kommunikationen sich immer genau abwechseln.
Da der Baustein POSITION zum Z�ahlen eine globale Variable benutzt, ist es
m�oglich (aus einem anderen Thread heraus) den Wert auf die im Baustein an-
gegebene Zahl zu setzen und damit die Ausf�uhrung des Bausteins zu beenden.
Es ist zwar nicht dokumentiert, aber der Baustein sollte in diesem Fall auch
wirklich immer zu Ende gehen, da ein Test zeigt, da� auch eine �Uber-, bzw.
Unterschreitung der angegebenen Zahl zum Abbruch f�uhrt. Es ist also durch
"Missbrauch" des Bausteines POSITION realisierbar, einen Thread { m�oglicher-
weise2 ohne Ressourcenverbrauch { auf ein durch einen anderen Thread aus-
2Die konkrete Implementation von POSITION ist nicht dokumentiert.
18 3. PROGRAMMIERSYSTEME F�UR ROBOTER
gel�ostes Ereignis warten zu lassen (siehe Abbildung 3.3 auf der vorherigen Seite).
Will man durch ein Ereignis, wie z.B. eine steigende Flanke, einen bestimm-
ten Vorgang ansto�en und w�ahrenddessen aber weiterhin den Eingang auf ein
erneutes Eintreten des Ereignisses �uberwachen, mu� man sich darum selber
k�ummern. Aus diesem Grund gibt es zur kontinuierlichen Bestimmung der Po-
sition des Towers im Algorithmus von LiSe zwei Threads, denn um die Position
einer Achse mit einem der vorgesehenen Impulsr�ader zu �uberwachen, reicht der
Baustein POSITION alleine nicht aus.
VAR8=1
0
1
VAR71 VAR71-(VA...VAR71 VAR71+(VA...
VAR73 VAR72
VAR74 VAR73
VAR72 > VAR73
1
0
AQUIRE
RELEASE
E 34
1
0
E 44
0
1
E34=1 bedeutet M4 dreht linksherum.
Seit dem letzten Durchgang beobachtete Flanken je nachMotorzustand von der Positionsvariable (VAR71)subtrahieren (Rechtsdrehung) oder zu ihr addieren (Linksdrehung).
Beginn eines exklusiven Zugriffs-bereichs. VAR82 wird dafür ähnlichwie Semaphore verwendet.
Ende desExklusiven Bereiches
Positionsthread.Speichert Position vonM4 ständig in VAR71.
Die Berechnung ist von einem Aquire-Release Paarumgeben, um einen sicheren Reset der Variablenzur Laufzeit zu ermöglichen. (RST_ROT)
E44=1 heißt M4 rechtsdrehend.
In VAR8 steht die letzte Suchrichtung.
E8 VAR72 32000
Flanken beobachten und mit minimalemAufwand speichern um möglichst keineFlanke zu verpassen.
Abbildung 3.4: Ein Thread z�ahlt die Flanken, der andere bestimmt die Richtung. W�urdedas der gleiche Thread machen, gingen Flanken leichter verloren. Leider ist es nichtganz einfach die Drehrichtung eines Motors zu bestimmen, der vielleicht inzwischenschon wieder aus ist ...
Einer der Threads be�ndet sich st�andig im Baustein POSITION, damit m�og-
lichst keine Flanke verloren geht. Ein weiterer beobachtet die Z�ahlvariable die-
ses POSITION-Bausteins und addiert oder subtrahiert die Ver�anderungen seit
seinem letzten Schleifendurchlauf in einer weiteren Variablen, je nachdem in
welche Richtung sich der Tower gerade dreht, bzw. wahrscheinlich das letzte
Mal gedreht hat. Diese Variable enth�alt dann die Position des Towers (siehe
Abbildung 3.4).
Modularisierung und Wiederverwendung
Zur Strukturierung eines Programms hat man die M�oglichkeit, Unterprogram-
me zu erstellen. Parameter oder lokale Variablen gibt es nicht, ein Unterpro-
3.1. GRAFISCHE PROGRAMMIERSYSTEME 19
gramm greift auf dieselben globalen Variablen zu wie der Rest des Programms.
Wiederverwendung von (Teilen von) Programmen in anderen Programmen
ist nur durch Cut&Paste einer Selektion von Symbolen auf dem Arbeitsblatt
m�oglich. Dabei mu� au�erdem darauf geachtet werden, da� eventuell mitselek-
tierte Unterprogramme bereits vorher kopiert wurden und den gleichen Namen
bekommen haben.
Eine gro�e Schwierigkeit stellen in diesem Zusammenhang die Variablen dar.
Da alle Variablen global und deren Anzahl beschr�ankt ist, mu� man zwangsl�au-
�g irgendwann mit �Uberschneidungen bei den verwendeten Variablen rechnen.
Dann m�ussen Variablen umbenannt werden, wodurch sehr leicht neue Fehler
entstehen. Welche Variable im jeweiligen Projekt noch nicht benutzt wurde, ist
nur durch eigene Kontrolle herauszu�nden, eine Suchfunktion oder �Ahnliches
gibt es nicht. In einem Projekt wie Ariadne werden die Variablen bereits knapp,
vor allem, wenn man wenigstens etwas Struktur hineinbringen und Variablen
innerhalb eines gemeinsamen Kontextes systematisch ausw�ahlen m�ochte.
Im Beispiel kann der Algorithmus von LiSe ohne Anpassungen (wenn man
einmal von Variablennamen absieht) per Cut&Paste �ubernommen werden. Bei
Trusty ist das schon etwas schwieriger, da die Steuerung von Ariadne nicht
gleichzeitig mit der Trusty-Steuerung auf die gleichen Ausg�ange zugreifen darf.
Diese beiden Steuerungen m�ussen miteinander kooperieren. Durch Subsumption
(siehe Abschnitt 1.2 auf Seite 4) ist die Integration von Trusty und Ariadne
zwar einfach, aber nicht ohne Ver�anderung des bereits f�ur Trusty existierenden
Schedulers (vgl. Abbildung 3.3 auf Seite 17) zu bewerkstelligen, da Ariadne in
dessen Vorrangregelung integriert werden mu�.
3.1.2 RCX-Code
Die mit dem Robotics Invention System mitgelieferte Programmierumgebung,
RCX-Code, basiert im Wesentlichen auf stark spezialisierten und eingeschr�ank-
ten Struktogrammen nach Nassi-Shneiderman (Schreiner, 2000b). Das bedeutet
unter anderem, da� die einzelnen Bausteine des Programms direkt aneinander-
gesetzt und daher im Gegensatz zu LLWin und Robolab daf�ur keine Dr�ahte
gezogen werden m�ussen.
Auch in RCX-Code gibt es einen Start-Baustein und �uber die sogenannten
"Sensor-Watcher" k�onnen Threads beim Auftreten eines, durch den Sensor-
Watcher de�nierten, Events gestartet werden. Der gesamte Sprachumfang des
RCX-Code aus dem RIS 1.0 ist recht ausf�uhrlich in Schreiner (2000b) doku-
mentiert. In den Versionen f�ur das RIS 1.5 und 2.0 ist RCX-Code nochmals
deutlich erweitert worden.
Die gesamte Ober �ache des RIS, in welche die Programmierumgebung RCX-
Code eingebettet ist, wird vor allem der erkl�arten Zielgruppe des Produkts
gerecht. Sie ist ansprechend, fast wie ein Computerspiel, gestaltet und um sich
in RCX-Code hineinzu�nden sind kaum Erfahrungen in der Programmierung
notwendig. Die Bausteine sind weitgehend selbsterkl�arend und nicht schwer
zu �nden, da der Zugri� auf den gesamten Vorrat praktisch direkt auf der
Ober �ache, ohne irgendwelche Men�uzugri�e m�oglich ist.
Wer bereits mit "richtigen" Programmierersprachen Erfahrungen gemacht
20 3. PROGRAMMIERSYSTEME F�UR ROBOTER
Abbildung 3.5: Ein Programm in RCX-Code, der Programmierumgebung des RIS. Es
steuert einen Trusty-�ahnlichen Roboter und demonstriert die M�oglichkeit, da� Threads
�uber die einzige Variable in RCX-Code, den "Counter", kommunizieren.
hat, wird jedoch sehr bald feststellen, da� er f�ur die gewohnte L�osung von
erstaunlich vielen Programmieraufgaben Variablen braucht. Leider sind diese
in RCX-Code praktisch gar nicht vorhanden. Auf diese Weise sind auch die
M�oglichkeiten zum Datenaustausch zwischen den einzelnen Threads nur sehr
eingeschr�ankt m�oglich.
Andererseits ist es auch erstaunlich, was trotzdem alles mit dieser Umge-
bung programmierbar ist. Zudem liegt der Ursprung des RIS amMassachusettes
Institute of Technology { dem gleichen Umfeld aus dem auch die LOGO-Turtle
kommt (Papert, 1999). Die Idee, Programmieren durch Verwendung einer bei-
nahe zustandsfreien Programmiersprache zu lernen und dabei auch noch Spa�
zu haben, wird daher im RIS nur auf eine neue, aufregende Art umgesetzt.
In diesem Kontext ist es nicht weiter verwunderlich, da� mit RCX-Code die
Verwendung von mehreren Sensoren an dem gleichen Eingang nicht unterst�utzt
wird. Dadurch ist die Programmierung des Beispielroboters aber kaum m�oglich.
Lediglich Trusty w�urde keine Schwierigkeiten machen, ein Beispielprogramm f�ur
einen Trusty-�ahnlichen Roboter zeigt die Abblidung 3.5.
3.1.3 Robolab
Robolab wird von LEGO Dacta als Steuerungs-Software f�ur den Brick verkauft.
Es l�auft sowohl unter Windows als auch unter MacOS. Robolab zeichnet sich vor
allem durch seine M�oglichkeiten zur Auswertung von Messdaten aus. Die schiere
F�ulle an Symbolen, die zum Aufbau eines Programms auf dem Arbeitsblatt
plaziert werden k�onnen, ist { zumindest am Anfang { kaum zu �uberblicken.
3.1. GRAFISCHE PROGRAMMIERSYSTEME 21
Es gibt daher f�unf verschiedene "Schwierigkeitsstufen" und nur in einer steht
einem wirklich das komplette Arsenal an Operationen zur Verf�ugung.
Abbildung 3.6: Ein einfaches Programm in Robolab, welches die Motoren A und C
laufen l�asst, bis der Taster am Eingang 1 gedr�uckt wird.
Es gibt Variablen (sogenannte Container) und alle wichtigen arithmetischen
Operationen. F�ur Motoren und die verschiedenen Sensortypen gibt es gleich
mehrere Bausteine, um diese anzusteuern. Hervozuheben ist die Integration
eines speziellen Sensoradapters �uber den z.B. schlicht eine Spannung gemessen
werden kann.
Die Programmierung erfolgt, indem man Symbole, die f�ur bestimmte Daten,
bzw. Operationen stehen, auf dem Arbeitsblatt plaziert und durch Dr�ahte mit-
einander verbindet. �Uber die Dr�ahte wird in Robolab nicht nur der Programm-,
sondern auch der Daten uss gesteuert. Wie man in Abblidung 3.6 sehen kann,
wird z.B. der Baustein, der einen Motor einschaltet, �uber die Verkn�upfung mit
den Containern 'A', 'C' und '5' kon�guriert. Der eigentliche Kontroll uss bei
der Ausf�uhrung geht entlang der gestricheleten Linien von der gr�unen bis zur
roten Ampel, im Beispiel also von links nach rechts.
Leider ist Robolab nicht in der Lage, einen Sensoreingang des Brick, zu ver-
schiedenen Zeitpunkten des Programmablaufs, f�ur die Verwendung mit mehre-
ren Sensoren, unterschiedlich zu kon�gurieren. M�ochte man also mehrere Sen-
soren �ubereinander an einem Port ansteuern, geht das nur eingeschr�ankt und
etwas umst�andlich. Man mu� sich zum einen auf einen Typ Sensorbaustein be-
schr�anken und au�erdem sind Kombinationen { z.B. mit dem Rotationssensor
{ �uberhaupt nicht m�oglich. Das macht die Entwicklung eines Steuerungspro-
gramms f�ur Ariadne unm�oglich, da die Lego-Version verlangt, insgesamt sechs
Sensoren an den drei vorhanden Anschl�ussen anzusteuern. So h�angt z.B. der
22 3. PROGRAMMIERSYSTEME F�UR ROBOTER
End-Abschalter von LiSe mit dem Rotationssensor zusammen an einem Ein-
gang.
Aus diesem Grund wird hier auf Beispielprogramme verzichtet. F�ur die Ver-
wendung des Brick zur Erfassung und Auswertung von Messreihen ist Robolab
aber o�enbar sehr gut geeignet und daf�ur ist es wohl in erster Linie auch ge-
dacht. In Bezug auf Threads und Wiederverwendung besitzt Robolab �ahnliche
F�ahigkeiten wie LLWin.
3.2 Textbasierte Programmiersysteme
Textbasierte Programmiersysteme haben gegen�uber den gra�schen oft den Vor-
teil, da� das Format, in welchem der Programm-Code gespeichert wird, einfa-
cher Text ist. Das er�o�net weitreichende M�oglichkeiten die Programmierumge-
bung durch Verwendung weiterer Tools zu verbessern, was im Falle von pro-
priet�aren Formaten schwieriger ist. Besonders naheliegend ist z.B. die Verwen-
dung eines Pr�aprozessors oder eines Tools zur Versionskontrolle. Zudem ist man
als "richtiger" Programmierer einfach daran gew�ohnt, Programme in Form von
Text aufzuschreiben. Vor allem die frei verf�ugbaren Systeme, die meistens aus
purem Enthusiasmus entwickelt wurden, sorgen in diesem Bereich f�ur eine sehr
gro�e Auswahl.
Es existieren im Wesentlichen drei verschiedene Ans�atze, welche hier in ei-
genen Abschnitten vorgestellt werden sollen. Als letztes wird leJOS genauer
diskutiert. Es handelt sich dabei um ein Betriebssystem f�ur den Brick, welches
erlaubt in Java geschriebenen Code auf dem Brick auszuf�uhren.
3.2.1 Fernsteuerungen
In diesem Abschnitt werden einige Systeme vorgestellt, die es erlauben, Pro-
gramme zu schreiben, die auf einem normalen PC ablaufen und die Interface-
Hardware von dort aus fernsteuern.
FishFace
F�ur Fischertechnik unter Windows gibt es FishFace von Ulrich M�uller (http:
//www.ftComputing.de/). Es bietet Zugri� auf alle Ein- und Ausg�ange des
parallelen Interface und des Intelligent Interface. Es enth�alt au�erdem einige
Funktionen, die vor allem den Umgang mit Motoren und Impulsr�adern verein-
fachen.
Zugri� auf Events bekommt man, indem bestimmte Methoden des FishFace-
Objekts �uberschrieben werden. Es gibt aber nur Methoden f�ur die digitalen
Eing�ange und, da es nur ein Interface-Objekt gibt, ist auch nur ein Objekt
als "Listener" verwendbar, n�amlich das FishFace-Objekt selbst. Die Methoden
lauten im einzelnen:
InputImpuls(InputNr as Integer, ImpulsNr as Integer)
PositionChange(PositionListe as Variant)
3.2. TEXTBASIERTE PROGRAMMIERSYSTEME 23
InterfaceStatus(Quelle as Integer)
Die ersten beiden dienen zur �Uberwachung des Status der Methoden Wait-
ForChange(InputNr, NrOfChanges) und MoveTo/MoveDelta (Bewegung eines
Motors f�ur eine bestimmte angegeben Anzahl von Impulsen) und werden nur
ausgel�ost, wenn gerade die entsprechende Methode abgearbeitet und dabei ein
Statuswechsel an den digitalen Eing�angen erkannt wird. Die letzte Methode
wird bei jedem erkannten Statuswechsel der igitalen Eing�ange aufgerufen.
Die Dokumentation von FishFace (M�uller, 2000) macht keine explizite Aus-
sage �uber die Implementierung von FishFace im Zusammenhang mit Threads.
Prinzipiell scheint das Interface-Objekt keinen eigenen Thread zu besitzen, der
sich um das Interface k�ummert. Ruft man keine Methode auf, so "verstum-
men" die Motoren des Interfaces bald. Dies w�urde nicht passieren, wenn z.B.
st�andig die Inputs abgefragt w�urden. Daraus folgt, da� diese nur dann abge-
fragt werden, wenn man entweder selbst irgendwelche Methoden aufruft, das
Diagnose-Panel aktiviert oder die Kontrolle durch Ausf�uhrung der Methoden
WaitForChange/Low oder MoveTo/MoveDelta an das Interface abgibt.
Spirit.ocx
Von Lego gibt es das Active-X Control Spirit.ocx (The LEGO Group, 1998)
welches, unter Windows z.B. aus Visual Basic heraus, Zugang zur jeweiligen
Roboterhardware bietet. Es ist ziemlich genau auf die F�ahigkeiten der Standard-
Firmware von Lego abgestimmt, was aber auch nicht weiter verwundert. Da
z.B. auch der Lego Cybermaster eine �ahnliche Firmware enth�alt, funktioniert
Spirit.ocx f�ur diesen ebenfalls. Die Interface-Hardware wird durch ein Objekt
repr�asentiert, welches die gesamte Funktionalit�at enth�alt.
Es gibt nat�urlich Funktionen um die Sensoren abzufragen oder die Motoren
zu beein ussen. Das eigentlich besondere ist aber, da� Spirit.ocx auch einen
Modus kennt, in welchem die aufgerufenen Funktionen nicht sofort ausgef�uhrt,
sondern als Task auf den Brick �ubertragen werden. Da f�ur diesen Modus auch
Funktionen wie "if" und "while" zur Verf�ugung stehen, k�onnen auf diese Weise
"richtige" Programme entwickelt werden. Dieser Code ist zwar nicht mehr sch�on
anzusehen, aber einige Systeme beruhen auf dieser F�ahigkeit und stellen den
Code einfach eleganter dar. So soll z.B. das in Abschnitt 3.1.2 auf Seite 19 vor-
gestellte RCX-Code auf Spirit.ocx beruhen und "Gordon's Brick Programmer"
(http://www.umbra.demon.co.uk/gbp.html) stellt ein solches Programm z.B.
als einen attributierten Baum dar.
Inzwischen stellt Lego einen neueren PC-basierten Treiber namens Ghost
bereit. Dieser konnte jedoch f�ur die vorliegende Arbeit nicht mehr untersucht
werden.
Java-package "ft"
Das Java-package "ft" (Schreiner, 2000b) funktioniert im Prinzip auf allen
Plattformen, f�ur die eine Implementierung des Java Communications API (Sun
Microsystems, 1998) existiert. Dies gilt nur f�ur die Ansteuerung des Intelligent
Interface. Das parallele Interface wird nur unter Windows direkt unterst�utzt.
24 3. PROGRAMMIERSYSTEME F�UR ROBOTER
Das "ft"-package geht schon recht weit in die Richtung die auch mit dieser
Arbeit angestrebt wird. Es ist bereits eine Bibliothek von mehreren Klassen.
Gra�sche Views und Controls, sowie Model-Objekte f�ur Motoren sowie die ana-
logen und digitalen Eing�ange. Es bietet zudem ein Observer-Modell mit Events
von Motoren und den digitalen und analogen Eing�angen.
Ein Steuerungsalgorithmus ist prim�ar als Unterklasse von ft.Controller
vorgesehen. Eine Abweichung von diesem Schema w�urde sofort zu einem erh�oh-
ten Arbeitsaufwand f�uhren. Eigene Threads und deren Synchronisierung sind
nat�urlich ganz normal wie in Java �ublich verwendbar.
Das "ft"-package liefert jedoch kaum Konzepte f�ur die Wiederverwendung
des Steuerungsalgorithmus, zumindest nicht �uber Modell-, bzw. Interface-Gren-
zen hinweg. Zudem wird die an die Ein- und Ausg�ange angeschlossene Periphe-
rie immer als ein Motor oder ein digitaler, bzw. analoger, Sensor angesehen.
Eine Unterscheidung zwischen z.B. einer Lampe und einem Motor wird nicht
gemacht. Daf�ur ist es aber recht schnell, denn f�ur Windows ist sogar ein eigener
spezialisierter JavaComm-Treiber f�ur die serielle Schnittstelle enthalten, der die
recht langsame Implementierung von Sun umgeht.
3.2.2 Sprachen f�ur die Lego-Firmware
Auf den Brick k�onnen leicht Programme geladen werden, die dort lokal zur
Ausf�uhrung gebracht werden. Dies geschieht entweder unter Verwendung von
Spirit.ocx, oder "per Hand", da das Format, welches die Standard-Firmware
von Lego verwendet, bekannt ist. Darauf werden nun viele Sprachen, bzw. Pro-
grammierumgebungen aufgesetzt. Am bekanntesten ist wohl NQC (http://
www.enteract.com/~dbaum/nqc/), das eine C-�ahnliche Syntax und einen eben-
solchen Pr�aprozessor verwendet und einem praktisch alle M�oglichkeiten er�o�-
net, die in der Original-Firmware von Lego existieren. Lego hat inzwischen
auch eine eigene textbasierte Sprache namens MindScript die auf dem von Le-
go nun in der Version 2.0 ausgelieferten Betriebssystem f�ur den Brick basiert
(http://mindstorms.lego.com/sdk2/). MindScript soll laut Lego zudem als
Zwischenformat f�ur gra�sche Systeme verwendet werden.
Die neue Lego-Firmware bietet kon�gurierbare Events und ist im Zusam-
menhang mit Variablen wesentlich leistungsf�ahiger geworden. Es gibt nun auch
lokale Variablen innerhalb eines Tasks. Diese neuen Features werden auch von
NQC unterst�utzt.
F�ur die Programmierung nach dem Subsumption-Konzept ist die neu ein-
gebaute M�oglichkeit, den Zugri� einzelner Tasks auf bestimmte Ressourcen
abh�angig von deren Priorit�at zu regeln, besonders interessant.
Listing 1:
acquire(acquire turnMotor) {monitor(EVENT MASK(nullPositionReachedEvent)
| EVENT MASK(maxPositionReachedEvent)) {PlayTone(3000, 20);SetDirection(turnMotor, rightDirection);On(turnMotor);
➥
3.2. TEXTBASIERTE PROGRAMMIERSYSTEME 25
Listing 1: (Fortsetzung)
do {Wait(10);sees light right(result);
} until(result == 0);} catch {
Off(turnMotor);}
} catch {}
Listing 1 zeigt die Verwendung von Events und der Zugri�skontrolle in NQC.
Es stammt aus dem Beispiel-Programm f�ur LiSe. Mit acquire wird auf die im
Argument angegebene Ressource Zugri� verlangt. Wenn kein anderer Task mit
h�oherer Priorit�at Zugri� auf diese Ressource verlangt, wird der Block hinter
acquire ausgef�uhrt, andernfalls springt die Ausf�uhrung zu dem korrespondie-
renden catch-Block. Der Sprung zum catch erfolgt auch aus dem acquire-
Block heraus, falls sp�ater ein Task mit h�oherer Priorit�at die Ressource f�ur
sich beansprucht. Tasks k�onnen also in gewisser Weise dadurch unterbrochen
werden. Das monitor-Statement funktioniert �ahnlich, zur genaueren Erkl�arung
wird auf das Manual zu NQC verwiesen (Baum, 2001).
3.2.3 Systeme mit eigener Firmware
F�ur den Brick gibt es mehrere alternative Betriebssysteme (Firmware), welche
man verwenden kann, um die Standard-Firmware von Lego zu ersetzen. Der
erste, der es gescha�t hat ein von LEGO unabh�angiges, lau��ahiges System im
Internet bereitzustellen, war wohl Kekoa Proudfoot (Proudfoot, 1998). Zu den
Systemen, die mit einer solchen Ersatz-Firmware kommen, z�ahlen vor allem:
� legOS von Markus L. Noga
� pbForth von Ralph Hempel
� leJOS von Jose L. Solorzano
In diesem Abschnitt werden legOS und pbForth vorgestellt. leJOS wird,
wegen seiner Java-F�ahigkeiten, besonders ausf�uhrlich in Abschnitt 3.2.4 auf
Seite 27 behandelt.
legOS
Das Betriebssystem, welches { in Bezug auf Geschwindigkeit und Speicher-
haushalt { auf dem Brick am besten abschneidet, ist legOS (http://legos.
sourceforge.net/). Es wurde urspr�unglich von Markus L. Noga entwickelt
(Noga, 1999). legOS verwendet eine Version des GNU C-Compilers (gcc) die
Code f�ur den Hitachi H8 Mikroprozessor des Brick erzeugt. F�ur die Verwen-
dung spezieller Peripherie des Brick, wie den Ein- und Ausg�angen, IR-Port,
LCD und Sound enth�alt es entsprechende, spezialisierte Bibliotheken.
26 3. PROGRAMMIERSYSTEME F�UR ROBOTER
Wo es ging wurde versucht POSIX-Konform zu bleiben. Neue Prozesse wer-
den mit fork() erzeugt und Zeichenketten mit cputs() ausgegeben, um nur ein
paar Beispiele zu nennen. Nat�urlich gibt es au�er der Speichergr�o�e des Brick
keine Beschr�ankungen in Bezug auf die Anzahl oder den Typ von Variablen.
Die direkte Verwendung der Hardware, ohne irgendwelche Umwege, macht le-
gOS so schnell, da� es hei�t, man k�onnte sogar Sprache damit samplen { wenn
einem nicht bereits nach ein paar Sekunden der Speicher ausgehen w�urde.
Die Tools von legOS zur Kommunikation zwischen Brick und PC, die man
braucht um beispielsweise ein ausf�uhrbares Programm zu laden, gibt es lei-
der nur f�ur Windows und Linux. Sowohl MacOS und andere Unix-Systeme als
Linux werden nicht unterst�utzt, da die Tools dort wegen einer anderen I/O-
Schnittstelle bisher nicht laufen (Baum et al., 2000, S. 152).
Neben der Geschwindigkeit ragt als Besonderheit auch noch die Netzwerk-
Schicht LNP3 hervor. Sie unterst�utzt eine paketbasierte Kommunikation zwi-
schen einem PC mit IR-Tower und mehreren addressierbaren Bricks.
Was gegen legOS spricht, sind die Gr�unde, die auch gegen eine Verwendung
von C auf einem Betriebssystem wie Linux sprechen. Es ist einfach schwieriger
in C fehlerfrei zu programmieren, als das z.B. mit Java der Fall ist. Wenn es
also auf eine extrem hohe Geschwindigkeit nicht ankommt und z.B. der Inter-
aktivit�at oder dem Software-Design ein gr�o�erer Stellenwert einger�aumt wird,
dann haben die anderen Systeme wie pbForth und leJOS die Nase vorn.
pbForth
Forth ist eine bereits recht alte Programmiersprache, die speziell daf�ur ent-
wickelt wurde, um Computer mit wenig Ressourcen zu bedienen (Baum et al.,
2000, S. 70�). Forth gibt es daher f�ur eine Vielzahl von kleinen Computersy-
stemen, doch auch f�ur z.B. Linux ist Forth (gForth) verf�ugbar.
Das besondere an pbForth (http://www.hempeldesigngroup.com/lego/
pbForth/homePage.html) gegen�uber allen anderen Betriebssystemen f�ur den
Brick ist seine Interaktivit�at. Wenn man den Brick mit pbForth programmiert,
wird ein PC nur als Terminal f�ur die Konsole von pbForth verwendet. Alles, was
man tippt, landet direkt im Brick und wird dort St�uck f�ur St�uck interpretiert.
Das er�o�net v�ollig neue M�oglichkeiten zum Testen, da man alles direkt "online"
ausprobieren kann und sofort eine R�uckmeldung bekommt.
In Forth verwendet man sogenannte words um Werte auf einem Stack zu
manipulieren. Diese Art zu programmieren ist im Vergleich zu "normalen" Pro-
grammiersprachen recht unkonventionell und erfordert daher in vielen F�allen
eine ganz andere Denkweise. Da Forth interaktiv ist kann man sehr bequem
ausprobieren. Dadurch macht das Lernen mehr Spass.
Man kann neue Funktionen in Form von weiteren words hinzuf�ugen. Mehrere
Threads sind nur mittels kooperativem Scheduling m�oglich und erfordern daher
eine sehr sorgf�altige Planung. Da pbForth eine Stack-Maschine ist, m�usste man
im Prinzip auch andere Sprachen f�ur pbForth �ubersetzen k�onnen.
3'NP' steht f�ur Networking Protocol, �uber die Bedeutung des 'L' gibt es hingegen je nach
Quelle unterschiedliche Au�assungen (Lego, legOS, layered, . . . ).
3.2. TEXTBASIERTE PROGRAMMIERSYSTEME 27
3.2.4 leJOS
Auf der ROM interface library librcx von Kekoa Proudfoot (Proudfoot, 1998)
beruht auch die Variante eines Betriebssystems f�ur den Brick die mit Java
programmiert werden kann: leJOS (http://lejos.sourceforge.net/).4
Es gibt noch mindestens ein weiteres Projekt um Java direkt auf dem Brick
ausf�uhrbar zu machen, doch ist es noch in einem sehr unvollst�andigen Stadium
(RCXJVM, http://misc.traveller.com/rcxjvm/). Es sieht zudem so aus,
als h�atte sich dort schon seit l�angerer Zeit nichts mehr getan.
Der Vorl�aufer von leJOS hei�t tinyVM und wird nicht mehr, bzw. nur noch
minimal weiterentwickelt. tinyVM enth�alt weniger Features, hat daf�ur aber auch
einen etwas kleineren footprint : 10KB, gegen�uber 16KB f�ur leJOS. Da der Brick
insgesamt nur �uber 32KB Speicher verf�ugt und das ROM (bzw. das Betriebs-
system) au�erdem eine gewisse Menge zus�atzlichen Speicher braucht, bleiben
f�ur ein Anwendungsprogramm unter leJOS nur etwa 10KB Speicher �ubrig. F�ur
das komplette hier vorgestellte Beispielprogramm f�ur Ariadne ist das bereits zu
knapp, doch dazu am Ende dieses Abschnitts mehr.
Inzwischen (Version 1.0.4.alpha) existiert in der Distribution von leJOS ein
neues package (josx.robotics) welches Klassen zur Navigation eines Roboters
und zur Programmierung nach dem Subsumption-Konzept (siehe 1.2 auf Sei-
te 4) enth�alt. Die Beispielprogramme f�ur Trusty, LiSe und Ariadne verwenden
diese nicht, da bereits eigene (wenn auch einfachere) Klassen f�ur diese Funktio-
nalit�at vorhanden waren. Nichtsdestotrotz zeigt es, da� die im Rahmen dieser
Arbeit verwendeten Beispiele durchaus typische Aufgaben und Probleme adres-
sieren.
Modularisierung & Wiederverwendung
Wenn man einmal von den geringen Ressourcen absieht, die einem unter le-
JOS zur Verf�ugung stehen, hat man (fast) alle M�oglichkeiten die einem die
Sprache Java bietet. Verst�andlicherweise ist aber nicht das komplette API von
Java implementiert, sondern nur die n�otigsten Klassen und auch dort nur die
wichtigsten Methoden.
Es ist aber kein Problem, mehrere Klassen zu erstellen. Alle notwendigen
Java-Features sind vorhanden, auch innere Klassen sind kein Problem (was
aber, angesichts der Tatsache, da� innere Klassen ein Compiler-Feature und kein
Runtime-Feature von Java sind, auch eher verwundern w�urde). Das Einzige was
einem imWeg steht ist der begrenzte Speicher und manche Limitierung des API
zum Zugri� auf die besondernen Eigenschaften des Brick, wie z.B. die Motoren
oder Sensoren (package josx.platform.rcx).
Als Basis f�ur mobile Roboter kann z.B. die folgende Klasse DriveTrain
verwendet werden:
4Und es ist wahrscheinlich das einzige Betriebssystem f�ur den Brick,
das man mit Fug und Recht als "weltraumtauglich" bezeichnen kann. Sie-
he Artikel in der Online-Ausgabe der S�uddeutschen Zeitung vom 14.12.2001:
http://www.sueddeutsche.de/computer/neuetechnik/hardware/32701
28 3. PROGRAMMIERSYSTEME F�UR ROBOTER
Listing 2: code/leJOS/DriveTrain.java
public class DriveTrain {// note that this is not a josx.platform.rcx.Motorprotected Motor leftMotor;protected Motor rightMotor;
public DriveTrain(Motor leftMotor, Motor rightMotor) {this.leftMotor = leftMotor;this.rightMotor = rightMotor;
}
public void forward() {leftMotor.forward();rightMotor.forward();
}
public void backward() {leftMotor.backward();rightMotor.backward();
}
public void leftSpin() {leftMotor.backward();rightMotor.forward();
}
public void rightSpin() {leftMotor.forward();rightMotor.backward();
}
public void stop() {leftMotor.stop();rightMotor.stop();
}}
In DriveTrain wird die Koordination von zwei Motoren in der Anord-
nung wie bei Trusty gekapselt. Sie bietet einfacher verwendbare und verst�and-
lichere Methoden wie forward(), um den Roboter vorw�arts zu bewegen, oder
leftSpin() um den Roboter linksherum um die eigene Achse zu drehen.
Doch bereits wenn man die Motoren andersherum gepolt anschlie�t, funk-
tioniert diese Klasse nicht mehr, denn forward() ist dann ja die andere Dreh-
richtung. Hat man bei der Polung aus irgendeinem Grund nicht die Wahl (ganz
profan: Das Kabel kann f�ur eine andere Polung manchmal zu kurz sein), m�ochte
man im Grunde DriveTrain daf�ur nicht editieren m�ussen. Es w�are besser die
Polung in Software zu kapseln, indem man einem Motor mitteilen kann, welche
Drehrichtung "forward" sein soll.
Die in leJOS verhandene Klasse zur Ansteuerung eines Motors (josx.plat-
form.rcx.Motor) bietet eine solche M�oglichkeit nicht. Eine �ubliche Vorgehens-
weise um das nachzur�usten w�are, eine Unterklasse von josx.platform.rcx.
3.2. TEXTBASIERTE PROGRAMMIERSYSTEME 29
Motor zu entwickeln. Sie besitzt eine neue Methode wie z.B. setReversed(boo-
lean) um die Polung anzugeben und leitet die Aufrufe wie forward() an die
jeweils passende backward()- oder forward()- Methode ihrer Unterklasse wei-
ter.
Da aber alle Methoden von josx.platform.rcx.Motor final deklariert
sind, ist dieser Weg verbaut. Den Grund f�ur die Wahl von final kann man
wohl folgendem Auszug aus den "performance-tips" von tinyVM entnehmen:
"Declare your methods to be either private, �nal or static when-
ever possible."
Man kann Instanzen von josx.platform.rcx.Motor auch gar nicht selbst
erzeugen, da der einzige Konstruktor der Klasse private deklariert ist und es
keine Klassenmethode gibt, die eine indirekte Konstruktion erm�oglichen w�urde.
Es existieren nur die drei Klassenvariablen A, B und C (jeweils vom Typ josx.
platform.rcx.Motor) { eine f�ur jeden Ausgang des Brick. Das Erstellen einer
Unterklasse ist daher nicht m�oglich, da diese auf wenigstens einen Konstruk-
tor zugreifen k�onnen m�usste. Die Klasse josx.platform.rcx.Motor h�atte also
ebensogut final deklariert werden k�onnen.
Aus diesem Grund wurde eine komplett neue Klasse geschrieben, die nicht
von josx.platform.rcx.Motor abstammt und daher auch nicht verwendet
werden kann, als w�are sie ein josx.platform.rcx.Motor { ein wichtiger Me-
chanismus der Objektorientierung ist damit ausgehebelt. Diese Klasse hat einen
josx.platform.rcx.Motor als Ziel, an den sie alle ben�otigten Methoden ex-
plizit weiterleitet und in Bezug auf die Drehrichtung die erw�ahnte Anpassung
vornimmt.
Leider sind diese Probleme auch bei der Sensor-Klasse, sowie der Klasse
Button, mit der die vier Kn�opfe des Brick abgefragt werden k�onnen, vorhanden.
Die meisten anderen Klassen aus dem package josx.platform.rcx, wie z.B.
zum Zugri� auf das eingebaute Display oder zur Ausgabe von T�onen, bieten
nur Klassenmethoden und sehen keine Instanz vor.
Das hei�t, Modularisierung ja, aber Wiederverwendung nur eingeschr�ankt,
da Rechenzeit und Speicher so knapp ist, da� daf�ur o�enbar nicht alle M�oglich-
keiten, die Java bietet, ausgesch�opft werden (k�onnen).
Threads & Synchronisierung
Die Klasse java.lang.Thread ist in leJOS vorhanden und bietet nicht nur die
M�oglichkeit neue Threads zu erzeugen, sondern man kann diesen auch verschie-
dene Priorit�aten zuweisen, sie unterbrechen und ein Thread kann auch �uber
yield() oder sleep() in der Ausf�uhrung zwischendurch anhalten. Es gibt so-
gar daemon-Threads, welche die virtuelle Maschine nicht am laufen halten und
somit daf�ur gedacht sind, "unbemerkt" im Hintergrund ablaufen zu k�onnen.
Verschiedene Threads werden in Java �uber einen Mechanismus synchroni-
siert, bei dem ein Thread einen sogenannten Monitor f�ur ein Objekt besitzen
kann. Diesen Monitor kann zu einem Zeitpunkt nur ein Thread besitzen, womit
die Synchronisierung bewerkstelligt wird. Den Monitor f�ur ein Objekt bekommt
ein Thread, w�ahrend er einen synchronized-Block ausf�uhrt, bzw. wenn er eine
30 3. PROGRAMMIERSYSTEME F�UR ROBOTER
Methode ausf�uhrt, die mit dem Schl�usselwort synchronized deklariert wur-
de. Das Objekt, auf das synchronisiert wird, ist im Falle der synchronized-
Methode das Objekt dessen synchronized-Methode gerade ausgef�uhrt wird,
bzw. das Klassenobjekt der Klasse die die Methode implementiert, wenn es sich
um eine Klassenmethode handelt. Im Falle des synchronized-Block wird das
Objekt, auf das synchronisiert werden soll, explizit angegeben. Da es in leJOS
keine Klassenobjekte gibt, wird das Synchronisieren auf ein solches, z.B. �uber
eine synchronized deklarierte Klassenmethode, nicht funktionieren.
Die Methoden wait() und notify() sind seit Version 1.0.2 vom 4.9.2001
implementiert, womit einer vollst�andigen Ausnutzung der Synchronisations-
m�oglichkeiten von Java nun nichts mehr im Wege steht. F�ur die Beispielim-
plementierung von Ariadne wurde aber noch keine explizite Synchronisierung
verwendet, so da� hier daf�ur kein Beispiel gezeigt wird, solche m�ussten sich aber
eigentlich in jedem Lehrbuch zu Java �nden lassen.
Um einen Algorithmus mit Subsumption (siehe 1.2 auf Seite 4) zu program-
mieren bieten sich wait() und notify() an. Wer daran Interesse hat, kann z.B.
die f�ur Subsumption im Beispiel verwendete Klasse SubsumptionScheduler
entsprechend umschreiben.
Events
F�ur die Kn�opfe und f�ur die Sensoren des Brick gibt es in leJOS die M�oglichkeit
�ahnlich wie beim AWT von Java sogenannte ButtonListener, bzw. Sensor-
Listener zu verwenden. Sobald eine �Anderung des Werts des jeweiligen Sensors
aufgetreten ist, bzw. der entsprechende Knopf gedr�uckt oder losgelassen wurde,
wird die in den Listener-Interfaces de�nierte Methode aufgerufen. Im Unter-
schied zum AWT gibt es aber keine eigene Klasse, die die zum Event geh�orenden
Daten repr�asentiert, sondern es wird nur die Quelle des Events, sowie bei den
SensorListenern der alte und der neue Wert �ubergeben.
Der Aufruf der Listener-Methoden erfolgt aus einem Thread heraus, wel-
cher automatisch erzeugt wird, wenn er wegen des Vorhandenseins von Listenern
ben�otigt wird. Da die Methoden wait() und notify() inzwischen implemen-
tiert sind, existieren optimale Voraussetzungen, um weitere Threads auf belie-
bige Ereignisse warten zu lassen, ohne da� sie unn�otig Rechenzeit verbrauchen.
Dies kann man z.B. verwenden, wenn man beim Auftreten eines bestimmten
Ereignisses eine l�angere Berechnung "ansto�en" m�ochte. Wie beim AWT auch,
sollten n�amlich die Listener-Methoden recht z�ugig zuende gehen, da f�ur meh-
rere Listener nur ein einziger Thread vorhanden ist.
Ein Design von LiSe mit mehreren Listenern (jede der Verhaltensweisen hat
einen "eigenen" f�ur die von ihr verwendeten Sensoren) st�o�t an eine Grenze. Die
Klassen Button und Sensor erlauben nur maximal vier Listener. Damit wird
aber eine Modularisierung erschwert, denn wenn ein anderes Modul auch Liste-
ner verwendet, so k�onnen es schnell mehr als vier werden, wenn man nicht genau
aufpasst, welches Modul f�ur welchen Sensor wieviele Listener registriert. Damit
werden die Grenzen zwischen den Modulen "verschmiert", da sie im Grunde un-
gewollt aufeinander Ein uss nehmen. Daher ist es bei Algorithmen in Bezug auf
die m�ogliche Wiederverwendung besser, weitestgehend auf die Verwendung von
3.2. TEXTBASIERTE PROGRAMMIERSYSTEME 31
Listenern zu verzichten, bzw. einen eigenen Event-Mechanismus zu implemen-
tieren (z.B. mittels der Klasse Poll, die so etwas wie den Unix-Systemaufruf
select f�ur Sensoren darstellt).
Ariadne
Der Algorithmus f�ur den Beispielroboter ist in Klassen f�ur Trusty, LiSe und
Ariadne aufgeteilt, sowie in drei weitere Klassen. Dies sind die bereits gezeigte
Klasse DriveTrain, zur Kapselung des Antriebsmechanismus von Trusty, die
bereits erw�ahnte Klasse Motor, die einen "umpolbaren" Motor repr�asentiert,
und eine Klasse zur Koordination der einzelnen Verhaltensweisen bei Subsump-
tion (siehe Abschnitt 1.2 auf Seite 4). Sowohl Trusty als auch LiSe sind nach
dem Subsumption-Konzept programmiert worden. Einzeln funktionieren LiSe
und Trusty auch sehr gut. Es ist auch sehr sch�on m�oglich, Trusty und LiSe f�ur
Ariadne wiederzuverwenden, doch leider ist die Implementierung von Ariadne
so nicht lau��ahig { leJOS geht schlicht der Speicher aus.
Man kann Ariadne aber trotzdem ausf�uhren. Kurz bevor es zu dem Out-
OfMemoryError kommt, h�alt die Ausf�uhrung an und gibt auf dem Display des
RCX das Resultat von System.freeMemory() aus. Nach einem Druck auf die
"Prgm"-Taste (nach einer kurzen Wartezeit) wird das Ergebnis von System.
totalMemory() ausgegeben. Weiteres Dr�ucken auf "Prgm" (mehrmals) f�uhrt
dazu, da� letztenendes die Ausf�uhrung bei dem OutOfMemoryError ankommt.
Dabei wird ganz rechts im Display des Brick die Zahl, die normalerweise die
Nummer des aktuellen Programms anzeigt, hochgez�ahlt. So kann man, wenn
man dabei den Code der Methode start() in Ariadne.java anschaut, ganz
gut erkennen wo der OutOfMemoryError auftritt:
Listing 3: code/leJOS/Ariadne.java
public void start() {// just show memory and ’step’ by pressing the PRGM-Button// because this method results in an OutOfMemoryError// on leJOS 1.0.4.alphaLCD.showProgramNumber(1);LCD.showNumber((int)System.getRuntime().freeMemory());try {
Button.PRGM.waitForPressAndRelease();} catch (InterruptedException ie) {}LCD.showProgramNumber(2);LCD.showNumber((int)System.getRuntime().totalMemory());try {
Button.PRGM.waitForPressAndRelease();} catch (InterruptedException ie) {}LCD.showProgramNumber(3);try {
lise.start();} catch(OutOfMemoryError e) {
LCD.showProgramNumber(4);➥
32 3. PROGRAMMIERSYSTEME F�UR ROBOTER
Listing 3: code/leJOS/Ariadne.java (Fortsetzung)
try {Button.PRGM.waitForPressAndRelease();
} catch (InterruptedException ie) {}throw e;
}try {
super.start();} catch(OutOfMemoryError e) {
LCD.showProgramNumber(5);try {
Button.PRGM.waitForPressAndRelease();} catch (InterruptedException ie) {}throw e;
}}
Die Beispiele wurden nicht auf Speicherverbrauch optimiert entwickelt. So
wird z.B. in der Implementierung von Ariadne Speicher "verschwendet", weil
Instanzenvariablen von Trusty neue Objekte zugewiesen bekommen. Die alten
Objekte k�onnten im Grunde aus dem Speicher verschwinden, da sie nicht mehr
gebraucht werden, doch in leJOS ist keine garbage collection implementiert.
Die fehlende garbage collection vereinfacht die Realisierung von leJOS, vor
allem in Bezug auf vorhersagbare Ausf�uhrungs- und damit auch Reaktionszei-
ten. Dies steht aber einer modularen, objektorientierten Entwicklung etwas im
Weg, denn man darf z.B. nicht f�ur jedes Ereignis ein neues Objekt erzeugen.
Der Algorithmus f�ur die garbage collection wird oft mit mark-sweep realisiert
und darf in diesem Fall, wenn die garbage collection erst einmal in Bewegung
gesetzt wurde, nicht mehr unterbrochen werden. Dieses Problem ist f�ur Java in
Bezug auf Echtzeitf�ahigkeit nicht ungew�ohnlich (Brich et al., 2000).
Auf den Abdruck des restlichen Codes wurde verzichtet. Dieser Abschnitt
ist ja auch nicht als Tutorial f�ur die Benutzung von leJOS gedacht. Der in-
teressierte Leser mag aber vielleicht in die beiliegenden Quellen schauen und
seine eigenen Versuche mit leJOS durchf�uhren. Ob es wohl m�oglich ist, einen in
eigenst�andige Trusty- und LiSe-Klassen aufgeteilten Algorithmus zu schreiben,
der f�ur Ariadne wiederverwendet werden kann, so da� trotzdem der Speicher
reicht?
3.3 Vorl�au�ges Fazit
Es ist bereits m�oglich Roboter objektorientiert zu programmieren. M�ogliche Sy-
steme f�ur Lego und Fischertechnik sind z.B. Spirit.ocx/Fishface (Windows/VB,
VC++), legOS (C++), leJOS (Java). Dies gilt aber nur f�ur die grunds�atzlichen
M�oglichkeiten, die die jeweiligen Sprachen bieten. In Bezug auf die Schnittstel-
le zum Roboter werden die M�oglichkeiten von Objektorientierung nicht ausge-
nutzt. Nur leJOS, legOS und das Java-package "ft" bieten echte Klassen f�ur
3.3. VORL�AUFIGES FAZIT 33
Sensoren und Motoren. Bei leJOS ist aber z.B. keine wirkliche �Unterst�utzung
f�ur Unterklassen davon vorhanden und legOS hat noch nicht einmal richtige
Motor-Klassen. Das ft-package wiederum hat zwar vern�unftige Klassen f�ur Sen-
soren und Motoren, bietet aber nur die allern�otigtste Funktionalit�at und keine
Unterscheidung zwischen verschiedenen Sensoren oder anderen an die Ausg�ange
angeschlossenen Aktuatoren.
Die Unterst�utzung eines Event-Mechanismus nimmt hingegen zu. Selbst das
Standard-System von Lego hat seit der neuesten Verion (RCX 2.0) eine Form
von Events integriert, so da� alle darauf beruhenden Systeme wie z.B. Mind-
Script und NQC die Verwendung von Events unterst�utzen.
Die Verwendung von Threads ist teilweise recht einfach umgesetzt (LL-
Win/leJOS) und wird von praktisch allen Systemen erlaubt. Leider existieren
aber meistens nur unzureichende Kontrollmechanismen (Synchronisierung). An-
dere Systeme als die Java-basierten bieten zwar auch Synchronisierung, sind
aber schwieriger zu benutzen (pbForth/legOS).
Wenn die jeweilige Sprache eine Modularisierung und Abstrahierung un-
terst�utzt, ist diese, solange es auf Performance oder Gr�o�e wirklich ankommt,
wenn �uberhaupt nur unsauber zu realisieren (siehe Performance-Tips zu ti-
nyVM: http://tinyVM.sourceforge.net/userguide.html). Gerade die gra-
�schen Systeme sind jedoch einfach nicht f�ur sehr umfangreiche Software-Pro-
jekte ausgelegt. Wird die Software lokal auf dem Brick ausgef�uhrt, wiegen Fak-
toren wie verf�ugbarer Platz und Rechenkapazit�at in vielen F�allen auch schwerer,
d.h. um m�oglichst weitgehende Modularisierung und Abstraktion zu erm�ogli-
chen, ist f�ur die Roboterhardware von Lego und Fischertechnik eine Steuerung
von einem externen Computer aus notwendig.
In Bezug auf die Hardware-Unabh�angigkeit der Systeme kommt es zum Teil
auf die Sichtweise an: iCon-L/LabView gibt es f�ur verschiedene Hardware und
viele Systeme isolieren die hardware-abh�angige Schnittstelle, wie z.B. leJOS,
das daf�ur ein separates package (josx.platform.rcx) enth�alt. Die eigentliche
Schnittstelle zu der Hardware ist aber nirgendwo abstrahiert. Es wird immer f�ur
ein bestimmtes Zielsystem programmiert, da die Verwendung von Motoren und
Sensoren immer unterschiedlich und sehr auf die konkrete Hardware bezogen
umgesetzt ist. Nat�urlich kann man einige der Systeme mit viel Eigenarbeit auch
benutzen um portabel zu programmieren, man wird dabei aber nicht gezielt
unterst�utzt und w�urde, bei den lokal auf dem Brick ausgef�uhrten Systemen,
auch schnell an Kapazit�atsgrenzen sto�en.
34 3. PROGRAMMIERSYSTEME F�UR ROBOTER
4
Die Idee des Abstract Robot
Toolkit
Wenn in dieser Arbeit von Robotern die Rede ist, so ist das in gewisser Weise
sehr allgemein gemeint. Zu dem Begri� Roboter wei� die Encyclop�dia Britan-
nica (www.britannica.com) Folgendes zu sagen:
"Any automatically operated machine that replaces human ef-
fort, though it may not resemble human beings in appearance or
perform functions in a humanlike manner. The term is derived from
the Czech word robota, meaning 'forced labour'. Modern use of the
term stems from the play R.U.R., written in 1920 by the Czech aut-
hor Karel Capek, which depicts society as having become dependent
on mechanical workers called robots that are capable of doing any
kind of mental or physical work."
Nach dieser De�nition kann bereits ein Computer als Roboter aufgefa�t
werden. Ein Roboter im Sinn dieser Arbeit besitzt Sensoren (wie z.B. einen
Licht- oder einen Ber�uhrungssensor), mit denen die Umwelt wahrgenommen
wird, und Aktuatoren (wie z.B. einen Motor oder eine Lampe), mit denen auf
die Umwelt Ein u� genommen werden kann. Die Aktuatoren werden von einem
Algorithmus gesteuert, der mehr oder weniger von den Messungen der Senso-
ren abh�angt. Diese Sichtweise auf das, was einen Roboter ausmacht, pa�t gut
zu Robotern wie man sie mit den Lego- und Fischertechnik-Bauk�asten bauen
kann. Die Bauk�asten enthalten Sensoren und Motoren und einen kleinen Com-
puter f�ur den Steuerungsalgorithmus, bzw. ein Hardware-Interface, �uber das ein
"richtiger" PC die Steuerung �ubernehmen kann.
Interessanterweise ist dieses Konzept der Sensoren und Aktuatoren im Grun-
de auch auf jede andere Maschine anwendbar, die genau genommen sogar nicht
einmal computergesteuert sein mu�. Diese Au�assung steht einem auch nicht im
Weg, wenn man sich von dem externen Hardware-Interface entfernt und wieder
den Computer an sich bereits als Roboter au�a�t. Im Grunde sind eine Tasta-
tur und eine Maus auch nichts weiter als Sensoren, genauso wie ein Bildschirm,
Drucker oder Lautsprecher im Prinzip als Aktuatoren aufgefa�t werden k�onnen.
Doch um die Arbeit nicht ausufern zu lassen, werden Sensoren und Aktuatoren
35
36 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
Actuator
setValue(...)
Algorithmkeeps state
in syncinforms about
state−change
output−state
may depend on
input−state
setting of
Input2
Output1
Output2
Input1
RobotInterface
Sensor
addListener(...)
getValue()
Classes (Software)Robot (Hardware)
Abbildung 4.1: Regelkreis, wie er durch die Modellierung des Roboters in Software ent-
steht.
vorerst haupts�achlich im engeren, "greifbareren" Sinne verwendet und es wird
nicht genauer untersucht, was diese Sichtweise f�ur die Programmierung eines
Computers an sich bedeuten kann.
Ziel der Arbeit ist es, ein System zur Roboter-Programmierung zu scha�en,
mit dem Software-Probleml�osungen f�ur Roboter m�oglichst weitgehend wieder-
verwendbar sind. So wie in dem Lego-Baukasten viele Bausteine enthalten sind,
die sich auf vielf�altige Weise immer wieder neu zusammenstecken lassen, sol-
len in dem Programmiersystem Software-Bausteine { als Objekte realisiert {
vorhanden sein, die sich ebenso zusammenstecken lassen. Au�erdem soll ein
einmal gebautes "Ding" mit einer bestimmten Funktion (z.B. ein Chassis f�ur
ein Fahrzeug) als Modul wiederverwendet werden k�onnen. In Hardware mu�
man das zwar meistens immer wieder neu bauen, da der Vorrat an Bausteinen
normalerweise beschr�ankt ist und man die Steine daher zwischendurch f�ur etwas
anderes gebraucht hat, bei Software ist es aber leicht (und billig) etwas einmal
"Gebautes" aufzuheben, f�ur den Fall, da� man es noch einmal brauchen kann.
Der Steuerungsalgorithmus wird sehr gut wiederverwendbar, wenn m�og-
lichst weit von der konkreten Hardware abstrahiert wird. Dies f�uhrt zwar prak-
tisch immer zu einem gr�o�eren Hardware-Aufwand, bzw. einer langsameren
Steuerung, entspricht aber einer Entwicklung, wie sie auch in anderen Berei-
chen der Softwareentwicklung { wie zum Beispiel bei Programmiersprachen {
statt�ndet, bzw. stattgefunden hat. Es ist einfach wesentlich produktiver, Soft-
ware in einer Umgebung wie Java zu entwickeln, als Maschinencode f�ur einen
bestimmten Prozessor von Hand zu schreiben. Ein gro�er Teil der Rechenkapa-
zit�at heutiger Computer wird in diesem Sinne einer schnelleren und beherrsch-
bareren Softwareentwickung "geopfert", wodurch aber komplexere Software erst
erm�oglicht wird.
4.1 Tutebot
Kommt es mehr auf Geschwindigkeit statt auf Flexibilit�at und Wiederverwend-
barkeit an, so k�onnte man auch ganz auf einen Computer verzichten. Ein Ro-
boter, dessen "Steuerungsalgorithmus" rein aus einer Schaltung von diskre-
ten Bauelementen besteht, wird in Jones et al. (1998) beschrieben. Die fertig
aufgebaute Schaltung, montiert auf einem Fischertechnik-Chassis, ist in Abbil-
4.1. TUTEBOT 37
dung 4.3 auf der n�achsten Seite dargestellt; Abbildung 4.2 zeigt das Schaltkreis-
Diagramm. Mit dieser Schaltung l�a�t sich ein Roboter wie Trusty steuern, der
mittels Tastsensoren registrierten Hindernissen ausweicht. Die Steuerung ist
aber nur f�ur einen einzelnen Sensor ausgelegt, so da� nicht zwischen einem
Hindernis auf der rechten oder der linken Seite unterschieden wird. Diese Ver-
haltensweise wird auch als wall-following bezeichnet.
Abbildung 4.2: Der Steuerungs-Algorithmus von Tutebot ist mit dieser diskreten Schal-tung realisiert. (entnommen aus Jones et al. (1998))
Solch eine Robotersteuerung durch eine diskrete Schaltung ist sozusagen
die primitivste Form eines Algorithmus. Sie reagiert sehr schnell, ist billig, ver-
braucht wenig Energie und nimmt kaum Platz ein. Daf�ur ist es aber nicht ein-
fach, sich solch eine Schaltung auszudenken (vor allem dann, wenn die Steue-
rungen komplizierter werden), und sie ist sehr un exibel, was Erweiterungen
oder �Anderungen angeht. Au�erdem steht man vor dem gleichen Problem, das
man bereits mit den Lego-, bzw. Fischertechnik-Bausteinen hat: Der Vorrat an
Bauteilen ist begrenzt und um neue Schaltungen zu bauen, mu� man entwe-
der die alten zerst�oren, um an ihre Bauteile zu kommen, oder man mu� st�andig
neue Bauteile nachkaufen. Hebt man also viele solche "Algorithmen" auf, um sie
sp�ater wieder zur Verf�ugung zu haben, geht das entweder nur als Bauplan oder
ist mit erheblichen Kosten verbunden, da man st�andig neue Bauteile kaufen
mu�.
Wenn also auch die Entwicklungszeit wichtig oder der Algorithmus sehr
38 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
Abbildung 4.3: Die Wei�e Platine ist der "Steuerungsalgorithmus" des Tutebot ausJones et al. (1998). Rechts oben im Bild sieht man die Sensoren, mit denen der Roboterz.B. Tischkanten registrieren kann.
komplex ist, es einfacher sein soll �Anderungen vorzunehmen oder einmal er-
stellte Algorithmen (f�ur eine eventuelle sp�atere Verwendung) aufgehoben wer-
den sollen, nimmt man einen "richtigen" Computer. In der Verbindung zu den
Sensoren und Aktuatoren wird auch dort wieder auf verschiedenen Abstrakti-
onsebenen agiert. So kann man direkt die elektrischen Signale (bzw. das Pro-
tokoll) programmieren, um die Interface-Hardware dazu zu veranlassen, zum
Beispiel einen Motor an- und auszuschalten. H�au�g gibt es auch eine Software-
Schnittstelle, die von den genauen elektrischen Signalen, bzw. dem Kommuni-
kationsprotokoll, abstrahiert. Diese sind dann aber immer noch sehr stark an
die konkrete Hardware angelehnt. So hat man zum Beispiel bei der Verwen-
dung des Active-X Control Spirit.ocx von Lego eine Funktion zur Steuerung
des Power-Level eines Motors zur Verf�ugung, welche es bei den Fischertechnik-
Treibern FishFace oder 30402Drive (die noch am ehesten als mit Spirit.ocx
vergleichbar angesehen werden k�onnen) nicht gibt, da die Interface-Hardware
von Fischertechnik eine Drosselung der Motoren nicht direkt vorsieht.
Mit dem Abstract Robot Toolkit soll noch weiter abstrahiert werden, um
z.B. zu einem Motor immer die gleiche Schnittstelle zur Verf�ugung stellen, egal
ob dieser nun von Lego, Fischertechnik oder irgendeiner anderen Hardware ge-
steuert wird. Das erfordert nat�urlich, sich dar�uber Gedanken zu machen, was
zum Beispiel einen Motor auszeichnet. Welche Funktionalit�at soll ein Motor z.B.
immer aufweisen und welche nur optional? Welche Basisfunktionalit�at ist not-
wendig um alles weitere darauf aufbauend "in Software" realisieren zu k�onnen?
Dies alles kostet nat�urlich Rechenzeit und Speicherplatz im Computer. Au-
�erdem wird der komplette Vorgang weniger transparent, denn es ist nicht mehr
4.2. FISCHERTECHNIK UND LEGO MINDSTORMS 39
so leicht, wirklich alles, jede Komponente, die an der Steuerung beteiligt ist,
zu verstehen. In gewisser Weise ist das eine �ahnliche Entwicklung wie bei Pro-
grammiersprachen: von Assembler �uber C bis Java.
4.2 Fischertechnik und Lego Mindstorms
Axel Schreiner entwickelte im Sommer 1998 eine Art "Java-Treiber" f�ur das
parallele Interface von Fischertechnik und stellte dabei fest, da� sich Konzepte
wie Events, Objekte und Threads fast schon auf nat�urliche Weise im Zusam-
menhang mit den Fischertechnik-Robotern verwenden lie�en.
Bereits kurze Zeit sp�ater kam Lego Mindstorms auf den Markt. Im Gegen-
satz zu dem ersten Computer-Interface von Fischertechnik, das bereits 1984 auf
den Markt kam, entwickelte sich sehr schnell eine gro�e Fan-Gemeinde aus vie-
len Teilen der Welt. Es entstanden recht schnell mehrere, von Lego unabh�angige
Programmiersysteme f�ur den Brick, von denen vor allem NQC und legOS in-
zwischen eine gro�e Bekanntheit und Verbreitung erreicht haben.
Viele Dinge, wie zum Beispiel die Verbindung der Steuerung eines Lego-
Roboters mit einem Programm auf einem normalen PC, kann man mit NQC
oder mit dem im RIS mitgelieferten RCX-Code nicht bewerkstelligen. Dies ist
zwar mit dem von Lego ver�o�entlichten Active-X Control Spirit.ocx m�oglich,
aber nur unter Windows, mit einer der von Microsoft zur Verf�ugung gestellten
Sprachen wie Visual Basic, Visual C++ usw. Auch legOS enth�alt inzwischen
eine integrierte Kommunikations-Schnittstelle, das LNP (siehe Abschnitt 3.2.3
auf Seite 25).
Im Internet war auch Java-Code zu �nden (Dario Laverde, "RCX Java API",
http://www.escape.com/~dario/java/rcx/) mit dem man den Brick steuern
konnte. Das ging zwar nur sehr rudiment�ar, weil man immer noch die Bytecodes
kennen mu�te, die der Brick versteht, aber im Gegensatz zu der Ansteuerung des
Fischertechnik-Interface war man nicht einmal auf plattformabh�angigen C-Code
angewiesen, da es bereits das damals noch relativ neue Java Communications
API verwendete. Die Bytecodes konnte man der Webseite von Kekoa Proudfoot
entnehmen (Proudfoot, 1998).
Mit all diesen Informationen war es schlie�lich m�oglich, eine Lego-Umsetz-
ung f�ur das im Wintersemester 1999/2000 in der Vorlesung "Ober �achen pro-
grammieren" verwendete Beispiel "Niki, der Roboter"1, zu entwickeln. Das ei-
gentliche Beispiel diente dazu, objektorientierte Design-Pattern und deren An-
wendung in der Ober �achenprogrammierung zu demonstrieren. Daher ist der
Roboter dort { rein virtuell { ein Objekt, welches gewisse Aktionen ausf�uhren
und unter anderem mit einer gra�schen Ober �ache dargestellt und gesteuert
werden kann.
Die Lego-Umsetzung wurde in dieses System als eine weitere View (sie-
he Abschnitt 4.7 auf Seite 56) f�ur das Roboter-Objekt integriert, welche die
Aktionen des virtuellen Roboters in der Wirklichkeit nachvollzieht. Dies gesch-
ah alles in Java, da das zu diesem Zeitpunkt bereits die in dieser Vorlesung
1Niki beruht seinerseits auf "Karel the Robot", einer Idee die urspr�unglich aus Pattis (1981)
stammt.
40 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
haupts�achlich verwendete Programmiersprache war.
Doch die Ansteuerung von Lego aus Java heraus war mit viel Arbeit ver-
bunden. Daraus entstand das Bed�urfnis einen vern�unftigen, universell wieder-
verwendbaren "Java-Treiber" auch f�ur Lego zu entwickeln. Da� dies nicht an
einem Nachmittag zu bewerkstelligen ist, war sp�atestens dann klar, als zudem
eine elegante Repr�asentierung der Elemente eines Roboters in Objekten erreicht
werden sollte. Es ging also gleich um eine ganze Sammlung von Objekten { eine
Klassenbibliothek, bzw. ein objektorientiertes API.
Das Fischertechnik-Interface ist aber viel zu verschieden von dem Brick, als
da� es einfach m�oglich gewesen w�are, die Klassen des ft-package f�ur Fischertech-
nik in dieser Form auch f�ur Lego zu verwenden. Von Java war man aber bereits
gewohnt, da� man Software nicht f�ur jede Plattform neu entwickeln mu�. Da-
her war klar, da� das API auf eine Weise realisiert werden sollte, die es erlaubt,
sowohl f�ur Lego als auch f�ur Fischertechnik die gleichen Klassen zu benutzen.
Das Ziel war nun die Scha�ung einer abstrahierten Schnittstelle zur Steue-
rung von Robotern. Dabei sollten die Vorteile objektorientierter Programmie-
rung ausgenutzt werden. Die Unterst�utzung f�ur eine ereignisgesteuerte Pro-
grammierung, wie sie z.B. auch das Event-Modell des AWT vorsieht, verstand
sich in diesem Zusammenhang beinahe von selbst. Da� daf�ur dann mehrere
Threads verwendet werden (m�ussen) ist klar, sofern eine Programmierung nicht
nur noch ereignisgesteuert m�oglich sein sollte. Grunds�atzlich ist die Idee bei
Ereignissen (Events) ja auch die, da� sie im Prinzip zu jeder Zeit, also auch
gleichzeitig und nicht nur nacheinander auftreten k�onnen. F�ur ein (scheinbar)
gleichzeitiges Auftreten und vor allem f�ur eine gleichzeitige, dabei aber vonein-
ander unabh�angige Behandlung von Events sind Threads daher unerl�a�lich.
Die Implementierung sollte f�ur Lego und f�ur Fischertechnik erfolgen, da dies
die vorhandene Hardware war. Dabei sollte aber darauf geachtet werden, da� die
entwickelten Abstraktionen, Modelle und Abh�angigkeiten eine Implementierung
f�ur andere Plattformen weitgehend o�en halten. Da die Hardware von Lego
und Fischertechnik relativ verschieden ist, ist deren Wahl (auch wenn sie mehr
pragmatische Gr�unde hatte) relativ gut, um die entwickelte Abstraktion auf
ihre Tauglichkeit zu testen.
4.3 Java
Die Implementierung des Abstract Robot Toolkit(ART) wurde in Java rea-
lisiert. Die entwickelten Abstraktionen von den Bestandteilen eines Roboters
sind aber im Prinzip von Java unabh�angig, sie sollten sich auch in anderen ob-
jektorientierten Sprachen umsetzen lassen. Java besitzt jedoch den Vorteil, da�
es �ahnliche Ziele verfolgt wie die, die hinter der Enwicklung ART stehen, denn
Java ist "einfach, objekt-orientiert, [. . . ] architektur-neutral, multi-threaded,
garbage collected, robust, [. . . ] erweiterbar und klar verstanden. Vor allem aber
macht Java Spa�!" (Entnommen aus Schreiner (2000a), dort nach van Ho�,
Shaio und Starbuck).
Einigen dieser Punkte wird man in Bezug auf Java nicht unbedingt in vollem
Umfang zustimmen. Die Vor- und Nachteile der Sprache Java sind auch nicht
4.4. DESIGN-PATTERN UND DAS AWT 41
Thema dieser Arbeit. Die Verwendung von Java bildet f�ur ART aber eine gute
Basis, denn bis auf die Abstraktion eines Roboters und dessen Schnittstelle (die
Roboter-Hardware-Unabh�angigkeit), ist weitgehend alles vorhanden was man
braucht.
Der Vorteil einer objektorientierten Sprache liegt in der leichten Zug�ang-
lichkeit in Bezug auf die De�nition und Verwendung von Software-Modulen.
Objektorientierung kann man so verstehen, da� reale Dinge in abgeschlossenen
Softwarest�ucken abgebildet werden, die dann miteinander agieren. So wird ganz
nat�urlich die Menge der Motoren auf eine entsprechende Klasse Motor abgebil-
det und ein einzelner realer Motor auf eine Instanz, ein Objekt dieser Klasse.
Objekte werden verwendet, indem man mit ihnen �uber sogenannte Nachrich-
ten kommuniziert. Durch die Verwendung von Objekten wird ein nichtlinearer
Programmablauf unterst�utzt { eine durch Ereignisse ausgel�oste Interaktion von
miteinander verbundenen Objekten.
Die Idee einer virtuellen Maschine, bzw. von interpretiertem Code und einer
Klassenbibliothek, die eine unabh�angige Schnittstelle zur Hardware bereitstellt,
�ndet sich keineswegs nur bei Java. Bereits Smalltalk-80 beruhte auf einem sol-
chen Konzept (Goldberg und Robson, 1983) und auch Klassen- bzw. Funkti-
onsbibliotheken zur plattform�ubergreifenden Programmierung gibt es mehrere.
Java ist aber einer der modernsten und weitgehendsten dieser Ans�atze und
�ndet vor allem immer breitere Unterst�utzung.
4.4 Design-Pattern und das AWT
Wie der Name schon andeutet, besteht zwischen dem im Rahmen dieser Arbeit
entwickelten Abstract Robot Toolkit (ART) und dem Abstract Window Tool-
kit (AWT) von Java eine gewisse Gemeinsamkeit. Das AWT ist eine Sammlung
von Klassen f�ur die Programmierung eines Graphical User Interface (GUI).
Es erm�oglicht, eigene Unterklassen zu entwickeln und komplexe GUI's wer-
den durch ein Zusammenspiel zwischen vielen einzelnen Objekten verschiede-
ner Klassen realisiert. Instanzen der Klassen werden miteinander verbunden,
gleichsam "zusammengesteckt". Jede Instanz �ubernimmt dabei einen gewissen
Teil in einem komplexen Zusammenspiel, welches im Falle des AWT am Ende
zum Beispiel ein Fenster ergibt { das GUI eben.
Solche Aufteilungen in verschiedene Klassen, die vor allem gemeinsam ihre
volle St�arke ausspielen k�onnen, bzw. dadurch wesentlich besser wiederverwend-
bar werden, sind ein Aspekt des Designs der Software. Aber sogar die Art
der Aufteilung selbst kann man "wiederverwenden" { man spricht dann auch
von einem Design-Pattern. Die klassische Auswahl und Erkl�arung von solchen
Design-Pattern steht in Gamma et al. (1995). Der Aspekt, da� ein Container
f�ur Component-Instanzen selbst eine solche Component-Instanz ist und daher
mit ihm mehrere Komponenten zu einer zusammengefa�t und auch als eine
einzige behandelt werden k�onnen, wird dort als das Composite-Pattern be-
schrieben. Das sogenannte Strategy-Pattern wird mit der Kapselung des Layout-
Algorithmus und der Layout-Daten in separaten Klassen umgesetzt. Dieses Pat-
tern wird auch manchmal als Delegate-Pattern bezeichnet, bzw. ist mit diesem
42 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
verwandt.
Auch in ART �nden sich solche bekannten Design-Pattern wieder, wobei
nicht gezielt versucht wurde, bestimmte Design-Pattern aus Gamma et al.
(1995) zu verwenden. Vielmehr soll der Verweis auf bestimmte Design-Pattern
dazu dienen einen leichteren Zugang zu ART zu erm�oglichen. So k�onnen Struk-
turen, die einem in der angegebenen Literatur behandelten Design-Pattern
�ahneln, leichter erkl�art werden, da f�ur sie bereits ein Name existiert. Ein Le-
ser, dem diese Begri�e nicht gel�au�g sind, kann in der angegebenen Literatur
nachschlagen. Dies erm�oglicht eine pr�agnantere Darstellung, da nur noch die
konkrete Auspr�agung des Patterns erkl�art werden mu� und die grunds�atzliche
Struktur sozusagen "mit einem Wort" klar ist.
4.5. DAS ABSTRACT ROBOT TOOLKIT 43
4.5 Das Abstract Robot Toolkit
Wie bereits erw�ahnt, diente das AWT in gewisser Weise als Vorbild bei der
Implementierung von ART. Das AWT bietet Klassen wie Button oder Window,
welche die entsprechenden GUI-Elemente unabh�angig von der jeweiligen Platt-
form repr�asentieren. Diese Klassen k�onnen leicht, z.B. durch subclassing, um
plattform�ubergreifende Funktionalit�at erweitert werden und Implementierung-
en f�ur weitere Plattformen k�onnen ohne �Anderung an ihnen hinzugef�ugt werden.
Dies ist nat�urlich auch f�ur ART eine w�unschenswerte Eigenschaft. So gibt
es z.B. Klassen f�ur einen BooleanSensor oder einen Motor, welche genau so wie
beim AWT durch die Implementierung von Unterklassen spezialisiert und erwei-
tert werden k�onnen. Dies geschieht unabh�angig von der konkreten Ansteuerung
eines Hardware-Interfaces und kann daher mit jeder Hardware wiederverwendet
werden, auch mit zuk�unftigen Implementierungen f�ur weitere Hardware.
Design-Pattern in der Architektur
Sowohl im AWT als auch in ART wird dies durch Einsatz des sogenannten
Bridge-Pattern erreicht. Dies ist nicht ungew�ohnlich f�ur die Implementierung
eines plattform�ubergreifenden API, da es genau die eben beschriebenen Vor-
teile er�o�net. Die Klassen wie Motor sind eine Art Proxy f�ur die eigentli-
che Hardware-abh�angige Implementierung eines Motor. Ein Motor besitzt dazu
einen Verweis auf ein Objekt, welches die eigentliche Funktionalit�at, n�amlich die
konkrete Hardware anzusteuern, �ubernimmt. Dieses Objekt wird im AWT und
daher auch in ART als der Peer bezeichnet, was sich auch im Klassennamen
MotorPeer widerspiegelt.
Um eine weitgehend exible Implementierung der Peers zu erm�oglichen, sind
deren Schnittstellen in Form von Java-Interfaces de�niert. Welche Implemen-
tierung nun als konkreter Peer verwendet wird, mu� aber irgendwo entschieden
werden. Im Falle des AWT gibt es das sogenannte Toolkit-Objekt, welches
als Factory f�ur alle Peers dient. Im AWT wird die Toolkit-Klasse ihrerseits
als Factory f�ur ein konkretes Toolkit-Objekt verwendet. Die Klassenmethode
getDefaultToolkit() wird dazu verwendet. Welche Unterklasse von Toolkit
diese Methode zur Erzeugung der Toolkit-Instanz verwendet, kann �uber eine
Property (awt.toolkit) gesteuert werden.
Das gew�unschte Toolkit
Im Allgemeinen wird man bei der Verwendung des AWT nicht selbst ange-
ben, welche Toolkit-Implementierung benutzt werden soll. Dies ist auch nicht
notwendig, da die ben�otigte Implementierung im Wesentlichen nur von dem
Betriebssystem, auf dem die Java-Maschine l�auft, abh�angig ist. Eine eventuelle
Verteilung der Applikation, indem zum Beispiel das Fenster einer Applikation
auf einem ganz anderen Rechner erscheint, wird nicht durch Verwendung unter-
schiedlicher Toolkits erreicht, sondern dadurch, da� das entsprechende Toolkit
dieses f�ur die Applikation unmerklich selbst realisiert. Ist sogar die gesamte
Applikation verteilt programmiert, z.B. �uber RMI, dann "lebt" trotzdem jedes
44 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
Input2
Output1
Output2
Input1
RobotInterface
MotorPeer
stop()
backward()
forward()
ActuatorPort
Motor
stop()
backward()
forward()"Wire"
Ethernet, ...)Bluetooth,
(RS232, I2C,
Robot(Hardware)
creates
& delivers
represents a specific
kind of interpretation
of the actuator−hardware
to the hardware−port
actuator−hardware connected
represents a specific type of
of the hardware−interface
represents a specific portrepresents a
hardware−interface
Abbildung 4.4: Das grunds�atzliche Zusammenspiel und die Bedeutung der Komponentendes ART. Ein Steuerungsalgorithmus verwendet in erster Linie bestimmte Interpreta-tionen der Elemente eines Roboters (in der Gra�k durch Motor dargestellt).
Objekt zu einem bestimmten Zeitpunkt auch nur auf einem Rechner, bzw. in
einer Java-Maschine, und daher werden im Allgemeinen auch nicht mehrere
Toolkit-Implementierungen ben�otigt.
Bei der Ansteuerung von Robotern verh�alt sich das etwas anders. Welche
Roboterhardware einem Programm zur Verf�ugung steht, h�angt nicht mit dem
Betriebssystem oder der jeweiligen Java-Implementierung zusammen. Dies kann
sich im Prinzip sogar w�ahrend eines Programmablaufs �andern.2 Nun k�onnte
man zwar �ahnlich wie bei Toolkit die konkrete Implementierung �uber eine Pro-
perty ausw�ahlbar machen, diese Property m�usste aber dann eigentlich immer
einen Wert enthalten, da es so etwas wie die Standardhardware zur Roboter-
steuerung nicht gibt und folglich auch keine Standardimplementierung gew�ahlt
werden kann.
Die in ART enthaltenen Implementierungen f�ur den Brick und das Intel-
ligent Interface f�ur Fischertechnik k�onnen bis zu einem gewissen Grad auto-
matisch heraus�nden, welche Interface-Hardware angeschlossen ist. Es ist aber
auch vorstellbar, da� man f�ur einen entsprechend aufw�andigen Roboter meh-
rere Interfaces verwenden m�ochte, vielleicht sogar von verschiedenen Typen,
so da� letzten Endes Hardware von z.B. Lego und Fischertechnik gleichzei-
tig verwendet wird. Aus diesem Grund ist die Wahl der konkreten Toolkit-
Implementierung, also der Factory f�ur die Peers von Sensoren und Motoren,
nicht so einfach automatisch zu bewerkstelligen wie beim AWT, wo man sich
damit nicht notwendigerweise auseinandersetzen mu�.
Die meisten Hardware-Interfaces werden au�erdem mehrere Anschl�usse f�ur
Sensoren oder Motoren haben und es mu� daher auch ausgew�ahlt werden, an
welchem dieser Anschl�usse z.B. der entsprechende Motor angeschlossen sein soll.
Den grundlegenden Aufbau illustriert Abbildung 4.4. Damit eine der Frontend-
Klassen zu ihrem Peer kommt sind drei Schritte notwendig:
1. Das Hardware-Interface mu� ausgew�ahlt werden. Ein Objekt der Klas-
2Bevor sich nun jemand falsche Ho�nungen macht: Ein mehr oder weniger automatischer
Wechsel der Roboterhardware zur Laufzeit wird, zumindest zum jetzigen Zeitpunkt, nicht von
ART unterst�utzt.
4.5. DAS ABSTRACT ROBOT TOOLKIT 45
se RobotInterfaceFactory hat mehrere Factory-Methoden mit denen
Instanzen der Klasse RobotInterface erzeugt werden k�onnen. Eine In-
stanz von RobotInterface repr�asentiert dann ein Hardware-Interface.3
Viele der Factory-Methoden von RobotInterfaceFactory erzeugen meh-
rere Interfaces, von denen man dann ausw�ahlt, welche man verwenden
m�ochte.
2. Ein RobotInterface besitzt mehrere Anschl�usse, welche durch Instanzen
der Klasse Port repr�asentiert werden. Diese Anschl�usse h�angen aber ih-
rerseits so eng mit der jeweiligen Instanz von RobotInterface zusammen,
da� man sie nicht selbst erzeugt, sondern auch RobotInterface wieder
eine Factory darstellt und zwar f�ur die Port-Objekte.
3. Instanzen von Motor, BooleanSensor usw. kommen �uber ein solches Port-
Objekt an ihren MotorPeer, bzw. BooleanSensorPeer, indem man sie
mit diesem Port verbindet. Erst dann ist die Verbindung komplett, so
da� z.B. die BooleanSensor-Instanz den Zustand eines realen Schalters
widerspiegelt. Somit hat das verwendete Port-Objekt als erstes Ein u�
darauf, welchen Peer z.B. der Motor bekommt.
Diese Aufteilung in mehrere Schritte, bis z.B. ein BooleanSensor auch wirk-
lich komplett benutzbar ist, erscheint eventuell unn�otig umst�andlich. Es gibt
aber einige Argumente, die diese Aufteilung unterst�utzen:
� Diese Aufteilung entspricht sehr gut der Realit�at, was eines der wichtig-
sten Argumente �uberhaupt ist. Dort w�ahlt man auch erst einmal einen
Sensor und eine Interface-Hardware aus und schliesst dann den Sensor an
einen der zur Verf�ugung stehenden Anschl�usse an.
� Um einen der Software-Sensoren wie BooleanSensor mit etwas zu ver-
binden reicht ein Port-Objekt, welches wesentlich weniger komplex als
ein RobotInterface ist. Wenn man also z.B. einen BooleanSensor ha-
ben m�ochte, der gar nicht mit einer realen Hardware in Verbindung steht,
sondern z.B. immer einen konstanten Wert liefert, so kann man das (f�ur
den BooleanSensor v�ollig unmerklich) alleine dadurch erreichen, da� man
einen eigenen Port daf�ur erstellt. Ein ganzes RobotInterface ist in die-
sem Fall nicht n�otig.
Im AWT bekommt ein Button seinen ButtonPeer, indem er einem Con-
tainer (der letzten Endes in einem Window "steckt") hinzugef�ugt wird und
dessen Toolkit �ubernimmt;4 in ART bekommt ein Motor seinen MotorPeer,
indem er mit einem Port verbunden wird. Ein Port-Objekt kann aber nicht
3Eine Implementierung von RobotInterface mu� nicht notwendigerweise mit einer realen
Hardware im Zusammenhang stehen, bzw. genau ein Hardware-Interface repr�asentieren (siehe
Kapitel 6 auf Seite 107).4Falls der Container selbst noch kein Toolkit haben sollte, bekommt der Button erst dann
ein solches, wenn auch der Container eines bekommt. Die Verbindung mit einem Peer, bzw.
die Wahl eines Toolkit, wird in der Regel durch Aufruf der Methode pack() in der Klasse
Frame veranlasst.
46 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
so direkt erzeugt werden wie zum Beispiel ein Frame, da (wie bereits erw�ahnt)
kein default-RobotInterface zur Verf�ugung steht. Das macht die Handhabung
zwar ein klein wenig aufw�andiger, daf�ur erh�alt man aber eine sichtbare Kontrolle
dar�uber, welcher Anschlu� an welcher Hardware, bzw. welche Implementierung
von RobotInterface, verwendet wird.
Jedem das Seine
Ein weiterer Unterschied zwischen dem AWT und ART besteht darin, da� das
Toolkit des AWT nur Peers von bestimmten festgelegten Klassen erzeugt. Im
wesentlichen gibt es im AWT in der Klasse Toolkit f�ur jedes GUI-Element wie
Button oder TextField eine entsprechende Methode createButton(Button),
bzw. createTextField(TextField). Diese Festlegung gibt es in ART so nicht.
Es gibt genau einen Peer, den jedes RobotInterface, bzw. ein Port-Objekt,
zur Verf�ugung stellen mu� und zwar eine Implementierung von SensorPeer. Es
w�urde gar keinen Sinn machen, wenn jedes RobotInterface jede m�ogliche Form
eines Sensors oder Actuators durch einen entsprechenden Peer zur Verf�ugung
stellt, da sich die Hardware oft viel zu sehr unterscheidet. Manche Sensoren
oder Actuatoren haben auf mancher Hardware einfach keine Unterst�utzung.
Auch in Java gibt es diesen Ansatz seit der Einf�uhrung von Swing. Swing ist
eine Implementierung von GUI-Elementen, die alle auf einem einzigen Peer
f�ur Component beruhen, einem sogenannten LightweightPeer. Dieser stellt im
Wesentlichen nur eine Fl�ache auf dem Bildschirm zur Verf�ugung und verarbeitet
Events des zugrundeliegenden Fenstersystems, so da� z.B. Tastatureingaben
verarbeitet werden k�onnen.
In ART werden beide Ans�atze miteinander kombiniert. Wie bereits erw�ahnt,
mu� eine Implementierung von RobotInterface nur einen SensorPeer bereit-
stellen.5 Dar�uber hinaus hat ein RobotInterface aber die M�oglichkeit, be-
liebige Spezialisierungen eines SensorPeer zur Verf�ugung zu stellen. Je nach-
dem von welcher Klasse das Objekt ist, f�ur das ein Peer ben�otigt wird, oder
auch je nachdem welche Peers bereits erzeugt wurden, k�onnen unterschiedli-
che Peers erzeugt (bzw. geliefert) werden. Es gibt in ART bereits De�nitionen
f�ur solche Spezialisierungen (z.B. MotorPeer oder LightSensorPeer) in Form
von weiteren Java-Interfaces, die das Interface SensorPeer erweitern. Zudem
geh�oren zu jedem dieser Java-Interfaces gewisse Klassen (wie z.B. Motor oder
LightSensor), die mindestens einen SensorPeer ben�otigen aber einen Peer,
der das zugeh�orige Interface implementiert hat, bevorzugen. Wird ein Port eines
bestimmten RobotInterface nun nach einem Peer f�ur ein bestimmtes Sensor-
Objekt gefragt (man beachte, da� auch Motoren als Sensoren f�ur ihren Zustand
aufgefasst werden), dann gilt die folgende Vereinbarung :
� Wenn ein Peer geliefert wird, dann ist es "der Beste" der f�ur diesen Port
zu diesem Zeitpunkt und f�ur dieses Sensor -Objekt geliefert werden kann.
Im schlechtesten Fall ist es ein einfacher SensorPeer.
5Genaugenommen nicht mal das, da ein RobotInterface theoretisch auch 0 Ports haben
kann { nur wird das kaum sinnvoll verwendet werden k�onnen.
4.5. DAS ABSTRACT ROBOT TOOLKIT 47
� Welcher von den momentan m�oglichen Peers der Beste ist, wird anhand
der Klasse des Sensor-Objektes entschieden.
Peers f�ur zuk�unftige Sensor-Klassen
Bisher sind in ART die in Abbildung 4.5 enthaltenen Peers de�niert. M�ochte
man bei einer selbst implementierten Sensor-Klasse einen bereits existierenden
Typ Peer bevorzugen, dann mu� die eigene Sensor-Klasse von einer der Sensor-
Klassen abstammen, die zusammen mit dem entsprechenden Peer de�niert wur-
den. Man kann sich zwar auch eigene Peer-Typen in Form von Java-Interfaces
ausdenken, mu� dann aber auch daf�ur sorgen, da� irgendeine Implementierung
von RobotInterface, bzw. Port, diesen Peer auch unterst�utzt (also eine Im-
plementierung davon kennt und zur�uckliefern kann) um etwas davon zu haben.
SensorPeer
StateSensorPeer
CountSensorPeer
AngleSensorPeer
ActuatorPeer
MotorPeer
StepperMotorPeer
ServoPeer
LightSensorPeer
BooleanSensorPeer
For hardware which is able to measure angles
For hardware which can control stepper motors
For hardware which can distinguish between true and false
For counting state−transitions
For hardware which is able to measure light
For discrete values
Base interface − must be implemented by every peer
Base Interface for values that can be set (output of hardware)
For hardware which can control motors
For hardware which can control servo motors
Abbildung 4.5: Die Klassenhierarchie der Peers f�ur die Frontend-Klassen. Die Abstam-mungsverh�altnisse sind die gleichen wie bei den Sensor-Implementierungen (siehe Ab-bildung 4.6 auf Seite 52).
Beispielsweise wird der in ART bereits vorhandene MotorPeer logischer-
weise von der Klasse Motor bevorzugt. Diese Bevorzugung ist aber nicht dy-
namisch, sondern an die Klassen gebunden. Hiermit ist nicht die dynamische
Bindung in einer objektorientierten Sprache gemeint, in der eine Nachricht erst
zur Laufzeit an eine bestimmte Methoden-Implementierung gebunden wird,
sondern der Umstand, da� die Zuordnung Klasse$Peer in die Implementie-
rungen von RobotInterface fest hineincodiert ist. Eine Implementierung von
RobotInterface mu� die Klassen, f�ur die sie einen besonderen Peer liefern
m�ochte, daher kennen. Im momentanen Entwicklungsstadium von ART ist es
48 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
nicht m�oglich, eine Implementierung von Sensor nach ihren bevorzugten Peer-
Klassen zu fragen, daher ist es bei einer eigenen Klasse, die z.B. einen MotorPeer
bevorzugen m�ochte eben noch notwendig, da� sie von Motor abstammt. Nur
wenn man sich auch mit einem einfachen SensorPeer zufrieden geben kann ist
es m�oglich, von einer beliebigen Klasse abzuleiten, denn Sensor ist ein Java-
Interface und nur dessen Adaptierung ist Vorraussetzung daf�ur, da� ein Objekt
als Sensor im Zusammenhang mit ART verwendet werden kann.
Interpretationen von Werten
Es ist also m�oglich, da� eine Implementierung der Ansteuerung eines Hard-
ware-Interface nur genau die speziellen Peers enth�alt, die f�ur diese Hardware
auch sinnvoll sind. So gibt es zum Beispiel weder f�ur die Ansteuerung von Fi-
schertechnik noch f�ur die Ansteuerung von Lego einen BooleanSensorPeer,
denn ein spezieller "Wahrheitssensor" existiert f�ur diese Hardware h�ochstwahr-
scheinlich nicht. W�are die Interface-Hardware, die man ansteuert, aber z.B. ein
L�ugendetektor, dann k�onnte ein BooleanSensorPeer durchaus Sinn machen.
Ein RobotInterface k�onnte auch spezielle Anschl�usse (Instanzen von Port)
anbieten, die einen Peer liefern der diese Interpretation in true oder false vor-
nimmt. Es ist zum Beispiel denkbar, einen solchen Port anzubieten, um �uber
einen BooleanSensor zu erfahren, ob die Verbindung zwischen der Implementie-
rung von RobotInterface und der eigentlichen Interface-Hardware noch steht
oder ob sie momentan zusammengebrochen ist. �Uberhaupt kann man feststel-
len, da� "Sensoren" f�ur "Werte" ein Konzept ist, das nicht notwendigerweise
mit Dingen aus der "greifbaren" Welt, wie z.B. einem Schalter, zu tun haben
mu�. Im Grunde k�onnen Sensoren auch rein virtuell sein und innerhalb der
Komponenten einer Software zum Datenaustausch dienen.
Grunds�atzlich unterscheiden sich Sensor-Objekte in zwei f�ur die Abstrak-
tion (und den damit verbundenen M�oglichkeiten zur Wiederverwendung) wich-
tigen Punkten: Erstens ist es wichtig, ob sie nur einen Wert, oder sogar einen
Wert mit einer bestimmten Interpretation repr�asentieren, und zweitens, ob die-
ser Wert einer bestimmten Skala entspricht, also bereits kalibriert ist. Eine
Interpretation liefern im Grunde alle Sensoren au�er dem allereinfachsten (in
ART der RawSensor).
Bei einem BooleanSensor ist die Existenz einer gewissen Interpretation am
o�ensichtlichsten, aber auch ein LightSensor steht daf�ur, da� sein Wert der
Intensit�at eines Lichts entspricht. Die Interpretation geht aber unterschiedlich
weit. So ist bei einem LightSensor z.B. noch nicht klar, welche Lichtquelle mit
ihm gemessen wird, oder bei einem StateSensor ist zwar klar, da� sein Wert
einen gewissen (in ART als diskret de�nierten) Zustand ausdr�uckt, doch die
Bedeutung des Zustands kennt er noch nicht. So m�u�te der Zustand eigentlich
noch eine Bedeutung bekommen, wie z.B. Rot, Gr�un, Blau (und je nach Anwen-
dung auch noch "unde�niert") f�ur einen Sensor der eine der drei Grundfarben
des RGB-Modells erkennen soll.
Dies ist deshalb wichtig, weil durch diese Unterscheidung ein Algorithmus
unabh�angig(er) von der konkreten Auspr�agung eines Sensors wird. Ben�otigt ein
Algorithmus einen Sensor f�ur bestimmte Farben, weil er z.B. je nach registrierter
4.5. DAS ABSTRACT ROBOT TOOLKIT 49
Grundfarbe eine von drei Aktionen implementiert, so sollte er auch auf einem
"PrimaryColorSensor" beruhen und nicht auf einem StateSensor oder gar ei-
nem LightSensor. So wird n�amlich das Problem, wie z.B. anhand des Werts
eines Lichtsensors entschieden werden kann, welcher Farbe der gemessene Wert
entspricht, nicht verschleppt. Wenn sich dann die Hardware �andert, also der
Lichtsensor auf einmal andere Werte liefert, weil er genauer wird oder weil man
mehrere Sensoren mit Farb�ltern verwendet usw., so braucht man nur die Zu-
ordnung zu den Farben neu zu implementieren, ohne an dem Algorithmus, der
aufgrund der Farben entscheidet, etwas zu ver�andern.
Nat�urlich ist es (gerade f�ur Zust�ande) nicht m�oglich, alle Interpretationen
abzudecken { es gibt unendlich viele. Man m�ochte bestimmt auch nicht st�andig
neue Sensor-Klassen implementieren, so da� man sich im Falle der Zust�ande
oft auch schon zufrieden geben wird, wenn man schlicht in der Dokumentation
des Algorithmus de�niert, wie die Zust�ande, also die "nackten" Zahlen, inter-
pretiert werden. Viel sinnvoller ist die Verwendung spezieller Sensor-Klassen,
wenn durch sie eine bestimmte physikalische Gr�o�e repr�asentiert wird, also z.B.
eine Lichtintensit�at, Temperatur, Strecke oder ein Winkel. Dort wird dann auch
die Skala wichtig (obwohl die Zustands-Interpretation im Grunde auch bereits
eine Art Skala ist).
Im Optimalfall, bei entsprechender Unterst�utzung durch die Hardware, lie-
fert der Sensor bereits richtig kalibrierte Werte. Dies ist zum Beispiel bei dem
Temperatursensor von Lego f�ur den Brick der Fall. Die Lichtsensoren von Lego
und Fischertechnik sind hingegen nicht kalibriert, so da� man dies dort entwe-
der "per Hand" vornehmen mu�, oder ganz darauf verzichtet. Grunds�atzlich
wird bei Sensoren f�ur physikalische Gr�o�en in ART davon ausgegegangen, da�
diese auch kalibriert sind, trotzdem sollte ein Algorithmus aber dokumentie-
ren, inwieweit er von einer Kalibrierung abh�angig ist, damit man einsch�atzen
kann, welche Hardware im Zusammenhang mit dem Algorithmus �uberhaupt
verwendbar ist.
Die Programmierung des Beispielroboters LiSe in Abschnitt 5.6 auf Seite 94
verwendet z.B. nur die Grenzen des messbaren Bereichs sowie die Genauigkeit
der Lichtsensoren. Sie kommt ansonsten allein mit dem Unterschied zwischen
zwei (gleich skalierten) Lichtsensoren aus. Die Genauigkeit ist eine Eigenschaft
die jeder Sensor besitzt und einen messbaren Bereich hat im Grunde jeder phy-
sikalische Sensor.6 Solche Werte, die die Eigenschaften des Sensors beschreiben,
bekommen die Sensor-Objekte in der Regel von ihrem Peer (wenn er den je-
weiligen Wert unterst�utzt). Die Werte k�onnen aber auch manipuliert werden,
um sie an die Erfordernisse eines bestimmten Algorithmus anzupassen. F�ur
den beschriebenen Algorithmus f�ur LiSe ist jedoch nur wichtig, da� die beiden
Lichtsensoren Werte auf der gleichen Skala liefern, also untereinander vergleich-
bar sind { welche Skala das ist, ist nicht relevant. Normalerweise sollte das bei
gleicher Hardware sowieso schon der Fall sein. Verwendet man aber z.B. f�ur
den einen Sensor Fischertechnik- und f�ur den anderen Lego-Hardware, so mu�
man daf�ur selbst sorgen. Welche M�oglichkeiten die in ART enthaltenen Sensor-
Klassen daf�ur unter anderem bieten wird im n�achsten Abschnitt beschrieben.
6Der in ART enthaltene AngleSensor kennt diese Eigenschaft bisher noch nicht.
50 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
4.6 Die Sensor-Implementierungen in ART
Die in ART existierenden Implementierungen des Java-Interface Sensor sind
darauf ausgelegt, eine exible Nutzung, im Zusammenhang mit den jeweils
m�oglichen Peer-Implementierungen, zu gestatten.
Grundfunktionalit�at SensorPeer
Da jeder Peer eine Implementierung von SensorPeer sein mu�, kann jeder
Sensor im Grunde mit jedem Peer wenigstens soviel anfangen, da� er an einen
Wert kommt. Ein Sensor hat die M�oglichkeit, mit dem Peer, den er bekom-
men hat, das zu simulieren, was er von seinem Wunsch-Peer erwartet h�atte.
Nicht jede Funktionalit�at kann simuliert werden. Wenn z.B. ein Motor nur einen
einfachen SensorPeer und nicht wenigstens einen ActuatorPeer bekommt,
dann kann der Aufruf der Methode on() bei einem solchen Motor-Objekt kei-
nen E�ekt auf die Interface-Hardware haben, denn SensorPeer bietet f�ur das
Setzen eines Werts keine Methode { die gibt es erst bei ActuatorPeer. Ein
ActuatorPeer hingegen reicht dem in ART enthaltenen Motor bereits, um sei-
ne volle Funktionalit�at zu besitzen.
Damit wird weitgehend ein Problem adressiert, da� bei Java unter ande-
rem zur bereits erw�ahnten Entwicklung von Swing gef�uhrt hat. Anfangs wurde
mit dem AWT nur solche Funktionalit�at angeboten, die auf jeder unterst�utzten
Plattform bereits vorhanden ist. Im Gegensatz dazu wird bei Swing (und auch
in ART) versucht, mit dem was jede Plattform bietet, die gr�o�tm�ogliche Funk-
tionalit�at zu erreichen. Statt der Schnittmenge nun also die Vereinigungsmenge.
Es ist klar, da� dieser Ansatz auch seine Grenzen hat. Er bietet aber den Vor-
teil, da� die Klassen des API bereits mit einer relativ geringen Peer-seitigen
Unterst�utzung auskommen. Zumindest in ART bleibt einem trotzdem noch der
Weg o�en, sich bei der Implementierung eines RobotInterface nicht nur auf
die Simulation in der jeweiligen Sensor-Klasse zu verlassen, sondern spezielle
Unterst�utzung, durch entsprechende Peer-Implementierungen, zu liefern.
In den meisten F�allen, in denen die Simulation erforderlich ist, wird es n�otig
sein, dem Sensor dann noch weitere Parameter zur Verf�ugung zu stellen, die
festlegen, wie diese Simulation aussehen soll. So wird z.B. der in ART enthaltene
BooleanSensor, wenn er einen Peer hat, der kein BooleanSensorPeer ist, den
Wert, den er von seinem Peer bekommt, selbst in true oder false umwandeln.
Daf�ur interpretiert er den Wert 0 als false und alle anderen Werte als true.
Dies ist aber nur sein Standard-Verhalten, wenn er f�ur die Interpretation keinen
Delegate hat.7
Basisklasse RawSensor
Alle in ART enthaltenen Implementierungen von Sensor stammen von der Klas-
se RawSensor ab. Die Klasse RawSensor ist bereits ein vollwertiger Sensor. Alle
7In Gamma et al. (1995) wird kein Delegate-Pattern erw�ahnt. Ein vergleichbares Pattern
hei�t dort Strategy. Der Name Delegate, bzw. Delegation wird aber z.B. in Schreiner (1999)
und Gar�nkel und Mahoney (1993) daf�ur verwendet.
4.6. DIE SENSOR-IMPLEMENTIERUNGEN IN ART 51
notwendigen Dinge, wie die Verbindung zum Port bzw. SensorPeer herzustel-
len, Events zu verarbeiten und an SensorListener weiterzuleiten sowie ein
paar einfache Kon�gurationsm�oglichkeiten, werden von einem RawSensor zur
Verf�ugung gestellt. Um denWert, der vom Peer (also von der Hardware) kommt,
auf eine spezielle Weise zu verarbeiten, bietet ein RawSensor im Wesentlichen
drei Ansatzpunkte, welche bestimmten Design-Pattern entsprechen:
Template-Method : Zu bestimmten festgelegten Zeitpunkten bei der Verar-
beitung eines Werts vom Peer, werden bestimmte Methoden aufgerufen, die den
Wert umwandeln k�onnen. Wenn z.B. ein Wert vom Peer ankommt, entweder
durch einen Event oder weil der Peer explizit nach seinem Wert gefragt wurde,
dann ruft RawSensor bei sich selbst die Methode convertToIncoming(double)
auf und wandelt damit den gerade erhaltenen Wert um. Das bietet einer Un-
terklasse die M�oglichkeit, diese Methode zu �uberschreiben und eine eigene Um-
wandlung zu implementieren. Gegebenenfalls kann die neue Methode der Un-
terklasse auch die Methode der Oberklasse aufrufen, um ihr auch noch eine
Chance f�ur eine eventuelle Umwandlung zu geben. Bei einem RawSensor ist
dies auch sehr sinnvoll - nicht weil sie eine eigene Umwandlung durchf�uhren
w�urde, sondern weil mit ihr der n�achste Punkt implementiert ist:
Strategy (Delegate): Ein RawSensor kann einen incomingConversionDe-
legate besitzen. Dieser Delegate mu� ein Objekt einer Klasse sein, die das
Java-Interface de.jaetzold.util.Conversion implementiert, welches im We-
sentlichen vorschreibt, da� eine Methode convert(double) existiert, die ihrer-
seits ein double zur�uckliefert. Diese Methode wird dann zum Umwandeln des
Wertes aufgerufen, vorausgesetzt, da� �uberhaupt ein Delegate angegeben wur-
de. Eine Conversion bietet au�erdem noch Methoden, um zu erfahren ob ihre
Umwandlung cacheable ist8 und ob man erwarten kann, da� sie nicht nur ganz-
zahlige Resultate liefert (deliversFloatingPoint). Dies sind Informationen,
die ein Sensor auch in Bezug auf sich selbst liefern m�ochte, und er ist daher
darauf angewiesen, dies auch �uber den Delegate zu wissen, da dieser den Wert
ja umwandelt. Au�erdem ist in RawSensor ein Mechanismus implementiert, der
vom Peer erhaltene Werte sowie eben deren Umwandlungen zwischenspeichert,
um bei h�au�gen Anfragen schneller antworten zu k�onnen. Diese Zwischenspei-
cherung wird aktiviert, wenn der Peer sowieso bei jeder �Anderung des Werts
einen Event verschickt, denn dann mu� er nicht noch extra gefragt werden, wel-
chen Wert er nun momentan hat { es ist einfach der letzte, f�ur den ein Event
angekommen ist.
Decorator : Man kann mehrere Sensor-Instanzen "hintereinanderschalten".
Die Methode getSensorPort() liefert einen SensorPort der, wenn man dort
einen weiteren Sensor anschliesst, diesem einen Peer liefert, der den Wert der
ersten Sensor-Instanz repr�asentiert. Dies ist der einzige Ansatzpunkt f�ur eine
spezielle Behandlung des Sensorwertes, der bereits im Java-Interface Sensor
8Damit ist gemeint, ob sie durch eine unver�anderliche Zuordnungstabelle repr�asentiert
werden kann und daher f�ur ein Argument in Zukunft immer das gleiche Resultat liefert.
52 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
de�niert ist. Dies hat den Grund, da� �uber diese Schnittstelle das Verhalten
des urspr�unglichen Objektes nicht wirklich beein u�t wird. Angenommen, ein
Sensor wird noch von anderen Objekten verwendet, von denen man vielleicht
nicht einmal etwas wei� (womit man im Grunde immer rechnen sollte), dann
kann es zu Problemen f�uhren, wenn man z.B. auf den Delegate des Sensors
Ein u� nimmt, da dann die Berechnung des Sensorwerts f�ur alle, die diesen
Sensor verwenden, beein u�t wird. Ein Beispiel hierf�ur und wieso das im Falle
des Brick noch weitere Vorteile bietet, steht in Abschnitt 5.5 auf Seite 86.
Unterklassen von RawSensor
Die in ART enthaltenen Sensor-Klassen sind wie gesagt alle als Unterklassen
von RawSensor realisiert. Sie nutzen das Template-Method-Pattern, um f�ur den
Fall, da� sie nicht "ihren" Peer bekommen haben, dessen Funktionalit�at �uber
eine Umwandlung des Wertes zu simulieren. Hat aber z.B. ein LightSensor
einen LightSensorPeer, dann braucht er nicht umzuwandeln, da der Peer be-
reits einen entsprechenden Licht-Wert liefert.
RawSensor
CountSensor
StateSensor
BooleanSensor
LightSensor RawActuator
Servo
Actuator
Sensor
AngleSensor
Motor
StepperMotor
implementing classes interfaces
Abbildung 4.6: Die Klassenhierarchie der Actuator- und Sensorklassen.
Das Strategy-Pattern hingegen dient mehr dazu eine Umwandlung zu ver-
wenden, die von einer bestimmten Sensor-Klasse unabh�angig ist und daher
mit vielen Sensoren verwendet werden kann { z.B. eine einfache Skalierung des
Wertes. Ein Delegate ist aber nicht verp ichtet, �uberhaupt eine Umwandlung
vorzunehmen, die in irgendeiner Beziehung zu dem eigentlichen Wert steht.
Es w�are denkbar, da� ein solcher Delegate z.B. als Resultat immer System.
currentTimeMillis() liefert und pl�otzlich hat man eine Art TimeSensor. F�ur
einen TimeSensor w�are es aber sinnvoller, eine eigene Unterklasse zu erstel-
len, um die Art des Sensorwertes deutlich zu machen. Der Delegate ist besser
daf�ur geeignet, die entsprechenden Skalierungen vorzunehmen, z.B. f�ur den Fall,
da� ein LightSensor keinen LightSensorPeer bekommt und man trotzdem
m�ochte, da� der Wert einer bestimmten Skala entspricht.
4.6. DIE SENSOR-IMPLEMENTIERUNGEN IN ART 53
Das Decorator-Pattern kann man auch f�ur eine Umwandlung des Werts nut-
zen. Dies ist vor allem dann wichtig, wenn man nicht ausschlie�en kann, durch
die Umwandlung mit dem Bed�urfnis anderer Objekte, die den Sensor verwen-
den, zu interferieren. Die M�oglichkeit, mehrere Sensoren quasi "hintereinander-
zuschalten", erlaubt einem au�erdem, darauf Ein u� zu nehmen, �uber welchen
Peer ein Sensor letztendlich an seinen Wert kommt. So kann man ganz bewu�t
zum Beispiel einen CountSensor verwenden, der an einen LightSensor ange-
schlossen ist und daher (wenn der LightSensor an einen entsprechenden Port
angeschlossen ist) seinen Wert im Grunde auch von einem LightSensorPeer
erh�alt. Zudem gibt es einen CombinedSensor, welcher die Werte zweier Sensor-
Instanzen �uber eine Implementierung des Java-Interface de.jaetzold.util.
BinaryConversionmiteinander verkn�upft. Die eine dieser Sensor-Instanzen ist
der CombinedSensor aber selbst, d.h. um beliebige Sensoren zu verwenden mu�
man auch den CombinedSensor an den SensorPort eines anderen Sensor an-
schlie�en. Die andere Sensor-Instanz wird dem CombinedSensor als Parameter
�ubergeben. Die Repr�asentation der Verkn�upfung zweier Objekte durch ein Ein-
zelnes, das sich im Prinzip genau so verh�alt wie die Objekte, deren Verkn�upfung
es darstellt, ist ein Beispiel f�ur das bereits angesprochene Composite-Pattern.
Die Peer-Interfaces
Die bisher in ART de�nierten Peer-Interfaces sind in Abbildung 4.5 auf Seite 47
dargestellt. Das Verhalten der Sensor-Klassen in ART bei unterschiedlichen
Peers kann man im Wesentlichen folgenderma�en zusammenfassen:
� Hat ein Sensor einen genau zu ihm passenden Peer, dann wird der Wert,
der vom Peer kommt, im Allgemeinen nicht ver�andert. Je nach Sensor
k�onnen noch weitere Parameter vom Peer �ubernommen werden (so z.B.
der AngleSensor eines Servo oder die Grenzen des Messbereichs bei ei-
nem LightSensor).
� Hat ein Sensor nicht einen Peer, der ihm alle gew�unschte Funktionalit�at
bietet, so �ubernimmt er den Wert so, wie er von der Oberklasse bereits
verarbeitet wurde, und nimmt dann noch eventuell geeignete �Anderun-
gen daran vor. Weitere Parameter haben entweder default-Werte oder
m�ussen in diesem Fall dem Sensor-Objekt �ubergeben werden. Zum Bei-
spiel braucht ein StateSensor in einem solchen Fall ein Objekt, welches
das Interface StateDecider implementiert hat. Den StateDecider ver-
wendet der StateSensor, um aus dem Wert seiner Oberklasse RawSensor
einen Wert zu erhalten, der einen gewissen Zustand repr�asentiert. Solch
ein StateDecider ist im Grunde eine Verkn�upfung von zwei Werten zu ei-
nem, welcher eine Verallgemeinerung einer Zustands�ubergangsmatrix dar-
stellt. Er bekommt den aktuellen Zustandswert sowie den Raw-Wert der
Oberklasse als Parameter und liefert den neuen Zustandswert als Resul-
tat. Ein StateSensor hat nat�urlich auch einen defaultStateDecider,
welcher aber nichts weiter macht, als den Raw-Wert unver�andert zur�uck-
zugeben.
54 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
� Hat ein Sensor nicht einen passenden Peer und ist die Umwandlung, die
er dann an dem Wert vornimmt, um auch ohne einen speziellen Peer klar-
zukommen, f�ur die gew�unschte Anwendung nicht geeignet, so wird es in
den meisten F�allen ausreichen und auch am einfachsten sein, dem Sensor
eine Conversion als conversionDelegate9 zu �ubergeben. Zumindest die
in ART enthaltenen Sensor-Klassen bieten diese M�oglichkeit, da sie alle
von RawSensor abstammen.
� Es kann auch vorkommen, da� ein Sensor zwar seinen bevorzugten Peer-
Typ bekommt, dessen Wert aber nicht richtig skaliert ist. Der Light-
SensorPeer { in den Implementierungen sowohl f�ur Lego als auch f�ur Fi-
schertechnik { ist solch ein Fall. Die Hardware besitzt zwar Lichtsensoren
und mu� im Falle von Lego den Eingang sogar entsprechend kon�gurie-
ren, diese Sensoren liefern aber keinen Wert der auf eine geeignete Skala
kalibriert ist. F�ur Lichtsensoren w�are wahrscheinlich Lux eine gute phy-
sikalische Einheit, doch konnte keine Quelle gefunden werden, welche die
Werte der jeweiligen Lichtsensoren zu einer solchen Skala in Beziehung
setzt. Man kann wohl auch davon ausgehen, da� die Fertigungstoleranz
und die Abh�angigkeit von der Umgebung (Temperatur, Stromversorgung,
. . . ) bei diesen Bauelementen eher hoch ist, da sie andernfalls sehr viel
teurer w�aren. Aus diesem Grund liefert der LightSensorPeer bei Lego
und Fischertechnik keinen kalibrierten Wert. Ein solcher ist aber trotz-
dem vorhanden um wenigstens die Grenzen des Messbereichs angeben zu
k�onnen. Damit sind zumindest Informationen wie "kein Licht", "Licht"
oder "sehr viel Licht" portabel, also von der Hardware unabh�angig ver-
arbeitbar. Wird doch eine Kalibrierung z.B. auf Lux-Werte gew�unscht,
kann man sich auch hier mit einer entsprechenden Conversion als Dele-
gate helfen.
Ein Port ohne RobotInterface
Da jede der in ART enthaltenen Implementierungen von Sensor auch ohne
ihren bevorzugten Peer auf eine bestimmte Weise versucht trotzdem ihre ge-
samte Funktionalit�at, f�ur die sie gedacht ist, zur Verf�ugung zu stellen, ist
es leicht, diese auch mit einfachen Datenquellen zu verwenden. So stellt die
Klasse ConstantSensorPort zum Beispiel einen Port dar, der einen einfachen
SensorPeer liefert, der immer einen konstanten Wert hat. Mit diesem Port
kann man dann einfach Lichtsensoren, boolesche Sensoren usw. erzeugen, die
ihren Wert nicht ver�andern.
Besondere Beachtung verdient an dieser Stelle auch noch einmal das De-
corator-Pattern. In den Sensor-Klassen ist das dadurch umgesetzt, da� ein
Sensor immer auch einen SensorPort liefern kann, an den weitere Sensoren
angeschlossen werden k�onnen. Diese weiteren Sensoren haben dann einen Peer,
der als Wert immer denWert des Sensors liefert an dessen Port sie angeschlossen
9Genau genommen hat ein RawSensor zwei solche Delegates, einen f�ur die Konvertierung
des Peer-Werts in einen "internen" Wert und einen f�ur die Konvertierung des internen Werts
in den Wert, den er nach au�en hin repr�asentiert (der z.B. von getValue() geliefert wird).
4.6. DIE SENSOR-IMPLEMENTIERUNGEN IN ART 55
sind. Da auch die Events weitergeleitet werden, macht es in der Benutzung
am Ende keinen Unterschied, ob ein Sensor-Objekt direkt an den Port eines
RobotInterface angeschlossen ist oder ob da noch weitere Sensor-Objekte
gewisserma�en "zwischengeschaltet" sind. Eine Anwendung dieser Technik w�are
zum Beispiel ein BooleanSensor, der immer dann den Wert true annimmt,
wenn der CountSensor, an den er angeschlossen ist, einen bestimmten Wert
erreicht.
F�ur den Brick, der seine Anschl�usse f�ur die Sensoren unterschiedlich kon-
�gurieren kann (z.B. ob der angeschlossene Sensor mit Strom versorgt wird
oder nicht), ist die Klasse des an einen Port angeschlossenen Sensor-Objektes
ausschlaggebend f�ur die Kon�guration. Schlie�t man nun einen CountSensor
direkt an einen Port der Implementierung von RobotInterface f�ur den Brick
an, so wird der Anschlu� so kon�guriert, wie das im Normalfall f�ur den Ro-
tationssensor von Lego vorgesehen ist. Dies ist nicht immer w�unschenswert,
denn manchmal m�ochte man vielleicht zus�atzlich noch einen Taster anschlie-
�en oder mit dem Rotationssensor etwas anderes machen, als die Rotation zu
messen, wof�ur man dann nicht m�ochte, da� der Brick den Wert des Rotations-
sensors schon vorverabeitet. In diesem Fall kann man direkt an den Port des
RobotInterface einen Sensor anschlie�en, welcher der gew�unschten Kon�gu-
ration entspricht (z.B. einen einfachen RawSensor). An diesen Sensor schlie�t
man wiederum den CountSensor an, den man in diesem Fall nat�urlich "per
Hand" f�ur das Z�ahlen kon�gurieren mu�. Ein Beispiel daf�ur enth�alt der Ab-
schnitt 5.5.2.
56 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
4.7 Model, View, Controller
Smalltalk-80 (Goldberg und Robson, 1983) enth�alt das ber�uhmte Model-View-
Controller (MVC) Paradigma f�ur die Programmierung von Ober �achen. Eine
Applikation besteht demnach aus drei wesentlichen Bestandteilen:
Ein Model enth�alt die Funktionen und Daten, welche den Kern der An-
wendung ausmachen.
Der Model-Zustand wird auf der Ober �ache von einer View dargestellt.
Der Controller ist schlie�lich f�ur die Interpretation von (meist durch Be-
nutzereingaben ausgel�osten) Events zust�andig und folglich daf�ur verant-
wortlich, wie bestimmte Events von dem Model aufgenommen werden.
ViewModel
Controller
retrieves state
notifies
registers
registers
sends event
manipulates
in response to
events
Abbildung 4.7: Die Beziehungen innerhalb einer Architektur nach Model-View-
Controller.
In Smalltalk-80 war es notwendig, da� Model, View und Controller von
speziellen Klassen abstammen, da auf diese Weise Applikationen besonders ein-
fach in das System integriert werden konnten. Das eigentliche Design-Pattern
dahinter hat sich inzwischen als sehr vorteilhaft erwiesen { auch unabh�angig
von speziellen daf�ur vorgesehenen Basisklassen und Smalltalk. Es de�niert eine
Trennung von Zustandsaufbewahrung (Model) und Zustandsdarstellung (View).
Entsprechend wird durch den Controller die Zuordnung von Operationen auf
dem Zustand (Model) zu den Ausl�osern (Events von der View) exibel.
Das Standardbeispiel f�ur die Demonstration von MVC de�niert zu einem
Model mehrere Views. Diese Views k�onnen v�ollig unabh�angig voneinander ver-
wendet werden, da jede View nichts anderes macht, als den Zustand des Model
darzustellen. Unter Umst�anden bietet sie au�erdem Ansatzpunkte f�ur Benut-
zereingaben, die an den Controller weitergeleitet und von diesem interpretiert
werden. Die Views von dem gleichen Model erscheinen aus Benutzersicht so,
als w�aren sie direkt untereinander verbunden, da eine �Anderung an dem Model
von allen seinen Views wiedergespiegelt wird.
4.8. SUBSUMPTION-ARCHITEKTUR 57
Obwohl MVC urspr�unglich nur f�ur die Trennung von User-Interface und
Algorithmus entwickelt wurde, ist diese Aufteilung auch in anderen Bereichen
anwendbar. So ist in ART z.B. ein Motor im Grunde eine View f�ur den Zustand
eines ActuatorPeer. Ein Servo ist eine andere View, die an den gleichen Peer
angeschlossen sein kann. Beide Objekte repr�asentieren den gleichen Zustand
und sind, �uber den gemeinsamen ActuatorPeer, aneinander gekoppelt.
Nun kann es aber sinnvoll sein, eine Instanz von Motor wiederum selbst als
ein Model anzusehen, nicht nur weil er (wie ein typisches Model) Methoden
besitzt, um den Zustand des Motors zu manipulieren. Wenn man z.B. in einer
gra�schen Ober �ache, zur Diagnose des Roboters, den Zustand seiner Motoren
darstellen m�ochte, dann ist es vern�unftig (in diesem Kontext) das Motor-Objekt
als Model aufzufassen, dessen Zustand von einer View auf der Ober �ache dar-
gestellt wird.
Meiner Meinung nach mu� die Zuordnung eines Objekts zu einer der drei
Kategorien immer im Verh�altnis zu den anderen Objekten gesehen werden.
Nicht immer ist ein Objekt eindeutig und ausschlie�lich nur ein Model, eine
View oder ein Controller. In den Beziehungen der Objekte untereinander mu�
diese Zuordnung aber eindeutig sein, um den Vorteil von MVC, n�amlich die Un-
abh�angigkeit und Austauschbarkeit der einzelnen Bestandteile, weiter aufrecht
zu erhalten.
4.8 Subsumption-Architektur
Wie bereits in Abschnitt 1.2 auf Seite 4 erw�ahnt ist Subsumption ein von Rod-
ney A. Brooks (Brooks, 1985) erdachtes Konzept zur Steuerung von Robotern.
Aus der Anlehnung an Re exe resultiert, da� die Reaktionen schnell und un-
bewu�t erfolgen sollen. Angewandt auf Roboter bedeutet dieses Konzept, da�
keine langwierigen Berechnungen durchgef�uhrt werden oder ein umfassendes
Weltmodell entworfen wird. Stattdessen werden, f�ur eine einfache Verhaltens-
weise, schnell zu erkennende Zust�ande einer gewissen Teilmenge der Inputs mit
eindeutigen Zust�anden einer gewissen Teilmenge der Outputs verkn�upft. Da die
einzelnen Verhaltensweisen zu verschiedenen Zust�anden f�uhren k�onnen, ist eine
Vorrangregelung notwendig. Durch die Aufteilung in mehrere Verhaltenswei-
sen wird eine gewisse Unabh�angigkeit von Teilbereichen erreicht. Es ist einfach,
neue "Re exe" hinzuzuf�ugen, und f�allt ein Modul aus, so k�onnen die davon
unabh�angigen Teile einfach weiterarbeiten. Die Bedingungen, die zur Aktivie-
rung der Verhaltensweisen f�uhren sowie deren Priorit�aten, sind sogar �uber die
Sofware lernbar (Maes und Brooks, 1990).
Ein gewisses Problem entsteht durch die Forderung nach einem eindeutigen,
schnell zu realisierenden Zustand der Ausg�ange. Teilt man Trusty z.B. in Ver-
haltensweisen auf, so da� eine Verhaltensweise f�ur das Ausweichen zust�andig
ist, so st�o�t man auf genau dieses Problem. Das Ausweichen ist ein Vorgang,
der Zeit ben�otigt und mit einer ganzen Abfolge von Zust�anden der Ausg�ange
realisiert wird.
Prinzipiell ergeben sich hier zwei unterschiedliche L�osungsans�atze:
1. Eine Verhaltensweise wird unterbrechbar. Sie kann dann solange brauchen
58 4. DIE IDEE DES ABSTRACT ROBOT TOOLKIT
wie sie will, denn wenn sie nicht mehr aktiv sein soll, weil die Bedingungen
sich ge�andert haben, dann wird sie eben unterbrochen. Dies ist, zumindest
in Java, nicht so ohne weiteres realisierbar, da ein Thread nicht einfach
von einem anderen unterbrochen werden darf. Hier mu� man beachten,
da� zwar eine eventuell geeignete Methode (suspend()) existiert, diese
aber seit Version 2 der Java Platform als deprecated gilt, da deren Ver-
wendung leicht zu deadlocks f�uhren kann. Weiterhin sollte ein Thread
mitbekommen k�onnen, wenn er unterbrochen wurde, damit er wei�, da�
der Zustand der Ausg�ange eventuell zwischendurch von au�en ver�andert
wurde.
2. Die Aufteilung in eine Verhaltensweise f�ur das Ausweichen ist nicht ad�a-
quat. F�ur jede Zustands�anderung bei der Durchf�uhrung von Ausweichen
mu� eine eigene Verhaltensweise implementiert werden. So k�onnte zum
Beispiel eine Verhaltensweise a, die Trusty r�uckw�arts fahren l�a�t, durch
das Ausl�osen des Bumpers aktiviert werden und nach dem Ausl�osen f�ur
einen gewissen Zeitraum aktiv sein. Eine weitere Verhaltensweise b k�onnte
als Input f�ur die Entscheidung �uber ihre Aktivierung genau den Aktivie-
rungszustand von a besitzen. Sobald a "lange genug" aktiv war oder von
aktiv auf inaktiv wechselt, wird b aktiv und dreht Trusty ein wenig { dies
nat�urlich wieder nach dem gleichen Prinzip, da� b nicht andere Verhal-
tensweisen w�ahrenddessen blockiert.
F�ur den ersten beschriebenen L�osungsansatz w�are es notwendig, da� eine
Verhaltensweise, die l�anger braucht, h�au�g genug selbst eine M�oglichkeit zur
Unterbrechung von au�en vorsieht. Ob dies dann im Zusammenhang mit Sub-
sumption �uberhaupt noch eine sinnvolle Architektur ist, mu� sich erst zeigen.
Der 2. L�osungsansatz geht zwar konform mit dem Subsumption-Konzept,
ist aber auch nur mit etwas Aufwand zu implementieren. Hier k�onnte eventuell
ein geeigenetes API die Idee, da� manche Verhaltensweisen in einer gewissen
zeitlichen Abfolge geschehen sollen, unterst�utzen.
5
Ariadne & ART - Ein Tutorial
Dieses Kapitel ist in erster Linie ein Tutorial f�ur die Benutzung des Abstract
Robot Toolkit (ART). Es bietet sich aber an, dies mit der Ausarbeitung der
Vor- und Nachteile der Benutzung dieses Systems im Gegensatz zu den Pro-
grammiersystemen aus Kapitel 3 auf Seite 13 zu verbinden.
Anhand einiger kleinerer Beispiele wird zuerst die grunds�atzliche Bedeutung
der Frontend-Klassen erkl�art. Dazu geh�oren alle Klassen, die dazu gedacht sind,
da� ein Algorithmus sie zur Steuerung eines Roboters verwendet, sowie die Klas-
sen, mit denen man eine Verbindung zu der eigentlichen Hardwareansteuerung,
dem Backend, herstellt.
Auf dieser �Ubersicht und den einfachen Beispielen bauen die folgenden Ab-
schnitte auf, in denen es um die Programmierung des Beispielroboters Ariadne
mit seinen Modulen Trusty und LiSe geht. Manch einem Leser wird die Anzahl
der dabei erzeugten Klassen vielleicht unn�otig gro� vorkommen. Sp�atestens bei
der Verbindung von Trusty und LiSe zu Ariadne sollte aber der Vorteil der
verschiedenen Abstraktionsebenen sichtbar werden.
Die vollst�andigen Quelltexte f�ur die Beispiele sind auf der CD zu dieser
Arbeit enthalten und im Internet verf�ugbar. Siehe dazu Abschnitt A.1 auf Sei-
te 136 im Anhang zu dieser Arbeit.
Zum Verst�andnis dieses Kapitels sind Kenntnisse in objektorientierter Pro-
grammierung und der Programmiersprache Java notwendig. Zum Lernen von
Java und zum Nachschlagen kann man z.B. Flanagan (1999), Sun Microsystems
(2001) und Schreiner (2000a) verwenden.
Weiterhin ist es f�ur das Verst�andnis einiger der komplexeren Beispiele hilf-
reich, zu wissen was Subsumption ist, da dieses Programmier-Konzept f�ur Robo-
ter mehrmals zur Anwendung kommt. Eine kurze Einf�uhrung in dieses Konzept
steht in Abschnitt 1.2 auf Seite 4.
5.1 "Hello, Robot!"
Das Standardbeispiel bei der Einf�uhrung in eine neue Programmiersprache ist
"Hello, World!" (Kernighan und Ritchie, 1990, S. 5). Nun geht es hier zwar nicht
um eine neue Sprache, aber doch immerhin um eine neue Klassenbibliothek zur
Steuerung von Robotern. Ein m�oglichst einfaches Programm, das einen Motor
59
60 5. ARIADNE & ART - EIN TUTORIAL
einschaltet, k�onnte folgenderma�en aussehen:
Listing 4: code/art/examples/HelloRobot.java
package de.jaetzold.art.examples;
import de.jaetzold.art.RobotInterfaceFactory;import de.jaetzold.art.RobotInterface;import de.jaetzold.art.ActuatorPort;import de.jaetzold.art.Motor;
public class HelloRobot {public static void main(String[] argv) {
RobotInterfaceFactory factory = new RobotInterfaceFactory();RobotInterface hardware = factory.getInterface();ActuatorPort port = hardware.getActuatorPorts(0);
Motor motor = new Motor();motor.connectWith(port);
motor.on();
try {Thread.sleep(1000);
} catch(InterruptedException ie) {}
}}
Zum Kompilieren und Ausf�uhren m�ussen nat�urlich alle ben�otigten Klassen
�uber den Klassenpfad erreichbar sein. Falls also eine Fehlermeldung kommt,
da� irgendein Typ nicht de�niert ist oder eine Klasse nicht gefunden wurde,
empfehle ich Abschnitt A auf Seite 135 im Anhang.
Funktioniert alles erwartungsgem�a�, l�auft ein (an dem ersten Ausgang des
Interfaces angeschlossener) Motor kurz an und es erscheint in etwa die folgende
Ausgabe auf der Konsole:
Successfully initialized Fischertechnik-Interface, found onserialport COM2
Erscheint die Ausgabe nicht oder l�auft der Motor nicht an, dann hilft viel-
leicht ein Blick in Anhang B auf Seite 139. Eine eventuelle NullPointer-
Exception weist darauf hin, da� kein Interface gefunden wurde. Eine Array-
IndexOutOfBoundsException hat wahrscheinlich die Ursache, da� das gefunde-
ne Interface keine ActuatorPorts bereitstellt, von denen man aber wenigstens
einen ben�otigt, um den Motor daran anzuschlie�en.
Die NullPointerException entsteht, weil getInterface() einen null-
Verweis liefert, wenn keine Interface-Hardware gefunden werden konnte. Beson-
dere Fehlersituationen werden in Java eigentlich �uber Exceptions signalisiert
und es ist kein guter Stil, wenn dies �uber das Resultat geschieht. In diesem kon-
kreten Fall kann man aber eine Ausnahme machen, da einerseits ausgeschlos-
5.1. "HELLO, ROBOT!" 61
sen werden kann, da� f�ur diese Methode null als "regul�ares" Resultat jemals
wirklich gebraucht wird und andererseits das Fehlen einer Interface-Hardware
ja nicht unbedingt als eine Fehlersituation angesehen werden mu�. Insbe-
sondere gibt es noch andere Methoden, wie getInterfaces(), die ein ganzes
Array mit den gefundenen RobotInterface zur�uckliefert. Wird keins gefun-
den, ist es ein Array der L�ange 0. Eine Exception w�are f�ur diese Methode
nicht sinnvoll und entsteht daher auch nicht in den anderen Methoden von
RobotInterfaceFactory die ein RobotInterface als Resultat haben.
Nun zur Beschreibung des Listing 4 auf der vorherigen Seite:
Als erstes wird ein Objekt der Klasse RobotInterfaceFactory aus dem packa-
ge de.jaetzold.art1 erzeugt. Damit f�angt immer alles an bei der Program-
mierung mit ART, denn Roboter haben immer ein Hardware-Interface, das
der Computer ansteuert, und dessen Repr�asentierung, in Form eines Robot-
Interface, bekommt man durch den Aufruf getInterface() von der Robot-
InterfaceFactory.
Eines der Anliegen von ART ist es, von der konkreten Interface-Hardware
zu abstrahieren. Aus diesem Grund wird das Objekt, welches diese Hardware
repr�asentieren soll, nicht vom Programmierer selbst erzeugt. Der Programmie-
rer soll am besten gar nicht wissen, welche Klasse welches Interface an welchem
Anschluss ansteuert.
Objekte der Klasse2 RobotInterfacewerden also nach dem Factory-Muster
erzeugt. Zur n�aheren Erl�auterung dieses Design Patterns siehe Gamma et al.
(1995) oder auch Schreiner (1999).
Will man aus irgendeinem Grund nur einen ganz bestimmten Typ Interface
ansteuern oder legt besonderen Wert auf die Anschl�usse, an denen nach Inter-
faces geschaut wird, kann man das durch einen Parameter zu getInterface()
beein ussen, doch dazu kommen wir erst sp�ater, auf Seite 68 in Abschnitt 5.2.
Jetzt geht's erst einmal weiter mit HelloRobot.
Der Verweis hardware ist also das Objekt, welches das Harware-Interface
repr�asentiert. Um einen Motor zu steuern, mu� dieser aber an die Hardware
angeschlossen werden, genauer, an einen Port des RobotInterface. Genau-
genommen sogar an einen ActuatorPort, denn diese repr�asentieren einzelne
Ausg�ange der Interface-Hardware.
Alle ActuatorPorts eines RobotInterface werden in einem Array gespei-
chert und man kann nach JavaBeans-Manier darauf zugreifen (sogenannte in-
dexed property, siehe Sun Microsystems (1997)). Wenn das erste gefundene In-
terface also �uberhaupt Ausg�ange hat, dann hat das Array der ActuatorPorts
mindestens einen Eintrag, und den ersten davon bekommt man durch die Nach-
richt getActuatorPorts(0) an hardware.
Nun kann ein Objekt der Klasse Motor mit dem port verbunden werden.
Daf�ur schickt man dem motor die Nachricht connectWith mit dem port als
Parameter. Der motor wird hier zwar erst kurz vorher erzeugt, das ist aber
nicht notwendig. Man h�atte den motor auch als allererstes erzeugen k�onnen,
1Im folgenden Text werden, wenn es sich aus dem Kontext bereits erschlie�en l�a�t, keine
vollquali�zierten Klassennamen mehr angegeben.2Genaugenommen ist RobotInterface gar keine Klasse sondern ein Java-interface, an
dieser Stelle kann man aber so tun, als w�are es eine Klasse.
62 5. ARIADNE & ART - EIN TUTORIAL
noch bevor die Factory erzeugt wird, doch wenn dann gar kein Anschlu� f�ur
den Motor zur Verf�ugung gestanden h�atte, w�are er ja umsonst erzeugt worden.
Sobald ein Motor mit einem ActuatorPort eines RobotInterface erfolg-
reich verbunden wurde, kann er auch schon benutzt werden, um den Zustand
eines realen, mit der Interface-Hardware verbundenen Motors zu manipulieren.
Da die Aufgabe von HelloRobotwar, einen Motor anzuschalten, ist das auch
schon der n�achste Aufruf: motor.on(). Der Rest des Codes dient nur dazu, da�
das Programm nicht sofort wieder zu Ende geht, denn dann w�urde der Motor
unter Umst�anden, sofort nachdem er angeschaltet wurde, wieder ausgehen, weil
die Interfacehardware keine Signale mehr vom Computer erh�alt.
Implementierungen von RobotInterface erzeugen zwar in den meisten F�al-
len eigene Threads zur Kommunikation mit der Interfacehardware, diese sollten
aber Daemon-Threads sein, die die virtuelle Maschine nicht von sich aus am
Laufen halten.
5.2 DriveTrain
Wer sich im vorherigen Abschnitt gefragt hat, wie das denn funktionieren soll
{ einen Roboter zu steuern, ohne etwas �uber ihn zu wissen { ist hier richtig. Es
wird nun auch darum gehen, wie man einen Steuerungsalgorithmus f�ur mehrere
Roboter wiederverwenden kann.
In diesem Abschnitt wird ein Roboter programmiert, der fahren kann. Eine
typische Bauweise f�ur einen einfachen fahrenden Roboter ist, zwei gegen�uberlie-
gende R�ader oder Ketten unabh�angig voneinander durch jeweils einen eigenen
Motor anzutreiben. Auf diese Weise kann der Roboter vorw�arts und r�uckw�arts
fahren, sich auf der Stelle links- und rechtsherum drehen sowie { durch verschie-
den schnelles Drehen der R�ader { richtige Kurven fahren. Die Fortbewegung von
Kettenfahrzeugen, wie z.B. Baggern, funktioniert �ahnlich.
Abbildung 5.1: Zwei einfache fahrende Roboter aus den Bauanleitungen des Lego RIS
1.5 und Fischertechnik Mobile Robots.
5.2. DRIVETRAIN 63
Abbildung 5.1 auf der vorherigen Seite zeigt eine solche Konstruktion mit
LEGO sowie mit Fischertechnik. Bauanleitungen f�ur �ahnliche Roboter �ndet
man auch in Knudsen (1999) und Baum (2000).
Das Java-Interface DriveTrain
Letzten Endes ist es doch so, da� man einen Roboter haben m�ochte, der sich
fortbewegen kann { am besten in beliebige Richtungen (vorw�arts, r�uckw�arts,
seitw�arts, hoch, runter, usw.). Au�erdem soll er noch in der Lage sein, seine
Orientierung zu ver�andern (sich drehen). Damit die Bewegung sch�on aussieht,
w�are es au�erdem w�unschenswert, wenn die Fortbewegung nicht nur auf Ge-
raden sondern auch auf Kurven verlaufen k�onnte. Dabei w�are es sinnvoll, dem
Roboter nicht nur sagen zu k�onnen, wie weit er sich bewegen soll, sondern auch
wie schnell.
Das sind alles zusammen ziemlich gro�e Anspr�uche an den Roboter und,
da� sich ein Roboter beliebig im Raum bewegen und orientieren kann, erfor-
dert einen gro�en Konstruktionsaufwand, um den es an dieser Stelle nicht geht.
Um also realistisch zu bleiben, werden die Anforderungen an den Roboter fol-
genderma�en festgelegt:
Der Roboter soll vorw�arts fahren,
r�uckw�arts fahren,
sich auf der Stelle linksherum drehen,
sich auf der Stelle rechtsherum drehen
und anhalten (!)
k�onnen. Au�erdem wird die Angabe einer Strecke und/oder Geschwindigkeit
(zumindest vorerst) der Einfachheit halber au�er Acht gelassen.
Die eben de�nierte Funktionalit�at erfordert einen Algorithmus, der sie im-
plementiert. Au�erdem mu� ein Roboter (wie z.B. in Abbildung 5.1 auf der
vorherigen Seite) her, der diese Funktionen hardware-technisch erm�oglicht. Die
Umsetzung von einem Befehl wie "Vorw�arts" auf Befehle an die Komponenten
des Roboters, wie z.B. die Motoren, h�angt davon ab, wie der Roboter gebaut
wurde. Daher ist es sinnvoll die Anforderungen von der konkreten Umsetzung zu
trennen. In Java macht man dies �uber die De�nition eines sogenannten Interface,
welches dann von verschiedenen Klassen adaptiert (und damit implementiert)
werden kann:3
Listing 5: code/art/examples/DriveTrain.java
package de.jaetzold.art.examples;
public interface DriveTrain {public void forward();
➥3Eine andere M�oglichkeit w�are die De�nition einer Basisklasse.
64 5. ARIADNE & ART - EIN TUTORIAL
Listing 5: code/art/examples/DriveTrain.java (Fortsetzung)
public void backward();public void leftSpin();public void rightSpin();public void stop();public void resume();
<see Listing 11 on page 73>}
Anhand des Namens der Methoden ist leicht erkennbar, welche der eben de�-
nierten Anforderungen mit ihr umgesetzt werden soll. Die neu hinzugekommene
Methode resume()mu� noch de�niert werden. Aus Gr�unden der �Ubersichtlich-
keit wurde auf die Angabe von JavaDoc-Kommentaren verzichtet.4
Die ersten vier Methoden forward() bis rightSpin() sollen alle daf�ur sor-
gen, da� nicht nur die Bewegungsrichtung festgelegt wird sondern der Roboter
sich dann auch wirklich in Bewegung setzt (falls er vorher still gestanden hat).
Bis auf forward() ist eine Implementierung von DriveTrain aber nicht
verp ichtet, diesen Zustand auch wirklich umzusetzen. Es w�are ja zum Bei-
spiel ein Roboter vorstellbar, der nur vorw�arts und r�uckw�arts fahren, sich aber
nicht drehen kann. Knudsen's Minerva (Knudsen, 1999, S. 82�) z.B. kann nur
vorw�arts fahren und sich in eine Richtung drehen. Aus diesem Grund soll hier
darauf hingewiesen werden, da� die Methoden backward(), leftSpin() und
rightSpin() eine Exception ausl�osen k�onnen. Da keine throws-Klausel an-
gegeben wurde, darf das nur eine RuntimeException sein. Eine java.util.
UnsupportedOperationException bietet sich in diesem Fall an. Ein Algorith-
mus, der DriveTrain benutzt, f�angt diese sinnvollerweise auch nur dann ab,
wenn er auf die fehlende Funktionalit�at selber reagieren m�ochte, indem er es
z.B. mit einer Linksdrehung versucht, falls eine Rechtsdrehung nicht unterst�utzt
wird.
Die Methode stop() h�alt die Bewegung des Roboters an. Die Methode
resume() dient dazu, nach einem oder mehreren aufeinanderfolgenden stop(),
wieder zu dem Zustand vor STOP zur�uckzukehren. Zu Beginn startet Drive-
TrainSimpleAlgorithm im STOP-Zustand und es gibt keinen anderen Zustand
in den resume() zur�uckkehren k�onnte. In diesem Fall bewirkt resume() daher
nichts.
Der Code von DriveTrain wurde in Listing 5 nicht ganz vollst�andig an-
gegeben. Die ausgelassenen Zeilen de�nieren die Zust�ande in denen sich ein
DriveTrain be�nden kann, und eine Methode um selbigen abzufragen. Dies
kann verwendet werden um z.B. eine gra�sche View f�ur DriveTrain-Objekte
zu implementieren (siehe Abschnitt 5.3 auf Seite 73).
Implementierung von DriveTrain
Ein Interface wie DriveTrain beschreibt nur, wie ein Objekt einer Klasse, die
dieses Interface adaptiert, benutzt werden kann. F�ur einen Roboter wie aus
4Die Klassen aus dem package de.jaetzold.art.examples, in denen keine JavaDoc-
Kommentare enthalten sind, werden in diesem Tutorial beschrieben.
5.2. DRIVETRAIN 65
Abbildung 5.1 auf Seite 62 soll nun eine einfache Implementierung vorgestellt
werden.
Das entscheidende Kriterium f�ur einen solchen Roboter ist, da� er zwei Mo-
toren hat, einen linken und einen rechten, und da� diese Motoren so eingebaut
und angeschlossen sind, da� folgende Bedingungen erf�ullt sind:
� Sind beide Motoren an und auf "forward" gestellt, f�ahrt der Roboter
vorw�arts.
� Sind beide Motoren an und auf "backward" gestellt, f�ahrt der Roboter
r�uckw�arts.
� Sind beide Motoren an und drehen sich in verschiedene Richtungen, dann
dreht sich auch der Roboter. Er dreht sich linksherum, wenn der linke
Motor auf "backward" gestellt ist. Er dreht sich rechtsherum, wenn es
der rechte Motor ist, der r�uckw�arts l�auft.
Daf�ur, da� die Motoren diese Anforderungen erf�ullen, soll nicht die Drive-
Train-Implementierung zust�andig sein. Vielmehr sollen in dieser Implemen-
tierung erst einmal die Anforderungen des DriveTrain-Interfaces auf solche
Motoren umgesetzt werden:
Listing 6: code/art/examples/DriveTrainSimpleAlgorithm.java (Ref. in Listing 7 S. 66)
protected Motor left;protected Motor right;
protected int state;
public void stop() {right.off();left.off();
setState(state | STOP);}
public void resume() {if(state != STOP) {
right.on();left.on();
setState(state & ˜ STOP);}
}
public void forward() {right.forward();left.forward();
setState(FORWARD);resume();
}➥
66 5. ARIADNE & ART - EIN TUTORIAL
Listing 6: code/art/examples/DriveTrainSimpleAlgorithm.java (Fortsetzung)
...public void leftSpin() {
right.forward();left.backward();
setState(LEFT SPIN);resume();
}...
Die Programmierung entspricht ziemlich genau der Funktionalit�at, die von
den Motoren verlangt wird. Die Verweise auf die Motoren sind mit left und
right bezeichnet und besitzen Methoden f�ur on(), off(), forward() und
backward(), deren Bedeutung weitgehend selbsterkl�arend ist. Eine genaue Be-
schreibung �ndet sich in der Klassendokumentation zu Motor (Siehe Anhang A.2
auf Seite 137).
Nachdem die Motoren z.B. in der Methode leftSpin() auf entgegengesetzte
Drehrichtung gesetzt wurden, wird der Zustand des DriveTrain durch den
Aufruf von setState(int) neu gesetzt. Auf diese Methode wird in Abschnitt
5.3 eingegangen.
Wichtig ist aber, da� nach dem Setzen der Zust�ande5 der Motoren noch
resume() aufgerufen wird. Die Zust�ande an/aus, sowie vorw�arts/r�uckw�arts ei-
nes Motor-Objekts sind voneinander unabh�angig und ein einfacher Aufruf von
forward() f�uhrt nicht dazu, da� sich der entsprechende Motor einschaltet, son-
dern nur dazu, da� er sich vorw�arts dreht, wenn er bereits an ist bzw. sobald
er angeschaltet wird.
Die Methode resume() schaltet die Motoren an, sofern sich DriveTrain
nicht in einem reinen STOP-Zustand be�ndet. Ein eventuell trotzdem vorhan-
denes STOP-Bit in der Variable state, welche den aktuellen Zustand speichert,
wird gel�oscht. Entsprechend werden in stop() die Motoren ausgeschaltet und
das STOP-Bit gesetzt.
Die Stellen mit ". . . " markieren Auslassungen der Methoden backward()
und rightSpin(), weil diese analog zu den abgedruckten Methoden imple-
mentiert sind, sowie Stellen, die f�ur ein Verst�andnis der Implementierung nicht
unbedingt notwendig sind. Der interessierte Leser wird ermuntert, selbst einen
Blick in die Quellen zu werfen.
Es fehlt noch der Teil des Codes, in dem das DriveTrainSimpleAlgorithm-
Objekt initialisiert wird, der Konstruktor :
Listing 7: code/art/examples/DriveTrainSimpleAlgorithm.java
package de.jaetzold.art.examples;
import de.jaetzold.art.Motor;...
➥
5Die Erkl�arung der dabei verwendeten Konstanten wie RIGHT SPIN und STOP be�ndet sich
bei Listing 11 auf Seite 73.
5.2. DRIVETRAIN 67
Listing 7: code/art/examples/DriveTrainSimpleAlgorithm.java (Fortsetzung)
public class DriveTrainSimpleAlgorithm implements DriveTrain {public DriveTrainSimpleAlgorithm(Robot robot) {
this.left = robot.getLeftMotor();this.right = robot.getRightMotor();setState(STOP);stop();
}<see Listing 8 on page 67><see Listing 6 on page 65><see Listing 13 on page 75>...
}
An dieser Stelle kommen wir der eigentlichen Roboterhardware n�aher. Der
Konstruktor mu� irgendwie die beiden Motor-Verweise left und right initia-
lisieren. Dazu wird ein Robot-Objekt �ubergeben, das man nach den Motoren
fragen kann.
Damit DriveTrainSimpleAlgorithm korrekt funktioniert, m�ussen die Mo-
toren den Anforderungen gen�ugen, die auf Seite 65 festegelegt wurden. Welcher
Motor an welchem Anschlu� der Hardware angeschlossen ist, sowie die Polung
der Motoren, ist f�ur den Algorithmus DriveTrainSimpleAlgorithm jedoch gar
nicht interessant. Es reicht v�ollig aus, wenn der Algorithmus bereits richtig
angeschlossene und kon�gurierte Motoren bekommt und wei�, welcher rechts
und welcher links ist. Daher de�niert der Algorithmus ein Interface Robot.
Dieses Interface beschreibt damit sozusagen Roboter, die von dem Algorithmus
gesteuert werden k�onnen.
Listing 8: code/art/examples/DriveTrainSimpleAlgorithm.java (Ref. in Listing 7 S. 66)
public static interface Robot {public Motor getLeftMotor();public Motor getRightMotor();
}
Ein Interface legt eigentlich nur die Signaturen, sowie die Resultattypen von
Methoden fest. Trotzdem gibt es f�ur jede De�nition eine Vereinbarung dar�uber,
was von ihr erwartet wird. Diese Vereinbarung sollte, z.B. durch einen JavaDoc-
Kommentar, explizit gemacht werden, damit klar ist, wof�ur das Interface genau
steht. Nur durch Angabe einer Signatur und des Resultattyps ist zwar klar, wie
eine Methode benutzt werden kann, aber nicht wof�ur. Um ein m�oglichst gleich-
artiges und vorhersagbares Verhalten von verschiedenen Implementierungen ei-
nes Interfaces zu gew�ahrleisten ist es unerl�a�lich, dieses Verhalten zusammen
mit dem Interface zu de�nieren. F�ur DriveTrainSimpleAlgorithm.Robothei�t
das, da� die Methoden Motor-Objekte als Resultat liefern sollten, die den An-
forderungen gen�ugen von Seite 65 gen�ugen.
Eine solche Robot-Implementierung be�ndet sich auf einer "niedrigeren"
Abstraktionsebene in Bezug auf den zu steuernden Roboter. Sie h�angt st�arker
als der vorher gezeigte Algorithmus von dem konkreten Roboter ab, den man
68 5. ARIADNE & ART - EIN TUTORIAL
steuern m�ochte. Wichtig ist zum Beispiel, an welchen ActuatorPort welcher
Motor angeschlossen werden mu�, ob �uberhaupt echte Motor-Objekte erzeugt
werden, oder etwa eine Unterklasse davon. Schlie�lich mu� auch die Drehrich-
tung eventuell an das Modell angepasst werden, damit "vorw�arts" auch die
korrekte Bedeutung f�ur den Roboter erh�alt.
Hier soll ein Beispiel f�ur eine Implementierung von DriveTrainSimple-
Algorithm.Robot gegeben werden, die davon ausgeht, da� bei dem zu steuern-
den Roboter die entsprechenden, ganz normalen Motoren an den Ausg�angen 0
und 1 angeschlossen sind. Welche Anschl�usse damit an der jeweiligen Hardware
gemeint sind, h�angt nat�urlich von der Implementierung von RobotInterface
ab. Bei der in ART enthaltenen Implementierung f�ur Fischertechnik sind das
M1 und M2, beim Cybermaster die beiden eingebauten Motoren und beim Brick
die Anschl�usse 1 und 2. Die Drehrichtung wird in diesem Beispiel noch nicht
angepasst. Im weiteren Verlauf dieses Kapitels wird aber auch darauf R�ucksicht
genommen.
Listing 9: code/art/examples/DriveTrainSimpleRobot.java
package de.jaetzold.art.examples;
import de.jaetzold.art.Motor;import de.jaetzold.art.ActuatorPort;import de.jaetzold.art.RobotInterface;import de.jaetzold.art.RobotInterfaceDefinition;import de.jaetzold.art.RobotInterfaceStringDefinition;
public class DriveTrainSimpleRobotimplements DriveTrainSimpleAlgorithm.Robot
{protected Motor leftMotor;protected Motor rightMotor;
public DriveTrainSimpleRobot(RobotInterface iface) {ActuatorPort[] ports = iface.getActuatorPorts();if(ports.length < 2) {
throw new IllegalArgumentException("RobotInterface "+iface+" provides only "+ports.length+" Ports, minimum is 2.");
}
leftMotor = new Motor();leftMotor.connectWith(ports[0]);
rightMotor = new Motor();rightMotor.connectWith(ports[1]);
}
public Motor getLeftMotor() {return leftMotor;
➥
5.2. DRIVETRAIN 69
Listing 9: code/art/examples/DriveTrainSimpleRobot.java (Fortsetzung)
}public Motor getRightMotor() {
return rightMotor;}public DriveTrainSimpleRobot(RobotInterfaceDefinition definition) {
this(new de.jaetzold.art.RobotInterfaceFactory().getInterface(definition));
}public DriveTrainSimpleRobot(String interfacePortName) {
this(new RobotInterfaceStringDefinition(interfacePortName));}public DriveTrainSimpleRobot() {
this(System.getProperty(className +".portName", "ALL"));}private static String className =
"de.jaetzold.art.examples.DriveTrainSimpleRobot";...
}
Eine Implementierung f�ur einen weiteren, etwas anderen Roboter zeigt Lis-
ting 10 auf Seite 71. Die hier gezeigte Implementierung besitzt mehrere Kon-
struktoren, um auf verschiedenen Ebenen in die Erzeugung des RobotInterface
eingreifen zu k�onnen:
Der erste Konstruktor erwartet bereits ein fertiges RobotInterface, das
nichts weiter aufweisen mu� als zwei Ports, um die Motoren daran anzuschlie-
�en. Wie das genau geht, wurde bereits in Abschnitt 5.1 auf Seite 59 erl�autert.
Der an Port 0 angeschlossene Motor mu� der linke sein, der an Port 1 ange-
schlossene der rechte.
Der zweite Konstruktor von DriveTrainSimpleRobot gibt einem die M�og-
lichkeit, �uber ein String-Objekt festzulegen, welches Interface an welchem
Port f�ur den Anschlu� der Motoren verwendet werden soll. RobotInterface-
Definition ist ein Interface, welches keine Methoden vorschreibt. Objekte
von Klassen, die dieses Interface adaptiert haben, k�onnen aber der Robot-
InterfaceFactory als Parameter f�ur die Konstruktion eines RobotInterface
�ubergeben werden. Damit kann eingeschr�ankt werden, welche Implementierung-
en von RobotInterface die Factory zur�uckliefert.
Von einer Implementierung von RobotInterfaceDefinition wird nichts
Besonderes erwartet, es h�atten auch schlicht Objekte vom Typ Object ver-
langt werden k�onnen, doch w�are damit der Sinn dieses Parameters schwieriger
zug�anglich gewesen. Welche Implementierung von RobotInterfaceDefinition
ben�otigt wird, h�angt von den Implementierungen von RobotInterface, die
einen speziell interessieren, ab und sollte in deren Beschreibung dokumentiert
sein.
Die bereits vorhandenen Implementierungen f�ur LEGO und Fischertechnik
reagieren auf Strings, welche von einer RobotInterfaceStringDefinition ge-
kapselt werden. Zur einfacheren Benutzung wird daher auch gleich noch ein
Konstruktor mit einem String als Parameter zur Verf�ugung gestellt.
Strings als RobotInterfaceDefinition bieten den Vorteil, da� �uber soge-
70 5. ARIADNE & ART - EIN TUTORIAL
nannte Properties wie z.B. im dritten, parameterlosen Konstruktor leicht ge-
steuert werden kann, welches Interface genommen werden soll. Dieser String
beginnt mit einer Kennzeichnung f�ur das Interface, welches man haben m�ochte,
und darauf folgt (optional) der Name des (seriellen) Ports, an dem nach dem
Interface gesucht werden soll. Beispiele:
"FT" { die Implementierung von Fischertechnik sucht auf allen m�oglichen Ports
nach einem Interface
"MSCOM2" { auf dem von JavaComm6 mit COM2 bezeichneten seriellen Port wird
nach einem Brick gesucht
"CM/dev/ttyS0" { auf dem Port mit Namen /dev/ttyS0 wird nach einem
Cybermaster-Interface gesucht7
"RCX1" { auf COM1, /dev/ttyS0 sowie dem ersten von JavaComm gelieferten
Portnamen f�ur einen seriellen Port wird nach einem Cybermaster oder
einem Brick gesucht
Das letzte Beispiel steht f�ur eine besondere Interpretation des Portnamens
durch die Implementierungen f�ur Lego und Fischertechnik. Besteht der Port-
name ausschlie�lich aus einer Zahl "n", werden als Portnamen die beiden vor-
de�nierten Namen COMn und /dev/ttySn genommen und au�erdem der n-te
Name eines seriellen Ports aus der Liste von Portnamen, die man von Java-
Comm als Enumeration geliefert bekommt. Es ist zwar nicht genau spezi�ziert,
aber bei Tests wurden die seriellen Ports bisher immer in der Reihenfolge an-
gegeben, wie sie auch vom jeweiligen System nummeriert werden. Die Namens-
gebung ist jedoch von der verwendeten Implementierung des Java Communi-
cations API abh�angig. Die Implementierung von Sun f�ur Windows, sowie die
Implementierung von IBM f�ur Linux und f�ur Windows, nennen serielle Ports
COM1, COM2 usw. Die freie Implementierung "rxtx" ( http://www.rxtx.org )
nennt serielle Ports unter Linux wie die entsprechenden Device-Namen, also z.B.
/dev/ttyS0, /dev/ttyS1 usw. Eine Einf�uhrung in das Java Communications
API gibt J�atzold (1999).
Tests zeigen, da� die Implementierung von Sun unter Windows extrem lang-
sam ist. Die in Schreiner (2000b) beschriebenen Geschwindigkeitsprobleme tre-
ten sowohl mit rxtx unter Linux als auch mit der Implementierung von IBM
unter Windows und Linux nicht auf.
Zum Testen, ob alles funktioniert, kann die Klasse DriveTrainSimpleRobot
auch direkt ausgef�uhrt werden. Die main-Methode erzeugt sogar aus einem �uber-
gebenen Argument die verwendete RobotInterfaceStringdefinition. Alter-
nativ kann auch "von Hand" die Property de.jaetzold.art.examples.Drive-
TrainSimpleRobot.portName gesetzt werden:
$ java -Dde.jaetzold.art.examples.DriveTrainSimpleRobot.port➥
6Java Communications API. Siehe Sun Microsystems (1998) und J�atzold (1999).7Der Cybermaster wird in der aktuellen Version von ART nicht mehr unterst�utzt. Zuk�unf-
tige Versionen k�onnten diese Unterst�utzung aber m�oglicherweise wieder enthalten.
5.2. DRIVETRAIN 71
Name=RCX2 de.jaetzold.art.examples.DriveTrainSimpleRobot
Successfully initialized LEGO-Mindstorms Interface, found onserialport COM2$
Kommt eine vergleichbare Meldung, dann hat alles wie erwartet geklappt.
Kommt aber zum Beispiel eine Exception, hilft ein Blick in den Anhang, Ab-
schnitt B auf Seite 139 und/oder der Abschnitt A auf Seite 135, m�oglicherweise
weiter. Das Fenster, das bei erfolgreicher Ausf�uhrung ge�o�net wird, kann man
schon mal zum Testen verwenden. Wieso dieses Fenster �uberhaupt auftaucht,
wird am Ende dieses Abschnitts und in dem darauf Folgenden n�aher erl�autert.
Hat man aus irgendeinem Grund bereits ein fertiges Robotermodell, bei
dem die Motoren nicht so angeschlossen sind, da� DriveTrainSimpleRobot f�ur
DriveTrainSimpleAlgorithm geeignet ist, mu� man nicht unbedingt das ge-
samte Interface DriveTrainSimpleAlgorithm.Robot neu implementieren. Man
kann sich die F�ahigkeiten von objektorientierter Programmierung zunutze ma-
chen und einfach eine Unterklasse von DriveTrainSimpleRobot implementie-
ren, die die Motoren auf geeignete Weise manipuliert.
Der f�ur diese Arbeit gebaute Beispielroboter hat in seiner Lego-Version die
Motoren anders angeschlossen:
Listing 10: code/art/examples/DriveTrainSimpleMSDemoRobot.java
public class DriveTrainSimpleMSDemoRobotextends DriveTrainSimpleRobot
{public DriveTrainSimpleMSDemoRobot(RobotInterface iface) {
super(iface);// the Motors on the MS-Robot are on different sidesMotor tmp = leftMotor;leftMotor = rightMotor;rightMotor = tmp;// the Motors on the MS-Robot turn the other way roundleftMotor.setReversed(true);rightMotor.setReversed(true);
}public DriveTrainSimpleMSDemoRobot(String interfacePortName) {
this(new de.jaetzold.art.RobotInterfaceFactory().getInterface(interfacePortName));
}public DriveTrainSimpleMSDemoRobot() {
this(System.getProperty(className +".portName", "MS"));}private static String className =
"de.jaetzold.art.examples.DriveTrainSimpleMSDemoRobot";...
}
Da die Motoren aber auch an den ersten und den zweiten Ausgang ange-
schlossen sind, ist es nicht n�otig neue Motor-Objekte zu erzeugen. Die Vorhan-
72 5. ARIADNE & ART - EIN TUTORIAL
denen werden einfach vertauscht und auf die umgekehrte Drehrichtung gesetzt.
Ansonsten kann weiterhin zur Steuerung DriveTrainSimpleAlgorithmverwen-
det werden.
DTSA.Robot
getRightMotor()
getLeftMotor()
DTSRobot
Motor
stop()
backward()
forward()
DTSAlgorithm
leftMotor
ActuatorPort
(e.g. CountSensor)Virtual Sensors:
SwitchSensor2
AnalogSensor1
Motor1
Motor2
Motor3
Motor4
SwitchSensor1
AnalogSensor2
SwitchSensor8
RobotInterface
"Wire"
Ethernet, ...)Bluetooth,(RS232, I2C,
state−changeinforms about
retrieved fromRobotInterface
withinitialized
is a
(implements)retrieved
from factory
connects Motorwith interface
with port& connects
& deliversMotor
configurescreates,
DTSA = DriveTrainSimpleAlgorithm
DTS = DriveTrainSimple
Abbildung 5.2: Ein m�ogliches Design-Pattern f�ur die Programmierung mit ART. Der
Algorithmus kann unterschiedliche "Roboter" steuern, sofern f�ur diese eine geeignete
Implementierung des Robot-Interface vorliegt.
Diese Aufteilung, in einen Algorithmus sowie eine Ansammlung von Para-
metern in Form eines Java-Interface, hat den Vorteil, da� der Algorithmus f�ur
verschiedene Roboter wiederverwendet werden kann.
Der Algorithmus DriveTrainSimpleAlgorithm kann mit jedem Roboter
verwendet werden, f�ur den eine Implementierung des Java-Interface Drive-
TrainSimpleAlgorithm.Robot existiert (bzw. erzeugt werden kann), die si-
cherstellt, da� die auf Seite 65 beschriebenen Anforderungen an die Motoren
erf�ullt sind.
Die Abbildung 5.2 veranschaulicht die Verbindungen, Zusammenh�ange und
Verantwortlichkeiten dieses Design-Patterns auf gra�sche Weise. Da die Klas-
sennamen sehr lang sind, wurden Abk�urzungen in Form von Buchstabenfol-
gen verwendet, deren Bedeutung in der Gra�k angegeben ist. Das dargestell-
te RobotInterface entspricht in etwa der Implementierung f�ur Fischertech-
nik. Auf die Anzahl und Art der dort angegebenen Motoren und Sensoren
kommt es jedoch nicht an, sie dienen nur als Beispiel, zur Verdeutlichung wof�ur
RobotInterface steht.
Eine genauere Beschreibung von RobotInterface und dem Zusammenspiel
zwischen diesem mit Motor, MotorPeer, ActuatorPort usw. be�ndet sich in
Kapitel 6. Eine genauere Erkl�arung, wof�ur die Klassen jeweils stehen gibt au-
�erdem Kapitel 4.
Auch die Klasse DriveTrainSimpleMSDemoRobot aus Listing 10 auf der vor-
herigen Seite hat eine main-Methode, die man zum Testen direkt ausf�uhren
kann. Die �Ubergabe eines Parameters f�ur das gew�unschte Interface ist dies-
mal weggelassen, da DriveTrainSimpleMSDemoRobot sowieso als default ein
Mindstorms-Interface verlangt (der zweite Parameter zu System.getProper-
5.3. UND WIE BENUTZT MAN DAS NUN? 73
ty() gibt den default-Wert an und lautet "MS").
$ java de.jaetzold.art.examples.DriveTrainSimpleMSDemoRobot
Successfully initialized LEGO-Mindstorms Interface, found onserialport COM2$
Im folgenden Text wird nun nicht mehr jedesmal auf die M�oglichkeit der
Ausf�uhrung zum Testen hingewiesen und ein Beispiel gezeigt. Fast alle Klassen
aus dem package de.jaetzold.art.examples, welche das Wort "Robot" im
Namen enthalten, sind auf diese Weise ausf�uhrbar.
5.3 Und wie benutzt man das nun?
Im letzten Abschnitt wurden viele Klassen, bzw. Interfaces geschrieben, aber
nichts hat sich bewegt. Objekte der Klasse DriveTrainSimpleAlgorithm ma-
chen von alleine ja nicht viel, sie steuern den Roboter nur, wenn sie durch einen
Methodenaufruf explizit dazu aufgefordert werden.
Abbildung 5.3: Testfenster f�ur DriveTrain
Nur wer vorhin wirklich die vorgestellten Roboter-Implementationen zur
Ausf�uhrung gebracht hat, wird wahrscheinlich bemerkt haben (wenn alles ge-
klappt hat), da� in diesem Fall ein Fenster auf der Ober �ache erscheint, welches
in etwa so aussieht wie das in Abbildung 5.3 gezeigte.
In diesem Abschnitt wird diese einfache View f�ur DriveTrain-Objekte auf
Basis des AWT vorgestellt. Sie ist aufgeteilt in zwei Klassen. Die Klasse Drive-
TrainView stellt den Zustand eines DriveTrain in einem Textfeld dar. Die
andere Klasse (DriveTrainCommands) bietet eine Art Armaturenbrett um einen
DriveTrain zu beein ussen. Beide sind als Unterklassen von Panel realisiert
und daher leicht in eine beliebige Ober �ache integrierbar.
Welche Zust�ande es gibt, wurde bereits im Interface DriveTrain de�niert:
Listing 11: code/art/examples/DriveTrain.java (Ref. in Listing 5 S. 63)
public int getState();
public static final int FORWARD = 1;public static final int BACKWARD = 2;
➥
74 5. ARIADNE & ART - EIN TUTORIAL
Listing 11: code/art/examples/DriveTrain.java (Fortsetzung)
public static final int LEFT SPIN = 3;public static final int RIGHT SPIN = 4;public static final int STOP = 8;
Der Zustand kann mit getState() abgefragt werden und �uber die Konstan-
ten kann eine Zuordnung des dabei erhaltenen int-Werts, zu einer bestimmten
Bedeutung, erfolgen.
Die eine View macht nichts weiter, als den Zustand eines DriveTrain-
Objekts in einen String umzuwandeln und diesen in einem Textfeld auszugeben:
Listing 12: code/art/examples/DriveTrainView.java
package de.jaetzold.art.examples;
import java.awt.Panel;import java.awt.TextField;import java.beans.PropertyChangeListener;import java.beans.PropertyChangeEvent;
public class DriveTrainView extends Panelimplements PropertyChangeListener
{protected TextField stateOutput = new TextField("", 15);
public void propertyChange(PropertyChangeEvent pce) {if(pce.getSource() instanceof DriveTrain
&& pce.getPropertyName().equals("state")&& pce.getNewValue() instanceof Number) {
String stateName = "";int state = ((Number)pce.getNewValue()).intValue();switch(state & ˜ DriveTrain.STOP) {
case DriveTrain.FORWARD:stateName = "forward";break;
...default:
stateName = "";}if((state & DriveTrain.STOP) == DriveTrain.STOP) {
stateName = "stop " +stateName;}stateOutput.setText(stateName);
}}
...}
Die DriveTrainView ist ein PropertyChangeListener und versucht den
neuenWert eines PropertyChangeEvent als Zustand eines DriveTrain-Objekts
zu interpretieren, sofern der Event von einem DriveTrain-Objekt kommt und
die zugeh�orige Property den Namen state hat.
5.3. UND WIE BENUTZT MAN DAS NUN? 75
Nicht jedes DriveTrain-Objekt mu� einen PropertyChangeListener f�ur
die Beobachtung seines Zustands haben k�onnen, daher sind die Methoden zum
Hinzuf�ugen oder Entfernen eines solchen nicht bereits im Interface DriveTrain
enthalten. Das hat den Grund, da� das Hinzuf�ugen von Views in der Regel am
besten dort geschieht, wo das DriveTrain-Objekt erzeugt wurde. Daher reicht
es, wenn nur die implementierenden Klassen die entsprechenden Methoden bie-
ten:
Listing 13: code/art/examples/DriveTrainSimpleAlgorithm.java (Ref. in Listing 7 S. 66)
protected PropertyChangeSupport changes =new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener l) {changes.addPropertyChangeListener(l);
}public void removePropertyChangeListener(PropertyChangeListener l) {
changes.removePropertyChangeListener(l);}
protected void setState(int state) {int previousState = this.state;this.state = state;changes.firePropertyChange("state", previousState, state);
}public int getState() {
return state;}
Das Setzen eines Zustands in DriveTrainSimpleAlgorithm sollte immer
�uber die Methode setState(int) geschehen, damit eventuelle java.beans.
PropertyChangeListener automatisch informiert werden. Den n�otigen java.
beans.PropertyChangeEvent erzeugt die mit changes bezeichnete Instanz von
java.beans.PropertyChangeSupport. Dieses Vorgehen entspricht (bis auf die
Verwendung von PropertyChangeSupport) dem Design-Pattern, wie es von
der JavaBeans Speci�cation 1.01 (Sun Microsystems, 1997) in Bezug auf soge-
nannte Bound-Properties vorgeschrieben ist. Zu ART im Zusammenhang mit
JavaBeans siehe Abschnitt 7.8.
Die DriveTrainView ist zum Beobachten des Zustands eines DriveTrain-
Objektes da. Damit etwas passiert, mu� dieser Zustand aber auch manipuliert
werden k�onnen. F�ur eine gra�sche Ober �ache bietet die Klasse DriveTrain-
Commands diese M�oglichkeit:
Listing 14: code/art/examples/DriveTrainCommands.java
package de.jaetzold.art.examples;
import java.awt.Panel;import java.awt.Button;import java.awt.event.ActionListener;import java.awt.event.ActionEvent;
➥
76 5. ARIADNE & ART - EIN TUTORIAL
Listing 14: code/art/examples/DriveTrainCommands.java (Fortsetzung)
public class DriveTrainCommands extends Panel {public DriveTrainCommands(DriveTrain driveTrain) {
this();setDriveTrain(driveTrain);
}public DriveTrainCommands() {
Button stop = new Button("stop");stop.addActionListener(
new ActionListener() {public void actionPerformed(ActionEvent ae) {
if(dt != null) {dt.stop();
}}
});add(stop);
...}
protected DriveTrain dt;public void setDriveTrain(DriveTrain dt) {
this.dt = dt;}
}
DriveTrainCommands stammt genau wie die DriveTrainView von Panel
ab, damit sie leicht in eine Ober �ache integriert werden kann. F�ur jede Me-
thode von DriveTrain, die dessen Zustand beein usst, gibt es einen Button,
�uber den die entsprechende Methode aufgerufen werden kann. Textfelder oder
andere M�oglichkeiten zur weiteren Dateneingabe sind nicht n�otig, da die ent-
sprechenden Methoden alle parameterlos sind. Der Code f�ur die Konstruktion
der anderen Kn�opfe wurde hier im Text aus Gr�unden der �Ubersichtlichkeit
weggelassen.
Die Klasse DriveTrainSimpleAlgorithm besitzt zum Testen eine main-
Methode, die eine DriveTrainView und ein -Commands mit einem Objekt von
sich verbindet und in einem Frame darstellt:
$ java de.jaetzold.art.examples.DriveTrainSimpleAlgorithm RCX
Successfully initialized LEGO-Mindstorms Interface, found onserialport COM2$
Welche Implementierung des Robot-Interface verwendet wird, kann man
auch hier wieder �uber eine Property steuern (de.jaetzold.art.examples.
DriveTrainSimpleAlgorithm.robotClass). Per Voreinstellung wird ansonsten
die Klasse DriveTrainSimpleRobot genommen. Der Aufruf bringt das in Abb-
lidung 5.3 auf Seite 73 gezeigte Fenster zum Vorschein, mit dem die Klasse
schon ganz gut ausprobiert werden kann.
5.4. TRUSTY 77
5.4 Trusty
Bisher wurden in diesem Tutorial nur Motoren, also Ausg�ange eines Roboters
gesteuert. Ein "richtiger" Roboter mu� aber auch Eing�ange haben, Sensoren mit
denen die Umwelt wahrgenommen werden kann. Wie in Kapitel 2 beschrieben,
hat Trusty daf�ur zwei Bumper mit denen er Hindernisse registrieren kann. Daher
soll anhand seiner Programmierung der einfache Umgang mit Sensoren in ART
demonstriert werden.
Zun�achst ist der prinzipielle Aufbau von Trusty wieder �ahnlich wie bei
DriveTrain, um eine m�oglichst weitgehende Unabh�angigkeit des Algorithmus
zu erreichen. Es gibt ein Interface, das die F�ahigkeiten von Trusty de�niert:
Listing 15: code/art/examples/Trusty.java
package de.jaetzold.art.examples;
public interface Trusty {public void move();public void stop();public void avoidLeft();public void avoidRight();
...}
Die F�ahigkeiten von Trusty umfassen also ein irgendwie geartetes Fortbe-
wegen (move()), Anhalten (stop()) sowie nach links bzw. rechts Ausweichen
(avoidLeft(), avoidRight()).
Die Zuordnung des Verhaltens "ausweichen" zu einem entsprechenden Ereig-
nis, wie z.B. dem Ausl�osen eines der Bumper, wird an dieser Stelle vorerst au�er
Acht gelassen. Durch diese Trennung erreicht man eine gr�o�ere Flexibilit�at im
Umgang mit den entwickelten Algorithmen, da die dann nur lose miteinander
gekoppelten Verhaltensweisen und Ereignisse leichter neu gruppiert, sowie in
einem anderen Kontext verwendet werden k�onnen. Dies wird aber auch durch
eine weitere Verteilung des Codes auf noch mehr Klassen erkauft, was nicht nur
Vorteile mit sich bringt. Weil die Zusammenh�ange nicht mehr zentral festgelegt
werden, sind diese auch nicht immer sofort ersichtlich.
Die einfache Beispiel-Implementierung von Trusty ist TrustySimpleAlgo-
rithm, welche eine spezielle Form eines DriveTrain verwendet, die eine Anga-
be von Strecken bzw. Winkeln als Parameter zu Methoden wie forward bzw.
leftSpin() erlaubt:
Listing 16: code/art/examples/NormalizedDriveTrain.java
package de.jaetzold.art.examples;
public interface NormalizedDriveTrain extends DriveTrain {public void forward(double meters);
...}
Wird bei einem NormalizedDriveTrain z.B. forward mit einem Parameter
78 5. ARIADNE & ART - EIN TUTORIAL
aufgerufen, dann wird von einer Implementierung erwartet, da� sich der Robo-
ter die entsprechende Strecke in Metern vorw�arts bewegt und danach anh�alt.
Entsprechendes gilt f�ur die Methoden zum Drehen des Roboters, die Ma�einheit
sind in diesem Fall ganze Umdrehungen. Ein Aufruf von backward(0.1) bewegt
den Roboter also um 10cm nach hinten und ein Aufruf von leftSpin(0.25)
dreht den Roboter um 90Æ nach links. Die Wahl der Ma�einheit ist reine Ge-
schmackssache.
Die zugeh�orige Implementierung ist in der Klasse NormalizedDriveTrain-
SimpleAlgorithm enthalten, welche auch ein eigenes Robot-Interface de�niert.
Dieses Interface schreibt zus�atzlich zu den Methoden aus DriveTrainSimple-
Algorithm.Robot noch zwei weitere vor:
Listing 17: code/art/examples/NormalizedDriveTrainSimpleAlgorithm.java
public interface Robot extends DriveTrainSimpleAlgorithm.Robot {public int getMeterMilliseconds();public int getFullTurnMilliseconds();
}
Diese Methoden dienen dazu, die Zeitdauer anzugeben, die der jeweilige Ro-
boter braucht, um einen Meter vorw�arts zu kommen bzw. eine volle Umdrehung
zu machen. Damit ist eine einfache Implementierung von NormalizedDrive-
Train m�oglich, die f�ur die entsprechende Zeitdauer das gew�unschte Verhalten
"einstellt" und danach anh�alt:
Listing 18: code/art/examples/NormalizedDriveTrainSimpleAlgorithm.java
public void forward(double meters) {if(meters<0) {
backward(meters*-1);}forward();try {
Thread.sleep((long)(meterMilliseconds*meters));} catch(InterruptedException ie) {}stop();
}
Auf die Angabe des restlichen Codes, insbesondere der Initialisierung, wird
verzichtet, da dieser nach den nun bereits bekannten Schemata realisiert ist.
Eine gra�sche �Ubersicht �uber die Zusammenh�ange zeigt Abbildung 5.4 auf der
n�achsten Seite. Wie es zu der Verbindung zum eigentlichen Roboter kommt,
wurde bereits in Abbildung 5.2 auf Seite 72 gezeigt und ist an dieser Stelle
zugunsten einer besseren �Ubersichtlichkeit ausgespart worden.
Es wird in diesem Zusammenhang vielleicht noch ein weiterer Vorteil der Zu-
sammenfassung der Parameter des Algorithmus zu einem Java-Interface deut-
lich: Die Parameter sind nicht voneinander unabh�angig und m�ussen daher als
Einheit angesehen werden, was sich auf diese Weise auch syntaktisch widerspie-
gelt. Welche Zeit der Roboter z.B. f�ur das Zur�ucklegen der Strecke von einem
Meter braucht und in welchem Verh�altnis diese zu der Zeit f�ur eine ganze Um-
5.4. TRUSTY 79
DTSRobot
DTSMSDemoRobot
DTSAlgorithm DriveTrain
forward()
leftSpin()
NDTSBaseRobot
NDTSMSDemoRobot
NormalizedDT
forward(meters)
leftSpin(turns)
NDTSAlgorithm
DTSA.Robot
getRightMotor()
getLeftMotor()
NDTSA.Robot
getTurnMillis()
getMeterMillis()
NDTSFTDemoRobot
& initializesextends
extends
& initializes
extends
implements
extendsextends
initializes
defines &
with
defines &
withinitializes
implements
implements
implements
NDTSA = NormalizedDriveTrainSimpleAlgorithm
NDTS = NormalizedDriveTrainSimple
DTSA = DriveTrainSimpleAlgorithm
DTS = DriveTrainSimple
extends& initializes
uses
to forward
Abbildung 5.4: Design der Beispielimplementierung von NormalizedDriveTrain.
drehung steht, h�angt zum Beispiel direkt mit der konkreten Implementierung
der verwendeten Motoren zusammen und kann daher auch nicht unabh�angig
von dieser sinnvoll verwendet werden.
Zudem w�are es vorstellbar, da� ein Parameter erst erzeugt bzw. berechnet
wird, wenn er gebraucht wird (d.h. wenn die Interface-Implementierung danach
gefragt wird). Davon wird zwar bei keinem der Beispiele in diesem Kapitel
Gebrauch gemacht, man sollte diese M�oglichkeit aber im Hinterkopf behalten,
wenn man mit einem solchen Robot-Interface arbeitet. Vielleicht �andern sich
manche Parameter ja sogar (noch w�ahrend einer Programmausf�uhrung) mit
der Zeit?
Ich m�ochte auch noch auf eine Schwachstelle der gezeigten Implementierung
von NormalizedDriveTrain hinweisen. Sie geht davon aus, da� die Zeit, die der
Roboter zum Fahren einer Strecke braucht, in einem linearen Zusammenhang
mit der Strecke steht. Genau genommen ist es sogar so, da� die angenommene
Zeit ein Vielfaches der gew�unschten Strecke ist. Da ein Roboter aber auch Zeit
zum Beschleunigen braucht, bzw. die Kommandos wie forward() oder stop()
einen gewissen zeitlichen "overhead" haben k�onnen, ist diese Implementierung
nicht sehr pr�azise. Dies wird sich sp�ater bei der Implementierung von Trusty
f�ur den Lego-Roboter noch als ein Problem herausstellen.8
Doch nun wieder zur�uck zu der Implementierung von Trusty, dem eigent-
lichen Anliegen in diesem Abschnitt. Mit einem NormalizedDriveTrain ist
zum Beispiel eine einfache Implementierung von avoidLeft() folgenderma�en
m�oglich:
Listing 19: code/art/examples/TrustySimpleAlgorithm.java (Ref. in Listing 20 S. 80)
public void avoidLeft() {➥
8Das Problem mit dem Brick liegt in der Zeit, die vergeht, bis ein Befehl an den Motor
auch im Roboter angekommen und umgesetzt ist.
80 5. ARIADNE & ART - EIN TUTORIAL
Listing 19: code/art/examples/TrustySimpleAlgorithm.java (Fortsetzung)
setState(AVOID LEFT);driveTrain.backward(backupMeters);driveTrain.rightSpin(turnAngle);stop();
}
Es wird einfach eine gewisse Strecke zur�uckgefahren und dann eine Rechts-
drehung vollzogen. Wie weit zur�uckgefahren wird und der Umfang der Drehung
sind Parameter die wieder, ebenso wie driveTrain, bei der Konstruktion des
TrustySimpleAlgorithm in Form eines entsprechenden Robot-Objektes �uber-
geben und auf diese Weise initialisiert wurden:
Listing 20: code/art/examples/TrustySimpleAlgorithm.java
package de.jaetzold.art.examples;...public class TrustySimpleAlgorithm implements Trusty {
protected NormalizedDriveTrain driveTrain;protected double backupMeters;protected double turnAngle;
public TrustySimpleAlgorithm(Robot robot) {driveTrain = robot.getNormalizedDriveTrain();backupMeters = robot.getBackupMeters();turnAngle = robot.getTurnAngle();
}public static interface Robot {
public NormalizedDriveTrain getNormalizedDriveTrain();public double getBackupMeters();public double getTurnAngle();
}<see Listing 19 on page 79>...
}
In diesem Fall werden (wie schon bei NormalizedDriveTrain) gar keine
Objekte mehr, die direkt aus ART stammen, �ubergeben, sondern einfach nur
statische Parameter f�ur den Algorithmus, sowie ein weiterer Algorithmus (in die-
sem Fall ein NormalizedDriveTrain), auf dem der Algorithmus arbeitet. Diese
werden wieder, um das bisherige Robot-Design-Pattern beizubehalten, mittels
einer interface-De�nition gruppiert (siehe Abbildung 5.5 auf der n�achsten
Seite).
Damit sind die grunds�atzlichen Aktionen von Trusty bereits implementiert
und k�onnen auch durch Ausf�uhrung der jeweiligen Robot-Implementierung ge-
testet werden. Zur Erinnerung: Die hier besprochenen Robot-Beispielimplemen-
tierungen enthalten alle eine main-Methode, welche eine Ober �ache zum Vor-
schein bringt, mit der man den Algorithmus f�ur den jeweiligen Roboter testen
kann.
In diesem Zusammenhang sei f�ur eigene Gehversuche nochmals darauf hin-
gewiesen, da� die in ART enthaltenen Implementierungen von RobotInterface
5.4. TRUSTY 81
TSAlgorithmTSBaseRobot Trusty
move()
avoidLeft()
TS = TrustySimple
TSA = TrustySimpleAlgorithm
NDT = NormalizedDriveTrain
DT = DriveTrain
TSA.Robot
getNDT()
getTurnAngle()
TSMSDemoRobot TSFTDemoRobot
NormalizedDT
forward(meters)
leftSpin(turns)
initializes
defines &
with
implementsimplements
appropriate
creates/delivers
appropriate
creates/delivers
extends extends
& initializes& initializes
Abbildung 5.5: Design der Beispielimplementierung von Trusty. Eine vorhandene Im-
plementierung von NormalizedDriveTrain wird wiederverwendet.
zwar eigene Threads erzeugen, diese aber allesamt sogenannte daemon-Threads
sind, welche die Java-Maschine nicht von sich aus am Laufen halten. Damit
wird die Tatsache, da� �uberhaupt eigene Threads verwendet werden, besser
versteckt, denn ein Programm, das ART verwendet, geht genau auf die gleiche
Weise zu Ende, wie das ohne die Verwendung von ART der Fall w�are. Ansonsten
m�u�te man entweder System.exit() aufrufen, um ein Programm zu beenden
{ wie das z.B. bei Verwendung des AWT notwendig ist { oder in ART eine ei-
gene Methode zur Verf�ugung stellen, mit der man die erzeugten nicht-daemon
Threads beenden kann.
Einer dieser Threads ist daf�ur zust�andig, die Events, die eventuelle Ver�ande-
rungen an den Hardware-Sensoren repr�asentieren, an die Komponenten (die
Sensoren "in Software") auszuliefern, die an den zugeh�origen Port angeschlossen
sind. An diese Sensor-Objekte k�onnen wiederum SensorListener angeschlos-
sen sein, die ihrerseits dann auch von einem Thread informiert werden, indem
bei ihnen die Methode processEvent(SensorEvent) aufgerufen wird. Bei den
in ART vorhandenen Implementierungen von Sensoren ist dieser Thread nor-
malerweise derselbe wie der, mit dem der Sensor selbst benachrichtigt wurde.
Sensoren wurden in diesem Kapitel bisher noch gar nicht direkt behan-
delt. Trusty hat aber zwei "Bumper". Um die gew�unschte Verhaltensweise von
Trusty zu erreichen, da� er einem auf diese Weise registrierten Hindernis au-
tomatisch ausweicht, mu� der Event, da� Trusty irgendwo angesto�en ist, dem
entsprechenden Ausweich-Verhalten zugeordnet werden.
Der Zustand, ob Trusty mit einem seiner Bumper irgendwo angesto�en ist,
wird am einfachsten �uber einen BooleanSensor repr�asentiert. Ein Boolean-
Sensor interpretiert den von einem Sensor erhaltenen Wert per Voreinstellung
so, da� der Wert 0 false bedeutet und alle anderen Werte (insbesondere 1)
true zugeordnet werden. Ist also der BooleanSensor f�ur den linken Bumper
im Zustand true, dann soll links ausgewichen werden, entsprechendes gilt f�ur
den Sensor f�ur den rechten Bumper.
Man kann dieses Verhalten auch so darstellen, da� Trusty im allgemeinen
geradeaus f�ahrt und nur dann ausweicht, wenn ein Event, der true entspricht,
von einem der Sensoren eintri�t. Das Ausweich-Verhalten �uberdeckt dann das
82 5. ARIADNE & ART - EIN TUTORIAL
normale Fahrverhalten { es hat eine h�ohere Priorit�at.
Trusty eignet sich daher als ein einfaches Beispiel f�ur Subsumption. Auf
Subsumption wird in Abschnitt 1.2 auf Seite 4 genauer eingegangen, an dieser
Stelle soll mehr die Anwendung f�ur Trusty mittels der Hilfsklasse de.jaetzold.
art.subsumption.SchedulerTask im Vordergrund stehen.
Das Robot-Interface von SubsumptionTrusty liefert einen Trusty, sowie
zwei BooleanSensor-Objekte, welche links und rechts zugeordent sind:
Listing 21: code/art/examples/SubsumptionTrusty.java
public interface Robot {public Trusty getTrusty();public BooleanSensor getLeftSensor();public BooleanSensor getRightSensor();
}
Die Implementierung dieses Interface gestaltet sich genauso einfach wie bis-
her. Der Anschlu� von Sensoren unterscheidet sich nur in den verwendeten Ports
von dem Anschlu� von Motoren:9
Listing 22: code/art/examples/SubsumptionTrustyBaseRobot.java
public SubsumptionTrustyBaseRobot( Trusty trusty,RobotInterface iface)
{this.trusty = trusty;
SensorPort[] ports = iface.getSensorPorts();if(ports.length < 2) {
throw new IllegalArgumentException("RobotInterface " +iface+" provides only "+ports.length+" SensorPorts"+", minimum is 2.");
}
leftSensor = new BooleanSensor();leftSensor.connectWith(ports[0]);
rightSensor = new BooleanSensor();rightSensor.connectWith(ports[1]);
}
Wieder wird in dieser Basis-Implementierung davon ausgegangen, da� die
Sensoren an den ersten beiden SensorPorts angeschlossen werden m�ussen und
keiner weiteren Kon�guration bed�urfen. Wie Sensoren kon�guriert werden m�us-
sen und worauf man dabei achten mu�, bzw. welche M�oglichkeiten man hat,
erkl�art Abschnitt 5.5.
Die booleschen Sensoren k�onnen im Konstruktor von SubsumptionTrusty
direkt dazu verwendet werden, die Verhaltensweisen f�ur das Ausweichen zu dem
SchedulerTask hinzuzuf�ugen:
9Genau genommen ist sogar jeder Motor ein Sensor f�ur seinen eigenen Zustand.
5.4. TRUSTY 83
Listing 23: code/art/examples/SubsumptionTrusty.java
// behavior for avoiding left side obstaclesavoidLeft = new Task() {
public Event perform() {trusty.avoidLeft();return null;
}};scheduler.addBehaviorFor(new Double(1),
robot.getLeftSensor(),avoidLeft);
Als Priorit�at erwartet SchedulerTask ein Comparable. Als aktiviert gilt
eine Verhaltensweise dann, wenn der zugeh�orige BooleanSensor den Zustand
true hat. Ausgef�uhrt wird eine Verhaltensweise �uber den zugeh�origen Task.
Das Hinzuf�ugen f�ur den rechten Sensor geschieht analog. Man verwendet ei-
nem Task der entsprechend avoidRight() aufruft und eine andere Priorit�at
hat. Ob h�oher, gleich oder niedriger ist in diesem Fall zwar egal, die Klasse
SchedulerTask ist aber so implementiert, da� sie pro Priorit�at nur einen Ein-
trag erlaubt.
Exkurs: de.jaetzold.util.Task Das Interface Task beschreibt im Grunde
eine Art Runnable mit dem Unterschied, da� die Methode zum Ausf�uhren des
Task perform() hei�t und ein de.jaetzold.util.Event-Objekt als Resultat
haben kann. Dieser Unterschied ist hier zwar bisher nicht von Bedeutung, da
SchedulerTask dieses Resultat nicht weiter verarbeitet, wird aber an anderer
Stelle (bei den Implementierungen von RobotInterface, siehe Kapitel 6 auf
Seite 107) gebraucht.
Man kann sich nun fragen: Wieso dann �uberhaupt Task und nicht Runnable?
Der Vorteil liegt in dem Umstand begr�undet, da� { wie der Name schon an-
deutet { SchedulerTask selbst ein Task ist. Damit wird es m�oglich, mehre-
re SchedulerTask-Instanzen zu verschachteln. Die Methode SchedulerTask.
perform() w�ahlt von allen eingetragenen Verhaltensweisen, deren Boolean-
Sensor den Zustand true hat, diejenige mit der h�ochsten Priorit�at und f�uhrt
deren Task �uber perform() aus. Dies k�onnte dann nat�urlich auch ein weiterer
SchedulerTask sein.
Die Methode perform() einer Implementierung von Task darf au�erdem {
im Gegensatz zu run() von Runnable { jede Exception verursachen, da dies
bereits von Task.perform() so deklariert wird.
F�ur Task-Instanzen gibt es au�erdem die M�oglichkeit diese �uber einen de.
jetzold.util.TaskPerformer und eine zugeh�orige de.jaetzold.util.Queue
auszuf�uhren:
Listing 24: code/art/examples/SubsumptionTrusty.java
Queue taskQueue = new Queue();schedulerPerformer = new TaskPerformer(taskQueue);// schedulerPerformer.start(); // start the performer from the outside
➥
84 5. ARIADNE & ART - EIN TUTORIAL
Listing 24: code/art/examples/SubsumptionTrusty.java (Fortsetzung)
schedulerPerformer.haltPerforming(true);taskQueue.post(new RequeueTask(scheduler, taskQueue));
Ein TaskPerformer wird mit einer Queue initialisiert. Sobald die Queue ein
Element enth�alt, versucht der TaskPerformer dieses als Task zu interpretieren
und f�uhrt es gegebenenfalls aus.
Zu einer Queue werden Elemente mittels post(Object) hinzugef�ugt. Ein
TaskPerformer kann mittels haltPerforming(boolean) angehalten werden.
Ist das Argument true, so wird au�erdem dem ausf�uhrenden Thread des Task-
Performer die Nachricht interrupt() geschickt. Au�erdem geht die Methode
in diesem Fall erst dann zu Ende, wenn der TaskPerformer auch wirklich keinen
Task mehr ausf�uhrt.
Um einen Task wiederholt zur Ausf�uhrung zu bringen, kann man wie in dem
Beispiel einen de.jaetzold.util.RequeueTask benutzen, welcher den �uberge-
benen Task ausf�uhrt und sich selbst danach immer wieder zu der Queue hin-
zuf�ugt.
F�ur den hier genannten Zweck h�atte man eventuell auch java.util.Timer
und java.util.TimerTask verwenden k�onnen, doch die Klassen Task, Task-
Performer und Queue bieten dar�uber hinaus noch ein paar weitere Methoden,
welche sie von der Klasse Timer und einem TimerTask unterscheiden. Au�erdem
sind Timer und TimerTask erst seit Version 1.3 im API der Java Platform
enthalten. Die hier verwendeten Klassen sind aber mindestens mit Java 1.1
kompatibel und eignen sich daher auch zur Verwendung auf einer Maschine
wie dem TINI-Board (siehe 7.6 auf Seite 130), die im wesentlichen nur diese
Java-Version bietet.
Weiter mit SubsumptionTrusty: Au�er den Verhaltensweisen f�ur das Aus-
weichen mu� auch noch eine Verhaltensweise f�ur das normale Geradeausfahren
hinzugef�ugt werden:
Listing 25: code/art/examples/SubsumptionTrusty.java
// standard always-active behavior ’ move’BooleanSensor constantTrue = new BooleanSensor();constantTrue.connectWith(new ConstantSensorPort(1));move = new Task() {
public Event perform() {trusty.move();return null;
}};scheduler.addBehaviorFor(new Double(0), constantTrue, move);
Diese in Listing 25 gezeigte Verhaltensweise hat die niedrigste Priorit�at (in-
nerhalb von SubsumptionTrusty) und kommt daher immer dann zum Zuge,
wenn Trusty nicht ausweichen "will". Eigentlich m�u�te sie daher gar nicht ex-
tra aktiviert werden, weil sie immer aktiviert ist. Am einfachsten tr�agt man
diesem Umstand Rechnung, indem ihr ein BooleanSensor zugeordent wird,
5.4. TRUSTY 85
der immer true ist.
Zur Verwendung von Sensoren und der Klasse SensorPort ohne ein direkt
zugeordentes RobotInterface, kann man auch in Abschnitt 5.5 und in Kapitel
4 mehr erfahren.
STBaseRobot SubsumptionTrustyST.Robot
getTrusty()
getLeftSensor()
Trusty
move()
avoidLeft()
STFTDemoRobot
STMSDemoRobot
Trusty
move()
avoidLeft()
BooleanSensor
getValue()
addSListener()
initializes
defines &
with
implements implements
& initializes
extends
2x
uses to
implement behavior uses to
decide
what behavior
to exhibitappropriate
creates/delivers
creates/delivers appropriate
S = SensorST = SubsumptionTrusty
Abbildung 5.6: Design der Beispielimplementierung von Trusty, welche auch
selbst�andig �uber ein eventuelles Ausweich-Verhalten entscheiden kann. Eine vorhan-
dene Implementierung von Trusty wird wiederverwendet.
Die Zusammenh�ange der Roboter-Klassen bei SubsumptionTrusty wird in
Abbildung 5.6 dargestellt. Bekommt SubsumptionTrusty die Nachricht move()
wird der zugeh�orige TaskPerformermit dem in einen RequeueTask verpackten
SchedulerTask gestartet:
Listing 26: code/art/examples/SubsumptionTrusty.java
public void move() {// start algorithmif(!schedulerPerformer.isAlive()) {
// it’ either has not been started yettry {
schedulerPerformer.start();} catch(IllegalThreadStateException itse) {
// or already finished execution}
}schedulerPerformer.resumePerforming();
}public void stop() {
// stop algorithmschedulerPerformer.haltPerforming();try {
schedulerPerformer.waitForHalt();} catch(InterruptedException ie) {}trusty.stop();
}public void avoidLeft() {
➥
86 5. ARIADNE & ART - EIN TUTORIAL
Listing 26: code/art/examples/SubsumptionTrusty.java (Fortsetzung)
// TBD: instead of stopping subsumption, the corresponding// BooleanSensor could deliver true, to initiate an ’ avoid’stop();trusty.avoidLeft();
}
Da der TaskPerformer schedulerPerformer nicht schon bei der Initiali-
sierung gestartet wird, haben eventuelle Unterklassen von SubsumptionTrusty
die M�oglichkeit schedulerPerformer als daemon-Thread zu starten, bzw. ihn
vielleicht sogar durch einen anderen TaskPerformer zu ersetzen.
Die anderen vom Interface Trusty vorgeschriebenen Methoden halten die
Ausf�uhrung von schedulerPerformer an und leiten den Methodenaufruf an-
sonsten zu dem eigentlichen Trusty weiter.
Wie immer kann nat�urlich auch dieser Roboter getestet werden. Ein Bild
des Demo-Roboters aus Fischertechnik und aus Lego zeigt Abbildung 2.3 auf
Seite 10 aus Kapitel 2.
5.5 Kon�gurierte Sensoren
Die Verwendung von Sensoren wurde bisher nur sehr knapp behandelt. Die-
ser Abschnitt zeigt zuerst, worauf bei der Implementierung des Subsumption-
Trusty.Robot f�ur den Beispielroboter auf Lego-Basis geachtet werden mu�te,
damit an dem gleichen Anschlu� auch noch "Platz" f�ur die Lichtsensoren von
LiSe bleibt.
Darauf folgt eine Beschreibung, wie man durch geschickte Kon�guration
eines CountSensor den Rotationssensor aus dem Lego-Mindstorms Programm
selbst zum z�ahlen benutzen kann, ohne einen Brick daf�ur zu ben�otigen.
Zuletzt wird noch eine Hilfsklasse vorgestellt, mit der es auf einfache Weise
m�oglich ist, die Z�ahlrichtung bei der Verwendung eines Fischertechnik-Impuls-
rads mit der Drehrichtung eines Fischertechnik-Motors zu koppeln.
5.5.1 SubsumptionTrustyMSDemoRobot
Wie bereits eingangs erw�ahnt, sollen bei dem Beispielroboter aus Lego die Licht-
sensoren f�ur LiSe an die gleichen Eing�ange des Brick angeschlossen werden, wie
die Ber�uhrungssensoren f�ur Trusty.
Bei einem Aufruf von connectWith(Port) bei einem Sensor wird dieser mit
"dem Besten" Peer verbunden, den der Port f�ur diese Sensor-Klasse zu bieten
hat. Was "der Beste" jeweils genau zu bedeuten hat, sollte in der Dokumen-
tation der jeweiligen Implementierung von RobotInterface, von der man das
Port-Objekt erhalten hat, spezi�ziert sein. F�ur bisherigen Implementierungen
steht das auch in Kapitel 4.
F�ur die in ART enthaltene Implementierung f�ur den Brick bedeutet das im
Falle eines BooleanSensor, da� der entsprechende Anschlu� des Brick auf den
Standard-Modus f�ur die normalen Lego-Ber�uhrungssensoren kon�guriert wird
(zur Kon�guration von Sensoren im Brick siehe: The LEGO Group (1998) sowie
5.5. KONFIGURIERTE SENSOREN 87
alle B�ucher �uber Lego-Mindstorms in der Literaturliste usw.). Das bedeutet,
der Sensor wird nicht mit Strom versorgt und der eigentliche Sensorwert wird
in einen booleschen Wert verwandelt, welcher durch die Zahlenwerte 1 und 0
repr�asentiert wird.
F�ur diesen BooleanSensor wird aber nicht der normale Sensor-Wert ausge-
lesen, sondern der Wert des Registers mit der Standard-Umwandlung in einen
booleschen Wert. Dieses Register ist f�ur jede Sensorkon�guration g�ultig (Baum,
2001). Auf diese Weise liefert der Sensor auch dann noch einen (mehr oder we-
niger) sinnvollen Wert, falls der Anschlu� sp�ater noch umkon�guriert werden
sollte.
Diese Standard-Umwandlung ist aber im Falle einer Kon�guration des An-
schlusses f�ur einen Lichtsensor nicht geeignet, um den Zustand des Ber�uhrungs-
sensors zu erfahren. Vielmehr mu� der unver�anderte Sensorwert ausgelesen und
dann entsprechend interpretiert werden:
Listing 27: code/art/examples/SubsumptionTrustyMSDemoRobot.java
public SubsumptionTrustyMSDemoRobot(Trusty trusty,RobotInterface iface)
{super(trusty, iface);
// switch left and rightBooleanSensor tmp = leftSensor;leftSensor = rightSensor;rightSensor = tmp;
// in anticipation of LiSe the BooleanSensor’ s have to be// configured to handle the Raw value since they are connected// to the same ports as the LightSensor’ s of LiSePort leftPort = leftSensor.getPort();Port rightPort = rightSensor.getPort();leftSensor.disconnect();rightSensor.disconnect();
// put a RawSensor in-betweenRawSensor leftRaw = new RawSensor();RawSensor rightRaw = new RawSensor();leftRaw.connectWith(leftPort);rightRaw.connectWith(rightPort);leftSensor.connectWith(leftRaw.getSensorPort());rightSensor.connectWith(rightRaw.getSensorPort());
// first Range is for 0, second for 1 and third is hysteresis// these are non-standard RCX-values because Light-Sensors// can deliver values as low as 320leftSensor.setStateDecider(
new RCXRawSwitchStateDecider(new SingleRange(220),new SingleRange(0, 190),new SingleRange(190, 220)
➥
88 5. ARIADNE & ART - EIN TUTORIAL
Listing 27: code/art/examples/SubsumptionTrustyMSDemoRobot.java (Fortsetzung)
));rightSensor.setStateDecider(
new RCXRawSwitchStateDecider(new SingleRange(220),new SingleRange(0, 190),new SingleRange(190, 220)
));// a different contructor could be nice here, which only needs// one Range which is the hysteresis
}
Als erstes wird der linke mit dem rechten Sensor vertauscht, da das f�ur den
Demo-Roboter erforderlich ist.
Danach wird jeweils ein RawSensor mit dem Port verbunden, an dem die
Sensoren bisher angeschlossen waren. Der Anschlu� eines RawSensor f�uhrt da-
zu, da� der entsprechende Anschlu� am Brick auf den Raw -Modus kon�guriert
wird. Als Wert bekommt ein solcher Sensor den Inhalt des Registers f�ur den
unver�anderten Sensorwert geliefert, welches genauso wie die boolesche Umwand-
lung auch f�ur jede Kon�guration des Port g�ultig ist.
An diesen RawSensor, welcher nun (egal wie der Anschlu� sp�ater noch kon-
�guriert wird) immer den an dem Anschlu� gemessenen Wert unverarbeitet
bekommt, wird nun ihrerseits die entsprechende BooleanSensor-Instanz ange-
schlossen.
Jeder Sensor liefert mit getPort() den Port, an dem er selber angeschlos-
sen ist und mit getSensorPort() einen SensorPort, an den man weitere Sen-
soren anschlie�en kann, die dann den von ihm bereits verarbeiteten Wert be-
kommen.
Es wurde also sozusagen ein RawSensor "zwischendrin" eingef�ugt. Damit
wird erreicht, da� der BooleanSensor den Wert des Raw-Registers aus dem
Brick bekommt und selbst zu einem booleschen Wert umwandeln kann.
Die Umwandlung geschieht hier durch eine Wandlung des Sensorwertes in
den Zustand 0 oder 1, welcher dann von dem BooleanSensor normal interpre-
tiert wird (0 ist false, 1 ist true). Sie wird von der Hilfsklasse RCXRawSwitch-
StateDecider vorgenommen, welche drei Range-Instanzen bekommt. Eine f�ur
den Wert 0, eine f�ur die Hysterese (siehe z.B. Baum (2000)) und eine f�ur den
Wert 1.
Instanzen der Klasse SingleRange repr�asentieren Intervalle von (reellen)
Zahlen. Wird eine SingleRange wie in dem Beispiel nur mit einer Zahl in-
itialisiert, geht das Intervall von dieser Zahl bis Double.POSITIVE_INFINITY.
Wird sie mit zwei Zahlen initialisiert bezeichnet der erste Parameter die unte-
re Schranke und der zweite Parameter die obere Schranke eines geschlossenen
Intervalls. O�ene Intervalle sind auch m�oglich, wer mehr wissen will, schaue
sich die JavaDoc-Dokumentation der entsprechenden Range-Klassen aus den
package de.jaetzold.util an.
5.5. KONFIGURIERTE SENSOREN 89
5.5.2 Der Lego-Rotationssensor
Wenn man einen Lego-Rotationssensor ganz normal mit dem Brick verwen-
den m�ochte, ist das einfach. Man braucht nur einen CountSensor mit einem
SensorPort des entsprechenden RobotInterface zu verbinden und der Ein-
gang wird automatisch richtig kon�guriert. Der CountSensor liefert dann den
vom Brick bereits berechneten Wert.
Ein CountSensor kann aber auch f�ur beliebige Ports zum Z�ahlen benutzt
werden, auch wenn diese gar nicht speziell f�ur einen CountSensor gedacht sind.
Daf�ur mu� der CountSensor entsprechend kon�guriert werden.
Um dem CountSensor erst einmal den unverarbeiteten Sensorwert zu lie-
fern, wird dieser mit dem SensorPort eines RawSensor verbunden (welcher
nat�urlich seinerseits auch mit einem Port verbunden werden mu�). Das ge-
schieht genau so wie bei den BooleanSensor-Instanzen von Subsumption-
TrustyMSDemoRobot in Abschnitt 5.5.1 auf Seite 86:
RawSensor raw = new RawSensor();CountSensor count = new CountSensor();count.connectWith(raw.getSensorPort());
Von der Oberklasse StateSensor von CountSensor wird der vom Robot-
Interface erhaltene Wert ohne weitere Verarbeitung als Zustand (State) inter-
pretiert. Der CountSensor wiederum z�ahlt in seiner Voreinstellung den Abso-
lutwert der Di�erenz zwischen zwei Zustands-Werten. Ein StateSensor beginnt
im Zustand 0, das hei�t, kommt vom RobotInterface z.B. als erstes der Wert
3 und dann der Wert 1, hat der CountSensor bereits bis 5 gez�ahlt. Kommt
dann noch der Wert -5, ist der CountSensor bereits bei 11 mit dem Z�ahlen
angelangt.
Sowohl StateSensor als auch CountSensor haben einen Delegate der Klas-
se StateDecider (stateDecider und countDecider genannt). Der Delegate
vom StateSensor bekommt den aktuellen (letzen) state-Wert des Sensors so-
wie den neuen Wert vom RobotInterface als Parameter und berechnet einen
neuen state-Wert. Der Delegate vom CountSensor bekommt auch den aktuel-
len (letzen) state-Wert des Sensors, sowie den neuen bereits in einen state-Wert
umgewandelten Wert vom StateSensor als Parameter und berechnet die Di�e-
renz zum aktuellen count-Wert. Diese Di�erenz wird immer zu dem letzten Wert
hinzuaddiert, das hei�t, soll der count-Wert kleiner werden mu� die Di�erenz
negativ sein.10
Um nun den raw-Wert des Rotationssensors zu verarbeiten, soll dieser erst
einmal in einen von vier Zust�anden umgewandelt werden (der Rotationssensor
erzeugt immer einen Wert in einem von vier engen Wertebereichen):
Listing 28: code/art/examples/RCXRotationSensorStateDecider.java
public class RCXRotationSensorStateDecider implements StateDecider {protected StateDecider target;
➥
10Ist der Sensor auf reversed eingestellt, wird die Di�erenz abgezogen anstatt hinzugez�ahlt
90 5. ARIADNE & ART - EIN TUTORIAL
Listing 28: code/art/examples/RCXRotationSensorStateDecider.java (Fortsetzung)
public RCXRotationSensorStateDecider() {target = new RangeStateDecider(
new Range[] {null,new SingleRange(352,366),new SingleRange(520,535),new SingleRange(1020,1024),new SingleRange(770,785)});
}
public double nextStateValue(double actualState, double value) {double newState = target.nextStateValue(actualState, value);if(newState == 0) {
// The standard rcx-interpretation of such// ’ false’ values seems to simply ignore themreturn actualState;
} else {return newState;
}}
...}
Der bei der Initialisierung erzeugte RangeStateDecider liefert als Resul-
tat von nextStateValue(double,double) immer den Index der ersten Range
in dem �ubergebenen Array, die nicht null ist und die den zweiten Parame-
ter zu nextStateValue(double,double) (welcher der neu erhaltene Wert ist)
enth�alt. Enth�alt keine der Range-Instanzen denWert, so liefert ein RangeState-
Decider das Resultat 0.
Der RCXRotationSensorStateDecider wird dem CountSensor als State-
Decider-Delegate �ubergeben:
count.setStateDecider(new RCXRotationSensorStateDecider());
Die vier Zust�ande des RCXRotationSensorStateDecider werden nun von
einem weiteren StateDecider in die Di�erenz zum alten count-Wert umgewan-
delt:
Listing 29: code/art/examples/RCXRotationCountStateDecider.java
public class RCXRotationCountStateDeciderextends TransitionMatrixStateDecider
{private static int[][] countMatrix =
new int[][]{/* l\n 0, 1, 2, 3, 4 *//* 0*/ { 0, 0, 0, 0, 0},/* 1*/ { 0, 0, 1, 0,-1},/* 2*/ { 0,-1, 0, 1, 0},/* 3*/ { 0, 0,-1, 0, 1},/* 4*/ { 0, 1, 0,-1, 0}};
➥
5.5. KONFIGURIERTE SENSOREN 91
Listing 29: code/art/examples/RCXRotationCountStateDecider.java (Fortsetzung)
public RCXRotationCountStateDecider() {super(countMatrix);
}}
Die Bereiche f�ur die vier Zust�ande wurden so gew�ahlt, da� ein �Ubergang
von 1 nach 2, von 2 nach 3 usw., sowie von 4 nach 1 bedeutet, da� der count-
Wert um 1 hochgez�ahlt werden muss. Zustands�uberg�ange von 4 nach 3, 3 nach
2 usw., sowie von 1 nach 4 m�ussen eine Verringerung des count-Wertes um 1
nach sich ziehen, damit das gleiche Resultat wie im Brick erreicht wird.
Zust�ande von 0, sowie gr�o�ere Spr�unge werden ignoriert, was dem beobacht-
baren Verhalten des Brick in diesem Fall entspricht.
Zust�ande von 0 k�onnen zum Beispiel auftreten, wenn ein Ber�uhrungssensor
an den gleichen Eingang wie der Rotationssensor angeschlossen ist, weil dieser
auch Werte au�erhalb der 4 Bereiche des Rotationssensors liefern kann.
Ob gr�o�ere Spr�unge vom Brick wirklich ignoriert werden, kann nicht sicher
best�atigt werden, es ist aber so, da� zumindest ab einer Geschwindigkeit von
etwa 1000 U/min der vom Brick berechnete count-Wert nicht mehr korrekt ist.
Bei der hier vorgestellten Variante nat�urlich bereits viel fr�uher (sch�atzungsweise
so etwa im Bereich von 4-40 U/min, je nach sonstiger Rechnerauslastung).
Mehr zu Sensoren f�ur den Brick, vor allem auf Hardware-Ebene, �ndet man
unter Gasperi (1998) und in Baum et al. (2000).
5.5.3 Rotationsmessung mit den Fischertechnik-Impulsr�adern
Um mit Fischertechnik die Rotation einer Achse zu messen, benutzt man �uber-
licherweise die daf�ur vorgesehenen Impulsr�ader. Abbildung 5.7 zeigt das Im-
pulsrad von LiSe.
Abbildung 5.7: Der schwarze Taster ist zusammen mit dem Impulsrad (das ist das kleineschwarze Zahnrad in der Mitte mit nur vier Z�ahnen) so montiert, da� eine Drehungan dem Rad den Taster fortlaufend bet�atigt.
92 5. ARIADNE & ART - EIN TUTORIAL
Leider ist man damit nicht in der Lage, automatisch die Richtung, in die sich
die Achse dreht, zu bestimmen. Z�ahlt man nur die am Schalter auftretenden
Impulse, so wird der daraus resultierende Wert immer gr�o�er, egal in welche
Richtung sich die Achse dreht.
H�au�g ist solch eine Achse aber an einen Motor angeschlossen und in die-
sem Fall wei� man eigentlich, in welche Richtung sich die Achse dreht. Da-
mit nun die Z�ahlrichtung nicht immer per Hand eingestellt werden mu�, kann
man einen StateDecider wie im folgenden Beispiel als countDecider f�ur den
CountSensor verwenden:
Listing 30: code/art/examples/FTRotationCountStateDecider.java
package de.jaetzold.art.examples;...public class FTRotationCountStateDecider
extends StateDeciderLinkedStateDecider{...
public FTRotationCountStateDecider(Sensor directionSensor) {try {
setFirstDecider(new SensorStateDecider( directionSensor,true));
setSecondDecider(new BinaryOperatorStateDecider(
new BinaryOperator() {public double combine( double first,
double second){
return Math.abs(first-second);}
},true
));
setLinkDecider(new BinaryOperatorStateDecider(
new BinaryOperator() {private int seq;private int last;public double combine( double first,
double second){
if(first>0) {last = 1;
} else if(first<0) {last = -1;
} else {...
}return last*second;
➥
5.5. KONFIGURIERTE SENSOREN 93
Listing 30: code/art/examples/FTRotationCountStateDecider.java (Fortsetzung)
}},true
));
} catch(PropertyVetoException pve) {}
}}
Die Klasse FTRotationCountStateDecider stammt von der Klasse State-
DeciderLinkedStateDecider ab, welche ihrerseits drei StateDecider-Instan-
zen besitzt, an die die Entscheidung �uber den neuen Zustand delegiert wird.
Die ersten beiden Instanzen (firstDecider und secondDecider) bekom-
men die Parameter zu nextStateValue(double,double) jeweils direkt �uber-
geben. Die dritte Instanz (linkDecider), bekommt das Resultat von first-
Decider als ersten und das Resultat von secondDecider als zweiten Parameter
�ubergeben.
Bei der Initialisierung bekommt ein FTRotationCountStateDecider einen
Sensor mit (das kann auch ein Motor sein, denn die sind Sensoren f�ur ihren
Zustand), erzeugt damit einen SensorStateDecider und initialisiertmit diesem
wiederum den firstDecider der Oberklasse. Ein SensorStateDecider ist ein
StateDecider der als Resultat immer den Wert des Sensors liefert.
Als secondDecider wird ein StateDecider erzeugt, wie ihn ein Count-
Sensor auch als Voreinstellung benutzt. Die absolute Di�erenz zwischen ak-
tuellem Zustand (Parameter first zu combine(double,double) des Binary-
Operator) und dem neuen Wert (Parameter second, der dann sinnvollerweise
auch ein Zustand sein sollte) liefert dieser als Resultat.
Verbunden werden die Resultate der beiden ersten StateDecider �uber den
dritten, welcher { je nachdem ob der Sensorwert gerade positiv oder negativ ist {
die Di�erenz unver�andert als Resultat �ubergibt oder vorher mit -1 multipliziert.
Ein Sonderfall tritt auf, wenn der Sensor den Wert 0 liefert. Damit der
CountSensor in diesem Fall trotzdem z�ahlt, wird einfach angenommen, da�
in die gleiche "Richtung" wie beim letzten Aufruf gez�ahlt werden soll. Damit
wird dem Umstand Rechnung getragen, da� die Events von dem Sensor f�ur das
Impulsrad zu einem Zeitpunkt ankommen k�onnen, zu dem der Motor bereits
wieder aus ist.
Diesen StateDecider kann man nun auf einfache Weise verwenden, um ein
Impulsrad mit einem Motor zu koppeln:
CountSensor count = new CountSensor();Sensor motor = new Motor();count.setCountDecider(new FTRotationCountStateDecider(motor));
Nat�urlich m�ussen motor und count auch noch mit einem Port verbunden
werden. Das d�urfte mittlerweile aber klar sein.
94 5. ARIADNE & ART - EIN TUTORIAL
5.6 LiSe
Die Programmierung von LiSe (und im folgenden Abschnitt auch Ariadne) wird
l�angst nicht so ausf�uhrlich behandelt wie das bei den bisherigen Beispielen der
Fall war. Wer daran weitergehendes Interesse hat, sollte inzwischen auch in
der Lage sein, den Code der Beispielimplementierungen selbst zu verstehen.
Das Konzept ist im Grunde das gleiche wie auch schon bei DriveTrain und
Trusty.
Trotzdem gibt es einige Besonderheiten und Elemente des ART, die vorher
noch nicht zu Tage getreten sind, so da� auf diese hier noch eingegangen wird.
LiSe ist besonders interessant als Beispiel f�ur die Verwendung von Sensoren.
Ein BooleanSensor wird f�ur den Endabschalter verwendet, ein AngleSensor
f�ur die Position des Turms, sowie zwei Objekte der Klasse LightSensor mit
denen die Taschenlampe "gefunden" werden kann.
Zur Bewegung des Turmes mit den Lichtsensoren benutzt LiSe einen Servo.
Nun bieten sowohl der Brick, als auch das Intelligent Interface von Fischertech-
nik keine Anschlu�m�oglichkeit f�ur echte Servos. Da der Turm sich aber nicht
beliebig weit drehen l�a�t und LiSe ihn nicht nur bewegen, sondern auch �uber die
(absolute) Position Bescheid wissen m�ochte, w�are ein Servo eigentlich besonders
gut f�ur die Turmbewegung geeignet.
Um diese h�au�g verwendete Konstruktionsweise (Motor mit Rotationssensor
und Endabschalter) besonders einfach steuerbar zu machen, kann die Klasse
Servo die Funktion eines echten Servo simulieren. Wird eine Servo-Instanz mit
einem Port verbunden, der keine echten Servos unterst�utzt, so mu� man sie nur
mit
� einem AngleSensor zur Positionsmessung
� einem BooleanSensor zur Kalibrierung der Position
� einer "Richtung", in die sich der Servo (Servo ist eine Unterklasse von
Motor) drehen soll, um die Position des BooleanSensor anzufahren
� einer unteren und einer oberen Schranke f�ur den Bewegungsbereich des
Servo
versorgen und kann sie danach ganz normal wie einen Servo verwenden, das
hei�t, zumindest einigerma�en so wie einen Servo. Im allgemeinen wird ein
Servo wesentlich pr�aziser und schneller als diese Simulation mit einem Motor
usw. sein.
Andere Implementierungen von RobotInterface k�onnten aber durchaus
auch echte Servos unterst�utzen. Was dabei unter anderem zu beachten ist be-
schreibt Abschnitt 6.
Die Beispielimplementierung LiSeSimpleAlgorithm verlangt eine Imple-
mentierung des folgenden Interface zur Initialisierung:
Listing 31: code/art/examples/LiSeSimpleAlgorithm.java
public interface Robot {➥
5.6. LISE 95
Listing 31: code/art/examples/LiSeSimpleAlgorithm.java (Fortsetzung)
public double getMaxRightAngle();public double getMaxLeftAngle();public double getSearchSpeed();public double getFollowSpeed();public Servo getTurnServo();
}
Die Werte maxRightAngle und maxLeftAngle bezeichnen die Grenzen des
Bereiches, in dem gesucht werden soll. Au�erdem wird dar�uber festgelegt, ob
negative Winkel links oder rechts sind.
Mit searchSpeed und followSpeed wird die Geschwindigkeit der Tower-
bewegung zum Suchen des Lichtes und um einem bereits gefundenen Licht zu
folgen angegeben. Ich bin mir nicht sicher, ob echte Servos normalerweise eine
Regelung der Geschwindigkeit mit der sie sich bewegen zulassen. Die �uber die
Motoren simulierten tun das auf jeden Fall.
Es macht aber nichts, hier eine Geschwindigkeit zu verwenden, denn nach
der De�nition von ART ist jeder Servo auch ein Motor (Die Klasse Servo ist
eine Unterklasse von Motor) und bietet daher die M�oglichkeit, eine Geschwin-
digkeit anzugeben. Genau genommen ist es sogar so, da� die Geschwindigkeit
erst bei einem StepperMotor angegeben werden kann und bei einem Motor
nur die Kraft (power), das hat aber bei den Implementierungen f�ur Lego und
Fischertechnik die gleichen Auswirkungen, da sie StepperMotor nicht direkt
unterst�utzen.11
Damit k�onnen die vom Interface LiSe (siehe Sourcecode) vorgeschriebenen
Methoden folgenderma�en implementiert werden:
Listing 32: code/art/examples/LiSeSimpleAlgorithm.java
private boolean searchRight;public synchronized void seeLeft() {
servo.setSpeed(robot.getFollowSpeed());servo.setValue(robot.getMaxLeftAngle());searchRight = false;setState(SEE LEFT);
}public synchronized void seeRight() {
servo.setSpeed(robot.getFollowSpeed());servo.setValue(robot.getMaxRightAngle());searchRight = true;setState(SEE RIGHT);
}public synchronized void search() {
// important for direction-change when reaching MaxAnglesetState(SEARCH);
if(servo.isPositionedAt(robot.getMaxRightAngle())) {➥
11F�ur weitere Informationen, insbesondere zu StepperMotor, dient die JavaDoc API-
Dokumentation von ART.
96 5. ARIADNE & ART - EIN TUTORIAL
Listing 32: code/art/examples/LiSeSimpleAlgorithm.java (Fortsetzung)
searchRight = false;} else if(servo.isPositionedAt(robot.getMaxLeftAngle())) {
searchRight = true;}
servo.setSpeed(robot.getSearchSpeed());if(searchRight) {
servo.setValue(robot.getMaxRightAngle());} else {
servo.setValue(robot.getMaxLeftAngle());}
}public synchronized void seeCenter() {
servo.keepCurrentPosition();setState(SEE CENTER);
}public synchronized void stop() {
servo.keepCurrentPosition();setState(STOP);
}public synchronized boolean isSeeing() {
switch(getState()) {case STOP:case SEARCH:
return false;default:
return true;}
}public AngleSensor getAngleSensor() {
return servo.getAngleSensor();}
Die Methoden seeLeft() und seeRight() implementieren das Verhalten,
einem Licht das bereits erkannt wurde nach links, bzw. rechts zu folgen. In der
Variable searchRight wird die letzte Richtung, in der das Licht gesehen wurde
festgehalten, damit in dieser Richtung von der Methode search() auch zuerst
gesucht wird.
Die Methoden seeCenter() und stop() halten die (eventuelle) Bewegung
des Servos an und stellen ihn auf die aktuelle Position ein. Sie unterscheiden
sich nur in dem Zustand von LiSe.
Der Zustand, ob LiSe im Moment �uberhaupt ein Licht "sieht", kann man
�uber isSeeing() erfragen. Der R�uckgabewert gibt damit an, ob der Wert des
AngleSensor von LiSe als die Position eines Lichtes interpretiert werden kann.
Die Methode search() bewegt den Tower in der aktuellen Suchrichtung bis
an den Rand des Suchbereiches. Damit LiSe dort umdreht und den Tower an
den anderen Rand bewegt, ist (indirekt) bei dem Servo ein SensorListener
registriert, welcher search() bei Erreichen des Randes im SEARCH-Zustand ein-
fach erneut aufruft:
5.6. LISE 97
Listing 33: code/art/examples/LiSeSimpleAlgorithm.java
final BooleanSensor borderSensor = servo.getPositionReachedSensor();borderSensor.addSensorListener(
new SensorListener() {public void processEvent(SensorEvent se) {
synchronized(LiSeSimpleAlgorithm.this) {boolean reached =
borderSensor.convertToBoolean(se.getValue());if(debug.debug) {
debug.printInfo("reached=" +reached+", value=" +se.getValue()+", state=" +state);
}
if(reached && (state == SEARCH)) {search();
}}
}}
);
Dies zeigt ein besonderes Konzept: Sensoren in ART stehen immer f�ur einen
Wert der eindimensional, also z.B. in einer Variable vom Typ double, abgebildet
werden kann. Ist das nicht m�oglich, wird von dem eigentlichen Sensor nur eine
einzige Dimension abgebildet und f�ur alle weiteren kann man spezielle Sensoren
von dem eigentlichen Sensor bekommen. Dies ist bei Servo z.B. f�ur den Zustand,
ob er sich an der zugewiesenen Position be�ndet der Fall. Da der Sensorwert
des Servo selbst den Bewegungszustand des Servo repr�asentiert, um mit Motor
kompatibel zu bleiben, gibt es f�ur die Position einen weiteren Sensor, den man
�uber getAngleSensor() erhalten kann.
Die Sensoren, sowie der Servo werden (wie �ublich) in einer Basisimplemen-
tierung des Interface (LiSeSimpleAlgorithm.Robot) hinterlegt. Entsprechende
Unterklassen f�ur den Beispielroboter aus Lego, sowie f�ur den aus Fischertechnik
erzeugen, verbinden und kon�gurieren diese dann auf geeignete Weise.
In der Fischertechnik-Variante von LiSe sieht das folgenderma�en aus:
Listing 34: code/art/examples/LiSeSimpleFTDemoRobot.java
public class LiSeSimpleFTDemoRobot extends LiSeSimpleBaseRobot {public LiSeSimpleFTDemoRobot(RobotInterface ft) {
// fake parameters and set them afterwardssuper(null, 0, 0, 0, 0);
Port endSwitchPort = ft.getPort("E7");Port countSensorPort = ft.getPort("E8");Port motorPort = ft.getPort("M4");
// gear ratio Tower:Sensor = 1:6➥
98 5. ARIADNE & ART - EIN TUTORIAL
Listing 34: code/art/examples/LiSeSimpleFTDemoRobot.java (Fortsetzung)
double countsPerFullTurn = 48;AngleSensor turnSensor = new AngleSensor(countsPerFullTurn);
int endSwitchDirection = 1;BooleanSensor endSwitch = new BooleanSensor();
// Angles are measured in full turnsdouble positiveBorder = 21/48.0;double negativeBorder = -20/48.0;double endSwitchAngle = positiveBorder;
maxRightAngle = 0.33;maxLeftAngle = -0.25;searchSpeed = 1;followSpeed = 0.33;
turnServo = new Servo(turnSensor,endSwitch,endSwitchAngle,positiveBorder,negativeBorder,endSwitchDirection);
turnSensor.setCountDecider(new FTRotationCountStateDecider(turnServo));
endSwitch.connectWith(endSwitchPort);turnSensor.connectWith(countSensorPort);turnServo.connectWith(motorPort);
// let the servo be a bit more sloppy in positioning// (that (mostly) prevents it from occasionally moving// back and forth around the desired position)turnServo.setPrecision(turnServo.getPrecision()*2);
}...
}
Die nicht deklarierten Variablen maxRightAngle, maxLeftAngle, search-
Speed, followSpeed und turnServo sind die, die von einem LiSeSimple-
Algorithm.Robot geliefert werden sollen und stammen aus der Basisklasse
LiSeSimpleBaseRobot, wo auch die zugeh�origen get-Methoden implementiert
sind.
Die Port-Objekte f�ur den Anschlu� des Endabschalters, des Impulsrades
und des Motors werden hier auf eine sehr intuitive Art von dem Interface erfragt.
Da an dieser Stelle eigentlich sowieso nur ein RobotInterface f�ur Fischertech-
nik benutzt werden soll, kann man die Ports auch anhand ihrer Bezeichnung
identi�zieren.
Welche Bezeichnungen g�ultig sind und mit welchem Anschlu� an der Hard-
ware sie korrespondieren, ist in der Dokumentation f�ur die jeweilige Implemen-
tierung von RobotInterface beschrieben. Im Allgemeinen entspricht sie einem
5.6. LISE 99
eventuellen Aufdruck auf der Hardware oder der Bezeichnung der Anschl�usse,
wie sie in der Dokumentation der Hardware verwendet werden.
Auf diese Weise ist die Zuordnung eines von einem RobotInterface erhal-
tenen Port zu einem Anschlu� an der Hardware am intuitivsten (und damit
potentiell nicht fehlerbehaftet) handhabbar.
Als n�achstes wird der AngleSensor mit der Anzahl von Impulsen, die einer
ganzen Umdrehung entsprechen initialisiert. Damit kann der count-Wert der
Oberklasse CountSensor auch ohne spezielle Unterst�utzung durch das Robot-
Interface in einen Winkel umgerechnet werden. Das Standardma� f�ur Winkel
in ART sind ganze Umdrehungen von 360Æ.
Schlie�lich wird der Servo mit dem Endabschalter, dem Impulsrad und den
sonstigen (vom Robotermodell abh�angigen) n�otigen Werten initialisiert.
Damit turnSensor auftretende Impulse je nach der aktuellen Bewegungs-
richtung des Servo korrekt zum count-Wert dazuz�ahlt oder abzieht, wird er
au�erdem mit einer Instanz des im Abschnitt 5.5 auf Seite 86 besprochenen
FTRotationCountStateDecider als Delegate zum Z�ahlen versorgt.
Als Letztes wird der mangelnden Geschwindigkeit bei der Auslieferung von
Events Rechnung getragen, indem die Pr�azision des Servo halbiert wird. Die
Eigenschaft precision repr�asentiert die Genauigkeit in Form des erwartbaren
Fehlers des Sensorwertes und ist daher bei einer geringeren Pr�azision gr�o�er.
Wenn man diese Einstellung nicht vornimmt, kommt es h�au�ger vor, da�
der Motor erst zum Stillstand kommt, wenn er bereits �uber die gew�unschte
Position hinaus gefahren ist. Das registriert der Servo nat�urlich und bewegt
ihn wieder ein St�uck zur�uck. Das kann wieder zu weit sein und sich theoretisch
endlos hinziehen. Es w�are gut, wenn Servo in der N�ahe der gew�unschten Posi-
tion die Geschwindigkeit drosseln w�urde, um diesen E�ekt zu vermeiden, diese
Funktionalit�at ist in Servo aber (noch) nicht integriert.
Eine entsprechende Implementierung von LiSeSimpleAlgorithm.Robot exi-
stiert auch f�ur die Lego-Variante von LiSe. Diese wird hier nicht gezeigt, da sie
nichts wirklich Neues enth�alt.
Nat�urlich kann auch diese Beispielimplementierung wieder auf dem �ubli-
chen Weg getestet werden (was darunter zu verstehen ist wurde am Ende von
Abschnitt 5.2 und in Abschnitt 5.3 auf Seite 73 n�aher beschrieben).
SubsumptionLiSe
Es fehlt noch die Integration der Lichtsensoren, damit LiSe auch automa-
tisch einem Licht folgen kann. Das wird auf die gleiche Weise bewerkstelligt
wie schon bei SubsumptionTrusty. Nur ist es diesmal mit etwas mehr Auf-
wand verbunden, die n�otigen BooleanSensor-Objekte f�ur die Initialisierung
des SchedulerTask mit den unterschiedlichen Verhaltensweisen zu erzeugen.
Diese m�ussen beide Lichtsensoren in Betracht ziehen. Stellvertretend wird hier
nur eine Konstruktion gezeigt:
Listing 35: code/art/examples/SubsumptionLiSe.java
// behavior for seeing light on the left side➥
100 5. ARIADNE & ART - EIN TUTORIAL
Listing 35: code/art/examples/SubsumptionLiSe.java (Fortsetzung)
final LightSensor leftSensor = robot.getLeftSensor();final LightSensor rightSensor = robot.getRightSensor();BinaryOperator seeLeftOperator =
new BinaryOperator() {public double combine( double leftValue,
double rightValue) {// at least one of them has to ’ see’ somethingif(leftValue >= leftSensor.getMeasurableMin()
|| rightValue >= rightSensor.getMeasurableMin()) {// Standard BooleanConversion: only 0 is falsereturn leftValue>rightValue ? 1 : 0;
}return 0;
}};
CombinedSensor seeLeftSensor = new CombinedSensor();seeLeftSensor.connectWith(leftSensor.getSensorPort());seeLeftSensor.setOperator(seeLeftOperator);seeLeftSensor.setOperand(rightSensor);
BooleanSensor seeLeftActivator = new BooleanSensor();seeLeftActivator.connectWith(seeLeftSensor.getSensorPort());
seeLeftTask = new Task() {public Event perform() {
lise.seeLeft();return null;
}};scheduler.addBehaviorFor(new Double(1),
seeLeftActivator,seeLeftTask);
Ein CombinedSensor verkn�upft den Wert, den er von seinem eigenen An-
schlu� bekommt mit dem Wert, den er von einem weiteren Sensor (operand)
bekommt, �uber einen BinaryOperator (operator).
Ein BinaryOperator ist hier als lokale anonyme innere Klasse realisiert. Die
Methode combine(double,double) bekommt als ersten Parameter den Wert
des linken LightSensor und als zweiten Parameter den des rechten Light-
Sensor und liefert 1, falls einer der beiden Werte gr�o�er ist, als der Wert der Ei-
genschaft measurableMin des jeweiligen LightSensor und au�erdem der Wert
f�ur links gr�o�er ist als der Wert f�ur rechts.
Die Werte measurableMin und measurableMax eines LightSensor bezeich-
nen die Grenzen des Bereichs in dem der LightSensor Werte liefert, die (im
Rahmen seiner precision) genau sind. Im Prinzip sollte LightSensor inner-
halb dieses Bereiches Werte liefern, die dem tats�achlich gemessenen Lux-Wert
entsprechen.
In combine() wird measurableMin daf�ur genutzt, um herauszu�nden, ob
der Sensor �uberhaupt ein st�arkeres Licht, wie z.B. von einer Taschenlampe, regi-
5.7. ARIADNE 101
striert hat. Daf�ur ist es notwendig, da� measurableMin auch den momentanen
Lichtverh�altnissen entspricht. Man kann diesen Wert, genauso wie measurable-
Max f�ur einen Lichtsensor, auch festlegen { LightSensor bietet daf�ur eine Me-
thode. Wird der Wert nicht festgelegt, liefert LightSensor das, was das Robot-
Interface, an dem er �uber einen Port angeschlossen wurde, liefert, sofern dieses
einen LightSensor �uberhaupt direkt unterst�utzt. Ist beides nicht der Fall, ist
der Wert von beiden Eigenschaften 0.
An den CombinedSensormu� nun nur noch ein BooleanSensor angeschlos-
sen werden. Dieser konvertiert in der Voreinstellung den Wert 0 zu false und
alle anderen Werte zu true, was bei der Implementierung des BinaryOperator
ein paar Zeilen dar�uber ausgenutzt wurde. Man kann einem BooleanSensor
auch einen Delegate BooleanConversion f�ur die Konvertierung mitgeben, falls
man das m�ochte.
Die Erzeugung des Task und das Hinzuf�ugen der Verhaltensweise zu dem
SchedulerTask scheduler geschieht ansonsten auf die gleiche Weise wie schon
bei SubsumptionTrusty und wurde dort bereits erkl�art. Neu bei Subsumption-
LiSe ist nur die Art und Weise wie der BooleanSensor, der das Verhalten
aktiviert, erzeugt bzw. angeschlossen wurde.
5.7 Ariadne
Nun ist es soweit: Trusty und LiSe werden miteinander verkn�upft, damit sie
zusammen Ariadne ergeben. Die Verkn�upfung soll im Ergebnis dazu f�uhren, da�
Ariadne auf ein Licht zuf�ahrt und dabei eventuellen Hindernissen ausweicht.
Um dies zu erreichen, kann Ariadne sich an dem Winkel, in dem ein Licht
ausgemacht wurde orientieren, indem der NormalizedDriveTrain (der auch
von Trusty verwendet wird) um den entsprechenden Winkel gedreht wird, so
da� danach eine Bewegung nach vorne, in Richtung des Lichts geht.
Wieder beginnt das Beispiel mit einem interface Ariadne, das die Schnitt-
stelle zu der F�ahigkeit von Ariadne, sich an einem Winkel zu orientieren, de�-
niert:
Listing 36: code/art/examples/Ariadne.java
package de.jaetzold.art.examples;
public interface Ariadne {public void orientByAngle(double angle);
...}
Die Beispielimplementierung AriadneSimpleAlgorithm wird (wieder �uber
ein Robot-Interface) mit einem NormalizedDriveTrain, sowie einem Winkel
initialisiert. Dieser Winkel stellt die Richtung dar, in die Ariadne gerne aus-
gerichtet w�are. Die Methode orientByAngle(double) macht nun nichts wei-
ter, als den �ubergebenen Wert als die momentane Orientierung aufzufassen,
und daher den NormalizedDriveTrain um die entsprechende Di�erenz zu dem
Winkel, mit dem AriadneSimpleAlgorithm initialisiert wurde, zu drehen:
102 5. ARIADNE & ART - EIN TUTORIAL
Listing 37: code/art/examples/AriadneSimpleAlgorithm.java
public void orientByAngle(double angle) {setState(ORIENTING);if((angle - orientationAngle)*leftDirection > 0) {
driveTrain.leftSpin(Math.abs(angle - orientationAngle));} else if((angle - orientationAngle)*leftDirection < 0) {
driveTrain.rightSpin(Math.abs(angle - orientationAngle));}setState(ORIENTED);
}
Der Wert leftDirection stammt auch von AriadneSimpleAlgorithm.
Robot und sagt aus, ob negative Winkelwerte links oder rechts sind.
Das m�u�te inzwischen alles sehr einfach anmuten, da das Prinzip immer
noch dasselbe ist und AriadneSimpleAlgorithm nichts enth�alt, was nicht schon
mal behandelt worden ist. Daher wird auch keine der Robot-Implementierungen
(AriadneSimpleMSDemoRobot usw.) gezeigt. Diese sind aber vorhanden, so da�
man bei Interesse selbst nachschauen kann.
Interessanter ist die Steuerung mit SubsumptionAriadne, welche als Unter-
klasse von SubsumptionTrusty realisiert wurde. Das zugeh�orige Robot-Inter-
face ist auch eine Erweiterung von SubsumptionTrusty.Robot und kann daher
einfach an den entsprechenden Konstruktor der Oberklasse �ubergeben werden:
Listing 38: code/art/examples/SubsumptionAriadne.java (Ref. in Listing 40 S. 103)
public interface Robot extends SubsumptionTrusty.Robot {public Ariadne getAriadne();public LiSe getLiSe();public long getMinOrientInterval();
}
Der Konstruktor von SubsumptionAriadne macht nicht mehr, als eine wei-
tere Verhaltensweise in den SchedulerTask der Oberklasse einzuf�ugen, diese ist
aber etwas schwieriger zu realisieren, da die Methode orientByAngle(double)
diesmal einen Parameter braucht:
Listing 39: code/art/examples/SubsumptionAriadne.java (Ref. in Listing 40 S. 103)
double angle;long lastOrientMillis;boolean oneShot;Object oneShotMonitor = new Object();Task behavior = new Task() {
public Event perform() {double localAngle;synchronized(oneShotMonitor) {
// be sure to only orient once by each angleif(!oneShot) {
return null;}lastOrientMillis = System.currentTimeMillis();
➥
5.7. ARIADNE 103
Listing 39: code/art/examples/SubsumptionAriadne.java (Fortsetzung)
localAngle = angle;oneShot = false;
}ariadne.orientByAngle(localAngle);// since it depends on some values// (lastOrientMillis, oneShot) it is better// not to assume that there will be an event// from liseAngle soon enoughactivator.update();
return null;}
};
Dieser Parameter wird in der Variable angle gespeichert, wenn der zugeh�ori-
ge BooleanSensor die Verhaltensweise aktivieren m�ochte. Die Orientierung soll
aber nur genau einmal statt�nden und danach erst neu aktiviert werden. Daher
ruft der Task behavior die Methode orientByAngle(double) nur dann auf,
wenn oneShot true ist und setzt oneShot danach auf false. Damit behavior
also �uberhaupt etwas macht, mu� der BooleanSensor au�er selbst den Wert
true anzunehmen auch noch oneShot auf true setzen:
Listing 40: code/art/examples/SubsumptionAriadne.java
public class SubsumptionAriadne extends SubsumptionTrustyimplements Ariadne,
Trusty{
protected Ariadne ariadne;public SubsumptionAriadne(final Robot robot) {
super(robot);this.ariadne = robot.getAriadne();final LiSe lise = robot.getLiSe();final AngleSensor liseAngle = lise.getAngleSensor();
// construct orient-behavior// behavior and activator need to exchange informationSchedulerTask.Entry entry = new SchedulerTask.Entry() {
<see Listing 39 on page 102>BooleanSensor activator = new BooleanSensor();{
activator.connectWith(liseAngle.getSensorPort());activator.setBooleanConversionDelegate(
new BooleanConversion() {public boolean convertToBoolean(double value) {
synchronized(oneShotMonitor) {// only if lise sees something at all:if(!lise.isSeeing()) {
return false;}
// Future Idea: don’ t orient if lise sees➥
104 5. ARIADNE & ART - EIN TUTORIAL
Listing 40: code/art/examples/SubsumptionAriadne.java (Fortsetzung)
// light inthe direction we already are// or only orient if the seeing-angle// differs enough from the orient-to-angle (0)
if((System.currentTimeMillis()- lastOrientMillis) >=
robot.getMinOrientInterval()){
angle = value;oneShot = true;
}return oneShot;
}}public boolean isCacheable() {
return false;}
});
}...
};// insert orient-behavior into scheduler of superclass:scheduler.add(entry);
}<see Listing 38 on page 102>...
}
Der Roboter soll nicht st�andig neu orientiert werden, sondern Subsumption-
Trusty soll zwischendurch auch ein wenig Zeit zum Fahren bekommen. Daher
wird der BooleanSensor activator nur dann true, wenn der letzte Aufruf von
orientByAngle(double) lange genug her ist. Welcher Zeitraum hier sinnvoll
ist, h�angt vom Roboter und der Implementierung von Trusty ab und ist daher
auch eine Eigenschaft des Robot-Interface (minOrientInterval).
Der Austausch zwischen activator und behavior �uber die Variablen one-
Shot, angle und lastOrientMillis erfordert, da� diese f�ur beide Objekte
sichtbar sind. Daher sind Task, BooleanSensor und Comparable in einem
SchedulerTask.Entry zusammengefa�t und werden als dieser gemeinsam an
scheduler �ubergeben.
Die neu hinzugef�ugte Verhaltensweise bekommt die Priorit�at 0.5. Das ist
niedriger als die Priorit�aten von SubsumptionTrusty f�ur das Ausweichen, aber
h�oher als f�ur das geradeaus Fahren.
Die Implementierung von SubsumptionAriadne.Robot gestaltet sich sehr
einfach, da nur die in den vorausgegangenen Abschnitten besprochenen Bei-
spielalgorithmen mit den jeweiligen Robot-Implementierungen erzeugt werden
m�ussen. Eine weitere Kon�guration ist (bis auf minOrientInterval) nicht
mehr notwendig. Aus diesem Grund wird auf den Abdruck hier verzichtet.
Nat�urlich kann auch SubsumptionAriadne auf die �ublicheWeise durch einen
Aufruf wie zum Beispiel
5.7. ARIADNE 105
$ java de.jaetzold.art.examples.SubsumptionAriadne
Successfully initialized LEGO-Mindstorms Interface, found onserialport COM1$
auf die �ubliche Weise getestet werden. Die vorhandenen Beispiel-Imple-
mentierungen SubsumptionAriadneFTDemoRobot sowie SubsumptionAriadne-
MSDemoRobot erzeugen dabei ein (recht gro�es) Fenster, in dem die "State"- und
"Commands"-Views f�ur NormalizedDriveTrain, Trusty, LiSe sowie Ariadne
enthalten sind (siehe Abbildung 5.8. Damit SubsumptionAriadne loslegt, mu�
man wie bei SubsumptionTrusty auch, auf den Knopf move dr�ucken, was da-
zu f�uhrt, da� bei SubsumptionAriadne (die ja ein Trusty ist) der Thread f�ur
Subsumption gestartet wird.
Abbildung 5.8: Testfenster f�ur die Komponenten von SubsumptionAriadne
106 5. ARIADNE & ART - EIN TUTORIAL
6
Implementierung eines
RobotInterface
In der aktuellen Version von ART sind "Treiber" f�ur das Intelligent Interface
von Fischertechnik und den Brick aus dem Robotics Invention System von Le-
go implementiert. Das parallele Interface von Fischertechnik, der Scout oder
der Cybermaster sowie die Ansteuerung des Brick �uber den neuen Tower mit
USB-Anschlu�, wie er im aktuellen RIS 2.0 enthalten ist, wird nicht direkt
unterst�utzt.
Dieses Kapitel zeigt wie eine solche Unterst�utzung implementiert wird. Die
M�oglichkeiten dazu sind nat�urlich nicht auf die eben genannten Beipiele be-
schr�ankt, sondern ART wurde mit dem Anspruch entwickelt, m�oglichst jede
Form von Roboter-Hardware unterst�utzen zu k�onnen.
In ART besteht ein solcher Treiber im wesentlichen aus einer Implementie-
rung des Java-Interface RobotInterface und einer geeigneten Menge von Port
und SensorPeer-Klassen. Ein RobotInterface repr�asentiert aus der Sicht von
ART ein logisches Hardware-Interface. Die komplette Treiberimplementierung
kann trotzdem aus mehreren Klassen bestehen und es ist auch m�oglich, da�
eine Implementierung mehrere echte Hardware-Interfaces nur logisch als eine
Einheit repr�asentiert. Genau genommen mu� es sich nicht einmal um echte
Hardware handeln. Zu Testzwecken z.B., kann es sogar sehr n�utzlich sein, wenn
die Roboter-"Hardware" nur virtuell im Computer existiert, denn so werden
manche Tests einfacher, billiger und vor allem risikoloser. Es kann in solch ei-
nem Fall keine teure Roboter-Hardware zerst�ort werden, wenn der zu testende
Steuerungsalgorithmus Fehler enth�alt.
6.1 Ein virtuelles RobotInterface
Die Implementierung eines virtuellen RobotInterface dient in diesem Kapitel
als Beispiel. Es ist sehr einfach aufgebaut und nicht dazu gedacht, die Low-
Level Ansteuerung von Roboterhardware z.B. �uber den seriellen Port in Java
zu beschreiben. Das Beispiel zeigt lediglich, wie sich eine solche Ansteuerung in
ART integrieren l�a�t und worauf man dabei achten mu�.
Im allgemeinen wird man z.B. mit mehreren Threads hantieren m�ussen und
107
108 6. IMPLEMENTIERUNG EINES ROBOTINTERFACE
Abbildung 6.1: Screenshot der Ober �ache von AWTRobotInterface. Rechts sind dieTextfelder f�ur die Ausgabe (Actuatoren) zu sehen, die Werte wurden durch den Steue-rungsalgorithmus von Trusty gesetzt, der "Roboter" dreht sich also gerade.
Byte-Str�ome codieren und decodieren. Die hier gezeigte Beispielimplementie-
rung AWTRobotInterface nutzt zur Ein- und Ausgabe (als Gegenst�uck zu Sen-
soren und Aktuatoren) einfache Textfelder in einem Fenster des AWT. Sensor-
werte tr�agt man schlicht in das entsprechende Textfeld ein und �uber die Text-
felder f�ur die Aktuatoren kann man deren Zustand beobachten. Es mu� nicht
einmal ein eigener Thread erzeugt werden, der die Events zustellt, denn ein sol-
cher existiert im AWT schon. Ein ActionEvent der von einem der Textfelder
kommt wird entsprechend von dem zugeh�origen SensorPeer als SensorEvent
an die angeschlossenen Sensor-Instanzen weitergeleitet.
Bei einer Implementierung f�ur eine echte Roboterhardware ist es nicht un-
bedingt empfehlenswert, einen der Threads, die mit der Interface-Hardware di-
rekt kommunizieren, auf diese Weise zu verwenden. Der Thread, der die Events
zustellt, wird sozusagen "in die freie Wildbahn" hinausgelassen und wird be-
liebigen Code ausf�uhren, �uber den man, als Programmierer des entsprechenden
RobotInterface, keine Kontrolle mehr hat. Die Implementierungen der Klas-
sen de.jaetzold.art.platform.fischertechnik.Fischertechnik und de.
jaetzold.art.platform.lego.Mindstorms nehmen eine solche Trennung der
Threads vor, bei Interesse empfehle ich dem Leser, direkt in die entsprechenden
Quelldateien hineinzusehen. F�ur die Erkl�arung, wie ein neues RobotInterface
implementiert und in ART integriert werden kann, ist dies nicht notwendig.
Listing 41: code/art/examples/AWTRobotInterface.java
package de.jaetzold.art.examples;
// lots of imports...
public class AWTRobotInterface extends RobotInterfaceAdapter {private static Debug debug =
new Debug("de.jaetzold.art.platform.awt.AWTRobotInterface");
protected TextField in0 = new TextField("0.0");➥
6.1. EIN VIRTUELLES ROBOTINTERFACE 109
Listing 41: code/art/examples/AWTRobotInterface.java (Fortsetzung)
protected TextField in1 = new TextField("0.0");protected TextField out0 = new TextField("0.0");protected TextField out1 = new TextField("0.0");
// RobotInterfaceDefinition:<see Listing 46 on page 117>
// Factory-Method for creating an AWTRobotInterface:<see Listing 50 on page 121>
// Factory-Methods for creating the Peers:<see Listing 48 on page 119>
// isConnected:<see Listing 43 on page 112>
// isAlive:<see Listing 49 on page 120>
// Factory-Methods for the Ports:<see Listing 47 on page 118>
// Inner classes for the Ports:<see Listing 45 on page 114>
// Inner class for the SensorPeer:// maybe put outside of class because it is of more general use?
<see Listing 42 on page 110>
// Inner class for the ActuatorPeer:<see Listing 44 on page 113>
}
Listing 41 auf der vorherigen Seite zeigt den groben K�orper der Beispielim-
plementierung AWTRobotInterface. Neben dem eigentlichen RobotInterface
m�ussen aber auch noch Port- und SensorPeer-Klassen implementiert werden.
Diese sind f�ur dieses Beispiel alle als innere Klassen von AWTRobotInterface
realisiert worden. Die Verweise in Listing 41 f�uhren unter anderem zu deren
Implementierungen.
AWTRobotInterface stammt von RobotInterfaceAdapter ab. Das ist zwar
nicht notwendig, da nur das Java-Interface RobotInterface implementiert wer-
den m�u�te, aber diese Basisklasse enth�alt Hilfsmethoden in der Behandlung von
seriellen Ports. Serielle Ports werden f�ur das Beispiel nicht ben�otigt. Interface-
Hardware mit seriellem Anschlu� ist jedoch nicht un�ublich und in den Imple-
mentierungen f�ur Lego und Fischertechnik wird von den Hilfsmethoden Ge-
brauch gemacht. Ansonsten enth�alt RobotInterfaceAdapter bereits imple-
mentierte Methoden, um Port-Objekte des RobotInterface �uber ein bestimm-
tes Identi�er -Object (siehe Dokumentation von Port.conformsTo(Object))
oder einen Index, im Stil einer indexed property von JavaBeans, auszuw�ahlen.
Die Technik, sowohl ein Java-Interface als auch eine implementierende Ba-
110 6. IMPLEMENTIERUNG EINES ROBOTINTERFACE
sisklasse zu verwenden, wird unter anderem in Flanagan (1999, S. 115) vorge-
schlagen. Sie wird zum Beispiel auch bei den { in der Java 2 Plattform neuen
{ Collection-Klassen angewandt. Ein Problem von Interfaces in Java ist, da�
man an ihnen kaum etwas �andern kann, ohne implementierende Klassen anpas-
sen zu m�ussen. Daher hat ein zwischengeschalteter Adapter einen weiteren Vor-
teil: Wenn das Java-Interface ge�andert wird, dann reicht es (unter Umst�anden)
den Adapter zu �andern und alle davon abgeleiteten Klassen funktionieren sofort
wieder, auch mit dem ge�anderten Java-Interface.
6.2 Die Peers
Damit das Beispiel-RobotInterface auch ausreicht, um z.B. die Trusty-Algo-
rithmen darauf laufen zu lassen, mu� es mindestens zwei Anschl�usse f�ur Senso-
ren und zwei f�ur Aktuatoren zur Verf�ugung stellen. In dem Beispiel werden
nur die einfachsten Peers implementiert. Die ActuatorPorts liefern "reine"
ActuatorPeer- und die SensorPorts "reine" SensorPeer-Instanzen. Es sind
keine Spezialisierungen enthalten, wie z.B. ein BooleanSensorPeer, der auf der
Seite der Ober �ache z.B. durch einen Button oder eine Checkbox repr�asentiert
werden k�onnte.
Auf der Ober �ache (siehe Abbildung 6.1 auf Seite 108) kann man den Zu-
stand der Actuatoren beobachten und auf den Wert der Sensoren Ein u� neh-
men, indem man in das entsprechende Textfeld einen neuen Wert eintr�agt und
mit Enter best�atigt. Das l�ost bei einem TextField im AWT einen ActionEvent
aus und dieser wird von einer zugeh�origen Instanz der inneren Klasse Text-
FieldSensorPeer verarbeitet:
Listing 42: code/art/examples/AWTRobotInterface.java (Ref. in Listing 41 S. 108)
protected class TextFieldSensorPeer extends BaseSensorPeer {protected TextField input;public TextFieldSensorPeer(TextField input) {
this.input = input;
input.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {try {
double oldValue = currentValue;currentValue = Double.parseDouble(
ae.getActionCommand());
processEvent(new SensorEvent(this,// source
null, // descriptorSystem.currentTimeMillis(),oldValue,currentValue,true, // isFloatingPointnull) // propagationId
➥
6.2. DIE PEERS 111
Listing 42: code/art/examples/AWTRobotInterface.java (Fortsetzung)
);} catch(NumberFormatException nfe) {
TextFieldSensorPeer.this.input.setText("" +currentValue);
}}
});
}
protected double currentValue;public double getValue() {
return currentValue;}
public boolean isConnected() {return AWTRobotInterface.this.isConnected();
}}
Bei Abstammung von der abstrakten Klasse BaseSensorPeer, ist die einzi-
ge Methode, die implementiert werden mu�, getValue(). F�ur die meisten F�alle
wird es au�erdem sinnvoll sein, die Methode isConnected() an das entspre-
chende RobotInterface weiterzuleiten.
Wichtig ist au�erdem getPrecision(). Der R�uckgabewert dieser Methode
soll dem maximal erwarteten Fehler des Sensors entsprechen, der von diesem
Peer repr�asentiert wird. Die Voreinstellung von BaseSensorPeer ist ein Wert
von 0 f�ur precision { im Zusammenhang mit realen Sensoren wird dieser
Wert sicherlich gr�o�er ausfallen. In dem Beispiel hier ist die Voreinstellung aus-
reichend, da die Werte, die in das Textfeld eingetragen werden, ja so "genau"
sind wie man m�ochte. Zudem ist es ein guter Test f�ur eventuelle Algorithmen,
ob diese auch mit diesem Extremwert klarkommen. Man mu� bedenken, da�
precision nur als Anhaltspunkt daf�ur angesehen werden kann, wie genau der
Sensor ist. Nicht f�ur jeden Sensor existieren genaue Spezi�kationen und dann ist
man auf Sch�atzwerte und eigene Tests angewiesen. Der Sensor kann zudem von
verschiedenen Faktoren, wie z.B. der Umgebungstemperatur, auf unvorherge-
sehene (oder zwar auf vorhergesehene, aber in der Peer-Implementierung nicht
in Betracht gezogene) Weise beein u�t werden, so da� ein Algorithmus keine
"lebenswichtigen" Funktionen von dessen Korrektheit abh�angen lassen sollte.
Ereignisse wie die �Anderung eines Werts f�ur einen Sensor werden in Form
von SensorEvent-Objekten dargestellt. Diese werden an alle SensorListener,
die sich f�ur solche Ereignisse interessieren, weitergeleitet. Die Verteilung von
Events und die Verwaltung von Listenern wird bereits in BaseSensorPeer
soweit geregelt, da� dies f�ur das Beispiel ausreicht. �Uber einen Aufruf von
processEvent(SensorEvent) wird der �ubergebene SensorEvent an alle bei
diesem Peer registrierten Listener weitergeleitet.
An das TextField ist ein ActionListener angeschlossen. Er steht in Form
einer anonymen lokalen inneren Klasse im Konstruktor von TextFieldSensor-
112 6. IMPLEMENTIERUNG EINES ROBOTINTERFACE
Peer. Die Methode actionPerformed(ActionEvent)des ActionListenerwan-
delt den Inhalt des Textfelds in eine double um und verpackt diesen Wert in
einen SensorEvent. Dieser Event enth�alt neben dem neuen und dem alten Wert
{ die durchaus auch gleich sein k�onnen { einen Verweis auf seinen Ursprung und
den Zeitpunkt zu dem er aufgetreten ist. Die anderen Parameter sind (bisher)
nur von untergeordneter Bedeutung:
� descriptor ist vom Typ SensorEventDescriptor und wird momentan
noch nicht wirklich ben�otigt. Er soll sp�ater dazu dienen, verschiedene
Events bestimmten Gruppen zuzuordnen, so da� man als SensorEvent-
Listener angeben kann, nur bestimmte Events zu bekommen, bzw. da�
ein Peer nur bestimmte Events �uberhaupt generiert (siehe SensorPeer.
enableEvents(SensorEventDescriptor)).
� isFloatingPoint gibt an, ob der Event von einem Sensor stammt, von
dem prinzipiell echte oatingpoint-Werte kommen k�onnen. Ist dieser Pa-
rameter false bedeutet das, da� ein Wert, der durch diesen Event re-
pr�asentiert wird, ohne Genauigkeitsverlust auf int gecastet werden kann.
� propagationId wird ben�otigt, wenn ein SensorEvent als ein Property-
ChangeEvent weitergeleitet wird (siehe JavaBeans API De�nition: java.
beans.PropertyChangeEvent.getPropagationId()).
Zu guter Letzt wird der Wert in dem TextField auf den vorherigen Wert
zur�uckgesetzt, wenn der String aus dem TextField nicht korrekt in eine double
umgewandelt werden kann. Der jeweils letzte erfolgreich in eine double umge-
wandelte Wert steht in currentValue, daher kann dieser Wert in der Methode
getValue() auch direkt zur�uckgegeben werden, ohne da� das Textfeld nochmal
"gefragt" werden mu�. Somit wird auch kein String umgewandelt, den man
z.B. gerade in dem Textfeld am editieren ist.
Die Methode isConnected() dient dazu herauszu�nden, ob der Peer noch
eine Verbindung zu dem Hardware-Sensor hat, den er repr�asentiert. In der Re-
gel wird das solange der Fall sein, wie noch Kontakt zur Interface-Hardware
besteht. Es ist daher im Prinzip richtig diesen Methodenaufruf an die Imple-
mentierung von RobotInterface (also in diesem Fall AWTRobotInterface) wei-
terzuleiten. In diesem konkreten Fall w�are es eventuell auch richtig gewesen
z.B. das zugeh�orige TextField zu fragen, ob es �uberhaupt noch sichtbar ist
(isShowing()). Die genaue Bedeutung von isConnected() h�angt von der je-
weiligen Implementierung ab. Mit dem Peer verbundene Sensoren fragen diesen
Wert ab, wenn sie ihrerseits nach isConnected() gefragt werden. Im Falle des
AWTRobotInterface ist der Wert immer true, da das Fenster sowieso so lange
auf dem Bildschirm bleibt wie die Java-Maschine l�auft:
Listing 43: code/art/examples/AWTRobotInterface.java (Ref. in Listing 41 S. 108)
public boolean isConnected() {return true; // maybe use Frame.isDisplayable() ?
}
6.2. DIE PEERS 113
F�ur Aktuatoren, wie z.B. einen Motor, wird ein weiterer Peer (Actuator-
Peer) ben�otigt, da hier der Wert nicht nur abgefragt, sondern auch gesetzt
werden kann:
Listing 44: code/art/examples/AWTRobotInterface.java (Ref. in Listing 41 S. 108)
protected class TextFieldActuatorPeer extends TextFieldSensorPeerimplements ActuatorPeer
{protected TextField output;public TextFieldActuatorPeer(TextField output) {
super(output);this.output = output;
}
public void setValue(double value) {setValue(value, true);
}public synchronized void setValue( double value,
boolean waitMode){
String tmp = "" +value;
double oldValue = getValue();output.setText(tmp);// beware if the TextField is changed by someone else// in this momentif(oldValue != value) {
ActionEvent ae =new ActionEvent(output,
ActionEvent.ACTION PERFORMED,tmp);
// output.dispatchEvent(ae); // Don’ t let this Thread// process the Event!
// This way the Thread processing the Event is the// real AWT-Event-Thread:output.getToolkit().getSystemEventQueue()
.postEvent(ae);}
}}
Der Parameter waitMode besagt (wenn er false ist), da� bei R�uckkehr der
Methode die eigentliche Operation, n�amlich das Setzen des neuen Werts, noch
nicht unbedingt abgeschlossen sein mu�. Dies kann z.B. dann m�oglich sein,
wenn sich ein anderer Thread um die Fertigstellung k�ummert. Da das Setzen
des neuen Werts in dem Beispiel aber sowieso kaum Zeit in Anspruch nimmt,
wird waitMode nicht weiter beachtet.
Falls sich der Wert ge�andert haben sollte, wird au�erdem daf�ur gesorgt, da�
vom Event-Thread des AWT ein entsprechender ActionEvent verarbeitet wird.
Damit bekommt die Oberklasse auf "nat�urliche" Weise mit, da� sich der Wert
ge�andert hat und kann eventuelle SensorListener, mittels eines SensorEvent,
114 6. IMPLEMENTIERUNG EINES ROBOTINTERFACE
�uber die �Anderung in Kenntnis setzen.
Dieser Event wird nur bei einer �Anderung des Werts erzeugt, da ansonsten
der Event-Thread ziemlich �uberlastet werden kann. Manche Algorithmen (z.B.
Trusty mit Subsumption) setzen in manchen Situationen ziemlich schnell nach-
einander den gleichenWert. Das vom Algorithmus verwendete Actuator-Objekt
leitet den Wert trotzdem jedesmal an den Peer weiter, da es ja sein kann, da�
jemand anderes zwischendurch den Wert beim Peer ge�andert hat, ohne da� der
Actuator es (schon) wei�. Der Peer selbst kann den neuen Wert dann verwer-
fen, wenn sichergestellt ist, da� eine �Anderung an dem Textfeld nicht vonstatten
geht, ohne da� er informiert wird. Dies kann am einfachsten dadurch geschehen,
da� f�ur jedes Textfeld nur ein Peer erzeugt wird. Trotzdem sollte die Methode
synchronized sein, denn sonst kann es sein, da� die ActionEvents in einer
anderen Reihenfolge verarbeitet werden als die Ver�anderung an dem Inhalt des
Textfeldes.
Der ActionEvent ist notwendig, damit die Oberklasse merkt, da� sich an
dem Inhalt des Textfeldes etwas ge�andert hat. Auf diese Weise kann dort der
gleiche Mechanismus verwendet werden, der auch schon beim normalen edi-
tieren mit der Hand daf�ur sorgt, da� ein neuer Wert zu einem SensorEvent
f�uhrt.
Man k�onnte sich �uberlegen, ob man nicht verschiedene Peers implemen-
tiert, die je nach dem, welcher Sensor angeschlossen wird, verwendet werden.
Ein StateSensor k�onnte dann z.B. durch eine Instanz von Choice abgebildet
werden und ein BooleanSensor mit einer Checkbox.
6.3 Die Ports
Au�er den Peers geh�oren zu einer Implementierung von RobotInterface auch
entsprechende Implementierungen von SensorPort, bzw. ActuatorPort. Es
kann auch weitere Port-Typen geben, die dann aber noch zu de�nieren w�aren.
Listing 45: code/art/examples/AWTRobotInterface.java (Ref. in Listing 41 S. 108)
protected class AWTSensorPort extends BaseSensorPort {AWTSensorPort(String portName,
int portNumber,double minimumValue,double maximumValue,long granularity,Port[] excludedPorts) {
super(AWTRobotInterface.this,portName,portNumber,minimumValue,maximumValue,granularity,excludedPorts);
}
➥
6.3. DIE PORTS 115
Listing 45: code/art/examples/AWTRobotInterface.java (Fortsetzung)
public SensorPeer getPeerFor(Sensor sensor) {SensorPeer peer = getSensorPeer(this, sensor);return peer;
}}
protected class AWTActuatorPort extends BaseActuatorPort {AWTActuatorPort(String portName,
int portNumber,String type,double minimumValue,double maximumValue,long granularity,Port[] excludedPorts) {
super(AWTRobotInterface.this,portName,portNumber,type,minimumValue,maximumValue,granularity,excludedPorts);
}
public SensorPeer getPeerFor(Sensor sensor) {ActuatorPeer peer = getActuatorPeer(this, sensor);return peer;
}public ActuatorPeer getPeerFor(Actuator actuator) {
ActuatorPeer peer = getActuatorPeer(this, actuator);return peer;
}}
Die Klassen BaseSensorPort und BaseActuatorPort werden nicht extra
beschrieben. Sie kapseln im Grunde nur die Parameter eines Ports, die dem Kon-
struktor �ubergeben werden. Dazu geh�ort auch die Instanz von RobotInterface,
zu der sie geh�oren. Die jeweiligen Port-Objekte sind daf�ur zust�andig, die Peer-
Objekte zu liefern, wenn ein Sensor-Objekt mit ihnen verbunden wird.
Bis es zu einem Peer kommt, werden folglich mehrere Stufen eines Factory-
Pattern (Gamma et al., 1995) durchlaufen: Die RobotInterfaceFactory er-
zeugt das RobotInterface welches wiederum die Ports erzeugt, die dann ih-
rerseits die Peer-Objekte liefern. Wie der entsprechende Peer genau erzeugt
wird ist dem Port, bzw. dem zugeh�origen RobotInterface �uberlassen. In dem
Beispiel werden die Peers gar nicht direkt von einem Port erzeugt, sondern die
Ports verwenden ihrerseits wieder "ihr" RobotInterface als Factory. Wichtig
ist nur, da� der Typ des Port aussagt, welchen Typ Peer man (mindestens)
erwarten kann.
In ART sind bisher nur SensorPort und ActuatorPort de�niert. Ein Axiom
der Port-Klassen soll es sein, da� sie jeweils einen bestimmten Peer-Typ garan-
116 6. IMPLEMENTIERUNG EINES ROBOTINTERFACE
tieren. F�ur den SensorPort kann dieser Peer-Typ noch als Resultat der Me-
thode getPeerFor(Sensor) direkt angegeben werden. F�ur ActuatorPort, als
von SensorPort abgeleitetes Java-Interface, funktioniert das f�ur die gleiche Me-
thode nat�urlich nicht mehr. Dort mu� man dann entweder glauben bzw. �uber-
pr�ufen, da� der zur�uckgegebene SensorPeer auch wirklich ein ActuatorPeer
ist, oder man verwendet gleich die Methode getPeerFor(Actuator).
Mit "mindestens" ist in diesem Zusammenhang gemeint, da� je nach den
Umst�anden (wie z.B. dem Typ des Objektes f�ur das der Peer erzeugt wird),
auch eine entsprechende Unterklasse als Peer erzeugt werden kann. So liefert
ein ActuatorPort der Implementierung f�ur den Brick einen MotorPeer, wenn
ein Motor (oder eine Unterklasse davon) an den ActuatorPort angeschlossen
wird.1
Die restlichen Parameter der Konstruktoren f�ur die Ports sind:
portName: eine Bezeichnung f�ur den Port, die analog zur Bezeichnung der
Interface-Hardware f�ur diesen Anschluss (wenn eine solche existiert) lau-
ten sollte, um eine m�oglichst einfache Zuordnung durch einen Anwender
zu erm�oglichen.
portNumber: eine weitere Bezeichnung zur Identi�zierung des Ports innerhalb
der Implementierung von RobotInterface.
type: eine Bezeichnung anhand derer die Art des Actuators identi�ziert wer-
den kann. Bisher wird dies noch nicht weiter verwendet und dient nur
dazu, zus�atzliche Informationen �uber den ActuatorPort anzugeben, die
beispielsweise f�ur einen Benutzer ausgegeben werden k�onnen (z.B. ob der
Ausgang Gleich- oder Wechselstrom verwendet).
minimumValue: der kleinste Wert, der von einem Peer geliefert w�urde, den
Sensor-Objekte bekommen, �uber deren Klasse sonst nichts weiter be-
kannt ist (also die, die keinen speziellen Peer bekommen).
maximumValue: entsprechend minimumValue, nur eben der gr�o�te Wert.
granularity: die Anzahl von Abstufungen, die zwischen minimumValue und
maximumValue f�ur diesen "einfachsten" Peer existieren.
excludedPorts: ein Array von Port-Objekten, die Anschl�usse repr�asentieren,
die nicht gleichzeitig mit dem Anschlu� dieses Port-Objekts verwendet
werden k�onnen.
1MotorPeer ist ein subinterface von ActuatorPeer (siehe Abbildung 4.5 auf Seite 47 und
Abschnitt 4.6 auf Seite 50)
6.4. DIE METHODEN VON ROBOTINTERFACE 117
6.4 Die Methoden von RobotInterface
In diesem Abschnitt geht es vor allem um die Methoden, die von dem Java-
Interface RobotInterface vorgeschrieben werden.
RobotInterfaceDe�nition
Wie bereits in Kapitel 5 auf Seite 59 beschrieben wurde, kann man bestimmte
Instanzen von RobotInterface �uber die Angabe einer RobotInterfaceDefi-
nition ausw�ahlen:
Listing 46: code/art/examples/AWTRobotInterface.java (Ref. in Listing 41 S. 108)
private static String definitionPrefix = "AWT";private RobotInterfaceDefinition definition;
public RobotInterfaceDefinition getInterfaceDefinition() {return definition;
}
public boolean conformsTo(RobotInterfaceDefinition definition) {if(this.definition.equals(definition)
|| new RobotInterfaceStringDefinition(definitionPrefix).equals(definition)) {
return true;} else {
return false;}
}
private AWTRobotInterface(int interfaceNumber) {String definitionString = definitionPrefix +interfaceNumber;definition =
new RobotInterfaceStringDefinition(definitionString);...
}
Jedes RobotInterface besitzt eine Instanz von RobotInterfaceDefini-
tion, die dazu geeignet w�are, genau diese Instanz von RobotInterface aus-
zuw�ahlen. Diese RobotInterfaceDefinition bekommt man �uber getInter-
faceDefinition() und sie sollte eindeutig einer einzigen Instanz vom Typ
RobotInterface zugeordnet sein, in dem Sinne, da� nur eine einzige (erfolg-
reich erzeugte) Instanz vom Typ RobotInterface, bei Aufruf von conforms-
To(RobotInterfaceDefinition) den Wert true zur�uckliefert. Auf diese Wei-
se bekommt man von RobotInterfaceFactory, bei Aufruf von getInter-
face(RobotInterfaceDefinition), f�ur diese eine RobotInterfaceDefini-
tion, immer die gleiche Instanz pr�asentiert. F�ur diese Eindeutigkeit mu� man in
dem Beispiel beim Aufruf des Konstruktors sorgen. Da dieser private markiert
ist, ist er aber nur innerhalb dieser Klasse �uberhaupt sichtbar. Er wird von der
Klassenmethode getInterfaces(RobotInterfaceDefinition[]) aufgerufen,
welche in Abschnitt 6.5 auf Seite 120 n�aher beschrieben wird.
118 6. IMPLEMENTIERUNG EINES ROBOTINTERFACE
Im Gegenzug hei�t das jedoch nicht, da� es keine RobotInterfaceDefini-
tion geben kann, f�ur die sich auch mehrere Instanzen vom Typ RobotInterface
"zust�andig" f�uhlen k�onnen. Im Beispiel w�are das f�ur eine RobotInterface-
StringDefinition der Fall, wenn diese den String "AWT" repr�asentiert. Robot-
InterfaceDefinition ist nur ein sogenanntes tagging-interface, welches kei-
ne Methoden vorschreibt. Welche Klasse als RobotInterfaceDefinition ver-
wendet wird, bleibt voll und ganz der jeweiligen Implementierung von Robot-
Interface �uberlassen. Im Beispiel { entsprechend den anderen bereits in ART
vorhandenen RobotInterface-Implementierungen { wird die Klasse Robot-
InterfaceStringDefinition verwendet, welche f�ur die meisten F�alle ausrei-
chen sollte.
Erzeugen der Ports
Die Ports eines RobotInterface braucht man, um Sensoren anschlie�en zu
k�onnen. Von RobotInterface werden Methoden de�niert, mit denen die Port-
Instanzen erfragt werden k�onnen. Diese sind nicht weiter aufw�andig, es werden
nur entsprechend initialisierte Sensor- bzw. ActuatorPort-Objekte zur�uckge-
geben:
Listing 47: code/art/examples/AWTRobotInterface.java (Ref. in Listing 41 S. 108)
public SensorPort[] getSensorPorts() {return new SensorPort[]{
new AWTSensorPort( "TextFieldInput0",0,Double.NEGATIVE INFINITY,Double.POSITIVE INFINITY,-1,null),
new AWTSensorPort( "TextFieldInput1",1,Double.NEGATIVE INFINITY,Double.POSITIVE INFINITY,-1,null)};
}
public ActuatorPort[] getActuatorPorts() {return new ActuatorPort[]{
new AWTActuatorPort("TextFieldOutput0",0,"",Double.NEGATIVE INFINITY,Double.POSITIVE INFINITY,-1,null),
new AWTActuatorPort("TextFieldOutput1",1,"",Double.NEGATIVE INFINITY,
➥
6.4. DIE METHODEN VON ROBOTINTERFACE 119
Listing 47: code/art/examples/AWTRobotInterface.java (Fortsetzung)
Double.POSITIVE INFINITY,-1,null)};
}
Eine Erkl�arung der Bedeutung der einzelnen Parameter, der Konstruktoren
f�ur die Ports, wurde bereits in Abschnitt 6.3 auf Seite 114 gegeben.
Erzeugen der Peers
Die Methoden zur Erzeugung der Peer-Instanzen in AWTRobotInterface sind
nicht public und werden von dem Java-Interface RobotInterface auch nicht
vorgegeben:
Listing 48: code/art/examples/AWTRobotInterface.java (Ref. in Listing 41 S. 108)
private TextFieldSensorPeer inPeer0;private TextFieldSensorPeer inPeer1;SensorPeer getSensorPeer(SensorPort port, Sensor sensor) {
switch(port.getPortNumber()) {case 0:
if(inPeer0 == null) {inPeer0 = new TextFieldSensorPeer(in0);
}return inPeer0;
case 1:if(inPeer1 == null) {
inPeer1 = new TextFieldSensorPeer(in1);}return inPeer1;
default:return null;
}}
private TextFieldActuatorPeer outPeer0;private TextFieldActuatorPeer outPeer1;ActuatorPeer getActuatorPeer(ActuatorPort port, Sensor sensor) {
switch(port.getPortNumber()) {case 0:
if(outPeer0 == null) {outPeer0 = new TextFieldActuatorPeer(out0);
}return outPeer0;
case 1:if(outPeer1 == null) {
outPeer1 = new TextFieldActuatorPeer(out1);}return outPeer1;
default:return null;
➥
120 6. IMPLEMENTIERUNG EINES ROBOTINTERFACE
Listing 48: code/art/examples/AWTRobotInterface.java (Fortsetzung)
}}
In diesen beiden Methoden mu� der Port nicht darauf �uberpr�uft werden, ob
er "einer von uns" ist, da die Methode keinen public-access besitzt und daher
nicht von beliebigem Code aus aufgerufen werden kann. Wie bereits erw�ahnt
sollen Peer-Instanzen nur �uber die jeweiligen Port-Objekte "nach au�en" ge-
langen. Auf diese Weise ist der konkrete Vorgang, wie ein Port zu dem Peer f�ur
einen Sensor kommt, vor dem Anwender versteckt. Damit bekommt man die
Freiheit, die Peers dort zu erzeugen wo man es f�ur sinnvoll h�alt.
Es w�are durchaus denkbar, da� die Port-Klassen von AWTRobotInterface
die Peer-Instanzen direkt selbst erzeugen. Dies ist nur eine Stil- und Designfrage
innerhalb der Implementierung von RobotInterface. Die Verwendung zentraler
Methoden hat den Vorteil, da� die Erzeugung der Peers nicht �uber mehrere Stel-
len verteilt geschieht. Wenn man z.B. eine Unterklasse von AWTRobotInterface
erstellt, in der man auf die Erzeugung der Peers Ein u� nehmen will, so mu�
man nur eine der beiden Factory-Methods (Gamma et al., 1995) �uberschreiben.
W�urde man daf�ur neue Port-Klassen erstellen, so m�u�te man au�erdem alle
die Stellen �andern, an denen Instanzen dieser Klassen erzeugt werden.
Die Bedeutung der Methode isConnected wurde bereits in Abschnitt 6.2
auf Seite 110 behandelt. Sie ist daher dort abgedruckt (Listing 43 auf Seite 112).
Die letzte zu implementierende Methode isAlive() hat eine damit verwandte
Bedeutung:
Listing 49: code/art/examples/AWTRobotInterface.java (Ref. in Listing 41 S. 108)
public boolean isAlive() {return true;
}
Die Methode isAlive() dient dazu herauszu�nden, ob die Interface-Hard-
ware noch wie erwartet antwortet, ob die Verbindung also nicht nur physikalisch,
sondern auch logisch in Ordnung ist. Das ist bei Textfeldern im AWT im Prinzip
immer der Fall.
6.5 Die Einbindung in ART: RobotInterfaceFactory
Instanzen vom Typ RobotInterface erzeugt man in ART nicht direkt. Daf�ur
ist die Klasse RobotInterfaceFactory vorgesehen. Diese hat mehrere Metho-
den, die alle eine oder mehrere Instanzen vom Typ RobotInterface zur�uck-
liefern. Welche das sind, kann man haupts�achlich �uber die Angabe von einer
(oder mehreren) RobotInterfaceDefinition beein ussen, denen die zur�uck-
gelieferten Instanzen gen�ugen m�ussen (siehe die Beschreibung zu Listing 46 auf
Seite 117).
Die Klasse RobotInterfaceFactory besitzt daf�ur eine Liste von Klassen,
denen sie die Gelegenheit gibt, Instanzen f�ur die entsprechende Menge von
RobotInterfaceDefinitions zu erzeugen. Die Methode die RobotInterface-
6.5. DIE EINBINDUNG IN ART: ROBOTINTERFACEFACTORY 121
Factory bei diesen Klassen daf�ur aufruft ist getInterfaces(RobotInterface-
Definition[]):
Listing 50: code/art/examples/AWTRobotInterface.java (Ref. in Listing 41 S. 108)
..public static synchronized RobotInterface[]
getInterfaces(RobotInterfaceDefinition[] ports){
// ’ search’ port-array for the AWT-onesVector interfaces = new Vector();for(int i=0; i<ports.length; i++) {
...// don’ t accept String ’ ALL’ , only return an// AWTInterface if explicitly asked for
...iface =
new AWTRobotInterface(interfaceNumber);// was initialization successful ?if(iface.isConnected()) {
interfaces.add(iface);portNamesToInterfaces
.put(new Integer(interfaceNumber),iface);
} else {if(debug.debug) {
debug.printInfo(iface+" is not connected,"+" not adding.");
}}
...}
RobotInterface[] result =new RobotInterface[interfaces.size()];
interfaces.copyInto(result);return result;
}
Diese Implementierung stammt aus dem Beispiel AWTRobotInterface. Der
gr�o�te Teil ist hier f�ur eine bessere �Ubersichtlichkeit nicht abgedruckt, da er zum
grunds�atzlichen Verst�andnis nicht notwendig ist. Wenn man wirklich ein eigenes
RobotInterface implementieren m�ochte, oder einfach nur Interesse daran hat,
kann man ja selbst in die Quelldatei schauen.
Wichtig ist, da� die Methode ein Array von RobotInterface-Objekten
zur�uckliefert, die alle soweit erfolgreich initialisiert werden konnten. F�ur jedes
zur�uckgelieferte RobotInterface sollte sichergestellt sein, da� f�ur wenigstens
eine RobotInterfaceDefinition in dem Array ports die conformsTo(Robot-
InterfaceDefinition)-Methode den Wert true liefert, damit die semantische
Konsistenz gewahrt bleibt.
Zudem sollte die Methode versuchen, m�oglichst f�ur alle angegebenen Robot-
122 6. IMPLEMENTIERUNG EINES ROBOTINTERFACE
InterfaceDefinition, die zu der entsprechenden Implementierung von Robot-
Interface geh�oren, eine Instanz zu liefern. Daf�ur kann es notwendig sein, sich
bei fr�uheren Aufrufen erzeugte Instanzen zu merken und bei Bedarf erneut
zur�uckzuliefern. Wenn z.B. ein echtes Hardware-Interface im Spiel ist, w�are es
wohl meistens nicht praktikabel mehrere Instanzen von RobotInterface zu er-
zeugen, die sich alle um dieselbe Hardware "k�ummern" wollen. Es kann aber
durchaus sein, da� ein und dasselbe RobotInterface von verschiedenen Pro-
grammteilen verwendet wird, die durch unterschiedliche Anfragen an Robot-
InterfaceFactory, Zugri� auf dieselbe Hardware bekommen m�ochten.
Die Liste der Klassen, die von RobotInterfaceFactory gefragt werden,
stammt aus der Property
de.jaetzold.art.RobotInterfaceFactory.platforms
Diese Property sollte eine durch whitespace getrennte Liste von voll quali-
�zierten Klassennamen enthalten. Alle diese Klassen werden �uber re ect dar-
auf untersucht, ob sie die Klassenmethode getInterfaces(RobotInterface-
Definition[]) besitzen. Diese werden dann dazu verwendet, die Instanzen von
RobotInterface zu erzeugen. Daf�ur geht die RobotInterfaceFactory davon
aus, da� der R�uckgabetyp der Methode RobotInterface[] ist.
Abbildung 6.2: Der Algorithmus von Trusty steuert einen virtuellen Roboter mit dem
AWTRobotInterface.
Um also das Beispiel AWTRobotInterface auch �uber die RobotInterface-
Factory zug�anglich zu machen, mu� man nur die Property entsprechend setzen.
Es gibt daf�ur einerseits die Kon�gurationsdatei de.jaetzold.art.properties,
die von RobotInterfaceFactory gelesen wird, andererseits werden auch even-
tuelle System-Properties ausgewertet, und zwar mit Vorrang vor dem Inhalt
der Kon�gurationsdatei. F�ur einen Test reicht es also v�ollig, die Property beim
Aufruf von java mit dem Parameter -D zu setzen:
$ java -Dde.jaetzold.art.RobotInterfaceFactory.platforms=de.jaetzold.art.examples.AWTRobotInterface de.jaetzold.art.examples.SubsumptionTrustyAWTDemoRobot
Genauso, wie f�ur die Implementierungen f�ur Fischertechnik und den Brick,
gibt es im package de.jaetzold.art.examples f�ur die Beispielalgorithmen,
6.5. DIE EINBINDUNG IN ART: ROBOTINTERFACEFACTORY 123
die f�ur Trusty mit Subsumption ben�otigt werden, Implementierungen f�ur deren
Robot Java-Interface, die mit dem AWTRobotInterface funktionieren.
Auf der erscheinenden Ober �ache (siehe Abbildung 6.2 auf der vorherigen
Seite) kann man dann, wie schon in Kapitel 5 auf Seite 59, die Funktionen
von Trusty aufrufen bzw. mit move den Subsumption-Algorithmus in Bewe-
gung setzen. Im Fenster des AWTRobotInterface kann man die Ver�anderung
der Motoren beobachten und durch Eingabe von 1 in den Textfeldern f�ur die
Sensoren, Trusty's Ausweichverhalten ausl�osen, denn der Wert 1 wird von ei-
nem BooleanSensor standardm�a�ig als texttttrue interpretiert und daher von
den Trusty-Algorithmen wie ein gedr�uckter Schalter verstanden.
124 6. IMPLEMENTIERUNG EINES ROBOTINTERFACE
7
Zusammenfassung
Neben einem kurzen Fazit wird in diesem Kapitel das Abstract Robot Toolkit
nochmal gegen�uber den Systemen aus Kapitel 3 abgegrenzt. Zudem wird auf
die Punkte n�aher eingegangen, in deren Zusammenhang fruchtbar erscheinende
Entwicklungsperspektiven existieren.
7.1 Ergebnis
Die Beispiele aus Kapitel 5 haben gezeigt, da� es mit ART m�oglich ist, einen
Algorithmus zur Robotersteuerung auf verschiedenen Hardware-Plattformen
wiederzuverwenden. Es wurden daf�ur teilweise eigene, objektorientierte De-
signs entwickelt, mit denen weitgehend unabh�angig von der konkreten Aus-
pr�agung der Komponenten des jeweiligen Roboters, ein Steuerungsalgorithmus
entwickelt werden kann. Die Verwendung von Objekten zur Repr�asentation der
Komponenten eines Roboters ist elegant und eine event-getriebene Programmie-
rung unter Verwendung von mehreren Threads erweist sich f�ur die Abstraktion
des Roboters und seines Verhaltens als sehr tauglich.
Im Fachbereich Wirtschaftswissenschaften an der Universit�at Osnabr�uck
wird ART seit Oktober 2001 eingesetzt. In dem Projekt "Virtuelles Seminar"
(http://vsem.oec.uni-osnabrueck.de/) werden �uber ein Fischertechnik-In-
terface Kameras undMikrofone geschaltet, sowie "Meldungen" von Teilnehmern
registriert. ART wird dort verwendet, weil es die einzige bekannte Java-L�osung
ist, die auch das Expansion-Modul zum Intelligent Interface von Fischertechnik
ansteuern kann.
7.2 Geschwindigkeit
Wenn an manchen Stellen, im Design und in der Implementierung, Kompromis-
se zwischen Performance und Flexibilit�at eingegangen werden mussten, wurde
meistens auf Kosten der Performance entschieden. So wird z.B. bei jeder re-
gistrierten �Anderung eines Sensorwerts mindestens ein neues Event-Objekt er-
zeugt. Das logische Design und die strukturellen M�oglichkeiten standen immer
im Vordergrund.
125
126 7. ZUSAMMENFASSUNG
F�ur Anwendungen, bei denen es wirklich auf Geschwindigkeit ankommt, ist
aber schon die gesamte Umgebung ungeeignet, da das normale Java Develop-
ment Kit von Sun nicht echtzeitf�ahig ist. Es existieren zwar bereits Bestrebun-
gen f�ur eine standardisierte Echtzeit-Erweiterung (Bollella et al., 2000), doch
Implementierungen davon sind zumindest bisher nicht frei verf�ugbar. Zudem ist
in diesem Zusammenhang z.B. die Kommunikation mit der Interface-Hardware
�uber Infrarot, wie das beim Brick der Fall ist, problematisch. Au�erdem mu�
nicht nur Java, sondern auch das zugrunde liegende Betriebssystem echtzeitf�ahig
sein, was zumindest f�ur Linux und Windows spezielle Erweiterungen erfordert.
Mit der Performance eines Systems wie legOS kann ART auf keinen Fall
mithalten, obwohl bestimmt noch einige M�oglichkeiten zur Optimierung der
Performance, in den Implementierungen f�ur den Brick und das Intelligent In-
terface, vorhanden sind. Kommt es auf einigerma�en schnelle Reaktionszeiten
an, ist ART im Zusammenhang mit Lego bisher nicht verwendbar, was aber
zu einem erheblichen Teil an der langsamen und unzuverl�assigen Infrarotkom-
munikation liegt. Bei der Ansteuerung des Brick vergeht dort viel Zeit. Bis ein
Befehl an den Brick angekommen und umgesetzt ist, k�onnen leicht 100 Milli-
sekunden vergehen. In Kombination mit der Tatsache, da� der Brick eigentlich
drei Befehle ben�otigt, um den kompletten Zustand eines Motors zu beschreiben,
ist es z.B. kaum m�oglich zwei Motoren ann�ahernd gleichzeitig zu starten oder
zu stoppen. Zum Teil k�onnten aber auch noch bessere Strategien entwickelt
werden, vor allem in der Ansteuerung der Motoren.
Die F�ahigkeit des Brick, eigene Prozesse auszuf�uhren, wird nicht genutzt.
Zusammen mit der existierenden M�oglichkeit, Speicherpl�atze f�ur Variablen im
Brick zu lesen und zu schreiben, kann man im Grunde ein eigenes Kommuni-
kationsprotokoll zur Ansteuerung der Motoren entwickeln. Damit l�asst sich das
Problem, da� die Motoren nicht synchron geschaltet werden k�onnen, ganz gut
umgehen. F�ur die Steuerung eines fu�ballspielenden Lego Cybermaster, wurde
diese Technik bereits einmal verwendet (J�atzold, 2002). Sie wurde jedoch nicht
allgemein in ART integriert.
Die Ansteuerung von Fischertechnik geht hingegen recht schnell. Trotzdem
ist f�ur Zeiten unter 20 Millisekunden selbst Fischertechnik nicht mehr ausrei-
chend. Das kann an der seriellen Kommunikation in Java liegen, wie der Unter-
schied zum parallelen Interface in Schreiner (2000b) zeigt.
7.3 Flexibilit�at
Es hat sich gezeigt, da� die Menge von Dingen, die man mit Sensoren und Ac-
tuatoren abbilden kann, �uber die Komponenten eines Roboters hinausgeht. Dies
ist aber nur folgerichtig, wenn man bedenkt, da� es das Ziel von ART war, von
der konkreten Roboter-Hardware zu abstrahieren. So wird ein BooleanSensor
verwendet, um dem Scheduler f�ur Subsumption die Bereitschaft einer Verhal-
tensweise zu signalisieren. Konstante Werte werden, durch einen Constant-
SensorPort, f�ur beliebige Sensoren verf�ugbar gemacht. Die M�oglichkeiten sind
sehr weitgehend und k�onnen wahrscheinlich noch zu sehr interessanten Ergeb-
nissen f�uhren.
7.4. ARCHITEKTUR 127
Das Ariadne-Beispiel f�ur leJOS ist wesentlich einfacher gestrickt als Ariad-
ne f�ur ART. Das h�angt vor allem damit zusammen, da� der Speicher auf dem
Brick sehr begrenzt ist und daher in leJOS nur sehr kleine Teile des norma-
len Java-API verf�ugbar sein k�onnen. Daher konnten auch viele f�ur das ART-
Beispiel verwendete Hilfsklassen nicht, bzw. nicht ohne weiteres, �ubernommen
werden. leJOs ist zwar schneller in den Reaktionszeiten, aber was Speicher,
Klassenbibliothek und Rechengeschwindigeit angeht ist die ART-L�osung auf
einem modernen PC wesentlich leistungsf�ahiger.
Das entwickelte Toolkit bietet eine sehr bequeme M�oglichkeit, um einfache
Fernsteuerungen von Lego und/oder Fischertechnik von einem PC aus in Java
zu realisieren { beispielsweise die Bewegung einer Webcam. Andere Java-L�osun-
gen, die auf Interna der verwendeten Roboter-Hardware beruhen, sind deutlich
schwieriger zu benutzen, zumindest wenn man die Interna erst kennen lernen
mu�.
Naivere Verwendung: Bis man in ART soweit ist, da� man einen Sensor
auch wirklich verwenden kann, sindmehrere Schritte erforderlich. Dies erschwert
eine sehr naive Verwendung von Sensoren ein wenig. Ein Sensor mu� immer
mit einem Port verbunden werden, sonst hat er keinen SensorPeer. Der Port
wiederum wird, in den meisten F�allen, von einem RobotInterface stammen,
das seinerseits von einer Factory erzeugt wurde.
Im Prinzip ist es m�oglich, da� ein Sensor-Objekt, wenn es erzeugt wird,
von selbst versucht, eine Verbindung zu einem geeigneten Peer herzustellen.
Es bleibt o�en nach welchen Regeln dieser Peer dann ausgew�ahlt wird. Es ist
bisher nicht klar, inwieweit diese Regeln kon�gurierbar sein m�ussten, damit eine
eindeutige und trotzdem exible Verbindung zwischen den Sensor-Objekten der
Software und den Sensoren der Hardware m�oglich bleibt.
7.4 Architektur
F�ur die Verwendung mit Lego und Fischertechnik ist die entwickelte Archi-
tektur von Sensor-Klassen gut geeignet. Es wurde au�erdem weitgehend auf
m�ogliche andere Hardware-Plattformen R�ucksicht genommen. So existiert z.B.
f�ur einen Servo ein eigener ServoPeer, obwohl weder Lego noch Fischertechnik
Servomotoren auf der Hardware-Seite direkt unterst�utzen.
Es m�u�te nun untersucht werden inwieweit die Architektur anderen Plattfor-
men gerecht wird. Es ist nicht m�oglich auszuschlie�en, da� sich manches, insbe-
sondere Teile der Hierarchie der Sensor-Klassen, f�ur eine bestimmte Hardware-
Plattform als schwierig herausstellt.
Die Klasse AngleSensor ist m�oglicherweise nicht ideal in die Hierarchie
der Sensor-Klassen eingeordnet. So ist ein AngleSensor in ART eine Unter-
klasse von CountSensor und damit auch von StateSensor. Ein StateSensor
repr�asentiert aber diskrete Zust�ande, ein AngleSensor hingegen eine physi-
kalische Gr�o�e. Die momentane Position von AngleSensor in der Klassen-
Hierarchie ist vor allem der Hardware von Lego und Fischertechnik angemessen,
da bei beiden Winkel durch Rotationen und diese wiederum durch Folgen von
128 7. ZUSAMMENFASSUNG
Zustands�anderungen gemessen werden. Somit ist ein AngleSensor als Unter-
klasse von CountSensor, welcher f�ur das Z�ahlen der Zustands�anderungen ge-
dacht ist, sehr praktisch. Es ist aber auch vorstellbar, da� ein Winkel z.B. durch
ein Potentiometer gemessen wird. Das hat mit Zust�anden und deren �Anderun-
gen nichts mehr zu tun. Eine erneute Implementierung sollte wohl besser einen
AngleSensor de�nieren, der an einen CountSensor angeschlossen werden kann,
anstatt einen, der ein CountSensor ist.
Weiterhin k�onnen die bisher de�nierten, beschreibenden Eigenschaften der
Sensoren, wie z.B. die Genauigkeit oder der Messbereich, wahrscheinlich nicht
allen m�oglichen F�allen gerecht werden. Vor allem die Genauigkeit ist auch inso-
fern ein Problem, da praktisch jeder Algorithmus von ihr abh�angt, wenn auch
in unterschiedlichem Ausma�. Ein einzelner Wert, zur Beschreibung der Ge-
nauigkeit eines Sensors, ist aber selbst schon ungenau, denn ein Sensor kann
z.B. in unterschiedlichen Messbereichen oder Umgebungsbedingungen unter-
schiedlich genau sein. Daher m�u�te die Genauigkeit durch eine Funktion mit
vielen Parametern beschrieben werden und nicht nur durch einen einzelnen
Wert. Eventuell f�uhrt das aber nun zu weit und man hat eine der Grenzen der
hardware-unabh�angigen Steuerung von Robotern gefunden.
Programmierbare Tasks: Die F�ahigkeiten des Brick wurden nicht komplett
umgesetzt. Es gibt bisher z.B. keine eigene Klasse f�ur den Temperatursensor.
Dies w�are aber notwendig, wenn man den Eingang des Brick speziell daf�ur
kon�gurieren k�onnen will. Das Betriebssystem von Lego f�ur den Brick bietet
eine Umrechnungsmethode f�ur boolesche Sensorwerte an, die bisher auch nicht
erreichbar ist (EdgeCount). Auf die M�oglichkeit sogenannte Messages zu ver-
schicken, auf lokale Variablen des Brick zuzugreifen oder gar eigene Tasks zu
laden und auf dem Brick zur Ausf�uhrung zu bringen, mu� man bisher verzich-
ten.
Programme zu de�nieren und auf die Interface-Hardware zu �ubertragen,
ist mit ART bisher nicht m�oglich. Analog zu den M�oglichkeiten von Spirit.ocx
(siehe 3.2.1 auf Seite 23) k�onnte man aber eventuell ein Task-Objekt entwickeln.
Vielleicht ist die Programmierung von Tasks auch �uber eine entsprechende
Erweiterung der Eigenschaft "waitForCompletion" m�oglich. So k�onnten even-
tuell Befehle, die man z.B. an einen Motor sendet, erst einmal gesammelt und
dann als Block auf das Interface �ubertragen und ausgef�uhrt werden.
Eine Integration der F�ahigkeit des Bricks, eigene Tasks auszuf�uhren, d�urfte
aber sehr interessant werden. Eventuell kann man damit viele der bisherigen
Geschwindigkeitsprobleme mit dem Brick in den Gri� bekommen. Es stellt sich
hier jedoch die Frage, inwieweit diese Technik wirklich plattformunabh�angig
realisierbar ist. Eventuell ist es am geschicktesten, daf�ur eine eigene einfache
Sprache zu entwickeln, die je nach Zielsystem entsprechend interpretiert werden
kann.
7.5. SICHERHEIT 129
7.5 Sicherheit
Sowohl das Intelligent Interface von Fischertechnik, als auch das �altere Interface
mit einem Anschluss f�ur die parallele Schnittstelle, haben eine Sicherheitsfunk-
tion eingebaut. Werden vom PC aus an das Interface f�ur eine gewisse Zeit (etwa
300-500 Millisekunden) keine Daten mehr verschickt, so werden alle Ausg�ange
abgeschaltet. Dies ist solange der Fall bis das Interface wieder einen Befehl vom
PC erh�alt.
Eine solche Schutzfunktion ist im Grunde wichtig. Software enth�alt leider
auch Fehler. Wenn solch ein Fehler dann dazu f�uhrt, da� der Roboter v�ollig
unkontrollierte Aktionen ausf�uhrt und sich selbst (oder sogar seine Umgebung)
"zerlegt", kann das teuer werden.
Der Brick hat eine solche Schutzfunktion nicht. Auch Fischertechnik ist
nat�urlich durch die beschriebene Technik nicht vor allen Fehlern gesch�utzt.
Wenn z.B. ein wichtiger Schalter nicht richtig angeschlossen ist, oder die Soft-
ware zwar mit dem Interface kommuniziert, aber eben nicht wie erwartet, so
greift der Schutzmechanismus nicht. Gerade beim Brick w�are diese Funktion
aber bei der Steuerung vom PC aus hilfreich, denn allzuoft passiert es, da� er
aus dem Sendebereich des Towers herausf�ahrt oder aus irgendeinem anderen
Grund die Infrarotkommunikation gest�ort ist.
In ART ist solch eine Schutzfunktion bisher nicht implementiert. Auch der
eingebaute Schutz von Fischertechnik wird teilweise ausgehebelt, da es durch-
aus sein kann, da� der Steuerungsalgorithmus stehengeblieben oder abgest�urzt
ist, ohne die Threads, die bei Fischertechnik mit der Interface-Hardware kom-
munizieren, in Mitleidenschaft gezogen zu haben.1
Um eine solche Schutzfunktion zur Verf�ugung zu stellen, w�are es also not-
wendig, da� eine Implementierung von RobotInterface regelm�a�ig gewisse Be-
dingungen �uberpr�uft und gegebenenfalls einige oder alle Ausg�ange abschaltet.
Im Optimalfall w�urden nur genau die Ausg�ange stillgelegt, f�ur die ein steu-
ernder Algorithmus in irgendeiner Form nicht mehr "richtig" funktioniert. Falls
der Steuerungsalgorithmus sich wieder aufrappeln sollte, oder ein anderer daf�ur
sorgt, da� die Ausg�ange vern�unftige Werte haben, w�are es w�unschenswert wenn
die Ausg�ange auch wieder aktiviert w�urden.
Ob der Algorithmus noch korrekt funktioniert, kann { wenn �uberhaupt {
nur der Algorithmus selbst wissen. Zum Beispiel k�onnte ein Algorithmus re-
gelm�a�ig checken, ob er sich noch in einem konsistenten Zustand be�ndet oder
alle seine Threads noch laufen. Wenn er sich aber nicht mehr in einem konsi-
stenten Zustand be�ndet und erst recht wenn keiner seiner Threads mehr aktiv
ist, kann er sich wahrscheinlich nicht mehr von sich aus "melden". Damit nun
aber nicht jeder Algorithmus darauf achten mu�, z.B. alle 300 Millisekunden
eine Nachricht an das RobotInterface zu schicken, kann man auch den umge-
kehrten Weg gehen: Das RobotInterface, bzw. die Actuator-Objekte, fragen
den Algorithmus von sich aus regelm�a�ig danach, ob noch alles in Ordnung ist.
Dies ist wieder ein typischer Fall f�ur das Delegate-Design-Pattern, welches
1Genaugenommen werden die Threads, die mit der Interface-Hardware kommunizieren,
sogar niemals durch das Versagen eines Steuerungsalgorithmus gest�ort.
130 7. ZUSAMMENFASSUNG
auch schon bei der Beschreibung der Sensor-Klassen, in Abschnitt 4.6 auf Sei-
te 50, erw�ahnt wurde. Ein RobotInterface (bzw. ein Actuator, wenn man
eine feinere "Au �osung" haben m�ochte) k�onnte eine Art aliveDelegate besitzen.
Dieser w�urde dann regelm�a�ig gefragt ob noch alles in Ordnung ist und falls
nicht, w�urde sich das RobotInterface, bzw. der Actuator, tempor�ar abschal-
ten, bis eine Anfrage an den Delegate ergibt, da� wieder alles in Ordnung ist.
M�oglicherweise w�urde der oben beschriebene optimale Fall auch noch weitere
Kon�gurationsm�oglichkeiten oder Delegate-Methoden erfordern.
Solch ein Delegate wird von ART bisher nicht unterst�utzt. Vor allem die
Umsetzung f�ur den Brick w�urde einen Mehraufwand erfordern, f�ur den einfach
keine Zeit mehr vorhanden war.
7.6 Andere Hardware
Es existiert heute Hardware, die einerseits zwar immer noch klein ist und we-
niger Rechenkapazit�at als ein moderner PC besitzt, auf der anderen Seite aber
deutlich schneller ist und vor allem mehr Speicher enth�alt, als dies f�ur den
Brick der Fall ist. So gibt es f�ur viele aktuelle Mobiltelefone oder die sogenann-
ten PDA's (Personal Digital Assistant) eine Implementierung der J2ME (Java
2 Platform, Micro Edition). Speziell f�ur die Verwendung von Java auf einem
embedded device innerhalb eines Netzwerks, ist auch das TINI-Board (http:
//www.ibutton.com/TINI/) geeignet. Es zeichnet sich durch einen integrier-
ten seriellen Anschluss, seine Ethernetf�ahigkeit (TCP/IP inkl. FTP-, HTTP-
und Telnet-Server) und durch die Unterst�utzung weiterer Bussysteme aus. Das
TINI-Board gibt es mit 0.5 oder 1.0 MB Speicher und einer beinahe komplet-
ten Implementierung der Java-packages java.lang, java.io, java.net und
java.util. Kann man etwas l�oten oder kommt man mit einem der bereits vor-
handenen Bussysteme aus, so sollte es sich gut f�ur die Steuerung von Robotern
verwenden lassen. Ein naiver Versuch das Abstract Robot Toolkit auf TINI zu
�ubertragen, war nicht erfolgreich. Zur Fehlersuche kann man zwar ganz normal,
aus dem Programm heraus, Text zur Ablaufverfolgung ausgeben, jedoch wird
speziell durch diese Art von I/O-Operationen das TINI-Board besonders stark
beansprucht. Aus diesem Grund �andert sich das Zeitverhalten signi�kant. Dies
ist aber bei der verwendeten Interface-Hardware von schwerwiegender Bedeu-
tung, denn das Intelligent Interface schaltet sich z.B. ab, wenn es f�ur etwa 300
Millisekunden nichts mehr �uber die serielle Leitung empfangen hat.
7.7 Subsumption
Im Rahmen dieser Arbeit wurde Subsumption (siehe Abschnitt 1.2) zwar an-
gewandt, als wichtiges Konzept, das eventuell ganz neue M�oglichkeiten er�o�-
net, aber nur gestreift. F�ur die Programmierung mit ART ist die Klasse de.
jaetzold.art.subsumption.SchedulerTask ein Ansatz, um auf einfache Wei-
se Subsumption als Design-Pattern f�ur verhaltensbasierte Robotersteuerung zu
verwenden.
7.8. JAVABEANS 131
Es k�onnte sich durchaus als fruchtbar erweisen, in dieser Richtung ein ei-
genes API zu entwickeln. Das mu� nicht unbedingt sehr umfangreich sein,
aber wenn die Schnittstellen zu den Verhaltensweisen und dem Scheduler wohl-
de�niert sind, dann ist eine Wiederverwendung der Verhaltensweisen { und
nat�urlich auch des Schedulers { leichter. So k�onnte sich mit der Zeit eine ganze
Bibliothek von fertigen Verhaltensweisen ansammeln.
Die M�oglichkeit, einen Roboter zu programmieren, indem bereits fertige
Algorithmen praktisch "zusammengesteckt" werden, ist vor allem auch im Hin-
blick auf die Programmierung mittels einer gra�schen Ober �ache sehr attraktiv.
Subsumption wird nicht nur f�ur die Programmierung echter, physikalischer
Roboter verwendet, sondern z.B. auch im Bereich der agentenbasierten Mo-
dellierung (Wooldridge, 1999). Aus diesem Grund w�are es vorteilhaft, wenn
das gleiche API { im Optimalfall sogar die gleichen Verhaltensweisen { sowohl
f�ur die Simulation im Computer als auch f�ur die Steuerung echter Roboter-
Hardware verwendet werden kann. Mit ART existiert daf�ur eine Basis. In Ka-
pitel 6 wurde gezeigt, da� die Algorithmen f�ur Trusty ohne gro�en Aufwand
auch mit dem rein softwarebasierten AWTRobotInterface verwendet werden
k�onnen.
Inwieweit aber ansonsten zwischen echten Robotern und Softwareagenten
�uberhaupt Gemeinsamkeiten bestehen, die eine Verwendung von Algorithmen
in beiden Bereichen rechtfertigen, be�ndet sich au�erhalb des Themas dieser
Arbeit.
7.8 JavaBeans
Nach der Spezi�kation von Sun (Sun Microsystems, 1997) bezeichnet das Ja-
vaBeans API eine Menge von Design-Pattern und Hilfsklassen und soll als
Komponentenmodell f�ur Java dienen. Das Ziel sind portable, wiederverwend-
bare Softwarekomponenten, die als solche verkauft werden k�onnen und vom
K�aufer je nach Anforderungen zu fertigen Applikationen zusammengebaut wer-
den { am besten in einem (gra�schen) Editor. Dies erinnert an die Paletten
f�ur den Interface-Builder unter OpenStep (Gar�nkel und Mahoney, 1993) und
nun MacOS-X. Au�erdem sollen Beans als eine Art Plugin dienen um Appli-
kationen zu erweitern. Diese Funktionalit�at kann auch in plattformabh�angige
Komponentenarchitekturen wie OpenDoc, OLE, COM, Active-X usw. integriert
werden.
Im Zusammenhang mit der Programmierung von Robotern halte ich vor
allem das Arrangement und die Manipulation von Komponenten in einem gra-
�schen Editor f�ur interessant. Nicht von ungef�ahr werden von Lego und Fi-
schertechnik in erster Linie gra�sche Programmierumgebungen zur Ansteue-
rung ihrer Interface-Hardware angeboten. Die gr�o�eren Geschwister von Robo-
Lab und LLWin (LabView und iCon-L) werden in Labors zur Datenerfassung
und Auswertung sowie zur Industrieautomation eingesetzt. Es besteht also of-
fenbar Bedarf an M�oglichkeiten zur gra�schen Programmierung von Robotern.
Dabei bieten Beans noch den Vorteil, da� sie auf ganz normalen Java-Klassen
beruhen und daher auch auf "konventionellem" Wege als Bibliothek von Objek-
132 7. ZUSAMMENFASSUNG
ten verwendet werden k�onnen. Zudem k�onnen die M�oglichkeiten der gra�schen
Umgebung durch Entwicklung weiterer Beans leicht erweitert werden.
Eines der Ziele von ART ist die M�oglichkeit der Wiederverwendung von
Steuerungsalgorithmen f�ur verschiedene Roboter. Solch ein Roboter mu� dann
aber gewisse, vom Algorithmus de�nierte Anforderungen erf�ullen. So mu� ein
Trusty-Roboter z.B. irgendwelche Motoren haben, mit denen der Roboter be-
wegt werden kann, sowie Sensoren um Hindernisse zu registrieren. Der Algorith-
mus de�niert, was passieren soll, wenn die Motoren auf gewisse Weise gesteuert
werden, und welche Sensoren er ben�otigt bzw. wie deren Wert interpretiert wird.
Dazu kann es notwendig sein, die Motor- und Sensor-Objekte vor der �Ubergabe
an den Algorithmus geeignet zu kon�gurieren. Au�erdem mu� festgelegt wer-
den, an welchem Anschluss und (aufgrund der Hardware-Unabh�angigkeit von
ART) an welcher Hardware der Motor angeschlossen ist. Dies kann sicherlich in
vielen F�allen, auch bei erfahrenen Programmierern, am einfachsten mit einem
gra�schen Tool geschehen.
Es ist aber auch denkbar, da� der Algorithmus selbst als Bean realisiert ist.
So k�onnen nicht nur die Hardware-Komponenten des Roboters, sondern auch
seine gesamte Steuerung, in solch einem Tool gleichsam "zusammengeklickt"
werden. Mit Subsumption (siehe Abschnitt 4.8 auf Seite 57) gibt es sogar ein
Konzept, das die Kombination von vielen (einfachen) Steuerungsalgorithmen
zu einem komplexen Verhalten vereinfacht, so da� es m�oglich sein sollte, mit
zunehmendem Vorrat an Steuerungsalgorithmen immer mehr L�osungen allein
durch das "zusammenstecken" von bereits vorhandenen Komponenten zu rea-
lisieren.
Die Vorteile einer gra�schen Umgebung sehe ich in der leichteren Zug�ang-
lichkeit f�ur Neulinge, dem schnelleren "eben mal" Ausprobieren auch f�ur erfah-
renere Entwickler, f�ur Demos und rapid Prototyping, da man praktisch "onli-
ne" einfache Algorithmen zusammenklicken kann, w�ahrend man z.B. mit einem
Kunden f�ur die Software (bzw. den Roboter) die Anforderungen diskutiert. Zu-
mindest im Bereich der Ober �achenprogrammierung haben sich gra�sche Tools
inzwischen in den meisten IDE's einen festen Platz erobert.
Neben all den Vorteilen, die man von Beans hat, die sich vor allem aus
der exiblen Wiederverwendung ergeben, steht { wie so oft { der Nachteil, da�
sie einen recht hohen Entwicklungsaufwand ben�otigen. Dieser ist sogar noch
gr�o�er als bei einer normalen Software-Bibliothek, da f�ur jede Eigenschaft ei-
ner Komponente bestimmte Design-Pattern eingehalten werden m�ussen, um
die gr�o�tm�ogliche Interaktion und Integration von (sich im Grunde v�ollig unbe-
kannten) Objekten zu erreichen. Genauer f�uhre ich die Design-Pattern und die
Verwendung der Hilfsklassen hier nicht aus, verweise aber auf das Tutorial von
Sun (Campione et al., 1998) sowie einen Seminarvortrag (von Toschanowitz,
1999) in dem ich zum ersten mal N�aheres �uber JavaBeans erfahren habe.
7.9 XML
Die Implementierungen der Robot Java-Interfaces, die einen konkreten Robo-
ter f�ur einen bestimmten Steuerungsalgorithmus zug�anglich machen, kapseln
7.10. PERSISTENZ 133
h�au�g nur statische Informationen �uber den jeweiligen Roboter. So wird dort
z.B. festgelegt, welche Klassen f�ur die Aktuatoren und Sensoren an welchen Port
angeschlossen sind und wie diese kon�guriert werden sollen. Nat�urlich k�onnen
auch noch weitere Parameter f�ur den Algorithmus enthalten sein, wie das z.B.
bei der gezeigten Implementierung f�ur NormalizedDriveTrainder Fall ist. Dort
wurde die Zeit, die der Roboter braucht, um einen Meter weit zu fahren bzw. ei-
ne ganze Umdrehung durchzuf�uhren, in der Implementierung des Java-Interface
NormalizedDriveTrainSimpleAlgorithm.Robot gespeichert.
Solche Implementierungen sollten aber eigentlich auch automatisch gene-
rierbar sein, wenn die entsprechenden Informationen in einem entsprechenden
Format gespeichert sind { zum Beispiel in XML. Am elegantesten stelle ich mir
eine L�osung vor, in der man den Roboter gra�sch zusammenklickt und kon�-
guriert. Ob die so erstellte Information dann in XML oder wie bei JavaBeans
�ublich, als Strom von persistenten Objekten, gespeichert wird { oder in einem
ganz anderen Format { beruht letzten Endes auch auf der Frage, inwieweit es
Tools gibt, die einen bei der Verwendung des jeweiligen Formats unterst�utzen.
F�ur JavaBeans und XML gibt es bereits gra�sche Editoren und insbesondere
XML strebt an, durch eine immer gleiche Syntax, das Format der Zukunft f�ur
die Speicherung und den Austausch von Dokumenten zu werden.
7.10 Persistenz
Wenn etwas in einem Editor erstellt wurde, dann m�ochte man diese Arbeit
auch speichern k�onnen und zu einem sp�ateren Zeitpunkt auf einem anderen
Rechner weiter be- bzw. verarbeiten. Im Falle von Beans sind das Objekte
sowie deren Kon�guration und Abh�angigkeiten untereinander, die gespeichert
werden m�ussen. Sie zusammen bilden z.B. das Programm, welches man gra�sch
erstellt hat.
Da die Beans im Editor echte "lebendige" Objekte sind, kann man sie dort,
noch w�ahrend man mit ihnen eine Applikation erstellt, gleich richtig verwenden.
Jede Kon�guration und Verbindung die man erstellt, kann sofort ausprobiert
werden, so da� das Programm, bzw. der Programmteil, den man im Editor
erstellt, dort im Grunde schon l�auft. Nun k�onnte man zwar im Prinzip alle
Arbeitsschritte, die zu diesem "Bean-Dokument" gef�uhrt haben, abspeichern
und immer wiederholen, wenn man dieses "Dokument" braucht, man kann aber
auch den aktuellen Zustand der Objekte abspeichern und dann sp�ater genau
so restaurieren, wie er vorher im Editor war. Letzteres bezeichnet man mit
Persistenz.
Die Notwendigkeit, da� JavaBeans persistent (serialisierbar) sein m�ussen, ist
der haupts�achliche Grund daf�ur, da� die Objekte von ART (noch) keine Beans
sind. Wie eine Serialisierung von Objekten, die einen Roboter repr�asentieren,
�uberhaupt aussehen kann, mu� noch untersucht werden. Da diese Roboter-
Objekte z.B. von zus�atzlicher Hardware abh�angen, die nicht auf jedem Com-
puter gleich (bzw. unter Umst�anden gar nicht) vorhanden ist, mu� bei der De-
serialisierung irgendwie die Verbindung zur Roboter-Hardware neu hergestellt
werden.
134 7. ZUSAMMENFASSUNG
Inwieweit sich eine Serialisierung von Roboter-Objekten umsetzen l�asst und
wieviel zus�atzliche (Benutzer-)Information daf�ur erforderlich ist, wurde im Rah-
men dieser Arbeit nicht weiter verfolgt. Die Design-Pattern zum Zugri� auf
Events und Properties wurden in ART aber an den meisten Stellen bereits
ber�ucksichtigt, so da� bereits eine gute Grundlage zur Entwicklung echter Ja-
vaBeans f�ur Roboter gescha�en wurde.
Anhang A
Installation des Abstract
Robot Toolkit
Dieser Abschnitt gibt eine Schritt-f�ur-Schritt �Ubersicht �uber die notwendigen
Schritte und Voraussetzungen, damit das Abstract Robot Toolkit verwendet
werden kann.
Java ART braucht Java um lau��ahig zu sein. ART wurde vor allem mit dem
jdk1.3 von IBM unter Linux entwickelt. Au�erdem wurde es auf die grund-
s�atzliche Lau��ahigkeit in Verbindung mit dem jdk1.2.2 von Sun unter
Linux und dem jdk1.3 von IBM unter Windows getestet.
Java Development Kits oder Java Runtime Environments f�ur verschiedene
Plattformen in unterschiedlichen Versionen bekommt man z.B. unter
http://java.sun.com/j2se/ oder
http://www.ibm.com/developerworks/java/jdk/index.html
In Bezug auf die Installation mu� deren eigene Dokumentation konsultiert
werden.
Das jdk1.3 von IBM wird f�ur ART empfohlen, denn vor allem unter Win-
dows kann man so am einfachsten Schwierigkeiten mit den seriellen Ports
umgehen.
JavaComm ART beruht auf dem Java Communications API in Bezug auf die
Ansteuerung der seriellen Schnittstelle. Dieses API ist nicht in jeder Java-
Installation enthalten, man mu� eine Version w�ahlen die zum installierten
Java passt.
F�ur das jdk1.3 von IBM kann man eine Implementierung des CommAPI
an der gleichen Stelle bekommen wie das jdk selbst:
http://www.ibm.com/developerworks/java/jdk/index.html
F�ur Windows und Solaris bietet Sun eine Implementierung an:
http://java.sun.com/products/javacomm/index.html
F�ur Linux (und wahrscheinlich auch Windows) gibt es eine freie Imple-
mentation:
http://www.rxtx.org/
135
136 ANHANG A. INSTALLATION DES ABSTRACT ROBOT TOOLKIT
Auch hier wird f�ur die Installation auf die in den Paketen enthaltene
Dokumentation verwiesen.
ART Das Archiv art.jar enth�alt die bereits compilierten Klassendateien und
ist auf der CD zu dieser Arbeit enthalten. Neuere Versionen sind �uber das
Internet (http://www.jaetzold.de/art/) zu bekommen. Unter der Vor-
aussetzung, da� Java ab Version 1.2 und das Java Communications API
installiert sind, mu� man, um ART verwenden zu k�onnen, im Prinzip
nichts weiter tun als das Archiv art.jar in den Klassenpfad aufzuneh-
men.
Man mu� jedoch darauf achten, da� die property de.jaetzold.art.
RobotInterfaceFactory.platforms die gew�unschte Liste von Klassen-
namen f�ur die Treiber-Implementierungen enth�alt. Wenn die Datei de.
jaetzold.art.properties im aktuellen Pfad vorhanden ist wird ver-
sucht die entsprechende property aus dieser Datei zu laden. Eine Version
dieser Datei be�ndet sich an den gleichen Stellen wie auch art.jar. An-
sonsten hat man noch die M�oglichkeit die System-Property de.jaetzold.
art.RobotInterfaceFactory.platforms z.B. beim Start der Java-Ma-
schine �uber den Parameter -D zu setzen.
Die Quelldateien zu ART sind ausgepackt im Verzeichnis code/art auf
der CD oder als Archiv im Verzeichnis archive zu �nden. Auch von
den Quelldateien werden neuere Versionen �uber http://www.jaetzold.
de/art/ zum download bereit stehen. Es existiert ein make�le, dessen
Pfade per Hand an das jeweilige System, auf dem �ubersetzt werden soll,
angepasst werden m�ussen.
Roboter Momentan kann man mit ART den gelben "Brick", manchmal auch
"RCX" genannt, aus dem Lego-Mindstorms Robotics Invention System
und das Intelligent Interface von Fischertechnik steuern. F�ur die Steue-
rung des Brick ist der Infrarot-Tower in der Variante mit einem seriellen
Anschluss n�otig. Ob und wann weitere Systeme unterst�utzt werden, ist
nicht bekannt. �Uber http://www.jaetzold.de/art/ werden diese gege-
benenfalls bekannt gemacht.
Sonst Im Allgemeinen wird man auch die Dokumentation zu ART ben�otigen,
siehe dazu Abschnitt A.2 auf der n�achsten Seite.
A.1 Installation der Beispiele
Die Beispiele f�ur die Systeme aus Kapitel 3 be�nden sich in entsprechend be-
nannten Unterverzeichnissen auf der CD zu dieser Arbeit. Sie sind nicht all-
gemein im Internet verf�ugbar sondern nur auf Anfrage. Sie k�onnen mit dem
jeweiligen Programmiersystem direkt geladen, bzw. verwendet werden.
Die ART-Beispiele aus den Kapiteln 5 und 6 be�nden sich ebenfalls auf der
CD zu dieser Arbeit. Sie liegen im Verzeichnis code/art/examples. Alternativ
kann man diese auch aus dem WWW laden (http://www.jaetzold.de/art/)
oder als gepacktes Archiv dem Verzeichnis archive auf der CD entnehmen.
A.2. KLASSENDOKUMENTATION 137
Die ART-Beispiele erfordern nicht mehr Installation als andere Java-Klassen
auch, vorausgesetzt, da� ART bereits korrekt l�auft. Die Klassen m�ussen �uber-
setzt und �uber den Klassenpfad erreichbar sein.
A.2 Klassendokumentation
Einerseits stellt das vorliegende Dokument eine Dokumentation von ART dar,
doch existiert auch eine direkte JavaDoc-Dokumentation der Klassen von ART.
Sie ist auf der CD �uber die Datei java/index.html erreichbar. Zus�atzlich ist
sie auch im Verzeichnis archive archiviert.
Eine druckbare Postscript- und eine PDF-Version der wichtigsten packages
stellt die Datei package-docs.ps, bzw. package-docs.pdf dar. Sie wurde mit
dem LATEX-doclet von 2C Technologies, Inc. generiert (http://www.c2-tech.
com/java/TexDoclet/).
Im Zusammenhang mit neueren Versionen von ART kann die entsprechende
neue Dokumentation nat�urlich auch wieder �uber die Web-Seite http://www.
jaetzold.de/art/ bezogen werden.
138 ANHANG A. INSTALLATION DES ABSTRACT ROBOT TOOLKIT
Anhang B
Troubleshooting
Dieses Kapitel stellt die h�au�gsten Fragen bei Problemen in der Verwendung
von ART zusammen und gibt Antworten. Nat�urlich k�onnen nicht alle Fragen
und Probleme vorausgesehen werden. Taucht eine wichtige Frage bzw. eine Ant-
wort hier nicht auf, kann man unter http://www.jaetzold.de/art/ nach einer
Antwort suchen. Dort wird dieser Abschnitt St�uck f�ur St�uck vervollst�andigt.
Die Antworten hier sind nicht ersch�opfend, sondern sollten nur helfen, die
Ursache f�ur ein Problem zu erkennen. Es kann unter Umst�anden erforderlich
sein, um z.B. zu verstehen was ein Klassenpfad ist, weiter Dokumentationen zu
Rate zu ziehen.
F�ur weitere Fragen und Anregungen steht der Autor unter der e-mail
zur Verf�ugung. F�ur jeden Hinweis im Zusammenhang mit dieser Arbeit und
dem Abstract Robot Toolkit an diese Adresse ist er dankbar.
B.1 Exceptions
de.jaetzold.util.NotConnectedException: No driver selected.
Check value of property de.jaetzold.art.RobotInterfaceFactory.
platforms
Kommt diese Exception konnte die RobotInterfaceFactory keine Treiber-
klassen laden, weil die property de.jaetzold.art.RobotInterfaceFactory.
platforms leer ist. Entweder man setzt diese property explizit, z.B. beim Start
der Java-Maschine �uber die Option -D, oder erstellt eine Datei de.jaetzold.
art.properties, die �uber den aktuellen Pfad erreichbar ist (siehe auch Ab-
schnitt 6.5 auf Seite 120). Eine Version dieser Datei mit den aktuellen Treibern
bekommt man �uberall dort, wo man auch ART herbekommt, also z.B. unter
http://www.jaetzold.de/art/.
Wenn diese Exception mit einer anderen Meldung kommt, dann stimmt
etwas mit der Verbindung zwischen einem Software-Sensor, seinem Peer und
dem zugeh�origen RobotInterface nicht. Es kann auch sein, da� der Kontakt
zur Interface-Hardware unterbrochen wurde. Die jeweilige Meldung sollte einen
Anhaltspunkt f�ur die genaue Ursache liefern.
139
140 ANHANG B. TROUBLESHOOTING
java.lang.NoClassDefFoundError: <irgendein Klassenname>
Das liegt in den allermeisten F�allen an einem falschen Klassenpfad. Den Klas-
senpfad setzt man z.B. mit der Option -classpath beim Aufruf der Java-
Maschine. Alternativ kann man auch die System-Variable CLASSPATH verwen-
den. Der Kassenpfad mu� alle jar-Archive enthalten, sowie alle Verzeichnisse
in denen Java-Klassen in den ihren package-Namen entsprechenden Unterver-
zeichnissen enthalten sind.
Es kann auch sein, da� der Klassenpfad zwar richtig ist, aber die Klasse
noch gar nicht kompiliert wurde. Es soll auch vorkommen, da� man sich bei der
Angabe des Klassennamens vertippt . . .
Zu guter Letzt kann es auch sein, da� das Java Communications API nicht
richtig installiert wurde. In diesem Fall weist der in der Fehlerausgabe angege-
bene Klassenname auf die Ursache hin.
java.lang.NullPointerException
java.lang.ArrayIndexOutOufBoundsException
Dies kann ein Hinweis darauf sein, da� keine Interface-Hardware gefunden wur-
de. Siehe dazu die Erkl�arungen in Abschnitt B.3.
B.2 Geschwindigkeit
Probleme mit der Geschwindigkeit k�onnen verschiedene Ursachen haben. Einige
wurden z.B. in Abschnitt 7.2 auf Seite 125 beschrieben. F�ur viele Geschwindig-
keitsprobleme gibt es momentan keine einfache L�osung. Die besten Erfahrun-
gen wurden aber mit dem jdk1.3 von IBM und der zugeh�origen CommAPI-
Implementierung gemacht, daher wird empfohlen es erst einmal damit zu ver-
suchen. Es sind manchmal durchaus Steigerungen um den Faktor 10 m�oglich.
B.3 Automatische Erkennung
Es kann sein, da� kein Hardware-Interface gefunden wird, bzw. da� das falsche
Hardware-Interface gefunden wird. In diesem Fall kann es helfen, den Strom
der Interface-Hardware abzuschalten. Vor allem das Intelligent Interface kann
in einen Zustand geraten, in dem es nicht mehr antwortet und aus dem es nur
durch einen solchen Reset wieder herauszubekommen ist.
Um nur ein ganz bestimmtes Interface zu bekommen, kann man bei Robot-
InterfaceFactory bestimmte Parameter angeben. Damit kann man verhin-
dern, da� die automatische Erkennung nach bestimmten Hardware-Interfaces
sucht. Die Parameter sind in der Klassendokumentation zu RobotInterfaceFactory
und in Abschnitt 5.2 auf Seite 70 beschrieben.
Abbildungsverzeichnis
2.1 Schema von Trusty . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2 Schema von LiSe . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 Ariadne aus LEGO und Fischertechnik . . . . . . . . . . . . . . . 10
3.1 Synchronisation in LLWin (Initialisierung) . . . . . . . . . . . . . 15
3.2 Semaphore mit LLWin . . . . . . . . . . . . . . . . . . . . . . . . 16
3.3 Synchronisation in LLWin (Subsumption) . . . . . . . . . . . . . 17
3.4 Positionsmessung in LLWin . . . . . . . . . . . . . . . . . . . . . 18
3.5 RIS/RCX-Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.6 Robolab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.1 Regelkreis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2 Schaltplan von Tutebot . . . . . . . . . . . . . . . . . . . . . . . 37
4.3 Tutebot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.4 Das grunds�atzliche Zusammenspiel der Komponenten des ART . 44
4.5 Hierarchie der Peers . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.6 Hierarchie der Frontend-Klassen . . . . . . . . . . . . . . . . . . 52
4.7 Model, View, Controller . . . . . . . . . . . . . . . . . . . . . . . 56
5.1 Einfacher fahrender Roboter aus Lego oder Fischertechnik . . . . 62
5.2 Design-Pattern f�ur die Programmierung mit ART . . . . . . . . . 72
5.3 Testfenster f�ur DriveTrain . . . . . . . . . . . . . . . . . . . . . 73
5.4 Design von NormalizedDriveTrain . . . . . . . . . . . . . . . . 79
5.5 Design von Trusty . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.6 Design von SubsumptionTrusty . . . . . . . . . . . . . . . . . . . 85
5.7 Impulsrad von Fischertechnik . . . . . . . . . . . . . . . . . . . . 91
5.8 Testfenster f�ur die Komponenten von SubsumptionAriadne . . . 105
6.1 Screenshot der Ober �ache von AWTRobotInterface . . . . . . . . 108
6.2 Die Trusty-Steuerung und das AWTRobotInterface . . . . . . . 122
141
142 ABBILDUNGSVERZEICHNIS
Literaturverzeichnis
Baum, D., (2000). Dave Baum's de�nitive guide to LEGO MINDSTORMS.
Apress.
Baum, D., (2001). NQC Programmers Guide, Version 2.3. http://www.
enteract.com/~dbaum/nqc/doc/NQC_Guide.pdf.
Baum, D., M. Gasperi, R. Hempel und L. Villa, (2000). Extreme MIND-
STORMS: An Advanced Guide to LEGO MINDSTORMS. Apress.
Bollella, G., J. Gosling, B. M. Brosgol, P. Dibble, S. Furr, D. Har-
din und M. Turnbull, (2000). The Realtime Speci�cation for Java. Addi-
son Wesley.
Brich, P., G. Hinsken und K.-H. Krause, (2000). Echtzeitprogrammierung
in JAVA : Automatisieren im Millisekundenbereich. Publicis MCD Verlag,
erste Au .
Brooks, R. A., (1985). A Robust Layered Control System for a Mobile Robot.
http://www.ai.mit.edu/people/brooks/papers/AIM-864.pdf.
Campione, M., K. Walrath, A. Huml et al., (1998). The Java Tutorial.
Sun Microsystems. http://java.sun.com/docs/books/tutorial/.
Dijkstra, E. W., (1965). Co-operating Sequential Processes. In F. Genuys
(Hrsg.), Programming Languages. London : Academic Press.
Ferrari, M. und G. Ferrari, (2002). Building Robots with Lego Mindstorms
: The Ultimate Tool for Mindstorms Maniacs! Syngress Publishing, Inc.
Flanagan, D., (1999). Java in a nutshell. O'Reilly, dritte Au .
Gamma, E., R. Helm, R. Johnson und J. Vlissides, (1995). Design Pat-
terns : Elements of Reusable Object-Oriented Software. Addison Wesley.
Garfinkel, S. L. und M. K. Mahoney, (1993). Nextstep Programming :
Step One, Object-Oriented Applications. Springer-Verlag New York.
Gasperi, M., (1998). Mindstorms Sensor Input. http://www.plazaearth.
com/usr/gasperi/lego.htm.
Goldberg, A. und D. Robson, (1983). Smalltalk-80 : The Language and its
Implementation. Addison Wesley, erste Au .
143
144 LITERATURVERZEICHNIS
Gosling, J., B. Joy, G. Steele und G. Bracha, (2000). The Java Language
Speci�cation. Addison Wesley, zweite Au .
J�atzold, S., (1999). JavaComm, Das Java Communications API. http://
www.vorlesungen.uos.de/informatik/javaapi/javacomm/index.html.
J�atzold, S., (2002). Programming Robots with Java. http://www.jaetzold.
de/talks/2002-01-rit/.
Jones, J. L., A. M. Flynn und B. A. Seiger, (1998). Mobile Robots :
Inspiration to Implementation. A.K. Peters Ltd., zweite Au .
Kernighan, B. W. und D. M. Ritchie, (1990). Programmieren in C. Carl
Hanser Verlag M�unchen Wien, zweite Au .
Knudsen, J., (1999). The UnoÆcial Guide to LEGO MINDSTORMS Robots.
O'Reilly.
Lindholm, T. und F. Yellin, (1999). The Java Virtual Machine Speci�cati-
on. Addison Wesley, zweite Au .
Maes, P. und R. A. Brooks, (1990). Learning to coordinate behaviors. http:
//www.ai.mit.edu/people/brooks/papers/learning.pdf.
M�uller, U., (2000). FishFace Handbuch, Version 3.1. http://www.
ftComputing.de/zip/fishma31.zip.
Nievergelt, J., (1999). Roboter programmieren { ein Kinderspiel : Bewegt
sich auch etwas in der Allgemeinbildung? Informatik Spektrum.
Noga, M. L., (1999). Designing the legOS Multitasking Operating System :
An OS for the Hitachi H8 microcontroller. Dr. Dobb's Journal. http://www.
noga.de/legOS.
Papert, S., (1999). Mindstorms : Children, Computers, and Powerful Ideas.
Basic Books, zweite Au .
Pattis, R. E., (1981). Karel the Robot: A Gentle Introduction to The Art of
Programming with Pascal. Wiley and Sons, erste Au .
Proudfoot, K., (1998). RCX Internals. http://graphics.stanford.edu/~
kekoa/rcx/.
Schreiner, A.-T., (1999). Ober �achen benutzen und programmieren : Java
und die Yellow Box. http://www.vorlesungen.uos.de/informatik/c99/
html/skript.html.
Schreiner, A.-T., (2000a). Programmieren mit Java (1.)2. http://
www.vorlesungen.uos.de/informatik/java00/html/skript/0_Preface%
.htmld/index.html.
Schreiner, A.-T., (2000b). Roboter programmieren. http://www.
vorlesungen.uos.de/informatik/robot00/html/skript.html.
LITERATURVERZEICHNIS 145
Sun Microsystems, (1997). JavaBeans 1.01 Speci�cation. http://java.sun.
com/products/javabeans/docs/spec.html.
Sun Microsystems, (1998). Java Communications API. http://java.sun.
com/products/javacomm/.
Sun Microsystems, (2001). Java 2 Platform, Standard Edition, v 1.3.1 API
Speci�cation. http://java.sun.com/j2se/1.3/docs/api/index.html.
Tanenbaum, A. S., (1990). Betriebssysteme - Entwurf und Realisierung. Wien
: Hanser.
The LEGO Group, (1998). Controlling LEGO Programmable Bricks : Spi-
rit.ocx technical reference. http://www.legomindstorms.com/sdk.
von Toschanowitz, K. T., (1999). JavaBeans. http://www.vorlesungen.
uos.de/informatik/javaapi/javabeans/index.html% .
Wooldridge, M., (1999). Intelligent Agents. In G. Weiss (Hrsg.), Multiagent
Systems : A Modern Approach to Distributed Arti�cial Intelligence, S. 27{77.
MIT Press, zweite Au .