Space Vector Modulation

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 that1, 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
S0 L L L
S1 H L L
S2 H H L
S3 L H L
S4 L H H
S5 L L H
S6 H L H
S7 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 iu iv iw
S0 0 0 0
S1 1.0 -0.5 -0.5
S2 0.5 0.5 -1.0
S3 -0.5 1.0 -0.5
S4 -1.0 0.5 0.5
S5 -0.5 -0.5 1.0
S6 0.5 -1.0 0.5
S7 0 0 0

Mapping those states to the picture above, we can see that for example, the current in S1 projects entirely across the u phase. Similarly, we can see S3 projects across v, and S5 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 S0 or S7, 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 axes2. 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 clever3.

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 Sn and Sn + 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 S0 (LLL) or S7 (HHH). We need to spend some time in S1 (HLL) and also S2 (HHL). We need to return to the same idle state at the end of the period.

Start in S0 Start in S7
S0 - LLL S7 - HHH
S1 - HLL S1 - HLL
S2 - HHL S2 - HHL
S0 - LLL S7 - 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 S0 Start in S7
S0 - LLL S7 - HHH
S1 - HLL S2 - HHL
S2 - HHL S1 - HLL
S7 - HHH S0 - LLL
S2 - HHL S1 - HLL
S1 - HLL S2 - HHL
S0 - LLL S7 - HHH

Depending on if we treat S0 or S7 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 S0 or S7, 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 S0-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 engineering4. Either way, we’re unlikely to do much better than ~15.4 microseconds (@ 65 kHz).

If we choose S7-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,

Tpwm = T0 + Tk + Tk + 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 S1), 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 * S1 + 1.14 * S2.

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 S2 and using the S0-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 S0 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:

Tk + Tk + 1 > (maximum duty cycle)

Then we simply re-scale them:

Tk = (maximum duty cycle) * Tk / (Tk + Tk + 1)
Tk + 1 = (maximum duty cycle) * Tk + 1 / (Tk + Tk + 1)

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

T1 = 0.95 * 0.42 / (0.42 + 1.14) = ~0.26
T2 = 0.95 * 1.14 / (0.42 + 1.14) = ~0.69
T0+7 = 1.0 - T1 - T2 = ~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!

  1. 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.

  2. 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.

  3. The best reference for implementation tricks I’ve found is probably this one.

  4. Well, your employer might buy you lunch, but even then it’s not free, it’s just free-to-you.