Hallo zusammen!
eine Grundsatzfrage:
ich möchte schlussendlich an einen Rpi mehrere Motoren ansteuern (ferngesteuerter Radlader von Lego). Die Ansteuerung erfolgt per Bluetooth durch Tastatur oder PS3controller.
Da ich noch recht neu bin möchte ich wissen, welchen Weg ihr einschlagen würdet. Input, curses oder vllt. was anderes?
Raspi, Motoren per Tastatur/Controller ansteuern
@gamble: Tastatur könnte man mit `curses` lösen, aber so ein PS3-Controller wird ja wahrscheinlich aus Sicht des Systems keine Tastatur sondern ein Joystick sein. Da müsste man sich dann eine separate Bibliothek für suchen, oder man nimmt so etwas wie Pygame, was Tastatur und Joysticks abfragen kann.
@gamble: Vielleicht haben die ja eine Homepage. Ich nehme mal an das es Python-Module sind?
nein sind keine Python module. Sind kleine Programme welche die Anbindung des Controllers ermöglichen.
Mittlerweile habe ich aber gelesen, dass raspbian nicht unbedingt das beste Betriebssystem ist um mein Projekt umzusetzen und so werde ich auf ubuntu wechseln. DOrt müssten die beiden auch zu finden sein
Mittlerweile habe ich aber gelesen, dass raspbian nicht unbedingt das beste Betriebssystem ist um mein Projekt umzusetzen und so werde ich auf ubuntu wechseln. DOrt müssten die beiden auch zu finden sein
so nun habe ich erste Erfolge und Zeilen geschrieben und mein Radlader fährt auch schon so wie ichs will. Haben die Profis noch ein paar Tipps, was besser oder "sauberer" geschrieben werden kann?
Das sind ca. 20% vom Programm. wie wird das ev. mit der Auslastung sein?
Code: Alles auswählen
#!/usr/bin/env python
import RPi.GPIO as GPIO
import time
import sys
import pygame
GPIO.setwarnings (False)
# setup
Dsp = 10
Mfw = 11
Mbw = 12
GPIO.setmode(GPIO.BOARD)
GPIO.setup(Dsp, GPIO.OUT) # drive speed
GPIO.setup(Mfw, GPIO.OUT) # drive forward
GPIO.setup(Mbw, GPIO.OUT) # drive backword
GPIO.output(Mfw, GPIO.LOW)
GPIO.output(Mbw, GPIO.LOW)
Fsp = GPIO.PWM(Dsp, 1000)
Fsp.start(0)
pygame.init()
pygame.display.set_mode((200,200))
pygame.display.set_caption("Lader")
#variables
timedrive = 0.2 # time between speed steps
drivemin = 20 # minimal driving speed
drivemax = 100 # maximal driving speed
drivestep = 5 # steps driving speed
setfw = 0 # status forward
setbw = 0 # status backward
fw = 0
bw = 0
def loop(fw, bw, setfw, setbw, Fsp):
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
destroy()
if event.key == pygame.K_UP:
fw = 1
if event.key == pygame.K_DOWN:
bw = 1
if event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
fw = 0
if event.key == pygame.K_DOWN:
bw = 0
if fw == 0 and bw == 0 and setfw == 0 and setbw == 0: #nothing is going on
time.sleep(timedrive)
elif (fw == 1): #drive forward
if (setbw == 0):
GPIO.output (Mfw, True)
if setfw < drivemin:
setfw = drivemin
Fsp.ChangeDutyCycle(setfw)
print (setfw)
time.sleep(timedrive)
else:
if (setfw + drivestep) < drivemax:
setfw = setfw + drivestep
Fsp.ChangeDutyCycle(setfw)
print (setfw)
time.sleep(timedrive)
else:
setfw = drivemax
time.sleep(timedrive)
else:
if setbw > drivemin:
setbw = setbw - drivestep
Fsp.ChangeDutyCycle(setbw)
print (setbw)
time.sleep(timedrive)
else:
setbw = 0
GPIO.output (Mbw, False)
Fsp.ChangeDutyCycle(setbw)
print (setbw)
time.sleep(timedrive)
elif (bw == 1): #drive backward
if (setfw == 0):
GPIO.output (Mbw, True)
if setbw < drivemin:
setbw = drivemin
Fsp.ChangeDutyCycle(setbw)
print (setbw)
time.sleep(timedrive)
else:
if (setbw + drivestep) < drivemax:
setbw = setbw + drivestep
Fsp.ChangeDutyCycle(setbw)
print (setbw)
time.sleep(timedrive)
else:
setbw = drivemax
time.sleep(timedrive)
else:
if setfw > drivemin:
setfw = setfw - drivestep
Fsp.ChangeDutyCycle(setfw)
print (setfw)
time.sleep(timedrive)
else:
setfw = 0
GPIO.output (Mfw, False)
Fsp.ChangeDutyCycle(setfw)
print (setfw)
time.sleep(timedrive)
elif (setfw > 0):
if setfw > drivemin:
setfw = setfw - drivestep
Fsp.ChangeDutyCycle(setfw)
print (setfw)
time.sleep(timedrive)
else:
setfw = 0
GPIO.output (Mfw, False)
Fsp.ChangeDutyCycle(setfw)
print (setfw)
time.sleep(timedrive)
elif (setbw > 0):
if setbw > drivemin:
setbw = setbw - drivestep
Fsp.ChangeDutyCycle(setbw)
print (setbw)
time.sleep(timedrive)
else:
setbw = 0
GPIO.output (Mbw, False)
Fsp.ChangeDutyCycle(setbw)
print (setbw)
time.sleep(timedrive)
else:
print ("Fehler!!!!")
def destroy():
GPIO.cleanup()
sys.exit()
try:
loop(fw, bw, setfw, setbw, Fsp)
except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed.
destroy()
@gamble: Das was Du da als „variables“ im Kommentar bezeichnet hast sind keine Variablen. Es sind Konstanten. Einige davon eher nutzlose Konstanten, weil die nicht wirklich gebraucht werden ausser um als Argumente an `loop()` übergeben zu werden. Variablen haben auf Modulebene auch nichts zu suchen. Da gehören nur Definitionen von Konstanten, Funktionen, und Klassen hin. Eine Funktion sollte in sich geschlossen sein und nur Werte (ausser Konstanten) verwenden die als Argumente übergeben wurden. Sonst werden Programme sehr schnell unübersichtlich. Auch der Initialisierungscode von GPIO und Pygame sollte in einer Funktion stehen. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Konstanten werden per Konvention komplett in Grossbuchstaben geschrieben. Andere Namen, ausser Klassennamen, haben keine Grossbuchstaben. Siehe auch den Style Guide for Python Code. Da steht auch das vier Leerzeichen pro Ebene zum Einrücken verwendet werden.
Die Namen sind teilweise sehr schlecht gewählt. Namen sollen dem Leser vermitteln was der Wert bedeutet und nicht aus kryptischen Kürzeln bestehen bei denen man erst einmal rätseln muss was die bedeuten. Wenn man ein Kürzel hat und einen Kommentar mit dem *eigentlichen* Namen dran schreibt, dann sollte der Namen besser sein, und der Kommentar wird dann überflüssig.
Das erste ``if`` in der Ereignisschleife sollte eher als ``else`` am Ende stehen. Dann ist es einfacher diese Schleife zu erweitern statt das man die Bedingung im ersten ``if`` um alles Mögliche erweitern muss. Die Logik dort erscheint mir auch etwas sehr kompliziert formuliert zu sein. Der Code sieht in den einzelnen Zweigen sehr ähnlich aus, als wenn da grosse Teile kopiert und leicht angepasst wurden. Das ist gar nicht gut. Ausserdem scheint *jeder* Zweig mit dem gleichen `sleep()` zu enden. Das gehört dann nicht in *jeden* Zweig sondern *einmal* ans Ende. Wobei das in einer Ereignisschleife vielleicht auch gar nichts zu suchen hat. Das hält nur auf. Man würde da eher ein Timer-Ereignis mit in die Schleife einbauen.
Um Bedingungen gehören keine unnötigen Klammern.
Wenn man Wahrheitswerte meint, sollte man nicht 0 und 1 verwenden. Und dann kann man auch gleich das vergleichen mit literalen Werten weglassen. Aus ``if fw == 1:`` wird dann ``if fw:`` und aus ``if fw == 0:`` wird ``if not fw:``.
Die `destroy()`-Funktion ist nicht schön. Man sollte `sys.exit()` vermeiden wenn man das auch durch den normalen Programmfluss ausdrücken kann. Also zum Beispiel bei der Escape-Taste einfach die Ereignisschleife verlassen. Und Aufräumarbeiten in den ``finally``-Zweig von einem ``try``/``finally`` stecken.
Was man statt so vieler einzelner Flags und Werte eigentlich nur braucht ist ein Flag für die Richtung und eine Zahl für die Geschwindigkeit. Wenn man will, kann man das sogar auf eine Zahl für die Geschwindigkeit reduzieren und das Vorzeichen für die Richtung verwenden. Wenn man da dann noch vernünftige Namen verwendet, wird das alles kürzer und verständlicher.
Und danach könnte man dann Anfangen das ganze objektorientiert zu schreiben und zum Beispiel den Motor in einen eigenen Datentyp kapseln.
Konstanten werden per Konvention komplett in Grossbuchstaben geschrieben. Andere Namen, ausser Klassennamen, haben keine Grossbuchstaben. Siehe auch den Style Guide for Python Code. Da steht auch das vier Leerzeichen pro Ebene zum Einrücken verwendet werden.
Die Namen sind teilweise sehr schlecht gewählt. Namen sollen dem Leser vermitteln was der Wert bedeutet und nicht aus kryptischen Kürzeln bestehen bei denen man erst einmal rätseln muss was die bedeuten. Wenn man ein Kürzel hat und einen Kommentar mit dem *eigentlichen* Namen dran schreibt, dann sollte der Namen besser sein, und der Kommentar wird dann überflüssig.
Das erste ``if`` in der Ereignisschleife sollte eher als ``else`` am Ende stehen. Dann ist es einfacher diese Schleife zu erweitern statt das man die Bedingung im ersten ``if`` um alles Mögliche erweitern muss. Die Logik dort erscheint mir auch etwas sehr kompliziert formuliert zu sein. Der Code sieht in den einzelnen Zweigen sehr ähnlich aus, als wenn da grosse Teile kopiert und leicht angepasst wurden. Das ist gar nicht gut. Ausserdem scheint *jeder* Zweig mit dem gleichen `sleep()` zu enden. Das gehört dann nicht in *jeden* Zweig sondern *einmal* ans Ende. Wobei das in einer Ereignisschleife vielleicht auch gar nichts zu suchen hat. Das hält nur auf. Man würde da eher ein Timer-Ereignis mit in die Schleife einbauen.
Um Bedingungen gehören keine unnötigen Klammern.
Wenn man Wahrheitswerte meint, sollte man nicht 0 und 1 verwenden. Und dann kann man auch gleich das vergleichen mit literalen Werten weglassen. Aus ``if fw == 1:`` wird dann ``if fw:`` und aus ``if fw == 0:`` wird ``if not fw:``.
Die `destroy()`-Funktion ist nicht schön. Man sollte `sys.exit()` vermeiden wenn man das auch durch den normalen Programmfluss ausdrücken kann. Also zum Beispiel bei der Escape-Taste einfach die Ereignisschleife verlassen. Und Aufräumarbeiten in den ``finally``-Zweig von einem ``try``/``finally`` stecken.
Was man statt so vieler einzelner Flags und Werte eigentlich nur braucht ist ein Flag für die Richtung und eine Zahl für die Geschwindigkeit. Wenn man will, kann man das sogar auf eine Zahl für die Geschwindigkeit reduzieren und das Vorzeichen für die Richtung verwenden. Wenn man da dann noch vernünftige Namen verwendet, wird das alles kürzer und verständlicher.
Und danach könnte man dann Anfangen das ganze objektorientiert zu schreiben und zum Beispiel den Motor in einen eigenen Datentyp kapseln.
Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python
import time
import sys
import pygame
from RPi import GPIO
SPEED_PIN = 10
FORWARD_PIN = 11
BACKWARD_PIN = 12
SPEED_CHANGE_INTERVAL = 0.2
MIN_SPEED = 20
MAX_SPEED = 100
SPEED_STEP = 5
BACKWARD, STOP, FORWARD = -1, 0, 1
def run(speed_pwm):
speed = 0
requested_direction = None
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return
elif event.key == pygame.K_UP:
requested_direction = FORWARD
elif event.key == pygame.K_DOWN:
requested_direction = BACKWARD
elif event.type == pygame.KEYUP:
if event.key in [pygame.K_UP, pygame.K_DOWN]:
requested_direction = None
if requested_direction == FORWARD:
speed_delta = SPEED_STEP
elif requested_direction == BACKWARD:
speed_delta = -SPEED_STEP
elif requested_direction is None:
speed_delta = 0
else:
assert False
speed = max(min(speed + speed_delta, MAX_SPEED), -MAX_SPEED)
if abs(speed) < MIN_SPEED:
speed = MIN_SPEED * (-1 if speed < 0 else 1)
GPIO.output(FORWARD_PIN, speed > 0)
GPIO.output(BACKWARD_PIN, speed < 0)
speed_pwm.ChangeDutyCycle(abs(speed))
time.sleep(SPEED_CHANGE_INTERVAL)
def main():
GPIO.setwarnings(False)
try:
GPIO.setmode(GPIO.BOARD)
GPIO.setup(
[SPEED_PIN, FORWARD_PIN, BACKWARD_PIN], GPIO.OUT, initial=GPIO.LOW
)
speed_pwm = GPIO.PWM(SPEED_PIN, 1000)
speed_pwm.start(0)
pygame.init()
try:
pygame.display.set_mode((200, 200))
pygame.display.set_caption('Lader')
run(speed_pwm)
finally:
pygame.quit()
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
if __name__ == '__main__':
main()
so ich habe noch ein "Sanftes Anfahren-" und "stehen bleiben" eingefügt. ist das soweit vertretbar?
Code: Alles auswählen
#!/usr/bin/env python
import time
import sys
import pygame
from RPi import GPIO
SPEED_PIN = 10
FORWARD_PIN = 11
BACKWARD_PIN = 12
SPEED_CHANGE_INTERVAL = 0.2
MIN_SPEED = 50
MAX_SPEED = 100
SPEED_STEP_UP = 5
SPEED_STEP_DOWN = 10
BACKWARD, STOP, FORWARD = -1, 0, 1
def run(speed_pwm):
speed = 0
requested_direction = None
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return
elif event.key == pygame.K_UP:
requested_direction = FORWARD
elif event.key == pygame.K_DOWN:
requested_direction = BACKWARD
elif event.type == pygame.KEYUP:
if event.key in [pygame.K_UP, pygame.K_DOWN]:
requested_direction = None
if requested_direction != None:
if speed * requested_direction < MIN_SPEED:
speed = MIN_SPEED * requested_direction
elif speed * requested_direction < MAX_SPEED:
speed += SPEED_STEP_UP * requested_direction
else:
if speed > 0:
if speed > MIN_SPEED:
speed -= SPEED_STEP_DOWN
else:
speed = 0
elif speed < 0:
if abs(speed) > MIN_SPEED:
speed += SPEED_STEP_DOWN
else:
speed = 0
GPIO.output(FORWARD_PIN, speed > 0)
GPIO.output(BACKWARD_PIN, speed < 0)
speed_pwm.ChangeDutyCycle(abs(speed))
time.sleep(SPEED_CHANGE_INTERVAL)
def main():
GPIO.setwarnings(False)
try:
GPIO.setmode(GPIO.BOARD)
GPIO.setup(
[SPEED_PIN, FORWARD_PIN, BACKWARD_PIN], GPIO.OUT, initial=GPIO.LOW
)
speed_pwm = GPIO.PWM(SPEED_PIN, 1000)
speed_pwm.start(0)
pygame.init()
try:
pygame.display.set_mode((200, 200))
pygame.display.set_caption('Lader')
run(speed_pwm)
finally:
pygame.quit()
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
if __name__ == '__main__':
main()
Zuletzt geändert von Anonymous am Freitag 27. Mai 2016, 21:20, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
@gamble: warum definierst Du Dir eine Konstante STOP, wenn Du für Stop None verwendest?
Das Abbremsen kann man kompakter schreiben als:
Das Abbremsen kann man kompakter schreiben als:
Code: Alles auswählen
speed = (-1 if speed < 0 else 1) * max(0, abs(speed) - SPEED_STEP_DOWN)
@Sirius3: Die Konstanten sind ja von mir übernommen und `requested_direction` auf STOP zu setzen hat eine andere Aussage (Ich will anhalten) als None (Ich will nichts ändern). `STOP` ist als Konstante überflüssig und noch ein Überbleibsel als ich übergangsweise noch Richtung und Geschwindigkeit getrennt modelliert hatte statt das Vorzeichen der Geschwindigkeit für die Richtung zu verwenden.
@ Sirius3 - mein Abbremsen darf nur bis ur MIN-Grenze gehen, da der Motor darunter nicht mehr genug Drehmoment hat...
So der Radlader funktioniert nun vollständig. Auch selbstständiges "stehenbleiben" und "zurücklenken" funktioniert bestens.
seit ihr soweit zufrieden oder was muss noch verbessert werden?
So der Radlader funktioniert nun vollständig. Auch selbstständiges "stehenbleiben" und "zurücklenken" funktioniert bestens.
seit ihr soweit zufrieden oder was muss noch verbessert werden?
Code: Alles auswählen
#!/usr/bin/env python
import time
import sys
import pygame
from RPi import GPIO
SPEED_PIN = 10
FORWARD_PIN = 11
BACKWARD_PIN = 12
STEERING_PIN = 13
RIGHT_PIN = 15
LEFT_PIN = 16
UP_PIN = 35
DOWN_PIN = 36
OPEN_PIN = 37
CLOSE_PIN = 38
READY_PIN = 40
CHANGE_INTERVAL = 0.2
MIN_SPEED = 50
MAX_SPEED = 100
SPEED_STEP_UP = 5
SPEED_STEP_DOWN = 10
BACKWARD, FORWARD = -1, 1
LEFT, RIGHT = 1, -1
pos = [0.0, 26.4, 37.9, 49.4, 63.2, 74.7, 87.4, 100.0]
def run(speed_pwm, right_pwm, left_pwm):
speed = 0
steering = 0
step_steering = 0
requested_direction = None
requested_steering = None
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
return
elif event.key == pygame.K_UP:
requested_direction = FORWARD
elif event.key == pygame.K_DOWN:
requested_direction = BACKWARD
elif event.key == pygame.K_LEFT:
requested_steering = LEFT
elif event.key == pygame.K_RIGHT:
requested_steering = RIGHT
elif event.key == pygame.K_w:
GPIO.output(UP_PIN, GPIO.HIGH)
elif event.key == pygame.K_s:
GPIO.output(DOWN_PIN, GPIO.HIGH)
elif event.key == pygame.K_a:
GPIO.output(OPEN_PIN, GPIO.HIGH)
elif event.key == pygame.K_d:
GPIO.output(CLOSE_PIN, GPIO.HIGH)
elif event.type == pygame.KEYUP:
if event.key in [pygame.K_UP, pygame.K_DOWN]:
requested_direction = None
elif event.key in [pygame.K_LEFT, pygame.K_RIGHT]:
requested_steering = None
elif event.key == pygame.K_w:
GPIO.output(UP_PIN, GPIO.LOW)
elif event.key == pygame.K_s:
GPIO.output(DOWN_PIN, GPIO.LOW)
elif event.key == pygame.K_a:
GPIO.output(OPEN_PIN, GPIO.LOW)
elif event.key == pygame.K_d:
GPIO.output(CLOSE_PIN, GPIO.LOW)
if requested_direction != None:
if speed * requested_direction < MIN_SPEED:
speed = MIN_SPEED * requested_direction
elif speed * requested_direction < MAX_SPEED:
speed += SPEED_STEP_UP * requested_direction
else:
if speed > 0:
if speed > MIN_SPEED:
speed -= SPEED_STEP_DOWN
else:
speed = 0
elif speed < 0:
if abs(speed) > MIN_SPEED:
speed += SPEED_STEP_DOWN
else:
speed = 0
else:
right_pwm.ChangeDutyCycle(0)
left_pwm.ChangeDutyCycle(0)
steering = pos[abs(step_steering)]
if step_steering > 0:
right_pwm.ChangeDutyCycle(abs(steering))
elif step_steering < 0:
left_pwm.ChangeDutyCycle(abs(steering))
if requested_steering != None:
if abs(step_steering) < 7:
step_steering += requested_steering
else:
if step_steering < 0:
step_steering += 1
elif step_steering > 0:
step_steering -= 1
GPIO.output(FORWARD_PIN, speed > 0)
GPIO.output(BACKWARD_PIN, speed < 0)
speed_pwm.ChangeDutyCycle(abs(speed))
time.sleep(CHANGE_INTERVAL)
def main():
GPIO.setwarnings(False)
try:
GPIO.setmode(GPIO.BOARD)
GPIO.setup(
[SPEED_PIN, FORWARD_PIN, BACKWARD_PIN, STEERING_PIN, RIGHT_PIN, LEFT_PIN,
UP_PIN, DOWN_PIN, OPEN_PIN, CLOSE_PIN, READY_PIN], GPIO.OUT, initial=GPIO.LOW
)
GPIO.output(READY_PIN, GPIO.HIGH)
GPIO.output(STEERING_PIN, GPIO.HIGH)
speed_pwm = GPIO.PWM(SPEED_PIN, 1000)
speed_pwm.start(0)
right_pwm = GPIO.PWM(RIGHT_PIN, 1150)
right_pwm.start(0)
left_pwm = GPIO.PWM(LEFT_PIN, 1150)
left_pwm.start(0)
pygame.init()
try:
pygame.display.set_mode((200, 200))
pygame.display.set_caption('Lader')
run(speed_pwm, right_pwm, left_pwm)
finally:
pygame.quit()
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
if __name__ == '__main__':
main()
so kleines update...
mein Radlader funktioniert nun auch mit Ps3 Controller und Tastatur gleichzeitig. Ist zwar etwas umständlich, aber was macht man nicht alles um ans Ziel zu kommen :K
Info am Rade:
Legomotoren werden über Motorcontroller angesteuert. Die Lenkung ist allerdings direkt über RIGHT_PIN und LEFT_PIN gesteuert und bekommt per STEERING_Pin nur die Versorgungsspannung...
mein Radlader funktioniert nun auch mit Ps3 Controller und Tastatur gleichzeitig. Ist zwar etwas umständlich, aber was macht man nicht alles um ans Ziel zu kommen :K
Info am Rade:
Legomotoren werden über Motorcontroller angesteuert. Die Lenkung ist allerdings direkt über RIGHT_PIN und LEFT_PIN gesteuert und bekommt per STEERING_Pin nur die Versorgungsspannung...
Code: Alles auswählen
#!/usr/bin/python
import time
import sys
import os
import pygame
from RPi import GPIO
SPEED_PIN = 10
FORWARD_PIN = 11
BACKWARD_PIN = 12
STEERING_PIN = 13
RIGHT_PIN = 15
LEFT_PIN = 16
UP_PIN = 35
DOWN_PIN = 36
OPEN_PIN = 37
CLOSE_PIN = 38
READY_PIN = 40
CHANGE_INTERVAL = 0.1
MIN_SPEED = 55
MAX_SPEED = 100
SPEED_STEP_UP = 3
SPEED_STEP_DOWN = 5
BACKWARD, FORWARD = -1, 1
LEFT, RIGHT = -1, 1
JOYSTICK, KEYBOARD = 2, 1
pos = [0.0, 26.4, 37.9, 49.4, 63.2, 74.7, 87.4, 100.0]
def run(speed_pwm, right_pwm, left_pwm, controller):
speed = 0
speed_from_keyboard = 0
speed_from_controller = 0
steering = 0
steeringleft = 0
steeringright = 0
steering_keyboard = 0
steering_controller = 0
step_steering = 0
axis_steering = 0
axis_direction = 0
keyboard_direction = None
keyboard_side = None
requested_input = JOYSTICK
while True:
if controller.get_button(14) or pygame.key.get_pressed()[pygame.K_ESCAPE]: # PS Taste
return
if controller.get_button(3): # Start Taste
requested_input = JOYSTICK
if controller.get_button(0): # Select Taste
requested_input = KEYBOARD
if controller.get_axis(0) > 0.3 or pygame.key.get_pressed()[pygame.K_d]:
GPIO.output(OPEN_PIN, GPIO.HIGH)
else:
GPIO.output(OPEN_PIN, GPIO.LOW)
if controller.get_axis(0) < -0.3 or pygame.key.get_pressed()[pygame.K_a]:
GPIO.output(CLOSE_PIN, GPIO.HIGH)
else:
GPIO.output(CLOSE_PIN, GPIO.LOW)
if controller.get_axis(1) > 0.3 or pygame.key.get_pressed()[pygame.K_s]:
GPIO.output(DOWN_PIN, GPIO.HIGH)
else:
GPIO.output(DOWN_PIN, GPIO.LOW)
if controller.get_axis(1) < -0.3 or pygame.key.get_pressed()[pygame.K_w]:
GPIO.output(UP_PIN, GPIO.HIGH)
else:
GPIO.output(UP_PIN, GPIO.LOW)
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
keyboard_direction = FORWARD
elif event.key == pygame.K_DOWN:
keyboard_direction = BACKWARD
elif event.key == pygame.K_LEFT:
keyboard_side = LEFT
elif event.key == pygame.K_RIGHT:
keyboard_side = RIGHT
elif event.type == pygame.KEYUP:
if event.key in [pygame.K_UP, pygame.K_DOWN]:
keyboard_direction = None
elif event.key in [pygame.K_LEFT, pygame.K_RIGHT]:
keyboard_side = None
# Keyboard speed
if keyboard_direction != None:
if speed_from_keyboard * keyboard_direction < MIN_SPEED:
speed_from_keyboard = MIN_SPEED * keyboard_direction
elif speed_from_keyboard * keyboard_direction < MAX_SPEED:
speed_from_keyboard += SPEED_STEP_UP * keyboard_direction
else:
if speed_from_keyboard > 0:
if speed_from_keyboard > MIN_SPEED:
speed_from_keyboard -= SPEED_STEP_DOWN
else:
speed_from_keyboard = 0
elif speed_from_keyboard < 0:
if abs(speed_from_keyboard) > MIN_SPEED:
speed_from_keyboard += SPEED_STEP_DOWN
else:
speed_from_keyboard = 0
else:
speed_from_keyboard = 0
# Keyboard steering
if keyboard_side != None:
if abs(step_steering) < 7:
step_steering += keyboard_side
else:
if step_steering < 0:
step_steering += 1
elif step_steering > 0:
step_steering -= 1
if step_steering < 0:
steering_keyboard = -1 * pos[abs(step_steering)]
else:
steering_keyboard = pos[abs(step_steering)]
# Controller speed
if (controller.get_axis(3) * -1.0) > 0.3:
speed_from_controller = round(100.0 - ((1.0 - (controller.get_axis(3) * -1.0)) * 64.3), 1)
elif controller.get_axis(3) > 0.3:
speed_from_controller = (round(100.0 - ((1.0 - controller.get_axis(3)) * 64.3), 1)) * -1
else:
speed_from_controller = 0
# Controller steering
axis_steering = controller.get_axis(2) * 100
if controller.get_axis(2) < 0:
axis_direction = -1
else:
axis_direction = 1
if abs(axis_steering) > 94:
steering_controller = pos[7] * axis_direction
elif abs(axis_steering) > 81:
steering_controller = pos[6] * axis_direction
elif abs(axis_steering) > 69:
steering_controller = pos[5] * axis_direction
elif abs(axis_steering) > 57:
steering_controller = pos[4] * axis_direction
elif abs(axis_steering) > 45:
steering_controller = pos[3] * axis_direction
elif abs(axis_steering) > 32:
steering_controller = pos[2] * axis_direction
elif abs(axis_steering) > 20:
steering_controller = pos[1] * axis_direction
else:
steering_controller = 0
# Which input?
if requested_input == KEYBOARD:
speed = speed_from_keyboard
steering = steering_keyboard
else:
speed = speed_from_controller
steering = steering_controller
# steering output
if steering < 0:
steeringleft = 0
steeringright = abs(steering)
elif steering > 0:
steeringright = 0
steeringleft = abs(steering)
else:
steeringleft = 0
steeringright = 0
right_pwm.ChangeDutyCycle(steeringright)
left_pwm.ChangeDutyCycle(steeringleft)
GPIO.output(FORWARD_PIN, speed > 0)
GPIO.output(BACKWARD_PIN, speed < 0)
speed_pwm.ChangeDutyCycle(abs(speed))
time.sleep(CHANGE_INTERVAL)
def main():
GPIO.setwarnings(False)
printed = 0
try:
while not os.path.exists("/dev/input/js0"):
if printed:
time.sleep(0.5)
else:
print ("waiting for controller")
printed = 1
print ("controller connected")
GPIO.setmode(GPIO.BOARD)
GPIO.setup(
[SPEED_PIN, FORWARD_PIN, BACKWARD_PIN, STEERING_PIN, RIGHT_PIN, LEFT_PIN,
UP_PIN, DOWN_PIN, OPEN_PIN, CLOSE_PIN, READY_PIN], GPIO.OUT, initial=GPIO.LOW
)
GPIO.output(READY_PIN, GPIO.HIGH)
GPIO.output(STEERING_PIN, GPIO.HIGH)
speed_pwm = GPIO.PWM(SPEED_PIN, 1000)
speed_pwm.start(0)
right_pwm = GPIO.PWM(RIGHT_PIN, 1150)
right_pwm.start(0)
left_pwm = GPIO.PWM(LEFT_PIN, 1150)
left_pwm.start(0)
pygame.init()
controller = pygame.joystick.Joystick(0)
controller.init()
try:
run(speed_pwm, right_pwm, left_pwm, controller)
finally:
pygame.quit()
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
os.system("sudo shutdown -h now")
if __name__ == '__main__':
main()
@gamble: Mal kurz drüber geschaut:
`sys` wird importiert, aber nicht verwendet.
`pos` ist nicht in Grossbuchstaben wie die anderen Konstanten, und auch kein wirklich guter Name. `STEERING_FACTORS` oder `KEYBOARD_STEERING_FACTORS` würde den Wert besser beschreiben.
Warum werden die GPIO-Warnungen ausgeschaltet? Besser wäre es denen nachzugehen und sie zu vermeiden.
Der Pfad zum Joystick-Device wäre als Konstante gut aufgehoben. Dann könnte man den Code zum Testen ob es bereits eingebunden ist, auch ohne das `printed`-Flag schreiben und ohne den Pfad tatsächlich zweimal schreiben zu müssen.
``print`` ist in Python 2 keine Funktion, also sollte man das ”Argument” entweder ohne Klammern schreiben, oder durch den entsprechenden `__future__`-Import eine Funktion daraus machen. Dann gehört aber auch kein Leerzeichen zwischen Funktionsname und öffnender Klammer vom Aufruf.
`os.system()` sollte man seit dem es das `subprocess`-Modul gibt, nicht mehr verwenden.
Die `run()`-Funktion ist viel zu lang. Zu viele Zeilen und zu viele lokale Namen.
Das es zum Beispiel zwei Sätze von Namen mit Werten von gleicher Bedeutung für Tastatur und Joystick gibt, ist beispielsweise ein Hinweis, dass man Tastatur- und Joystick-Behandlung da getrennt herausziehen könnte.
Für den Joystick gibt es ”magische” Zahlen die in Kommentaren erklärt werden, wo man besser Konstanten definieren würde, also beispielsweise ``START_BUTTON = 3``.
Eine weitere ”magische” Zahl ist die 7 beim vergleich mit `step_steering` die wohl mit der Länge der `pos`-Liste zusammenhängt. Also sollte man die von ``len(pos)`` ableiten. Denn sonst bekommt man Probleme wenn man da mal mehr oder weniger Faktoren in die Liste schreiben möchte und im ganzen Programm Zahlen suchen und anpassen muss, die etwas mit der Länge der Liste zu tun haben.
Ebenfalls ”magisch” sind die Zahlen die beim Joystick entscheiden welcher Faktor verwendet wird. Die würde ich in eine Liste packen und dann per Schleife den richtigen Faktor ermitteln.
Ungetestet:
Aus der Monsterfunktion `run()` kann man mindestens drei Objekte isolieren: Eine Klasse die das Fahrzeug und seine Motorenansteuerung kapselt, und je eine Klasse für Tastatur und Joystick.
Das Programm ist momentan so geschrieben das man es per Joystick und/oder Tastatur steuern kann, es kann aber nicht mit Tastatur gesteuert werden wenn kein Joystick angeschlossen ist. Wenn man den mal nicht zur Hand hat, läuft das Programm nicht, obwohl es das eigentlich könnte, wenn man es liesse.
`sys` wird importiert, aber nicht verwendet.
`pos` ist nicht in Grossbuchstaben wie die anderen Konstanten, und auch kein wirklich guter Name. `STEERING_FACTORS` oder `KEYBOARD_STEERING_FACTORS` würde den Wert besser beschreiben.
Warum werden die GPIO-Warnungen ausgeschaltet? Besser wäre es denen nachzugehen und sie zu vermeiden.
Der Pfad zum Joystick-Device wäre als Konstante gut aufgehoben. Dann könnte man den Code zum Testen ob es bereits eingebunden ist, auch ohne das `printed`-Flag schreiben und ohne den Pfad tatsächlich zweimal schreiben zu müssen.
``print`` ist in Python 2 keine Funktion, also sollte man das ”Argument” entweder ohne Klammern schreiben, oder durch den entsprechenden `__future__`-Import eine Funktion daraus machen. Dann gehört aber auch kein Leerzeichen zwischen Funktionsname und öffnender Klammer vom Aufruf.
`os.system()` sollte man seit dem es das `subprocess`-Modul gibt, nicht mehr verwenden.
Die `run()`-Funktion ist viel zu lang. Zu viele Zeilen und zu viele lokale Namen.
Das es zum Beispiel zwei Sätze von Namen mit Werten von gleicher Bedeutung für Tastatur und Joystick gibt, ist beispielsweise ein Hinweis, dass man Tastatur- und Joystick-Behandlung da getrennt herausziehen könnte.
Für den Joystick gibt es ”magische” Zahlen die in Kommentaren erklärt werden, wo man besser Konstanten definieren würde, also beispielsweise ``START_BUTTON = 3``.
Eine weitere ”magische” Zahl ist die 7 beim vergleich mit `step_steering` die wohl mit der Länge der `pos`-Liste zusammenhängt. Also sollte man die von ``len(pos)`` ableiten. Denn sonst bekommt man Probleme wenn man da mal mehr oder weniger Faktoren in die Liste schreiben möchte und im ganzen Programm Zahlen suchen und anpassen muss, die etwas mit der Länge der Liste zu tun haben.
Ebenfalls ”magisch” sind die Zahlen die beim Joystick entscheiden welcher Faktor verwendet wird. Die würde ich in eine Liste packen und dann per Schleife den richtigen Faktor ermitteln.
Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import os
import subprocess
import time
import pygame
from RPi import GPIO
JOYSTICK_DEVICE_PATH = '/dev/input/js0'
PS_BUTTON = 14
START_BUTTON = 3
SELECT_BUTTON = 0
SPEED_PIN = 10
FORWARD_PIN = 11
BACKWARD_PIN = 12
STEERING_PIN = 13
RIGHT_PIN = 15
LEFT_PIN = 16
UP_PIN = 35
DOWN_PIN = 36
OPEN_PIN = 37
CLOSE_PIN = 38
READY_PIN = 40
CHANGE_INTERVAL = 0.1
MIN_SPEED = 55
MAX_SPEED = 100
SPEED_STEP_UP = 3
SPEED_STEP_DOWN = 5
BACKWARD, FORWARD = -1, 1
LEFT, RIGHT = -1, 1
JOYSTICK, KEYBOARD = 2, 1
STEERING_FACTORS = [0.0, 26.4, 37.9, 49.4, 63.2, 74.7, 87.4, 100.0]
def run(speed_pwm, right_pwm, left_pwm, controller):
speed = 0
speed_from_keyboard = 0
speed_from_controller = 0
steering = 0
steeringleft = 0
steeringright = 0
steering_keyboard = 0
steering_controller = 0
step_steering = 0
axis_steering = 0
axis_direction = 0
keyboard_direction = None
keyboard_side = None
requested_input = JOYSTICK
while True:
if (
controller.get_button(PS_BUTTON)
or pygame.key.get_pressed()[pygame.K_ESCAPE]
):
return
if controller.get_button(START_BUTTON):
requested_input = JOYSTICK
if controller.get_button(SELECT_BUTTON):
requested_input = KEYBOARD
if controller.get_axis(0) > 0.3 or pygame.key.get_pressed()[pygame.K_d]:
GPIO.output(OPEN_PIN, GPIO.HIGH)
else:
GPIO.output(OPEN_PIN, GPIO.LOW)
if controller.get_axis(0) < -0.3 or pygame.key.get_pressed()[pygame.K_a]:
GPIO.output(CLOSE_PIN, GPIO.HIGH)
else:
GPIO.output(CLOSE_PIN, GPIO.LOW)
if controller.get_axis(1) > 0.3 or pygame.key.get_pressed()[pygame.K_s]:
GPIO.output(DOWN_PIN, GPIO.HIGH)
else:
GPIO.output(DOWN_PIN, GPIO.LOW)
if controller.get_axis(1) < -0.3 or pygame.key.get_pressed()[pygame.K_w]:
GPIO.output(UP_PIN, GPIO.HIGH)
else:
GPIO.output(UP_PIN, GPIO.LOW)
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
keyboard_direction = FORWARD
elif event.key == pygame.K_DOWN:
keyboard_direction = BACKWARD
elif event.key == pygame.K_LEFT:
keyboard_side = LEFT
elif event.key == pygame.K_RIGHT:
keyboard_side = RIGHT
elif event.type == pygame.KEYUP:
if event.key in [pygame.K_UP, pygame.K_DOWN]:
keyboard_direction = None
elif event.key in [pygame.K_LEFT, pygame.K_RIGHT]:
keyboard_side = None
# Keyboard speed
if keyboard_direction != None:
if speed_from_keyboard * keyboard_direction < MIN_SPEED:
speed_from_keyboard = MIN_SPEED * keyboard_direction
elif speed_from_keyboard * keyboard_direction < MAX_SPEED:
speed_from_keyboard += SPEED_STEP_UP * keyboard_direction
else:
if speed_from_keyboard > 0:
if speed_from_keyboard > MIN_SPEED:
speed_from_keyboard -= SPEED_STEP_DOWN
else:
speed_from_keyboard = 0
elif speed_from_keyboard < 0:
if abs(speed_from_keyboard) > MIN_SPEED:
speed_from_keyboard += SPEED_STEP_DOWN
else:
speed_from_keyboard = 0
else:
speed_from_keyboard = 0
# Keyboard steering
if keyboard_side != None:
if abs(step_steering) < len(STEERING_FACTORS) - 1:
step_steering += keyboard_side
else:
if step_steering < 0:
step_steering += 1
elif step_steering > 0:
step_steering -= 1
if step_steering < 0:
steering_keyboard = -1 * STEERING_FACTORS[abs(step_steering)]
else:
steering_keyboard = STEERING_FACTORS[abs(step_steering)]
# Controller speed
if (controller.get_axis(3) * -1.0) > 0.3:
speed_from_controller = round(100.0 - ((1.0 - (controller.get_axis(3) * -1.0)) * 64.3), 1)
elif controller.get_axis(3) > 0.3:
speed_from_controller = round(100.0 - ((1.0 - controller.get_axis(3)) * 64.3), 1) * -1
else:
speed_from_controller = 0
# Controller steering
axis_steering = controller.get_axis(2) * 100
if controller.get_axis(2) < 0:
axis_direction = -1
else:
axis_direction = 1
if abs(axis_steering) > 94:
steering_controller = STEERING_FACTORS[7] * axis_direction
elif abs(axis_steering) > 81:
steering_controller = STEERING_FACTORS[6] * axis_direction
elif abs(axis_steering) > 69:
steering_controller = STEERING_FACTORS[5] * axis_direction
elif abs(axis_steering) > 57:
steering_controller = STEERING_FACTORS[4] * axis_direction
elif abs(axis_steering) > 45:
steering_controller = STEERING_FACTORS[3] * axis_direction
elif abs(axis_steering) > 32:
steering_controller = STEERING_FACTORS[2] * axis_direction
elif abs(axis_steering) > 20:
steering_controller = STEERING_FACTORS[1] * axis_direction
else:
steering_controller = 0
# Which input?
if requested_input == KEYBOARD:
speed = speed_from_keyboard
steering = steering_keyboard
else:
speed = speed_from_controller
steering = steering_controller
# steering output
if steering < 0:
steeringleft = 0
steeringright = abs(steering)
elif steering > 0:
steeringright = 0
steeringleft = abs(steering)
else:
steeringleft = 0
steeringright = 0
right_pwm.ChangeDutyCycle(steeringright)
left_pwm.ChangeDutyCycle(steeringleft)
GPIO.output(FORWARD_PIN, speed > 0)
GPIO.output(BACKWARD_PIN, speed < 0)
speed_pwm.ChangeDutyCycle(abs(speed))
time.sleep(CHANGE_INTERVAL)
def main():
if not os.path.exists(JOYSTICK_DEVICE_PATH):
print('waiting for controller')
while not os.path.exists(JOYSTICK_DEVICE_PATH):
time.sleep(0.5)
print('controller connected')
GPIO.setwarnings(False) # TODO Why?
try:
GPIO.setmode(GPIO.BOARD)
GPIO.setup(
[
SPEED_PIN, FORWARD_PIN, BACKWARD_PIN, STEERING_PIN, RIGHT_PIN,
LEFT_PIN, UP_PIN, DOWN_PIN, OPEN_PIN, CLOSE_PIN, READY_PIN,
],
GPIO.OUT,
initial=GPIO.LOW
)
GPIO.output([READY_PIN, STEERING_PIN], GPIO.HIGH)
speed_pwm = GPIO.PWM(SPEED_PIN, 1000)
speed_pwm.start(0)
right_pwm = GPIO.PWM(RIGHT_PIN, 1150)
right_pwm.start(0)
left_pwm = GPIO.PWM(LEFT_PIN, 1150)
left_pwm.start(0)
pygame.init()
controller = pygame.joystick.Joystick(0)
controller.init()
try:
run(speed_pwm, right_pwm, left_pwm, controller)
finally:
pygame.quit()
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
subprocess.call(['sudo', 'shutdown', '-h', 'now'])
if __name__ == '__main__':
main()
Aus der Monsterfunktion `run()` kann man mindestens drei Objekte isolieren: Eine Klasse die das Fahrzeug und seine Motorenansteuerung kapselt, und je eine Klasse für Tastatur und Joystick.
Das Programm ist momentan so geschrieben das man es per Joystick und/oder Tastatur steuern kann, es kann aber nicht mit Tastatur gesteuert werden wenn kein Joystick angeschlossen ist. Wenn man den mal nicht zur Hand hat, läuft das Programm nicht, obwohl es das eigentlich könnte, wenn man es liesse.
[codebox=jquery file=Unbenannt.js][/code]aber was ich nicht ganz verstehe:
warum kennt das Skript X_Button und CHANGE_INTERVAL aber nicht "speed_from_keyboard"?
Code: Alles auswählen
#!/usr/bin/python3
X_Button = 14
JOYSTICK_DEVICE_PATH = '/dev/input/js0'
CHANGE_INTERVAL = 0.1
STEERING_LEVELS = [0.0, 26.4, 37.9, 49.4, 63.2, 74.7, 87.4, 100.0]
speed_from_keyboard = 0
def run(speed_pwm, right_pwm, left_pwm, controller):
while True:
if controller.get_button(X_Button):
return
... irgendwas mit speed_from_keyboard
time.sleep(CHANGE_INTERVAL)
def main():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(
[
SPEED_PIN, FORWARD_PIN, BACKWARD_PIN, STEERING_PIN, RIGHT_PIN, LEFT_PIN,
UP_PIN, DOWN_PIN, OPEN_PIN, CLOSE_PIN, READY_PIN,
],
GPIO.OUT,
initial=GPIO.LOW
)
GPIO.output(READY_PIN, GPIO.HIGH)
try:
controller = pygame.joystick.Joystick(0)
controller.init()
try:
run(speed_pwm, right_pwm, left_pwm, controller)
finally:
pygame.quit()
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
if __name__ == "__main__":
main()
@gamble: Weil Du bei dem ”irgendwas” sicherlich irgendwo dem Namen etwas zuweist, und damit ist das ein lokaler Name der innerhalb der Funktion sichtbar ist und den gleichen Namen auf Modulebene verdeckt. Wenn Du innerhalb einer Funktion etwas zuweist, dann willst Du das ja aber sowieso nicht auf Modulebene zuweisen, denn das wäre dann ja eine globale Variable und damit Böse™.
`X_Button` sollte konventionell `X_BUTTON` heissen. Sonst denkt am Ende noch jemand es sei der Name einer Klasse. Auch wenn in *den* Namen normalerweise keine Unterstriche vorkommen.
`X_Button` sollte konventionell `X_BUTTON` heissen. Sonst denkt am Ende noch jemand es sei der Name einer Klasse. Auch wenn in *den* Namen normalerweise keine Unterstriche vorkommen.