How to get sinusoidal s-curve for a stepper motor

After some experimentation, and delving a bit into the math of motion, I realized there can be a significant difference on the motion of a machine by just making small changes.

A common and popular approach to smooth one axis of motion is to use a trapezoidal speed pattern, where acceleration and deceleration are uniformly accelerated movements (aka constant acceleration motion) separated by a portion happening at cruising speed.

The problem comes with the fact that, while that approach is simple to understand and to code, it does contain sudden changes in the acceleration, that show as spikes on the acceleration derivative, called jerk. These sudden changes in the acceleration translate into undesired vibration in our machines.

Most of that can be eliminated by selecting a motion pattern that does not contain sudden changes of acceleration. If we chose a mathematical function for our speed whose second derivative is continuous, then we reduce system vibration quite a lot. One may think that can only happen if we slow down the motion, but that does not necessarily true. What changes is the way acceleration takes place, but the average acceleration may be kept to a similar value as the fixed value used for trapezoidal moves. If done that way, the motion will take exactly the same time as before, but hopefully, it will cause less vibration.

One of the mathematical functions we can use that be derived endlessly is the sine function. So let's see how we can build an S-curve speed motion profile based on sinusoidal functions.

First of all, we replace the usual acceleration step of the trapezoidal motion by a similar duration wave based on the expression (1-cos(t)), where t ranges from 0 to 2Π

During this acceleration period, the speed will grow as the integral over time of that, so speed will follow the signal t-sin(t), that will create a smooth S-shape curve.

And the covered distance along this motion, will be t^2/2+cos(t)-1 (please note the -1 is an integration constant, that is need so the distance traveled when t=0 is zero too).

So now the point is how to translate this into the code for driving a stepper motor, a little bit like Atmel's AVR446.

Following the same logic, we need to determine what is the time that corresponds to different step pulses according to the distance equation above. But there is a bit of a problem: our distance equation is a transcendental one, so there is no way we can obtain an inverse function that given the distance will tell us the time.

And to make things worse, we want, ideally, to have a quick way to calculate the time delay for each next step so we would need a solution that can work in real-time.

The solution

For a given motion, the acceleration part of it using a stepper motor consists of a given number of steps, N. We want our acceleration pattern shown in the third graph to match these N steps timing. But time variable will need to be scaled properly. 

Let us assume we use the same acceleration time as in a trapezoidal move. Our first step (out of the total N for acceleration phase) will correspond to a "virtual" displacement in our distance scale of 2Π/N and we need to know what time that corresponds to. For that we do not have a direct expression.

So we have dist=f(t), where the unknown is t but the distance is known.  We can use numeric methods to solve that, like for example the Newton method. But doing that within the interrupt code of a timer seems far from optimal. 

A simpler approach could be to build an array with all the N values needed before starting the movement, this way the calculation is no longer happening inside the interrupt code. But that might be a problem if the number of steps N is larger than the available RAM, which may happen for long moves if acceleration values are low and cruising speed is high. 

An alternative idea could be to create a table lookup that will be in fact be a discrete representation of the inverse of f(t), with a size according to the available Flash memory space. And then, a simple linear interpolation can be used for any other distance value not contained in the table. And what's best, this calculation requires no trigonometric functions and it can be done very quickly.

Once the table is exhausted, cruise speed has been reached and that time interval is maintained till the deceleration phase starts, playing the same intervals as before but in the opposite order now.

A very simple demo code.


Popular posts from this blog

VFD control with Arduino using RS485 link

4xiDraw: Another pen plotter

Arduino mood light