@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:
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.
