The satellite's true orientation is one rigid-body quaternion q, integrated every frame from the world-frame angular velocity ω via the exact kinematic equation
q̇ = ½ · Ω(ω) · q, Ω(ω) = (0, ωx, ωy, ωz)
ω̇ = τ / I (I = scalar moment of inertia)
Both controllers are PD loops that output a torque τ, but they measure the error differently:
- Quaternion PD — computes the error quaternion qe = qtarget·q-1, converts it to a single rotation axis and angle (θe), and sets τ = Kp·(axis·θe) − Kd·ω. This is coordinate-free: it works identically at every orientation, including straight through 90° pitch.
- Naive Euler PD — decomposes the current attitude into yaw/pitch/roll (YXZ order) and runs three independent PD loops, one per axis, then projects each correction onto that axis's real physical direction in space: yaw about world Ŷ, pitch about (Ŷ-rotated) X̂, and roll about the satellite's own current Ẑ. Those three axes are only mutually independent when pitch is away from ±90°.
At pitch θ = 90° the roll axis (Ẑ rotated by yaw then pitch) swings around to point along the very same line as the yaw axis (Ŷ) — this is gimbal lock: two of the three "independent" Euler controllers are now commanding the same physical rotation, so a correction meant for roll fights or duplicates the yaw correction and one true degree of freedom of control is lost. The two projected gimbal rings below visualize exactly this: the roll ring's plane swings to overlap the yaw ring's plane as pitch approaches ±90°. The lock meter shows |ŷaw·r̂oll| — it climbs toward 100% in both modes, but only the naive Euler controller actually loses authority there; the quaternion controller keeps converging smoothly because it never decomposes into three separate angles at all.
This is exactly why real spacecraft ADCS flight software (and inertial nav systems before it) moved to quaternions: no singular attitude exists for a unit quaternion, so there is no orientation at which the controller's own math breaks down.