Uniformly accelerated rectilinear motion with non-zero initial velocity

99 views
Skip to first unread message

veronica

unread,
May 7, 2025, 9:37:07 AMMay 7
to accelstepper
Hello everyone. I've been struggling with a project for several weeks now, and I need help because I've tried everything and haven't reached my final goal. The problem is this: I'm trying to move a cell phone in a rectilinear motion, starting from a non-zero speed and stopping the system abruptly when it reaches a desired distance.

I'm working with a Nema17 stepper motor controlled by a DRV8825 driver and an Arduino UNO. The motor is mounted on a rail, moving a toothed belt. The pinouts and step/linear distance conversion are as follows:

---------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------
// Pin Definitions
//-------------------------------------------------
int stp = 7;   // STEP
int dir = 6;   // DIR
int M2 = 9;
int M1 = 10;
int M0 = 11;
int enb = 13;  // ENABLE

// Microstepping conversion:
// For example, if the motor makes 200 full steps per revolution,
// one revolution moves 40 mm, and we're using a microstepping multiplier (mp)
float mp = 32.0;  
const float PASOS_POR_MM = 200.0 * mp / 40.0;

void setup() {
  Serial.begin(9600);
  while (!Serial);

  // Configure DRV8825 microstepping pins
  pinMode(M0, OUTPUT);
  pinMode(M1, OUTPUT);
  pinMode(M2, OUTPUT);
  pinMode(enb, OUTPUT);
 
  digitalWrite(M0, HIGH);
  digitalWrite(M1, HIGH);
  digitalWrite(M2, HIGH);
 
  // Enable the motor driver (active LOW)
  digitalWrite(enb, LOW);
 
  // Display instructions via Serial
  Serial.println("System ready. Enter Serial commands:");
  Serial.println("v <initial speed> : Set initial speed (mm/s)");
  Serial.println("l <limit speed> : Set limit speed (mm/s)");
  Serial.println("x <acceleration> : Set acceleration (mm/s^2)");
  Serial.println("c <distance> : Set maximum distance (mm)");
  Serial.println("a : Start movement to the left");
  Serial.println("b : Start movement to the right");
}
---------------------------------------------------------------------------------------------------------------------------------------------


I need a code that allows me to change the acceleration in a rectilinear motion at three critical points:
- First, I need the acceleration to be very large for a very short period of time, so that the system quickly reaches the initial velocity set by the "v" command.
- Then, I need the acceleration to instantly change to the one I set using the "x" command, and the system to continue moving at that acceleration until either the limiting velocity (set by the "l" command) is reached (in which case, the acceleration will become 0 and the system will move at a constant velocity until it reaches the distance set by the "c" command) or the maximum distance is reached, without the system having reached the limit speed.
- In both cases (reached the speed limit or not), I need the motor to stop abruptly upon reaching the specified distance, and not decelerate until reaching zero velocity. Therefore, the acceleration in this third instance should be very large again so that the motor stops suddenly and does not decelerate.

So far, I've only managed to get the system to stop abruptly, but there's a problem: the system accelerates to a very small speed and stays there, completely ignoring the speed limit I pass through the serial port. Anyway, I can post the code here if anyone wants to see it.

Do you have any idea if what I'm trying to achieve is possible?

Jim Larson

unread,
May 7, 2025, 2:45:51 PMMay 7
to accelstepper
Welcome! Yes, please post your code so we can see the details. Please tell us why you want to move the cell phone in this manner. Are you taking video?

As a general comment, trying to start and stop a mechanical system instantly is going to be problematic! The laws of physics are not on your side.
That said, if you can allow a bit of movement for acceleration and decelleration, then it should be possible to get satisfactory results.

            -jim

veronica

unread,
May 7, 2025, 3:44:34 PMMay 7
to accelstepper
Thank you very much for your response Jim. Yes, the final idea is to record a video of the cell phone moving while focusing on a vat full of water. It is totally allowed that the movement is not completely abrupt, a little movement can be allowed for acceleration and deceleration, but the less, the better.

Here is the code I have written so far

#include <AccelStepper.h>

//-------------------------------------------------
// Pin Definitions
//-------------------------------------------------
int stp = 7;   // STEP
int dir = 6;   // DIR
int M2 = 9;
int M1 = 10;
int M0 = 11;
int enb = 13;  // ENABLE

//-------------------------------------------------
// Create the stepper object (DRIVER mode uses 2 pins)
//-------------------------------------------------
AccelStepper stepper(AccelStepper::DRIVER, stp, dir);

//-------------------------------------------------
// Default Motion Parameters (in mm, mm/s, mm/s²)
//-------------------------------------------------
float velocidad_mm_s = 100.0;     // Maximum speed in mm/s
float aceleracion_mm_s2 = 5.0;     // Acceleration in mm/s²
float maxDistance_mm = 100.0;      // Maximum travel distance (mm), limited to 400 mm

// Microstepping conversion:
// For example, if the motor makes 200 full steps per revolution,
// one revolution moves 40 mm, and we're using a microstepping multiplier (mp)
float mp = 32.0;  
const float PASOS_POR_MM = 200.0 * mp / 40.0;

//-------------------------------------------------
// Global State Variables
//-------------------------------------------------
bool moviendo = false;             // True when a movement is in progress
bool abruptStopTriggered = false;  // Ensures abrupt stop is only triggered once per move
long initialPosition = 0;          // Starting position when movement begins
int direction = 0;                 // Movement direction: +1 for right (command "b") or -1 for left (command "a")
long limitSteps = 0;               // Travel limit in steps (derived from maxDistance_mm)

float originalAceleracion = aceleracion_mm_s2;  // Stores the original acceleration for later restoration

void setup() {
  Serial.begin(9600);
  while (!Serial);

  // Configure DRV8825 microstepping pins
  pinMode(M0, OUTPUT);
  pinMode(M1, OUTPUT);
  pinMode(M2, OUTPUT);
  pinMode(enb, OUTPUT);
 
  digitalWrite(M0, HIGH);
  digitalWrite(M1, HIGH);
  digitalWrite(M2, HIGH);
 
  // Enable the motor driver (active LOW)
  digitalWrite(enb, LOW);
 
  // Set up the stepper with converted mm/s and mm/s² values
  stepper.setMaxSpeed(velocidad_mm_s * PASOS_POR_MM);
  stepper.setAcceleration(aceleracion_mm_s2 * PASOS_POR_MM);
  stepper.setCurrentPosition(0);
 
  // Display instructions via Serial
  Serial.println("Sistema listo. Ingrese comandos Serial:");
  Serial.println("  v <velocidad> : Establecer velocidad (mm/s)");
  Serial.println("  x <aceleración> : Establecer aceleracion (mm/s^2)");
  Serial.println("  c <distancia> : Establecer distancia maxima (mm)");
  Serial.println("  a        : Mover hacia la izquierda");
  Serial.println("  b        : Mover hacia la derecha");
}

void loop() {
  // Process Serial commands if available
  if (Serial.available() > 0) {
    char comando = Serial.read();
   
    if (comando == 'v' || comando == 'V') {
      float valor = Serial.parseFloat();
      if (valor > 0) {
        velocidad_mm_s = valor;
        stepper.setMaxSpeed(velocidad_mm_s * PASOS_POR_MM);
        Serial.print("Velocidad establecida a: ");
        Serial.print(velocidad_mm_s);
        Serial.println(" mm/s");
      }
    }
    else if (comando == 'x' || comando == 'X') {
      float valor = Serial.parseFloat();
      if (valor > 0) {
        aceleracion_mm_s2 = valor;
        originalAceleracion = aceleracion_mm_s2;
        stepper.setAcceleration(aceleracion_mm_s2 * PASOS_POR_MM);
        Serial.print("Aceleracion establecida a: ");
        Serial.print(aceleracion_mm_s2);
        Serial.println(" mm/s^2");
      }
    }
    else if (comando == 'c' || comando == 'C') {
      float valor = Serial.parseFloat();
      if (valor > 0) {
        if (valor > 400.0) {
          maxDistance_mm = 400.0;
          Serial.println("Distancia maxima establecida a: 400 mm (limite del riel)");
        } else {
          maxDistance_mm = valor;
          Serial.print("Distancia maxima establecida a: ");
          Serial.print(maxDistance_mm);
          Serial.println(" mm");
        }
      }
    }
    // Command to move left ("a") if not already moving
    else if ((comando == 'b' || comando == 'B') && !moviendo) {
      direction = -1;  // Negative direction
      iniciarMovimiento();
      Serial.println("Movimiento hacia la izquierda iniciado.");
    }
    // Command to move right ("b") if not already moving
    else if ((comando == 'a' || comando == 'A') && !moviendo) {
      direction = +1;  // Positive direction
      iniciarMovimiento();
      Serial.println("Movimiento hacia la derecha iniciado.");
    }
  }
 
  // If a movement has been initiated, run the motion profile
  if (moviendo) {
    stepper.run();  // This updates the motor position according to the acceleration profile
   
    // Check if the traveled distance meets or exceeds the user-set limit:
    // For rightward motion: currentPosition >= initialPosition + limitSteps
    // For leftward motion: currentPosition <= initialPosition - limitSteps
    if (!abruptStopTriggered) {
      if (direction > 0 && stepper.currentPosition() >= initialPosition + limitSteps) {
        Serial.println("Distancia maxima alcanzada (derecha). Deteniendo abruptamente...");
        triggerAbruptStop();
      } else if (direction < 0 && stepper.currentPosition() <= initialPosition - limitSteps) {
        Serial.println("Distancia maxima alcanzada (izquierda). Deteniendo abruptamente...");
        triggerAbruptStop();
      }
    }
   
    // After triggering the stop, wait until the motor speed drops to zero
    if (abruptStopTriggered && (stepper.speed() == 0)) {
      // Restore the original acceleration for future moves
      stepper.setAcceleration(originalAceleracion * PASOS_POR_MM);
      moviendo = false;
    }
  }
}

//-------------------------------------------------
// Function to initiate movement in the chosen direction
//-------------------------------------------------
void iniciarMovimiento() {
  // Record the starting position
  initialPosition = stepper.currentPosition();
  // Compute the travel limit (in steps) from the distance in mm
  limitSteps = (long)(maxDistance_mm * PASOS_POR_MM);
  // Reset the movement control flags
  moviendo = true;
  abruptStopTriggered = false;
 
  // Set a faraway target in the desired direction to force uniform acceleration.
  // For rightward movement, use a large positive target; for leftward, a large negative target.
  if (direction > 0) {
    stepper.moveTo(1000000);
  } else {
    stepper.moveTo(-1000000);
  }
}

//-------------------------------------------------
// Function to trigger abrupt stop by temporarily boosting acceleration
//-------------------------------------------------
void triggerAbruptStop() {
  // Temporarily set acceleration to a very high value (simulate an abrupt deceleration)
  stepper.setAcceleration(1000000.0 * PASOS_POR_MM);
  // Issue stop command; this sets the target to the current position.
  stepper.stop();
  abruptStopTriggered = true;
}

As I said, this code fulfills the function of stopping the motor abruptly, but it does not fulfill the condition of rapidly accelerating to an initial speed and continuing the movement from there, and furthermore the speed at which it reaches is well below the one that I introduce as the upper limit.

Jim Larson

unread,
May 8, 2025, 4:30:55 PMMay 8
to accelstepper
Rather than speculating on what might or might not be wrong, give me a day or two to set this up and test it.

         -jim

gjgsm...@gmail.com

unread,
May 9, 2025, 9:51:10 PMMay 9
to accelstepper

I won’t preempt what Jim is working out for you but after a quick read I had a couple of comments I could make.

 -  for high acceleration your PSU needs to be able to supply sufficient power say, 24 vdc at 3 amps.

 -  the UNO will generate about 5000 steps per second max so, if you are micro stepping (x32) that means you need 6,400 steps per revolution giving you a max linear speed of around 30 seconds per metre.

 -  If I am reading this correctly you want, high acceleration to a nominated speed and then high decceleration to a stop after a nominated distance. This can be done by setting acceleration and maxSpeed, try values of say 3000 or 4000 and vary from there. The ‘stop’ function should bring it to an abrupt stop given your setup.

 -  The stepper should run in its own loop with minimal control code. Serial.prints and inputs etc… should not be in that loop. Speed and acceleration settings will probably need to be input before stepper movement commences and not during movement.

 -  and you may need something like an interrupt (ISR) to flag when the nominated distance has been completed.

Jim Larson

unread,
May 10, 2025, 7:10:40 PMMay 10
to accelstepper
OK. After a bit of fighting to get my set up running, I have your code running with only a few modifications. I'm running a fairly powerful NEMA 17 motor (1 Ohm resistance) and driving it with an A4988 driver (very similar to the DRV8825) using a 12V, 2A supply. My Arduino version is an STM32G430, faster than an UNO, and 3.3V logic.

All that [gigsmithvr] said is true and is good advice, but probably applies mostly if you truly need to run a powerful stepper at high speed. You may be able to get satisfactory performance with less effort.

The main change I made was to reduce your microstepping to 4 instead of 32. Now your initial maximum speed is 2000 steps per second. A target acceleration of 2000m/s^2 is certainly possible. If you truly need fine microstepping, you will probably need a (much) faster Arduino. I suggest trying the lower microstepping  and see if the performance is adequate or not.

I made one change to your code. In the "x" command, you appear to intend to save the old value of acceleration before updating to the new value. Here is your code:
aceleracion_mm_s2 = valor;
originalAceleracion = aceleracion_mm_s2;
I think it should look like this:
originalAceleracion = aceleracion_mm_s2; //jkl
aceleracion_mm_s2 = valor;

In your original post, you mention needing to change acceleration after reaching your target speed. There is no need to do anything special. Once maxSpeed is reached, accelStepper will run your motor at constant speed until the target location is reached, or the motor is stopped. Note that the stop() command will not actually stop the motor, but will set the target distance to the current position. This causes a stop using the current value of acceleration. It appears that this is what your code does. It seems to work as intended.

When I ran your code, I did not see any slow down due to calls to Serial, unless I tried to issue commands while the motor was running. It seems to me that you probably want to issue set up commands, select the direction of movement (which starts the motor), and let it complete the motion before issuing more commands. If that's true, I think you're OK.

So the main change was to change the microstepping so that the maximum stepping speed was limited to a reasonable number. Other than that (and the minor correction above) your code was great!

                           -jim

veronica

unread,
May 14, 2025, 9:45:37 AMMay 14
to accelstepper
Jim, I have no words to thank you for this. I really appreciate that you took the time to put the experiment together. Thanks also [gigsmithvr] for your advice.

I have tried your recommendations and when changing the microstepping from 32 to 4 the motor makes a very loud noise and vibrates a lot, making it impossible to take a clear image, so this vibration is not acceptable. Did the same thing happen to you when you tried the code? Did it make a very loud noise and vibrate?

veronica

unread,
May 14, 2025, 10:27:47 AMMay 14
to accelstepper
Another thing, since it has worked for you, I am going to change my components for the same ones you have, but I can't find the Arduino STM32G430, I can only find a module called Stm32f103c8t6 (link: https://articulo.mercadolibre.com.ar/MLA-1421587053-modulo-blue-pill-desarrollo-stm32f103c8t6-stm32-ide-arduino-_JM#polycard_client=s search-nordic&position=15&search_layout=grid&type=item&tracking_id=1fcdd134-769e-457a-8978-9250a7973e8f&wid=MLA1421587053&sid=search), but I don't think that's what you have. Is there any other Arduino that you recommend that is equivalent?

Jim Larson

unread,
May 14, 2025, 2:38:38 PMMay 14
to accelstepper
Before you change your Arduino or driver, I suggest you try decreasing the current on the DRV8825. The easiest way I have found to adjust it is to have the motor running (even if it's making noise and vibrating), then turn the current adjustment. First decrease the current until the motor stops, then increase it until the motor is running at normal speed. Exactly how much current is needed depends on the load on the stepper. Be careful when adjusting the current control not to short it to adjacent components, that will kill the driver. Just pay attention as you do the adjustment.

         -jim

Ross Waddell

unread,
May 14, 2025, 4:48:35 PMMay 14
to accels...@googlegroups.com
Jim - I’ve always struggled trying to calculate the appropriate current  using the driver docs and then setting it by reading the Vref contact on my driver (TMC2208 SilentStepStick) with the motos disconnected. Will your method work on all stepper driver modules? It would make life so much easier ….

On May 14, 2025, at 2:38 PM, Jim Larson <jla...@pacifier.com> wrote:


--
You received this message because you are subscribed to the Google Groups "accelstepper" group.
To unsubscribe from this group and stop receiving emails from it, send an email to accelstepper...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/accelstepper/344d84fc-6f04-4275-aa1f-5d2efb577c5en%40googlegroups.com.

Jim Larson

unread,
May 14, 2025, 6:08:36 PMMay 14
to accelstepper
I've used it with DRV8825 and a4988 drivers. It should work with other drivers as well. YMMV All you are doing is reducing the current available to the drivers. Probably best to adjust slowly, but it's no different than using a weak power supply that limits current. As far as increasing current, just listen to your motor. It will make noises if the current is too high. On the drivers I've done this with, the motor may behave erratically and even stop if the current is too high. I find it interesting to move the adjustment through its range and observe the motor. Usually, the lowest setting that makes the motor run smoothly is the correct setting. I've used the method on at least 40 3D printers and numerous other motors as well.

         -jim

Ross Waddell

unread,
May 14, 2025, 6:22:35 PMMay 14
to accels...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages