Robotertraum
Lego-Roboter per Bluetooth programmieren
Linienverfolger
Einem Roboter beizubringen, einer schwarzen Linie zu folgen, ist eine einfache Aufgabe mit gutem Lerneffekt. Es bietet sich deshalb als Einstieg in die Programmierung von Robotern abb.
Der Roboter fährt, solange er sich auf der Linie befindet, geradeaus. Sobald er die Linie verlässt, versucht er, sie wieder zu finden. Da er nicht wissen kann, nach welcher Seite er die schwarze Linie verlassen hat, muss er in beide Richtungen suchen. Am leichtesten findet er die Linie wieder, indem er abwechselnd in die verschiedenen Richtungen dreht und dabei den maximalen Drehwinkel immer mehr zu erhöht. Dies tut der Roboter so lange, bis er die schwarze Linie wieder gefunden hat. Findet er die Linie, fährt er erneut geradeaus, bis er die Linie wieder verlässt. Die Umsetzung dieses Verfahrens in ein NXC-Programm finden Sie in Listing 1.
Der Start der Software ruft den main-Task auf (Zeile 14). Die while-Schleife ab Zeile 18 sorgt dafür, dass dieser laufend wiederholt wird. Vor dieser Endlosschleife müssen noch drei Befehle im Programm enthalten sein, die nur einmal ausgeführt werden sollen: Man muss den Lichtsensor starten, den Startwert für die maximale Drehzahl setzen und den Startwert der Variable state setzen, auf die der Artikel unten noch genauer eingeht.
Alle anderen Befehle soll der Roboter endlos wiederholen. Als erstes liest das Programm zwei Sensoren aus (Zeile 19 und 20) und speichert die Werte in den Variablen light und rot_c, die das Programm in Zeile 8 definiert hat.
Der Roboter kann sich in drei verschiedenen Zuständen befinden (DRIVE, TURN_LEFT, TURN_RIGHT). Deshalb ist es sinnvoll, die Software statusbasierend aufzubauen: In einem Zustand soll der Roboter einfach nur geradeaus fahren und in den anderen zwei Zuständen soll er sich entweder nach links oder nach rechts drehen. Dazu dient der switch-Aufruf, der je nach dem Wert der Variable state andere Befehle ausführt. Verschiedene Konstanten für die einzelnen Zustände halten den Quelltext übersichtlicher. Diese Konstanten – jede mit einem eindeutigen Wert – finden sich im Definitionskopf ganz am Anfang des Programms.
Startet das Programm zum ersten Mal, so befindet sich der Roboter im Status DRIVE (Zeile 17). Zuerst gilt es – für den Fall, dass der Robot gerade die Linie wieder gefunden hat – den maximalen Drehwert zurückzusetzen. Der Befehl in Zeile 24 lässt den Roboter mit der in der Konstante GESCHW definierten Geschwindigkeit geradeaus fahren. Die if-Abfrage in Zeile 32 überprüft, ob der Lichtsensor einen Helligkeitswert über dem in LIGHT definierten Grenzwert gemessen hat. Ist das der Fall, befindet sich der Roboter nicht mehr auf der Linie – und wechselt durch das Ändern der Variable state den Status zu TURN_LEFT.
Nun dreht sich der Roboter nach links. Mit den ersten zwei Befehlen dieses Zustandes, dreht der Roboter den linken Motor rückwärts und den rechten Motor vorwärts – so wendet er ähnlich wie ein Kettenfahrzeug auf der Stelle nach links. Liegt anschließend der Helligkeitswert wieder unter den Grenzwert, dann hat der Roboter die Linie erneut gefunden. Er wechselt zurück in den Status DRIVE und setzt den Rotationssensor zurück, damit er im DRIVE-Status mit beiden Motoren synchron geradeaus fahren kann. Liegt der Helligkeitswert dagegen nicht unter dem Grenzwert, erfolgt eine weitere Abfrage, ob der Rotationssensor den maximalen Drehwert überschritten hat. Ist das der Fall, so erhöht sich der in turn gespeicherte Drehwert. Anschließend wechselt der Status zu TURN_RIGHT und der Rotationssensor wird zurück gesetzt, damit es bei der Drehwertkontrolle in die andere Richtung keine Probleme gibt (Zeilen 38 und 39).
Im Status TURN_RIGHT verhält sich der Roboter sinngemäß gleich wie bei TURN_LEFT – mit der Ausnahme, dass er sich nach rechts dreht,.
#define DRIVE 0
#define TURN_RIGHT 1
#define TURN_LEFT 2
#define GESCHW 30
#define LIGHT 40
#define TURN 10
int state, light, rot_c, turn;
sub dreherweiterung() {
turn = turn+2*TURN;
}
task main() {
SetSensorLight(IN_1);
turn = TURN;
state = DRIVE;
while(true) {
light = Sensor(IN_1);
rot_c = MotorRotationCount(OUT_C);
switch(state) {
case DRIVE:
turn = TURN;
OnFwdReg(OUT_BC, GESCHW, 1);
if(light > LIGHT) {
state = TURN_LEFT;
}
break;
case TURN_LEFT:
OnFwd(OUT_B,GESCHW);
OnRev(OUT_C,GESCHW);
if(light < LIGHT) {
state = DRIVE;
ResetRotationCount(OUT_BC);
}
else if(rot_c <= -turn) {
dreherweiterung();
state = TURN_RIGHT;
ResetRotationCount(OUT_BC);
}
break;
case TURN_RIGHT:
OnFwd(OUT_C,GESCHW);
OnRev(OUT_B,GESCHW);
if(light < LIGHT) {
state = DRIVE;
ResetRotationCount(OUT_BC);
}
else if(rot_c >= turn) {
dreherweiterung();
state = TURN_LEFT;
ResetRotationCount(OUT_BC);
}
break;
}
}
}
Noch mehr
Die geschilderte Aufgabe lässt sich beliebig erweitern – etwa durch ein Szenario, bei dem der Roboter zunächst ebenfalls der schwarzen Line folgen, dann aber ein Labyrinth durchqueren und einen Ball einsammeln muss (Abbildung 2).
Der Roboter besitzt einen Lichtsensor zum Messen des Helligkeitswerts des Bodens, einen Ultraschallsensor, um die Entfernung zu Objekten messen zu können, sowie einen Geräuschsensor, der Laute wahrnimmt. Außerdem verfügt der Roboter über drei Motoren. Zwei davon benutzt er wie bei dem Linienverfolger für die Fortbewegung, der dritte kommt für den Greifmechanismus zum Einsatz.
Der Roboter verfolgt zuerst die schwarze Linie – dieser Abschnitt funktioniert wie schon beschrieben. Sobald der Roboter ein Objekt in der Nahe wahrnimmt (Abbildung 3), weiß er, dass er sich in einem Labyrinth befindet. Dieses durchfährt er dann, indem er sich zuerst nach rechts dreht und dabei misst, ob in näherer Entfernung eine Wand vor ihm befindet. Ist das nicht der Fall ist, dreht er sich noch ein bisschen weiter (weil sich der Ultraschallsensor in der Mitte befindet) und fährt dann los.
Anderenfalls dreht er bis zu einem Winkel von 90 Grad. Hat er dann immer noch keinen Weg gefunden, dreht er in die andere Richtung. Nimmt der Lichtsensor eine schwarze Linie wahr und der Ultraschallsensor signalisiert, dass es sich um diejenige handelt, die zum Ball führt, dreht sich der Roboter in deren Richtung und verfolgt die Linie, bis er sich in der Nähe des Balls befindet. Nun fährt er zum Ball und greift diesen. Anschließend dreht er sich in die andere Richtung und verfolgt die Linie bis zur grünen Fläche, ohne sich von der Abzweigung (Abbildung 4) irritieren zu lassen. Ist der Roboter dort angekommen, stoppt der Anwender ihn durch ein Klatschen.
Den kompletten, kommentierten Quelltext für das zweite Szenario finden Sie auf der Heft-CD. Dort sind alle im Quelltext verwendeten Befehle beschrieben. Außerdem befindet sich auf der CD ein Video des Roboters beim Absolvieren der Aufgabe. Weitere Informationen zur Programmierung mit NXC finden Sie auf der Homepage des Autors [5].
[1] Developer Kits: http://mindstorms.lego.com/overview/NXTreme.aspx
[2] NXT-Manager Entwicklerseite: http://lukas.internet-freaks.net/nxtmanager.php
[3] LEGO::NXT: http://nxt.ivorycity.com
[4] NXC: http://bricxcc.sourceforge.net/nbc
[5] Tutorial: http://lukas.internet-freaks.net/nxt.php
[6] IT-Seminar Lörrach: http://it-seminar-loerrach.de
[] Lukas Probst ist 17 Jahre alt und studiert am Hans-Thoma-Gymnasium in Lörrach. Seine ersten Erfahrungen mit der Programmierung von Robotern sammelte er am IT-Seminar-Lörrach.



