Schrittmotor ansteuern

Hallo alle miteinander,

Ich habe mir vor kurzem kleine Peristaltik-Schlauchpumpen gekauft, die über einen Schrittmotor angetrieben werden. Mein Ziel ist es die Pumpe (später dann mehrere Pumpen) in einem definiertem Programm zu betreiben. Der Zusammenhang zwischen steps pro sec und Flussrate ist zum Glück linear, so dass ich den Motor also einfach auf die Flussrate kalibrieren kann. Mein Problem ist, dass ich beim Schreiben des Codes scheinbar ziemlich ungeschickt bin. Ich kriege zwar definierte Geschwindigkeiten konstant hin und über die Anzahl der steps kann ich auch die Zeit regulieren, ich schaffe aber meine gewünschten Wechsel nicht.

Hier erst mal kurz was ich überhaupt so benutze:

-ChipKit uC32 (hatte erst einen Arduino Uno, der schafft aber nicht genug steps mit der Accelstepper library - der Code sollte ja trotzdem indentisch sein oder?)
-Pololu A4988
-die Spezifikationen zum Motor habe ich mal angehangen

Meine Vorstellung für das Programm ist, dass ich den Motor 5 min lang mit einer gewissen Geschwindigkeit laufen lasse (also zum Beispiel 5000 steps) und nach 5 min dann auf 4000 steps runter gehe, 5 min später 3000 steps usw. Anpassungen bei den steps und der Zeit sind über die Zahlenwerte dann ja variabel. Die Drehrichtung des Motors soll immer gleich bleiben.

Kann mir jemand vielleicht unter die Arme greifen?

Beste Grüße,
Philipp

Muss da vieleicht mal deine Vorstellungen korrigieren. 5000 Steps sind keine Geschwindigkeit, sondern eine "Wegstrecke".

Dein Motor ist sehr komisch mit seinen 0,06° Halfstep. Das würde heisen, das man 6000Step/U benötigt.

Aber wenns so im Datenblatt steht, glauben wir es mal.

Ohne Programm ist es schwer zu sagen, was du da genau machst oder machen willst. Solltest also deinen bisherigen Code mal reinbringen, das man sieht wo du was falsch machst

Edit: Ich glaub ich habs raus. Wenn ich ein 1:14 Getriebe dazu rechne und Halfstep, komme ich auf 1,8° Motorstepwinkel. Halfstep sind dann 0,9° und 1:14 ergibt die ominösen 0,06..
Dann solltest du vorher mal testen ob der überhaupt 5000Hertz Stepfrequenz mitmacht.

Treter:
Kann mir jemand vielleicht unter die Arme greifen?

Was Du machen möchtest, würde ich als einen „endlichen Automat“ umsetzen. Was mir dazu eingefallen ist, habe ich hier ins Netz gekippt. Vielleicht hilft das.

Gruß

Gregor

Treter:
-ChipKit uC32 (hatte erst einen Arduino Uno, der schafft aber nicht genug steps mit der Accelstepper library - der Code sollte ja trotzdem indentisch sein oder?)

"80 Mhz 32-bit MIPS" dürfte ein 32-Bit Prozessoer sein, der UNO hat einen 8-Bit.
"Compatible with many existing Arduino code examples, reference materials and other resources" ist wenig konkret. Schaut man ih die Liste der mitgelieferten Programmbibliotheken, so ist diese recht lang. Es scheint doch Unterschiede zu geben, die das notwendig machen.

Die AccelStepper ist nicht dabei, weshalb man nun spekulieren kann, ob das nicht notwendig ist, oder ob es nicht als wichtig angesehen wurde.

gregorss:
Was Du machen möchtest, würde ich als einen „endlichen Automat“ umsetzen. Was mir dazu eingefallen ist, habe ich hier ins Netz gekippt. Vielleicht hilft das.

Gruß

Gregor

Danke ich schau mir das die Tage mal in Ruhe an und berichte ob ich klar komme.

@Chefin: Mit 5000 steps meinte ich 5000 steps pro sekunde, das entspricht dann halt einer gewissen Flussrate aufgrund der daraus resultierenden RPM...

Treter:
Danke ich schau mir das die Tage mal in Ruhe an und berichte ob ich klar komme.

Wirf auf jeden Fall auch einen Blick auf die Folgeseite. Da bekommt das Ganze noch ein Sahnehäubchen.

Gruß

Gregor

Okay, ich habe mich jetzt mal versucht das Ganze auf meine Anwendung zu übertragen. Ich bin wie gesagt nicht sonderlich geschickt beim Schreiben des Codes (fairerweise muss man sagen, dass ich nur Pharamzeut bin und vom Programmieren nicht wirklich Ahnung habe ^^) und hoffe man verzeiht mir gröbere Fehler...
Das ist jetzt erst mal der Code wie ich ihn gerade "angepasst" habe:

#include <AccelStepper.h>

//AccelStepper stepper; // Defaults to AccelStepper::DRIVER (2 pins)
AccelStepper stepper1(1, 2, 3);   // pin 1 = DRIVER; pin 2  = step, pin 3  = direction
enum Phase {Kinetic1, Kinetic2, Kinetic3, Kinetic4, Kinetic5, Kinetic6, Kinetic7}; // "Namen" der Phasen
int d[]={300000, 300000, 300000, 300000, 300000, 300000, 1800000}; //Dauer der Phasen in ms
Phase phase; //Enthält die aktuelle Phase
long int millisMem; //Merker für millis()

void setup()
{  
  phase=Kinetic1;
  millisMem=millis();

}

void loop()
{  
  switch(phase)
  {
    case Kinetic1:
                     if(millis()-millisMem < d[0])
                     { setMaxSpeed(5607);
                       setAcceleration(1500);}
                     else
                     { phase=Kinetic2; }
                     break;
    case Kinetic2:
                     if(millis()-millisMem < d[0]+d[1])
                     { setMaxSpeed(2810);
                       setAcceleration(1500);}
                     else
                     { phase=Kinetic3; }
                     break;
    case Kinetic3:
                     if(millis()-millisMem < d[0]+d[1]+d[2])
                     { setMaxSpeed(1412);
                       setAcceleration(1500);}
                     else
                     { phase=Kinetic4; }
                     break;
    case Kinetic4:
                     if(millis()-millisMem < d[0]+d[1]+d[2]+d[3])
                     { setMaxSpeed(713);
                       setAcceleration(1500);}
                     else
                     { phase=Kinetic5; }
                     break;
     case Kinetic5:
                     if(millis()-millisMem < d[0]+d[1]+d[2]+d[3]+d[4])
                     { setMaxSpeed(363);
                       setAcceleration(1500);}
                     else
                     { phase=Kinetic6; }
                     break;
     case Kinetic6:
                     if(millis()-millisMem < d[0]+d[1]+d[2]+d[3]+d[4]+d[5])
                     { setMaxSpeed(363);
                       setAcceleration(1500);}
                     else
                     { phase=Kinetic7; }
                     break;
     case Kinetic7:
                     if(millis()-millisMem < d[0]+d[1]+d[2]+d[3]+d[4]+d[5]+d[6])
                     { setMaxSpeed(188);
                       setAcceleration(1500);}
                     else
                     { phase=Kinetic1; }
                     break;
                     
  }
}

Ich wollte es eigentlich so haben, dass ich mit diesen 5706 steps/sec starte, nach 5 min dann auf 2810 steps/sec runter gehe usw. Die letzte Flussrate sollte dann für 30 min laufen, danach hab ich das ganze erst mal provisorisch wieder zur ersten Flussrate geschickt, theoretisch könnte das Programm dann auch ende, spielt alos grundlegend keine Rolle. DIe Rampe hab ich mir über try and error so rausgesucht - Motor und Board kommen mit der Beschleunigung einfach gut zurecht.
Ich kriege jetzt allerdings für jeden "setMaxSpeed"- und"setAcceleration" - Befehl die Meldung, dass ich diese Befehle nicht erklärt hätte. Meine Hoffnung war, dass das schon über die Accelstepper library passieren würde.
Muss ich da im setup noch Sachen angeben oder kann ich die Befehle aus der library gar nicht ohne weiteres nutzen so wie ich mir das jetzt vorstelle?

Hier mal der Code den ich nutze wenn der Motor mit einer bestimmten Geschwindigkeit für 5 min laufen soll:

#include <AccelStepper.h>

//AccelStepper stepper; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3, 4, 5
AccelStepper stepper1(1, 2, 3);   // pin 1 = DRIVER; pin 2  = step, pin 3  = direction


void setup()
{  
   stepper1.setMaxSpeed(5607);     // 6965 steps pro sekunde(150 rpm)
   stepper1.setAcceleration(1500); // Rampe
   stepper1.moveTo(1682100);       // steps für 5 min
}

void loop()
{  
   stepper1.run();
}

Der funktioniert auch, so dass ich dachte, dass ich die Befehle einfach beibehalten könnte.

Das fehlt im ersten Sketch:

stepper1.setMaxSpeed(5607);

agmue:
Das fehlt im ersten Sketch:

stepper1.setMaxSpeed(5607);

Oh man bin ich ein Esel... ^^
Danke erst mal!

Der Sketch an sich ist erst mal okay, es kommt zumindest keine Fehlermeldung mehr. Allerdings reagiert der Motor überhaupt nicht.
Hat jemand vielleicht eine Idee?

5607Hertz entspricht beim 1,8° Motor ca 1682U/min Motorseitig. Am Getriebeausgang meiner Rechnung nach 120U/min

Ohne Rampe aus dem Stand auf diese Drehzahl unter Last (auch ohne Flüssigkeit wird da einiges an Energie benötigt) schaffen nur wenige Motoren. Und die sind meist sehr teuer. In der Regel sind Drehzahlen über 1000U/min beim Schrittmotor grenzwertig.

Brummt der Motor kurz? Wenn ja, dann Anlaufprobleme.

Ansonsten müsstest du mal die Verdrahtung zeigen, dann liegt wohl dort das Problem.

chefin:
5607Hertz entspricht beim 1,8° Motor ca 1682U/min Motorseitig. Am Getriebeausgang meiner Rechnung nach 120U/min

Ohne Rampe aus dem Stand auf diese Drehzahl unter Last (auch ohne Flüssigkeit wird da einiges an Energie benötigt) schaffen nur wenige Motoren. Und die sind meist sehr teuer. In der Regel sind Drehzahlen über 1000U/min beim Schrittmotor grenzwertig.

Brummt der Motor kurz? Wenn ja, dann Anlaufprobleme.

Ansonsten müsstest du mal die Verdrahtung zeigen, dann liegt wohl dort das Problem.

Der Motor hat laut Datenblatt 0,1286° pro step, wären bei der Taktung also etwa die von dir genannten 120 rpm.

Für die Rampe hab ich ja den Befehl setAcceleration(1500) im Sketch. Im zweiten Sketch den ich gepostet habe funktioniert das auch wunderbar. Der funktioniert auch insgesamt, so dass die Verkabelung theoretisch auch okay sein sollte. Hab trotzdem mal ein Bild angehangen.
Dieses typische Geräusch wenn er sich fest fährt weil die rampe zu steil ist oder gar nicht vorhanden ist kommt auch nicht. Man hörrt nur ein kontinuierliches schwaches Surren, Strom kommt also ganz normal an.

int d[]={300000, 300000, 300000, 300000, 300000, 300000, 1800000}; //Dauer der Phasen in ms

Ich kenne Deinen µC nicht, wie groß darf da der Typ int sein? Bei 8 Bits würde es nicht passen. Muß es möglicherweise 1800000L heißen?

long int millisMem; //Merker für millis()

Das kennt die Referenz nicht, entspricht das uint32_t oder unsigned long?

Hallo Philipp,
wenn ich mir deine beiden Sketches anschaue, dann fehlen im 1. Sketch doch einige Befehle. Du setzt zwar die Speed und die Rampe, sagst dem Motor aber gar nicht, dass er loslaufen soll. Im 2. Sketch ist das vorhanden:

  stepper1.moveTo(1682100);       // steps für 5 min

Ausserdem fehlt das Aufrufen der 'run' Methode im loop, ohne den die Accel Stepperlib meines Wissens gar nicht funktioniert.

Du musst speed und Rampe im loop auch nicht dauernd setzen ( so wie es jetzt ist, wird das bei jedem Durchlauf aufgerufen).
Das Anfangstempo setzt Du im setup() ( Wie im 2. Sketch ). Die jeweils nächsten Werte setzt Du dann immer nur, wenn die Statemachine den Zustand wechselt ( also da, wo du auch die Variable 'phase' setzt).
Franz-Peter

agmue:

int d[]={300000, 300000, 300000, 300000, 300000, 300000, 1800000}; //Dauer der Phasen in ms

Ich kenne Deinen µC nicht, wie groß darf da der Typ int sein? Bei 8 Bits würde es nicht passen. Muß es möglicherweise 1800000L heißen?

Der µC ist mit 80 MHz und 32 bit angegeben. Sollte ich sonst einfach lieber long als Typ nehmen? Das sollte dann ja problemlos reichen... starten tut der Motor dann trotzdem erst mal nicht, was ja aber nicht heißt, dass hier eine Anpassung nicht besser bzw. nötig wäre.

agmue:

long int millisMem; //Merker für millis()

Das kennt die Referenz nicht, entspricht das uint32_t oder unsigned long?

Das hab ich von hier: Weekender – Ein Automat ohne delay()
einfach übernommen. Sind uint32_t und unsigned long bei den Zahlenwerten nciht mehr oder weniger das Gleiche?

MicroBahner:
Hallo Philipp,
wenn ich mir deine beiden Sketches anschaue, dann fehlen im 1. Sketch doch einige Befehle. Du setzt zwar die Speed und die Rampe, sagst dem Motor aber gar nicht, dass er loslaufen soll. Im 2. Sketch ist das vorhanden:

  stepper1.moveTo(1682100);       // steps für 5 min

Ausserdem fehlt das Aufrufen der 'run' Methode im loop, ohne den die Accel Stepperlib meines Wissens gar nicht funktioniert.

Du musst speed und Rampe im loop auch nicht dauernd setzen ( so wie es jetzt ist, wird das bei jedem Durchlauf aufgerufen).
Das Anfangstempo setzt Du im setup() ( Wie im 2. Sketch ). Die jeweils nächsten Werte setzt Du dann immer nur, wenn die Statemachine den Zustand wechselt ( also da, wo du auch die Variable 'phase' setzt).
Franz-Peter

Okay, dachte die millis geben dann als Timer vor wie lange der Motor mit welcher Geschwindigkeit laufen soll. Werde es so mal probieren.

Edit: okay grundlegend geht es jetzt erst mal, muss nur noch einmal eine Feinabstimmung bezüglich der millis und der gesamten steps finden, weil ich zwischen den Übergängen minimale Pausen habe.
Einziges und zugleich nicht unwesentliches Problem ist, das sich jetzt bei jedem Phasenwechsel auch die Drehrichtung des Motors ändert.

Sollte ich sonst einfach lieber long als Typ nehmen?

Sind uint32_t und unsigned long bei den Zahlenwerten nciht mehr oder weniger das Gleiche?

Nicht unbedingt. "Mehr oder weniger" hast du schon meist recht, aber: Das einzige, was ein c-Compiler beachten muss ist, dass long nicht kleiner als int ist (und short nicht größer als int) :wink:
Ich würde mir generell genaue Gedanken über Datentypen machen, und im Zweifelsfall wederintnochlongnehmen sondern exakte Größen wie uint32_t.

Der Vorteil, dass int so schnell zu tippen ist, hat schon manche Probleme verursacht. :wink:

Ein Vergleich der Prozessoren bezüglich der größten zu speichernden positiven Zahl:

8-Bit Prozessor (UNO)
unsigned int:      65535
unsigned long int: 4294967295
unsigned long:     4294967295
short:             32767
uint32_t:          4294967295

32-Bit Prozessor (Teensy 3.2)
unsigned int:      4294967295
unsigned long int: 4294967295
unsigned long:     4294967295
short:             32767
uint32_t:          4294967295

Will man hinsichtlich der Bitzahl sicher gehen und portabel bleiben, bietet sich die Schreibweise uint32_t an. Siehe Integer (Datentyp)

gregorss:
Was Du machen möchtest, würde ich als einen „endlichen Automat“ umsetzen. Was mir dazu eingefallen ist, habe ich hier ins Netz gekippt. Vielleicht hilft das.

Ich hoffe, Kritik ist erlaubt?

Die Verwendung von long int finde ich irritierend, unsigned long oder uint32_t fände ich besser.

Das Aufsummieren d[0]+d[1]+d[2]+d[3] funktioniert, würde ich aber durch Wiederholung von millisMem=millis(); beim Weiterschalten zur nächsten Phase ersetzen.

Was hältst Du davon? Wenn Du ein eigenes Thema hast, dann gerne auch dort :slight_smile:

agmue:
Ich hoffe, Kritik ist erlaubt?
...
Was hältst Du davon?

Dass der Code dort nicht der Beste ist, ist klar. Als ich das geschrieben habe, fand ich es so halt für am besten Nachvollziehbar (für einen Anfänger). Deshalb weise ich üblicherweise auch nicht nur auf diese Seite sondern auch auf die Folgeseite hin.

Und ob man nun long int nimmt oder etwas Anderes, hängt auch vom Geschmack ab. uint32_t finde ich ungünstiger, weil man das nur versteht, wenn man diese Typen und stdint.h bereits kennt. Ich habe diese Typen erst lange nach den diversen ints kennengelernt.

Gruß

Gregor

Die Folgeseite hatte ich nicht genügend gewürdigt, sorry.

Erst mal möchte ich mich soweit hier bei allen für die Hilfe bedanken! Im Groben läuft das Ganze wie gesagt jetzt auch schon.

Hier nochmal die momentane Version des Sketches:

#include <AccelStepper.h>

//AccelStepper stepper; // Defaults to AccelStepper::DRIVER (2 pins)
AccelStepper stepper1(1, 2, 3);   // pin 1 = DRIVER; pin 2  = step, pin 3  = direction
enum Phase {Kinetic1, Kinetic2, Kinetic3, Kinetic4, Kinetic5, Kinetic6, Kinetic7}; // "Namen" der Phasen
uint32_t d[]={300000, 300000, 300000, 300000, 300000, 300000, 1800000}; //Dauer der Phasen in ms
Phase phase; //Enthaelt die aktuelle Phase
uint32_t millisMem; //Merker für millis()

void setup()
{  
   phase=Kinetic1;
  millisMem=millis();
}

void loop()
{  
  switch(phase)
  {
    case Kinetic1:
                     if(millis()-millisMem < d[0])
                     { stepper1.setMaxSpeed(5607);
                       stepper1.setAcceleration(1500);
                       stepper1.moveTo(1682100);}
                     else
                     { phase=Kinetic2; }
                     break;
    case Kinetic2:
                     if(millis()-millisMem < d[0]+d[1])
                     { stepper1.setMaxSpeed(2810);
                       stepper1.setAcceleration(1500);
                       stepper1.moveTo(843000);}
                     else
                     { phase=Kinetic3; }
                     break;
    case Kinetic3:
                     if(millis()-millisMem < d[0]+d[1]+d[2])
                     { stepper1.setMaxSpeed(1412);
                       stepper1.setAcceleration(1500);
                       stepper1.moveTo(423600);}
                     else
                     { phase=Kinetic4; }
                     break;
    case Kinetic4:
                     if(millis()-millisMem < d[0]+d[1]+d[2]+d[3])
                     { stepper1.setMaxSpeed(713);
                       stepper1.setAcceleration(1500);
                     stepper1.moveTo(213900);}
                     else
                     { phase=Kinetic5; }
                     break;
     case Kinetic5:
                     if(millis()-millisMem < d[0]+d[1]+d[2]+d[3]+d[4])
                     { stepper1.setMaxSpeed(363);
                       stepper1.setAcceleration(500);
                       stepper1.moveTo(108900);}
                     else
                     { phase=Kinetic6; }
                     break;
     case Kinetic6:
                     if(millis()-millisMem < d[0]+d[1]+d[2]+d[3]+d[4]+d[5])
                     { stepper1.setMaxSpeed(188);
                       stepper1.setAcceleration(100);
                       stepper1.moveTo(56400);}
                     else
                     { phase=Kinetic7; }
                     break;
     case Kinetic7:
                     if(millis()-millisMem < d[0]+d[1]+d[2]+d[3]+d[4]+d[5]+d[6])
                     { stepper1.setMaxSpeed(472);
                       stepper1.setAcceleration(250);
                       stepper1.moveTo(141600);}
                     else
                     { phase=Kinetic1; }
                     break;
                      }
                     stepper1.run();
                   }

Den Hinweisen zum trotz hab ich die Abrufe von Geschwindigkeit und Beschleunigung erst mal so stehen lassen, weil ich es besser nachvollziehen kann in der momentanen Fassung und weil ich das Gefühl habe, dass es den Leuten hier bei mir im Labor so auch einfacher zu erklären ist. Verfeinern kann man es dann ja immer noch.

Was mich jetzt noch irritiert ist, dass sich beim Wechsel von Phase 1 zu Phase 2 die Drehrichtung des Motors ändert. Beim Wechsel von Phase 6 zu Phase 7 scheint das auch so zu sein, ist schwer zu beurteilen, weil der Motor sich da leider gerade immer festfährt. Vielleicht wird das mit der Addition der millis da dann doch zu viel und ich muss das eleganter lösen...
Wie dem auch sei, so richtig kann ich gerade nicht nachvollziehen warum sich die Drehrichtung beim ersten Mal ändert und bei den ganzen folgenden Schritten dann erst mal nicht.