Visual walkthrough — Attitude estimation — triad method (two vector measurements)
Before line one, three plain-word promises:
- An arrow (a vector) is just a direction with a length. A unit vector is an arrow whose length is exactly — we only care about which way it points. We draw unit vectors with a little hat: .
- A frame is a set of three arrows at right angles to each other, all length — like the corner of a room where three walls meet. We call such a set orthonormal (ortho = right-angled, normal = length one).
- A rotation matrix is a machine: feed it an arrow, it spits out the same arrow turned by a fixed amount. It never stretches or flips anything.
Step 1 — The raw ingredients: two arrows, twice
WHAT. We start with two known directions in space — say the Sun and Earth's magnetic field. Each is known in two coordinate systems:
- In the reference frame (a fixed map of the sky): and .
- In the body frame (what the tumbling spacecraft's sensors actually read): and .
Here = where the Sun should be on the map, and = where the sensor says the Sun is right now. Same physical Sun, two descriptions.
WHY. One arrow alone leaves a hole: the spacecraft could still spin around that arrow and every sensor would read the same. Look at the figure — spinning around the yellow arrow slides the whole craft but never moves that arrow. The second arrow (blue) is what pins down that leftover spin.
PICTURE.

Step 2 — Anchor the first axis (trust one arrow completely)
WHAT. We make our very first frame-axis just a copy of the most trusted arrow:
WHY. Real sensors have noise. TRIAD's whole philosophy is: don't spread the error around — trust one sensor perfectly and use the other only for "which side is up." So becomes axis-1 untouched. Whatever tiny error it has, it stays; whatever error the second arrow has, we are about to throw most of it away.
PICTURE. The yellow arrow is promoted to be the first wall of our "corner of the room."

Step 3 — The cross product builds a perpendicular axis
WHAT. We need a second axis at a perfect right angle to the first. We get it with the cross product:
Let's decode the two pieces:
- — the cross product outputs an arrow perpendicular to both inputs. That's exactly the property we want for a right-angled frame.
- — the double bars mean length. The cross product's length is (where is the angle between the two arrows), so it is not unit length. Dividing by that length forces it back to length .
WHY this tool and not another? We could have tried "subtract off the overlap" (Gram–Schmidt), but the cross product gives us the perpendicular for free in one step AND it fixes a handedness (a left-vs-right choice) automatically — which we'll need in Step 4. Most importantly: the cross product's direction depends only on the plane the two arrows lie in, not on the exact angle between them. That is the secret sauce — the noisy angle of gets discarded, only its "side of the plane" survives.
PICTURE. Watch the pink arrow pop straight out of the plane of the two flat arrows.

Step 4 — Close the frame with a third cross product
WHAT. Two perpendicular axes exist; the third is forced:
WHY. Crossing two arrows that are already unit length and already perpendicular gives a third arrow that is automatically unit length and perpendicular to both — no normalizing needed this time (since ). Doing it in this order () guarantees the frame is right-handed: curl the fingers of your right hand from axis-1 to axis-2 and your thumb points along axis-3. A right-handed frame has determinant , which is exactly what makes it a proper rotation (a turn, never a mirror-flip).
PICTURE. The blue arrow completes the corner-of-a-room.

Stack the three axes side-by-side as columns of a matrix:
is orthonormal by construction — its columns are unit length and mutually perpendicular.
Step 5 — Do the identical recipe with the body arrows
WHAT. Same three steps, now feeding the sensor readings :
\hat{\mathbf t}_2^{(b)}=\frac{\hat{\mathbf b}_1\times\hat{\mathbf b}_2}{\lVert \hat{\mathbf b}_1\times\hat{\mathbf b}_2\rVert},\quad \hat{\mathbf t}_3^{(b)}=\hat{\mathbf t}_1^{(b)}\times\hat{\mathbf t}_2^{(b)}$$ $$M_b = \big[\,\hat{\mathbf t}_1^{(b)}\ \ \hat{\mathbf t}_2^{(b)}\ \ \hat{\mathbf t}_3^{(b)}\,\big]$$ **WHY.** Here is the deep point: $M_r$ and $M_b$ are **the same physical corner-of-a-room**, just written down in two different coordinate languages. The reference frame writes it as $M_r$; the body frame writes it as $M_b$. Because both were built by the *same* recipe from the *same* two physical directions, they describe one and the same triad. **PICTURE.** Left: the triad in map-language ($M_r$). Right: the same triad, tumbled, in sensor-language ($M_b$). ![[deepdives/dd-physics-3.5.12-d2-s05.png]] --- ## Step 6 — Match the frames to read off the rotation **WHAT.** The attitude $A$ (see [[Rotation Matrices and SO(3)]]) is *defined* by "turn each reference axis onto its body twin": $$A\,\hat{\mathbf t}_i^{(r)} = \hat{\mathbf t}_i^{(b)} \quad\text{for } i=1,2,3 \qquad\Longleftrightarrow\qquad A\,M_r = M_b$$ Reading the arrow-version: $A$ takes column $i$ of $M_r$ and lands it exactly on column $i$ of $M_b$. Stacking all three columns is the single matrix equation $AM_r = M_b$. **WHY multiply by the transpose?** To isolate $A$ we need to undo $M_r$ on the right — i.e. multiply by its **inverse** $M_r^{-1}$. But $M_r$ is orthonormal, and orthonormal matrices have a magic shortcut: their inverse **is** their transpose $M_r^{\mathsf T}$ (flip rows and columns — no division, no determinant). That is *why* we insisted on normalizing back in Step 3. So: $$A = M_b\,M_r^{-1} = M_b\,\underbrace{M_r^{\mathsf T}}_{\text{transpose = inverse, because orthonormal}}$$ > [!formula] TRIAD attitude matrix — derived, not dumped > $$\boxed{A = M_b\,M_r^{\mathsf T}}$$ > $M_b$ = the physical triad in body coordinates; $M_r^{\mathsf T}$ = the *inverse* of the same triad in reference coordinates. Multiplying "into body, out of reference" is exactly the turn from reference to body. **Check it's a real rotation.** Using $M M^{\mathsf T}=I$ for both: $$A A^{\mathsf T} = M_b M_r^{\mathsf T} M_r M_b^{\mathsf T} = M_b\,I\,M_b^{\mathsf T} = I, \qquad \det A = (+1)(+1) = +1 \ \checkmark$$ **PICTURE.** The single machine $A$ that carries the whole left corner onto the whole right corner. ![[deepdives/dd-physics-3.5.12-d2-s06.png]] --- ## Step 7 — The degenerate case: when TRIAD breaks **WHAT.** What if the two arrows point *almost the same way*? Say the angle $\theta$ between them shrinks toward $0$. **WHY it fails.** The cross-product length is $\sin\theta$. As $\theta \to 0$, $\sin\theta \to 0$, so in Step 3 we divide by a number crawling toward zero. Any tiny measurement wiggle in the numerator gets **blown up enormously** — the perpendicular axis rattles wildly. Geometrically the "plane" of the two arrows is ill-defined when they lie on top of each other, so the spin they were supposed to lock down becomes unknowable. $$\lVert \hat{\mathbf r}_1 \times \hat{\mathbf r}_2 \rVert = \sin\theta \;\xrightarrow[\ \theta\to 0\ ]{}\; 0 \quad\Rightarrow\quad \text{noise } \div \text{ tiny} = \text{disaster}$$ **Fix:** choose sensors whose directions are well separated — best near $90^\circ$, where $\sin\theta = 1$ and the frame is maximally stable. This is exactly why the smarter [[QUEST algorithm|QUEST]] and [[Wahba's Problem|Wahba]] approaches exist when you can't guarantee separation. **PICTURE.** Left: healthy $90^\circ$ split, crisp perpendicular. Right: near-parallel, the perpendicular flails. ![[deepdives/dd-physics-3.5.12-d2-s07.png]] --- ## The one-picture summary Everything on this page in a single flow: two arrows → build a corner → do it twice → match the corners → out pops $A$. And a reminder of the escape hatch when the arrows are too close. ![[deepdives/dd-physics-3.5.12-d2-s08.png]] ```mermaid flowchart LR R["two ref arrows"] --> MR["reference triad Mr"] B["two body arrows"] --> MB["body triad Mb"] MR --> A["A equals Mb times Mr transpose"] MB --> A A --> OUT["attitude matrix"] ``` > [!recall]- Feynman retelling — the whole walkthrough in plain words > You have two arrows you can spot in the sky — say the Sun and a compass field. You know where each *should* be on a fixed star-map, and where your spinning spacecraft's eyes *actually* see them. **Step one:** grab the arrow you trust most and call it "wall number one" of a little room-corner. **Step two:** cross it with the second arrow — the cross product pokes out a wall at a perfect right angle, and (this is the trick) it only cares which *side* the second arrow is on, so most of that arrow's sloppiness is thrown away. **Step three:** cross those two walls to get the floor — now you have a full corner-of-a-room, and it's guaranteed right-handed. **Step four:** build the *exact same corner* twice — once from the star-map arrows, once from the sensor arrows. They're the same physical corner in two languages. **Step five:** the rotation that carries the map-corner onto the sensor-corner *is* your spacecraft's attitude, and because a room-corner is orthonormal, flipping its rows and columns undoes it — giving $A = M_b M_r^{\mathsf T}$. **The one warning:** never pick two arrows pointing nearly the same way, or the right-angle wall becomes a jittery mess. > [!recall]- Quick checks > Why does the cross product discard sensor-2's angle? ::: Its direction depends only on the *plane* of the two arrows, not the angle between them. > Why transpose instead of a general inverse? ::: $M_r$ is orthonormal, so $M_r^{-1}=M_r^{\mathsf T}$. > What quantity vanishes in the degenerate case? ::: $\lVert\hat{\mathbf r}_1\times\hat{\mathbf r}_2\rVert=\sin\theta\to 0$, so we divide by nearly zero.