Zum Hauptinhalt springen

Auf einer Linie mit Kreuzungen und Unterbrechungen navigieren

Das Folgen einer Linie mit Kreuzungen, Unterbrechungen oder Ähnlichem erhöht die Komplexität des Linienfolgers.

Neben dem Regler für den Linienfolger muss der Roboter zusätzlich noch so programmiert werden, dass er regelmäßig überprüft, ob er sich gerade über einer Kreuzung oder Unterbrechung befindet. Hat der Roboter ein Hindernis erkannt, muss er abhängig vom Hindernis eine vorher festgelegte Aktion (links abbiegen, rechts abbiegen, weiterfahren, etc.) ausführen.

Im Folgenden sind ein paar Anregungen zum Erkennen einer Kreuzung oder zum geschicketen Reagieren auf Kreuzungen aufgeführt. Es handelt sich hier nicht um eine vollständige Lösung des Problems, sondern vielmehr um mögliche Ansätze für eigene Realisierungen.

Auf einer Linie mit Kreuzungen navigieren

Linienfolger mit Kreuzungen

Der vollständige Parcours mit Kreuzungen kann auch im A0 Format heruntergeladen werden.

caution

Hinweis: Die Nummerierung der Ereignisse (Kreuzungen, Wüste, Unterbrechung, etc ...) ist nur bedingt vollständig. Abhängig vom Verhalten des Roboters kann es notwendig sein, auch andere markante Stellen als Kreuzungen zu betrachten.

Hauptprogramm void loop()

Um den fortgeschrittenen Linienfolger programmieren zu können, ist es wichtig ein gut strukturiertes Hauptprogramm zu haben. Steht das Hauptprogramm, kann man sich relativ gut auf die einzelnen Teilfunktionen konzentrieren, ohne die Übersicht über die verschiedenen Aufgaben zu verlieren. Im wesentlichen soll im Hauptprogramm unterscheiden werden, ob der Roboter gerade der Linie folgen soll (folgeLine()) oder auf besondere Events (Kreuzung, Wüste, Unterbrehung) reagieren (reagierenAufEvent()) muss.

// Hauptprogramm für den fortgeschrittenen Linienfolger
int eventNummer = 0;

void loop() {

if (!istEvent()) // keine Kreuzung, Wüste, etc. also Linie
{
folgeLine(); // Regler zum Linienfolgen
}
else // Event erkannt
{
eventNummer++; // Zählvariable für die Events
if (eventNummer < 12) {
reagierenAufEvent(eventNummer);// je nach Event: abbiegen, fahren, ...
}
else { // letztes Event ist erreicht
motors.setSpeeds(0, 0);
lcd.print(" -ENDE- ");
}
}
}

Im oben dargestellten Beispielcode wird in der ersten if-Abfrage die Funktion istEvent() aufgerufen. Die Funktion istEvent() gibt true zurück, sobald sich der Roboter über einer Kreuzung oder ähnlichem (Wüste, Unterbrechung) befindet. Da in der if-Abfrage die Aussage mit ! negiert wird, folgt der Roboter so lange der Linie mit dem Befehl folgeLine(), wie sich der Roboter nicht auf einer Kreuzung befindet.

Wurde ein Event erkannt, wird der else Zweig aufgerufen und die Zählvariable eventNummer für die aktuelle Kreuzung um eins erhöht. Nachdem überprüft wurde, ob das letzte Event noch nicht erreicht wurde (if (eventNummer < 12)), wird die Funktion reagierenAufEvent(int eventNummer) mit der Eventnummer aufgerufen.

In der Funktion reagierenAufEvent(int eventNummer) sollte mit einer switch-case-Anweisung oder ähnlichem für jedes Event (eventNummer) eine Anweisung hinterlegt sein, wie sich der Roboter an entsprechender Stelle zu verhalten hat (links abbiegen, rechts abbiegen, weiterfahren, etc.).

Teilfunktionen

Eventerkennung isEvent()

Wie oben beschrieben, soll die Funktion istEvent() nur erkennen, ob sich der Roboter über einer Kreuzung, Wüste oder Unterbrechung befindet. Hat die Funktion eine Kreuzung erkannt, soll sie den boolschen Wert true zurückgeben. Ansonsten ist der Rückgabewert false. Dafür reicht es aus, die einzelnen Sensorwerte mit einem Schwellwert (z.B. 500) zu vergleichen und sich für die einzelnen Sensoren eine geeignete Bedingung zum Erkennen einer Kreuzung zu überlegen.

In der folgende Abbildung sind einige mögliche Kreuzungstypen und die zugehörigen Zustände der Bodensensoren dargestellt. Nutzen Sie diese Abbildung, um sich eine geeignete Bedingung zum Erkennen eines Events (Kreuzung, Wüste, Unterbrechung) zu überlegen. Eventuell ist es einfacher, sich eine Bedingung dafür zu überlegen, dass der Roboter sich nicht auf der Linie befindet, der gefolgt werden soll.

Verknüpfen Sie hierbei den Zustand verschiedener Sensoren mit logischen Ausdrücken (UND &&, ODER ||).

Erkennen von Kreuzungen
bool istEvent(){
/* Gibt `true` zurück, wenn ein Event (Kreuzungen,
Wüsten oder Unterbrechungen) erkannt wurde. Verwendet
die Bodensensoren zum Erkennen von Events. */
}
Aufgaben
  1. Schreiben Sie ein Programm, welches den Roboter einer Linie folgen lässt und bei jeder Kreuzung (bzw. Wüste oder Unterbrechung) stehen bleibt. Nachdem ein Taster betätigt wurde, soll der Roboter wieder der Linie bis zur nächsten Kreuzung folgen (ohne abzubiegen oder ähnliches). Nutzen Sie die Vorlage.

     // --- Unvollständige Code-Vorlage ---

    void loop() {
    if (!istEvent()){ // keine Kreuzung, also Linie
    folgeLinie(); // Regler zum Linienfolgen
    }
    else{ // Kreuzung (oder ähnlich) erkannt
    // 1. Roboter bleibt stehen
    // 2. Nach Tasterdruck soll der Roboter
    // bis zur nächsten Kreuzung weiterfahren
    }
    }

Auf die unterschiedlichen Events reagieren reagierenAufEvent()

void reagierenAufEvent(int pKreuzNummer) {
// Für jedes Event wird die zugehörige Aktion als `case` hinterlegt.
switch (pKreuzNummer) {
case 1:
case 2:
case 5:
abbiegen(RECHTS);
break;

case 3:
fahrenWueste();
break;

// Weitere Anweisungen ...
}
}

Funktion abbiegen(int pRichtung)

Die Funktion abbiegen(int pRichtung) soll den Roboter an einer Kreuzung nach links bzw. rechts abbiegen lassen. Der Parameter pRichtung gibt dabei die Richtung an. Damit der Code gut lesbar ist, können mit den Zeilen

#define LINKS 1
#define RECHTS 2

die Definitionen LINKS und RECHTS für die Werte 1 und 2 vorgenommen werden. Wird die Funktion dann aufgerufen, wird als Parameter nicht 1 oder 2 sondern LINKS und RECHTS übergeben. Dadurch wird die Lesbarkeit des Codes deutlich erhöht.

abbiegen(LINKS);  // entspricht abbiegen(1);

Nachdem der Roboter mit der Funktion abbiegen() abgebogen ist, sollte er die zu folgende Linie erkennen und dieser auch sofort problemlos mit der Funktion folgeLinie() folgen können. Daher ist gut, wenn der Roboter am Ende der Funktion abbiegen in möglichst exakt in Fahrtrichtung auf der neuen Linie steht und gleichzeitig der Mittelsensor die Linie erkennt.

Abbiegen eines Roboters an einer Kreuzung
// An Kreuzungen nach links oder rechts abbiegen
void abbiegen(int pRichtung){
// 1. Der Roboter fährt mittig auf die Kreuzung
// 2. Der Roboter dreht sich nach links/rechts
// 3. Das Drehen wird gestoppt, wenn der Roboter
// mittig auf dem neuen Weg steht
}

Hilfsfunktion zum Debuggen

Die Hilfsfunktion zeigenWerte() gibt auf dem Display zuerst die Sensorwerte des ganz linken und ganz rechten Sensors aus. Nach zwei Sekunden wird die Anzeige geändert, und die Werte des mittleren Sensors und die Position der Linie wird ausgegeben.

Verwenden Sie die Funktion zum Testen, ob der Roboter an den Kreuzungen auch wirklich das erkennt, was sie vermuten. Nutzen Sie die, oder eine ähnliche Funktion, lieber einmal zu viel als zu wenig. Es kann Ihnen viel Mühe und Kopfzerbrechen ersparen.

void zeigenWerte() {
char msg[8];
// wenn gewollt, aktualisieren der Sensorwerte
// int lPosition = lineSensors.readLine(lineSensorValues);

sprintf(msg, "0: %4d", lineSensorValues[0]); // Sensor ganz links
lcd.gotoXY(0, 0); lcd.print(msg);
sprintf(msg, "4: %4d", lineSensorValues[4]); // Sensor ganz rechts
lcd.gotoXY(0, 1); lcd.print(msg);
delay(2000);

sprintf(msg, "2: %4d", lineSensorValues[2]); // Mittelsensor
lcd.gotoXY(0, 0); lcd.print(msg);
sprintf(msg, "Pos %4d", lPosition); // Position der Linie
lcd.gotoXY(0, 1); lcd.print(msg);
delay(2000);
lcd.clear();
}

Zum Ausgeben der Werte auf dem Display wird die Funktion sprintf() genutzt. Die Funktion erlaubt es Text und Zahlenwerte nach bestimmten Regeln zu formatieren. Der Formatierte Text wird in den char-Array msg[] geschrieben und anschließend mit lcd.print(msg1) auf dem LCD-Display ausgegeben.

caution

Das LCD-Display des Zumo 32u4 hat pro Zeil nur 8 Zeichen. Also sollten nicht mehr als 8 Zeichen pro Zeile ausgegeben werden.