Mal wieder: Stepper Bibliothek + Plotter

Mahlzeit zusammen,

es war etwas Zeit ueber und im Schrott lagen alte CD/DVD Laufwerke..
Schnell die Schrittmotoren mit den dazugehoerigen Schienen ausgebaut
und einen kleinen(winzigen) Plotter gebaut.

Dabei stieß ich auf das leidige Thema der Stepper.h Bibliothek, welche blockierend
arbeitete. (Heiß: Faehrt eine Achse bleibt der gesamte loop stehen bis das Ziel
erreicht wurde.)

Also noch auf die schnelle eine eigene Bibliothek geschrieben..

BETA des DVD Plotters:

//FastStepper 0.0.8 by grillgemuese 07.2018
#include <FastStepper.h>
/**** Pins ****/
const uint8_t X_limit = A1, Y_limit = A0, Z_limit = A0; //!!!
/**** Plotter configuration ****/
const int16_t X_CAL_DIR = 1, Y_CAL_DIR = -1, Z_CAL_DIR = 1;
const int16_t Z_DOWN = 135, Z_UP = 100;
const int16_t X_SPR = 200,  Y_SPR = 200,  Z_SPR = 200;  //steps per revolution
const int16_t X_MAX = 260,  Y_MAX = 260,  Z_MAX = 200;  //max. steps in total
const float   X_MM = 38.0,  Y_MM = 38.0;                //max. mm in total
const float   X_RPM = 34.0, Y_RPM = 33.0, Z_RPM = 32.0; //(DVD ~40),(28BYJ-48 ~14)
const float CAL_RPM = 10.0;
const uint32_t drawDuration = 50; //ms
/**** Picture config ****/
const uint8_t X_RES = 32,   Y_RES = 32; //pic pixels
const float X_SPMM = (float)X_MAX / X_MM; //steps per mm
const float Y_SPMM = (float)Y_MAX / Y_MM;
const float X_SPPX = X_SPMM / ((float)X_RES / X_MM); //steps per pixel
const float Y_SPPX = Y_SPMM / ((float)Y_RES / Y_MM);
/**** Picture ****/
const bool pic[Y_RES][X_RES] = {
// 2,  4,  6,  8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32 
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // 1
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // 2
{0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0}, // 3
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // 4
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // 5
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // 6
{0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0}, // 7
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, // 8
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, // 9
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //10
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //11
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //12
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //13
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //14
{0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0}, //15
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //16
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //17
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //18
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //19
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //20
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //21
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0}, //22
{0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0}, //23
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, //24
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, //25
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, //26
{0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0}, //27
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, //28
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, //29
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, //30
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, //31
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, //32
};
/**** Process states ****/
enum {Start, Calibrate, Search, MoveXY, DownZ, Draw, UpZ, Finished, Pause, Proceed, Empty};
//X & Z
FastStepper X_Axis(X_SPR, 2, 4, 3, 5, "X");
FastStepper Y_Axis(Y_SPR, 6, 8, 7, 9, "Y");
FastStepper Z_Axis(Z_SPR, 10, 12, 11, 13, "Z");

void setup()
{
  X_Axis.begin();       Y_Axis.begin();       Z_Axis.begin();  
  Serial.begin(19200);  while(!Serial){}
  Serial.setTimeout(250);
  Serial.println(F("Available commands:"));
  Serial.println(F("<cal> calibrate X,Y,Z"));
  Serial.println(F("<start>"));
  Serial.println(F("<pause>")); //no emergency stop!
  Serial.println(F("<resume>"));
}//void setup() END

void loop()
{
  static bool firstCall = true, calibrated;
  static uint8_t activeState = Empty;
  static uint8_t act_X, act_Y;
  static uint32_t drawStartMS, printStartMS;
  
  if (Serial.available() > 0) 
  { 
    String msg = Serial.readStringUntil('\n');
    if (msg == "cal" && activeState == Empty) activeState = Calibrate;
    else if (msg == "start" && activeState == Empty) activeState = Start;
    else if (msg == "pause" && activeState != Empty) activeState = Pause;
    else if (msg == "resume" && activeState == Pause) activeState = Proceed;
    else Serial.println(F("not available!"));
    firstCall = true; //not 100% correct
  }

  switch (activeState)
  {
    case Start:
      if (calibrated) 
      {
        act_X = 0;
        act_Y = 0;
        printStartMS = millis();
        activeState = Search;
        Serial.println(F("start print"));
      } else {
        activeState = Empty;
        Serial.println(F("not calibrated!"));
      }
    break;
    case Calibrate:
      if (firstCall) {
        X_Axis.setRPM(CAL_RPM); Y_Axis.setRPM(CAL_RPM); Z_Axis.setRPM(CAL_RPM);
        firstCall = false;
        Serial.println(F("start calibration"));
      }
      if (!digitalRead(X_limit)) X_Axis.step(X_CAL_DIR);
      if (digitalRead(Y_limit)) Y_Axis.step(Y_CAL_DIR);
      if (digitalRead(Z_limit)) Z_Axis.step(Z_CAL_DIR);
      if (digitalRead(X_limit) && !digitalRead(Y_limit) && !digitalRead(Z_limit))
      {
        X_Axis.setStep(0);    Y_Axis.setStep(0);    Z_Axis.setStep(0);
        X_Axis.setRPM(X_RPM); Y_Axis.setRPM(Y_RPM); Z_Axis.setRPM(Z_RPM);
        calibrated = true;
        activeState = Empty;
        firstCall = true;
        Serial.println(F("end  calibration"));
      }
    break;
    case Search:
      act_X++;
      if (act_X > X_RES - 1)
      {
        act_X = 0;
        act_Y++;
        if (act_Y > Y_RES - 1) 
        {
          activeState = Finished;
          firstCall = true;
          break;
        }
      }
      if (pic[act_Y][act_X]) 
      {
        activeState = MoveXY; 
        firstCall = true;
      }
    break;
    case MoveXY:
      if (firstCall) {
        X_Axis.stepTo(act_X * (uint8_t)X_SPPX);
        Y_Axis.stepTo(act_Y * (uint8_t)Y_SPPX);
        firstCall = false;
      }
      else if (X_Axis.getStepStat() && Y_Axis.getStepStat()) 
      {
        activeState = DownZ;
        firstCall = true;
        Serial.print(X_Axis.getName()); Serial.print(X_Axis.getStep());
        Serial.print(F("\t"));
        Serial.print(Y_Axis.getName()); Serial.println(Y_Axis.getStep());
      }
    break;
    case DownZ:
      if (firstCall) {
        Z_Axis.stepTo(Z_DOWN);
        firstCall = false;
      }
      else if (Z_Axis.getStepStat())
      {
        drawStartMS = millis();
        activeState = Draw;
        firstCall = true;
      }
    break;
    case Draw:
      if (millis() - drawStartMS >= drawDuration) activeState = UpZ;
    break;
    case UpZ:
      if (firstCall) {
        Z_Axis.stepTo(Z_UP);
        firstCall = false;
      }
      else if (Z_Axis.getStepStat()) 
      {
        activeState = Search;
        firstCall = true;
      }
    break;
    case Finished:
      if (firstCall) {
        X_Axis.stepTo(0); Y_Axis.stepTo(0); Z_Axis.stepTo(0);
        firstCall = false;
        act_X = 0;
        act_Y = 0;
        Serial.print(F("Print finished after: ")); Serial.print(millis() - printStartMS);
        Serial.println(F("ms"));
      }
      else if (X_Axis.getStepStat()&&Y_Axis.getStepStat()&&Z_Axis.getStepStat())
      {
        X_Axis.sleep();  Y_Axis.sleep();  Z_Axis.sleep();
        activeState = Empty;
        firstCall = true;
      }
    break;
    case Pause:
      if (firstCall) {
        Z_Axis.stepTo(Z_UP);
        X_Axis.stepTo(X_Axis.getStep());
        Y_Axis.stepTo(Y_Axis.getStep());
        firstCall = false;
        Serial.println(F("pause print"));
      }
    break;
    case Proceed:
      activeState = MoveXY;
      Serial.println(F("resume print"));
    break;
    case Empty:
    break;
  }//switch(activeState) END
  
  X_Axis.run(); //call every loopcycle!
  Y_Axis.run(); //call every loopcycle!
  Z_Axis.run(); //call every loopcycle!
}//void loop() END

Gruß
grillgemuese :slight_smile:

FastStepper.zip (5.99 KB)

Hast Du Dir mal Accelstepper angeschaut?

Gruß Tommy

Tommy56:
Hast Du Dir mal Accelstepper angeschaut?

Hatte ich mal reingeschaut, wollte aber bei diesem "Projekt" den lerneffeckt gleich mitnutzen
und etwas in richtung Stepper & OOP lernen.
Accelstepper hat auch ein paar interessante Funktionen (wie der Name bereits verraet) z.B. die Beschleunigung und das Abremsen eines Schrittmotor in einer Rampenzeit.

Diese Funktionen wollte ich in naechster Zeit auch in meine Lib. einbauen.

Gruß
grillgemuese :slight_smile:

Du weißt, daß man für schräge Linien oder Kreise die Schrittmotoren mit unterschiedlicher Geschwindigkeit laufen lassen muß?

Das sollte eigentlich kein Problem sein, wenn Du die Bresenham Algorithmen verwendest. Die sagen Dir, welcher Schrittmotor als nächster einen Schritt machen soll. Das ganze läuft also mit lauter einzelnen Schritten, nur für die Geschwindigkeit muß man sich was einfallen lassen.

Hi

Bei der Geschwindigkeit bestimmt der am schnellsten laufende Stepper, wie schnell die Anderen werden können.
Klingt auch logisch: Wenn einer der Motoren am oberen Limit läuft, KANN Er ja nicht mehr schneller - dann müssen die Anderen langsamer.

Beschleunigungen wird noch ein Spaß werden, daß Da reinzubringen, auch hier kann dann der noch nicht schnell genug fahrende Motor die Schnellen ausbremsen - bzw. muß Er Das, da Er ja 'nicht schneller kann' - zumindest laut Seiner Rampe.

Überlegungen hierzu hatte ich auch schon ...

MfG

Wenn jeder Schritt einzeln ausgeführt wird, dann ist der Abstand zum vorherigen Schritt die Geschwindigkeit, mit der der Stepper läuft. Man muß nur in zeitlichen Abständen denken und rechnen, statt in Geschwindigkeiten.

Mahlzeit zusammen,

in der Mittagspause sollte mein "Plotter" lernen XYZ Koordinaten von der Seriellen-Schnittstelle
zu lesen.

Dabei stieß ich auf folgendes Problem:

#define TAB Serial.print(F("\t"))
const uint8_t maxLength = 9; //(+/-fff.fff) + SPACE
const char AxisChar[3] = {'X', 'Y', 'Z'};

void setup()
{
  Serial.begin(19200);
  while (!Serial) {}
  Serial.setTimeout(250);
  Serial.println(F("Example: X123.456 Y-234.321 Z-10.123\n"));
}//void setup() END

void loop()
{
  static float axisData[3];
  static String recLine;
  
  if (Serial.available() > 0)
  {
    recLine = Serial.readStringUntil('\n');
    String iStr;
    iStr.reserve(maxLength);
    for (uint8_t i = 0; i < 3; i++)
    {
      uint8_t iPos = recLine.indexOf(AxisChar[i]);
      if (i < 2) iStr = recLine.substring(iPos, recLine.indexOf(AxisChar[i+1])); //-1?
      else iStr = recLine.substring(iPos);
      axisData[i] = iStr.toFloat();
      Serial.print(i);    TAB;
      Serial.print(iPos); TAB; 
      Serial.print(iStr); TAB;
      Serial.println(axisData[i]);
    }
  }
  //...
}//void loop() END

Die Methode (String).toFloat() scheint nicht zu funktionieren.
Ist auch nicht als farbig hervorgehoben.

Reference string functions tofloat
Jemand eine Loesungs parat?

UPDATE: "If no valid conversion could be performed because the String doesn’t start with a digit, a zero is returned."
Das 'X',... scheint die Methode nicht zu moegen.

Besten Dank
grillgemuese :slight_smile:

Wenn Du damit länger arbeiten willst, dann verabschiede Dich am Besten gleich am Anfang von der Klasse String und nimm Char-Arrays. (Zeichenketten in C)
Mit Strings geht Dir füher oder später der RAM aus (fragmentiert) und dann kommen unvorhergesehene Reaktionen.

Gruß Tommy

Vielen Dank fuer den Tipp.

werde bei naechster Gelegenheit eine char[] Variante baugleich zur String-Version erstellen.
"Finale" String-Version:

const uint8_t AxisN = 3;
const char AxisChar[AxisN] = {'X', 'Y', 'Z'};

void setup()
{
  Serial.begin(19200);
  while (!Serial) {}
  Serial.setTimeout(250);
  Serial.println(F("Example: X123.456 Y-234.321 Z-0.123\n"));
}//void setup() END

void loop()
{
  static float axisData[AxisN];
  //parse takes about 250-280ms!
  if (Serial.available() > 0)
  {
    uint32_t startMS = millis();
    String recLine = Serial.readStringUntil('\n');
    for (uint8_t i = 0; i < AxisN; i++)
    {
      String fStr;
      int8_t charPos = recLine.indexOf(AxisChar[i]);
      if (charPos >= 0)
      {
        if (i < AxisN - 1) fStr = recLine.substring(charPos + 1, recLine.indexOf(AxisChar[i + 1]));
        else fStr = recLine.substring(charPos + 1);
        axisData[i] = fStr.toFloat();
        Serial.println(axisData[i], 3);
      }
      else
      {
        Serial.print(AxisChar[i]); Serial.println(F(" not found!"));
      }
    }
    Serial.print(F("Duration: ")); Serial.println(millis() - startMS);
  }
  //...
}//void loop() END

Gruß
grillgemuese :slight_smile:

Mahlzeit zusammen,

die "lite" Version der Bibliothek ist soweit einsatzbereit.

Eine kleine Frage an Euch:
Haltet Ihr es fuer sinnvoll den stepMode waehrend des Betriebs zu wechseln, heißt
der Wechsel zwischen full- und halfstep im laufenden Betrieb?
Oder sollte dieser statisch festgelegt werden.

DrDiettrich:
Du weißt, daß man für schräge Linien oder Kreise die Schrittmotoren mit unterschiedlicher Geschwindigkeit laufen lassen muß?

Im Beispiel Plotter_Circle sieht man eine Alternative zur Beschleunigung/Abbremsung einer Achse
mit einer festen samplerate aber wechselnder Schrittgroesse. Dadruch ergibt sich ja eine fuers Auge
erkennbare sanfte Beschleunigung/Abbremmsung.
(Wenn man im "line" Modus einen Kreis von r=10mm mit 3600/36000 samples faehrt, ergibt das
einen "perfekten" Kreis mit sanfter Achsbewegung.)

Gruß
grillgemuese :slight_smile:

FastStepper.zip (8.57 KB)

Hallo zusammen,

derzeit befasse ich mich mit der linearen Beschleunigung und Verzögerung eines Schrittmotors.
Die Beschleunigung klappt einwandfrei, jedoch hapert es an der Verzögerung in Bezug auf ein festgelegtes Ziel.
Heißt: ich lege fest, dass der Schrittmotor insgesamt XXX Umdrehungen(Schritte) fahren soll und mit einer Beschleunigungsrate von X.X RPM/s² bis zu seiner Zieldrehzahl X.X beschleunigt.
-> Die Beschleunigung klappt "perfekt" in dem ausgerechneten Zeitfenster.
Jetzt kommt aber die Verzögerung... ich legte bereits fest, dass das Zeil bei XXX Umdrehungen liegt und ich möchte gerne punktgenau auf dieses Ziel von meinen X.X RPM herrab bremsen.
In dem Sketch weiter unten klappt das Abbremsen in dem Zeitfenster(fast) auch, jedoch komme ich bei XX Rest Umdrehungen(Schritte) aus, welche dann mit der Niedrigsten RPM(10.0) abgefahren werden.
Daraus schleicht der Motor die übergebliebenen Schritte mit 10 RPM vor sich hin...

Kann mit jemand erklären was an der Zeile mit "if (myStepper.getRPM() / decelRate >..." falsch ist?

#include <FastStepper.h>

const FS_TYPE stepsPerRev = 48; //Brother printer

const float accelRate = 50.0; //RPM/s² acceleration
const float decelRate = 100.0; //RPM/s² deceleration
const uint32_t ADRefreshMS = 100;

FastStepper myStepper(stepsPerRev, 2, 3, 4, 5);

void setup()
{
  Serial.begin(115200);
  //Serial.setTimeout(100);
  myStepper.begin();
  //myStepper.setStepMode(_HALFSTEP); //~650, _FULLSTEP ~750RPM 
  myStepper.step(stepsPerRev * 200);
}//void setup() END

void loop()
{
  static uint32_t ADCalMS;
  static float targetRPM = 600.0; //TEST
  
  //1=stop inbetween 40 Revs, 2=stop in ramptime according to decelRate, 3=setRPM by RemSteps
  //if (myStepper.getRemSteps() < 40*stepsPerRev && targetRPM != 10.0) targetRPM = 10.0;
  /*if (myStepper.getRPM() / decelRate > (float(myStepper.getRemSteps() / stepsPerRev) * 60.0 / myStepper.getRPM()) * 2.0)
  {
    targetRPM = 10.0;
  } moved in AD.. cycle*/
  //if (myStepper.getRemSteps() < 1920 && targetRPM > 10.0) myStepper.setRPM(float(map(myStepper.getRemSteps(),1440,0,650,10)));
  
  uint32_t ADactMS = millis();
  if (ADactMS - ADCalMS >= ADRefreshMS && !myStepper.getStepStat())
  {
    if (myStepper.getRPM() / decelRate > float(myStepper.getRemSteps() / stepsPerRev) * 60.0 / myStepper.getRPM()* 2.0) //why do i need to multiply by 2?
    {
      targetRPM = 10.0;
    }
    Serial.print(myStepper.getRPM());
    Serial.print(" ");
    Serial.println(myStepper.getRemSteps());

    if (targetRPM > myStepper.getRPM())
    {
      float AVal = (float(ADactMS - ADCalMS) / 1000.0) * accelRate;
      AVal += myStepper.getRPM();
      if (AVal >= targetRPM) myStepper.setRPM(targetRPM);
      else myStepper.setRPM(AVal);
    }
    else if (targetRPM < myStepper.getRPM())
    {
      float DVal = (float(ADactMS - ADCalMS) / 1000.0) * decelRate;
      DVal = myStepper.getRPM() - DVal;
      if (DVal <= targetRPM) myStepper.setRPM(targetRPM);
      else myStepper.setRPM(DVal);
    }
    ADCalMS = ADactMS;
  }
  else if (myStepper.getStepStat()) myStepper.sleep();

  myStepper.run(); //call every loopcycle!
}//void loop() END

Besten Dank
grillgemuese :slight_smile:

FastStepper_009.zip (8.59 KB)

Wieso machst Du den Verlauf nicht symmetrisch, d.h. Beschleunigung und Abbremsen haben genau die gleiche Schrittzahl? AFAIR steckt das in AccelStepper auch schon drin.

DrDiettrich:
Wieso machst Du den Verlauf nicht symmetrisch, d.h. Beschleunigung und Abbremsen haben genau die gleiche Schrittzahl? AFAIR steckt das in AccelStepper auch schon drin.

Guten Morgen,

ob man nun für die Beschleunigung & Verzögerung ein und die selbe Rate verwendet ist Geschmacks abhängig.
In AccelStepper habe ich bereits reingeschaut, fand es aber wenig übersichtlich und wollte den "Lernprozess" gleich mitnehmen. :slight_smile:

Hauptaugenmerk liegt derzeit hier:

if (myStepper.getRPM() / decelRate > float(myStepper.getRemSteps() / stepsPerRev) * 60.0 / myStepper.getRPM()* 2.0) //why do i need to multiply by 2?
{
  targetRPM = 10.0;
}

Mein Rechnungsansatz besteht darin, dass ich mit der aktuellen RPM und der Verzögerungsrate die Rampenzeit in Sekunden herausfinde. Danach vergleiche ich die Zeit, welche ich bei der aktuellen RPM für die überrigen (RemSteps) Schritten noch brauche, damit.

Bsp: 600 RPM / 100 RPM/s² = 6s Rampenzeit
RemSteps(2000): 2000 / 48 * 60s / 600RPM (* 2) = 4.16s (= 8.33s)
RemSteps(1300): 1300 / 48 * 60s / 600RPM (* 2) = 2.70s (= 5.41s)

Sobald die erwartete Verzögerungszeit unter der der Rampenzeit ist, wird die targetRPM auf die min.RPM gesetzt und die Verzögerung gestartet.

Problem in meinem Post darüber.

Besten Dank
grillgemuese

Bei der Betrachtung Deines Codes und der Beispiele beschleicht mich Irgendwie das Gefühl, daß Dein Ansatz noch physikalische und mathematische Probleme hat.