Semesterbericht der Teilgruppe 42parti TKP: Semesterbericht der Teilgruppe 42parti

Datum: 08.11.95
42

Autoren:

Stichworte:


Zusammenfassung:

Mit diesem Bericht dokumentieren wir unsere Semesterarbeit im Sommersemester 1995 im Rahmen der Gruppe 42 des Projekts SAUS. Wir haben uns dabei mit einem Navigationsverfahren beschäftigt, das von A. W. Moore entwickelt wurde: dem Parti-game Algorithmus


Einleitung

Die Grundlage dieser Arbeit ist das Paper The Parti-game Algorithm for Variable Resolution Reinforcement Learning in Multidimensional State-spaces von Andrew W. Moore und Christopher G. Atkeson, das in Machine Learning, Kluwer Academic Publishers, Boston erschien.

In dem vorliegenden Bericht wird Schritt für Schritt ein Algorithmus zum Erlernen von Wegen zu Zielregionen in höherdimensionalen kontinuierlichen Zustandsräumen entwickelt. Die Idee des Algorithmus ist es, den Rechenaufwand so gering wie möglich zu halten und die Lernfähigkeit zu maximieren, indem der Zustandsraum in eine Art Grid World partitioniert wird. Dabei hat das Raster eine höhere Auflösung in schwierigen (also hindernisreichen) Bereichen. Aufgrund dieser Überlegungen soll der Parti-game Algorithmus auch in höher-dimensionalen Zustandsräumen recht gute Ergebnisse liefern, während herkömmliche Reinforcement Verfahren dort versagen, da der Aufwand exponentiell mit Anzahl der Dimensionen steigt. Um in den meist mehrdimensionalen, kontinuierlichen Zustandsräumen der Anwendungsgebiete noch vernünftig arbeiten zu können, zerlegt man diese normalerweise in ein mehrdimensionales, diskretes Raster. Daß dieser Ansatz aber häufig zu unbefriedigenden Ergebnissen führt, zeigen die Autoren mit ihrem ersten Algorithmus, der auf einer solchen Zerlegung basiert. Der Parti-game Algorithmus geht von folgenden Annahmen über das zu lernende Steuerungsproblem aus:

Die von den Autoren als Beschränkungen des entwickelten Algorithmus ausgelegten Punkte (Aufgabe bestimmt durch Zielregion, nicht durch beliebige Belohnungsfunktion; eine mögliche, aber nicht unbedingt die beste Lösung wird gefunden; Zielzustand ist bekannt) sind unseres Erachtens in bezug auf unsere Anwendung kein Nachteil. Im folgenden sollen die in der Abhandlung vorgestellten vier Algorithmen kurz betrachtet werden, wobei der Schwerpunkt auf den vierten Algorithmus (Parti-game) gelegt wird.

Algorithmus 1: Festes Raster

Zunächst wird der kontinuierliche Zustandsraum in n sogenannte Zellen (Hyperrechtecke) zerlegt. Eine solche Zelle ist ein diskretes "Ding", das eine kontinuierliche Menge von reell-wertigen Zustandsvektoren enthält. Andersherum gehört jeder reell-wertige Zustand genau zu einer Zelle. Von jeder Zelle i lassen sich eindeutig deren Nachbarzellen NEIGHS(i) bestimmen. Der Algorithmus erhält als Eingabe ein Modell der Umwelt und eine Zerlegung des Zustandsraums. Ein solches Modell muß lediglich in der Lage sein, für jeden reell-wertigen Zustand, jedes Steuerkommando und jede Zeitspanne den nachfolgenden reell-wertigen Zustand bestimmen zu können. Der Algorithmus liefert als Ergebnis eine Vorgehensweise: Eine Abbildung, die für jede Zelle des Rasters beschreibt, welches die nachfolgende Zelle sein sollte. Der Algorithmus basiert auf der NEXT-PARTITION-Funktion, die feststellt, in welcher Zelle man hängenbleibt, wenn man aus einem gegebenen reell-wertigen Zustand zur Mitte einer gegebenen Zelle läuft, bis man entweder die Anfangszelle verläßt oder steckenbleibt. Der Nachteil dieses Algorithmus liegt auf der Hand. Er findet nicht die wirklich kürzeste Strecke, sondern die mit der geringsten Anzahl von Zellen. Außerdem findet er möglicherweise unsinnige Lösungen, da er stets denkt, die Verbindung zwischen zwei Zellen ginge vom Mittelpunkt der Startzelle aus.

Algorithmus 2: Nimmt den schlimmsten Fall an

Dieser zweite Algorithmus verfolgt eine "worst-case"-Strategie. Die aus einer Zelle noch nötigen Schritte bis zum Ziel werden nicht mehr angenähert durch den Durchschnitt der nötigen Schritte von allen reellen Zuständen innerhalb dieser Zelle bis zum Ziel, sondern durch den schlechtesten Wert. Jede Zelle hat einige zugehörige Aktionen, die jeweils durch die benachbarten Zellen bestimmt sind ("fahre zu Zelle 2"). Jede dieser Aktionen j in einer Zelle i hat eine Menge von möglichen nachfolgenden Zellen. In dieser Menge sind z. B. die Zelle i selbst sowie die angepeilte Nachbarzelle enthalten (was soviel bedeutet, daß man entweder das erhoffte Ziel erreicht oder in der Ausgangszelle hängenbleibt). Diese Menge wird im folgenden auch als die Outcomes der Zelle i bezeichnet. Der Algorithmus wechselt aus einer Zelle immer in die Nachbarzelle mit dem geringsten Jwc-Wert. Dabei ist Jwc(i) die kürzeste(bzgl. der Anzahl der durchlaufenen Zellen) Strecke aus einer gegebenen Zelle i zum Ziel. Dabei wird angenommen, daß nach Bestimmung der aktuellen Zelle und der anvisierten nachfolgenden Nachbarzelle jemand den Roboter auf die ungünstigste Position innerhalb dieser Zelle verschiebt, bevor der lokale Controller aktiviert wird. Geht dieser Jwc-Wert gegen Unendlich, so spricht man von einer "Verliererzelle", von der aus das Ziel nicht erreichbar ist. Dieser Algorithmus findet häufig keine Lösung, obwohl es eine gibt. Dies liegt einfach an der "worst-case"-Annahme. Falls der Algorithmus allerdings ein Ergebnis n liefert, so ist gewährleistet, daß man nach maximal n Zellübergängen am Ziel sein wird.

Algorithmus 3: Nimmt den schlimmsten Fall an und lernt

Im Unterschied zu Algorithmus 2 wird hier nicht der absolut schlechteste Zustand einer Zelle gewählt, sondern der schlechteste der bislang bekannten. Auf diese Weise wird vermieden, daß ein Zustand aus einem Teil einer Zelle, der noch nie besucht wurde, gewählt wird. Die weitergehende Idee dieses Algorithmus ist es, ohne vorgegebenes Modell der Umwelt zu arbeiten, d. h. die Informationen über die Umwelt sollen durch Erfahrung gelernt werden. Als Eingaben werden benötigt: Der aktuelle, reellwertige Systemzustand s, eine Zerlegung P des Zustandsraums und eine Wissensbasis D, die alle bereits beobachteten Zellübergänge enthält (also z. B. (gestartet in i, wollte nach j und bin in k gelandet)). Der Algorihmus liefert als Ausgaben den Systemzustand und einen boolschen Wert ERFOLG oder MISSERFOLG. Das Vorgehen ist eine Endlosschleife, die aus folgenden Schritten besteht:

Die besondere Bedeutung dieses dritten Algorithmus wird im folgenden klar. Er ist Teil des Parti-game Algorithmus und damit auch Bestandteil unseres Programms.

Der Parti-game Algorithmus

Hier wird das in den beiden vorigen Varianten entstandene Problem der "Verliererzellen" gelöst, indem deren Auflösung durch Teilung in mehrere kleine Zellen erhöht wird. Es wird also nicht sofort MISSERFOLG gemeldet (wie noch in Algorithmus 3) sondern eine neue Zerlegung berechnet. Dabei wird wie folgt vorgegangen:

Noch einige Bemerkungen zum Parti-game Algorithmus: Die von Moore durchgeführten Experimente waren recht erfolgreich. Für unsere Anwendung interessant ist besonders die Navigation durch einen mit Hindernissen bestückten Raum. Daher haben wir bei unserer Implementierung des Parti-game Algorithmus stets diese Anwendung im Auge behalten. Nach dem etwas konfusen ersten Lernen, kann im zweiten Versuch ein relativ guter Weg ohne weiteres Erkunden des Raumes gefunden werden. "Uninteressante" Gebiete werden erst gar nicht besucht.

Grafischer Überblick (Flußdiagramm)

Eine grafische Übersicht zur Veranschaulichung des Kontrollflusses während der wichtigsten Phasen des Algorithmus ist in Abbildung flussdia zu sehen. (Abbildung: flussdia Flußdiagramm zum Algorithmus flussdia)

Klassenaufteilung

Nach dem Nachvollziehen des Parti-game Algorithmus haben wir versucht, eine Klassenaufteilung zu finden, die es ermöglichen soll, den eigentlichen Algorithmus möglichst direkt abschreiben zu können. Dazu haben wir das Programm zuerst grob in drei Bereiche aufgeteilt:

Da wir uns entschlossen haben, den Zustandsraum als Binärbaum (s. Implementation) und die Wissensbasis als Liste darzustellen, wurden Klassen zur Binärbaum- und Listenverwaltung notwendig. Daraus ergab sich dann die Aufteilung in folgende Klassen:

Implementation

Wir haben uns entschlossen, den Zustandsraum als Binärbaum zu implementieren, weil dadurch einige Vorteile gewonnen werden:

Bei der Implementation des Zustandsraumes haben wir darauf geachtet, uns nicht auf eine bestimmte Dimensionstiefe festzulegen, auch wenn wir momentan nur einen dreidimensionalen Zustandsraum benötigen. Die Anzahl der Dimensionen und die einzelnen Indexbezeichnungen sind in Basistypen als Konstanten deklariert; momentan sind dies xpos, ypos und ausr für Position und Ausrichtung des Roboters. Außerdem sind die Konstanten START = erste Dimension und ende = höchste Dimension+1 deklariert, sodaß Schleifen über alle Dimensionen von START bis ende-1 laufen können. Beim aktuellen Greedy-Kontroller konnte die Unabhängigkeit von der Dimensionstiefe allerdings nicht durchgehalten werden, da zur Winkel- und Entfernungsbestimmung die Koordinaten benutzt wurden.

Zur Implementation der Funktion J_{wc}() haben wir "dynamische Programmierung" eingesetzt. Für alle (n) Zellen wird die Entfernung zum Ziel als "Entfernung der günstigsten Nachbarzelle plus Übergangskosten zur Nachbarzelle" berechnet. Danach ist die Entfernung für die direkt ans Ziel angrenzenden Zellen korrekt. Nach höchstens n Aufrufen dieser Prozedur ist für alle Zellen die ermittelte Entfernung korrekt. Eine leichte Erkennung von Verliererzellen ist hierdurch auch möglich: bei jedem weiteren Aufruf bleibt die (korrekte) Entfernung in Gewinnerzellen konstant, während die Entfernung in Verliererzellen gegen Unendlich geht.

Den Versuch einer rekursiven Implementierung von J_{wc}() haben wir abgebrochen, da von einer Verliererzelle das Ziel nicht erreichbar und damit kein Rekursionsabbruch gegeben ist. Dies ließe sich zwar vermeiden, indem für jede in der Rekursion untersuchte Zelle gemerkt wird, ob sie schon untersucht wurde, aber wir sind uns noch nicht darüber im klaren, ob diese Programmierung den jetzt vorhandenen Aufwand der Ordnung n^2 überschreiten würde. (Diese Funktion ist offensichtlich noch nicht optimiert.)

Testläufe

In den folgenden beiden Unterabschnitten möchten wir anhand von zwei Beispielräumen die Funktionsweise unserer Implementierung des Parti-game Algorithmus noch einmal erläutern.

Labyrinth

In der Abbildung labyrinth wird das relativ schnelle Lernverfahren des Parti-game Algorithmus veranschaulicht. (Abbildung: raum2 Lernverhalten im Labyrinth labyrinth) Im oberen Teil der Abbildung erkennt man deutlich das Explorationsverhalten des Parti-game Algorithmus. Zunächst wird die Zielregion in der rechten oberen Ecke des Bildes direkt angepeilt. Daß dieser Versuch nach kurzer Fahrt an der ersten Wand endet (1), führt dazu, daß eine Zellteilung vorgenommen wird. Es gibt dann zwei Zellen, eine linke Raumhälfte und eine rechte. Auf dem Weg zum Mittelpunkt der linken Zelle stößt der Roboter wieder gegen die Wand (2), woraufhin die linke Hälfte des Raumes als Verliererzelle markiert wird, so daß es erneut zur Zellteilung kommt. Es werden beide Zellen geteilt - die linke, weil es aus ihr keinen Weg mehr zum Ziel gibt, die rechte, weil sie eine Nachbarzelle ist, von der vermutlich ein Weg zum Ziel existiert. Daraufhin gibt es im Raum fünf Zellen: die rechte Raumhälfte, und die in vier Quadrate geteilte linke Raumhälfte. Zunächst versucht der Algorithmus, die beiden im - von links aus gesehen - zweiten Gang liegenden Zellen anzupeilen. Dies schlägt jedoch wegen der Wand fehl. Folglich wird die Zelle unten links anvisiert. Deren Zentrum läßt sich auch ohne Schwierigkeiten erreichen (3).

Leider ist aber von diesem Punkt aus nicht auf geradem Wege das Zentrum der Zelle in der rechten unteren Ecke der linken Raumhälfte zu erreichen, da die Wand stört. Also wird noch einmal geteilt. Da die Zelle in der unteren linken Ecke des Bildes sich als Verliererzelle herausgestellt hat, gilt dies nun auch für die darüber liegende Zelle, da der bisher noch für möglich gehaltene Weg verbaut ist. Wie bereits erwähnt, wird bei der Teilung einer Zelle das bisher über diese Zelle erworbene Wissen "vergessen". Dies führt dazu, daß der Algorithmus noch einmal "nachguckt", ob es nicht doch einen Weg durch die Wand gibt. Danach ist die Auflösung ausreichend hoch, so daß der Weg in den nächsten Gang gefunden wird. Die dortige Zellendichte reicht bereits aus, um in den dritten Gang zu gelangen. Dort wiederholt sich das hier geschilderte Spielchen. Nach insgesamt 29 Zellteilungen wird das Ziel erreicht.

Steuert man im Anschluß daran den Roboter zurück in die Startregion, so findet er den Weg ohne weitere Zellteilungen vorzunehmen, allerdings eckt er noch einige Male an, da die Wissensbasis noch nicht vollständig aktualisiert ist. Fährt man von der Startregion dann noch einmal zum Ziel und zurück, so wird auf dieser Strecke ein fehlerfreier Weg gelernt.

Auf dem dritten Hinweg zum Ziel (vgl. unteren Raum in Abbildung labyrinth) ist die gewählte Trajektorie zu erkennen. Der Algorithmus ist nun in der Lage, jeden Punkt innerhalb des Labyrinths anzufahren, ohne weitere Zellteilungen vorzunehmen.

Es soll nicht unerwähnt bleiben, daß dieser Testraum auch deshalb gewählt wurde, weil hier zumindestens die ersten Zellteilungen zu Zellgrenzen führen, die mit den Wänden im realen Raum zusammenfallen. Dieser Raum stellt also gewissermaßen eine Extremsituation dar. Ferner ist zu bedenken, daß das Ergebnis zwar nach sehr wenigen Versuchen bereits recht akzeptabel ist, daß es sich aber in Zukunft auch nicht mehr verbessern wird. Dies liegt ganz einfach an der Konzeption des Parti-game Algorithmus. Es spricht jedoch nichts dagegen, die vom Parti-game Algorithmus gefundene Trajektorie noch weiter zu bearbeiten, um eventuell einen sanfteren Streckenverlauf zu erhalten.

Wohnraum

In diesem Kapitel beschreiben wir eine etwas realistischere Testumgebung, die zumindest entfernt an ein Wohnzimmer, in dem ein Tisch, ein Stuhl und ein Schrank stehen, erinnern soll.

(Abbildung: raum3a Eigenständiges Lernen eines relativ guten Weges. selbst)

Wir präsentieren im folgenden zwei Lernsituationen. Im ersten Beispiel (vgl. Abbildung selbst) lernt der Roboter rein zufällig einen relativ guten Weg aus der Startregion ins Ziel. Zunächst versucht er, einen Weg in die Zielregion in der unteren linken Ecke des Bildes zu finden. Dieser Weg ist kein direkter, da der Schrank im Weg steht. Mehr oder wenig zufällig wird ein Weg zwischen Tisch und Stuhl hindurch zum Ziel gefunden. Zufällig deshalb, weil diese Trajektorie nur gewählt wird, weil die angepeilten Zellzentren gerade so günstig in dem Korridor zwischen Tisch und Stuhl liegen, daß eine fast fehlerfreie Fahrt (bis auf eine Kollision mit dem Stuhl) möglich ist.

Für den Rückweg zur Startposition ist noch kein Wissen vorhanden, deshalb wird wieder zuerst versucht, den direkten Weg zu nehmen. Dies scheitert auch wieder am Wandschrank, worauf der Roboter zwischen Schrank, Stuhl und Wand pendelt. Dabei finden einige Zellteilungen statt, die sich im weiteren Verlauf als günstig erweisen werden. Schließlich findet der Roboter wieder den Korridor zwischen Stuhl und Tisch und findet von dort zurück zum Start.

Beim zweiten Versuch, den Roboter zum Ziel zu bringen, ist durch die vorherigen Zellteilungen ein großer Teil der Erfahrungen wieder verloren worden. Deshalb nochmal der scheiternde Versuch, direkt nach unten zu fahren. Da aber durch die Zellteilungen die Auflösung höher ist, wird nun ein kleinerer Schritt nach rechts gemacht, so daß der kürzere Weg zwischen Schrank und Stuhl nun gefunden wird. Auch auf dem Rückweg gibt es jetzt kein Problem, diesen Weg zu benutzen. Hierbei wurde also durch günstige Zellteilungen von alleine der beste Weg gefunden.

(Abbildung: raum3b Trainieren eines besseren Weges. vormachen)

Im zweiten Beispiel findet der Roboter von selbst einen ungünstigen Weg, kann aber durch Training den günstigeren Weg lernen. Der Roboter startet in der Mitte des Raumes unten vor dem Tisch. Die Zielregion liegt in der rechten oberen Ecke des Raumes. Da im ersten Beispiel schon Zellen auf der linken Seite des Raumes geteilt wurden, gibt es jetzt zwei Möglichkeiten, zum Ziel zu gelangen: direkt und links um den Tisch herum. Da die direkte Möglichkeit kürzer ist, wird sie zuerst ausprobiert. Sie scheitert an der Tischecke, so daß dieser Zellübergang als Unmöglich gemerkt wird. Da noch die zweite Möglichkeit existiert, wird noch keine Zelle geteilt. Der Weg links um den Tisch ist erfolgreich und wird gelernt. Auf dem Rückweg gibt es die gleiche Situation: der direkte Weg scheitert wieder an der Tischkante, der Umweg ist erfolgreich.

Nun soll dem Roboter beigebracht werden, den kurzen Weg rechts um den Tisch zu lernen. Dazu wird zuerst ein Zwischenziel rechts unten angegeben, das ohne Hindernisse erreichbar ist. Von hier aus ist auch die Zielregion rechts oben problemlos erreichbar. Da nun die Erfahrung gemacht wurde, daß der Zellübergang zwischen dem Startbereich und der davon rechten Zelle doch möglich ist, wird auch für den Rückweg dieser kurze Weg genommen und gelernt.

Was können wir schon?

Unsere primäre Absicht in der ersten Phase der Beschäftigung mit dem Parti-game Algorithmus war das Nachvollziehen der im Paper von A. Moore vorgestellten Ideen und die anschließende Implementierung in einer übersichtlichen und erweiterbaren Form von C++-Quellcode. In diesem Zeitraum (von Mitte Juli bis Mitte August 1995) haben wir uns über die Klassenaufteilung verständigt und die in Moores Paper an einigen Stelle recht unklare Problembeschreibung hinreichend präzisiert, so daß wir in der Lage waren, im Projektplenum am 2. August 1995 unsere erste lauffähige Version zu präsentieren.

In der zweiten Phase haben wir uns hauptsächlich ums Debuggen und "verschönern" des Codes kümmern müssen. Unsere Überlegung dabei ist die folgende: Wir ziehen es vor, die Richtigkeit des grundlegenden Algorithmus gründlich zu überprüfen, bevor weitere Funktionalität eingeführt wird. In Einklang mit dieser Idee befindet sich auch unser Beschluß, vor dem Angehen der im nächsten Kapitel beschriebenen noch zu lösenden Aufgaben nun erst einmal den "State of the Art" zu dokumentieren, was mit diesem Dokument geschehen ist.

Nichtsdestotrotz haben wir aber den von Moore vorgeschlagenen Algorithmus an einigen Stellen bereits verbessern können. So wird inzwischen nicht mehr die Entfernung in der Einheit "Zellen" gemessen, sondern es wird der korrekte Abstand zum Ziel betrachtet. Wir rechnen also nicht mehr mit Kosten 1 für den Übergang von einer Zelle zur anderen, sondern mit dem euklidischen Abstand der beiden Zellzentren in der xy-Ebene voneinander.

Zur Zeit arbeiten wir aus den oben genannten Gründen mit einer Reihe von Einschränkungen bzw. Überbleibseln aus der "Moore-Version". Die für den Betrachter offensichtlichste Unzulänglichkeit ist der Agent, auf den unser Controller angewendet wird. Um uns ganz auf das Geschehen innerhalb des Parti-game Algorithmus konzentrieren zu können, haben wir das Problem des Greedy-Controllers, der sicherstellen soll, daß in einer hindernisfreien Umgebung stets von einem beliebigen Punkt A zu einem beliebigen Punkt B gefahren werden kann, bisher außen vor gelassen. Aus diesem Grund arbeiten wir derzeit noch mit einer omnidirektionalen Tonne und einem Greedy-Controller, der einfach in die angepeilte Richtung steuert. Berücksichtigt man diese Einschränkung, macht es auch keinen Sinn, bei der Partitionierung die dritte Dimension unseres Konfigurationsraumes (die Ausrichtung) zu beachten. Wir teilen die Zellen derzeit also nur bezüglich der x- bzw. der y-Achse.

Daher reicht es auch, bei der Berechnung der Kosten für einen Zellübergang (die wie oben erwähnt wurde, im Gegensatz zum Moore-Paper, nicht mehr immer 1 betragen!) die Entfernung in der xy-Ebene zugrunde zu legen. Ein weiterer Punkt, der in naher Zukunft geändert wird, ist die globale Speicherung der Wissensbasis. Es macht unserer Ansicht nach mehr Sinn, wenn jede Zelle die bisher gemachten Erfahrungen, die sie selbst betreffen, verwaltet. Dies ist nur ein Implementationsdetail, das im Gegensatz zu der folgenden Überlegung vermutlich nicht das Verhalten des Agenten verändern wird. In der aktuellen Version werden vor jedem Schritt alle Jwc-Werte neu berechnet. Es gilt auszuprobieren, wie man dieses sehr rechenintensive Vorgehen optimieren kann, ohne dabei einen Leistungsrückgang in kauf nehmen zu müssen.

Was möchten wir erreichen?

Nachdem mit dieser Dokumentation und der Präsentation im Projektplenum am 2. November 1995 nun die Phase des Verstehens und der Grundlagenforschung erfolgreich abgeschlossen wurde, steht die nächste Etappe bevor: die Verfeinerung des Algorithmus und die Anpassung an unsere Rollstuhlanwendung.

Zu diesem Zweck werden wir zunächst einmal die im vorigen Kapitel vorgeschlagenen Verbesserungen vornehmen. Dies sind in erster Linie die lokale Speicherung der Wissensbasis und eine intelligente Strategie bei der Jwc-Berechnung.

Im Anschluß daran wird der Quantensprung hin zum Rollstuhl vorgenommen, d.h. statt der omnidirektionalen Tonne wird zunächst in der Simulation mit dem Rollstuhlobjekt und bei Verfügbarkeit der Schnittstellen und der Hardware des realen Rollstuhls mit diesem gearbeitet werden. Die Umstellung auf ein derartig kinematisch beschränktes Objekt erfordert verständlicherweise eine grundlegend neue Version des Greedy-Controllers. Es ist noch die Frage, wieviel Intelligenz in diesen neuen Controller gelegt werden sollte. Ein Ansatz wäre zum Beispiel der von Christoph Herwig und Matthias Heger vorgeschlagene, bei dem der Greedy-Controller die von ihm erreichbaren Positionen auf einer gekrümmten Fläche im Konfigurationsraum bestimmt.

Um erfolgreich mit dem realen Rollstuhl arbeiten zu können, sind wir stark auf die Realisierung der Positionsbestimmung durch die Bildverarbeitungsgruppe angewiesen.

Dokumentation

Klassendokumentation TKP: Klassendokumentation

Datum: 08.11.95

Autoren:

Stichworte:


Zusammenfassung:

Im folgenden Teil unseres Bericht werden die Klassen dokumentiert.


class SimRobot

Diese Klasse realisiert die Schnittstelle zu SimRobot. Sie wird vom Greedy-Controller benutzt, um Übergänge im Zustandsraum in Aktionen des Roboters umzusetzen und den neuen Zustand zu berechnen. Zur Anpassung an den Rollstuhl wird sie durch eine Klasse ersetzt, die die Schnittstelle zu den tatsächlich vorhandenen Hardware-Sensoren und Aktoren anspricht und mit Hilfe einer Karten-Klasse und einer Klasse der Bildverarbeitungsgruppe versucht, den Zustand des Rollstuhls zu bestimmen.


Synopsis: #include "SimRobot.hh"
Libary: SimRobot.o

Definition


private:
public:

class Greedy

Der Greedy-Controller hat die Aufgabe, den Roboter auf direktem Weg vom aktuellen Zustand in den Zielzustand zu bringen. Hierbei sind keine Informationen über vorhandene Erfahrungen oder bekannte Hindernisse, sondern nur über die kürzeste Verbindung zwischen zwei Zuständen vorhanden. Als Ergebnis wird dem Parti-game Algorithmus Erfolg oder Mißerfolg gemeldet. Der vorhandene Greedy-Controller geht bei der Wegplanung von einer omnidirektionalen Tonne aus. Da hierfür nur ein dreidimensionaler Zustandsraum mit Koordinaten und Ausrichtung benutzt wird, sind nur zwei einfache interne Funktionen winkel() und dist() nötig, um zu den Zielkoordinaten zu gelangen.

Wenn die Simulation auf den Rollstuhl umgestellt wird, muß der Greedy-Controller auf die begrenzteren Bewegungsmöglichkeiten angepaßt werden. Eine provisorische Möglichkeit ist, die Drehung auf der Stelle durch kurzes Vor - und Zurückfahren mit Maximaleinschlag zu erreichen. Später könnte der Zustandsraum dann so verändert werden, daß der Controller durch Kurvenfahrten benachbarte Zustände erreichen kann, d.h. möglicherweise Aufgabe der Koordinaten.

Außerdem beinhaltet diese Klasse die Schnittstellenklasse zu den Sensoren, die die Positionsbestimmung realisiert. Im Moment ist dies die Klasse SimRobot, sie muß später durch eine Klasse ersetzt werden, die die tatsächlichen Sensoren ansteuern kann.


Synopsis: #include "greedy.hh"
Libary: greedy.o

Definition


public:

class Liste

Diese Klasse stellt eine Schablonenklasse für Listen zur Verfügung, d.h. es können Listen mit Elementen beliebigen Typs verwaltet werden. Es stehen Funktionen zum Einfügen und Löschen von Elementen, zum Ausfiltern von Teillisten nach beliebigen Kriterien und zum Zuweisen und Verknüpfen von Listen mit Elementen gleichen Typs zur Verfügung.

ACHTUNG: da diese Klasse (noch) einige "undokumentierte Eigenheiten" besitzt, sollte sie momentan NUR von uns (siehe Autoren) und NICHT von anderen benutzt werden!!


Synopsis: #include "liste.hh"
Libary: liste.o

Definition


public:
private:

class ListenElement

Dies ist sozusagen der Container für ein Element der Liste. Er enthält ein Feld "eintrag" vom Element-Typ, einen Zeiger auf das nächste ListenElement, einen einfachen Konstruktor und eine Funktion zum Durchlaufen der Liste:


Synopsis: #include "liste.hh"
Libary: liste.o

Definition


public:

class Baum

Mit dieser und der Klasse "Knoten" wird ein einfacher Binärbaum gebildet. "Baum" beinhaltet als Felder nur einen Zeiger auf den Wurzel-"Knoten" und einen Zähler für die Anzahl der Blätter. Der Destruktor baut bei Vorhandensein den ganzen Baum ab.


Synopsis: #include "birnbaum.hh"
Libary: birnbaum.o

Definition


public:

class Knoten

Diese Klasse bildet zusammen mit der Klasse "Baum" einen Binärbaum.


Synopsis: #include "birnbaum.hh"
Libary: birnbaum.o

Definition


protected:
public:

class Party

Eine Instanz dieser Klasse wird benötigt, um eine Aufgabe zu lösen, z.B. fahre von A nach B. Zu diesem Zweck enthält die Klasse PARTY die Membervariablen WISSENSBASIS, in der die gemachten Erfahrungen abgespeichert werden, ZIEL, was die anzupeilende Zielzelle ist, ZUSTANDSRAUM, ein Binärbaum, in dem die die Partionierung des Zustandsraumes repräsentierende Datenstruktur abgelegt ist und einen CONTROLLER, der festlegt, auf welche Art und Weise der Agent sich fortbewegen soll, falls er sich im hindernisfreien Raum befindet.


Synopsis: #include "party.hh"
Libary: party.o

Definition


private:
public:

class Zelle

Diese Klasse stellt sämtliche Informationen und Funktionen bereit, die benötigt werden, um auf der Partitionierung des Konfigurationsraumes den Anforderungen des Algorithmus entsprechend operieren zu können. Die zur Repräsentation der Partition gewählte Datenstruktur wird in der Dokumentation zur Klasse BIRNBAUM erläutert. Eine Instanz der Klasse ZELLE entspricht einer Zelle, d.h. einem Hyperwürfel in dem möglicherweise mehrdimensionalen Konfigurationsraum, sofern es sich bei dieser Instanz um ein Blatt handelt. Ist eine Instanz kein Blatt, so ist die zu diesem Objekt gehörige Zelle zu einem früheren Zeitpunkt schon einmal geteilt worden. Die bekannten Informationen eines Objekts vom Typ ZELLE sind eine Liste der Nachbarzellen, seine Grenzen und seine Entfernung von der Zielzelle. Von einer Zelle lassen sich mit Hilfe von Memberfunktionen das Zentrum, die längste Achse, die Nachbarn, die Outcomes sowie die Entfernung zur Zielzelle bestimmen.


Synopsis: #include "zelle.hh"
Libary: zelle.o

Definition


private:
public: