Clarke and Park
A three-phase motor gives you three currents that never stop moving. Every one of them crosses zero a hundred times a second at a modest speed, and none of them individually tells you anything you can act on. You cannot put a PI controller on — by the time it has corrected an error, the target has moved.
Field-oriented control’s first move is to stop measuring the machine from the stator’s point of view and start measuring it from the rotor’s. Ride along with the rotor and the currents stop oscillating: they become two steady numbers, one that makes torque and one that does not. Then a PI controller works, because there is finally a constant to regulate.
Getting there takes two rotations of the coordinate system. Clarke collapses three axes into two. Park spins those two to match the rotor.
One vector, three descriptions
Before the algebra, the picture. The figure below shows a single current space vector. Drag anywhere in the circle to turn the rotor; drag the arrowhead to change where the current sits relative to it.
Turn the rotor and watch the three panels. The abc traces sweep through their sinusoids. The αβ traces do too — Clarke removes a redundant axis but changes nothing about the time behaviour. The dq traces are flat lines.
That is the entire idea. Everything below is the bookkeeping that makes it true.
Why three phases are one too many
The three stator windings sit 120° apart in space. Each carries a current, and each produces a magnetomotive force along its own axis. The machine does not respond to the three currents separately — it responds to their vector sum, a single MMF pointing somewhere in the airgap.
So three numbers are describing a two-dimensional thing. There is one degree of freedom too many, and in a star-connected machine with an isolated neutral it is pinned down for us:
There is nowhere for a common-mode current to go. Two measurements determine the third, which is why plenty of drives populate only two current shunts.
On the bench That constraint is a property of the wiring, not of the maths. Break the neutral open and it holds; tie the neutral to a source and it does not. Delta- connected machines satisfy it for line currents but circulate a third-harmonic current inside the delta that no line measurement will ever show you.
The Clarke transform
Project the three phase axes onto a plain orthogonal pair. Put α along phase and β 90° ahead of it:
The first two rows are geometry: on the α row and the matching sines on the β row. The third row is the zero-sequence component, the average of the three phases. For a balanced set it is zero, and in a two-shunt drive it is assumed zero.
That 2/3 out front
The is a choice, and it is the one place engineers reading two different textbooks start disagreeing.
With — the amplitude-invariant convention — a 10 A peak phase current gives A. Magnitudes carry straight through, which is what you want when the number in your debugger has to mean amps. The cost is that power picks up a factor: .
With — the power-invariant convention — the matrix is orthonormal, with no stray factor, and the inverse is just the transpose. Machine theory papers prefer it. Peak magnitudes come out larger than the physical phase current.
Neither is wrong. Mixing them silently is, and it is a genuinely common bug: your torque constant lands 22% off and everything still looks like it works.
On the bench This site uses amplitude-invariant scaling everywhere, because that is what drive firmware almost always ships. Every equation, plot and code sample here assumes it unless the text says otherwise.
The two-input form
Substituting gives the version that actually appears in an interrupt handler:
One multiply and an add. The α row collapses to unity because — a pleasant accident of the amplitude-invariant scaling, and one more reason firmware likes it.
Note what you gave up: with only two measurements there is no zero-sequence output, so a common-mode offset in your current sensing becomes invisible. It does not disappear — it lands in α and β as a DC error, and shows up downstream as a torque ripple at the electrical frequency.
The Park transform
Clarke left us in a frame that is still standing still while the rotor turns. Park spins the frame to catch up.
Let be the electrical angle of the rotor’s d-axis, measured from α. The d-axis points along the permanent-magnet flux; the q-axis leads it by 90°.
That is a rotation by : we are not moving the vector, we are moving the observer. In the rotating frame a vector that was spinning at synchronous speed now sits still.
Combining with Clarke, for a balanced set of amplitude whose vector sits at angle from the d-axis:
Both constants. Sinusoids in, DC out — provided is right.
Everything depends on θ
Park is only as good as the angle you feed it. This is where real drives fail, and it is worth being blunt about it: an incorrect does not produce an obviously wrong result. It produces a plausible one.
A constant angle error rotates the commanded current within the rotor frame. Ask for pure q-axis current and you get
At you lose 1.5% of your q-axis current — invisible — while pushing 17% of it into the d-axis, where it heats the machine and weakens the field. The drive spins. It just quietly runs at the wrong operating point.
| q-axis current retained | Current diverted to d | |
|---|---|---|
| 5° | 99.6% | 8.7% |
| 10° | 98.5% | 17.4% |
| 20° | 94.0% | 34.2% |
| 45° | 70.7% | 70.7% |
| 90° | 0% | 100% |
That table is geometry, and it is exact. Turning it into torque takes one more assumption — that torque comes only from — which holds for a surface-magnet machine and not for a salient one. On a salient machine the diverted d-axis current is negative, which produces reluctance torque and partly compensates the loss; at 10° it can more than compensate it. The current loop page measures this.
A lagging angle — the usual case, from sampling delay and computation time — is and therefore grows with speed. A drive that is perfectly tuned at 500 rpm and mysteriously loses efficiency at 4000 rpm is almost always paying this tax. The fix is angle prediction, not gain tuning.
The inverse path
The controller works in dq and the inverter switches in abc, so the same two rotations run in reverse on the output side.
Inverse Park is the transpose of Park — rotations are orthogonal, so the inverse is free. Inverse Clarke is not the transpose of Clarke under amplitude-invariant scaling, because the forward has to be undone. Under power-invariant scaling it is. One more reason to be certain which convention a code sample uses before you paste it.
On the bench Use the same for forward and inverse Park within one control cycle, and predict it forward by roughly 1.5 PWM periods for the output side — the voltage you compute now will not be applied until the next update, by which time the rotor has moved. Skipping this is the single most common reason a current loop that looks fine on paper goes unstable at high speed.
In code
The firmware-shaped version. Note that and are computed once and reused four times — on a machine without an FPU, the trig, not the transforms, is your ISR budget.
/* Amplitude-invariant, two-shunt, star-connected machine. */
static void foc_forward(float ia, float ib, float sin_t, float cos_t,
float *id, float *iq)
{
const float i_alpha = ia;
const float i_beta = (ia + 2.0f * ib) * ONE_OVER_SQRT3;
*id = i_alpha * cos_t + i_beta * sin_t;
*iq = -i_alpha * sin_t + i_beta * cos_t;
}
static void foc_inverse(float vd, float vq, float sin_t, float cos_t,
float *va, float *vb, float *vc)
{
const float v_alpha = vd * cos_t - vq * sin_t;
const float v_beta = vd * sin_t + vq * cos_t;
*va = v_alpha;
*vb = -0.5f * v_alpha + SQRT3_2 * v_beta;
*vc = -0.5f * v_alpha - SQRT3_2 * v_beta;
}
The TypeScript that drives every figure on this site lives in
src/core/transforms.ts and is validated against the
Python reference in python/transforms.py — same conventions, same numbers, to
1e-12.
What to take away
- Three phase currents describe a two-dimensional vector. Clarke drops the redundancy; it does not change the dynamics.
- Park changes the observer, not the signal. Sinusoids become DC only because you chose to watch from the rotor.
- and are now controllable by ordinary PI loops. That is the payoff, and the rest of FOC is built on it.
- The transforms are trivially cheap and essentially never the bug. The angle feeding them is where drives actually go wrong.
Next: what and physically do to the machine — torque production, saliency, and why is the right default right up until it isn’t.