Exp and Log on SO(3)
The algebra $\mathfrak{so}(3)$ is where calculus happens; the group $SO(3)$ is where rotations live. This part builds the two maps between them. $\operatorname{Exp}$ is derived twice, once from geometry and once from the power series, and both routes arrive at Rodrigues' formula. $\operatorname{Log}$ inverts it. Then comes the part most textbooks skip: the two places where the textbook formulas break numerically, what robust code does instead, and the ball of radius $\pi$ that gives $SO(3)$ its shape.
Rodrigues' formula from geometry
Split, rotate the part that moves, reassemble
Write the rotation vector as $\omega = \theta k$, with unit axis $k$ and angle $\theta = \lVert\omega\rVert$. To rotate $v$ about $k$, split it in two:
The parallel part is on the axis and does not move. The perpendicular part, together with $k\times v$, spans the plane of rotation: two perpendicular vectors of equal length, like the hands of a clock. Ordinary 2D rotation in that plane gives
Substitute $v_\parallel = v - v_\perp$, write the cross products with $[k]_\times$, and everything collects into one matrix acting on $v$. That matrix is Rodrigues' rotation formula:
The demo draws all three pieces. The circle traced by the tip lies in a plane perpendicular to $k$; its radius $\lVert v_\perp\rVert$ never changes; and $k\cdot v$ is the same before and after.
The same formula from the power series
One identity collapses an infinite sum
The matrix exponential is the same power series as for numbers: $\exp(A) = \sum_n A^n/n!$. With $A = \theta[k]_\times$, everything hinges on one fact: for a unit vector, $[k]_\times^3 = -[k]_\times$. You can check it by taking the cross product with $k$ three times. Every power therefore reduces to $\pm[k]_\times$ or $\pm[k]_\times^2$:
So "Exp" is not a metaphor: Rodrigues' formula is the matrix exponential, summed in closed form. In terms of the unnormalized $\omega$, which is how code stores it,
The chart shows the error of the truncated series for each number of terms. The closed form costs one sine and one cosine; the series needs more terms as $\theta$ grows. Truncated series still turn up in real code as small-angle approximations, and the chart shows what they cost.
$\log_{10}\lVert \sum_{n\le N}[\omega]_\times^n/n! - \operatorname{Exp}(\omega)\rVert$ for each $N$. The highlighted bar is the current $N$. The dashed line is double-precision round-off.
The logarithm: reading off axis and angle
Trace for the angle, skew part for the axis
Take the trace of Rodrigues' formula. $\operatorname{tr}[k]_\times = 0$ and $\operatorname{tr}[k]_\times^2 = -2$, so $\operatorname{tr}R = 1 + 2\cos\theta$. Take the skew part: $R - R^\top = 2\sin\theta\,[k]_\times$. That gives two formulas:
Build any rotation with the yaw, pitch and roll sliders (Euler angles are fine as an input device). The readout runs the logarithm step by step, then feeds the answer back through Exp to confirm it reproduces $R$. The principal logarithm always returns $\theta\in[0,\pi]$: every rotation by more than $180^\circ$ is reported as the shorter rotation the other way.
The gray frame is the identity, the colored frame is $R$. The magenta arrow is $\operatorname{Log}(R)$: the axis to turn about, with length equal to the angle (drawn at 0.4 of scale). The arc shows the turn itself. Drag to orbit.
Two numerical traps
Near zero and near π, the textbook formula fails
The Log formula divides $\theta$ by $2\sin\theta$. That goes wrong at both ends of the range.
Near $\theta = 0$: numerator and denominator both vanish, and their ratio tends to $\tfrac12$. Because that ratio barely depends on $\theta$, the formula stays accurate for a surprisingly long way down. But once $\theta$ drops below about $10^{-8}$, $\operatorname{tr}R$ rounds to exactly $3$, $\arccos$ returns exactly $0$, and the formula returns $0/0$, a NaN. That is a crash rather than an inaccuracy, at the identity, the most common input of all (every optimizer's converged step, every stationary IMU sample). The fix is to replace the ratio by its Taylor expansion $\theta/(2\sin\theta)\approx \tfrac12 + \theta^2/12$ below a threshold, and to compute $\theta$ with $\operatorname{atan2}(\lVert(R-R^\top)^\vee\rVert/2,\ (\operatorname{tr}R-1)/2)$, which is accurate at every angle.
Near $\theta = \pi$: $\sin\theta\to 0$ but $\theta$ does not, so the formula divides a vanishing skew part by a vanishing number, and round-off in $R-R^\top$ is magnified without bound. The axis has to come from the symmetric part instead. From Rodrigues' formula, $\tfrac12(R + R^\top) = \cos\theta\,I + (1-\cos\theta)\,kk^\top$, so
and $k$ is read off the largest diagonal entry, with its sign taken from the (tiny but still meaningful) skew part. The demo builds rotations at a controlled distance from each trap and measures the relative error $\lVert\operatorname{Log}(R) - \omega\rVert/\lVert\omega\rVert$ of the recovered rotation vector for both implementations. It uses exactly the code in the next listing.
Relative error of Log against distance from the trap (log–log). Magenta: textbook formula. Blue: robust branches. Left panel: θ → 0. Right panel: θ → π.
// SO(3) Log that is accurate at every angle (as in assets/js/lie-viz.js) function log(R) { const c = (trace(R) - 1) / 2; const sv = vee(R - Rᵀ); // = 2 sinθ · k const th = atan2(norm(sv) / 2, clamp(c, -1, 1)); if (th < 1e-4) // near 0: Taylor, no acos return sv * (0.5 + th*th/12); if (π - th < 1e-3) { // near π: axis from the symmetric part const B = (sym(R) - c·I) / (1 - c); // = k kᵀ let k = column of B with the largest diagonal, normalised; if (dot(k, sv) < 0) k = -k; // sign from the skew part return th * k; } return sv * th / (2 sin th); }
The same care applies to Exp. $(1-\cos\theta)/\theta^2$ suffers catastrophic cancellation for small $\theta$ (two nearly equal numbers subtracted), so robust code switches to $\tfrac12 - \theta^2/24$ below a threshold. Every Lie library (Sophus, GTSAM, manif) carries these branches, and so should any implementation you write.
The shape of SO(3): a ball of radius π
Every rotation once, with the surface glued
The principal Log sends each rotation to a vector $\omega$ with $\lVert\omega\rVert\le\pi$. Inside the ball the correspondence is one-to-one. On the surface, $\pi k$ and $-\pi k$ are the same rotation (a half-turn is the same in either direction), so the two antipodal points are identified. $SO(3)$ is exactly this ball with opposite surface points glued together. It is a compact, curved, three-dimensional space.
Spin a body steadily about a fixed axis and follow $\operatorname{Log}(R(t))$. It travels out along the axis to the surface, reappears at the opposite point, and comes back in to the centre after a full turn. A smooth motion in the group produces a jump in coordinates, which is the 3D version of the $\pm\pi$ cut on the circle. For a wobbling path, the random-walk button shows the same jumps happening wherever the path crosses the surface.
The sphere of radius π. The path is $\operatorname{Log}(R(t))$; segments that jump across the ball are drawn dashed. The frame at the right is $R(t)$ itself. Drag to orbit.
Measuring how far apart two rotations are
Geodesic angle versus chordal distance
Log gives the natural distance between two rotations: the angle of the rotation that takes one to the other,
They agree to first order ($2\sqrt2\sin(\theta/2)\approx\sqrt2\,\theta$) and are monotonically related, so for ranking or thresholding either works. For averaging they differ, because the chordal distance saturates at large angles and so weights outliers less. Part 10 uses exactly this difference in rotation averaging. The table gives both for common angles.
| Geodesic angle θ | 1° | 10° | 45° | 90° | 180° |
|---|---|---|---|---|---|
| $\lVert R_1-R_2\rVert_F$ | 0.0247 | 0.2465 | 1.0824 | 2.0000 | 2.8284 |
| $\sqrt2\,\theta$ (first-order) | 0.0247 | 0.2468 | 1.1107 | 2.2214 | 4.4429 |