Problem mit Macroschiene.

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
dravida
User
Beiträge: 11
Registriert: Donnerstag 8. Juli 2021, 03:52

Hallo,
ich baue mir gerade einen automatischen Makroschlitten welcher mit einem Python Script laufen soll.
Nach langer Suche habe ich folgendes Script gefunden und es mir was die "Pin Belegung" und "Steodelay" angeht angepasst.

Code: Alles auswählen

import sys
import wiringpi
from time import sleep

# direction (1)
# number of steps (200)
# delay between steps (2)

gpio = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_GPIO)  

motor_pin_a = 17
motor_pin_b = 4
motor_pin_c = 27
motor_pin_d = 10
shutterpin = 9

gpio.pinMode(motor_pin_a,gpio.OUTPUT)
gpio.pinMode(motor_pin_b,gpio.OUTPUT)
gpio.pinMode(motor_pin_c,gpio.OUTPUT)
gpio.pinMode(motor_pin_d,gpio.OUTPUT)
gpio.pinMode(shutterpin,gpio.OUTPUT)  

wiringpi.pinMode(shutterpin,1)
wiringpi.pinMode(motor_pin_a,1)
wiringpi.pinMode(motor_pin_b,1)
wiringpi.pinMode(motor_pin_c,1)
wiringpi.pinMode(motor_pin_d,1)

if sys.argv[1] == '1':
	start=0
	finish=int(sys.argv[2],base=10)
	increment=1
else:
	start=int(sys.argv[2],base=10)
	finish=0
	increment=-1

stepdelay=float(sys.argv[3])
a = [ '1000', '1100', '0100', '0110', '0010', '0011', '0001', '1001']

for j in range(start,finish,increment):
	i = j%4
	if a[i][0:1] == '1':
		gpio.digitalWrite(motor_pin_a,gpio.HIGH)
	else:
		gpio.digitalWrite(motor_pin_a,gpio.LOW)
	if a[i][1:2] == '1':
		gpio.digitalWrite(motor_pin_b,gpio.HIGH)
	else:
		gpio.digitalWrite(motor_pin_b,gpio.LOW)
	if a[i][2:3] == '1':
		gpio.digitalWrite(motor_pin_c,gpio.HIGH)
	else:
		gpio.digitalWrite(motor_pin_c,gpio.LOW)
	if a[i][3:4] == '1':
		gpio.digitalWrite(motor_pin_d,gpio.HIGH)
	else:
		gpio.digitalWrite(motor_pin_d,gpio.LOW)
	sleep(stepdelay)

# sleep 100ms to let things settle.
sleep(0.1)

# Trigger the camera shutter.
gpio.digitalWrite(shutterpin,gpio.HIGH)
sleep(0.1)
gpio.digitalWrite(shutterpin,gpio.LOW)
Soweit so gut...

Sobald ich dsa Script ausführe bekomme ich folgende Meldung:

Code: Alles auswählen

root@storageBox:~/rail# python macro.py
Traceback (most recent call last):
  File "macro.py", line 29, in <module>
    if sys.argv[1] == '1':
IndexError: list index out of range
Ich keinerlei Programmierkenntnisse und hoffe dennoch sehr dass mir hier geholfen werden kann.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

Der Fehler ensteht weil du keine Kommandozeilenargumente übergeben hast.

sys.argv[0] ist immer der Skriptpfad.

alle folgenden Einträge in der Liste sind optional. Du must also erst prüfen ob es überhaupt mehr als einen Eintrag in der Liste gibt:

Code: Alles auswählen

if len(sys.argv) > 1:
Bzw., da du ja teilweise noch mehr optionale Parameter erwartest muss dass entsprechend angepasst werden.
Vielleicht schaust du dir mal 'argparse' an um eine etwas komfortablere und robustere Argumentübergabe zu implementieren:
https://docs.python.org/3/library/argparse.html

Damit kannst du dann auch Fehleingaben behandeln. Denn wenn man falsche Werte für start, finish, increment eingibt, wird das zu ein Exception führen.

Code: Alles auswählen

# nicht nötig:
a[i][0:1]

# Es ist ja immer nur ein 'bit':
a[1][0]
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Als erstes sollte man die Codedopplungen etwas reduzieren:

Code: Alles auswählen

#!/usr/bin/env python
import sys
import wiringpi
from time import sleep
from itertools import cycle, islice

# direction (1)
# number of steps (200)
# delay between steps (2)

MOTOR_PINS = [17, 4, 27, 10]
SHUTTER_PIN = 9
MODES = [ '1000', '1100', '0100', '0110', '0010', '0011', '0001', '1001']

def initalize():
    gpio = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_GPIO)  
    for motor_pin in MOTOR_PINS:
        gpio.pinMode(motor_pin, gpio.OUTPUT)
    gpio.pinMode(SHUTTER_PIN, gpio.OUTPUT)  
    return gpio

def main():
    gpio = initialize()
    if len(sys.argv) != 4:
        print("Arguments: [direction] [steps] [stepdelay]")
        return
    direction = sys.argv[1]
    steps = sys.argv[2]
    stepdelay = float(sys.argv[3])
    
    if direction:
        modes = islice(cycle(MODES), steps)
    else:
        skip = -(steps % -len(MODES))
        modes = islice(cycle(reversed(MODES)), skip, skip + steps)

    for mode in modes:
        for pin, value in zip(MOTOR_PINS, mode):
            gpio.digitalWrite(pin, gpio.HIGH if value == '1' else gpio.LOW)
        sleep(stepdelay)

    # sleep 100ms to let things settle.
    sleep(0.1)

    # Trigger the camera shutter.
    gpio.digitalWrite(SHUTTER_PIN, gpio.HIGH)
    sleep(0.1)
    gpio.digitalWrite(SHUTTER_PIN, gpio.LOW)

if __name__ == "__main__":
    main()
Dann erwartet das Programm drei Kommandozeilenparameter: direction, number of steps und delay between steps.
Der Aufruf könnte also so aussehen:

Code: Alles auswählen

python programm.py 1 200 2
Zuletzt geändert von Sirius3 am Donnerstag 8. Juli 2021, 08:25, insgesamt 1-mal geändert.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Sirius3, haben sich da nicht ein paar Fehler eingeschlichen?
'direction' wird gar nicht mehr verwendet.

Müsste es nicht

Code: Alles auswählen

for mode in modes:
lauten?

Variablennamen die sich nur in der Groß- und Kleinschreibung unterscheiden, würde ich vermeiden.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Danke rogerb. Hab's oben korrigiert. Eigentlich weiß ich, wann ich eine Konstante benutzen will und wann eine Variable. Daher halte ich das bei komplett GROSS gegen komplett klein nicht für so kritisch. Wäre nur ein Buchstabe unterschied, sähe das anders aus.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei man vielleicht noch anmerken sollte, dass die zugrunde liegende `wiringpi`-Bibliothek vom Entwickler schon seit einer Weile nicht mehr gepflegt wird. Da sollte man sich besser nach einer Alternative umschauen. `gpiozero` beispielsweise.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
dravida
User
Beiträge: 11
Registriert: Donnerstag 8. Juli 2021, 03:52

]Zunächst danke für eure Hilfe!

Wenn ich den Coode von "Sirius3" nutze:

Code: Alles auswählen

python programm.py 1 200 2
bekomme ich folgenden Output:

Code: Alles auswählen

root@storageBox:~/rail# python programm.py 1 200 2
Traceback (most recent call last):
  File "forum1.py", line 51, in <module>
    main()
  File "forum1.py", line 23, in main
    gpio = initialize()
NameError: global name 'initialize' is not defined

Der Link den "rogerb" gepostet hat war leider nicht hilfreich da mir so ziemlich alle Basics in python fehlen.
Ich verstehe überwiegend was die einzelnen Zeilen in meinem Code machen, allerdings fehlt mir die nötige Expertise ihn selbst zu schreiben.
Mehr als das abändern von Werten ist mir leider "noch" nicht möglich und für das. Lernen der ganzen Sprache fehlt mir leider die Zeit.


Das WiringPi nicht mehr gepflegt wird habe ich heute morgen auch Festellen müssen.
Allerdings reicht das was es kann für meinen zweck aus.
Habe ich irgendwelche Vorteile mit "gpiozero" für meinen Verwendungszweck, __blackjack__?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

@dravida Sirius3 hat einen simplen Schreibfehler gemacht. Vergleich mal genau die entsprechenden Namen.
dravida
User
Beiträge: 11
Registriert: Donnerstag 8. Juli 2021, 03:52

@_deets_

Er hat keinen Fehler gemacht sondern ich.
Ich wollte das ganze der Übersicht halber abändern, habe aber leider vergessen alles zu ändern.

Der Output sieht wie folgt aus:

Code: Alles auswählen

root@storageBox:~/rail# python forum1.py 1 200 2
Traceback (most recent call last):
  File "forum1.py", line 51, in <module>
    main()
  File "forum1.py", line 23, in main
    gpio = initialize()
NameError: global name 'initialize' is not defined
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Das auf gpiozero umzuschreiben, sollte recht einfach sein:

Code: Alles auswählen

#!/usr/bin/env python
import sys
from gpiozero import DigitalOutputDevice
from time import sleep
from itertools import cycle, islice

# direction (1)
# number of steps (200)
# delay between steps (2)

MOTOR_PINS = [17, 4, 27, 10]
SHUTTER_PIN = 9
MODES = [ '1000', '1100', '0100', '0110', '0010', '0011', '0001', '1001']

def initialize():
    motor_pins = [
        DigitalOutputDevice(pin)
        for pin in MOTOR_PINS
    ]
    shutter = DigitalOutputDevice(SHUTTER_PIN)
    return motor_pins, shutter

def main():
    if len(sys.argv) != 4:
        print("Arguments: [direction] [steps] [stepdelay]")
        return
    direction = sys.argv[1]
    steps = sys.argv[2]
    stepdelay = float(sys.argv[3])
    
    if direction:
        modes = islice(cycle(MODES), steps)
    else:
        skip = -(steps % -len(MODES))
        modes = islice(cycle(reversed(MODES)), skip, skip + steps)

    motor_pins, shutter = initialize()
    for mode in modes:
        for pin, value in zip(motor_pins, mode):
            pin.value = int(value)
        sleep(stepdelay)

    # sleep 100ms to let things settle.
    sleep(0.1)

    # Trigger the camera shutter.
    shutter.on()
    sleep(0.1)
    shutter.off()

if __name__ == "__main__":
    main()
dravida
User
Beiträge: 11
Registriert: Donnerstag 8. Juli 2021, 03:52

@Sirius3 ich bin dir sehr dankbar dass du dir die Mühe machst!
Welche Vorteile bietet mir "gpiozero"?

Beim ausführen bekomme ich folgenden Output:

Code: Alles auswählen

root@storageBox:~/rail# python forum2.py
Arguments: [direction] [steps] [stepdelay]
root@storageBox:~/rail# python forum2.py 1 200 2
Traceback (most recent call last):
  File "forum2.py", line 52, in <module>
    main()
  File "forum2.py", line 32, in main
    modes = islice(cycle(MODES), steps)
ValueError: Stop argument for islice() must be None or an integer: 0 <= x <= maxint.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Ja, ich bin doof, weil ich es nicht testen kann. Man muß `steps` in ein int umwandeln.

Code: Alles auswählen

def main():
    if len(sys.argv) != 4:
        print("Arguments: [direction] [steps] [stepdelay]")
        return
    direction = sys.argv[1]
    steps = int(sys.argv[2])
    stepdelay = float(sys.argv[3])
    
    if direction == "1":
        modes = islice(cycle(MODES), steps)
    else:
        skip = -(steps % -len(MODES))
        modes = islice(cycle(reversed(MODES)), skip, skip + steps)

    ...
gpiozero ist besser, weil es breiter unterstützt wird und noch aktiv weiterentwickelt wird.
dravida
User
Beiträge: 11
Registriert: Donnerstag 8. Juli 2021, 03:52

Ich danke dir!
Der Code läuft nun durch und die LEDs am Motor-Driver leuchten in der richtigen Reihenfolge auf.
Allerdings bewegt sich trotz "ticken" im Stepper nichts vor- oder rückwärts, des Weiteren löst die Kamera leider nicht aus.

Ich habe "gpiozero" nun gegoogelt und verstehe zumindest dass es allein deshalb wichtig ist, dass sobald eine neue python Version released ist ich (sofern weiterentwickelt) alles was ich über die GPIO Pins machen kann, auch mit dem neuen Python fortführen kann.
Benutzeravatar
hyle
User
Beiträge: 96
Registriert: Sonntag 22. Dezember 2019, 23:19
Wohnort: Leipzig

Btw. Wenn Du das Skript mit
dravida hat geschrieben: Donnerstag 8. Juli 2021, 15:35

Code: Alles auswählen

root@storageBox:~/rail# python forum2.py
...
aufrufst, dann wird (zumindest beim Raspberry Pi OS) noch das veraltete und nicht mehr weiterentwickelte Python 2 verwendet.

Den Python 3 Interpreter nutzt Du mit:

Code: Alles auswählen

python3 skriptname.py
Im Skript wäre dann der Shebang folgender:

Code: Alles auswählen

#!/usr/bin/env python3
Alles was wir sind ist Sand im Wind Hoschi.
dravida
User
Beiträge: 11
Registriert: Donnerstag 8. Juli 2021, 03:52

Der Motor fährt nun wie gewünscht (Feintuning von delay und stepcount) allerdings besteht noch das Problem dass die Kamera "permanent" auslöst sobald sie angesteckt ist.
dravida
User
Beiträge: 11
Registriert: Donnerstag 8. Juli 2021, 03:52

DIe Kamera löst nun immer nach Ende des Programms aus, ich möchte allerdings dass die Kamera bei einem von mir zuvor festgelegten Zeitpunkt auslöst (z.B. nach jedem 5. Step).
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@dravida

Wie sieht denn jetzt dein Code aus. Das sollte ja leicht einzubauen sein.
dravida
User
Beiträge: 11
Registriert: Donnerstag 8. Juli 2021, 03:52

Das denke ich mir auch. Ich finde es auch interessant wie viel ich nebenbei aufschnappe, wobei es definitiv noch nicht dazu reicht etwas eigenes zu schreiben.

Mein Code sieht wie folgt aus:

Code: Alles auswählen

#!/usr/bin/env python
import sys
from gpiozero import DigitalOutputDevice
from time import sleep
from itertools import cycle, islice

# direction (1)
# number of steps (200)
# delay between steps (2)

MOTOR_PINS = [17, 4, 2, 10]
SHUTTER_PIN = 26
MODES = [ '1000', '1100', '0100', '0110', '0010', '0011', '0001', '1001']

def initialize():
    motor_pins = [
        DigitalOutputDevice(pin)
        for pin in MOTOR_PINS
    ]
    shutter = DigitalOutputDevice(SHUTTER_PIN)
    return motor_pins, shutter

def main():
    if len(sys.argv) != 4:
        print("Arguments: [direction] [steps] [stepdelay]")
        return
    direction = sys.argv[1]
    steps = int(sys.argv[2])
    stepdelay = float(sys.argv[3])
    
    if direction == "1":
        modes = islice(cycle(MODES), steps)
    else:
        skip = -(steps % -len(MODES))
        modes = islice(cycle(reversed(MODES)), skip, skip + steps)

    motor_pins, shutter = initialize()
    for mode in modes:
        for pin, value in zip(motor_pins, mode):
            pin.value = int(value)
        sleep(stepdelay)

    # sleep 100ms to let things settle.
    sleep(0.1)

    # Trigger the camera shutter.
    shutter.on()
    sleep(0.1)
    shutter.off()

if __name__ == "__main__":
    main()
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@dravida,

ich bin mir nicht ganz sicher was du mit "Step" meinst. Und ich kann es auch nicht selber ausprobieren.

Versuch doch mal die for-Schleife so zu verändern:

Code: Alles auswählen

for step_num, mode in enumerate(modes):
    for pin, value in zip(motor_pins, mode):
        pin.value = int(value)
    sleep(stepdelay)
    
    if step_num % 5 == 0:
        # Trigger the camera shutter.
        shutter.on()
        sleep(0.1)
        shutter.off()
dravida
User
Beiträge: 11
Registriert: Donnerstag 8. Juli 2021, 03:52

@rogerb

Weißt du denn was ein Schrittmotor ist?
Falls nicht, hier ist es besser erklärt als ich es selbst könnte.
Andernfalls lernt vielleicht Jemand der sich den Thread durchliest noch etwas.
Der Schrittmotor ist ein Synchronmotor, bei dem der Läufer des Motors sich in kleinen Schritten bewegen kann.
Die prinzipielle Funktionsweise entspricht dabei der eines Synchronmotors mit einer hohen Polpaarzahl.
Der Motor hat bauartbedingt eine bestimmte Anzahl an Positionen (Schritten), die er bei einer Ansteuerung einnehmen kann.

Zur Steigerung der Genauigkeit beziehungsweise zur Vergrößerung der Schritt-Zahl werden die Pole des Rotormagneten gezahnt ausgeführt.
Ein ständiges Umpolen der Ständerwicklungen ermöglicht eine kontinuierliche Drehbewegung des Ankers.
Quelle: https://glossar.item24.com/glossarindex ... motor.html


Ich bin mir nicht sicher ob ich den Code richtig eingefügt habe, daher habe ich mehrere Optionen ausprobiert.
Allerdings machte es so am meisten Sinn für mich:

Code: Alles auswählen

#!/usr/bin/env python
import sys
from gpiozero import DigitalOutputDevice
from time import sleep
from itertools import cycle, islice

# direction (1)
# number of steps (200)
# delay between steps (2)

MOTOR_PINS = [17, 4, 2, 10]
SHUTTER_PIN = 26
MODES = [ '1000', '1100', '0100', '0110', '0010', '0011', '0001', '1001']

def initialize():
    motor_pins = [
        DigitalOutputDevice(pin)
        for pin in MOTOR_PINS
    ]
    shutter = DigitalOutputDevice(SHUTTER_PIN)
    return motor_pins, shutter

def main():
    if len(sys.argv) != 4:
        print("Arguments: [direction] [steps] [stepdelay]")
        return
    direction = sys.argv[1]
    steps = int(sys.argv[2])
    stepdelay = float(sys.argv[3])
    
    if direction == "1":
        modes = islice(cycle(MODES), steps)
    else:
        skip = -(steps % -len(MODES))
        modes = islice(cycle(reversed(MODES)), skip, skip + steps)

    motor_pins, shutter = initialize()
for step_num, mode in enumerate(modes):
    for pin, value in zip(motor_pins, mode):
        pin.value = int(value)
    sleep(stepdelay)
    
    if step_num % 5 == 0:
        # Trigger the camera shutter.
        shutter.on()
        sleep(0.1)
        shutter.off()

if __name__ == "__main__":
    main()

Ich bekomme egal wie ich den Code eingefügt habe immer eine Meldung die etwas mit "step_num & modes" zu tun hat.

Code: Alles auswählen

root@storageBox:~/rail# python forum2.py 1 500 0.001
Traceback (most recent call last):
  File "forum2.py", line 38, in <module>
    for step_num, mode in enumerate(modes):
NameError: name 'modes' is not defined

Ich konnte aber etwas eventuell nützliches finden dass uns/dir vielleicht weiterhilft:
Antworten