When implementing control for a sinusoidal permanent magnet synchronous motor, a commonly used element is a hard-switched, two-level, three-phase inteverter. That is, the three motor phases are driven by three half-bridges, and PWM techniques are implemented to drive the required (sinusoidal) output waveforms into each phase.

This post will not attempt to explain the underlying theory of motor control in
any great depth–there are plenty of other books, articles, and application
notes for that^{1}, some with much prettier graphics–but since I’m in the
midst of implementing one such PWM technique (Space Vector
Modulation) I thought
I’d capture my paper notes into an interactive electronic form. Think of it as
a snapshot of my design process, more than an instructive article.

## y u do dis?

The main idea behind SVM is that instead of naively modulating each phase independently–that is, simply applying a sinusoidal voltage to each leg–better results can be had by analyzing and controlling the entire inverter as a single unit. The result is the voltage-to-ground waveforms for each phase no longer look like sinusoids, but the voltage across each motor coil is still able to be driven sinusoidally.

This is primarily accomplished by using switching patterns which manipulate the common-mode voltage present at the motor’s center (the so-called neutral voltage). The end result is that we can apply “more of” the bus voltage to the motor–that is, a larger amplitude of voltages applied across the motor coils without having to increase the bus voltage. Free power!

## So how does it work?

Without getting into too much mathematical notation, “space vector” modulation is, perhaps unsurprisingly, built around the notation for “space vectors.” Space vectors are very similar to phasors from more conventional circuit analysis–a sort of convenient two-dimensional mathematical notation for solving your circuit analysis problems.

Our motors are three-phase machines, and so the classic space vector picture looks something like this:

That is, in the two dimensional space formed by (α, β):

- The first motor phase (u) is aligned with the x axis.
- The second motor phase (v) is +120 degrees from the first phase.
- The third motor phase (w) is -120 degrees from the first phase.

Then as the motor “rotates” though physical space, the desired torque vector (as a space vector) also rotates. The magnitude separately expresses the desired torque, and this all makes a nice peachy abstraction for computing inverter outputs.

As mentioned above, our inverter has three switches with two levels each, meaning we can place the inverter in any one of 8 possible states:

State | u | v | w |
---|---|---|---|

S_{0} |
L | L | L |

S_{1} |
H | L | L |

S_{2} |
H | H | L |

S_{3} |
L | H | L |

S_{4} |
L | H | H |

S_{5} |
L | L | H |

S_{6} |
H | L | H |

S_{7} |
H | H | H |

By itself that table isn’t super helpful, but if you start thinking about the current induced through the motor coils you can begin to build a graphical intuition about what each state means. Rather than worry about absolute units right now, we’ll just use normalized numbers.

State | i_{u} |
i_{v} |
i_{w} |
---|---|---|---|

S_{0} |
0 | 0 | 0 |

S_{1} |
1.0 | -0.5 | -0.5 |

S_{2} |
0.5 | 0.5 | -1.0 |

S_{3} |
-0.5 | 1.0 | -0.5 |

S_{4} |
-1.0 | 0.5 | 0.5 |

S_{5} |
-0.5 | -0.5 | 1.0 |

S_{6} |
0.5 | -1.0 | 0.5 |

S_{7} |
0 | 0 | 0 |

Mapping those states to the picture above, we can see that for example, the
current in S_{1} projects entirely across the u phase. Similarly, we
can see S_{3} projects across v, and S_{5} projects with w.
What about the states in between? Well, they turn two phases half-on, so they
project “in between” the primary coil vectors.

We can visualize each of these states on the space vector diagram, similar to the one we just saw above:

The keen observer will note the absence of S_{0} or S_{7},
since those states drive all 3 phases to the same voltage, and thus no current
flows. (You can imagine them as arrows that start and end at the origin.)

Of course, we also need to command the motor with *continuous* sinusoids, not
just the 6 steps represented by these axes^{2}. So, we need to combine them proportionally in order to to
reach vectors other than theses 6. Lastly, we don’t command maximum torque at
all times, so the total magnitude of the arrow can vary. In short, our command
can come in the form of an arbitrary space vector, and we have to combine the
right states to produce it.

## How do we combine states?

The general idea of PWM is that if we switch a transistor fast enough, the net effect on the circuit is the same as a signal proportional to the duty cycle. Extending that idea, if we need to implement a command that’s part-way between two of the six states we can command directly, we can PWM from one state to the next, rather than just on/off.

More specifically we’ll synchronize the PWM periods of all three outputs, and then within a single PWM period, we’ll set it up so that the ratio of time spent in each state “adds up” to the desired command. How do we know what the right ratios are?

Well, from our exploration above we know the answer will take the form of some
combination of two inverter states. We can identify which states by
identifying which sextant the command is in–this can be done based on `atan2`

and checking the angle, or you can avoid the expensive trigonometric operation
by being a bit clever^{3}.

Either way, given the two component states, we “just” need to decompose the
space vector into terms of those component states. In “ordinary” Cartesian
geometry we do this by projecting onto the x and y axes, but that isn’t what
we’re doing here–we want to project onto S_{n} and S_{n +
1}, and those two vectors aren’t orthogonal. That said, they *are*
guaranteed to be 60° apart, so we have a known angle to work with, and the rest
is just boring mechanical trigonometry.

I won’t make you suffer through the derivation, but here’s an interactive plot showing you the results:

## Wiggling Pins

So now we have two components, indicating the relative amount of time to spend in each of the two active states. How to we make our microcontroller dance to the particular tune ordered up by these components?

Most microcontrollers these days have hardware for generating “center-aligned”
PWM signals. That is, a piece of hardware that counts `0`

to `n`

and back to
`0`

, outputting a signal when the counter is greater than some threshold `c`

.

Ignoring for now the specifics of whether the counter stops on or before `n`

,
etc., this gives us a PWM waveform with period of approximately `2 * n`

counts
and duty cycle `2 * (n - c) / (2 * n) = (n - c) / n`

. In the simulation above
we set `c = 0.8 * n`

, giving us about a 20% duty cycle.

Given a tool such as the center-aligned PWM mode of a counter, we can arrange for the three outputs to spend time in the desired inverter states. We’d like to avoid switching transistors more often than twice a cycle, and we’d like to avoid switching more than one of them at a time.

If we’re in the first sextant we can assume we start the PWM period
“idle”–that is, in either S_{0} (`LLL`

) or S_{7} (`HHH`

). We
need to spend some time in S_{1} (`HLL`

) and also S_{2}
(`HHL`

). We need to return to the same idle state at the end of the period.

Start in S_{0} |
Start in S_{7} |
---|---|

S_{0} - `LLL` |
S_{7} - `HHH` |

S_{1} - `HLL` |
S_{1} - `HLL` |

S_{2} - `HHL` |
S_{2} - `HHL` |

S_{0} - `LLL` |
S_{7} - `HHH` |

With this naive approach, no matter which idle state we choose we wind up with
at least one transition that toggles two transistors. We can do better,
though! If we divide the “idle” time across *both* null states, we can achieve
the desired goals.

Start in S_{0} |
Start in S_{7} |
---|---|

S_{0} - `LLL` |
S_{7} - `HHH` |

S_{1} - `HLL` |
S_{2} - `HHL` |

S_{2} - `HHL` |
S_{1} - `HLL` |

S_{7} - `HHH` |
S_{0} - `LLL` |

S_{2} - `HHL` |
S_{1} - `HLL` |

S_{1} - `HLL` |
S_{2} - `HHL` |

S_{0} - `LLL` |
S_{7} - `HHH` |

Depending on if we treat S_{0} or S_{7} as the initial state,
we have to reverse the order of the states. This also holds true when we move
between sextants–even and odd numbered sextants will have reversed ordering of
the component states. Either way, there’s always an ordering that will work
for each sextant.

## Kirk or Picard?

Given that we can make sequences that start with S_{0} *or*
S_{7}, how do we pick between the two? Well, there’s one “leaky” piece
of the SVM abstraction, which is that our inverter can only measure the current
through each phase when the phase output is “low.”

If we choose the S_{0}-first approach, that places the best current
measurement time at the boundary between PWM cycles. That is, we would do
something like the following.

- Wait for the PWM cycle to start.
- Trigger an ADC reading (probably using automatic hardware triggering on the “reload” event).
- When we receive the ADC result, run our control loops and enqueue our
desired output for the
*next*cycle. - Wait for the PWM cycle to end.
- (Have the next cycle outputs loaded automatically by hardware.)

This works, but the total *delay* from measurement -> output change is equal to
the PWM period. That is, given common PWM frequencies (15 - 65 kHz), we could
be adding as much as 66 microseconds of delay into our control path!

Delay is Enemy #1 for control systems–think of when you turn on your shower in the morning and you have to wait a while for the water to actually warm up. The less delay, the better.

Of course, we can increase the PWM frequency, but since our transistors are not
“ideal” devices they consume power every time they switch–faster switching =
more power wasted as heat instead of moving the motor. There are no free
lunches in engineering^{4}. Either way, we’re unlikely to do much better than
~15.4 microseconds (@ 65 kHz).

If we choose S_{7}-first though… now we’ve set ourselves up for a
challenge. The new order this this:

- Wait for the PWM cycle to start.
- Wait for half of the PWM cycle to elapse.
- Trigger an ADC reading (probably using automatic hardware triggering on the “top” event).
- When we receive the ADC result, run our control loops and enqueue our
desired output for the
*next*cycle. - Wait for the PWM cycle to end.
- (Have the next cycle outputs loaded automatically by hardware.)

We’ve halved the delay! Also, halved the amount of time we have to compute the next cycle’s outputs. The two go hand-in-hand, and if you imagine a 65 kHz PWM frequency this leaves us only ~7.7 microseconds to sample the ADCs and compute our next answer–~1300 clock cycles at 170 MHz.

Which one will I use? Check back later. Certainly minimizing delay is valuable, but so is being able to complete your computations on time, every time. Once I get more of the controller implemented and get a better feel for how speedy everything is, I’ll make the final call.

## One Last Detail

We had vector components, and now we have some notion of PWM duty cycles, but we haven’t connected the two. How do they relate?

We saw that the vector components give us the relative active-state times for each phase. We can assume the null-state time will consume the remainder of the PWM cycle. That is,

_{pwm}= T

_{0}+ T

_{k}+ T

_{k + 1}

When we were defining the space vector… space… I was intentionally pretty vague about absolute magnitudes. It turns out that when mapping between the scalar vectors (u,v,w) and space vectors there’s an arbitrary scaling factor involved in the transformation. We can ignore that factor (set it to 1), or choose it such that the magnitude of the vectors are equivalent (perhaps reinforcing the “voltage command” abstraction), or even choose it such that the power of the waveforms is equal.

This is the part where my understanding is the shakiest, but my current impression is:

- Since the scaling factor is relatively arbitrary, we
*could*apply some “ideal” scaling factor, - but we have other non-ideal effects, like dead time insertion, which mean we’ll be “wrong” anyway,
- so we’re going to throw a feedback controller around the outside of this whole loop either way,
- and the tuning of that controller should basically compensate any factor we insert here.

So assuming that holds true for the moment, we’re free to scale the actual
interpretation from component directly into an on-time ratio–that is, a duty
cycle. This intuitively lines up well: if you click at (1.0, 0.0) on the
picture above (i.e., 100% time spent in S_{1}), and you can see how it
makes sense to interpret that as a duty cycle.

There’s just one tiny problem: if you’re devious, you can click, for example,
in the very upper-right corner, somewhere like (0.99, 0.99). Our decomposition
will give us something like: 0.42 * S_{1} + 1.14 * S_{2}.

These values are nonsensical: firstly, we can’t apply 114% duty cycle to any
given state, and secondly, we certainly cannot assign 156% duty cycle overall.
*In fact*, we can’t even assign *100%* duty cycle: remember how we need the
switches in ‘low’ state to measure current? We can “get away” with only
measuring two of the phases within a quadrant, since the sum of all the
currents must be 0. Even still, there are problematic cases, like if we’re
near 100% duty cycle aligned with S_{2} and using the
S_{0}-first modulation scheme, we’ll have two of the switches toggling
high -> low right at the end of our PWM cycle. We must have enough time in
S_{0} for the current measurements to stabilize and be sampled by our
ADC.

The good news is that for the space vector to continue pointing “in the right
direction” the *ratio* of the two on-times need not change. We might not be
able to deliver the full value of the command’s magnitude, but we can get “as
close as possible.” The trick is if:

_{k}+ T

_{k + 1}> (maximum duty cycle)

Then we simply re-scale them:

_{k}= (maximum duty cycle) * T

_{k}/ (T

_{k}+ T

_{k + 1})

_{k + 1}= (maximum duty cycle) * T

_{k + 1}/ (T

_{k}+ T

_{k + 1})

Working through our (0.99, 0.99) example, assuming a maximum duty cycle of 95%, we get:

_{1}= 0.95 * 0.42 / (0.42 + 1.14) = ~0.26

_{2}= 0.95 * 1.14 / (0.42 + 1.14) = ~0.69

_{0+7}= 1.0 - T

_{1}- T

_{2}= ~0.05

That has certainly made the duty cycles more reasonable, but did it preserve (modulo scaled magnitude) the intended torque vector? I’ve reproduced the interactive command plot found above (they’re linked!), but with the duty cycle clamping in place. When the vector is duty cycle limited, a new green arrow will be drawn showing the effective command after clamping:

Seems like all my math checks out–at least in theory–so I’m going to go off and reduce it to practice!

While much more math-heavy than most application notes, I found this thesis to be a useful primer since it helped me get comfortable with the

*meaning*behind space vectors, rather than just providing a reference implementation.^{[return]}Of course, there

*exist*six-step modulation schemes, primarily for trapezoidal motors, but that’s a whole different topic from what we’re trying to do here.^{[return]}The best reference for implementation tricks I’ve found is probably this one.

^{[return]}Well, your employer might

*buy*you lunch, but even then it’s not free, it’s just free-to-you.^{[return]}