pi gestützte laborausrüstung

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Morila
User
Beiträge: 8
Registriert: Freitag 4. März 2016, 11:30

hi leute. ich würde gerne mit dc motoren und ir sensoren die den drehwinkel erfassen( also quasi ein stepper motor) ein system bauen, dass auf eingabebefehl (X: Y: ) einen bestimmten punkt anfährt und mich danach wieder fragt, welchen punkt als nächstes. der code steht schon fertig im grunde. es fehlen einige funktionen, wie z.b. Fahre0:0 an usw, aber zum testen sollte es reichen. leider bekomme ich immer die selbe fehlermeldung:

Traceback (most recent call last):
File "cnc.py", line 161, in <module>
Naechster_Punkt(Benutzer_eingabe_X,Benutzer_eingabe_Y)
File "cnc.py", line 142, in Naechster_Punkt
relativeBewegung(s,r)
File "cnc.py", line 121, in relativeBewegung
p = (Momentane_Koordinate_X + x)
UnboundLocalError: local variable 'Momentane_Koordinate_X' referenced before assignment

und ich weiß nicht woran das liegen könnte. 10 mal bin ich alles durchgegangen kann den fehler aber nicht entdecken.
danke im voraus schonmal für eure hilfe. den code hänge ich einfach drunter.

Euer Morila

Code: Alles auswählen

#!/usr/bin/pyton



print("Starte cnc Programm")
print("Importiere Libarys...")
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
import time
print("Fertig")



print("Setlle GPIO pins ein")
GPIO.cleanup()
IR_Pin_X = 18
IR_Pin_Y = 12
GPIO.setup(IR_Pin_X,GPIO.IN)
GPIO.setup(IR_Pin_Y,GPIO.IN)

Stepper_Pin_X_rechts = 23
Stepper_Pin_X_links = 24
Stepper_Pin_Y_rechts = 16
Stepper_Pin_Y_links = 20
GPIO.setup(Stepper_Pin_X_rechts,GPIO.OUT)
GPIO.setup(Stepper_Pin_X_links,GPIO.OUT)
GPIO.setup(Stepper_Pin_Y_rechts,GPIO.OUT)
GPIO.setup(Stepper_Pin_Y_links,GPIO.OUT)
GPIO.output(Stepper_Pin_X_rechts,GPIO.LOW)
GPIO.output(Stepper_Pin_X_links,GPIO.LOW)
GPIO.output(Stepper_Pin_Y_rechts,GPIO.LOW)
GPIO.output(Stepper_Pin_Y_links,GPIO.LOW)
print("Fertig")

print("Setze Weltweite Variabeln")
Momentane_Koordinate_X = 0
Momentane_Koordinate_Y = 0
Benutzer_eingabe_X = 0
Benutzer_eingabe_Y = 0
print("Fertig")




print("definiere unterprogramme")


print("Stepp_X")
def Stepp_X(x) :

   if ( x>0 ) :
      GPIO.output(Stepper_Pin_X_rechts,GPIO.HIGH)
   if ( x<0 ) :
      GPIO.output(Stepper_Pin_X_links,GPIO.HIGH)
   time.sleep(0.01)
 
   if ( GPIO.input(IR_Pin_X) == 1 ) :
       GPIO.output(Stepper_Pin_X_rechts,GPIO.LOW)
       GPIO.output(Stepper_Pin_X_links,GPIO.LOW) 
print("Fertig")


print("Stepp_Y")
def Stepp_Y(x) :

   if ( x>0 ) :
      GPIO.output(Stepper_Pin_Y_rechts,GPIO.HIGH)
   if ( x<0 ) :
      GPIO.output(Stepper_Pin_Y_links,GPIO.HIGH)
   time.sleep(0.01)

   if ( GPIO.input(IR_Pin_Y) == 1 ) :
       GPIO.output(Stepper_Pin_Y_rechts,GPIO.LOW)
       GPIO.output(Stepper_Pin_Y_links,GPIO.LOW)
print("Fertig")


print("relativeBewegung")
def relativeBewegung(x,y) :
   
   print("zu fahren x")
   print(x)
   print(" ")
   print("zu fahren y")
   print(y)
   print(" ")

   
   a = 0
   b = 0
   p = 0
   q = 0
   Schritte = 0
   Zaehler = 0

   if ( x<0 ) :
      a =(x*(-1))
   else :
      a=x
   if ( y<0 ) :
      b =(y*(-1))
   else : 
      b=y
   Schritte = (a*b)

   while ( Zaehler<=Schritte ) :

      if ( (Zaehler%b)==0 ) :
         if (x>0) :
            Stepp_X(1)
         if (x<0) :
            Stepp_X(-1)

      if ((Zaehler%a)==0):
         if (y>0) :
            Stepp_Y(1)
         if (y<0) :
            Stepp_Y(-1)
      Zaehler = (Zaehler+1) 
   
   p = (Momentane_Koordinate_X + x) 
   q = (Momentane_Koordinate_Y + y)

   Momentane_Koordinate_X = p 
   Momentane_Koordinate_Y = q

   a = 0
   b = 0
   Schritte = 0
   Zaehler = 0
print("Fertig")


print ("naechster punkt")
def Naechster_Punkt(x,y) :
   s = 0
   r = 0
 
   s = (x-Momentane_Koordinate_X)
   r = (y-Momentane_Koordinate_Y)

   relativeBewegung(s,r)
print("Fertig")


print("Fertig")
print("Setup Beendet")





while (1==1) :
   print(" ")
   print("Momentane koordinate X:",Momentane_Koordinate_X)
   print("Momentane koordinate Y:",Momentane_Koordinate_Y)
   print(" ")
   print("Naechster Punkt:")
   Benutzer_eingabe_X = input("X:")
   Benutzer_eingabe_Y = input("Y:")
   Naechster_Punkt(Benutzer_eingabe_X,Benutzer_eingabe_Y)
Zuletzt geändert von Anonymous am Freitag 4. März 2016, 12:29, insgesamt 1-mal geändert.
Grund: Quelltext in Code-Tags gesetzt.
BlackJack

@Morila: Der Fehler ist hinter ``print("Setze Weltweite Variabeln")``: So etwas macht man nicht. Globaler Zustand macht Programme schwer nachvollziehbar, schlecht testbar, schlecht erweiterbar, und darum fehleranfälliger. Wenn irgendwo innerhalb einer Funktion oder Methode eine Zuweisung an einen Namen steht, dann ist dieser Name für Python lokal zur Funktion/Methode. Das heisst `Momentane_Koordinate_X` auf Modulebene ist ein andere Name als `Momentane_Koordinate_X` in der Funktion `Naechster_Punkt()`. Die beiden sind unabhängig voneinander. Und in der Zeile wo Du die Ausnahme bekommst versuchst Du zu dem Wert von dem lokalen `Momentane_Koordinate_X` etwas zu addieren, allerdings wurde diesem lokalen Namen vorher noch kein Wert zugewiesen also weiss der Rechner nicht zu welchem Wert er da etwas addieren sollte.

Funktionen und Methoden sollten nur Werte (ausser Konstanten) verwenden, die als Argumente übergeben wurden, und nicht einfach auf magische Weise auf irgendwelche Variablen ”aus der Umgebung”, und sie sollte die schon gar nicht verändern wollen. Dafür gibt es Rückgabewerte. Globale Variablen führen sehr schnell zu Chaos und Problemen.

Damit man gar nicht in die Gefahr kommt, das eine Funktion oder Methode etwas verwendet was nicht als Argument übergeben wurde, sollte man auf Modulebene nur Code stehen haben der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm gehört auch in eine Funktion, die üblicherweise `main()` heisst. Die kann man dann sogar ”schützen”, so dass sie nur ausgeführt wird, wenn das Modul als Programm ausgeführt wird, aber nicht wenn es importiert wird.

Du solltest also entweder mit ”echten” Funktionen arbeiten, und/oder Dir objektorientierte Programmierung (OOP) anschauen.

Edit: Ein Blick in den Style Guide for Python Code wäre noch empfehlenswert was die Schreibweisen von Namen angeht. Und Funktionen/Methoden werden üblicherweise nach Tätigkeiten benannt weil die etwas *tun* und um sie von eher passiven Werte unterscheiden zu können.
BlackJack

@Morila: Ein paar weitere Anmerkungen:

Die Klammern bei den ``print``-Anweisungen gehören da nicht hin, denn die Shebang-Zeile und die Verwendung vom Rückgabewert der `input()`-Funktion deuten auf Python 2 hin. Also entweder weg mit dem Klammern, oder aus der ``print``-Anweisung mit dem entsprechenden `__future__`-Import tatsächlich eine Funktion machen.

Importe werden üblicherweise an den Anfang des Moduls geschrieben und nicht durch anderen Code unterbrochen.

Danach werden Konstanten definiert, die konventionell komplett in Grossbuchstaben geschrieben werden, damit man sie als solche erkennen kann.

Die Einrücktiefe ist *vier* Leerzeichen pro Ebene und nicht drei.

Die `Benutzer_eingabe_*` muss man nicht mit 0 vorbelegen, denn diese Werte werden im Programm nie benutzt. Auch an anderen Stellen werden Namen sinnlos an 0en gebunden die nie verwendet werden. Man sollte auch notwendige Initialisierungen nicht ”meilenweit” von der Stelle vornehmen wo der Name dann letztendlich benutzt wird. Das macht den Quelltext unübersichtlicher und erhöht die Gefahr das man am Ende Namen im Programm behält die am Ende gar nicht mehr verwendet werden.

``1 == 1`` ergibt *immer* `True`, also macht es keinen Sinn diesen Ausdruck hinzuschreiben wenn man eigentlich `True` meint.

Um Bedingungen gehören keine unnötigen Klammern und wenn man Klammern setzen muss, dann sind die Leerzeichen zwischen Klammern und Inhalt zu viel.

Die `input()`-Funktion sollte man in Python 2 nicht verwenden. Da kann der Benutzer beliebige Python-Ausdrücke eingeben. Das ist zum einen eine riesige Sicherheitslücke und zum anderen ein Alptraum was die Fehlerbehandlung angeht, denn dort kann letztendlich *jeder* nur erdenkliche Fehler auftreten, weil beliebige Ausdrücke ausgewertet werden.

`a`, `b`, `p`, und `q` sind keine guten Namen weil sie dem Leser nicht verraten wofür die daran gebundenen Werte stehen.

Die ``if``/``else``-Konstrukte für `a` und `b` sind einfach nur die `abs()`-Funktion.

Die ``while``-Schleife in `relativeBewegung()` sollte eine ``for``-Schleife sein, denn man weiss ja im voraus wie viele Durchläufe die haben wird.

Die Argumente für `Stepp_X()` und `Stepp_Y()` ändern sich in der Schleife nicht weil sich die Werte in der Bedingung nie ändern, das heisst die könnte man einmal *vor* der Schleife ermitteln.

An einigen Stellen im Programm würde ``elif`` anstelle eines ``if`` Sinn machen.

Die beiden `Stepp_*()`-Funktionen sind fast identisch, da sollte man die gemeinsamkeiten herausziehen und eine Funktion draus machen. Dabei könnte man dann auch gleich mal die PINs für einen Stepper zu einer Datenstruktur zusammenfassen.

Ich lande dann als Zwischenergebnis bei so etwas (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import time
from RPi import GPIO

X_STEPPER = {
    'ir_pin': 18,
    'pin_rechts': 23,
    'pin_links': 24,
}
Y_STEPPER = {
    'ir_pin': 12,
    'pin_rechts': 16,
    'pin_links': 20,
}


def go_step(stepper, direction):
    if direction > 0:
        GPIO.output(stepper['pin_rechts'], GPIO.HIGH)
    elif direction < 0:
        GPIO.output(stepper['pin_links'], GPIO.HIGH)

    time.sleep(0.01)

    if GPIO.input(stepper['ir_pin']):
        GPIO.output([stepper['pin_rechts'], stepper['pin_links']], GPIO.LOW)


def get_step_direction(delta):
    if delta < 0:
        return -1
    elif delta > 0:
        return 1
    else:
        return 0


def bewege_um((momentanes_x, momentanes_y), (x_delta, y_delta)):
    print('zu fahren x')
    print(x_delta)
    print()
    print('zu fahren y')
    print(y_delta)
    print()

    x_step_direction = get_step_direction(x_delta)
    y_step_direction = get_step_direction(y_delta)
    x_betrag = abs(x_delta)
    y_betrag = abs(y_delta)
    for zaehler in xrange(x_betrag * y_betrag + 1):
        if zaehler % x_betrag == 0:
            go_step(X_STEPPER, x_step_direction)
        if zaehler % y_betrag == 0:
            go_step(Y_STEPPER, y_step_direction)

    return (momentanes_x + x_delta, momentanes_y + y_delta)


def gehe_zu((momentanes_x, momentanes_y), (naechstes_x, naechstes_y)):
    return bewege_um(
        (momentanes_x, momentanes_y),
        (naechstes_x - momentanes_x, naechstes_y - momentanes_y)
    )


def main():
    print('Starte CNC Programm')
    GPIO.cleanup()
    GPIO.setmode(GPIO.BCM)
    GPIO.setup([s['ir_pin'] for s in [X_STEPPER, Y_STEPPER]], GPIO.IN)
    GPIO.setup(
        [
            X_STEPPER['pin_rechts'], X_STEPPER['pin_links'],
            Y_STEPPER['pin_rechts'], Y_STEPPER['pin_links'],
        ],
        GPIO.OUT,
        initial=GPIO.OUT
    )

    momentane_koordinate = (0, 0)

    while True:
        print()
        print('Momentane X-Koordinate X:', momentane_koordinate[0])
        print('Momentane Y-Koordinate Y:', momentane_koordinate[1])
        print()
        print('Naechster Punkt:')
        naechste_koordinate = (int(raw_input('X: ')), int(raw_input('Y: ')))
        momentane_koordinate = gehe_zu(
            momentane_koordinate, naechste_koordinate
        )


if __name__ == '__main__':
    main()
Die Eingabe neuer Koordinaten müsste man noch gegen Fehleingaben absichern. Und dann könnte man anfangen eigene Datentypen zu definieren, zum Beispiel für Koordinaten und für Stepper-Motoren.
Morila
User
Beiträge: 8
Registriert: Freitag 4. März 2016, 11:30

Wow.
Vielen dank für die rolle und schnelle Hilfe. Es funktioniert jetzt.
Top.
Danke. Euer morila
Antworten