Friday, January 8, 2010

3ph Duo Wrap-Up Part 1: Field-Oriented

After the single most difficult embedded programming challenge I've ever faced, I am finally ready to wrap-up the 3ph Duo field-oriented control upgrade. This is a slight hardware tweak and major software overhaul that, together, allow my dual-channel, 1-kW per channel brushless motor controller to operate as a true AC (sine wave) motor controller with field-oriented control. This post, Part 1 of 2, explains the "field-orienting" part. "Control" is left for Part 2. A massive (and I mean massive) write-up is also coming, if you really can't get enough of this stuff.

But my New Year's Resolution is to make my blog posts a little more understandable, so this might be a good time to review the big picture: What the heck is field-oriented control? And don't use any big words or cite any 355-page Master's theses. Okay, here's a motor with two magnets and three coils of wire:

To be more specific, it's a two-pole, three-phase outrunner motor. The stator has three coils, A, B, and C, spaced by 120º. The rotor has two magnets, N and S, spaced by 180º. It's about the simplest BLDC motor you could imagine, and thankfully the electrical coordinate system lines up with the physical coordinate system. Speaking of coordinate systems, let's define one:

The problem is 2-dimensional, so a simple set of perpendicular axes is useful. In motor control, the axes are called d (direct) and q (quadrature) instead of x and y, and they are defined with respect to the rotor. So, as the rotor rotates, so do the axes. The d-axis is always lined up with the magnets, and the q-axis is always 1/4-cycle ahead of the d-axis. (So, in the picture above, the motor is rotating counterclockwise.) If the motor had more poles, the axes would not be physically at right angles, but the d-axis would always be on magnets and the q-axis would always be in between magnets.

To get the motor to spin, the current is sent through the coils, turning them and the steel around them into electromagnets. These electromagnets attract or repel the permanent magnets. By alternating the direction of current in the coils, a rotational motion is created. Another way to look at it is that the coils are used to create a rotating magnetic field and the magnets are pulled along by this field.

The coils A, B, and C are aligned at 120º intervals, so any quantity (current, voltage, magnetic flux) that can be attributed to a coil has a particular direction to it. In other words, these quantities are vectors. As such, they can be projected onto any set of axes we want using simple trigonometry. In field-oriented control, these quantities are projected on the d-axis and q-axis as we have defined above. A simple example:


In this case, current is sent into coils B and C, and comes out of coil A. The magnitudes, represented by the lengths of the red arrows, are such that currents A+B+C = 0, since the coils are all connected at a single point. These three vectors, if added together, would give only d-axis current. (Current A has no q-axis component; B and C have equal and opposite components on the q-axis.)

But what does this mean for the motor? Well, the current passing through the coils turns them into electromagnets. The d-axis, which has the permanent magnets on it, would like to align itself with the electromagnet being created by the coils. In this case, it already is aligned. The motor is perfectly happy where it is and will resist being moved in either direction. To see how torque is produced, imagine this case:


Here, the resultant current is entirely on the q-axis. The magnets still want to align themselves with the current, though, so the rotor will try to rotate counterclockwise, producing torque as it does. By keeping the resultant current (the sum of the three current vectors, A, B, and C) on the q-axis as the motor rotates, the rotor is always trying to catch up and producing the maximum amount of torque.

The ultimate goal of field-oriented control is to be able to directly control both the magnitude and the angle of the total current vector by using the individual coils. Since the angle is defined with respect to the rotor, this requires knowledge of the rotor position. In order to make the vector math easier, everything is assumed to be a sine wave and the power electronics used to drive the coils produce a sine wave ouput.

This is very different than traditional BLDC control where the output is a square wave, and there is no knowledge or control of the current angle. In BLDC controllers, the timing of coil firing is fixed by Hall-effect sensors. Why not just fix the timing so that the coil fires on the q-axis, you say? Well, you can, and for most motors this works pretty well. However, the coils are also inductors, and inductors resist rapid changes in current. Thus, it takes time for current to build up in the coil after it has been fired. This delay means that although you may be firing on the q-axis, some current will lag behind and wind up on the d-axis:


Because the current is no longer on the q-axis exclusively, torque and/or efficiency are reduced. The actual angle by which the current lags is a function of motor inductance, resistance, and speed, so it is very difficult to predict the lag ahead of time and dead-reckon the correct coil firing time. (Some controllers do this! You will get back some of the torque, of course.) Field-oriented control can directly measure and control this effect. This post will focus on the measurement aspect, arguably the hardest part. Part 2 will focus on control.

In this ECN article, Daniel Torres says:
The most common technique utilized for controlling BLDC motors is trapezoidal control. However, both PMSM and BLDC motors can be controlled using sinusoidal control and Field Oriented Control (FOC).

FOC has become more popular in recent years, due to the fact that the cost required to implement this technique is no longer a constraint. The available technology and manufacturing processes now make it possible to implement this control technique in a 16-bit, fixed-point machine, such as Microchip’s dsPIC Digital Signal Controllers (DSCs).
And here are some application notes specific to the dsPIC that illustrate sinusoidal control and field-oriented control of BLDC motors:

AN1017: Sinusoidal Control of PMSM Motors with dsPIC30F DSC
AN1078: Sensorless Field Oriented Control of PMSM Motors
These very informative application notes both use the dsPIC30F, which, I will grant, is a fixed-point 16-bit processor. (It doesn't have hardware floating-point capability like an ARM or Pentium processor might have). But what it does have that my poor little MSP430F2274 does not is a 30Mhz instruction clock and single-cycle multiplier. Without the hardware mutiplier, the processor can only multiply by a power of 2 or add things together, meaning it takes several shift/add steps to multiply two arbitrary numbers by each other. In other words, while the dsPIC can multiply two numbers together in 33 nanoseconds (!!) it takes me somewhere in the neighborhood of 4,000 nanoseconds. :(

With that in mind, what exactly needs to happen for my controller to be able to execute sinusoidal, field-oriented control? First, let's focus on the sine wave part. As it turns out, this was the most difficult part of my particular endeavor. The hardware for producing a sine wave output is virtually the same as it is for square-wave BLDC control: six transistors (MOSFET or IGBT) switching at high speed to produce three voltages on the motor terminals. I'll spare you the details, but they'll be in the final write-up.

Although it didn't start out as such, my control program wound up converging on a pretty typical fast loop/slow loop configuration. The fast loop, which caused all the problems, handles sine wave generation and runs at the switching frequency of the transistors, which in this case is about 14.4kHz. This loop is responsible for looking up the base voltages from a table of sine values (doing actual trigonometry is out of the question) and then scaling them by some magnitude. It is also responsible for estimating rotor speed and position, which unfortunately involves a division. The problem here is that, while Daniel Torres insists it can be done, I don't have a hardware multiplier for scaling. Oh, and I need to do it for two motors. So how screwed was I? This little test reveals how screwed:


What you see there is a signal that turns on at the start of every 14.4kHz fast loop and turns off when the fast loop code is finished. In the 69 microseconds between each cycle, I needed to do six look-ups, six scaling operations, and occasionally two divisions! (Remember that each multiply takes about 4 microseconds.) The brighter line is the time it takes when not doing the divisions. The dimmer line is the extra time it takes to do the two divisions. You can see the code did finish in time, but only just barely, with about 2 microseconds to spare for all the other processes, including the slow control loop! You know how your computer gets when one program starts using 99% of the processor? Yeah, that's what happens. And occasionally the fast loop code would not finish on time...then very bad things happen. I needed to get back at least a tiny bit of processor time for the slow loop. (Or, you know, get a faster controller or just settle for one motor. NO!)

Here are just a few of the many tricks I needed to pull for this to work:
  1. You don't need to calculate all three values for each motor. Since they are balanced sine waves, A+B+C = 0 applies. It is sufficient to look up and scale two, then do a simple subtraction to find the third. This was hugely important.
  2. It's faster to use a variable zero-sequence voltage. I made a horrible attempt to explain this in a previous post. It basically means not adding in an extra offset to center the sine waves at half the DC (battery) voltage.
  3. 99.999% of the time, the motor speeds aren't calculated in the same fast loop. This is because a Hall-effect sensor triggers the calculation, and the chance of two coming in at the same time is very slim. But when it happens, the two divisions can cause the fast loop to fail. The simple solution: don't even try processing two. If they both come in, make one wait until the next cycle. An extra 69 microseconds is not going to make much of a difference to the speed calculation.
The result is a fast loop with a lot more cushion:


Now even with a division, the loop finishes in 53 microseconds. This leaves plenty of time for the slow loop to sneak in some processing time each cycle. The slow loop, which only runs once every 8.2 milliseconds, has plenty of time, about 120 fast loop cycles, to finish. This is time-division multi-threading...on an embedded processor. Wheeeeee!

The slow loop, running in these gaps of left-over processing time, does all the real work. It measures currents, voltages, throttle signals, initiates wireless data communication, and, oh yeah, it runs the control loop. But that's a topic for Part 2 of this wrap-up. The important thing to mention now is that, despite being slow, the loop knows the position of the rotor at the moment when current is measured in all three coils. (Okay, again, only two are measured. The third is calculated from A+B+C = 0.) So, it can project the three currents onto the d-axis and q-axis just like in the example above. Thus, the field is oriented! We know exactly where it is, even when it is lagging behind our coil timing.

Here is an example of this measurement, done on the front scooter motor under load:


What you see there is exactly what you would expect with no control of the coil timing, if they are fired at the same angle no matter what. The q-axis current is held steady at 20A. (The controller does do this.) But as the motor increases in speed, some current starts to lag into the d-axis. You could, in fact, use this data to estimate the motor inductance. (Actually, you need to know its resistance as well.) Sparing you the details, which will be in the huge write-up, you get about 0.1mH, which is about right...at least the right order of magnitude.

So the current lag really does exist, even on motors with relatively low inductance. The rear scooter motor is even more susceptible, since it has more turns per winding and thus a higher inductance. But if you have an accurate measurement of the current as a vector, with the d-axis and q-axis components known, it is a simple matter to advance the coil timing in real time to make up for the lag. This is the topic for next week, though, since this post is already too long! Also because my plant threw a tire so I can't take any more data until I get more urethane glue...

I have awesome plants.

3 comments:

  1. What is the scalability of your controller? I know you said 1KW per motor but could it be increased to say 10KW with the addition of multiple IGBTs?

    And let me say, excellent explanation. I understood it completely and am looking forward to the "Control" writeup.

    ReplyDelete
  2. Conceptually, it's easy to scale up to larger MOSFETs, even up to the 10kW range. I've built a 10kW brushed controller with FETs and the same components. I don't know as much about IGBTs, but I have read in a few places that they would prefer a negative gate drive voltage for turn-off. My controller design isn't set up for that as-is, but I think the only part that would need to change is the power supply for the gate drivers.

    Thanks!

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete