Where Speed and Angle Come From: The Complete Resolver Signal Chain
July 13, 2026 · Mihna Neerulpan · views
How does a motor controller know its rotor angle - fresh, every switching period, at any speed including zero? This article walks the full chain in three acts: the resolver that stores a sine wave in copper geometry, the tracking observer that extracts angle and speed without ever differentiating, and the production pipeline that calibrates and time-shifts the result before the Park transform consumes it.
Act 1 - The Resolver
Field-oriented control has one non-negotiable input: the rotor angle, fresh, every switching period. The Park transform rotates measured phase currents into the rotor's frame using that angle; get it wrong by a few degrees and torque commands quietly turn into torque errors. In a production traction inverter the angle usually comes from a resolver - a sensor with no electronics inside, just copper and steel - and the way it encodes the angle is worth understanding properly, because the whole demodulation scheme falls out of one idea:
The resolver stores a sine wave in its geometry, not in time. It is a rotary transformer with one excited rotor coil and two stator windings placed 90° apart. How strongly the rotor coil couples into each stator winding depends only on where the rotor points: the coupling into one winding varies as sin θ, into the other as cos θ. That sinusoid exists with the shaft bolted stationary - it is a function of angle, drawn once in copper, valid forever. Drag the slider through a full turn and watch the geometry on the left produce the two frozen curves on the right.
Two windings are non-negotiable. One channel alone reading sin θ = 0.5 cannot tell 30° from 150°; the pair (cos θ, sin θ) is the x-y coordinate of an arrow pointing where the rotor points, and a coordinate pair defines a direction uniquely. Every sample instant, the controller reads one snapshot of both coordinates and computes atan2(sin‑sample, cos‑sample). No cycle of anything needs to complete, no frequency needs to be measured - which is why a resolver reports absolute angle even at zero speed, where every back-EMF-based scheme is blind.
The carrier ride: how the coordinates reach the ADC
A transformer only passes AC, so the rotor coil is excited with a carrier - in the drive this design comes from, 10 kHz, generated by the microcontroller itself and locked to the same counter that runs the PWM. What comes back on each stator wire is that carrier, amplitude-scaled by the spatial coupling: amplitude modulation, twice over. The angle information rides in the envelopes; the carrier is just transport.
Here is the part that trips most people up at first: when the motor spins, the envelope wiggles at the electrical frequency - speed-dependent - so how do you sample "at the carrier peak" if things are moving? Answer: the carrier's frequency never changes, and since the controller generates the excitation from its own clock, it knows exactly when every carrier peak occurs - the peaks are scheduled, not detected. Sampling at those fixed instants reads the envelope's instantaneous value directly: peak-sampling is the demodulation, for free. Speed only changes how fast the envelope slides underneath the fixed sampling instants.
So the full chain reads: rotor angle → two amplitudes, by transformer geometry (spatial AM) → carried on a self-generated 10 kHz carrier (temporal AM) → peak-sampled to strip the carrier → atan2 to strip the sine/cosine. Frequency of the carrier: constant. Frequency of the envelope: incidental. Information: amplitudes only, read in pairs. One line to keep: a resolver is a sine stored in copper geometry, read out by rotation, and mailed to the ADC on a carrier you generate yourself - spatial encoding, temporal transport.
Act 2 - The Tracking Observer
Part 1 ended with the controller holding one clean (sin Θ, cos Θ) pair per switching period. The obvious next move - atan2(sin, cos) every period - works, but poorly: the result is an unfiltered angle (every ADC noise count lands in it directly), it wraps awkwardly, and it contains no speed. Differentiating it to get speed makes things worse, because differentiation amplifies exactly the noise you were hoping to ignore: one LSB of angle flicker across one 100 µs sample is an enormous fake speed spike.
The industry-standard fix is the angle tracking observer (ATO): a small phase-locked loop that chases the measured angle with an internal model, filters without lag at constant speed, and produces speed as a byproduct of staying locked. Here it is with every internal signal called out:
Node by node
- ① The measured pair never gets converted to an angle. No atan2 anywhere.
- ② The error comes from the identity sin(Θ−Θ̂) = sinΘ·cosΘ̂ − cosΘ·sinΘ̂ - two multiplies and a subtract. It is the cross product of the measured unit vector and the observer's own unit vector, and the cross product of two unit vectors is the sine of the angle between them. Bonus: unlike an angle subtraction, it never glitches at the ±180° wrap - the geometry does the modular arithmetic.
- ③ K₁ turns error into acceleration. The loop implements Θ̂̈ = K₁(Θ−Θ̂) - acceleration proportional to negative position error, which is literally a mass on a spring.
- ④ The first integrator's state IS the speed. For the output to ramp (constant speed) with zero error, this integrator must hold a constant - and that constant is ω̂. Speed isn't computed from the angle; it's the state the loop must maintain to stay locked. This is why the ATO hands you a clean speed while differentiating an atan2 angle hands you amplified noise: the noisy operation, differentiation, appears nowhere in the loop.
- ⑤ The second integrator is pure kinematics: θ = ∫ω dt. Integration, not differentiation, because the signal flow runs speed → angle here - and integration attenuates noise instead of amplifying it.
- ⑥ K₂ is the shock absorber. A spring alone oscillates forever (two integrators in a loop = 180° of phase lag at every frequency; closed-loop poles at ±j√K₁, zero damping at any gain). The K₂ bypass adds a velocity-proportional term - a damper - turning the bell into an instrument.
- ⑦-⑧ The output angle regenerates the comparison vector, closing the loop.
A radio engineer would recognize node ② instantly - it is a phase detector, the comparison element of every phase-locked loop, and an unusually good one. A radio PLL has only one input signal, so its detector is a single multiplier: input × local oscillator = the useful sin(Δφ) error term plus an unwanted double-frequency ripple term that must be low-pass filtered away, costing phase margin. The resolver hands the loop both quadrature components, so the full IQ product can be formed - and in sinΘcosΘ̂ − cosΘsinΘ̂ the double-frequency terms cancel algebraically. No ripple, no post-filter, no lag: the geometric reading (cross product of two unit vectors = sine of the angle between them) and the PLL reading (an ideal IQ phase detector) are the same fact in two vocabularies.
How a plain gain becomes a zero, a damper, and a short-range forecast
The K₂ path deserves a closer look, because three different descriptions of it circulate and they are all the same thing. Start with the algebra: from the speed node there are two parallel roads to the angle - the integrator and the gain - and parallel paths add:
1/s + K₂ = (1 + K₂s)/s
That numerator is “the zero.” Nothing was built - it is just what fraction addition produces when a path that skipped an integration is summed with one that didn’t. And seen from the output side, the gain path is adding K₂·ω̂, which is exactly K₂ times the derivative of the integrator path’s output - so the “constant gain in parallel” is secretly “plus a bit of derivative,” and derivatives are early: the result x + K₂ẋ ≈ x(t+K₂) is a first-order forecast of where the angle will be K₂ seconds from now. A delay e−sT eats phase; this term is its mirror image, and gives phase back.
Why that rescues stability: a feedback loop can only sustain an oscillation at the one frequency where the round-trip gain equals exactly 1 - the crossover. Everywhere else, circulating signals either get crushed (gain ≫ 1 forces the error to zero) or fade (gain < 1). So the two integrators’ 180° of lag is only dangerous if it is still present at the crossover. The K₂ corner at ω = 1/K₂ is where the loop changes personality:
| frequency | dominant path | loop behaves like | phase |
|---|---|---|---|
| ω ≪ 1/K₂ | integrator (huge 1/ω) | two integrators | ≈−180° - harmless here: gain enormous, no crossover |
| ω ≫ 1/K₂ | direct gain | one integrator | ≈−90° - safe |
Design K₂ so the handoff sits below the crossover, and the loop arrives at the only frequency that could ring already wearing its safe, single-integrator personality. The 180° region still exists - parked at low frequencies where the loop simply bulldozes errors flat. The two integrators keep their gift (zero lag on ramps is a low-frequency property, exactly where the double-integrator personality still reigns), and the zero takes over exactly where stability is decided. Damping ratio, phase lead, and prediction horizon are one knob: K₂ = 2ζ/ωₙ.
Why this beats filtering an atan2 output
The closed-loop transfer function is H(s) = K₁(1+K₂s) / (s²+K₁K₂s+K₁). Two properties matter. DC gain is exactly 1: a constant angle is tracked exactly. And because the loop contains two integrators, it is a type-2 system: zero steady-state error to a ramp input - meaning at constant speed the estimate has no lag at all. Any low-pass filter on an atan2 output lags a moving target forever; the ATO only lags during acceleration, bounded by ω̇/ωₙ².
Matching the denominator to the standard second-order form gives the design equations: K₁ = ωₙ², K₂ = 2ζ/ωₙ. Pick a bandwidth and a damping ratio, and both gains fall out. A production traction inverter I know well runs ωₙ = 500 rad/s (≈80 Hz) with ζ = 0.84 - the tracking loop sits a factor of five below the fastest electrical fundamental it must follow (the next section explains why that works) and well over a decade below the sampling rate that feeds it.
One subtlety: ε = sin(Θ−Θ̂) has two nulls - alignment and anti-alignment. Only the aligned one is stable: near 180° the error slope reverses, the feedback turns positive, and the loop falls away from it. The observer cannot latch 180° out.
Choosing the bandwidth: rotation is free, only change costs
A natural worry: the electrical angle spins at up to 400 Hz - how can an 80 Hz observer keep up? Because of what the input is: at any constant speed the angle is a ramp, and a type-2 loop tracks a ramp of any slope with exactly zero error - the speed integrator holds ω, the output ramps in lockstep, and nothing in the loop oscillates at the rotation frequency. Rotation costs a state variable, not bandwidth. (Cruise-control version: holding 130 km/h takes no reflexes; reflexes are for when the grade changes.) Bandwidth is consumed only by deviations from constant speed, which boxes in the choice:
| pressure | direction | the check |
|---|---|---|
| acceleration lag = ω̇/ωₙ² | pushes ωₙ up | a full-range launch (0→2513 elec-rad/s in 1 s) costs 0.6° at 80 Hz - the real bound is the torque-limited ω̇ = pp·Tmax/J |
| sampling rate | pushes down | the loop runs at 10 kHz; staying a factor ≫10 below keeps discretization honest |
| sensor error harmonics | pushes down | gain/offset/orthogonality imperfections produce angle errors at 1× and 2× the electrical frequency - at cruising speed those land above an 80 Hz corner and are rejected; a wider observer would pass the sensor’s periodic lies straight into the Park angle |
So the modest bandwidth is not a compromise but a filter placed exactly where it helps: the true trajectory (a ramp) passes with zero error at any speed, while acceleration transients cost a bounded fraction of a degree and the sensor’s speed-proportional harmonics get attenuated precisely when they are fastest.
Act 3 - From Observer to Park Transform
Part 2 ended with a locked tracking observer producing a clean angle and speed. A textbook would stop there. Production firmware doesn't - because the observer's output is the right angle for the wrong place and the wrong time: it's the resolver's angle, not the magnet's, and it describes where the rotor was at the sample instant, not where it will be when the commanded voltage actually reaches the windings. The pipeline below is what a shipping traction inverter wraps around the observer to fix both.
Stage 2: the angle is calibrated, not trusted
Two correction terms ride on the raw observer output, both burned into EEPROM at end-of-line test. The static offset is obvious once stated: the resolver is bolted to the housing at some arbitrary angle relative to the magnet axis, so there's a constant to subtract. The field procedure to find it is elegant - inject pure d-axis current at various speeds and adjust the offset until the machine produces zero torque; if the frame is truly aligned, d-axis current makes no torque by definition.
The speed-proportional term is subtler. Every fixed time lag in the analog chain - the resolver's own excitation-to-output phase shift (up to ±10° per AN1942's parameter table), the anti-alias filter, the sampling point - is constant in time. But a constant time lag becomes an angle error proportional to speed: lag × ω. So the calibration takes the form θ − ω·k, with k found as the slope of a straight-line fit of the zero-torque offset versus rpm. A linear trendline through five dyno points captures the entire family of fixed-lag errors at once.
Stage 3: why a second, slower observer
The tracking observer's speed state is already clean - but “clean enough to close a current loop” and “clean enough to display, log, and feed torque-limit tables” are different standards. Production firmware runs a second PLL of the same structure at a tenth the bandwidth (~10-25 Hz) purely to smooth the speed signal for its slower consumers. The layering rule is the same one that sets every bandwidth in the system: each loop only needs to be fast relative to the dynamics it actually handles, and each stage sits roughly a decade below the one feeding it.
One more note on that wrap guard, because it is the visible tax of the detector swap. Angles live on a circle, but subtraction treats them as points on a line: when the input wraps 359°→1° while the internal estimate sits at 359°, the subtraction reports −358° where the true misalignment is 2° - the 11:59-versus-12:01 problem. The guard rejects any error that jumped more than π in one sample (no physical motion can do that in 100 µs), treating the wrap as a one-sample dropout; the tidier alternative computes the circular difference directly, e = mod(θ−θ̂+π, 2π)−π. Either way, the pattern is general and worth naming: production loops are a linear design wrapped in a thin shell of nonlinear housekeeping - wrap guards, anti-windup, initialization - whose only job is to deliver the loop safely into the small-error regime where the linear analysis is true.
The same loop in the s-domain: one denominator, two bandwidths
Writing the loop in Laplace terms makes a subtle property visible. The open loop is G(s) = K₁(1+K₂s)/s²; closing it gives
Θ̂/Θ = K₁(1+K₂s) / (s²+K₁K₂s+K₁) and Ω̂/Ω = K₁ / (s²+K₁K₂s+K₁)
Same denominator, different numerators. The poles belong to the loop, so every output shares them - but the speed node sits before the (1+K₂s) branch, so the speed transfer has no zero. The angle tap is therefore the fast output (the zero boosts high frequencies) and the speed tap the smooth one, from a single loop. And the pole positions live in polar coordinates: ωₙ is their distance from the origin (the tempo of everything), ζ their angle (the character). At ζ ≈ 0.7-1 the bandwidth is roughly ωₙ; push ζ far above 1 and the pair splits into a fast pole and a slow pole at ≈ωₙ/2ζ that dominates everything - which is exactly the speed observer’s trick: ωₙ = 160 rad/s but ζ = 2.5 parks the slow pole at ≈5 Hz, turning the loop into a heavy smoother by angle rather than by radius, while the type-2 structure still guarantees zero lag at constant speed - smoothing a low-pass filter could only buy at the price of trailing every ramp forever.
Stage 4: which angle should the inverse Park use?
Here is the question Part 1 planted. The currents you sampled and the angle you measured describe the machine at the sampling instant. The voltage you compute from them will not reach the motor until the duty registers reload at the next period boundary, and its average lands at the middle of the period after that - roughly 1.5 Tₛ after the sample (the exact constant depends on where in the period your sample sits; 1.75 Tₛ in one production implementation). At 400 Hz electrical, 1.5 Tₛ of rotation is over 20 electrical degrees - apply the sampled angle directly to the inverse Park and every voltage vector you command lands ~22° behind the rotor, which reads as lost torque and phantom d-axis current.
The fix costs one multiply-add: θ_ahead = θ + ω·k·Tₛ - advance the angle by the speed times the known latency. Note what made this possible: the observer supplies a clean ω to extrapolate with (Part 2's “speed for free” paying rent), and the latency is known and constant because every event in the chain is scheduled by one crystal (Part 1's locked clocks paying rent). The forward Park - rotating measured currents into the rotor frame - keeps using the uncorrected angle, because the currents were measured at the sample instant; only the command path looks ahead. One angle, two consumers, two timestamps.
Where does k come from? It is read off the timing diagram, not tuned, and it splits into two pieces. The 1.0: freshly computed duty values wait in double-buffered registers until the next period boundary (updating a compare register mid-period could produce a malformed pulse), so a full period passes between sampling and the new duty taking effect, however fast the math ran. The 0.5 is subtler and trips most people up: the voltage is not delivered at an instant - it is a pulse pattern spread across the whole period, and the currents (far slower than the switching) respond to its average. A quantity spread uniformly over an interval acts, for anything slow, like a lump delivered at the interval’s center of mass - the midpoint, half a period in. (Formally: a zero-order hold has exactly the phase of a Tₛ/2 delay.) Salary paid continuously through the month is financially a lump sum on the 15th. And note a quiet benefit of center-aligned PWM: the pulses are symmetric about mid-period at every duty cycle, so the 0.5 is a true constant - edge-aligned modulation would make the centroid drift with duty, and the “constant” would lie.
The chain, complete
Reading the whole series backwards: the inverse Park uses an angle extrapolated to when the voltage will land (Part 3), computed from an observer that tracks without lag and yields speed as its lock variable (Part 2), fed by amplitude pairs that a self-generated carrier delivered to fixed sampling instants (Part 1), encoding a sine that was never a signal at all - just the geometry of two windings 90° apart (Part 1). Every stage hands the next exactly what it needs, and nothing anywhere differentiates a measurement.
References
Freescale/NXP AN1942, “56F80x Resolver Driver and Hardware Interface” - resolver theory, parameter table (including the ±10° phase shift behind the speed-proportional calibration), analog front end. Freescale/NXP AN3943, “Using the Resolver Interface eTPU Function” - the tracking observer's digital implementation and design equations.