Agent 00L – Liebesgrüße aus Helsinki
Linux Robotics
Hast Du Töne?
In den Spybot ist auch eine Tonausgabe integriert. Eine Übersicht, mit welchen Befehlen sie angesprochen werden kann, gibt Kasten 5. Den Laser spricht man entweder mit On(OUT_C); oder SendVLL(n
); (wobei n zwischen 0 und 127 liegt) an. Bleiben die Befehle, die sich mit internen Timern etc. beschäftigen, und die Event-Steuerung mit bis zu 16 verschiedenen, frei konfigurierbaren Ereignissen. Für meine Zwecke benötigte ich lediglich den Befehl Random(n
);, der einen zufälligen Wert zwischen 0 und n zurückliefert. Die weiteren Ergebnisse meiner Recherchen habe ich aber auf die Heft-CD (Agenten-CD?) eingeschleust; alternativ finden sich die Infos unter [3].
Kasten 5: NQC-Töne
PlaySound(sound) // sound kann sein // SOUND_CLICK, SOUND_DOUBLE_BEEP, SOUND_DOWN // SOUND_UP, SOUND_LOW_BEEP, SOUND_FAST_UP PlayTone(frequenz, dauer) // PlayTone(440,50) spielte // eine halbe Sekunde den Kammerton a MuteSound() // Stoppt alle Töne und schaltet stumm UnmuteSound() // Reinitialisiert die Tonausgabe und lässt Töne wieder zu ClearSound() // Löscht hinterherhängende Töne aus dem Speicher
Es wird ernst
Mit allem Notwendigen für die Programmierung des Spybots ausgestattet, legte ich mich vor dem besagten Gebäude auf die Lauer. Eine Möglichkeit, nachts unentdeckt ins Gebäude zu kommen, wollte mir einfach nicht einfallen, bis ich schließlich doch die rettende Idee hatte – die Lüftungsschächte. Deren Außenmauern waren nur mit leichten Gittern versehen und würden als Eingangstür für den kleinen Roboter dienen. Als falscher Pizzaverkäufer getarnt nahm ich den Aufbau des Gebäudes und insbesondere dort die Lüftung genauer unter die Lupe. Die Lüftungsschächte verliefen quer durch die Etagen. Große vergitterte Flächen sorgten für die nötige Luftzirkulation.
Damit stand mein Plan fest: Ich würde den Spybot nachts durch eine der leicht gesicherten Öffnungen ins Gebäude bringen, absetzen und dort losfahren lassen. Immer wenn der Spybot über ein Lochgitter führe, stiege die Helligkeit an. Wenn der Roboter das Loch passierte, würde es wieder dunkler – der Lichtsensor sollte dies erfassen können. Danach sollte der Roboter wieder ein Stück zurückzufahren bis aufs Gitter, einen Höllenlärm veranstalten und so die Alarmanlage auslösen, um anschließend schnell abzuhauen.
Im Übergang vom Gitterbereich in den verdeckten ist ein Helligkeitsrückgang von zehn Prozent, was durch periodisches Abfragen des Lichtsensors erkannt wird. Dieses erledigt der Task lichtkontrolle() im NQC-Programm helsinki.nqc (Listing 3, nur auf der Heft-CD). Weiterhin sollte der Spybot zufällig durch die Gänge brausen. An jeder T-Kreuzung oder in Sackgassen sollte er sich (Random()-gesteuert) für eine neue Richtung entscheiden – also eine Links- oder Rechtsdrehung vollführen. Dafür ist der Task hindernis() zuständig. Wichtig war an dieser Stelle, dass sich die beiden Aktionen lichtkontrolle() und hindernis() nicht gegenseitig behindern sollten. Es wäre fatal für den Auftrag, wenn der Spybot in einer Sackgasse ein ständiges Spektakel ausführte, nur weil vielleicht ein unvorhergesehener Lichteinfall aufträte. Um das zu verhindern, verwendete ich für die beiden Tasks die Semaphor-Technik: Ein Task sperrt vor einer Aktion alle anderen durch Setzen einer globalen Variablen sem. Nur wenn diese den Wert 0 hat, beginnt ein Task mit seiner speziellen Aktion. Solange diese ausgeführt wird, wird jede andere durch temporäres Setzen der Variable auf 1 gesperrt.
Das Programm helsinki.nqc enthält weiterhin eine Unterfunktion warten(), die den Spybot für die erste halbe Stunde an seinem Ort verharren lässt – genug Zeit, mich zu verdrücken. Nach etlichen Testfahrten in meinem Labor konnte ich die Wait()-Zeiten soweit anpassen, dass der Spybot zufriedenstellende 90-Grad-Drehungen fuhr.
Ich schickte Nachricht an den Pinguin, dass ich bereit war. In der verabredeten Nacht schraubte ich einen Spalt ins Außengitter der Belüftung, übergab den Spybot seinem Schicksal und sah zu, dass ich wegkam, denn schon eine gute halbe Stunde später schrillten im Gebäude die ersten Sirenen.
Agenten-
: <I>helsinki.nqc<I>
#include "spy.nqh"
// Semaphor zum gegenseitigen
// Sperren von Anstoßen und Laser
int sem=0;
void spektakel()
{
until(sem==0) // warten bis Semaphor frei
{
sem=1; // Semaphor sperren
}
PlaySound(SOUND_DOWN);
Toggle(OUT_A+OUT_B); // zurückfahren
Wait(100);
Float(OUT_A+OUT_B); // auslaufen lassen
// Drehen
Rev(OUT_A); Fwd(OUT_B); On(OUT_A+OUT_B);
repeat(8)
{
/*
// Laser an, Krach schlagen
*/
SendVLL(0);
PlaySound(SOUND_FAST_UP);
Wait(50);
}
Off(OUT_C); // Laser aus
// auf Schleichfahrt
MuteSound(); ClearSound(); UnmuteSound();
OnFwd(OUT_A+OUT_B); // abhauen
sem=0; // Semaphor freigeben
}
task lichtkontrolle()
{
int alte_helligkeit;
int aktuelle_helligkeit;
int intervall_lichtmessung=50;
alte_helligkeit=SENSOR_2; // initialisieren
while(true) // ständig die Helligkeit beobachten
{
aktuelle_helligkeit=SENSOR_2;
/*
// mehr als 10% dunkler geworden?
*/
if(aktuelle_helligkeit+10<alte_helligkeit)
{
spektakel(); // Inline-Funktion aufrufen
// Helligkeit sichern
alte_helligkeit=aktuelle_helligkeit;
}
Wait(intervall_lichtmessung); // Warten auf nächsten Messung
}
}
task hindernis()
{
while(true) // Warten auf Drucksensor
{
if(SENSOR_1==1) // Drucksensor betätigt?
{
until(sem==0) // warten bis Semaphor frei ist
{
sem=1; // Semaphor sperren
}
Toggle(OUT_A+OUT_B); // Motoren in umgekehrte Richtung
/*
// eine Sekunde in andere Richtung fahren lassen
*/
Wait(100);
if(Random(1)==0) // zufällige Drehrichtigung wählen
{
Toggle(OUT_A);
}
else
{
Toggle(OUT_B);
}
Wait(60); // drehen lassen
Fwd(OUT_A+OUT_B); // Beide Motoren wieder vorwärts
sem=0; // Semaphor freigeben
}
}
}
sub warten()
{
int zeit=0;
PlaySound(SOUND_DOUBLE_BEEP);
ClearTimer(0);
/*
// ca. eine halbe Stunde warten
*/
until(zeit>=15000)
{
zeit=Timer(0);
}
PlaySound(SOUND_LOW_BEEP);
}
task main()
{
int geschwindigkeit=7; // maximale Geschwindigkeit
// Initialisierung:
// Drucksensor erwartet "an" oder "aus"
// Lichtsensor auf 0 bis 100
SetSensorMode(SENSOR_1,SENSOR_MODE_BOOL);
SetSensorMode(SENSOR_2,SENSOR_MODE_PERCENT);
SetPower(OUT_A+OUT_B,geschwindigkeit);
/*
// Besonderheit Gigamesh G60 berücksichtigen
// wo alles umgekehrt definiert sein müsste
*/
SetGlobalDirection(OUT_A+OUT_B,OUT_REV);
warten();
OnFwd(OUT_A+OUT_B); // Los gehts
start lichtkontrolle; // Lichtkontrolle ein
start hindernis; // Hinderniskontrolle ein
}
Glossar
wine
ist eigentlich kein echter Emulator, der Windows in Linux emuliert. Vielmehr nimmt wine die System- und Bibliotheksaufrufe an Windows auf und biegt diese in Linux-Aufrufe um. Der LinuxUser hat in seiner Ausgabe 10/2000 ausführlich über wine berichtet [1].
User.ini
Datei mit benutzerspezifischen Konfigurationen von Programmen, die auf unter Windows (oder eben unter wine) laufen.
call by value
Nur der Wert eines variablen Parameters wird übergeben. Ändert sich der Wert des Parameters innerhalb der Funktion, hat er nach Funktionsende den ursprünglichen Wert.
call by reference
Die Änderungen eines variablen Parameters innerhalb einer Funktion bleiben auch nach Ende der Funktion noch erhalten. Eine solche Übergabe wird in NQC mit dem Symbol & im Funktionskopf deklariert.
Infos
[1] Peter Ganten: "Windows-Programme unter Linux – Red, Red Wine", LinuxUser 10/2000, S. 15 http://www.linux-user.de/ausgabe/2000/10/015-wine/wine.html
[2] Die wine-Homepage: http://www.winehq.com
[3] NQC: http://www.baumfamily.org/nqc/



