Unser Roboter kann inzwischen schon einiges und selbst ferngesteuert werden. Jedoch wollen wir zusätzlich einen Autopilot Modus erstellen, wodurch der Raspberry Pi Roboter selbstständig seinen Weg suchen kann. Putzroboter u.ä. nutzen vergleichbare Methoden, um nicht an einem Hindernis stecken zu bleiben.
Wir nutzen dafür einen Servo Motor und einen Ultraschallsensor, womit wir die Entfernung messen. Anschließend erweitern wir den bisherigen Code und lassen den Roboter dorthin fahren, wo am meisten Platz ist.
Zubehör
Diese Erweiterung setzt im Grunde nur auf zwei Komponenten, nämlich den Motor zum Drehen und außerdem auf das Ultraschall Modul zum Bestimmen der Entfernung. Insbesondere habe ich diese Teile verwendet:
- Servo Motor (ein leichter Motor wie der SG90 empfiehlt sich, um nicht zu schwer zu werden)
- Ultraschallsensor
- Widerstände (330Ω und 10kΩ)
Daneben brauchen wir außerdem noch die Jumper Kabel, Heißkleber und evtl. noch das Breadboard.
Befestigung des Motors und Ultraschallsensors
Bevor wir mit dem Anschluss fortfahren, müssen wir die beiden Module an der Roboter Karosserie befestigen. Dafür nutzen wir den Heißkleber, womit wir zuerst den Servo Motor an der Vorderseite (über dem Vorderrad und vor den IR Line-Follow Sensoren). Meine Karosserie hatte einen Papierüberzug über dem Plastik, welchen ich an der zu klebenden Stelle entfernt habe, damit der Kleber besser haftet.
Anschließend habe ich einen der beigefügten Aufsätze des Servomotors genommen und darauf das HC-SR04 Modul mit Heißkleber befestigt (siehe Bilder). Der Aufsatz lässt sich außerdem einfach auf dem Motor fixieren und auch wieder abnehmen. Die Kabel des Servos kannst du an der unteren Seite der Karosserie zum Pi führen (in der Mitte befindet sich ein Loch). Die Jumperkabel vom Ultraschallsensor habe ich zusammengeklebt und mit ein wenig Spielraum an einer Stelle befestigt. Diesen Schritt solltest du allerdings noch abwarten, bis wir die Funktionalität testen und damit auch die beste Position der Kabel finden.
Aufbau und Verkabelung
In den vorherigen Tutorials haben wir einige der GPIOs bereits mit den bisherigen Modulen belegt. Für dieses Tutorial benötigen wir jedoch nur drei zusätzliche GPIOs, wobei ich GPIO12, 13 (US Sensor) und 22 (Servo) verwende. Falls du andere IO Pins nutzen willst, musst du dies im Code später auch entsprechend anpassen.
Das Ultraschallmodul wird über die 5V des Pi’s mit Strom versorgt, wohingegen der Servomotor über die Batterien angeschlossen werden sollte. Masse / Ground vom Raspberry Pi und den Batterien sind (wie bisher auch) weiterhin miteinander verbunden.
Der Ultraschallsensor (ECHO) ist über einen 330Ω Widerstand an GPIO13 angeschlossen und außerdem über einen 10kΩ Widerstand an GND. Die genaue Funktionsweise des US Sensors habe ich bereits in einem vorherigen Tutorial beschrieben. Ebenso sieht es auch mit der Verwendung des Servomotors mittels PWM aus. Details zu beiden Modulen findest du dafür auch in den entsprechenden Tutorials.
Raspberry Pi Roboter Code erweitern
Um im Folgenden Distanzen von verschiedenen Richtungen aus messen zu können, erstellen wir eine neue Datei, in welcher wir für das bewegliche Ultraschallmodul eine Klasse definieren:
sudo nano ultrasonic.py
Die Klasse hat nur wenige Funktionen (zum rotieren und messen) und fällt deshalb nicht all zu lang aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
import RPi.GPIO as GPIO import time class US(): def rotate(self, angle): duty = float(angle) / 9.0 + 2.5 self.pwm.ChangeDutyCycle(duty) def distance(self): GPIO.output(self.triggerPin, True) time.sleep(0.00001) GPIO.output(self.triggerPin, False) start = time.time() stop = time.time() while GPIO.input(self.echoPin) == 0: start = time.time() while GPIO.input(self.echoPin) == 1: stop = time.time() #GPIO.wait_for_edge(self.echoPin, GPIO.BOTH, timeout=100) stop = time.time() return ((stop - start) * 34300.0) / 2.0 def findBestWay(self): # returns the angle where to go next func = reversed if self.rev else list arr = [0] * self.steps for i in func(range(self.steps)): #print(i * 180.0 / (self.steps-1)) self.rotate(i * 180.0 / (self.steps-1)) time.sleep(0.2) arr[i] = self.distance() # dist > 500cm or dist < 5cm means that a timeout occured => value wrong if arr[i] > 500 or arr[i] < 5: for j in range(3): arr[i] = self.distance() if arr[i] < 500 and arr[i] > 5: break else: arr[i] = -1 self.rev = not self.rev return arr def findWay(self, actualIndex): dist = self.distance() def __init__(self, servoPin = 22, triggerPin = 12, echoPin = 13): GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) # save vars self.echoPin = echoPin self.triggerPin = triggerPin GPIO.setup(self.echoPin, GPIO.IN) GPIO.setup(self.triggerPin, GPIO.OUT) # init servo self.servoPin = servoPin GPIO.setup(self.servoPin, GPIO.OUT) self.pwm = GPIO.PWM(servoPin, 100) self.pwm.start(5) self.steps = 7 # should be odd self.rev = False # set servo on 90 degree (mid) self.rotate(90) time.sleep(0.5) |
Gespeichert wird im Editor wie immer mit STRG+O und geschlossen mit STRG+X.
Bevor wir fortfahren wollen wir den Ultraschallsensor noch richtig positionieren. Dazu kannst du testweise die Python Konsole öffnen (sudo python
) und folgende Zeilen eingeben (geschlossen wird mit exit()
):
1 2 3 |
from ultrasonic import US modul = US() modul.rotate(90) |
Wir lassen den Servo Motor also um 90° rotieren und fixieren nun das Ultraschall Modul mittig auf dem Servomotor, sodass die „Augen“ direkt nach vorne schauen. Das machen wir, da der Motor gleich jeweils 90° nach links und recht rotieren wird und dabei die Entfernungen misst, auf welche wir daraufhin den Roboter reagieren lassen.
Anschließend müssen wir unsere bisherige robot.py
Datei noch erweitern. Dafür importieren wir am Anfang die gerade erstellte Klasse:
1 |
from ultrasonic import US |
Außerdem muss die Klasse initiliaisert werden. In der robot.py
Datei, ergänzen wir die die Parameter der __init__
Funktion und fügen eine Zeile hinzu, sodass die Funktion folgend aussieht:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def __init__(self, motor_left_pin1=17, motor_left_pin2=27, motor_right_pin1=23, motor_right_pin2=24, line_follow_pin_left=19, line_follow_pin_right=6, servo_pin=22, us_trigger_pin=12, us_echo_pin=13 ): GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) # init modules self.ultrasonic = US(servo_pin, us_trigger_pin, us_echo_pin) self.motor = L293D(motor_left_pin1, motor_left_pin2, motor_right_pin1, motor_right_pin2) self.line_follow_pin_left = line_follow_pin_left self.line_follow_pin_right = line_follow_pin_right GPIO.setup(self.line_follow_pin_left, GPIO.IN) GPIO.setup(self.line_follow_pin_right, GPIO.IN) |
Nun können wir die Funktion, die auf die gemessenen Distanzen reagiert und den Roboter entsprechend steuert, am Ende unserer Robot
-Klasse anlegen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
def autoPilotUSon(self): actualIndex = int(self.ultrasonic.steps / 2) degreePerStep = 180.0 / (self.ultrasonic.steps-1) while True: dists = self.ultrasonic.findBestWay() print(dists) maxIndex = dists.index(max(dists)) steps = abs(90.0 - maxIndex * degreePerStep) / degreePerStep + 1 # if distance is more than 500cm, the measurement is probably wrong -> stop if dists[maxIndex] > self.motor.DIST_PER_SEC / 2:# and dists[maxIndex] < 500: if maxIndex == int(self.ultrasonic.steps / 2): # straight forward self.motor.forward() elif maxIndex < int(self.ultrasonic.steps / 2): # turn right self.motor.forwardLeft() time.sleep(self.motor.SEC_PER_TURN / 360.0 * degreePerStep * steps) self.motor.forward() elif maxIndex > int(self.ultrasonic.steps / 2): # turn left self.motor.forwardRight() time.sleep(self.motor.SEC_PER_TURN / 360.0 * degreePerStep * steps) self.motor.forward() actualIndex = maxIndex else: print(dists[maxIndex], self.motor.DIST_PER_SEC) self.motor.stop() return |
Autopilot testen
Es wird Zeit den Autopilot Modus zu testen, weshalb wir eine kleine Testdatei erzeugen, in der die Roboter-Klasse geladen wird und der Ultraschall-Autopilot gestartet wird:
sudo nano test_us.py
Die Datei enthält lediglich folgenden Inhalt:
1 2 3 4 5 6 7 8 |
from robot import Robot import time try: r=Robot() r.autoPilotUSon() except: r.motor.stop() |
Nachdem die Datei gespeichert wurde, platzieren wir den Roboter auf dem Boden und rufen die Python Datei von der Konsole aus auf (abgebrochen werden kann mit STRG+C):
sudo python test_us.py
Wichtig: Falls dein Roboter sich zu schnell oder zu langsam dreht, liegt das aller Wahrscheinlichkeit nach an einer falsch eingestellten Geschwindigkeit. Im ersten Tutorial der Reihe haben wir bereits u.a. die Geschwindigkeit auf einer Geraden (DIST_PER_SEC
) und für eine Drehung (SEC_PER_TURN
) definiert. Da diese jedoch vom Untergrund abhängig ist, muss sie ggf. angepasst werden. Ich hatte z.B. beim Testen die Geschwindigkeit auf dem Tisch gemessen, welche aber von der tatsächlichen Geschwindigkeit auf dem Fußboden abweicht, weshalb erst eine Anpassung die Funktionen richtig arbeiten ließ.
Weiter geht es mit dem Folgen der eigenen Stimme.
39 Kommentare
Hallo,
eines ist mir noch nicht klar, wo sind der Raspi und das Steckbord?
Beide müssen doch auf dem „Wagen“ untergebracht sein?
Danke und beste Grüße
Hans
Entschuldigung, ich habe es auf dem Bild gesehen,
Steht alles hinten ‚drauf.
Hans
Hallo Felix,
Bin ein großer Fan deines Blogs. Aber der 10 KOhm als Spannungsteiler mit 330 Ohm kann nicht funktionieren. Die 330 Ohm und die 470 Ohm sind da eine bessere Alternative.
Hallo Felix,
ich habe da ein Problem. Habe alles wie auf dem Schaltplan beschrieben angeschlossen und die Datei ausgeführt. Der Servokopf dreht sich nach links und rechts um jeweils 90°.
Ist es normal, dass der sich permanent dreht?
Leider fährt der Roboter überall gegen die Möbel, weißt du evtl. woran das liegen könnte? Der Roboter macht keine Anstalten auszuweichen und fährt beliebig links und rechts, recht planlos 🙁 leider.
Das klingt so, als ob die Ultraschall Messungen fehlerhaft sind. Hast du das schon überprüft?
Wie überprüfe ich das denn? Und wie sehen die „richtigen“ Ergebnisse aus?
Zum Beispiel wie hier beschrieben: Entfernung messen mit Ultraschallsensor HC-SR04 – Raspberry Pi
Alles klar danke für die schnelle antwort
Hallo Felix, Hallo Klaus
Bei mir war es das gleiche Problem. Tausch mal den 10K gegen 470R und du wirst sehen das es klappt.Am 10K Widerstand fallen 4,84V an die an den 3,3V Raspi Eingang gehen, das ist zuviel.
lg
Harald
Es scheint hier auch unterschiedliche Module zu geben: Bei einigen ist ein 470k Ohm Widerstand ausreichen, bei anderen wird widerum ein 10k Ohm Widerstand benötigt.
Hallo Harald Frenzen,
danke für den Tip. Das werde ich demnächst mal probieren, sobald ich einen passenden Widerstand habe. Hast du den Roboter auch wie im ersten Teil des Tutorials beschrieben kalibriert? (Das müsste ich evtl. nochmal machen)
Funktioniert der Code bei dir bzw. weicht der Roboter den Hindernissen mit dem hier angegebenen Code aus? Das wär echt super.
Eine Frage noch, da hier leider kein Video zu sehen ist. Fährt der Roboter so lange geradeaus bis er ein Hindernis sieht und macht dann einen 90°-Manöver oder wie kann ich mir das vorstellen?
Der Roboter fährt dorthin, wo am meisten Platz ist. Dabei sind bis zu 90° Manöver Möglich, ja.
Hallo Klaus,
Ja ich hab den wie im Tutorial beschrieben kalibriert was auch unbedingt notwendig ist. Danach ist er jedoch wie du auch beschrieben hast mehr oder weniger wahllos durch die Gegend gefahren. Erst das umstellen des Widerstandes hat bei mir den gewünschten Erfolg gebracht. er fährt um Gegenstände herum und weicht diesen aus. Respekt an Felix, klasse Arbeit.
Hab jedoch zusätzlich bei meinem Robbi die Geschwindigkeit gedrosselt weil der Roboter mir auf den Fliesen viel zu schnell war. Hierzu kannst du die beiden Enable-Eingänge des L293D mit einem PWM Signal beschicken.
Lg
Harald
Das klingt ja ganz gut. Werde leider erst nächste Woche wieder Zeit haben das zu testen. Vielen Dank schonmal. Wie genau hast du das mit dem PWM gemacht? Ich habe auch Fliesen und würde den Roboter gern etwas langsamer fahren lassen.
Wäre nett, wenn du den Code dazu evtl. posten könntest, sofern das für dich und Felix okay ist 🙂
Danke und Grüße
Klaus
Hi ,
ich habe das Problem dass beim testen des Autopiloten mit test_us.py einfach gar nichts passiert bzw. das programm direkt in die exception springt.
Wenn ich nur den Servo ansteuere funktioniert er, gleiches beim Sensor.
Gebe ich die test_us.py Zeile für Zeile(ohne try und except) in die python konsole ein so kommt bei der Zeile r.autoPilotUSon() der Fehler “ AttributeError: Robot instance has no attribute ‚autoPilotUSon‘ “
Ich verstehe den Fehler nicht ganz da Ja eigentlich in jeder function ein self attribut steht..?
Würde mich freuen wenn jemand helfen kann 🙂
Gruß
Basti
Der Fehler besagt nur, dass die Funktion nicht vorhanden ist. Hast du in der Robot Datei geschaut und auf Rechtschreibfehler geprüft? Das self hat damit nichts zu tun (ist nur dazu da, um Klassenfunktionen / -variablen anzusprechen).
Hallo,
es funktioniert jetzt schon etwas besser, allerdings habe ich noch eine Frage. Mein Robbi steckt manchmal an einem Schuh oder Ähnlichem fest. Kann man den Code iwie anpassen, sodass er dann rückwärts fährt um aus solchen Situationen zu entweichen? :/
Leider weiß ich nicht wie 🙁
Und eine Frage noch. Was hast du auf dem Ultraschallsensor für eine grüne Platine montiert? War das schon so oder fehlt mir hier vll. ein Stück?
Liebe Grüße
Klaus
Die Platine hatte ich früher einmal montiert, um die Widerstände direkt anzubringen. Da ich das Teil noch so rumliegen hatte, habe ich es einfach verwendet. Ist aber nicht nötig – du kannst auch einfach der Schaltung folgen.
Falss das Ultraschallsignal den Schuh nicht erkennt, geht das nicht ohne weiteres 🙁 Du bräuchtest ggf. weitere Sensoren dazu.
Ja, aber der Schuh ist ja so hoch, dass der Sensorkopf ihn eigentlich erkennen/scannen müsste. Weißt du vll, wie der Roboter langsamer fahren kann oder was ich dafür am code ändern muss? 🙁
Leider klappt das alles nicht wie beschrieben, habe den Robbi kalibriert und alles, links und rechts ist auch nicht vertauscht, also fährt in die Richtungen wie er soll, allerdings weiß ich nicht, wieso er diese blöden Entscheidungen trifft 🙁
Brauch dringend Hilfe 🙁
Hast du zwischenzeitlich die gemessen Werte loggen lassen? Ich meine in einem der anderen Tutorials hat jemand gesagt, dass er PWM genutzt hat, um den Roboter langsamer fahren zu lassen.
Ich habe hierfür auch mal 1,5m x 1m mit Kartons „abgesteckt“, aber da fährt er auch als gegen 🙁
Wow, das ging schnell mit der Antwort. Nein, habe ich nicht, wie kann ich die denn loggen lassen? Die werden ja in der Konsole ausgegeben, während das Script läuft.
Ist es denn auch normal, dass der Robbi jedes mal nach links/rechts abbiegt, wenn der Sensorkopf links/rechts bei 90° angekommen ist? Ich kann mir nicht so recht vorstellen, wie das alles ablaufen soll 🙁
Mit
print()
kannst du etwas auf der Konsole ausgeben lassen. Am besten du loggst den Winkel und den Wert des Sensors. Ich denke, dass das das Problem ist, da wahrscheinlich der Abstand nicht richtig gemessen wird.Also, er fährt auch permanent durch ohne anzuhalten, ist das normal?
Wenn ich den Code richtig verstanden habe, sollten die Motoren stoppen, sobald er <5 cm vor einem Hindernis steht oder? 🙁
Man muss aber nicht den 3. Teil gemacht haben, damit der wagen fährt, oder?
Nein
Ich habe alles nach Plan durchgeführt und angeschlossen, aber schon 3 Servos zerschossen. Hilfe, was kann ich tun?
Wie zerschossen? Hast du zu viel Spannung drauf gegeben? Bald wird ein Tutorial zu einem Servo Driver Board kommen, womit man sehr einfach mehrere gleichzeitig steuern kann.
Hallo zusammen,
kann mir bitte jemand erklären, wie ich die Geschwindigkeit mit diesem pwm drosseln kann? Evtl. mit Codebeispiel? Ich steig da nicht durch leider 🙁
Wär Mega dankbar
Hallo Felix,
erstmal ein frohes neues Jahr 2018.
Dein Blog ist wirklich klasse. Ich konnte schon viele Ideen und Lösungsansätze aus Deinen Tutorials herausholen.
Ich bin ebenfalls ein totaler Anfänger was den Raspberry Pi, Python und Linux angeht.
Ich bin dabei, ein R2D2 Modell (ca.45cm hoch) zum Leben zu erwecken.
Er soll die Sensorik, ähnlich Deinem Roboter und ferner eine Gesichtserkennung (Open CV) und eine ordentliche Sprachausgabe (pico2wave) bekommen.
Ich werde sicherlich, noch viele Informationen aus Deinem Blog gebrauchen können.
Vielen Dank nochmal dafür.
Viele Grüße
Christjan
Hallo Christjan,
vielen Dank und viel Erfolg bei deinem Projekt. Gerne kannst du hier Bilder davon verlinken 🙂
LG, Felix
Hey Christjan, genau das gleiche habe ich auch vor, aber mache das erst ohne Modell. OpenCV hatte ich bereits erfolgreich installiert, aber irgendwie hatte ich doch keine Verwendung dafür. Was willst du damit machen? Und hast du schon raus, wie man den Roboter R2-mäßig piepen lassen kann?
Felix, ich stecke mittlerweile an einem anderen Problem. Ich möchte den Roboter fahren lassen, ohne den Sensor auf einem Servo zu betreiben. Wie muss ich dafür den Code anpassen? Ich habe schon probiert, die Teile, die „servo“ enthalten, einfach wegzulassen, aber das hat nicht funktioniert. Wenn ich test_us laufen lassen, dann passiert einfach nichts.
Die Funktion sollte nur noch eine Messung zurück geben. Aber wie willst du dann ausweichen? Du müsstest vor/zurück fahren und dort könnten Hindernisse warten.
bei mir wackelt der servo mmotor hin und her schon bei der 90 grad drehung am anfang
nach ultrasonic.py der code wo er sich auf die standart position dreht
auserdem hat mein servo motor einen braunen einen roten und einen orangenen draht ist das richtig:
braun= –
rot= +
orange=gpio22
also er dreht sich richtig wackelt danach in ca 1 grad schritten hin und her
habe schon mit dem Programm die Funktion der Entfernungsmessung https://tutorials-raspberrypi.de/entfernung-messen-mit-ultraschallsensor-hc-sr04/ getestet aber beim
Ultraschall Programm sieht es er so aus als ob er auf die Hindernisse zufährt anstatt ihnen auszuweichen .
kann es mir nicht erklären ,da die Entfernungsmessung Ultraschall funktioniert .
habe auch schon die widerstände geändert aber kein erfolg.
mag peter
Hallo, habe alles nachgebaut die Linienerkennung funktioniert.
Beim Hindernissen ausweichen geht gar nichts. Das Servo und die Entfernung funktioniert mit einem Testprogramm.
Wie kann ich beim oben beschriebenen Programm testen ob wer was ausgibt?
Bei python test_us.py wird das Programm sofort wieder beendet nur das Servo ruckelt etwas.
Habe Raspi 4 und Python 3.
Was kann ich tun bin leider Anfänger und komme nicht weiter.