Batch - Programmierung des Lego Mindstorms NXT

Ein Anfängerpraktikum im Studiengang Anwendungsorientierte Informatik (Bachelor)

Universität Heidelberg, Wintersemester 2006/2007

Inhalt

Abstract

Die Aufgabenstellung

Hauptaufgabenstellung war es, einen Weg zur automatischen (stapelbasierten) Programmierung des NXT zu finden. Der Hintergrund ist die Absicht, NXT-Roboter zur Ausführung von Problemlösungen zu bringen, die aus verschiedenen Gründen nicht direkt auf dem NXT berechnet werden können. Die Lösung des Problems sollte also extern auf einem "großen" Computer errechnet und anschließend automatisch in ein entsprechendes NXT-Programm generiert werden.

Verwendete Werkzeuge

NBC/NXC Compiler

NBC (Next Byte Codes) ist eine Assembler-ähnliche (d.h. low-level) Programmiersprache zur Entwicklung von Software für die originale Virtual Machine des NXT. Bei NXC (Not eXactly C) handelt es sich um eine Erweiterung der Sprache um C-ähnliche Konstrukte.

NextTool

NextTool, ein Kommandozeilenprogramm für Windows, bietet eine primitive Benutzungsschnittstelle für die Funktionen des NXT- Schnittstellentreibers. Der Umfang an Funktionen zur Wartung des NXT-Steins entspricht weitgehend dem der offiziellen Entwicklungsumgebung NEXT-G, wenngleich die Bedienung mangels GUI natürlich umständlicher ist. Der Vorteil ist, dass NextTool einfach per Kommandozeilenbefehl aus anderen Programmen heraus aufgerufen werden kann. Dies eröffnet die Möglichkeit, Operationen am NXT-Stein zu automatisieren.

Unser Anwendungsbeispiel

Das Traveling Salesman Problem

Das Traveling Salesman Problem (Problem des Handlungsreisenden) ist in Mathematik und Informatik ein bekanntes Beispiel für ein so genanntes NP-schweres Problem. Einfach ausgedrückt bedeutet dies, dass das Problem einen besonders hohen Lösungsaufwand erfordert, insbesondere steigt der Aufwand zur Lösung für eine Eingabegröße n mit wachsendem n überproportional an, was dazu führt, dass NP-schwere Probleme häufig schon für "eigentlich" geringe Eingabegrößen nicht mehr praktisch lösbar sind.

Es geht darum, dass für eine gegebene Menge von Städten mit bekannten Koordinaten der kürzeste Weg gefunden werden soll, auf dem jede Stadt einmal besucht wird.

Aufgrund der NP-Schwere des Problems handelt es sich um einen idealen Beispielfall für unsere Aufgabe, da man zu seiner Lösung mangels effizienter Algorithmen auf pure Rechenleistung angewiesen ist. Der NXT-Prozessor hätte keine Chance, das Traveling-Salesman-Problem selbst für eine geringe zweistellige Anzahl von Städten in akzeptabler Zeit zu lösen, die "Auslagerung" der Lösungsberechnung auf einen leistungsfähigeren Computer ist zwingend.

Datenakquise

Die wichtigste Information, welche zur Lösung des Traveling Saleman Problems benötigt wird, ist natürlich die Kenntnis über die Lage der zu besuchenden Städte. Wir modellieren die Städte als rote, etwa handtellergroße Kreisflächen, die über ein ca. 1x1 Meter großes Spielfeld verteilt werden. Die Koordinaten der Punkte möchten wir automatisch mit Hilfe einer Kamera und eines Bildauswertungsprogramms ermitteln. Um die realen Koordinaten der Punkte anhand der Koordinaten ihrer Pixel auf dem aufgenommenen Bild zu bestimmen, haben wir uns des Lochkameramodells bedient.

Theorie

Das Kameramodell

Das Lochkameramodell basiert auf einer perspektivischen Projektion der 3D-Koordinaten auf eine 2D-Bildebene.

Zunächst müssen die 3D-Koordinaten aus dem Koordinatensystem W(X Y Z) mit den Basisvektoren (x,y,z) des Kamerakoordinatensystems dargestellt werden. Der Basiswechsel besteht aus einer Rotation und einer Translation (affine Verschiebung des Koordinatensystems). Um sowohl Rotation als Auch Translation als lineare Transformation zu schreiben, bedient man sich der homogenen Koordinaten.

Homogene Koordinaten

Homogene Koordinaten sind normale Koordinaten, die um eine Dimension erweitert wurden und bei denen folgende Äquivalenzrelation existiert:

Das Kameramodell

Eine Rotation in homogenen Koordinaten :

wobei rij eine normale Rotationsmatrix ist.

Eine Translation in homogenen Koordinaten:

wobei der Translationsvektor ist.

Die Koordinatentransformation bestehend aus Rotation und Translation:

Bei der Perspektivischen Projektion werden die realen Koordinaten in C (x, y, z) auf die Bildebene wie folgt projiziert (Strahlensatz):

Oder in Homogenen Koordinaten:

Die Gesamte Transformation ergibt sich also zu:


und

Inverses Kameramodell

Die Matrix F lässt sich invertieren. Jedoch ergibt die resultierende Matrix keine Abbildung aus R² in den R³, sondern es ist eine Abbildung von R² in eine Sichtlinie („line of sight“). Diese Geraden müssen dann mit einer Ebene (der Spielfläche) geschnitten werden, um die 3D-Koordinaten zu ermitteln.

Die inverse Transformation in homogenen Koordinaten ergibt sich zu:

mit

.

Wobei Die Ebene in W-Koordinaten gegeben ist durch h0 + nh1 + mh2. Ziel der Kamerakalibrierung ist es, (FH)-1 zu erlernen.

Umsetzung - Kalibrierung der Kamera

Bei der Umsetzung waren die Ergebnisse mit dem Ersten Algorithmus nicht zufriedenstellend, weshalb wir auf eine zweite Methode zurückgegriffen haben. Es werden dennoch beide Methoden vorgestellt und die Probleme mit der ersten beschrieben.

Prinzipielle Idee

Mit der Kamera wird ein Raster aus roten Punkten mit vorgegebenem Abstand aufgenommen. Die Zentren dieser Punkte werden bestimmt. Mit einem Algorithmus wird ein Zuordnungsvorschrift zwischen „Real World“- und Pixelkoordinaten bestimmt. Diese wird dann bei der Durchführung des Salesmanroboterproblems benutzt, um die Positionen der Städte, des Roboters und seine Ausrichtung zu bestimmen.

Methode 1: Least squares approach

Seien yi Koordinaten im W-Frame und dessen Abbildungen ai auf der Bildebene bekannt. Gesucht sind die Parameter

}

mit

,

d.h. der quadratische Fehler zwischen berechneten und bekannten Koordinaten soll minimiert werden. Diese Minimierungsfunktion wurde in Matlab mit Hilfe der eingebauten Funktion lsqnonlin implementiert. Der Haken and dieser iterativen Methode ist die Startwertbestimmung der Parameter. Beim "lsqnonlin"-Löser handelt es sich um einen Gradientenabstiegsverfahren. Bei schlechten Startwerten kann der Algorithmus in einem lokalen Minimum konvergieren.

Probleme mit diesem Verfahren

Wir haben probiert die Startwerte per Hand abzuschätzen, was von Anfang an suboptimal war. Der Fehler in der Kalibrierung lag im besten Fall bei 5cm. Meistens aber knapp unter . Beim genaueren lesen von [1], und nachdem wir [2] gefunden hatten fanden wir heraus dass diese Methode nur dann sehr gute Ergebnisse Liefert, falls die Initialisierung der Parameter gut ist. Die erzielten Genauigkeiten lagen dann bei ~cm. Die Initialwerte, erhält man z.B. über die zweite Methode. Mit dieser Methode war der Fehler in der Kalibrierung im Millimeterbereich, was bei der Fahrungenauigkeit des Roboters völlig ausreichend ist.

Methode 2: DLT (Direct linear Transformation)

Die Idee ist, direkt die Transformationsmatrix F zu fitten. Im Anschluss wird (FH)-1 bestimmt. F ist eine 3 x 4 Matrix, ausgeschrieben:



Bei der Kalibrierung sind die (X Y Z 1)T und die dazugehörigen (ui vi 1)T bekannt. muss eliminiert werden . Dies ist gerade gegeben durch

Die einzelnen Komponenten ausgeschrieben und umgeformt ergeben:







Ähnlich gilt:

Um beide Gleichungen in eine zu schreiben, definiert man:

f' = (f11 f12 f13 f14 f21 f22 f24 f31 f32 f33 f34)T

und

und es gilt damit:

L * f' = 0

Diese Gleichung gilt für exakt bestimmte Pixelkoordinaten. Da jedoch die Positionbestimmung immer fehlerbehaftet ist brauchen wir eine Lösung im „least squares“ Sinn. Dazu muss die zu der obigen Gleichung zugehörigen Normalengleichung bestimmt werden.

LTL * f' = 0

Um die Triviale Lösung f’ = 0 zu vermeiden, muss eine Normalisierung angewendet werden. [2] schlägt f34 = 1 vor. (Was wohl aus Eigenschaften der homogenen Koordinaten hervorgeht. Dies ist aber nicht unbedingt ersichtlich).

Anmerkung: Dadurch, dass für unser Problem kein wirkliches 3D-Problem vorliegt, sondern alle zu Kalibrierende und zu Bestimmende Punkte auf einer Ebene liegen und man ein Koordinatensystem wählen kann, in der Z immer 0 ist. Bestimmen wir eigentlich , was jedoch egal ist, da wir später, diese Matrix mit eben dieser Ebene (H) schneiden (s.o.).

Bestimmen der Pixelpositionen der Städte

Einzige Sache die fehlt um die DLT zu verwenden sind die Pixelpositionen ai. Diese werden aus den aquirierten Bildern mit einem selbstentwickelten Algorithmus bestimmt. Die einzelnen Schritte werden nun beschrieben.
1. Vorverarbeitung

Die Kamera liefert RGB Bilder. Uns interessiert vor allem der Rotkanal, in der sich die Städte und der Roboter besonders hervorheben. Dieser Kanal wird betrachtet. Es handelt sich dabei um eine N x M Matrix mit Integer wertigen Einträgen.

Dieser Kanal wird mittels Medianfilter und Gaussfilter zunächst entrauscht. Danach wird
.
Damit werden alle Schwarz – Grau anteile aus dem Rotkanal entfernt.

Anmerkung: Dieser Schritt wurde eingeführt um den unterschiedlichen Belichtungsverhältnissen entgegenzuwirken. Der schwarze Untergrund wirkt unter Sonnenlicht fast weiß.

Als letztes wird aus dem Rot Kanal eine binäre Matrix der gleichen Größe erzeugt indem die Werte normierten Werte auf oder abgerundet werden.

2. Projektionen und Rückprojektion

Es wird als nächstes entlang der Dimensionen der Matrix aufsummiert. Vergleichen kann man das mit einem Schattenwurf. Man erhält dadurch zwei Vektoren der Größe n und m. Die Spalten der Matrix , in denen sich keine Städte (und damit rote Pixel) befinden werden sich als 0 im aufsummierten Vektor bemerkbar machen. Als nächstes werden die Bereiche bestimmt, in denen sich die Projizierten Bereiche überlappen (links in dunkelgrau).


Rekursion
Schritt 2 wird in den markierten Bereichen wiederholt, um leere Bereiche und doppelte Bereiche zu erkennen.

Das Verfahren zeigt seine Grenzen, wenn Die Städte zu nah beieinander sind:


Aufbau der Kalibrierung

Auf einem Din-A0-Bogen wird ein Raster wie in der obigen Abbildung gedruckt. Der Abstand zwischen den roten Punkten ist bekannt, und damit die yi. Ins Zentrum eines roten Punktes wird der Ursprung gelegt. Die restlichen Punkte ergeben sich aus geometrisch hen Überlegungen. Der Din-A0-Bogen wird mittels einer Videokamera mit Analog-zu-Digital-Wandler aufgenommen und die ai bestimmt. Danach werden diese Koordinaten in der DLT verwendet. Die Kalibrierung hatte einen Fehler im Millimeterbereich.

Durchführung - Auftakt zum TSP-solver

Nachdem in der Kalibrierung die Transformation zwischen Pixel und 3D Koordinaten bestimmt wurde wurde die eigentliche Problemstellung gelöst. Auf dem Spielfeld werden Rote Punkt als Position der Städte markiert. Und der Roboter auf das Feld gesetzt.

Zusammenfassung

Um nochmal zu wiederholen, was gemacht wurde:

  1. Mit der Kamera wird ein Raster aus roten Punkten aufgenommen und die Pixel Postionen bestimmt. Die 3D Postionen sind bekannt (durch die Struktur des Grids)
  2. Mit den Koordinaten wird mittels DLT die Kamera kalibriert.
  3. Im zweiten Teil werden auf dem Spielfeld mit roten Punkte die Städte markiert und der Roboter auf das Feld gesetzt.
  4. Die Pixelkoordinaten der Städte und des Roboters werden mit der gleichen Methode wie oben bestimmt.
  5. Die 3D Positionen wird durch anwenden der Transformation bestimmt.
  6. Die Koordinaten und die Ausrichtung wird in eine CSV Datei herausgeschrieben.
Bestimmen der Roboterposition/Ausrichtung

Am Roboter ist ein roter Punkt angebracht, so dass seine Position als Stadt miterkannt wird. Zusätzlich besitzt der Roboter eine grüne Zunge. Der center of mass des Grünanteils wird (nach ähnlicher Vorverarbeitung wie mit dem Rotkanal) bestimmt und unter den Koordinaten der Roten Punkte die Koordinate herausgesucht, die sich am nächsten befindet. Da der grüne und der rote Punkt eine feste räumliche Beziehung haben kann daraus die Ausrichtung des Roboters bestimmt werden.
Die Stadtpositionen werden mit obigem Algorithmus bestimmt. Die Pixelkoordinaten werden Transformiert und die 3D Koordinaten sowie die Ausrichtung des Roboters in eine CSV Datei geschrieben.

Lösen des Traveling-Salesman-Problems

Wir haben nun also die Koordinaten der zu besuchenden Städte ermittelt. Jetzt gilt es, das Traveling-Salesman-Problem für diese Städte zu lösen. Aus technischer Sicht bedeutet dies, die im vorigen Schritt ermittelten Koordinaten in die richtige (d.h. der Anforderung des TSP gerecht werdende) Reihenfolge zu bringen.

Zur Lösung des TSP verwenden wir einen primitiven, Stack-basierten Algorithmus, welcher einfach sämtliche möglichen Wege testet, um den kürzesten zu ermitteln. Der Algorithmus wurde in einem C++-Programm implementiert, welches die unsortierten Koordinaten aus einer CSV-Datei liest, in der Reihenfolge des kürzesten Weges anordnet und im gleichen Format wieder in eine Ausgabedatei schreibt. Er arbeitet auf folgende Weise:

  1. Wähle einen beliebigen vollständigen Weg und definiere ihn als den bisher kürzesten gefundenen.
  2. Erzeuge einen neuen Weg, der nur die Ausgangsstadt enthält, und lege ihn auf den Stack.
  3. Durchlaufe den Stack und leite aus jedem auf dem Stack liegenden unvollständigen Weg all diejenigen neuen Wege ab, die durch Hinzufügen jeweils einer der noch nicht besuchten Städte entstehen. Lege die so erzeugten neuen Wege ebenfalls auf dem Stack ab.
  4. Durchlaufe den Stack und überprüfe, ob ein Weg vollständig ist (d.h. ob alle Städte besucht werden).

    Wenn ja, füge den letzten Teilweg (von der letzten Stadt zurück zur Ausgangsstadt) hinzu und überprüfe, ob dieser Weg kürzer ist als derjenige, der bisher als kürzester gemerkt wurde.

    Wenn ja, merke den neuen Weg als bisher kürzesten und verwerfe den bisherigen.

  5. Wiederhole dies ab Schritt 3 so lange, bis alle möglichen Wege getestet wurden. Der nun als kürzester Weg gemerkte ist das Ergebnis.

Automatisches Generieren des NXT-Programms

Überlick

Wir haben also nun Koordinaten als Ausgabe des TSP-solvers vorliegen, daraus müssen wir nun ein Programm erzeugen, dass der von uns ausgesuchte Compiler (siehe Anhang: NXC/NBC) übersetzen kann in Motorenbefehle. Die sortierten Koordinaten liegen als CSV-Datei vor (Text-Datei mit durch Komma getrennte Werte), und werden nacheinander von einem Programm eingelesen und Zeile für Zeile in mehrere NXC-Befehle übersetzt.

CSV-Datei
Beispiel einer Ausgabedatei des TSP-solvers

Der Übersetzer

Das Programm zur Übersetzung hierfür wurde in C++ verfasst (der komplette Quellcode befindet sich im Anhang). Der Programmablauf lässt sich mit den folgenden Punkten zusammenfassen:

NXC-Datei
Beispiel einer Ausgabedatei des NXC-Programmgenerators

1. Einlesen des Ausrichtungsvektors
2. Berechnung der Ausrichtung des Roboters zu Anfang
3. Einlesen des ersten Wegpunkts
4. Einlesen des folgenden Wegpunkts
5. Berechnung der Drehung/der Entfernung zwischen den Koordinaten
6. Schreibe passende Fahrbefehle in Ausgabepuffer
7. Wiederhole 4., 5. und 6. bis zum Ende der Datei

Die genauen Funktionen und Aufrufe sind im Quellcode nachzulesen (siehe Anhang).

Einblick in die Ausgabedatei

NXC (Not eXactly C) ist ein Aufsatz für den freien NBC-Compiler um mit einer C ähnlichen Sprache komfortabler zu programmieren. Die Syntax ist fast genau wie bei C, daher auch der Name.
Um das Kalibrieren des Roboters zu vereinfachen, sind die eigentlichen Fahrbefehle in Makros verpackt. Das bedeutet eine einfache Ersetzung des Codes durch vorher definierte Funktionen. Somit sieht der generierte Code immer gleich aus, obwohl sich die genauen Fahrbefehle bzw. Kalibrierung ändern können.

Hier am Beispiel des DRIVE()-Makros:
DRIVE(x) (x ist die Distanz in Zentimeter) wird ersetzt durch:
RotateMotorEx(OUT_BC, 60, 21*(x), 0, true); ResetAllTachoCounts(OUT_BC); Wait(10)
(aus NXTmacros.h, zu finden im Quellcodepaket im Anhang)

In Worten ausgeschrieben: Drehe Motoren B und C synchron mit 60% Kraft um 21*x Grad vorwärts. Zurücksetzen des Rotationszählers und warten von 10 ms.

Ein ähnliches Makro existiert auch für den Befehl zur Drehung (TURN(s, x), s ist 1 oder -1 für links oder rechts, x die Drehung in Grad). Nun also haben wir korrekten NXC-Code in der Ausgabedatei, der nur noch vom Compiler übersetzt werden muss.

Zum Abschluss

Der NBC-Compiler wird vom Codegenerator aufgerufen und übernimmt das kompilieren und übertragen des Programms auf den NXT (per Bluetooth oder USB).

NBC Kommandozeilenaufruf
Beispeil eines Kommandozeilenaufrufs des NBC-Compilers

Man kann den NBC-Compiler natürlich auch direkt per Kommandozeile aufrufen (wie im Bild oben). Nach dem Übertragen ist das Programm auf dem NXT gespeichert und kann per Knopfdruck am NXT oder per Software gestartet werden.

Literatur

J. Heikillä , Geometric Camera Calibration using circular control points, IEEE transactions on pattern analysis and machine intelligence, vol. 22, no. 10, October 2000 J.Heikillä, O.Silvén, A Four-step Camera Calibration Procedure with Implicit Image Correction, 1997