Schrittmotoren bewegen sich nur hoch und runter?

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
SilverLotus
User
Beiträge: 4
Registriert: Sonntag 19. Juni 2022, 10:52

Hallo zusammen,

ich habe im Rahmen eines Projektes ein Miniatur Seilkamera-System gebaut.
Nun bewegen die vier Schrittmotoren die an Angelschnüren befestigte Kamera nur nach oben und nach unten, wobei ich gerne hätte, dass die Kamera, wie bei einer echten Seilkamera, entsprechend von rechts nach links oder umgekehrt schwenkt.

Weiß eventuell jemand wo hier der Haken liegt?

import math
import threading
import schrittmotor1 as sm1
import schrittmotor2 as sm2
import schrittmotor3 as sm3
import schrittmotor4 as sm4
import camera

# Festlegung der Vektoren der vier Masten
e = [0, 0, 42]
f = [100, 0, 42]
g = [100, 62, 42]
h = [0, 62, 42]

# Funktion zur Berechnung der Seillängen aus den Vektoren für Start- und Zielpunkt
def berechneSeillaenge(start, ziel):

# Liste zur Speicherung der Seillängen für die vier Masten
seillaengen = [0,0,0,0]

# Berechnung der Seillänge für Masten e
es = [start[0]-e[0], start[1]-e[1], start[2]-e[2]]
ez = [ziel[0]-e[0] , ziel[1]-e[1] , ziel[2]-e[2]]

ES = math.sqrt(es[0]**2 + es[1]**2 + es[2]**2)
EZ = math.sqrt(ez[0]**2 + ez[1]**2 + ez[2]**2)

seillaengen[0] = EZ-ES

# Berechnung der Seillänge für Masten f
fs = [start[0]-f[0], start[1]-f[1], start[2]-f[2]]
fz = [ziel[0]-f[0] , ziel[1]-f[1] , ziel[2]-f[2]]

FS =(math.sqrt(fs[0]**2 + fs[1]**2 + fs[2]**2))
FZ =(math.sqrt(fz[0]**2 + fz[1]**2 + fz[2]**2))

seillaengen[1] = FZ-FS

# Berechnung der Seillänge für Masten g
gs = [start[0]-g[0], start[1]-g[1], start[2]-g[2]]
gz = [ziel[0]-g[0] , ziel[1]-g[1] , ziel[2]-g[2]]

GS =(math.sqrt(gs[0]**2 + gs[1]**2 + gs[2]**2))
GZ =(math.sqrt(gz[0]**2 + gz[1]**2 + gz[2]**2))

seillaengen[2] = GZ-GS

# Berechnung der Seillänge für Masten h
hs = [start[0]-h[0], start[1]-h[1], start[2]-h[2]]
hz = [ziel[0]-h[0] , ziel[1]-h[1] , ziel[2]-h[2]]

HS =(math.sqrt(hs[0]**2 + hs[1]**2 + hs[2]**2))
HZ =(math.sqrt(hz[0]**2 + hz[1]**2 + hz[2]**2))

seillaengen[3] = HZ-HS

# Rückgabe der Liste der Seillängen
return seillaengen

# Funktion zur Berechnung der Umdrehungen der Motoren für die jeweilige Seillänge der Masten
def berechneMotorUmdrehungen(seillaengen):

# Liste zur Speicherung der Umdrehungen der vier Motoren
umdrehungen = [0,0,0,0]

umfang = 2*math.pi*3.25

# Berechnung der Umdrehung des Motors an Masten e
umdrehungen[0] = (seillaengen[0]/umfang)*512

# Berechnung der Umdrehung des Motors an Masten f
umdrehungen[1] = (seillaengen[1]/umfang)*512

# Berechnung der Umdrehung des Motors an Masten g
umdrehungen[2] = (seillaengen[2]/umfang)*512

# Berechnung der Umdrehung des Motors an Masten h
umdrehungen[3] = (seillaengen[3]/umfang)*512

# Rückgabe der Liste der Umdrehungen
return umdrehungen

#Funktion zur Steuerung des Motors auf Basis der ermittelten Umdrehungen und Seilängen
def steuereMotor(umdrehungen):

# Starte die Schrittmotoren
sm1.setup()
sm2.setup()
sm3.setup()
sm4.setup()

# Steuerung des Motors an Masten e für die ermittelte Umdrehung
# (mithilfe von Threads zu gleichzeitigen Ausführung der Umdrehungen)
if motorUMD[0]>0:
th1=threading.Thread(target=sm2.forward, args = (int(abs(umdrehungen[0])),))
th1.start()
else:
th1=threading.Thread(target=sm2.backward, args=(int(abs(umdrehungen[0])),))
th1.start()

# Steuerung des Motors an Masten f für die ermittelte Umdrehung
if motorUMD[1]>0:
th2=threading.Thread(target=sm1.forward, args = (int(abs(umdrehungen[1])),))
th2.start()
else:
th2=threading.Thread(target=sm1.backward, args = (int(abs(umdrehungen[1])),))
th2.start()

# Steuerung des Motors an Masten g für die ermittelte Umdrehung
if motorUMD[2]>0:
th3=threading.Thread(target=sm4.forward, args = (int(abs(umdrehungen[2])),))
th3.start()
else:
th3=threading.Thread(target=sm4.backward, args = (int(abs(umdrehungen[2])),))
th3.start()

# Steuerung des Motors an Masten h für die ermittelte Umdrehung
if motorUMD[3]>0:
th4=threading.Thread(target=sm3.forward, args = (int(abs(umdrehungen[3])),))
th4.start()
else:
th4=threading.Thread(target=sm3.backward, args = (int(abs(umdrehungen[3])),))
th4.start()

#Funktion zur Steuerung des Spidercam mithilfe der oben definierten Funktionen
def bewegeKamera (start, ziel):
# Erst werden die neuen Seillängen berechnet...
seillaengen = berechneSeillaenge(start, ziel)

# ... dann die Umdrehungen berechnet ...
umdrehungen = berechneMotorUMD (seillaengen)

# ... und auf Basis dieser Berechnungen die Motoren gesteuert
steuereMotor(umdrehungen)

# Zudem wird die Kamera für die Filmaufname gestartet
thread5=threading.Thread(target=camera.camera)
thread5.start()


Vielen Dank schon mal :)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Als Programmierer versucht man möglichst wenig Code zu schreiben, die vier Module schrittmotor1 bis 4 lassen schlimmes vermuten und auch der Code, den Du zeigst, besteht Größtenteils aus viermal dem selben.
Man initialisiert keine Listen mit Dummy-Werten, um sie danach mit den richtigen Werten zu füllen, sondern erzeugt die Listen gleich richtig.
Variablennamen sollen aussagekräftig sein, e bis g sind das definitiv nicht, Konstanten schreibt man zudem KOMPLETT GROSS, alle andere Variablen dagegen komplett_klein.
`motorUMD` wird nirgends definiert.

Mit ein paar Schleifen wird das ganze ziemlich simpel:

Code: Alles auswählen

import math
import threading
import schrittmotor
import camera 

# Festlegung der Vektoren der vier Masten
MASTEN = [
    [0, 0, 42],
    [100, 0, 42],
    [100, 62, 42],
    [0, 62, 42],
]
RADIUS = 3.25

def punkt_abstand(punkt1, punkt2):
    return sum((a - b)**2 for a, b in zip(punkt1, punkt2)) ** 0.5

def berechne_seillaenge(start, ziel):
    """
    Funktion zur Berechnung der Seillängen aus den Vektoren
    für Start- und Zielpunkt
    """
    return [
        punkt_abstand(ziel, mast) - punkt_abstand(start, mast)
        for mast in MASTEN
    ]


def berechne_motor_umdrehungen(seillaengen):
    """
    Funktion zur Berechnung der Umdrehungen der Motoren für die
    jeweilige Seillänge der Masten
    """
    umfang = 2 * math.pi * RADIUS
    return [
        a / umfang * 512
        for a in seillaengen
    ]


def steuere_motor(motoren, umdrehungen):
    """
    Funktion zur Steuerung des Motors auf Basis der ermittelten
    Umdrehungen und Seilängen
    """
    for motor, umdrehung in zip(motoren, umdrehungen):
        # Starte die Schrittmotoren
        motor.setup()
        threading.Thread(
            target=motor.forward if umdrehung > 0 else motor.backward,
            args=(int(abs(umdrehung)), )
        ).start()
Wobei das mit der Motorsteuerung noch etwas seltsam ist, warum werden da Threads gebraucht?

Und wie sieht Deine ganze Konstruktion aus? Hast Du da eine Zeichnung?
SilverLotus
User
Beiträge: 4
Registriert: Sonntag 19. Juni 2022, 10:52

Vielen Dank für den Code! Aber auch hier fährt er nur hoch und runter und bleibt nicht an einem Punkt.
Unten ist der Code des Motors, habe den Motor vier mal eingefügt, um jeweils alle vier Motoren steuern zu können, aber ein Code bewegt so schon alle vier Motoren, was mich ziemlich wundert.
Die Threads dienen dazu, dass sich die Motoren gleichzeitig ansteuern lassen, aber wie ich im Code bestimmte Vektoren/Parameter so definiere, dass die Kamera von einem Punkt zum anderen geht weiß ich leider nicht :/

Motorcode:

from time import sleep
import RPi.GPIO as GPIO

#Verwendete Pins des Raspberry Pi
A=18
B=23
C=24
D=25

time = 0.0025

def setup():
GPIO.setmode(GPIO.BCM)

#Pins als Ausgaenge definieren
GPIO.setup(A, GPIO.OUT)
GPIO.output(A, False)
GPIO.setup(B, GPIO.OUT)
GPIO.output(B, False)
GPIO.setup(C, GPIO.OUT)
GPIO.output(C, False)
GPIO.setup(D, GPIO.OUT)
GPIO.output(D, False)

#Schritte 1-8 festlegen
def Step1():
GPIO.output(D, True)
sleep(time)
GPIO.output(D, False)

def Step2():
GPIO.output(D, True)
GPIO.output(C, True)
sleep(time)
GPIO.output(D, False)
GPIO.output(C, False)

def Step3():
GPIO.output(C, True)
sleep(time)
GPIO.output(C, False)

def Step4():
GPIO.output(B, True)
GPIO.output(C, True)
sleep(time)
GPIO.output(B, False)
GPIO.output(C, False)

def Step5():
GPIO.output(B, True)
sleep(time)
GPIO.output(B, False)

def Step6():
GPIO.output(A, True)
GPIO.output(B, True)
sleep(time)
GPIO.output(A, False)
GPIO.output(B, False)

def Step7():
GPIO.output(A, True)
sleep(time)
GPIO.output(A, False)

def Step8():
GPIO.output(D, True)
GPIO.output(A, True)
sleep(time)
GPIO.output(D, False)
GPIO.output(A, False)

# Eine Umdrehung vorwaerts
def forward():
for i in range(512):
Step1()
Step2()
Step3()
Step4()
Step5()
Step6()
Step7()
Step8()


# Eine Umdrehung rueckwaerts
def backward():
for i in range(512):
Step8()
Step7()
Step6()
Step5()
Step4()
Step3()
Step2()
Step1()

# Setzt die Zeit, die die Geschwindigkeit regelt:
# Je groesser die Zeit, desto langsamer die Geschwindigkeit
def setSpeed(newtime):
time = newtime

def cleanup():
GPIO.cleanup()

Ich würde gerne hier ein Bild vom Konstrukt einfügen aber irgendwie funktioniert das bei mir auch nicht :lol:
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich bleibe ja dabei, dass der Ansatz das mit dem Pi und Python zu machen nicht erfolgreich werden wird. Der haelt das notwendige Timing nicht. Aber du scheinst es ja besser zu wissen.

Statt vier mal den Code zu wiederholen, baut man sich eine Motor-Klasse, welche sowohl die Pins bekommt, und dann eben ihren Thread startet. Der sitzt dann da & wartet da drauf, dass ein Fahrkommando kommt. Das Kommando besteht aus einer Schrittzahl (positiv oder negativ), und einer Wartezeit. Das arbeitet der Thread dann ab. Und ist danach wieder bereit fuer das neue Kommando.
SilverLotus
User
Beiträge: 4
Registriert: Sonntag 19. Juni 2022, 10:52

Mit Besserwissen hat das ganze nichts zutun, sondern eher mit der Tatsache, dass ich jetzt nicht auf einen Arduino wechseln kann und das soweit nun mit Python machen muss. Und wie du bereits lesen konntest, bin ich noch ganz neu in der Programmierung, also kann ich nur über das sprechen, was vor mir liegt und das ist eben nun mal dieser Code. Kann auch sein, dass das ganze mega einfach ist oder der Fehler banal ist, aber eine Sprache lernt man auch nicht von jetzt auf gleich, mir fehlt dementsprechend einiges an Erfahrung, daher habe ich hier nur um Rat gefragt, wie ich den Code insoweit abändern kann, dass er das tut was ich erwarte.
So wie du es schilderst macht ja Sinn, aber wie gesagt, wie ich das umsetzen soll in dem was vor mir ist weiß ich nicht.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich kann dir nur raten, demjenigen, der diese Vorgabe macht, klar zu machen, dass diese Aufgabe eben genau nicht banal ist. Sondern ziemlich komplex, und ggf. nicht lösbar (für dich mindestens mal) unter den gegebenen Randbedingungen. Ich arbeite mit dem Pi & Echtzeit Kommunikation, und würde es so nicht machen. Darum kommt nun mal diese Antwort von mir. Und wenn man mit Flipflops vor dem Mount Everest steht, und gesagt bekommt, das wird nix - dann ist der Hinweis auf mangelnde Erfahrung auch nicht zielführend. Denn genau um die geht es hier ja. Du hast sie nicht, ich hab sie, ich das dir, das wird eher nix. Was erwartest du jetzt? Das ich da meine Meinung wieder bessern Wissens ändere?

Auch der Ansatz mit Threads ist zum scheitern(*) verurteil, weil in Python Threads nicht wirklich parallel arbeiten können. Such mal nach “Python GIL”. Und damit kommen deine Timings notwendigerweise durcheinander, denn 4 Threads, die zeitgenau Schritte produzieren, werden so nicht funktionieren. Selbst mehrere Prozesse sind da im Zweifel nicht gut genug. Hinweis darauf: LinuxCNC, welches für seine Schrittmotoren ein spezielles Echtzeitlinux haben muss. Und dann auch nicht pro Motor einen Prozess fährt, sondern einen hochpräzisen Thread mit 50K Berechnungen pro Sekund, der die jeweils notwendige Wartezeit zum nächsten Schritt wartet, und den dann durchführt. Es gibt das aber AFAIK nicht für den Pi, ich habe zumindest nichts gefunden. Alles x86 + spezielle IO-Karten oder Parallelports. Und solche Geschwindigkeiten bekommst du mit Python schlicht nicht, und Echtzeit muss auch noch dabei, sonst harkt dir zb ein Netzwerkpaket dazwischen.

Also: ist nicht banal. Gibt es aber , zumindest in Bezug auf die Motoransteuerung - ich wiederhole mich - für kleines Geld mit Arduino schon fertig.

(*) scheitern wenn die Aufgabe erfordert, dass die Bewegung halbwegs geschmeidig ist. So lange Wackeln bis die Motoren stoppen ist für mich keine Lösung. Aber vielleicht für dich. Dann happy hacking.
Benutzeravatar
Dennis89
User
Beiträge: 1123
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

den Code mit der 'cleanup'-Funktion die nicht aufgerufen wird, habe ich irgendwo doch schon mal gesehen 🤔

Mal allgemein gesprochen:
Ich weis nicht ob ich das gerade übersehe, aber du hast ja Schrittmotoren. Würde es nicht Sinn machen, die gefahrenen Schritte mit den vorgegebenen zu vergleichen? Die Umdrehungen berechnest du ja, aber weiter passiert damit nichts.

An deiner Stelle würde ich erst mal einen einzigen Motor ansteuern, bis der genau das macht was du willst. Jonglieren lernt man auch nicht mit 3 Motorsägen und 5 Bällen. Am einfachsten lernt es sich übrigens wenn man den Ratschlägen der Erfahrenen folgt. Ich bin auch kein Programmierer und hab auch nur ein paar Sachen mit Hilfe von Foren gelernt und es hat sich ausgezahlt Schritt für Schritt zu befolgen. Was total in die Hose ging, war der Gedanke "Ich ändere kurz etwas Code ab und bin fertig".

Interessehalber, ist das ein Schulprojekt oder wieso musst du bei dem Pi bleiben? Das wäre doch sicherlich was für einen Microkontroller (?), der ist auch billiger und hat kein nerviges Betriebssystem.

@Sirius3 'schrittmotor' und 'camera' werden importiert, aber nicht genutzt. *duck und ganz schnell weg* scnr

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Da vielleicht ja auch das Kamera-Modul letztlich für irgend etwas zum Einsatz kommt, wäre vielleicht auch eine Kombination aus Raspi und Mikrocontroller ein Weg. Das man einen Arduino oder etwas in der Art für die Schrittmotoransteuerung verwendet, und mit dem vom Raspi aus kommuniziert was der machen soll.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

@__blackjack__: das ist ja auch mein Vorschlag. Wie komplex die Wegeplanung wird, ist mir noch nicht ganz klar. Ob man wirklich damit hinkommt, die Seillängen nur einmal zu berechnen, und dann linear zu verfahren, ist IMHO nicht ganz sicher. Alleine aus der Anschauung zb von oben auf eine Ecke der Kameraplattform, die sich von links nach recht bewegt, mit konstanter Geschwindigkeit, ergibt sich denke ich ein nicht-linearer Geschwindigkeitsverlauf für den Schrittmotor. Wenn man nur definierte Endpunkte haben will, reicht es ggf. Aber wenn eine komplette Trajektorie gegeben ist, wird das nicht reichen.
Antworten