In den bisherigen Blogs zum Robot Car haben wir die robusten Uno und Nano kompatiblen Micro Controller verwendet. Nun sollen die Raspianer zu ihrem Recht kommen. Für den Raspberry Pi gab es schon kurz nach seinem Erscheinen im Jahre 2012 Motor Controller auf Basis des ICs L293D und Bausätze für Roboterautos. Mit den jährlichen Pi Wars gibt es sogar einen Wettbewerb für die vielseitigsten Modelle; die Aufgaben waren bisher jedes Mal sehr anspruchsvoll.
In diesem Blog möchte ich einen der Vorteile des Raspi nutzen und eine Funktastatur am USB-Port anschließen. Des Weiteren benutze ich einen der seltenen Raspberry Pi 3A+, für unsere Zwecke bestens geeignet. Grundsätzlich funktioniert der unten aufgeführte Code aber für jedes Raspi-Modell. Als Motor Controller kommt bei mir der MotoZero von ThePiHut zum Einsatz. Wie der Name schon vermuten lässt, ist das ein HAT (Hardware Attached on Top) im Pi Zero Format. Aber der J6 Header (besser bekannt als GPIO-Leiste) ist ja seit Jahren unverändert für alle Modelle.
Unabdingbar für unseren Aufbau ist ein DC-DC-Converter; ich benutze den LM2596S Step-down DC-DC Buck Converter mit 3-stelliger Digitalanzeige, bei dem die Spannungen direkt angezeigt werden. Die Umschaltung zwischen Ein- oder Ausgangsspannung erfolgt über den kleinen Taster rechts neben dem dreistelligen Display. Da die Spannungsversorgung über den 5V-Pin (Pin 2 oder 4) erfolgt, muss die Eingangsspannung für den Raspi sehr exakt 5,0 bis 5,1 V sein. Spannungsspitzen können den Raspberry Pi zerstören. Das ist mir noch nie passiert. Aber auch ein nicht geplanter Neustart ist ärgerlich genug, denn der Bootvorgang dauert ja rund eine Minute.
Benötigte Hardware
Anzahl | Bauteil |
---|---|
1 | Raspberry Pi 3A+ |
oder Raspberry Pi 3B / 3B+ oder Raspberry Pi Zero / WH Anm.: Am Ende wollen wir auch WLAN nutzen |
|
1 | Motor Controller Moto Zero |
1 | LM2596S Step-down DC-DC Buck Converter mit 3-stelliger Digitalanzeige |
1 | Funktastatur |
1 | Bausatz Robot Car mit zwei Motoren/Rädern |
Batteriefach, Jumper Kabel, Kleinmaterial |
Den DC-DC-Converter habe ich im vorderen Bereich unterhalb des Chassis angeschraubt, Gehäuse für den Raspi und das Batteriefach habe ich wie schon zuvor mit Klettband oder zweiseitigem (Teppich-) Klebeband befestigt.
Bild: Robot Car von oben mit Raspberry Pi 3A+ und Battery Pack (2 LiPo Akkus)
Bild von unten: 2 Motoren und DC-DC-Converter
Noch jeweils ein paar Worte zum Raspberry Pi 3A+ und zum MotoZero: Das Modell 3A+ ist die abgespeckte Variante des 3B+ mit Vierkern-Prozessor, 512MB Arbeitsspeicher und WLAN/Bluetooth und einer USB-A-Buchse. Auf einen LAN-Anschluss und weitere USB-Buchse kann ich verzichten und habe dafür den geringeren Stromverbrauch. Beim Pi Zero mit dem einfachen Prozessor hat mich vor allem gestört, dass ich für HDMI-Kabel und USB stets Adapter benötige.
Der MotoZero ist ein einfacher Motor Controller mit zwei ICs vom Typ L293D. Damit können bis zu vier Motoren geregelt werden. Der Vorteil eines HAT ist, dass man durch das Aufstecken auf den J6-Header sehr viele Kabelverbindungen einsparen kann. Der MotoZero kommt als Bausatz und muss gelötet werden. Dazu gibt es diese Anleitung. Tipp: Verwenden Sie anstelle des mitgelieferten 40-poligen GPIO Headers einen sogenannten Stacking Header, bei dem die Pins deutlich nach oben herausragen. Die Motor Controller verwenden nicht alle GPIOs, so dass man einige Pins für Sensoren oder andere Erweiterungen nutzen kann. Aus baulichen Gründen habe ich die Anschlüsse Motor 3 und 4 verwendet. Und wie ich in Versuchen herausbekommen habe, liegt am Enable Pin des L293D die Referenzspannung für die Pulsweitenmodulation (PWM), also die Motorsteuerung, an.
Pinbelegung
Motor | 1 | 2 | 3 | 4 |
---|---|---|---|---|
Enable Pin | 5 | 17 | 12 | 25 |
Positive (+) Terminal | 24 | 6 | 23 | 13 |
Negative (-) Terminal | 27 | 22 | 16 | 18 |
Software
Wie in früheren Blog-Beiträgen zum Raspberry Pi und Python schwärme ich auch diesmal für das Programm-Modul gpiozero von Ben Nuttall u.a., für mich ein Paradebeispiel für die Objekt-orientierte Programmierung. Selbstverständlich sind alle Funktionen für die Steuerung von Motoren, aber auch die Auswertung von Sensoren, implementiert.
Da wir die gleichen Motoren wie vorher benutzen, habe ich mich wieder für das System mit den fünf Fahrtstufen je Richtung entschieden. Zur Erinnerung: Bei zu geringer Spannung brummen die Motoren nur, laufen aber nicht an. Und in Abhängigkeit von der Spannungsversorgung muss die Spannung auch nach oben abgeregelt werden. Deshalb kann die Liste speedLevel mit den 11 Elementen bei Ihnen anders aussehen. Bei gpiozero müssen die Werte zwischen -1 und +1 liegen; deshalb erfolgt bei der Wertzuweisung die Division durch 1000. Im Übrigen erhalte ich mir so die Möglichkeit, die selbst entwickelten Fernsteuerungen mit HC-12 und nRF24L01 nachzurüsten.
Python Script
#! /usr/bin/python3
# Code for driving a robot car with MotoZero
# Motors attached to M3 and M4
# For motor control, use keybord w,a,s,y or arrow keys
# By Bernd54Albrecht for AZ-Delivery
from gpiozero import Robot, Motor, OutputDevice, LED
from signal import pause
import _thread
import sys
import termios
import tty
robot = Robot(left=(16,23), right=(13,18))
motor1_enable = OutputDevice(12, initial_value=1)
motor2_enable = OutputDevice(25, initial_value=1)
led = LED(4)
led.on()
code = 505
inkey_buffer = 1
def inkey():
fd=sys.stdin.fileno()
remember_attributes=termios.tcgetattr(fd)
tty.setraw(sys.stdin.fileno())
character=sys.stdin.read(inkey_buffer)
termios.tcsetattr(fd,termios.TCSADRAIN, remember_attributes)
return character
def readkey():
k1 = inkey()
if ord(k1) != 0x1b:
return k1
k2 = inkey()
if ord(k2) != 0x5b:
return k1
k3 = inkey()
return chr(0x10 + ord(k3) - 65) # 16=Up, 17=Down, 18=Right, 19=Left arrows
def getCode():
global code
key = readkey()
print("key = ",key,"ord(key)",ord(key))
if (key=='w' or ord(key) == 16) and code<1000:
code = code + 100
elif (key=='y' or ord(key) == 17) and code>100:
code = code-100
elif (key=='s' or ord(key) == 18) and code%100<10:
code = code+1
elif (key=='a' or ord(key) == 19) and code%100>0:
code = code-1
elif key == ' ':
code = 505
elif ord(key) == 3:
code=505
led.off()
exit()
print(code)
return
def motor(y,x):
print("y = ",y, " x = ", x)
speedLevel = [-600,-500,-400,-320,-250,0,250,320,400,500,600]
speed = speedLevel[y]
if x==10:
left = min(speed+250,600)
right = max(speed-250,-600)
elif x==9:
left = min(speed+200,600)
right = max(speed-200,-600)
elif x==8:
left = min(speed+150,600)
right = max(speed-150,-600)
elif x==7:
left = min(speed+100,600)
right = max(speed-100,-600)
elif x==6:
left = min(speed+50,600)
right = max(speed-50,-600)
elif x==4:
right = min(speed+50,600)
left = max(speed-50,-600)
elif x==3:
right = min(speed+100,600)
left = max(speed-100,-600)
elif x==2:
right = min(speed+150,600)
left = max(speed-150,-600)
elif x==1:
right = min(speed+200,600)
left = max(speed-200,-600)
elif x==0:
right = min(speed+250,600)
left = max(speed-250,-600)
else:
left = speed
right = speed
robot.value = (left/1000, right/1000)
while True:
getCode()
y = int(code/100)
x = code - 100*y
_thread.start_new_thread(motor,(y,x))
Bei dem Programm nutze ich ein Programm-Modul, mit dem ich einen einzelnen Tastendruck auch ohne Return-Taste abfragen kann. Dieses Modul erfordert es allerdings, dass es im Terminal gestartet wird. Ansonsten erhält man folgende Fehlermeldung:
Bild: Fehlermeldung, wenn das Programm nicht im Terminal gestartet wird
Wer das nicht möchte, kann auf das Modul verzichten und mit dem input-Befehl arbeiten. Dann muss jedoch jede Eingabe mit der Return-Taste abgeschlossen werden.
Bei meinem Aufbau wird das Python-Programm ohnehin im Terminal gestartet, da ich den Programmstart in die Datei autostart geschrieben habe. So kann ich den Raspi auch ohne Monitor starten und habe nach dem Booten mein Robot Car fahrbereit. Für Autostart gibt es verschiedene Methoden, die sich zu allem Überfluss in den letzten Jahren auch noch geändert haben. Ich benutze die Möglichkeit, die offiziell „System method” genannt wird. Dazu die o.g. Datei öffnen mit:
sudo nano /etc/xdg/lxsession/LXDE-pi/autostart
Von den folgenden Zeilen werden Sie die ersten drei in der Datei autostart bereits vorfinden:
@lxpanel --profile LXDE-pi
@pcmanfm --desktop --profile LXDE-pi
@xscreensaver -no-splash
@lxterminal -e python3 /path/my_script.py
Mit der vierten Zeile starte ich das Terminal und führe dort unser Python3-Programm aus. Wichtig ist, dass das Programm mit dem vollständigen Pfad angegeben wird, in meinem Fall
@lxterminal -e python3 /home/pi/robotCar/robotCar_MotoZero3_4_Code505.py
Als Anzeige, dass der System- und Programmstart abgeschlossen ist, habe ich an Pin 4 eine LED angeschlossen. Wenn also das Licht angeht, ist das kleine Roboterauto einsatzbereit und wartet auf die Tastaturbefehle w, a, s, f, die Pfeiltasten oder Leertaste zum Schnellstop. Mit Strg+C wird das Programm beendet.
Wenn Sie keine Funktastatur besitzen, können Sie selbstverständlich das Programm auch in einem Terminalprogramm am PC, z.B. Putty, starten.
Bild: Putty Konfiguration und Start
Nach dem Einloggen wechseln Sie in das richtige Verzeichnis, in meinem Fall
cd robotCar
und starten Sie das Python-Programm mit
python3 robotCar_____.py
Dabei wird allerdings eine neue Instanz des Terminal- und Python-Programms gestartet. Wenn Ihr RobotCar also beim Fahren ruckelt, läuft parallel noch das mit autostart geöffnete Programm Das muss zunächst beendet und ggf. aus autostart entfernt werden.
Bild: Putty Login und Programmstart
Beendet wird das Programm mit Strg+C; und wenn Sie den Raspi geordnet herunterfahren wollen, geben Sie folgendes ein:
sudo shutdown – h now
Zum Schluss noch das Gegenteil zum Autostart, das automatische Herunterfahren ohne Monitor und Tastatur. Ein Tool von Adafruit, jedoch unverständlich ohne die Erklärung von Michael Horne (https://www.recantha.co.uk/blog/), die ich hier ins Deutsche übersetze.
Das Ganze funktioniert, indem man die letzten beiden Pins (Board #39=Ground und #40=GPIO21) kurz verbindet, egal, ob mit Kurzschlussstecker oder Büroklammer.
Benötigt wird das Programm git, das heutzutage im Raspberry Pi OS vorinstalliert ist. Wer es noch nicht hat, beginnt mit (nach dem obligatorischen update und upgrade)
sudo apt-get install git
Mit git clone wird dann das Adafruit Skript heruntergeladen:
git clone https://github.com/adafruit/Adafruit-GPIO-Halt
oder git clone git://github.com/adafruit/Adafruit-GPIO-Halt
Klonen bedeutet, dass das Verzeichnis jetzt auf unseren Raspi kopiert wurde. Wir wechseln in dieses Verzeichnis mit
cd Adafruit-GPIO.Halt
Dort geben wir nacheinander die folgenden Befehle ein:
make
sudo make install
Damit wird das Skript im Verzeichnis /usr/local/bin/gpio-halt installiert
Um dieses Skript als „service“ laufen zu lassen, erzeugen wir die erforderliche Datei mit:
sudo nano /lib/systemd/system/gpio-halt.service
[Unit]
Description=Short pins 21 and ground to shutdown the Pi
After=multi-user.target[Service]
Type=idle
ExecStart=/usr/local/bin/gpio-halt 21 &[Install]
WantedBy=multi-user.target
In nano wird das Skript mit Strg+o und okay gespeichert, dann wird nano mit Strg+x beendet.
Wir befinden uns wieder im Terminal auf der Command Line. Hier wird das Programm ausführbar gemacht mit:
sudo chmod 644 /lib/systemd/system/gpio-halt.service
Und systemd soll von nun an das Skript nutzen:
sudo systemctl daemon-reload
sudo systemctl enable gpio-halt.service
Zum Schluss wird der Raspi mit
neu gestartet und wir können das Skript ausprobieren.sudo reboot
Viel Spaß beim Ausprobieren.