Schrittmotor S - Kurve

Hallo zusammen,

ich benötige "etwas" Hilfe bei der Verwirklichung der "S - Kurve" für meinen Schrittmotor.

Technische Informationen:

  • Nema 17 Stepper
  • Arduino Mega2560

Er zieht einen Schlitten auf ca. 70 cm -> bei 16tel Step 84500 Impulse, soweit so gut.

Da das Geruckel dem Material nicht gut tut sowie bescheiden aussieht, habe ich eine lineare Anfahr-und Abbremsrampe integriert, die im Prinzip die Strecke in 1000 Teile teilt:

50 Anfahren
50 Abbremsen
900 Höchstgewschwindigkeit.

Vereinfacht gesagt, ich rechne die Differenz zwischen Höchst und Anfahrsgeschwindigkeit und alle X Schritte wird die Pause verringert bis die max. Geschwindigkeit erreicht wurde!

Resultat eine Art Treppe bis zur Höchstgeschwindigkeit und genauso wieder abwährts -> ein Trapez. Soweit nichts neues nur nerft mich noch der kleine Ruckler beim Übergang, da ich ein Glas Wasser transportiere und es nicht unnötig schwappen soll!

Jetzt habe ich von S-Rampen gelesen, Beispiel:

Sieht echt Hammer aus, jedoch bringt googeln nicht die gewünschten Ergebnisse. Zwar gibt es Code Snippets jedoch mit Probleme laut den Kommentaren sowie für mich nicht durchschaubar wie realisiert.

Ich habe immer die gleiche Strecken (1/2 Strecke und volle Strecke) und würde es gerne ohne Timer lösen!

Hat jemand Erfahrung wie das mit einem Arduino realisiert werden kann - könnte es auch in C# reinklopfen und die Werte in ein Array packen, muss nicht zwingend on the fly im Arduino berechnet werden.

Weitere Idee:
Könnte man sich eine schlampige S - Kurve mit 3 Linearfunktionen nachahmen? also recht flach, dann steil und wieder flach oder verschiebt sich dann das Problem nur? :confused:

Hallo,

sind 1/16 Steps wirklich notwendig?

Bisher hast du Ansteuer- und Pausenzeiten linear berechnet. Jetzt mußt du diese an Hand einer Sinusfunktion berechnen. Diese würde ich vorab berechnen und in ein Array legen wenn es zeitkritisch ist. Probiere das erstmal in Excel o.ä. mit Diagramm.

Richtig, wie Doc_Arduino sagt.
Exponentielles Beschleunigen mittels Umrechnung einer 1/4-Sinuskurve in Startphase und beim abbremsen dito.
Das sieht sehr geschmeidig aus !

Ich hatte mal so was vor ca. 2 Jahren fertig - leider Plattencrash ohne (aktuelle) Sicherung.
Suche ich mal - drück mir & dir die Daumen.

Ein NEMA17 macht i.d.R. 200 Steps/U -> da kannste den 16/tel Step wirklich abhaken,
wenn die Soft eben "soft" anzieht und auch bremst.
Wie "Rudolf" halt nach dem wecken usw. :slight_smile:

Hast Du schon mal die AccelStepper Bibliothek ausprobiert?

Wie wäre es mit der Tabelle:

// table of 256 sine values / one sine period / stored in flash memory
PROGMEM  prog_uchar sine256[]  = 
{   0,  0,  0,  0,  1,  1,  1,  2,  2,  3,  4,  5,  5,  6,  7,  9,
   10, 11, 12, 14, 15, 16, 18, 20, 21, 23, 25, 27, 29 ,31, 33, 35,
   37, 39, 42, 44, 46, 49, 51, 54, 56, 59, 62, 64, 67, 70, 73, 76,
   78, 81, 84, 87, 90, 93, 96, 99,102,105,108,111,115,118,121,124,

  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,
  176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,
  217,219,221,223,225,227,229,231,233,234,236,238,239,240,242,243,
  244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,

  254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,
  244,243,242,240,239,238,236,234,233,231,229,227,225,223,221,219,
  217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,
  176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,

  127,124,121,118,115,111,108,105,102, 99, 96, 93, 90, 87, 84, 81,
   78, 76, 73, 70, 67, 64, 62, 59, 56, 54, 51, 49, 46, 44, 42, 39,
   37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 16, 15, 14, 12, 11,
   10,  9,  7,  6,  5,  5,  4,  3,  2,  2,  1,  1,  1,  0,  0,  0
};

Wenn ich das so richtig erahne, machst du geamt 1000 Steps für die volle Strecke.
50 zum anfahren, 900 für "VollGas" dazwischen" und wieder 50 zum bremsen.

Sollte sich passend umrechnen lassen ....

Beim "Anfahren" die Tabelle von 0-127 zählen und Werte multiplizieren und "normalisieren".
Dito beim "Abbremsen" das gleiche von 128-255 ebenso.

... plus ein paar eigene Gedanken dazu.

Hallo,

soweit ich die Accel Lib überblicke, muss man ihr dennoch die gewünschten Werte übergeben, die sie dann verarbeteiten soll?

Ich zeige dir nochmal was ich, und bestimmt Terwi, meint.

Du berechnest dir deine Werte mit der Sinusfunktion, eben so fein abgestuft wie du es benötigst.
Ich habe es mal in 5° Schritten gemacht.

Die Werte in Spalte B mit 100 multipliziert ergeben Spalte C. Damit kannste was anfangen, weil Ganzzahlen. Aber sicherlich noch nicht direkt. Sonst hättest du ja steigende Pausenzeiten statt fallende. Du benötigst immer nur Werte eines 1/4 Kreises. Also rechnest du zum Bsp. immer 100-x (x sollen die Werte aus Spalte C sein) Damit haste kürzer werdende Pausenzeiten in Sinusform. Was du dann mit den Zahlen machst ist deine Sache. In die Größenordnung bringen die du benötigst oder was auch immer.

Falls du das mit Excel vorberechnest, dann laut die Formel =SIN(Winkel*PI()/180) ,weil Excel intern im Bogenmaß rechnet, sonst kommt Müll raus.

Mach was daraus ... :wink:

Hallo,

nochmal anders dargestellt, wenn du die Einzelwerte der 1/4 Kreisfunktion in der Reihenfolge richtig kombinierst, erhälste dann den gewünschten S förmigen Verlauf. Terwi hat das in seinem Array auf analogWrite (0...255) schon umgemünzt. Meine Berechnungen beziehen sich auf den allgemeinen Einheitskreis.

Ein Sinus ist physikalisch unpassend, da gibt es bessere Funktionen. Die AccelStepper Bibliothek arbeitet mit festen (vorgegebenen) Werten für Beschleunigung und maximale Geschwindigkeit, und berechnet den Rest selbständig. Ich wüßte nicht, was man da noch besser machen könnte.

Hallo,

ich weiß nicht wie die AccelStepper Bibliothek intern arbeitet. Man kann nichts auswählen. Man müßte sich den Code anschauen. Wenn man davon ausgeht dass sie für Schrittmotoren optimiert ist, dann wird sicherlich keine lineare Beschleunigung enthalten sein. :slight_smile:

Zur Erklärung abseits von Schrittmotoren war es sicherlich nicht schlecht. :wink: Für mehr Stauchung oder Streckung könnte ich mir vorstellen das dabei mit Parabelfunktionen gearbeitet wird.

Edit:
wenn man sich das näher anschaut, steckt unter der Oberfläche richtig was dahinter.
Die Links in der Headerdatei sollte man sich anschauen, sehr interessant.
Damit man die richtige Lib findet, hier der Link. AccelStepper: AccelStepper library for Arduino

Erstmal vielen, vielenDank für die ganzen Antworten und ja mit AccelStepper auch schon angeschaut, aber dann doch der Ehrgeiz bzw. den Lerneffekt mir sowas selber zu basteln.

Aktuell leider gerade Haare raufen und missverstehen - gebe es zu Mathe war in der 11. und 12. nicht meins :confused: :confused: .

Ich benötige am Ende der Excel die einmal die 84500 Gesamtinpulse/1000*50 = 4225 Pausenzeiten sowie 2112 Pausenzeiten für Hälfte der Strecke wie im Array wie von TERWI so das ich diese in einer Schleife für den Motor abarbeiten kann.

Ich habe jetzt mal die Sinus von euren Beispielen nachgebaut von 270 Grad also 3/7 PI und muss ja wieder zu 1/2 PI also 180 Grad wenn man das so sagen kann.

Vielleicht könnt Ihr mir so weiterhelfen, wenn ich es pragmatischer mache, was ich nicht verstehe:

Gesamte Impulse für die Strecke sind in meinem Fall: 84500.

Die Teilung mit 50 | 900 | 50 habe ich für die lineare Rampe mal frei gewählt, hat sich als praktisch erwießen.

  • Wie definiere ich nun in der Excel meine Anfangs- und Zielpausenzeit?

  • Was muss ich rechnen das ich die Pausenzeiten kriege?

Tut mir echt leid wenn ich das nicht sofort verstehe :sob:

Was mir einleuchtet das die Werte in der Mitte gespalten sind, jedoch ergeben die reinen Excel - Grafiken von der Beschriftung der Achsen keinen Sinn (für mich), zur Verdeutlichung hingegen klar.

Hallo,

naja, mal abgesehen davon das Sinus, Cosinus und Tangens vor der 10. behandelt wurden, mußte dir das nur weiter aufsplitten.
Angenommen du rechnet mit Winkelwerten in 1° Schritten, dann hast 180 Werte (270° ... 360° ... 90°). Nimmste 0,5° Schritte erhälste 360 Werte, nimmste 0,1° Schritte erhälste 1800 Werte. Die Schrittweite änderst du solange bis du 84500 Werte hast. Die Schrittweite kannste dir auch ausrechnen. 180° / 84500 = 0,00213° Schrittweite. Du müßtest dann 84500 Zeilen in Excel haben. Excel dient natürlich nur zur besseren optischen Vorstellung und ggf. Vorabberechnung.

Die Beschriftungen der Achsen basieren auf dem Einheitskreis. Der Sinus von 30° sind 0,5. Kann man oben ablesen. Man erhält nur Werte zwischen 0 und 1. Deswegen Einheitskreis. Ich denke du solltest dich näher mit den Kreisfunktionen beschäftigen.

Die Werte 0...1 vom Einheitskreis entsprechen schon den Pausenzeiten. Nur noch nicht in der Größenordnung und Reihenfolge die du möchtest. Also mußte die Werte nochmal behandeln, jedoch alle gleich. Möchtest du angenommen eine Anfangspausenzeit von 8000ms haben, dann multipilzierst du die Einheits "1" mit 8000 und den Rest auch, damit der Sinus erhalten bleibt.

Haste es denn mit der AccelStepper Bibliothek schon einmal praktisch probiert ob es funktioniert?

Ich hatte mir vor längerer Zeit mal eine S-kurvenfunktion für ein Servo gebastelt.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

}
int in = 100;
double out, Winkel_Ist;

void loop() {
    static double last_out;
    ServoWegstrecke_sanft(in, out);
    Winkel_Ist = out;
    if (abs(last_out - out) > 0.01) {  // Ausgabe der Werte nur wenn sich out geändert hat.
      Serial.print(millis()/1000.0); 
      Serial.print("\t"); Serial.print(in); 
      Serial.print("\t"); Serial.println(out);
    }
    last_out = out;
  
  static unsigned long ZS2;
  if (millis() - ZS2 > 4000) {   // Zum Test regelmäßig den Eingangswert ändern
    ZS2 = millis();
    if (in == 0) in = 100;
    else in = 0;
  }

}


/*************************************************************************************************
**  ServoWegstrecke_sanft                  
**************************************************************************************************
** Wandelt Sprünge in der Servowinkel-vorgabe in eine Wegkurve für den Servo mit    
** sanftem Anlauf und Bremsweg um.                
** 
**                          
**                            
**  Input: Servonummer, Sollwinkel des Servos,            
**  Output: Sollwinkel des Servos mit weicher Annäherung an engültigen Sollwert    
**  genutzte Globale Variablen:                
**************************************************************************************************/
void ServoWegstrecke_sanft(int _in, double &out) {
  const int _Schritte = 100;  // 
  const unsigned long Zeitscheibe = 10; //ms   _Schritte * Zeitscheibe = ca. VerfahrZeit
  static int Schritte;   // über Verstellweg angepasste Schrittzahl (mehr bei großen Winkeln)
  static int last_in, last_inS;  // Letzer Übergebener Winkel
  static int Schritt;  // Laufvariable für Wegberechnung
  static double last_W;  // letzter WegWinkel (ohne Offset)
  static int dir;    // Drehrichtung
  static double inkr_S;  // Inkrement pro Wegschritt (wird gewichtet mit Schritt)
  int in = _in;
  static unsigned long TS;
  static boolean init = true;


  if (millis() < (50))  return; // 50ms Einschwingzeit
  if ((millis() - TS) < (Zeitscheibe)) return;
  TS = millis();

  if (init) {   // Beim ersten Aufruf Werte Initialisieren
    last_in = Winkel_Ist;
    last_inS = last_in;
    init = false;
  }



  int Weg = in - last_in;
  if (abs(Weg) > 20) {  // ein neuer Winkel wurde übergeben. Parameter für Wegstrecke berechnen
    Schritte = _Schritte + (abs(Weg) / 512.0 * _Schritte); // für vollen Verfahrweg doppelte Zeit.
    double inkr = float(in - last_in) / float(Schritte) * 2.0; // max inkrement pro Schritt.c
    inkr_S = inkr / Schritte * 2;      // inkrement für Anlauf
    if (Weg > 0) dir = 1;  // Vorzeichen von Weg, also Drehrichtung
    else dir = -1;
    Schritt = 1; // Schrittzähler Rücksetzen.
    last_W = 0;
    if (Schritt >=  Schritte)last_inS = last_in;
    else last_inS = out;
    last_in = in;
  }
  if (Schritt < Schritte) { // nächsten Punkt der Wegkurve berechnen
    double dW = min(abs(inkr_S * Schritt), abs((Schritte - Schritt) * inkr_S)) * dir;
    double W = dW + last_W;
    last_W = W;
    out = W + last_inS;
    Schritt++;
  } else { // kleine Änderungen werden direkt durchgereicht
  last_in = in;
  out = in;
}
}  // End of ServoWegstrecke_sanft

Die Funktion ServoWegstrecke_sanft() macht einem Sollwertsprung einen sanfte S-Kurve.
Ist schon länger her, die Doku könnte auch besser sein, aber es funktioniert.
Vielleicht kannst du den Ansatz ja brauchen.

ich weiß nicht wie die AccelStepper Bibliothek intern arbeitet. Man kann nichts auswählen.

Kann man aber (wie Doc_Arduino im Edit schon festgestellt hat); man kann die Beschleunigung und Entschleunigung per Befehl individuell einstellen.
Ich arbeite in einem Projekt seit über einem Jahr mit der AccelStepper Bibliothek und habe nur positive Erfahrungen damit gemacht.

Man lernt zwar beim Selberprogrammieren sicher sehr viel mehr - aber das kann man ggf. auch etwas bequemer, indem man die Innereien der AccelStepper Bibliothek studiert; da steckt viel Erfahrung und KnowHow drin.

Der einzige Negativpunkt: die Bibliothek verträgt nur Impulse bis max. 4000/sec. Das wird aber auch passieren, wenn man die S-Kurve nachprogrammiert hat und dem Arduino mit 16 MHz diese Rechenaufgabe zwischen den Pulsen gibt.

Ich hab mal so was ähnliche gemacht: Eine bewegliche Zielscheibe. Das fing sehr einfach an und war zum Schluss recht komplex.
Leider ist der Code "entschwunden" - ich versuch mal aus dem Gedächtnis das Prinzip noch mal:

Prob 1 war:
Wenn man nicht grade einen speziellen Stepper wählt, sind die einfachen (mit 200 Steps/u) bei 4000 Hz -> 200 Umdrehungen gerne am Limit. Dann nimmt die Leistung ab.
Kleine Stepper "drehen" gerne mal über" wenn mit Last auf Vollgas gestartet wird - der vergisst dann einfach div Steps. Dito beim bremsen von 100 auf 0 ....

Prob 2:
Wenn man eine bekannte Strecke X hat und die Übersetzung des Antriebs kennt, kann man max. Steps ja berechnen. Hier 84500.
Desweiteren kann/wird man ja festlegen, in welchem Streckenbereichen angefahren und auch wieder abgebremmst werden soll. Sagen wir mal hier 5-10% jeweils - das wären dann je 4225-8450 Steps von Speed 0 auf max. und umgekehrt.

Wenn man das mit Steps zählen machen will (und das geht recht gut), braucht man allerdings wenigstens EINEN definierten Null-Punkt.
Das kann man mit am besten mit einer Lichtschranke am Start - und besser noch eine am Ende - bewerkstelligen, welche die Zähler jeweils wieder auf 0 bzw. max. setzen.
.... falls sich der Motor verschluckt.

Die Zeit (für die Strecke), in der der Schlitten auf gewünschten max. Speed kommt - also die Beschleunigung - muss man schon selbst festlegen: Sagen wir mal 2 Sekunden.
Nehmen wir nun den Sinus mit Winkel von 270° bis wieder 90° (360°, geteilt durch 2 -> 0 bis 1/100%).
Wir berechnen mal den Sinus mal in 2° Schritten. Das ist genau genug. 5° tun's auch
Aus den daraus resultierenden 180 Schritten machen wir eine For-Schleife und warten nach jedem Durchlauf 2000/180 = ~ 11ms. (2000/72=~ 28ms)
Den jeweiligen Float-Wert pro Schritt (2° - 0,0001 - 1.0000) multiplizieren wir nun mit der max. Drehzahl des Steppers und setzen pro Durchlauf den SPEED neu.
..... damit beschleunigt der Schlitten.

"Wo isser denn nun ?"
Die Anzahl der Schritte ist ja nun nicht linear und nimmt per Funktion ungleichmäßig zu.
Ich hab mir damals lange nen Kopf gemacht, wie man möglichst exakt die Schritte zählen / die Pos. ermitteln kann, damit "ds Objekt" nicht an einen mechanischen Anschlag knallt .....
Mathematisch mitzählen taugt nix aus Erfahrung - zu ungenau, wenn auch millis() berechnet und di CPU-Zeit nicht berücksichtigt.

Version 1)
Man setze eine "Tachoscheibe mit Lichtschranke" auf die Motorachse oder nimmt einen Motor, der so was schon hat. Kostet allerdings je nach dem ein kleines Vermögen und ist wieder extra Bastelei.
Vorteil: Auch wenn der Stepper mal durchrutschen sollte, hat mal immer die exakt gefahrenen Steps/Weg.

Verion 2)
Man verbinde den "STEPS"-Eingang der Steuerplatine mit einen Hardware-Interrupt-Eingang und zähle einfach in der ISR die Steps mit.
Eine kurze Abfrage auf "ist gleich max. erreicht ?" kann einen EMERGENCY-Stop auslösen.
Vorteil: Sehr einfach und ausreichend genau.
Nachteil: "Überdreher" wegen zu großer Last / Fliehkraft werden nicht registriert (deswegen Lichtschranken zur Sicherheit)

Will man nun statt Zeit auf einen definierten Anfahrtsweg hinarbeiten, muss man testweise die Zeitspanne des Anfahr-/Bremsweges und / oder auch max. Speed ändern.
Das muss man dann allerdings empirisch ermitteln - mit ein wenig mehr Muße und Hirnschmalz lässt sich das auch sicher per Formel bestimmen .....

Wenn ich mal ein bischen Zeit habe dieser Tage, bastel ich den Code noch mal neu ...... fällt mir grad ein, das ich den noch mal wieder gut brauchen könnte.

Nachtrag:

Hab damals auch mit div. Stepper-Libs rumprobiert. Es gab (?) gute, beesere, schlechte, bzw. "überfrachtete". Und reichich Funktionen, die man nicht brauchte / nicht so prall funktionierten ...

Ich hatte derzeit hier so ein fertiges Modul aus der Bucht mit Entsprechenden Eingängen für STEP, DIR, EN, ??? usw. Da brauchte es eigentlich nur ein Taktsignal.
Weil Speicher knapp war (das Proggy/der NANO hatte noch div. anderes zu tun), hab ich selbst einen 16-Bit-Timer mit direkter Port-Beschreibung (FastWrite) "verplichtet".
Schlanker Code & schnell !

.... davon hab ich glaub ich noch was ....

Hallo,

die Erklärungen greifen doch ganz gut ineinander, finde ich.

@ Gunther:
du berechnest damit keine Pausenzeiten sondern die Pulsweiten für den Servo damit dieser sich sanft Stück für Stück bewegt?

Ok beim 3. mal lesen und Nacht drüber schlafen hat es dann doch gefruchtet.

Danke Doc_Arduino für dein Beispiel

Also in meinem Fall dann 180° / 4425= 0,040677966° Schrittweite und die Werte dahin gehend abändern das das Ergebnis passt.

Sofern alles klappt lade ich es gerne mal auf YouTube hoch so Vergleich zwischen keine Rampe, meine Trapezrampe und die selbgeschnitze S - Rampe.

Ich werde es die Woche mal testen und berichten.

So far - vielen Dank schonmal!

Hallo,

ich habe nicht alleine geholfen. :slight_smile:
Die Unterhaltung wird sicherlich noch länger und interessant.
Wünsche schon einmal maximale Erfolge, auch wenn man sicherlich erstmal einen Knoten im Hirn bekommen hat.

Doc_Arduino:
@ Gunther:
du berechnest damit keine Pausenzeiten sondern die Pulsweiten für den Servo damit dieser sich sanft Stück für Stück bewegt?

Richtig. Aber das Verfahren ließe sich ja transferieren. Es geht ja darum, aus einem Sollwertsprung eine S-förmige Annäherungskurve zu erzeugen.

Ich wollte ja nur mal einen alternativen Lösungsansatz in die Runde werfen. :slight_smile:

Hallo,

ist ja richtig, mach ruhig, deine Lösungen oder Ideen sind immer interessant :slight_smile: passt auch zum Thema.
Mit meiner Nachfrage wollte ich nur sicherstellen das ich es halbwegs verstanden habe was du "verbrochen" hast. :wink: