Trapezodial velocity profile for a stepper motor
Math behind it
Discrete stepping and acceleration
When using a stepper motor, we want to avoid running it immediately at full speed especially if a mass is attached to the motor since it is quite likely to induce miss-stepping. In our case, we use a stepper attached to a lead screw forming a linear stage. The stage now performs a linear motion over a total distance defined as
In the ideal case we would like to follow a linear increase in velocity followed by a constant plateau of maximum movement velocity
The velocity of a stepper motor is thereby translated into the delay time
Linear acceleration phase
In the acceleration phase, our linearly accelerated velocity profile is described as:
with the velocity
Solving this equation for
Constant velocity phase
In this case we just have the velocity at a constant level
Linear deceleration phase
For the deceleration phase, we have the following description of the velocity profile:
or solved after
with its distance counterpart
or
Solving the quadratic equation through
leads to
Case switch
In practice we now need to find the thresholds for the three phases.
We can extract the first distance as
and the second one as
If
Microcontroller implementation (Teensy)
// define before
// ss [float] --> step size in mm
// vmax [float] --> maximum desired linear velocity in mm / s
// acc [float] --> acceleration in mm/(s*s)
// deltaS [float] --> distance to travel
// fallAndRise [bool] --> true if rising and falling edge lead to stepping, otherwise false
uint32_t absSteps = round(abs(deltaS) / ss); // number of steps we need to do
// define end point of acceleration and start point of deceleration
float s_1 = vmax * vmax / (2 * acc);
float s_2 = deltaS - s_1;
if (s_1 > s_2) // if we don't even reach full speed
{
s_1 = deltaS / 2;
s_2 = deltaS / 2;
vmax = sqrt(deltaS * acc);
}
float vcurr = 0;
bool stepPolarity = 0;
for (int32_t incStep = 0; incStep < absSteps; incStep++)
{
// calculate central position of current step
const float s = ((float) incStep + 0.5) * ss;
// calculate velocity at current step
if (s < s_1)
{
vcurr = sqrt(2 * s * acc);
}
else if (s < s_2)
{
vcurr = vmax;
}
else
{
vcurr = sqrt(vmax * vmax - 2 * (s - s_2) * acc);
}
// convert velocity to delay
const uint32_t tDelay = round(ss / vcurr * 1e6); // in micros
if (fallAndRise) {
stepPolarity = !stepPolarity;
digitalWriteFast(pinStep, stepPolarity);
delayMicroseconds(tDelay);
} else {
digitalWriteFast(pinStep, HIGH);
delayMicroseconds(tDelay / 2.0);
digitalWriteFast(pinStep, LOW);
delayMicroseconds(tDelay / 2.0);
}
}