Articles
Quaternion Interview Questions: Rotation Order, Coordinate Frames, and Interpolation
Practise quaternion interview questions with explicit rotation order, sensor-to-world frames, q and minus q, and interpolation counterexamples.
Quaternion interview questions often expose a convention error rather than a failure to remember a formula. You can normalize all four numbers and still rotate around the wrong axis, compose transformations in the wrong order, or interpolate between two representations of the same orientation through an invalid zero quaternion.
The useful answer begins with a contract: component ordering, rotation meaning, frame direction, and multiplication convention. Then demonstrate it on a vector whose expected result you can compute. Official API facts below come from SciPy's rotation documentation. Worked derivations and interview advice are original explanations using stated conventions. Candidate-report boundary: these are independent practice questions, not reported prompts from a named employer.
For related practice, Implement 3x3 matrix transforms and rotation exercises transformation bookkeeping. A 2D matrix task does not replace the 3D quaternion reasoning developed here.
Question 1: What does this four-number quaternion mean?
Suppose someone hands you [0, 0, 0, 1]. Is it the identity rotation? You cannot answer without knowing the component order.
Official fact: SciPy Rotation.from_quat defaults to scalar-last (x, y, z, w) and supports scalar-first through scalar_first=True. It interprets inputs as active rotations and normalizes them. That behavior is a library contract, not a convention shared automatically by every graphics, robotics, or simulation API.
For this guide, use right-handed axes, active rotations on column vectors, Hamilton quaternion multiplication, and scalar-last storage. A rotation of angle theta about a unit axis u has vector part u * sin(theta/2) and scalar part cos(theta/2).
Under those conventions, identity is (0,0,0,1). Positive 90 degrees about z is (0,0,s,s), where s = sqrt(2)/2. It sends the x basis vector to the y basis vector. If the same tuple is interpreted as scalar-first, its meaning changes completely.
An interview answer should state both representation and effect: “I will read this as x, y, z, w, and verify that applying it to positive x produces positive y.” That gives the interviewer an observable claim, not just a naming preference.
Also ask what happens to invalid input. A zero quaternion cannot be normalized into an orientation. A very small or nonfinite input needs an explicit boundary policy. Silently normalizing a malformed sensor message can turn an integration error into an apparently valid state.
Question 2: Which rotation happens first in a product?
Official fact: SciPy's rotation multiplication documentation defines p * q as q followed by p, equivalent to R_p @ R_q. Rotation composition is generally noncommutative.
Worked derivation: take fixed-axis positive 90-degree rotations R_x and R_z, and start with e_y = (0,1,0):
| Composition | First intermediate vector | Final vector |
|---|---|---|
R_z R_x e_y | R_x e_y = e_z | R_z e_z = e_z |
R_x R_z e_y | R_z e_y = -e_x | R_x (-e_x) = -e_x |
These outputs disagree, so this fixture catches a reversed multiplication order. Testing only identity would not. Testing rotations about the same axis can also hide the problem because those particular rotations commute.
The quaternions are q_x = (s,0,0,s) and q_z = (0,0,s,s). Their Hamilton products are:
q_z * q_x = (0.5, 0.5, 0.5, 0.5)
q_x * q_z = (0.5, -0.5, 0.5, 0.5)
For vector/scalar pairs (v_a,w_a) and (v_b,w_b), the product has vector part w_a v_b + w_b v_a + v_a cross v_b, and scalar part w_a w_b - v_a dot v_b. The cross-product sign explains the changed y component.
Do not conclude that the words “yaw then pitch” determine this order. They might describe rotations about fixed world axes or about moving local axes. Ask for the axes and whether each instruction acts in the world frame or the current body frame; then write the corresponding matrices or quaternion products.
Question 3: How do you compose sensor, body, and world frames?
Use a naming rule that includes direction. Define R_AB to map coordinates expressed in frame B into coordinates expressed in frame A. Thus:
v_A = R_AB v_B
R_WS = R_WB R_BS
The second expression maps sensor coordinates into body coordinates, then into world coordinates. The middle B labels connect. Treat this as a dimension check before doing arithmetic.
Original fixture: a sensor vector is v_S = e_y. The sensor-to-body rotation R_BS is positive 90 degrees about x. The body-to-world rotation R_WB is positive 90 degrees about z. Then v_B = e_z, and v_W = e_z. Reversing the factors yields -e_x, the counterexample above.
Now distinguish a direction from a point. A direction changes through rotation alone. A point also needs the origin displacement:
p_W = R_WB (R_BS p_S + t_BS) + t_WB
Here t_BS is the sensor origin expressed in body coordinates, and t_WB is the body origin expressed in world coordinates. A quaternion does not carry either translation.
For example, use p_S = e_y, t_BS = (1,0,0), and t_WB = (10,0,0). The body point becomes (1,0,1). Rotating it about z and adding the world translation produces (10,1,1). Adding t_BS directly in world coordinates would give a different answer because that vector must also pass through R_WB.
A strong answer names the source and destination frame of every intermediate value. If the API instead supplies world-to-body orientation, invert it before this chain. For a unit quaternion, inversion is conjugation; for a general nonzero quaternion, conjugation must be divided by the squared norm.
A follow-up might ask where an incremental rotation belongs. If the current orientation maps body coordinates to world coordinates, a small rotation expressed about world axes multiplies on the left under this guide's convention. A small rotation expressed about body axes multiplies on the right. The two increments need not produce the same final orientation even if their numerical quaternion components match.
Make this answer concrete by returning to the quarter-turn fixture. A body that already has the z rotation receives an x increment. A world-axis increment gives R_x R_z; a body-axis increment gives R_z R_x. Applied to the stored y vector, the results are negative x and positive z respectively. This is why an instruction such as “rotate the robot another 90 degrees” is incomplete without an axis frame. Resolve that ambiguity before choosing the multiplication side.
Question 4: Why do q and minus q describe the same orientation?
Official fact: SciPy Rotation.as_quat documents the two-to-one representation of rotations and provides a canonical sign option. Equality of component arrays is therefore not sufficient for equality of orientation.
Worked reasoning: applying a unit quaternion to a vector uses q (0,v) q_conjugate. Replacing both quaternion factors with their negatives cancels the signs. The rotated vector stays the same.
That has practical consequences. A stream alternating between q and -q may have no physical jump even though its numerical components change sharply. Component-wise differences, averaging, and interpolation must account for representation sign.
For unit inputs, an orientation comparison can use the magnitude of the quaternion dot product. A relative angular distance is 2 * acos(clamp(abs(dot(q0,q1)),0,1)). The absolute value handles the sign equivalence; clamping protects against small floating-point overshoot. This formula assumes valid normalized inputs.
Canonicalization is useful when you want deterministic serialization. Temporal sign continuity serves a different purpose: choose each incoming representation so its dot product with the previous chosen representation is nonnegative. Do not assume that canonicalizing every sample independently guarantees smooth component trajectories across every boundary.
Question 5: Why can linear interpolation fail?
Take identity q0 = (0,0,0,1) and its equivalent negative q1 = (0,0,0,-1). Their component-wise midpoint is (0,0,0,0), which has no orientation. Normalizing afterward cannot repair division by zero.
Preparation advice: for shortest-path interpolation between unit quaternions, first inspect the dot product. If it is negative, negate one endpoint. The sign change preserves its orientation while selecting the closer representation on the quaternion sphere.
Official fact: SciPy Slerp describes interpolation between consecutive orientations along a fixed axis at constant angular velocity, following the shortest path. That is stronger than merely producing normalized intermediate quaternions.
Worked derivation: after sign alignment, let d = dot(q0,q1) and omega = acos(clamp(d,-1,1)). For interpolation parameter t in [0,1], spherical interpolation uses:
q(t) = sin((1-t) omega) / sin(omega) * q0
+ sin(t omega) / sin(omega) * q1
When the endpoints are very close, the denominator becomes numerically awkward. A common implementation uses normalized linear interpolation in a near-parallel branch. State your threshold and test it rather than presenting a magic constant as universally optimal.
Normalized linear interpolation is useful and often cheaper, but generally does not give constant angular velocity. “Both outputs have norm one” does not prove the two methods follow the same timing. For identity to positive 120 degrees about z, compare a quarter-time sample, not only the midpoint where symmetry can hide the timing difference.
Also distinguish antipodal quaternion representations from orientations separated by a physical 180 degrees. The former are the same orientation. The latter can have equally short rotational paths in opposite directions; applications may need an additional continuity convention. Shortest-path interpolation also does not encode a deliberate full revolution between identical endpoint orientations. That requires motion information beyond two endpoint quaternions.
Question 6: What would you test before trusting the implementation?
Use invariants alongside counterexamples. A norm check alone cannot detect wrong frames or multiplication order.
| Check | Expected result | What it diagnoses |
|---|---|---|
| Identity applied to a vector | Same vector | Storage or application convention |
| Positive z quarter-turn applied to x | Positive y | Handedness, sign, and active/passive interpretation |
| x then z applied to y | Positive z | Product ordering |
| z then x applied to y | Negative x | A distinguishing noncommutative case |
| Apply q, then its inverse | Original vector within tolerance | Inversion and normalization |
| Apply q and -q separately | Equal rotated vectors | Orientation equality versus array equality |
| Interpolate q and -q | Constant orientation after sign handling | Zero-midpoint failure |
| Frame chain with translations | World point (10,1,1) | Rotation of origin offsets |
For numerical tests, compare with a tolerance appropriate to the calculation, not exact equality of floating-point outputs. Use asymmetric vectors as well as basis vectors so an accidentally preserved axis does not conceal a fault.
Separate errors by layer. If the first basis-vector check fails, inspect storage and handedness before debugging the frame graph. If basis checks pass but the two-order fixture fails, inspect multiplication and axis semantics. If static transforms pass but animation flips, inspect sign handling and interpolation timing.
A compact interview explanation might be: “I define R_AB as B to A. I will first validate the z quarter-turn, then show that R_z R_x and R_x R_z send y to different vectors. For interpolation I normalize valid inputs, align signs for the chosen path, and compare orientations rather than raw tuples.” Every part can be challenged with a concrete test.
Sources and Further Reading
- SciPy: Rotation.from_quat — ordering, active rotations, and normalization.
- SciPy: Rotation.mul — composition order and noncommutativity.
- SciPy: Rotation.as_quat — sign equivalence and canonical representation.
- SciPy: Slerp — shortest-path, constant-angular-velocity interpolation.