Skip to content
Snippets Groups Projects
Forked from an inaccessible project.

Die Challenges und der Code

Es wurden verschiedene Challenges gestellt, die der Bot mit unseren Skripten lösen sollte.
Die verschiedenen Challenges sind in diesem Repository zu finden.
Das eigene Repository enthält die Skripte zu den einzelnen Challenges. Diese sind aber bei jeder einzelnen Challenge nochmal mitverlinkt. Die einzelne Challenge, die ausgelassen wurde, ist die dritte Challenge, bei der es hieß, es sei nicht zwingend notwendig.

Challenge 0:

Aufgabenstellung

Wie weit ist die minimale Distanz zur Wand, ohne diese zu berühren?

Lösung

Mithilfe der Laser-Sensor-Daten des Wafflebot-Roboters konnte man die Distanz 360 Grad um den Roboter herum messen und ausgeben lassen. Der hierfür notwendige Parameter war msg.ranges[]. Die vier voreingestellten Distanzen waren folgende:

  • msg.ranges[0]: Die Distanz vor dem Roboter
  • msg.ranges[90]: Die Distanz links vom Roboter
  • msg.ranges[-90]: Die Distanz rechts vom Roboter
  • msg.ranges[180]: Die Distanz hinter dem Roboter

Die Angaben der Distanz waren zwischen inf. und 0:

  • inf.: Das nächste Objekt weiter entfernt, als der Sensor messen kann
  • 0: Das nächste Objekt würde quasi im Sensor stehen. 0 ist eigentlich nicht zu erreichen, da um den Sensor herum noch ein Bereich dazuzurechnen ist, da der Wafflebot breiter ist, als nur der Sensor.

Challenge 1:

Aufgabenstellung

Fahr so nah wie möglich zur roten Wand und bleib davor stehen, ohne damit in Berührung zu kommen

Lösung

Die Herangehensweise hierfür war schlicht und ergreifend, in die Methode scan_callback eine if-else-Schleife einzubauen.
In dieser Schleife wurde lediglich festgelegt, dass der Roboter so lange beschleunigen, also geradeaus fahren sollte, bis msg.ranges[0] kleiner oder gleich 0.2 war. Ist dieser Wert erreicht, so wird die Beschleunigung wieder auf den Wert 0 gesetzt; d.h. der Bot bleibt stehen. 0.2 war die perfekte Distanz, um ganz knapp vor der roten Wand stehen zu bleiben und nicht anzufahren.

Der relevante Code (hier zu finden) aus der scan_callback-Methode:

def __init__(self):
    self.safe_distance = 0.2

def scan_callback(self, msg):
    if msg.ranges[0] > self.safe_distance:
        self.vel(100, 0)
    else:
        self.vel(0, 0)
        print("Wall found! Stopping..")
        sys.exit()

Wörtlich bedeutet der Code:
Wenn die vordere Distanz größer als die festgelegte self.safe_distance ist, dann soll der Roboter mit 100% der maximalen Geschwindigkeit fahren. Wenn dies jedoch nicht zutrifft (was beim Unterschreiten der safe_distance erreicht wird), dann fährt der Roboter mit 0% der maximalen Geschwindigkeit; d.h. er bleibt stehen. Anschließend wird noch ein print-statement ausgeführt und das Programm wird mit sys.exit() beendet.

Hier eine Bilder-Kollage wie es abläuft:

Challenge 2:

Aufgabenstellung

Fahr zur roten Wand und bleib in einer sicheren Distanz stehen. Rotiere anschließend gegen den Uhrzeigersinn und fahr zur Holzwand und bleib stehen.

Lösung

Hierfür wurde das Skript aus Challenge 1 mitunter wiederverwendet. Als Lösung wurden nested if-else-Schleifen verbaut und die globalen Variablen self.drive_allowed und self.turn_allowed eingeführt, welche zu Beginn als True initialisiert wurden.

Das Prinzip bleibt wie bei Challenge 1: Es wird erst wieder solange beschleunigt, bis die self.safe_distance erreicht wird. Dieses mal jedoch wurde die Distanz auf 0.4 statt auf 0.2 gesetzt. Grund ist: Bei einer sicheren Distanz von 0.2 steht der Bot sehr nah an der Wand und bei einer darauf folgenden Drehung wird diese Wand mitgerissen. Bei einer sicheren Distanz von 0.4 ist genug Abstand zur Wand, sodass diese nicht umgeworfen wird.

Ist der Bot nun unter der sicheren Distanz von 0.4, wird mit einer weiteren if-else-Schleife geprüft, ob die Intensität vorne bei 2.0 liegt und self.turn_allowed auf True gesetzt ist. Die Intensität 2.0 entspricht der roten Wand. Kommt das Skript in diesen Bereich, so wird die Geschwindigkeit des Bots erst auf 0 gesetzt, um stehen zu bleiben. Anschließend wird mit einer Drehgeschwindigkeit von 85% der maximalen Drehgeschwindigkeit beschleunigt. Durch das nachfolgende sleep(1) wird festgelegt, dass der Bot genau eine Sekunde drehen soll und anschließend wieder auf eine Geschwindigkeit von 0% gesetzt wird und er somit stehen bleibt. Ist dies erfolgt, so wird self.turn_allowed auf False gesetzt, da drehen nun nicht mehr erlaubt ist. Somit fährt der Bot durch den äußeren Part der if-else-Schleife wieder los.

Anschließend kommt man in den elif-Part der Schleife. Hier wird geprüft, ob die Intensität auch nicht wieder 2.0 ist. Dann wird lediglich die Geschwindigkeit wieder auf 0% gesetzt und self.drive_allowed auf False gesetzt und das Skript beendet.

Der relevante Code aus der scan_callback-Methode:

def __init__(self):
    self.safe_distance = 0.4
    self.drive_allowed = True
    self.turn_allowed = True

def scan_callback(self, msg):
    if self.drive_allowed:
        if msg.ranges[0] > self.safe_distance:
            self.vel(80, 0)
        else:
            if msg.intensities[0] == 2.0 and self.turn_allowed:
                self.vel(0, 0)
                print("Red wall found! \nTurning...")
                self.vel(0, 85)
                sleep(1)
                self.vel(0, 0)
                self.turn_allowed = False
                pass
            elif msg.intensities[0] != 2.0:
                print("Wall found!")
                print("Stopping..")
                self.vel(0, 0)
                self.drive_allowed = False
                sys.exit()

Hier nochmal dargestellt in Bildern:

Challenge 4:

Aufgabenstellung

Weiche Gegenständen aus und fahre nicht an

Lösung

Die Herangehensweise an diese Challenge war etwas anders. Hierfür war meine Idee, den kompletten Bereich an Sensorwerten vor dem Wafflebot abzugleichen. Das heißt: Im Prinzip werden alle Laserdaten von msg.ranges[-90] bis msg.ranges[90] ausgelesen und ausgewertet.
Es wurde eine self.stop Variable eingeführt, die festlegt, ob der Bot nun fahren darf oder stoppen soll. Diese ist standardmäßig mit False initialisiert. Auch die self.turn_allowed Variable lässt sich hier wieder finden, die in diesem Fall aber von Anfang an mit False initialisiert wurde. Zuletzt gibt es noch die neue Variable self.timestamp, welche später die aktuelle Zeit in Sekunden abspeichern soll.

Für diese Challenge wurde eine neue Methode namens drive() eingeführt, in der sich der hauptsächliche Code befindet. Neben dieser Methode gibt es auch noch eine Methode mit dem Namen check_to_stop_turn(). Mehr dazu anschließend.

  • Zur drive()-Methode:

    • Zu Beginn wird geprüft, ob self.turn_allowed auf False gesetzt ist, sodass der Roboter nicht drehen soll. Ist dies der Fall, so kommt man in die Schleife hinein. Dann wird in einer weiteren if-Schleife geprüft, ob die sichere Distanz zur Wand in den vorgenannten Richtungen unterschritten ist oder nicht. Hierfür existiert eine Liste namens front_area, in der die Distanzwerte zwischen msg.ranges[-90] und msg.ranges[90] gespeichert sind.
      Ist die sichere Distanz in einem der Bereiche unterschritten, so wird geprüft, ob dort eine rote Wand (also das Ziel) ist. Falls ja, so ist der Bot angekommen, bleibt stehen und die Variable self.stop wird auf False gesetzt. Wenn nicht, wird self.turn_allowed auf True gesetzt und der Roboter hat damit prinzipiell die "Erlaubnis", zu drehen.

    • Damit kommt man in die nächste if-Schleife, in der es um das Drehen des Roboters geht. Hier wird zu Beginn geprüft, ob die Distanz links kleiner als rechts ist. Ist dies der Fall, so dreht sich der Roboter nach rechts und es wird kontinuierlich mit check_to_stop_turn(), ob der Roboter noch weiter drehen darf. Darf er das nicht mehr, also gibt die Methode True aus, so wird der Roboter gestopt und self.turn_allowed wird auf False gesetzt.
      Ist links die Distanz jedoch größer als rechts, so dreht sich der Roboter nach links und es wird, wie gerade erklärt, wieder geprüft, ob der Roboter den Dreh stoppen soll oder nicht.

  • Zur check_to_stop_turn()-Methode:
    Anfangs wird der Bereich, der zu prüfen ist, festgelegt und wieder in einer Liste namens front_area abgespeichert. Dann dreht sich der Wafflebot solange, bis die vordere Distanz größer als 1.0, die Distanz links und rechts jeweils größer als 2.0 und jede einzelne andere Distanz größer als die sichere Distanz ist. Erst dann wird True zurückgegeben und der Roboter soll somit das Drehen beenden. Ansonsten wird immer False zurückgegeben und der Bot dreht sich solange weiter.

Die beiden neuen Methoden als Code:

def __init__(self):
    self.safe_distance = 0.4
    self.turn_allowed = False
    self.stop = False
    self.timestamp = 0

def drive(self, msg):
    front = msg.ranges[0]
    left = msg.ranges[90]
    right = msg.ranges[-90]
    rear = msg.ranges[180]
    left_area = msg.ranges[330:360]
    right_area = msg.ranges[0:30]
    front_area = left_area + right_area

    if not self.stop:
        if not self.turn_allowed:
            for distance in front_area:
                if distance > self.safe_distance or distance == 0.0:
                    self.vel(60, 0)
                else:
                    # stop at red color
                    if msg.intensities[0] == 2.0:
                        self.vel(0, 0)
                        self.stop = False
                    else:
                        self.turn_allowed = True
                        self.timestamp = time()

        if self.turn_allowed:
            if left < right:
                print("Turning right!")
                self.vel(0, -50)
                sleep(0.25)
                if self.check_to_stop_turn(msg):
                    sleep(0.1)
                    self.vel(0, 0)
                    self.turn_allowed = False
            else:
                print("Turning left!")
                self.vel(0, 50)
                sleep(0.25)
                if self.check_to_stop_turn(msg):
                    sleep(0.1)
                    self.vel(0, 0)
                    self.turn_allowed = False

def check_to_stop_turn(self, msg):
    left_area = msg.ranges[330:360]
    right_area = msg.ranges[0:30]
    front_area = left_area + right_area
    for distance in front_area:
        if msg.ranges[0] > 1.0 and msg.ranges[90] > 0.2 and msg.ranges[-90] > 0.2 and distance > self.safe_distance:
            print("Turn stopped!")
            return True
        else:
            return False

Der Ablauf in Bildern:

Blockquotes

Kermit from the Muppet Movie:

Life's like a movie. Write your own ending.

Lists

  • apple
  • banana
  • cherry

Links

More info can be found on Python docs – Exceptions.

Images

Charles J. Sharp, CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0, via Wikimedia Commons

Code snippets

We can use find -iname STRING to search for a file in Unix.

The following code1 shuffles a list:

from random import shuffle

foo = [1, 2, 3, 4]
shuffle(foo) 
print(foo)

The end

If you want to try these, follow this Markdown tutorial. It also includes a web-based code editor.

  1. Code from PythonSnippets.dev