Motorkarte Library

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Dominikin9
User
Beiträge: 13
Registriert: Mittwoch 4. Januar 2017, 17:39

Motorkarte Library

Beitragvon Dominikin9 » Samstag 11. Februar 2017, 23:30

Guten Abend!

Ich bin gerade dabei eine Motorsteuerung zu programmieren und wollte gleichzeitig auf die selbe library zugreifen,
aber dann bekomme ich den Fehler "wiringPiSetup*: You must only call this one per program run. This is a fatal error.
Please fix your code."

Was kann ich da dagegen machen?
(im Anhang ist die library)

Mit freundlichen Grüßen
Dominic

  1. import wiringpi
  2.  
  3. # Motor speeds for this library are specified as numbers
  4. # between -MAX_SPEED and MAX_SPEED, inclusive.
  5. _max_speed = 480  # 19.2 MHz / 2 / 480 = 20 kHz
  6. MAX_SPEED = _max_speed
  7.  
  8. io_initialized = False
  9. def io_init():
  10.   global io_initialized
  11.   if io_initialized:
  12.     return
  13.  
  14.   wiringpi.wiringPiSetupGpio()
  15.   wiringpi.pinMode(12, wiringpi.GPIO.PWM_OUTPUT)
  16.   wiringpi.pinMode(13, wiringpi.GPIO.PWM_OUTPUT)
  17.  
  18.   wiringpi.pwmSetMode(wiringpi.GPIO.PWM_MODE_MS)
  19.   wiringpi.pwmSetRange(MAX_SPEED)
  20.   wiringpi.pwmSetClock(2)
  21.  
  22.   wiringpi.pinMode(22, wiringpi.GPIO.OUTPUT)
  23.   wiringpi.pinMode(23, wiringpi.GPIO.OUTPUT)
  24.   wiringpi.pinMode(24, wiringpi.GPIO.OUTPUT)
  25.   wiringpi.pinMode(25, wiringpi.GPIO.OUTPUT)
  26.  
  27.   io_initialized = True
  28.  
  29. class Motor(object):
  30.     MAX_SPEED = _max_speed
  31.  
  32.     def __init__(self, pwm_pin, dir_pin, en_pin):
  33.         self.pwm_pin = pwm_pin
  34.         self.dir_pin = dir_pin
  35.         self.en_pin = en_pin
  36.  
  37.     def enable(self):
  38.         io_init()
  39.         wiringpi.digitalWrite(self.en_pin, 1)
  40.  
  41.     def disable(self):
  42.         io_init()
  43.         wiringpi.digitalWrite(self.en_pin, 0)
  44.  
  45.     def setSpeed(self, speed):
  46.         if speed < 0:
  47.             speed = -speed
  48.             dir_value = 1
  49.         else:
  50.             dir_value = 0
  51.  
  52.         if speed > MAX_SPEED:
  53.             speed = MAX_SPEED
  54.  
  55.         io_init()
  56.         wiringpi.digitalWrite(self.dir_pin, dir_value)
  57.         wiringpi.pwmWrite(self.pwm_pin, speed)
  58.  
  59. class Motors(object):
  60.     MAX_SPEED = _max_speed
  61.  
  62.     def __init__(self):
  63.         self.motor1 = Motor(12, 24, 22)
  64.         self.motor2 = Motor(13, 25, 23)
  65.  
  66.     def enable(self):
  67.         self.motor1.enable()
  68.         self.motor2.enable()
  69.  
  70.     def disable(self):
  71.         self.motor1.disable()
  72.         self.motor2.disable()
  73.  
  74.     def setSpeeds(self, m1_speed, m2_speed):
  75.         self.motor1.setSpeed(m1_speed)
  76.         self.motor2.setSpeed(m2_speed)
  77.  
  78. motors = Motors()
Benutzeravatar
BlackJack
Moderator
Beiträge: 31913
Registriert: Dienstag 25. Januar 2005, 23:29
Wohnort: Berlin
Kontaktdaten:

Re: Motorkarte Library

Beitragvon BlackJack » Sonntag 12. Februar 2017, 01:27

@Dominikin9: Ich sehe da jetzt in dem Code kein Problem. Wie sieht denn der komplette Traceback aus?

Allerdings ist das mit dem ``global`` auch nicht gut. Ruf die Funktion einfach nur *einmal* auf. In der Funktion sind auch ein paar Sachen die ich dort nicht hinein schreiben würde. Die Pins der Motoren tauchen da beispielsweise auf. Ich würde es in der Verantwortung des Motorobjekts sehen wenn es erstellt wird, *seine* Pins entsprechend zu einzurichten.
„All religions are the same:
religion is basically guilt, with different holidays.” — Cathy Ladman
Dominikin9
User
Beiträge: 13
Registriert: Mittwoch 4. Januar 2017, 17:39

Re: Motorkarte Library

Beitragvon Dominikin9 » Sonntag 12. Februar 2017, 11:23

@BlackJack wie könnte man das mit dem global lösen?

Und den anderen Fehler hab ich gefunden. Der Fehler war, dass ich aus zwei Threats gleichzeitig das motors.enable() aufrufen wollte,
aber man das anscheinend nicht darf, ich weiß zwar nicht warum aber jetzt funktioniert es.

Vielleicht findest du eine Erklärung dazu und kannst es mir vielleicht erklären, wenn nicht auch ok :D.

Danke schon man für die Hilfe.

so funktioniert es nicht und gibt die Fehler Meldung wie zuerst (wiringPiSetup*: You must only call this one per program run. This is a fatal error.
Please fix your code.) und das ist der ganze Traceback

  1. from __future__ import print_function
  2. import time
  3. from dual_mc33926_rpi import motors, MAX_SPEED
  4. from threading import Thread
  5.  
  6. def m_1_control():
  7.    
  8.     motors.enable()
  9.     motors.setSpeeds(0, 0)
  10.    
  11.     while True:
  12.         print("Motor 1 forward")
  13.         motors.motor1.setSpeed(480)
  14.         time.sleep(3)
  15.        
  16.         print("Motor 1 reverse")
  17.         motors.motor1.setSpeed(-480)
  18.         time.sleep(1)
  19.         motors.motor1.setSpeed(0)
  20.        
  21.  
  22. def m_2_control():
  23.    
  24.     motors.enable()
  25.     motors.setSpeeds(0, 0)
  26.    
  27.     while True:
  28.         print("Motor 2 forward")
  29.         motors.motor2.setSpeed(480)
  30.         time.sleep(2)
  31.        
  32.         print("Motor 2 reverse")
  33.         motors.motor2.setSpeed(-480)
  34.         time.sleep(5)
  35.         motors.motor2.setSpeed(0)
  36.        
  37.  
  38. m_1 = Thread(target = m_1_control)
  39. m_2 = Thread(target = m_2_control)
  40.  
  41. m_1.start()
  42. m_2.start()
  43.  
  44. while True:
  45.     time.sleep(0.001)  


Und so funktioniert es

  1. from __future__ import print_function
  2. import time
  3. from dual_mc33926_rpi import motors, MAX_SPEED
  4. from threading import Thread
  5.  
  6. def m_1_control():
  7.    
  8.     while True:
  9.         print("Motor 1 forward")
  10.         motors.motor1.setSpeed(480)
  11.         time.sleep(3)
  12.        
  13.         print("Motor 1 reverse")
  14.         motors.motor1.setSpeed(-480)
  15.         time.sleep(1)
  16.         motors.motor1.setSpeed(0)
  17.        
  18.  
  19. def m_2_control():
  20.  
  21.     while True:
  22.         print("Motor 2 forward")
  23.         motors.motor2.setSpeed(480)
  24.         time.sleep(2)
  25.        
  26.         print("Motor 2 reverse")
  27.         motors.motor2.setSpeed(-480)
  28.         time.sleep(5)
  29.         motors.motor2.setSpeed(0)
  30.        
  31.  
  32. m_1 = Thread(target = m_1_control)
  33. m_2 = Thread(target = m_2_control)
  34. motors.enable()
  35. motors.setSpeeds(0, 0)
  36. m_1.start()
  37. m_2.start()
  38.  
  39. while True:
  40.     time.sleep(0.001)  


Mit freundlichen Grüßen
Dominic
Benutzeravatar
BlackJack
Moderator
Beiträge: 31913
Registriert: Dienstag 25. Januar 2005, 23:29
Wohnort: Berlin
Kontaktdaten:

Re: Motorkarte Library

Beitragvon BlackJack » Sonntag 12. Februar 2017, 12:30

@Dominikin9: Das mit dem ``global`` kann man wie gesagt dadurch lösen das man die Funktion nur einmal aufruft.

Threads laufen parallel. Wenn Du zweimal *gleichzeitig* `moter.enable()` aufrufst, wird auch von beiden Aufrufen `io_init()` gleichzeitig aufgerufen, und wenn das beide tun, dann ist für beide am Anfang der `io_init()` das globale `io_initialized()`-Flag noch `False` also führen auch beide den Code in der `io_init()` aus. Was zu zwei Aufrufen von `wiringPiSetupGpio()` führt. Erst wenn die `io_init()` komplett ausgeführt wurde ist das globale `io_initialized` auf `True` gesetzt.

Bevor Du jetzt auf die Idee kommst die letzte Zeile von `io_init()` weiter nach vorne zu ziehen: Es könnte dann so aussehen das es funktioniert, aber das wäre zufällig und ist dann auch nicht immer garantiert, denn das grundsätzliche Problem bleibt dabei bestehen, nur das Zeitfenster in dem der Fehler auftreten kann ist dadurch kleiner geworden.

Das Verändern von Daten aus verschiedenen Threads sollte man vermeiden oder man muss es entsprechend Absichern um zu garantieren das kritischer Code immer nur von einem Thread gleichzeitig ausgeführt werden kann. Das `threading`-Modul bietet Werkzeuge dafür. Nebenläufige Programmierung (also zum Beispiel mit Threads) ist ein schwieriges Thema und blöderweise kann man damit fehlerhafte Programme schreiben, die scheinbar funktionieren, aber dann irgendwann doch einmal über den Fehler stolpern.

Nochmal: Problem ist ganz einfach dadurch zu lösen, dass man `io_init()` überhaupt nur einmal aufruft.

Warum steckst Du erst die beiden Motoren in eine Klasse um dann doch von aussen auf beide einzeln zuzugreifen? Und Du hast da im Grunde auch wieder eine „race condition“ wenn beide gleichzeitig am Anfang die Geschwindigkeit beider Motoren auf 0 setzen und dann gleich darauf jeweils bei ihren eigenen Motor eine andere Geschwindigkeit setzen. Wenn einer der Threads an der Stelle geringfügig schneller ist und seinen Motor loslaufen lässt, dann kann es passieren das der andere Thread den danach wieder stoppt.

Das ist auch einer der Unterschiede zwischen den beiden Varianten die Du zeigst.

Es macht nicht so viel Sinn zwei Threads zu starten und im dritten dann *nichts* zu machen, in einer Schleife die Rechenzeit verbraucht. Da könnte man auch einen Thread starten und die Arbeit des anderen im Hauptthread erledigen.

Daneben das man ``global`` nicht verwenden sollte, gehören generell keine Variablen und kein Code auf Modulebene. Der Code dort sollte nur Konstanten, Funktionen, und Klassen definieren. Alles andere gehört in Funktionen. Man sollte ein Modul immer ohne weitere Effekte importieren können.
„All religions are the same:
religion is basically guilt, with different holidays.” — Cathy Ladman
Dominikin9
User
Beiträge: 13
Registriert: Mittwoch 4. Januar 2017, 17:39

Re: Motorkarte Library

Beitragvon Dominikin9 » Montag 13. Februar 2017, 20:04

@BlackJack Danke für die ausführliche Erklärung, sie hat mir einiges weitergeholfen und ich versuche es mal umzusetzen.

Mit freundlichen Grüßen
Dominic Amon

Zurück zu „Raspberry Pi und Co.“

Wer ist online?

Mitglieder in diesem Forum: Landixus