RCX-Simulator für Not-Quite-C-Programme
Einleitung
Zur Illustration der Funktionsweise von NQC-Programmen für den Lego-Mindstorms-Prozessorbaustein (RCX)
wurde aus einer bestehenden Billardspiel-Animation ein einfaches Java-Programm zur Simulation entwickelt.
Grundlegend nutzt dieses Demonstrationsprogramm die große Ähnlichkeit zwischen den Hochsprachen
C (vertreten durch NQC) und Java aus. Das NQC-Programm, genauer gesagt die einzelnen Tasks, werden
als "Methoden" in eine Java-Klasse eingebettet. Der "parallelen" Abarbeitung der Tasks sowie der
Simulationsrechnung wird durch die Verwendung von "Threads" in Java Rechnung getragen.
Es muss an dieser Stelle betont werden, dass dieser Ansatz die genauen zeitlichen Prozesse
im RCX-Prozessorbaustein nicht exakt wiedergeben kann und dass manche komplexeren Möglichkeiten der
NQC-Anwender-Programmier-Schnittstelle (API) nicht realisiert wurden, weil in den NQC-Quelltext
in zu großem Maße eingegriffen werden müsste. Weiterhin wird Java in diesem Zusammenhang nicht
korrekt als objektorientierte Programmiersprache eingesetzt. So hat beispielsweise die wichtigste
Klasse ("RCX") einen Umfang von über 2000 Zeilen!
Der Gebrauch des RCX-Simulators erfolgt auf eigenen Gefahr. Der Autor des RCX-Simulator übernimmt keine
Haftung für Schäden, die durch den Gebrauch des Simulators entstehen. Dies gilt insbesondere für
Zeitverluste, die durch die Anwendung des RCX-Simulators verursacht werden. Eine Übereinstimmung
des Programmverhaltens bei der Simulation und in der Wirklichkeit wird in keinem Fall garantiert.
Der RCX-Simulator kann frei verändert und weitergegeben werden, wobei Änderungen und deren
Urheberschaft klar erkenntlich sein müssen. Über Rückmeldungen bezüglich Fehlern und Verbesserungen
ist der Autor jederzeit dankbar (E-Mail: Robot@fh-offenburg.de)
Installation
Das Simulationsprogramm wurde mit Java2 in der Version 1.4 entwickelt und ausprobiert. Mit der
Version 1.3 lässt sich der Quelltext compilieren, der Roboter verhält sich aber aufgrund der
geringeren Leistungsfähigkeit dieser Version anders.
Die "Java2 Standard Edition (J2SE™)" kann von der Firma Sun über
http://java.sun.com frei heruntergeladen werden. Auf dieser Homepage findet man am rechten Rand unter
"Quick Links" das
Feld "J2SE 1.4 SDK", das direkt zur Download-Seite führt. Dort wählt man für Windows in
der Spalte "Download J2SE™ v1.4.0_01" die Variante "Windows (all languages, including English)"
und in der Spalte "SDK" dann "Download" aus. In einer weiteren Seite muss die Lizenz akzeptiert werden.
Daran anschließend wählt man den Link "Download j2sdk-1_4_0_01-windows-i586.exe". Es ist zu beachteten, dass
37 MB heruntergeladen werden müssen.
Sollte auf der Homepage von Java der direkte Weg zum Download nicht mehr vorhanden sein, so kann
man sich über "-J2SE" unter "Technologies" zur "Java™2 Platform, Standard Edition (J2SE™)"
durchhangeln
und findet dann die Java-Versionen am rechten Rand, die wiederum zu den Download-Seiten führen.
Die Dokumentation zum Java2-SDK findet sich auf der (ersten) Download-Seite unter "J2SE v1.4 Dokumentation".
Es muss wiederum die Lizenz akzeptiert und dieses Mal mit dem Link "Download j2sdk-1_4_0-doc.zip" (ca. 31 MB)
transferiert werden. Zum Entpacken bedarf es in diesem Fall z. B. des Programms "UnZip". Auf der Homepage
erreicht man die Dokumentation unter "Documentation" und "-APIs" am linken Rand.
Zur eigentlichen Installation von Java2 wird die heruntergeladene Datei "j2sdk-1_4_0_01-windows-i586.exe"
durch Doppelklicken ausgeführt. Der nachfolgende Installationsdialog fragt dann z. B. das
gewünschte Verzeichnis ab. Am Ende muss im DOS-Fenster die Pfad-Variable um die Pfadangabe der
binären Java-Dateien ergänzt werden: "SET PATH=%PATH%;C:\J2SDK1.4.0\bin", falls in das Verzeichnis
"C:\J2SDK1.4.0\" entpackt wurde. Möchte man dieses Kommando nicht nach jedem Neustart wiederholt ausführen,
muss ein entsprechender Eintrag in der "Systemsteuerung" unter "System" und dann "Umgebung" vorgenommen
werden. Mit Administratorrechten kann die Systemvariable "Path" um die Pfadangabe ergänzt werden,
andernfalls fügt man die Benutzervariable "Path" mit dem entsprechenden Wert hinzu.
Als letzter Schritt müssen noch die Quelltexte für die Simulation von dieser Roboterseite heruntergeladen
werden. Es sind dies die Dateien RCXSimulationsGUI.java,
RCXSimulation.java, RCXRun.java,
RCX.java, Wettbewerbstisch.java,
Tcheck_sensors.java und
Tmove_square.java. Die Klasse "RCX.java" ist so eingerichtet, dass das
nachfolgende einfache Beispiel startet. Weitere Beispiele sind als Kommentare beigefügt und können
aktiviert werden, nachdem das einfache Beispiel auskommentiert wurde.
Ein einfaches Beispiel für eine Simulation
Als ganz einfaches Beispiel soll das nachfolgende NQC-Programm "HalloRobot.nqc" dienen:
task main()
{
OnFwd(OUT_A);
OnFwd(OUT_C);
Wait(400);
OnRev(OUT_A+OUT_C);
Wait(400);
Off(OUT_A+OUT_C);
PlaySound(5);
}
In diesem Programm muss ausschließlich die erste Zeile in public void task_main() geändert werden.
Dieser geänderte Quelltext wird dann in der Datei "RCX.java" hinter die Zeile
// Hier wird das NQC-Task "task main()" eingepasst ------------ (anpassen!)
gesetzt, die man am besten mit der Suchfunktion des Editors lokalisiert. Der Java-Quelltext "RCX.java"
wird im DOS-Fenster mit dem Befehl "javac RCX.java" übersetzt. Mit der Compilation von
"RCXSimulationsGUI.java" (Befehl: "javac RCXSimulationsGUI.java") werden die restlichen benötigten
Java-Dateien automatisch mitübersetzt. Das Simulationsprogramm wird mit dem Befehl
"java RCXSimulationsGUI" gestartet. Man beachte, dass die Befehle genau in der aufgeführten Form
(mit Ausnahme der Gänsefüßchen) eingegeben werden müssen.
Das Simulationsprogramm legt nach der Zeile
// Startposition und Ausrichtung des Roboters ----------------- (anpassen!)
standardmäßig die Startposition des Roboters in der Startbox fest
(xPosition = 15.; yPosition = 0.;).
Der Roboter zeigt dabei auf den Ausgang der Startbox (theta = 0./180.*Math.PI;).
Nachdem der Roboter kurz die Nase aus der Startbox gesteckt hat, stößt er in diese zurück und bleibt stehen.
Das Programm muss dann von Hand gestoppt werden, indem das Fenster geschlossen wird.
Lädt man die Bytecode-Dateien RCXSimulationsGUI.class,
RCXSimulation.class, RCXRun.class,
RCX.class, Wettbewerbstisch.class
herunter, kann das Beispiel direkt mit "java RCXSimulationsGUI" gestartet
werden (- vorausgesetzt, Java wurde installiert).
Wenn die Simulation immer wieder ins Stocken gerät oder sehr langsam abläuft, kann das entweder an der
Leistungsfähigkeit des Rechners liegen oder aber an zu kurzen "Schlafzeiten" für die Threads. In diesem Fall
sollte man in der Klasse "RCXSimulation.java" nach der Zeile
// SimWait sollte mit SimWait in RCX.java abgestimmt sein -------- (anpassen!)
den Wert von SimWait erhöhen. In "RCX.java" muss dann der gleiche Wert nach der Zeile
// SimWait mit SimWait in RCXSimulation.java abgestimmen ---------- (anpassen!)
eingestellt werden.
Wie führt man eine Simulation mit mehreren NQC-Tasks durch?
Werden im Roboter mehrere Aufgaben "parallel" abgearbeitet, so verwendet NQC mehrere Tasks. Auch
diese Situation kann mit dem RCX-Simulator durchgespielt werden, allerdings sind dazu schon mehr
Änderungen am ursprünglichen NQC-Programm erforderlich. Als Beispiel dient das nachfolgende NQC-Programm,
mit dem der Roboter solange im Quadrat fährt, bis er auf ein Hindernis trifft. Nachdem er dem
Hindernis ausgewichen ist, setzt er seine Quadratfahrten fort. Das Task move_square() ist für die
Quadratfahrt zuständig und das Task check_sensors() fragt die Tastsensoren ab, ob es zu eine Berührung
eines Hindernisses kommt. Ist dies der Fall, so wird das Task move_square() gestoppt und
nach dem Ausweichen wieder gestart. Zu Beginn startet das Task main() die beiden anderen
Tasks:
task main()
{
SetSensor(SENSOR_1,SENSOR_TOUCH); // definiert den Sensor
start check_sensors; // startet Task, die Sensor ueberwacht
start move_square; // startet Task, die Quadrate faehrt
}
task move_square() // Task fuer Quadrate
{
while (true) { // Endlossschleife
OnFwd(OUT_A+OUT_C); Wait(200); // faehrt 2 s geradeaus
OnRev(OUT_C); Wait(113); // und fuer 1,1 s nach rechts
}
}
task check_sensors() // Task fuer Sensor
{
while (true) {
if (SENSOR_1 == 1) {
stop move_square; // Stoppt Task fuer Quadrate
OnRev(OUT_A+OUT_C); Wait(50);
OnFwd(OUT_A); Wait(80);
start move_square; // Startet Task fuer Quadrate.
}
}
}
Für die RCX-Simulation müssen in diesem Fall am NQC-Quelltext mehrere Änderungen vorgenommen werden:
- Die Tasks werden in
public void task_main(), public void task_move_square() und
public void task_check_sensors() umbenannt.
- Die start- und stop-Befehle werden nachgestellt:
check_sensors.start(),
move_square.start(), move_square.stop()
- Jedem start-Befehl, der einem stop-Befehl folgt, muss die Zeile
move_square = new Tmove_square(this);
vorangestellt werden (mit sinngemäßer Namensänderung).
Zusätzlich sind auch Änderungen am Rest des Java-Programms erforderlich:
private Tcheck_sensors check_sensors;
muss nach der Zeile
// Thread fuer NQC-Task "task check_sensors()" --------------- (anpassen!)
eingepasst werden. Entsprechend für das Task move_square. Nach der Zeile
// Fuer nebenlaeufige Tasks Threads aktivieren. ------------- (anpassen!)
folgen die beiden Zeilen:
check_sensors = new Tcheck_sensors(this);
move_square = new Tmove_square(this);
Tcheck_sensors und Tmove_square sind Klassen, die in eigenen Dateien
"Tcheck_sensors.java" und "Tmove_square.java" untergebracht werden. Als Beispiel ist die Klasse
Tcheck_sensors wiedergegeben:
public class Tcheck_sensors extends Thread {
private RCX Roboter;
public Tcheck_sensors() {}
public Tcheck_sensors(RCX Robot) {Roboter = Robot;}
public void run() {
Roboter.task_check_sensors();
}
}
Für die Klasse Tmove_square muss dieser Quelltext entsprechend angepasst werden.
Wie passt man den Roboter an?
Das geometrische Erscheinungsbild des Roboters wird in dessen Zeichen-Methode
zeichneRCX(Graphics big) festgelegt.
Nach der Zeile
// Festlegung der Abmessungen des Roboters ------------------- (anpassen!)
sind in der anschließenden for-Schleife die Abmessungen und Farben der Flächen umzuändern.
Der Roboter hat sein eigenes
Koordinatensystem: Der Ursprung liegt in der Mitte zwischen den
antreibenden Rädern. (Es wird von einem differentiellen Antrieb
ausgegangen.) Die x-Achse zeigt in Vorwätsrichtung, die y-Achse
steht senkrecht dazu und geht durch das linke Antriebsrad. Alle
Abmessungen sind in cm anzugeben.
Die Abmessungsdaten der Roboterteile werden alle durch Rechtecke dargestellt.
xOffset, yOffset ist jeweils die Mitte des Rechtecks mit der
Laenge entlang der x-Achse und der Breite entlang der y-Achse.
Je einfacher die Darstellung des Roboters erfolgt,
desto schneller läuft das Programm.
Werden die Abmessungen des Roboters geändert, so müssen auch die Positionsangaben für
die Sensoren jeweils nach den nachfolgenden Zeilen angepasst werden.
// Festlegung der Lichtsensorposition ----------------------- (anpassen!)
// Festlegung der Position des linken Tasters ---------------- (anpassen!)
// Festlegung der Position des rechten Tasters ---------------- (anpassen!)
Für die Berechnung der Bewegung des Roboters muss der Abstand zwischen den Rädern des
differentiellen Antriebs angegeben werden. Dabei handelt es sich streng genommen um den
Abstand der Kontaktpunkte der Räder mit dem Tisch. Die Festlegung erfolgt nach der Zeile:
// Festlegung des Abstands der Radmittelpunkte -------------- (anpassen!)
Welche NQC-Funktionen sind nicht implementiert?
Syntaxstrukturen werden natürlich nur soweit unterstützt, wie sie in Java auch vorhanden sind.
Die Abfragen until und repeat sowie die acquire- und
monitor-Strukturen sind nicht möglich. Da acquire "Ressourcen" und
monitor "Ereignisse (Events)" betrifft, sind die diesbezüglichen Anwenderfunktion
(API) nicht unterstützt.
Java kennt weiterhin keine "Compilerdirektiven", also insbesondere nicht die Definition von
Macros über #define. Die Verwendung lässt sich aber über den Einsatz von Variablen
oder Funktionen vermeiden.
Für den RCX-Simulator wurde bisher nicht detailliert untersucht, inwieweit Probleme mit
der arithmetische Genauigkeit auftreten. Beim RCX-Prozessor sind int-Variablen
16-Bit-Integerzahlen, wohingegen bei Java int-Variablen 32-Bit umfassen. D. h.
insbesondere, dass Zählvariablen im RCX schneller überlaufen als bei der Simulation.
Der Quelltext des RCX-Simulators wurde mit JavaDoc-Kommentaren versehen. Die davon abgeleitete
HTML-Dokumentation ist verfügbar und ist die
zuverlässigste Methode zu klären, ob eine bestimmte
API-Funktion von NQC im RCX-Simulator implementiert wurde. Es muss allerdings erwähnt werden,
dass einige Funktionen noch nicht vollständig implementiert wurden.
Michael Wülker, Fachhochschule Offenburg, 2002-06-26