2D Direction Cosine Matrix#

A Direction Cosine Matrix (DCM) rotates the components of a vector between two reference frames without moving the vector itself. The 2D version is the cleanest place to build that intuition: one frame stays fixed, the other rotates by an angle \(\theta\), and the same physical vector \(\mathbf{v}\) sits unchanged in space — only its component representation changes.

The convention#

This course uses the “components from frame a to frame b” convention:

\[\begin{split} \mathbf{v}^b = \mathbf{C}_a^b(\theta)\,\mathbf{v}^a, \qquad \mathbf{C}_a^b(\theta) = \begin{bmatrix} \cos\theta & \sin\theta \\ -\sin\theta & \cos\theta \end{bmatrix}. \end{split}\]

\(\mathbf{v}^a\) are the components of \(\mathbf{v}\) expressed in frame a; \(\mathbf{v}^b\) are the components of the same physical \(\mathbf{v}\) in frame b. As \(\theta\) changes, the b-frame’s axes rotate, so the components in that rotating frame change — but the physical vector itself never moves.

Interactive demo#

Open in full screen

Walkthrough#

Try the following sequence to build intuition:

  1. Start at \(\theta = 0°\). The b-frame is aligned with the a-frame, and \(\mathbf{v}^b = \mathbf{v}^a\). The rotation matrix is the identity.

  2. Click the “30°” preset. The b-frame rotates 30° counter-clockwise. The brown vector hasn’t moved — but the readout shows \(\mathbf{v}^b\) has changed: more component along b-x, less along b-y. This is exactly the matrix-vector product \(\mathbf{C}_a^b(30°)\,\mathbf{v}^a\).

  3. Click “90°.” The b-x axis now points where a-y used to, and b-y points opposite a-x. With the default \(\mathbf{v}^a = [3, 1]^\top\) this gives \(\mathbf{v}^b = [1, -3]^\top\) — a quarter-turn of the components.

  4. Press “Play.” The b-frame rotates continuously. Watch the \(\Delta = \lVert\mathbf{v}^b\rVert - \lVert\mathbf{v}^a\rVert\) readout. It stays at exactly 0 (within machine epsilon) for every angle — that equality is the numerical signature of an orthonormal DCM.

  5. Toggle “Show v_b decomposition.” Two dashed segments appear: from the origin along the b-x axis a length \(v_b^x\), then along the b-y axis a length \(v_b^y\). Head-to-tail, they land exactly on the brown vector tip — visual confirmation that the b-frame components really do rebuild the same physical vector.

  6. Edit \(\mathbf{v}^a\). Change x and y to anything you like. Repeat the rotation experiments and observe that the magnitudes always agree.

Key observations#

  • \(\lVert\mathbf{v}^a\rVert = \lVert\mathbf{v}^b\rVert\) at every \(\theta\), to machine precision. Norm preservation is the simplest unit test you can run on any DCM implementation: rotate a vector, check the norm. If it changed, you have a bug — most likely a sign flip in the matrix.

  • At \(\theta = 360°\) you should recover \(\mathbf{v}^b = \mathbf{v}^a\) exactly. If your implementation drifts after a full revolution (visible in the \(\Delta\) readout), something is wrong.

  • Frames are bookkeeping; vectors are real. The same physical \(\mathbf{v}\) has different components in different frames, but the vector — and any frame-invariant quantity such as its norm — is the same regardless of which frame you write it in.

Connection to 3D#

The 2D version exists to make the geometry visible. In 3D, DCMs are 3×3 orthonormal matrices and the same identities hold — \(\lVert\mathbf{v}^b\rVert = \lVert\mathbf{v}^a\rVert\), \(\mathbf{C}^\top\mathbf{C} = \mathbf{I}\), \(\det\mathbf{C} = +1\). The 3D rotation matrices used throughout the rest of the course (between body, NED, ECEF, and ECI frames) are direct generalizations. Block 2 starts using them in earnest for inertial-error mechanization.

Source#

MATLAB · code/DcmExample.m