Forum: Mikrocontroller und Digitale Elektronik Micropython Stepper-Motor


von dux1 (Gast)


Lesenswert?

Moin zusammen,

ich hab ein kleines Problem: Ich programmiere mit micropython meinen 
esp32 für den Antrieb eines StepperMotors.
Dazu habe ich eine Klasse mit Methoden geschrieben. Es funktoiniert auch 
alles soweit und der Motor lässt sich steuern. Was ich aber nicht ganz 
verstehe ist, sobald ich eine Methode zur Stuerung des Motors aufrufe, 
wird bei mir die _init_-Methode auch immer wieder aufgerufen. Dies 
erkenne ich daran, dass in meinem Ausgabefenster für jeden Schritt des 
Motors "Init der Klasse" erscheint. Warum deklariert er jedes Mal das 
Objekt neu?
Kann es eventuell daran liegen, dass man in der _init_-Methode keine 
Ausgänge definieren darf?
Danke für die Hilfe.

Hier mein Code:

class stepper_motor() :


    def 
__init__(self,step=0,times=0.005,Seq=[],out1=16,out2=17,out3=18,out4=19) 
:
        '''Initialisierung der Klasse'''
        import machine
        print('Init der Klasse')



        self.step1 = step
        self.times = times



        self.out1 = machine.Pin(out1, machine.Pin.OUT)
        self.out2 = machine.Pin(out2, machine.Pin.OUT)
        self.out3 = machine.Pin(out3, machine.Pin.OUT)
        self.out4 = machine.Pin(out4, machine.Pin.OUT)



        self.out1.value(0)
        self.out2.value(0)
        self.out3.value(0)
        self.out4.value(0)



        Seq = [[1,0,0,0],
        [1,1,0,0],
        [0,1,0,0],
        [0,1,1,0],
        [0,0,1,0],
        [0,0,1,1],
        [0,0,0,1],
        [1,0,0,1]]
        self.Seq = Seq



    def setzPin(self,w1,w2,w3,w4):
      '''Methode zur Setzung der Ausgaenge'''
      self.out1.value(w1)
      self.out2.value(w2)
      self.out3.value(w3)
      self.out4.value(w4)




    def vorwaerts(self, step):
      '''Methode für vorwaerts'''
      import time
      for i in range(step):
        for j in range(len(self.Seq)):
          stepper_motor().setzPin(self.Seq[j][0], self.Seq[j][1], 
self.Seq[j][2], self.Seq[j][3])
          time.sleep(self.times)

von test (Gast)


Lesenswert?

Du musst den Code zeigen der diese Klasse aufruft.

von test (Gast)


Lesenswert?

Ähm, gerade gesehen...

stepper_motor().setzPin(self.Seq[j][0], self.Seq[j][1], self.Seq[j][2], 
self.Seq[j][3])

ist natürlich falsch, es muss

srlf.setzPin(self.Seq[j][0], self.Seq[j][1], self.Seq[j][2], 
self.Seq[j][3])

von dux1 (Gast)


Lesenswert?

Ich habe in uPyCraft programmiert. Dies hat eine Eingabefenster, sodass 
man direkt im esp32 programmieren kann. Alternativ kann man auch Putty 
nutzen.
Dann kann man recht einfach den Code auf den esp32 laden und ausführen.

        c = stepper_motor()

Mit dieser Zeile wird das Objekt erzeugt und initalisiert. Es erscheint 
einmal Init der Klasse, was auch richtig ist. Danach rufe ich die 
Methode auf:

        c.vorwaerts(30)

Anschließend sehe ich im Ausgabefenster 30mal Init der Klasse 
auffloppen. Das müsste meiner Meinung nach eigentlich doch nicht 
auftauchen.

von dux1 (Gast)


Lesenswert?

test schrieb:
> ist natürlich falsch, es muss
>
> srlf.setzPin(self.Seq[j][0], self.Seq[j][1], self.Seq[j][2],
> self.Seq[j][3])

Danke dir das hat geklappt. Endlich den Fehler gefunden :)
Kannst du mir erklären, warum ich eigentlich nicht stepper_motor(). 
schreiben darf?

von test (Gast)


Lesenswert?

"srlf" meint natürlich "self"


"import time" kann natürlich nach _init_

von dux1 (Gast)


Lesenswert?

test schrieb:
> "import time" kann natürlich nach _init_

Dann bekomme ich einen Fehler gemeldet, dass er time nicht kennt. Ich 
weiß zurzeit noch nicht wie ich auf die Bibliothek in der init 
verweisen kann. Kannst du mir da weiterhelfen?
Danke

von test (Gast)


Lesenswert?

dux1 schrieb:
> warum ich eigentlich nicht stepper_motor(). schreiben darf?

Weil das der Name der Klasse (eine reine Schablone nach der die Objekte 
erstellt werden) ist.
Schreibt du das hin dann erstellt Python auf Basis der Klasse ein neues 
Objekt (also der eigentliche Programmcode).

Also Klassen sind Definitionen für Objekte, Objekte ist das was man 
nutzt.

Du hast deine stepper_motor Klasse, dann erstellst du im Programm 
basierend auf dieser Klasse dein Motor Objekt. Und dieses Motor Objekt 
nutzt du.

Hast du zwei Motoren dann erstellst du halt ein motor1 und ein motor2 
Objekt basierend auf deiner stepper_motor Klasse (Theoretisch, praktisch 
müsstest du deine Klasse dafür anders programmieren). Dann kannst du 
motor1.vorwaerts und motor2.vorwaerts im Code benutzen.



Ich hoffe das war halbwegs verständlich. Wenn nicht gibt es hier sicher 
welche die es besser erklären können.

Und ein Buch/Kurs über objektorientierter Programmierung wäre evtl. auch 
hilfreich. Das sind so die Grundlagen.

von test (Gast)


Lesenswert?

dux1 schrieb:
> test schrieb:
>> "import time" kann natürlich nach init
>
> Dann bekomme ich einen Fehler gemeldet, dass er time nicht kennt. Ich
> weiß zurzeit noch nicht wie ich auf die Bibliothek in der init verweisen
> kann. Kannst du mir da weiterhelfen?
> Danke

Kann auch sein das ich mich da jetzt vertan habe. Lass einfach so wenns 
geht, schad ja nix. Sah für mich erstmal nur seltsam aus.

von dux1 (Gast)


Lesenswert?

test schrieb:
> Ich hoffe das war halbwegs verständlich. Wenn nicht gibt es hier sicher
> welche die es besser erklären können.


Doch das war für mich verständlich. Danke dir :)

von Joachim S. (oyo)


Lesenswert?

Ich habe mir den Code Deiner Schrittmotor-Klasse eben mal angeschaut, 
und mir sind da noch ein paar andere Sachen aufgefallen, die ich 
persönlich ändern würde:

- Die Methode "vorwaerts" funktioniert nach einer etwas fragwürdigen 
Logik: Durch die verschachtelte Schleife kann man den Motor immer nur um 
ein Vielfaches der hartkodierten 8 Halbschritte bewegen; man kann also 
auch nur 1/8 aller möglichen Schritte anfahren

- Du hast den Namen der Klasse klein geschrieben. Normalerweise beginnen 
Klassennamen mit einem Grossbuchstaben. Auf diese Weise kann man auch 
schneller einer Klasse an sich und den Instanzen dieser Klasse 
unterscheiden; das hätte in diesem Fall evtl. auch den ursprünglichen 
Fehler vermieden

- self.step1 wird offenbar gar nicht benutzt, auch der der 
__init__-Funktion übergebene "step"-Parameter hat somit keine Funktion

- Man kann der __init__-Funktion zwar einen "Seq"-Parameter übergeben, 
der wird in der Praxis aber komplett ignoriert, stattdessen wird 
self.Seq in der __init__-Funktion eh auf einen hartkodierten Wert 
gesetzt. Weiterhin würde ich empfehlen, diese Variable nicht "Seq", 
sondern "seq" zu nennen, weil der Name "Seq" (also mit Grossbuchstabe am 
Anfang) halt suggeriert, dass es sich dabei um eine Klasse handelt

- Eventuell wäre es sinnvoll, "import time" aus der "vorwaerts"-Funktion 
vor die Klasse zu verschieben, damit time nur ein einziges Mal 
importiert wird, statt bei jedem Aufruf der vorwaerts-Funktion.

Weil ich den Grundgedanken Deiner Schrittmotor-Klasse aber gar nicht 
schlecht fand und so etwas bislang noch nicht in meiner Sammlung hatte, 
habe ich Deine Klasse übrigens etwas angepasst folgendermassen in meine 
Sammlung aufgenommen:
1
import machine
2
import time
3
4
def value_with_default(value, default_value):
5
  return (value or default_value)
6
7
class Stepper_Motor():
8
  def __init__(self, initial_step=None, step_time=None, steps=None, enabled=None, pin_1_id=None, pin_2_id=None, pin_3_id=None, pin_4_id=None):
9
    self.pin_1 = machine.Pin(value_with_default(pin_1_id, 16), machine.Pin.OUT)
10
    self.pin_2 = machine.Pin(value_with_default(pin_2_id, 17), machine.Pin.OUT)
11
    self.pin_3 = machine.Pin(value_with_default(pin_3_id, 18), machine.Pin.OUT)
12
    self.pin_4 = machine.Pin(value_with_default(pin_4_id, 19), machine.Pin.OUT)
13
    self.set_steps(steps)
14
    self.set_step_time(step_time)
15
    self.set_enabled(enabled)
16
    self.set_step(initial_step)
17
18
  def set_steps(self, steps=None):
19
    self.steps = value_with_default(steps, [
20
      [1,0,0,0],
21
      [1,1,0,0],
22
      [0,1,0,0],
23
      [0,1,1,0],
24
      [0,0,1,0],
25
      [0,0,1,1],
26
      [0,0,0,1],
27
      [1,0,0,1],
28
    ])
29
    self.number_of_steps = len(self.steps)
30
31
  def set_step_time(self, step_time=None):
32
    self.step_time = value_with_default(step_time, 0.005)
33
34
  def set_pin_states(self, pin_1_state, pin_2_state, pin_3_state, pin_4_state):
35
    self.pin_1.value(pin_1_state)
36
    self.pin_2.value(pin_2_state)
37
    self.pin_3.value(pin_3_state)
38
    self.pin_4.value(pin_4_state)
39
40
  def set_pins(self)
41
    if self.enabled:
42
      step_pin_states = self.steps[self.step % self.number_of_steps]
43
      self.set_pin_states(step_pin_states[0], step_pin_states[1], step_pin_states[2], step_pin_states[3])
44
    else:
45
      self.set_pins(0, 0, 0, 0)
46
47
  def set_step(self, step=None):
48
    self.step = value_with_default(step, 0)
49
    self.set_pins()
50
51
  def set_enabled(self, enabled=None):
52
    self.enabled = value_with_default(enabled, True)
53
    self.set_step(self.step)
54
55
  def move(self, number_of_steps):
56
    step_delta = 1
57
    if (number_of_steps < 0):
58
      step_delta = -1
59
      number_of_steps = -number_of_steps
60
    for step in range(number_of_steps):
61
      self.set_step(self.step + step_delta)
62
      time.sleep(self.step_time)
63
64
  def forward(self, number_of_steps=None):
65
    self.move(value_with_default(number_of_steps, 1))
66
67
  def reverse(self, number_of_steps=None):
68
    self.move(-value_with_default(number_of_steps, 1))

von dux1 (Gast)


Lesenswert?

Hallo Joachim S.,

danke dir für deine Hilfe. Man sieht das du dich schon ausführlich mit 
der Thematik beschäftigt hast. Ich beschäftige mich erstmals mit Klassen 
und Objekten. Ist ne Laboraufgabe bei uns.

Joachim S. schrieb:
> Die Methode "vorwaerts" funktioniert nach einer etwas fragwürdigen
> Logik: Durch die verschachtelte Schleife kann man den Motor immer nur um
> ein Vielfaches der hartkodierten 8 Halbschritte bewegen; man kann also
> auch nur 1/8 aller möglichen Schritte anfahren

Du hast in dieser Sache recht, aber wenn ich nur ein Vektor dieser 
Sequenz ausführe bewegt sich der Motor nicht. Der brauch einmal den 
kompletten Durchlauf. Oder die Bewegung ist so minimal, dass die mit dem 
Auge nicht sichtbar ist.

Mfg dux1

von Bernd K. (prof7bit)


Lesenswert?

test schrieb:
> "import time" kann natürlich nach _init_

Blödsinn. Alle Importe kommen global oben hin, das ist so Usus, und das 
ist sinnvoll, viele erfahrene Leute haben lange und hart nachgedacht um 
die empfohlenen Gepflogenheiten zu erarbeiten.

Grundsätzlich gilt: Vieles was bei statisch getypten Sprachen vom 
Compiler erzwungen wird muss man sich bei dynamischen Sprachen per 
Konvention festlegen und sich auch sklavisch dran halten (also in dem 
Fall die offiziellen Styleguides für Python) sonst verliert man schnell 
den Überblick und driftet in ein unstrukturiertes Chaos ab und alle 
anderen (allen voran auch das zukünftge Selbst nach einigen Jahren 
Erfahrung) werden den lieblos hingeworfenen Code hassen und ihn 
aufräumen wollen.

Der erste Schritt zu mehr diesbezüglicher Disziplin ist es im Editor 
oder in der IDE den Linter einzuschalten und auf schärfste Stufe zu 
stellen und dann so lange am Code zu arbeiten bis alle Warnungen 
verschwunden sind. Manches mag überpenibel erscheinen aber alles hat 
einen tieferen Sinn den man irgendwann erkennen wird - spätestens dann 
wenn man mal Quellcode von jemand anderem in die Finger bekommt der alle 
gut gemeinten Empfehlungen in den Wind geschossen hat.

von Joachim S. (oyo)


Lesenswert?

dux1 schrieb:
> Joachim S. schrieb:
>> Die Methode "vorwaerts" funktioniert nach einer etwas fragwürdigen
>> Logik: Durch die verschachtelte Schleife kann man den Motor immer nur um
>> ein Vielfaches der hartkodierten 8 Halbschritte bewegen; man kann also
>> auch nur 1/8 aller möglichen Schritte anfahren
>
> Du hast in dieser Sache recht, aber wenn ich nur ein Vektor dieser
> Sequenz ausführe bewegt sich der Motor nicht. Der brauch einmal den
> kompletten Durchlauf. Oder die Bewegung ist so minimal, dass die mit dem
> Auge nicht sichtbar ist.

Klar, wenn Du jeden Halbschritte ansteuern kannst, statt wie in Deiner 
aktuellen Fassung immer ein Vielfaches aller acht Halbschritte, dann ist 
jeder einzelne Schritt nur noch 1/8 so gross. Wenn Dir die Schritte 
damit zu klein sind, dann übergebe der vorwaerts()-Funktion doch einfach 
einen 8-mal so grossen Wert?
Aber falls Du dennoch dabei bleiben willst, immer ein Vielfaches aller 
acht Halbschritte zu gehen, dann macht es allerdings wenig Sinn, 
überhaupt Halbschritte zu gehen - dann kannst Du auch gleich 
Vollschritte gehen, indem Du Seq einfach abänderst in:
1
Seq = [
2
  [1,1,0,0],
3
  [0,1,1,0],
4
  [0,0,1,1],
5
  [1,0,0,1],
6
]
oder
1
Seq = [
2
  [1,0,0,0],
3
  [0,1,0,0],
4
  [0,0,1,0],
5
  [0,0,0,1],
6
]
Die erste Version hat ein kräftigeres Drehmoment, benötigt aber auch 
doppelt so viel Strom.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.