Havesomepi
Ein Raspberry-Pi-gesteuertes Modellauto
Hier beschreibe ich ein Bastelprojekt, bei dem ich ein Modellauto konstruiert habe, das einen Raspberry Pi als Schaltzentrale besitzt. Das Auto kann wahlweise autonom fahren oder über ein Gamepad gesteuert werden.
Inhalt:
Allgemeines
Ich werde hier keine vollständige Anleitung meines Projekts anbieten. Stattdessen will ich ganz grob erläutern, aus welchen Bauteilen und Grundkonzepten das Modellauto besteht und ich werde die wichtigsten Arbeitsschritte grob erklären. Ich hoffe, dass mit dieser Anleitung meine Leser dazu anregen kann, ihre eigenen Ideen in einem ähnlichen Projekt umzusetzen, nicht aber mein Modellauto komplett nachzubauen.
Gestell und Motoren
Das Grundgerüst des Modellautos bildet der Bausatz Robot-03 von Joy-It. Er enthält vier Elektromotoren mit Rädern sowie die Grundplatte, jedoch keinerlei Elektronik. Bei dem Set lagen Kabel bei, mit denen die Elektromotoren mit der Motorsteuerung verbunden werden künnen. Statt diese Kabel anzulüten habe ich jedoch andere Kabel mit Steckverbinder gekauft, damit ich die Motoren und die Steuerung zu Umbau- und Wartungsarbeiten voneinander trennen kann.
Elektronik
Mit ausnahme des Raspberry Pis und der Abstandssensoren wurde die gesamte Elektronik auf eine Platine gelötet, um den Kabelsalat gering zu halten und die Bauteile möglichts fest zu verbauen. Ein paar Tipps habe ich dafür parat:
- Es ist sinnvoll, für GND und die 5V-Spannung je eine lange Reihe auf der Platine zu reservieren, da man viele Geräte mit diesen Potentialen verbinden muss.
- Da man die Platine häufig abmontieren muss (bspw. wenn man ein neues Bauteil hinzufügen möchte), ist es sinnvoll, externe Bauteile mit Steckverbindungen zu verbinden, damit man die Teile für Umbaumaßnahmen wieder sauber trennen kann.
Stromversorgung
Ich nutze zwei separate Stromquellen für mein Projekt: Für die elektronischen Bauteile (inkl. Raspberry Pi, Arduino, Ultraschallsensoren, Beleuchtung) nutze ich eine Powerbank mit 5V Spannung. Die Powerbank wird direkt mit dem Raspberry Pi verbunden, dieser wiederum wird über die GPIO-Pins mit der Platine verbunden, welche alle anderen Geräte mit Strom versorgt.
Die Motoren allerdings werden an eine eigene Spannungsquelle angeschlossen, da sowohl die Spannung als auch die benötigte Stromstärke in einen ungleichen Verhätnis zu den anderen Geräten stehen.
Motortreiber
Bei meinen ersten Versuchen, ein Modellauto zu bauen, habe ich den Motortreiber L293D verwendet, da er in vielen Online-Tutorials erwähnt wird. Dabei habe ich leider sehr schlechte Erfahrungen gemacht. Der Treiber erhitzt schnell und geht somit auch schnell kaputt. In diesem Modell verwende ich den Treiber DRV8833. Mit einem dieser Geräte kann man zwei Motoren steuern, so dass man insgesamt zwei Stück benötigt.
Die Pins OUT1 und OUT2 werden gemeinsam mit einem der Motoren verbunden, die Pins OUT3 und OUT4 mit dem zweiten Motor. Die richtige Polung ist zunächst egal, da man für die Drehrichtung der Motoren im Nachhinein die Inputpins entsprechend wählen kann. Diese Entscheidung kann Softwareseitig erfolgen.
Die Pins IN1, IN2, IN3 und IN4 werden mit dem Raspberry Pi verbunden. Wird an einem dieser Pins eine Spannung angelegt, dann dreht sich eines der Räder in eine bestimmte Richtung. Hier kann man sich durch ausprobieren einen sehr guten Überblick verschaffen, wie die Pins angesteuert werden müssen, damit sich das richtige Rad in die richtige Richtung dreht.
GND wird mit Ground verbunden, VCC mit der Spannungsquelle für den Motor (in meinem Fall 6 x 1.2 V).
Der ULT-Pin sowie der EEP-Pin müssen nicht verbunden werden.
Code
Um die Motoren anzusteuern, habe ich die Pins IN1 bis IN4 mit GPIO-Pins am Raspberry Pi verbunden. Den Code habe ich in Python geschrieben. Mit dem Paket RPi.GPIO kann man die GPIO-Pins aus Python heraus steuern. Mit
import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM)bindet man das Paket ein und wählt das Schema für die Nummerierung der Pins aus (hier BCM). Mit dem Befehl
GPIO.setup(pinNumber, GPIO.OUT)legt man fest, dass der Pin mit Nummer pinNumber als Outputpin verwendet wird. Mit den Befehlen
GPIO.output(pinNumber, GPIO.HIGH) GPIO.output(pinNumber, GPIO.LOW)kann man auf einem Pin eine Spannung anlegen (HIGH) bzw. die Spannung auf 0 reduzieren (LOW).
Wenn man jedes Rad einzeln ansteuern will, dann benötigt man acht GPIO-Pins zum steuern der Räder, da jedes Rad sowohl vorwärts als auch rückwärts angesteuert werden kann. Um die Anzahl der verwendeten Pins am Raspberry Pi zu reduzieren, habe ich jedoch die beiden Räder auf der linken bzw. auf der rechten Seite jeweils so verbunden, dass ich immer nur beide linken oder beide rechten Räder gleichzeitig vorwärts oder rückwärts drehen lassen kann. Eine Funktion, die den Befehl "geradeaus fahren" ausführt, könnte daher so aussehen:
def goForward(): GPIO.output(backwardRight, GPIO.LOW) GPIO.output(forwardRight, GPIO.HIGH) GPIO.output(forwardLeft, GPIO.HIGH) GPIO.output(backwardLeft, GPIO.LOW)Um das Auto zu drehen, kann man die Räder auf der einen Seite nach vorne und auf der anderen Seite nach hinten drehen lassen. Der Code dazu könnte also so aussehen:
def turnClockwise(): GPIO.output(backwardRight, GPIO.HIGH) GPIO.output(forwardRight, GPIO.LOW) GPIO.output(forwardLeft, GPIO.HIGH) GPIO.output(backwardLeft, GPIO.LOW)
Abstandssensoren
Die Abstandssensoren sollen vor allem beim autonomen fahren verhindern, dass das Modellauto gegen Hindernisse stösst. Aber auch beim Fahren mit Fernsteuerung kann man das Auto automatisch stoppen lassen, um einen Unfall zu verhindern. Auch wenn vermutlich ein Abstandssensor fü das Fahren geradeaus ausreicht, habe ich mich dazu entschieden, vier zu verbauen. Ein Sensor für das Rückwärtsfahren, einer fü das Vorwärtsfahren und noch zwei weitere vorne links und rechts um zu prüfen, ob es seitliche Hindernisse gibt und ein Drehen möglich ist.
Aus mehreren Gründen habe ich mich dazu entschlossen, die Abstandssensoren über einen Arduino nano auszulesen und nicht direkt am Raspberry Pi. Zum einen hätte ich für vier Sensoren insgesamt acht GPIO-Pins benötigt (Trigger und Echo), und ich wollte die Pins am Raspberry freihalten, falls ich zu einem späteren Zeitpunkt noch weitere Geräte anschließen will. Zum anderen benötigt das Auslesen der Sensoren Zeit. Wenn der Raspberry Pi gleichzeitig auf andere Geräte (z.B. ein Gamepad für die Steuerung) reagieren soll, dann ist es einfacher, das Auslesen der Abstandssensoren auf den Arduino auszulagern.
Als Abstandssensor verwende ich den Ultraschallsensor HC-SR04. Da bereits zahlreiche Tutorials zum Umgang mit diesem Sensor existieren (z.B. hier), verzichte ich darauf, das Auslesen der Sensoren weiter auszuführen.
Kommunikation mit dem Raspberry Pi
Die Verarbeitung der Abstandsdaten erfolgt komplett auf dem Arduino, inklusive der Entscheidung, ob der gemessene Abstand innerhalb eines tollerierbaren Wertebereichs liegt. Daher wird auf dem Arduino bereits die Entscheidung getroffen, ob der Weg frei ist oder nicht, so dass nur ein binärer Wert übertragen werden muss. Über einen Output-Pin gibt der Arduino ein Signal aus (LOW oder HIGH), das am Raspberry Pi abgegriffen werden kann. Ist das Signal HIGH, so wurde ein Hindernis erkannt und das Auto muss stoppen.
Eine Halterung für die Sensoren
Um die Abstandssensoren am Gestell des Modellautos zu befestigen, habe ich mir ein CAD-Modell für eine Halterung entworfen und mit dem 3D-Drucker drucken lassen. Das Modell habe ich mit der Software FreeCAD erstellt und über einen kommerziellen 3D-Druckservice drucken lassen. Zwei Schraublöcher ermöglichen es, die Halterung am Gestell zu verschrauben. Die verwendeten Sensoren haben an den vier Ecken kleine Löcher, die perfekt auf die vier hervorstehenden Stifte an der Halterung passen.
Ich stelle das CAD-Modell gerne zum Download zur Verfügung. Wer ganz sicher gehen will, dass die Halterung auf den eigenen Sensor passt, muss evt. selbst abmessen und ein Modell erstellen (zum Abmessen ist eine Schiebelehre sehr zu empfehlen; die Abemssungen sollten bis auf zehntel Millimeter genau sein, um ganz sicher zu gehen).
Steuerung mit einem Gamepad
Wenn man das Auto zu einem ferngesteuerten Rennwagen umbauen will, dann kann man das mit einem kabellosen Gamepad sehr einfach machen.
Mit dem Python Modul inputs kann das Signal des Gamepads ausgelesen werden, so dass man die Tasten und das Steuerkreuz zum
Steuern des Autos verwenden kann.
Hier ist ein Minimalbeispiel, wie der Code aussehen könnte, um das Steuerkreuz zum Navigieren zu verwenden. Falls der Code nichts
tut, dann könnte es daran liegen, dass das Format des Rückgabewerts anders aussieht. Zum Testen kann man sich natürlich die
Werte event.code und event.state auf der Konsole ausgeben lassen. Die Funktion get_gamepad() hat das Problem, dass sie nach dem Aufruf unter Umständen so lange wartet, das Gamepad
tatsächlich ein echtes Event zurückgeben kann. Will man auch anderen Code ausführen, während man auf
eine Antwort wartet (z.B. auf den Abstandssensor reagieren), dann muss man für die Funktion einen Timeout definieren.
Das kann z.B. folgendermaßen realisiert werden: Die Funktion getGamepad() tut genau das selbe wie get_gamepad(), sie wartet jedoch maximal 0.1 Sekunden auf ein Signal
und bricht nach der Wartezeit ohne Rückgabewert ab.Minimalbeispiel inputs
from inputs import get_gamepad
while True:
events = get_gamepad()
event = events[0]
if 'X' in event.code and event.state == 1:
turnClockwise()
if 'X' in event.code and event.state == -1:
turnCounterclockwise()
if 'Y' in event.code and event.state == 1:
goBackward()
if 'Y' in event.code and event.state == -1:
goForward()
Timeout
import signal
timeToWaitForGamepadResponse = 0.1
def timeout(timeout):
def process_timeout(func):
def handle_timeout(signum, frame):
raise TimeoutError('the function timed out')
def wrapper(*args, **kwargs):
signal.signal(signal.SIGALRM, handle_timeout)
signal.setitimer(signal.ITIMER_REAL, timeout)
try:
return func(*args, **kwargs)
finally:
signal.alarm(0)
return wrapper
return process_timeout
@timeout(timeout=timeToWaitForGamepadResponse)
def getGamepad():
return get_gamepad()