Tuesday, 12 October 2010

Stepper Motor Control Theory

The mathematical and computational aspects of stepper motor control turn out to be quite interesting. The goal is to write an efficient microcontroller algorithm that can issue step signals to 3 (or more) motors simultaneously so that the drill bit follows a pre-defined path with a desired, possibly changing velocity profile.

The opponents are the limited microcontroller processing power, and complex modeling dynamics of real-world stepper motors.

I've started on my own home-brew version, but for now, a bit of an introductory summary with pointer for more reading...

Stepper Motor Modeling

At level one, we are interested in modeling stepper motor behaviour. A very nice introduction to this topic was published by D W Jones - Control of Stepping Motors - A Tutorial. It's an all-round introduction to stepper motors and discusses stepper motor types, stepper motor physics, basic control circuits, current limiting circuits, H-bridges and decay modes, micro-stepping and torque issues, and real-time control algorithms. The algorithms are a bit theoretical, but nevertheless, they provide a sound foundation to the topic.

Some more mathematical approaches are listed below - perfect for your lonely nights...
Without making you wade through pages of maths, just imagine a stepper motor like a damped pendulum that is subject to viscous friction. A model should take into account physical behaviour (inertia, friction, etc) as well as electrical characteristics (LR-circuitry, etc).

Interestingly, the actual approximation algorithms (discussed below) effectively ignore stepper motor modeling complexities and simply assert that three values, namely maximum acceleration, maximum deceleration, and maximum velocity, are all that are needed to build a working algorithm. They probably know why!

The proposed algorithms I've found are based on the concept of constant acceleration. I'd be interested to know why "constant acceleration" seems the accepted norm as the starting point, other than for mathematical elegance and simplicity? My car does not have constant acceleration. Every time I reach 150km/h in my local suburb, a car with flashing blue lights gets in the way and slows me down.

Maybe I should put my z-axis through some load testing to see what type of maximum acceleration profile works best, just to check against the maths.

For good measure, some more maths of my own...

It was raining outside...

The G251 Controller

The Gecko G251 controller sits between the Arduino and the stepper. Modeling stepper motor physical behaviour and then creating an acceleration algorithm based on the model will be difficult, as the G251 itself applies optimisation and I don't know its internal algorithm. For example, it smoothly changes from micro-stepping to full-stepping at higher speeds and also avoids mid-band resonance. This reinforces the suggested approach of "all you need to consider is maximum acceleration to get your stepper model".

Acceleration Algorithms

The microcontroller algorithm needs to issue a pulse train to the stepper/controller. The time between each pulse can be calculated from the desired velocity/acceleration profile. To cut to the chase, for constant acceleration, you need to calculate square-roots, which isn't the kind of thing a microcontroller wants to do 50,000 times per second.

A few good approximation schemes to get you started can be found here:
  • Atmel - Linear Speed Control of Stepper Motors (pdf): Outlines acceleration algorithm, including Taylor Series approximation. Also touches on resonant frequency issues.
  • D Austin - Generate stepper-motor speed profiles in real time: A very detailed derivation of Taylor Series approximation and enhancements. Probably the best article of the bunch, but it requires division, which is an expensive instruction for an Arduino (in the order of 100 clock cycles).
  • Aryeh Eiderman detailed the Leib Ramp (pdf), which is an approximation that does not require divisions. Probably the fastest version. Notably, he makes a possibly unintended (incorrect but workable and simplifying) assumption in equation 7 where he calculates the "real time delay" using the instantaneous velocity "vi' instead of the integral of velocity over the time period in question. Someone tell me if I'm wrong here.
  • There is also a compact implementation proposal by Atmel: Atmel AVR360: Step Motor Controller (pdf). They describe a 10-byte interrupt service routine to drive a stepper. Not exactly applicable to my needs, but interesting nevertheless.
A number of semi-related topics can be found in this list of articles: Discover Circuits - Motor Control Circuits.
 
Multi-Dimensional (2D, 3D, and beyond)

Once you've figured out how to drive a single stepper to its edge, you are left with the challenge to orchestrate all 3 of them to work together.

The guys at RepRap (forum, site) seem to have solved the problem by using a linear Bresenham algorithm and a single "interrupt" (discussed in this post). That means, they track the fastest axis and pull along the slower axes.

I think there is a flaw in that logic. Consider the extreme example of a path for a line with an dx:dy ratio of 1.5:1, for example from (0,0) to (15,10). The Bresenham algorithm steps x nicely and regularly by one step for each interrupt (assume constant velocity throughout). However, y is stepped as (0,1,1,2,3,3,4,5,5,6,7,7,8,9,9,10). This means y velocity changes by 50% up/down every other step. I guess it is workable because the stepper just smoothes over these bumps, but I'm not sure about all-over efficiency or issues with resonance. I'd be happy to stand corrected on this one.


Bresenham example: y-movement is not smooth.


So where to go from here ?

Firstly, I'm still interested in first-principles and would like to see if I can squeeze some nice maths & approximating algorithm out of a simplified physical stepper motor model, but I'm also aware that time is moving.

Second, none of the above methods suit my multi-dimensional requirements. I can either keep browsing, or continue my own path to enlightenment. DIY is much more fun. My constraints are going to be a challenge. Since the G251 is fixed at 10 microsteps, I will have to be able to run my steppers at around 50,000 steps per second. The Arduino clocks at 16Mhz, so that's only 320 clock cycles (and fewer instructions) per step. And that's for one axis only. I may have to accept some compromises, for example, not all axes might run at full throttle at the same time.

But only time and maths will tell...

Speaking of maths: I discovered that "router project progress" is indirectly proportional to "amount of sunshine". But you knew that already...

17 comments:

Anonymous said...

Hey there, I found this blog while looking for an existing publicly available algorithm for handling acceleration. I'm building an Arduino-based stepper driver pulser. (Not the stepper driver, the thing that sends step/dir signals to the stepper.) Do you intend to continue exploring this? I've looked at various gcode interpreters for Arduino and it seems they are all too complicated for top performance. I'd like have the host computer send very simple commands to the arduino with all the "heavy lifting" done on the host. Which means working out acceleration and sending it "prebaked" to the Arduino.

I'll check back, not comfortable entering my credentials in here.

Thomas said...

Hi
Yes, I intend to write my own controller (pulse train generator) using an Arduino Mega (or three). I'm currently a bit distracted by my other life, hence not much progress. I'm as yet undecided if I want to send gcode to the Arduino or use a different set of path descriptors, and just as you wrote, I intend to let a laptop do the heavy number crunching. However, I will have the Arduino do the acceleration bit, otherwise there will be too much data go through the usb/serial and I cannot rely on the laptop to create a real-time command stream. I'd be interested to hear how you intend to solve the acceleration problem through the PC.
You can contact me via email (on the blog - right sidebar) if you don't want yourself exposed on the web.
Thomas

Anonymous said...

Sounds good. I'm working on the hardware and software at the same time so progress is slow. I have the basic framework for path generation using Java/Processing including 3D visualization. It's all simulation at this point. If I can solve the acceleration problem everything else should fall into place. I will probably write a gcode parser that converts it to Java objects, then figure acceleration, then send movement commands to the Arduino. I'll let you know when I have something with acceleration.

/Daryl

니모아빠의 Debian Linux 옅보기 said...

your article is logically well organized and impressive because you've done what I was wondering how to figure out what I need to know. I am working around stepper controller on Arduino borrowing some open source code from reprap and others. Stepper driver come from Makerbot Stepper Driver 2.3.
Still I didn't get expected result considering Math based on your comment. I am checking it why this happen. Stepper Motor problem ? or Stepper driver problem.
Tomorrow I get new stepper motor. then everything will be clear.

On the other hand, I am testing CoreTex M3 evaluation board as a candidate of stepper controller.

I want to keep watching your post on CNC. Thank you for posts.

Thomas said...

Hi Debian Linux
Thanks for your kind words. Feel free to share what results you expected, what you did measure, and what your setup was. It might be interesting.

Thomas

Triffid Hunter said...

Hi,

I'm the author of the Teacup Firmware for reprap and potentially other CNC machines.

I'm now working on a new firmware called SoupCup which will not replace Teacup entirely, rather it has a different design goal of being closer to mathematically correct with fewer shortcuts.

SoupCup can be found at http://github.com/triffid/SoupCup_Firmware

I have tried controlling each axis independently, after altering their acceleration and deceleration profiles so that they all finish accelerating and start decelerating at the same time, but numeric precision prevents it being useful on chips such as the atmega.

I have fallen back on my 4D bresenham algorithm for maintaining geometric correctness. As you surmise, there are worst-case jitter scenarios, but the motors do smooth it out especially if you're microstepping.

It has the real-time stepper profiles acceleration/deceleration algorithm with per-axis acceleration limits. I'm currently writing the chunk of math that takes care of the case where we don't reach full speed because the move is too short, and came across your post while looking for some bits and pieces.

Just thought I'd let you know that you're not the only one puzzling over these issues, and that work has been done on them :)

Thomas said...

Hi Triffid Hunter

Thanks for your comment. I'll be keeping an eye on SoupCup once I get back to CNC programming. I'm currently very distracted with quadcopter and arm microcontroller playtime.

I'd still like to try to find a better approach than Bressenham, but like I wrote in the original post, only maths and time can tell, and the Atmel doesn't have a lot of clock cycles per step.

Thomas

Igor Kondrasovas said...

Hello Thomas,

Did you have any progress to solve the velocity problems you found in Bresenham algoritym.

I am working a 2D motion controll application using Netduino and need to have a controlled "vector velocity".

Thank you,

Igor.

Thomas said...

Hi Igor
No, unfortunately because of other distractions I haven't gotten around to do anything for my CNC project for some time now.
I'd be happy to find a commercial sponsor, though, to make it a full-time research project... :-)
Thomas

Anonymous said...

Could you not just use Bresenham, but scale it up? It would essentially be microstepping the Bresenham line internally, and then you just send "real" steps out every Nth microstep. That would allow you to reduce the amount of potential time jitter, since you'd be able to have the axes stepping out of sync with each other. Does that make sense? (It sounds good in my head. :)

I found this while looking for a way to handle turns within a set velocity profile, like the "CV" setting in Mach3.

JasonDorie

Thomas said...

JasonDorie: Mathematically, this is a very nice idea. If you select an internal step division k, such that dx/dy*k is a whole number (integer) (assuming dx>dy), then you will have smooth stepping in both dimensions.

The physical limitation is the number of times per second you can actually calculate a 'Bresenham step'. (I'm already hitting the Arduino's limit with 50,000 steps per second per axis at theoretical max speed).

Taking this to the max, I think the suggestion leads to the method of calculating the (clock-frequency-truncated) moments in time when an INDEPENDENT step in each dimension needs to be taken. That is, basically run 2 timers to trigger each dimension independently. I don't know if that would still be considered 'Bresenham' but it's a nice way of tying these two together.

Thomas

Anonymous said...

Thank you for your theoretical interest in this matter -I thought I was the only one crazy enough for that.

A few thoughts: Yes, division is slow, but table lookup is fast, and can often provide the resolution (accuracy) needed.

One of the things I realized is that by the time you get to step/direction, you've lost a lot of information. What you want is A/B stepper phase drive output directly. This way microstepping becomes a non-issue, you're "infinitely" microstepping depending on the velocity required -and of course your fundamental digital timing quantization.

Finally, the secret is not to think of positions, but of accelerations. Convert your position points to accel/hold/decel ramps and use that for the motor drive routine.

SLI

Anonymous said...

Hi,

max input frequency of common stepper motor driver is about 200kHz, regardless of microsteping is used or not. If you are able compute and emit 200000 setp/dir pulses per second, you could operate at microstep level same way as at fullstep level. So throw away problem of microsteping for now.


your problem "non-smooth acceleration using Bresenham alghoritm" not in alghoritm itself, but in discrete timing you have used. It is based on constant "base frequency" at which your ATMEL sends pusle or not. Pulse have to go now or next time. Nothing between. You do this:

time[ms] X Y
-----------------
1 1 1
2 1 0
3 1 1 *
4 1 1
5 1 0
6 1 1
7 1 1
8 1 0

just imagine that * puls for axis Y could be sent at time 2,5 ms. Take a output of Bresenham algoritm and revrite every Y puls to new separate row:

X Y time[us]
--------------
1
1
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?

Now compute values for "?" to be acceleration smooth. It is possible, isn't it ?

I apologize you, do this computation on PC and try to express time as integer in units of your external device timer. Your external hardware could consist only from memory buffer and 1Mhz counter (at least) In buffer are stored time values from table above. Device have a comaparator which comare value of memory cell and value of his hi-speed counter. when it is equal, sedns pulse to motor driver and increment adress of memory cell and so on. When adress is near to end of buffer, send pulse to PC for new data to be loaded to first or second half of buffer (cyclyc buffer). Replacing ATMEL by hardwired device you take advantage of much more precise timing (event up to 1Ghz if you want).

Joe

pepino@quick.cz

Anonymous said...

Hi,

max input frequency of common stepper motor driver is about 200kHz, regardless of microsteping is used or not. If you are able compute and emit 200000 setp/dir pulses per second, you could operate at microstep level same way as at fullstep level. So throw away problem of microsteping for now.


your problem "non-smooth acceleration using Bresenham alghoritm" not in alghoritm itself, but in discrete timing you have used. It is based on constant "base frequency" at which your ATMEL sends pusle or not. Pulse have to go now or next time. Nothing between. You do this:

time[ms] X Y
-----------------
1 1 1
2 1 0
3 1 1 *
4 1 1
5 1 0
6 1 1
7 1 1
8 1 0

just imagine that * puls for axis Y could be sent at time 2,5 ms. Take a output of Bresenham algoritm and revrite every Y puls to new separate row:

X Y time[us]
--------------
1
1
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?
1 ?

Now compute values for "?" to be acceleration smooth. It is possible, isn't it ?

I apologize you, do this computation on PC and try to express time as integer in units of your external device timer. Your external hardware could consist only from memory buffer and 1Mhz counter (at least) In buffer are stored time values from table above. Device have a comaparator which comare value of memory cell and value of his hi-speed counter. when it is equal, sedns pulse to motor driver and increment adress of memory cell and so on. When adress is near to end of buffer, send pulse to PC for new data to be loaded to first or second half of buffer (cyclyc buffer). Replacing ATMEL by hardwired device you take advantage of much more precise timing (event up to 1Ghz if you want).

Joe

pepino@quick.cz

Anonymous said...

sorry for formating, second table should look like this:

X Y time[us]
----------------
1 0 ? = 1000
0 1 ? = 1500
1 0 ? = 2000
1 0 ? = 3000
0 1 ? = 3000
1 0 ? = 4000
0 1 ? = 4500
1 0 ? = 5000
1 0 ? = 6000
0 1 ? = 6000
1 0 ? = 7000
0 1 ? = 7500
1 0 ? = 8000

as you can see, pulses for X–axis are equidistant (delta=1ms) and pulses for Y–axis too (delta=1.5ms).
Zero acceleration between steps, so movement is smooth (spindle is going along straight line 3:2 with constant velocity)
Geometry of spindle path is still based on Bresenham algoritm. As you can see, it is not bad. B. algorithm is about geometry not about timing. Timing is independent task. May be more complicated (think about safe decceleration before movement is changed to right angle)

as i have described before, the values in last column can be compared with value of 1 MHz timer (by discrete logic comparator rather than by program loop of ATMEL). When counter counts to 1000, output of comparator sends pulse for X axis and takes next value to compare with (1500). When it counts to 1500, your device sends puls for Y axis and so on.

Thomas said...

Hi Joe [pepino]

If I understand your posts, you are basically calculating the step-times for each axis independently and then wait for the required time(s) to step either axis.

This works fine so long as your microcontroller does nothing else, but I am not sure it will work with an interrupt-driven model, where an interrupt is raised with the processor and the processor takes a step. In that scenario, if the two steps are "close" in time, there will be a small delay (hopefully too small to be relevant).

Another option would be to use two different hardware timers that can directly drive the output pins, so all the microcontroller has to do is calculate the next delta-t and load that into the timer. Ability to do so would depend on the hardware in use.

Finally, we would also need to ensure that the delta-t calculations and all the other bits and pieces that go with the entire sofware can be calculated on-chip in a short-enough time at high step rates.

Thomas

Anonymous said...

Hi Thomas,

you are right, for interrupt-driven model with constant frequency of asking for step is my idea useless. Except of case, interrupt frequency is let say 10 times higher than step rate (9 of 10 interrupts ends with "no step", every 10-th interrupt do step in X, while step in Y could be done for example in 15-th, 30-th, 45-th, etc. interrupt)


I know, 10-times is a lot. Let say, you can afford two interupts per one B. algorithm calculation. If dx:dy = n is integer, B. algorithm produces Y-step every n-th sample. If not ( n < dx:dy < n+1 ), Y-step is generated every n or n+1 sample. For example n,n,n+1,n,n,n+1,n,n,etc. If controller is interrupted 2x per one calculation, it has option do Y-step in first interrupt or in second. How decide in which ? Rule is simple: If current interval is longer then previous, do Y-step in first interrupt. If current interval is shorter than previous, do Y-step in second interrupt. If both intervals are equal, do Y-step in the same interrupt (in first or second) as in previous step. Timing of pulses is 2x times better and microsteping level is unchanged.


Time correction i have described above, is not for free, it spends any microcontroler cycles. My question is, is not easier use 2x higher microsteping instead ? It will smooth both, geometry and timing. But for cost of 2x higher frequency of B. algorithm calculations. As i assume, you are using such high microsteping level, you can process and therefore you are looking for better algorithm witout extra processing.

I affraid, better algorithm which meets your kriteria can't exist in principle. You draw line with declination dx:dy = 3:2, so you need spread 2000 Y-steps over 3000 X-steps. Your presumption is, that every Y-step have to go together with any X-step. It means, that you have list of 3000 equidistant X-steps and you have to color 2000 of them. You wish, the colored steps would be equidistant too. And this is not posible, if dx:dy is not integer.


You mentioned, you are using microsteping. In that case, don't worry about temporrary exceedings acceleration limit. If you emit very quicky two pulses to driver in fullstep mode, rotor return back to original position which is nearest attractive position (step loss). If you do it in microsteping mode, everithink is OK, vector of magnetic field of stator take place his position somewere between two fullsteps. This position is still atrracting rotor, so it can take it for a while (no step loss). Fail will happen only in case, you exceed acceleration limit in scope of fullstep, that means you have such signal iregularities, that rotor cant follow more than 16 pulses in sequence. It is not your case :)

Joe