Lesson 0.2.3 — The Exponential Map, Twists, and Screw Theory: From so(3)/se(3) to the Product of Exponentials
Week 3 · Prerequisites: 0.2.1, 0.2.2, 0.1.2.
Prior results used. This lesson uses 0.2.1’s SE(3) composition rule, inverse, and point/vector distinction; 0.2.2’s so(3), Rodrigues’ formula, exp/log on SO(3), and the π-singularity; and 0.1.2’s autograd-over-matmul-chains result. Each is restated at the point of use.
Hands forward. This lesson hands forward one capability: the learner can represent a joint’s motion as a twist, exponentiate a twist to a pose, compose a chain of joints as a product of exponentials, transform a twist between frames with the adjoint, and diagnose the four twist-related silent bugs from their symptoms — the capability that Lesson 1.1.1 (the PoE forward-kinematics lesson) and every Jacobian, velocity, and pose-learning lesson in Phase 1–3 assumes.
Running case. The same 6-DOF MuJoCo arm from 0.2.1–0.2.2: six revolute joints with alternating z/y axes, link lengths L_i = a = 0.2, and the end-effector at the origin of the sixth frame. Its chain FK is T(q) = T_1(q_1)·…·T_6(q_6) with T_i = Rot_{axis_i}(q_i)·Trans_x(a); its product-of-exponentials FK is T(q) = exp(ξ̂₁q₁)·…·exp(ξ̂₆q₆)·M with M = Trans_x(6a), and this lesson’s central result is that the two formulas describe the same arm.
§L.0 — Lesson map, objectives, and misconceptions
§L.0.1 — Lesson map
| Part | Content |
|---|---|
| A | so(3) and se(3) as Lie algebras; the bracket; algebra vs. group |
| B | The exp map on SE(3): the twist, the left Jacobian, and the two forms (unit vs. arbitrary) |
| C | Screw motion: axis, pitch, point on the axis; classification |
| D | The adjoint representation; body vs. spatial twists; velocities |
| E | Failure taxonomy, lab synthesis, cheat sheet, exercises, exit test |
§L.0.2 — Objectives
By the end of this lesson you will be able to do eight things:
- State what a Lie algebra is, distinguish
so(3)fromSO(3)andse(3)fromSE(3), and compute the bracket[ξ₁, ξ₂]for two twists. - Apply the exp map
exp : se(3) → SE(3)in both its unit-twist form and its arbitrary-twist form, and computeexp(ξ̂ q)by hand, showing the left Jacobian’s contribution. - State what a screw motion is (axis, pitch, point on the axis), compute the pitch of a given twist, and classify the twist as pure rotation, pure translation, or general screw.
- Express the running case’s 6-DOF FK as a product of exponentials, with the home configuration and the six spatial twists, and verify that it matches the chain FK of
0.2.1at a non-trivial configuration. - Derive the adjoint representation
Ad_T = [[R, 0], [t̂R, R]], and transform a twist between frames by hand, showing thet × (Rω)cross-product term. - Distinguish a body twist from a spatial twist, and state the frame convention for each.
- Diagnose four silent twist bugs — missing
Vinexp, body vs. spatial confusion, missing cross-product inAd, wrong reference point in the twist — from their symptoms. - Produce a written “twist audit” of a kinematic pipeline: each joint’s twist, the frame it is expressed in, the composition order, and the adjoint conversions between frames.
§L.0.3 — Misconception inventory
| Wrong model the learner probably holds | Why it is attractive | Where displaced | The correct model in one sentence |
|---|---|---|---|
M1 — “so(3) and SE(3) are the same objects.” | They are both “rotations” or “poses,” and the notation looks like a typo. | §L.1 (Part A) | so(3) is the Lie algebra — a 3-dimensional vector space of skew matrices — and SO(3) is the Lie group — a curved manifold of rotations; the exp map bridges them. |
M2 — “exp((ω, v)) is (exp(ω̂), v).” | The rotation part’s exp is 0.2.2’s, and the translation looks like it should just be v. | §L.2 (Part B) | exp(ξ̂) = (exp(ω̂), V(ω)·v) where V is the left Jacobian; the translation is V·v, not v, and the difference is the rotation-translation coupling. |
| M3 — “A twist is an angular velocity and a linear velocity.” | The 6-vector (ω, v) looks like (angular, linear), and the analogy to a rigid body’s velocity is tempting. | §L.2 and §L.3 | A twist’s v is the velocity of a point on the screw axis — equivalently, the velocity of the origin plus the ω × q correction — not the velocity of the origin unless the origin is on the axis. |
M4 — “The adjoint is the 4×4 T applied to a 6-vector.” | A twist is a 6-vector, a pose is a 4×4, and “transform the twist by the pose” sounds like a matrix-vector product. | §L.4 (Part D) | The adjoint is a 6×6 matrix Ad_T = [[R, 0], [t̂R, R]], not T; the linear part transforms as R v + t × (Rω), with a cross-product term that T’s multiplication does not have. |
| M5 — “Body and spatial twists are interchangeable.” | Both are 6-vectors, both describe the same joint’s motion, and the notation does not always show the frame. | §L.4 (Part D) | A body twist is expressed in the joint’s own frame and is constant; a spatial twist is expressed in the base frame and is constant only at the home configuration; the two are related by the adjoint, and mixing them is a silent FK error. |
Everything is self-contained. Where a prerequisite lesson is used, the result is restated at the point of use.
§L.0.5 — Retrieval warm-up
This is the seventh lesson; the warm-up draws from 0.2.1 and 0.2.2. 0.2.2 is one step back and is excluded.
Warm-up 1 (from 0.2.1, SE(3) composition). For T₁ = (R₁, t₁) and T₂ = (R₂, t₂), write the composition rule and state which frame each translation is in. Answer: T₁·T₂ = (R₁R₂, R₁t₂ + t₁); t₂ is in T₁’s child frame and must be rotated by R₁ into T₁’s parent frame before adding. How this is used: §L.2’s exp(ξ̂) has the same structure — the rotation’s exp and the translation’s V·v are the analogue of R and Rt₂ + t₁, and the V matrix is what plays the role of the R factor.
Warm-up 2 (from 0.2.1, point vs. vector). A point p transforms as T[p; 1]; a direction v transforms as T[v; 0]. Why does a point’s velocity not transform like a direction? Answer: the velocity of a point is d/dt(Rp + t) = Ṙp + ṫ, which contains both the rotational part ω̂(Rp) and the translational part ṫ — the velocity transforms under the adjoint (Part D), not under T, and its transformation has a cross-product term that a direction’s does not. How this is used: §L.4’s “the adjoint is the velocity’s transformation law, and it is not T” is exactly this observation.
Warm-up 3 (from 0.2.2, the SO(3) exp and log). R = Rz(θ). Write its exp and log representations. Answer: R = exp(θ·ẑ) with ẑ the skew matrix of (0, 0, 1), and log(R) = θ·ẑ. At θ = π, the axis is ambiguous and the log is singular. How this is used: §L.2’s SE(3) exp is the so(3) exp plus the left Jacobian’s translation; the SO(3) singularity at π is inherited by the SE(3) log.
Warm-up 4 (from 0.2.2, the double cover). The quaternion q and −q represent the same rotation. Is the exponential coordinate ω double-covered? Answer: No — exp(ω̂) is injective on ‖ω‖ < π, so each rotation has a unique ω in that ball; the π boundary is where the map fails to be injective (both πu and −πu map to the same half-turn), but the interior is a diffeomorphism. How this is used: §L.4’s “the se(3) exp is a diffeomorphism on a suitable domain” is the SO(3) result extended to SE(3).
Part A — so(3) and se(3) as Lie Algebras
§L.1 — Objects: algebra vs. group, the bracket, and the two spaces
The Lie algebra of a Lie group G is the tangent space at the identity, written 𝔤 = T_e G, a vector space with a bilinear, antisymmetric bracket [·,·] : 𝔤 × 𝔤 → 𝔤.
For SO(3), the Lie algebra is so(3) = {ω̂ ∈ ℝ^{3×3} : ω̂ᵀ = −ω̂} — the 3×3 skew-symmetric matrices, a 3-dimensional vector space with basis ê₁, ê₂, ê₃ corresponding to ω = (ω₁, ω₂, ω₃) via ω̂ = ω₁ê₁ + ω₂ê₂ + ω₃ê₃. The bracket is the matrix commutator [ω̂₁, ω̂₂] = ω̂₁ω̂₂ − ω̂₂ω̂₁, which corresponds to \widehat{ω₁ × ω₂} — the cross product of the underlying vectors.
For SE(3), the Lie algebra is se(3) = {ξ̂ = [[ω̂, v], [0, 0]] : ω̂ ∈ so(3), v ∈ ℝ³} — a 6-dimensional vector space with coordinates ξ = (ω, v), called a twist. The bracket is [(ω₁,v₁), (ω₂,v₂)] = (ω₁×ω₂, ω₁×v₂ − ω₂×v₁).
The key structural facts:
- The algebra is a vector space (you can add and scale twists); the group is a curved manifold (you cannot add rotations).
- The bracket measures the group’s non-commutativity:
[A, B]is the leading-order failure ofexp(A)exp(B) = exp(A+B). - The exp map
exp : 𝔤 → Gis the bridge, a diffeomorphism on a neighborhood of the identity.
§L.1.1 — Result: the algebra is a vector space, the group is curved, and the bracket measures the failure of exp(A+B) = exp(A)exp(B)
Claim. For A, B ∈ 𝔤, exp(A+B) = exp(A)exp(B) if and only if [A, B] = 0. In general, the Baker–Campbell–Hausdorff formula is log(exp(A)exp(B)) = A + B + ½[A,B] + (1/12)[A,[A,B]] − (1/12)[B,[A,B]] + …, and the bracket is the leading-order correction.
Proof sketch. The BCH series is exact, and its terms are all iterated brackets. When [A, B] = 0, all higher terms vanish, so log(exp(A)exp(B)) = A + B. When [A, B] ≠ 0, the ½[A,B] term is the first non-vanishing correction, and it is the measure of the non-commutativity.
Corollary. The group is non-abelian for both SO(3) and SE(3), so a joint’s composition exp(ξ̂₁q₁)exp(ξ̂₂q₂) is not exp(ξ̂₁q₁ + ξ̂₂q₂), and the product of exponentials is a product, not a sum.
Corollary. The bracket’s antisymmetry ([A, A] = 0) and the Jacobi identity ([A,[B,C]] + [B,[C,A]] + [C,[A,B]] = 0) are the Lie algebra’s axioms, and they are the structural properties that make the BCH formula well-defined.
§L.1.2 — Worked-example ladder
WE-L.1.a — Fully worked: the bracket of two so(3) elements, by hand
Input. ω₁ = (1, 0, 0), ω₂ = (0, 1, 0).
Trace.
ω̂₁ = [[0,0,0],[0,0,-1],[0,1,0]],ω̂₂ = [[0,0,1],[0,0,0],[-1,0,0]].ω̂₁ω̂₂ = [[0,0,0],[1,0,0],[0,0,0]].ω̂₂ω̂₁ = [[0,1,0],[0,0,0],[0,0,0]].[ω̂₁, ω̂₂] = [[0,-1,0],[1,0,0],[0,0,0]] = \widehat{(0,0,1)}. So[ω₁, ω₂] = ω₁ × ω₂ = (0, 0, 1).
Output and interpretation. The bracket of the x- and y-rotations is the z-rotation, the cross product of the two axes. The result is non-zero, meaning exp(θ₁ x̂)exp(θ₂ ŷ) ≠ exp(θ₁ x̂ + θ₂ ŷ) — the z-axis is the axis of the commutator’s rotation, about which the two rotations fail to commute.
Self-explanation 1. Before reading on: why is the bracket of two skew matrices itself skew, and what does this tell you about
so(3)?Answer.
(ω̂₁ω̂₂ − ω̂₂ω̂₁)ᵀ = ω̂₂ᵀω̂₁ᵀ − ω̂₁ᵀω̂₂ᵀ = (−ω̂₂)(−ω̂₁) − (−ω̂₁)(−ω̂₂) = ω̂₂ω̂₁ − ω̂₁ω̂₂ = −[ω̂₁, ω̂₂], so the bracket is skew. The closure is the defining property of a Lie algebra, and the reasonso(3)is 3-dimensional (the bracket of two 3-vectors is a 3-vector) rather than something larger.
WE-L.1.b — Completion problem
Input. ξ₁ = (0, 0, 1, 0, 0, 0) (rotation about z through the origin), ξ₂ = (0, 1, 0, 0, 0, 0) (rotation about y through the origin).
Trace (final 25% as prompts).
ω₁ = (0,0,1),v₁ = 0;ω₂ = (0,1,0),v₂ = 0.ω₁ × ω₂ = (0,0,1) × (0,1,0) = (0·0 − 1·1, 1·0 − 0·0, 0·1 − 0·0) = (−1, 0, 0). ⟨your turn: verify⟩ω₁ × v₂ − ω₂ × v₁ = 0 − 0 = 0. ⟨your turn: verify⟩[ξ₁, ξ₂] = (−1, 0, 0, 0, 0, 0). ⟨your turn: what does the bracket’s linear part being zero mean?⟩
Answers.
(−1, 0, 0)— the−x-axis.- Both cross products vanish, because
v₁ = v₂ = 0. - The linear part is zero because both twists are pure rotations about the origin, and their bracket is a pure rotation about the cross product’s axis, also through the origin. The general twist’s bracket can have a non-zero linear part when the linear velocities are non-zero.
The lesson. The bracket’s angular part is the cross product of the angular parts; the linear part is ω₁ × v₂ − ω₂ × v₁, vanishing for pure rotations and non-zero for general screws. The bracket is the only operation beyond vector addition that a Lie algebra has, and its structure is what the exp map “integrates” into the group’s non-commutativity.
WE-L.1.c — Faded: the BCH correction for two so(3) rotations
Setup. A = θ₁ x̂, B = θ₂ ŷ, with θ₁ = θ₂ = 0.1 rad.
Question. What is the first-order BCH correction ½[A, B]? What is exp(A)exp(B) vs. exp(A+B)?
You must produce the intermediate steps. The setup and the answer are given.
Answer. [A, B] = θ₁θ₂ [x̂, ŷ] = 0.01 · ẑ. The first-order BCH correction is ½·0.01·ẑ = 0.005·ẑ, so exp(A)exp(B) ≈ exp(A + B + 0.005·ẑ + O(θ³)), and the difference from exp(A+B) is a 0.005 rad rotation about z. For small angles, this is O(θ²) — small — but it is systematic and grows with the angles.
The lesson. The bracket is the first-order correction to additivity; for small angles it is O(θ²), small enough to be missed, but for chains of many joints it accumulates and the sum-and-exp approximation diverges from the product.
WE-L.1.d — Contrastive near-miss: the “add the twists” FK
Correct solution. T(q) = exp(ξ̂₁q₁)exp(ξ̂₂q₂)…exp(ξ̂₆q₆)·M — a product of exponentials.
Near-miss. T(q) = exp(ξ̂₁q₁ + ξ̂₂q₂ + … + ξ̂₆q₆)·M — a sum in the exponent.
Why the near-miss produces a plausible but incorrect number. The sum’s exp is a legal SE(3) element (a valid rigid motion), and at q = 0 it coincides with the correct product. For small q, the difference is O(q²) (the BCH correction), so the FK looks right. For larger q or longer chains, the BCH corrections accumulate, the end-effector drifts from the correct position, and the policy trained on it is a policy for a different arm. The failure is silent in the small-q test and visible only at larger q or against the chain FK.
The discrepancy, stated. The correct product and the sum differ by exp(½[ξ̂₁q₁, ξ̂₂q₂] + …) (BCH), which is a rotation about the commutator’s axis. The difference vanishes at q = 0 and at [ξ̂_i, ξ̂_j] = 0 (aligned axes), and is non-zero otherwise. The near-miss’s error is exactly the BCH correction, and it is the silent part of the “add the twists” bug.
Self-explanation 2. Before reading on: if all joints’ twists commute, does the sum-and-exp give the correct FK?
Answer. Yes — if all twists commute, the BCH higher-order terms vanish, and
exp(sum) = product. The commuting case is the planar arm with all joints about the same axis (allz-rotations). The running case’s alternatingz/yaxes do not commute (WE-L.1.a’s bracket is non-zero), so the sum-and-exp is wrong for the running case.
§L.1.3 — Comparative table: the two spaces and the two operations
| Object or operation | Set | Structure | Addition | Composition | Dimension | Code artifact |
|---|---|---|---|---|---|---|
so(3) | 3×3 skew-symmetric matrices | vector space with a bracket | Legal — the sum is skew | [·,·], closed | 3 | skew(ω) |
SO(3) | rotation matrices | curved group | Illegal — R₁ + R₂ ∉ SO(3) | matrix product, closed | 3 | exp_so3(ω) |
se(3) | 4×4 blocks [[ω̂, v], [0, 0]] | vector space with a bracket | Legal — the sum is in se(3) | [·,·], closed | 6 | xi = (ω, v) |
SE(3) | homogeneous matrices [[R, t], [0, 1]] | curved group | Illegal — the sum is not a pose | matrix product, closed | 6 | exp_se3(xi, q) |
exp | 𝔤 → G | diffeomorphism near the identity | — | converts a sum into a product only when [A, B] = 0 | — | the bridge |
Design principle (P7.4). Add in the algebra, multiply in the group, and never let a sum reach the group without an exp. The sum ξ̂₁q₁ + … + ξ̂₆q₆ is a legal element of se(3), so its exponential is a legal pose — which is exactly why the sum-and-exp FK compiles, runs, and returns something plausible. The legality of the intermediate is not evidence about the result: the FK is the product of the exponentials, and the sum is its O(q²) approximation, exact only when the brackets vanish.
§L.1.4 — Concept check
Concept check (displaces M1). A learner says: “
so(3)andSE(3)are the same kind of object — both are ‘rotations’ written as matrices, so the exp map is a change of notation.” Which structural facts has the learner failed to model, and what bug class follows?Answer. Three. The dimension (3 against 6: a twist carries a translation that a rotation matrix has nowhere to put), the kind (
so(3)is a vector space of skew matrices,SE(3)is a curved group of poses), and the direction of the bridge (expis a diffeomorphism only near the identity, so it does not transport addition). The bug class is sum-for-product: the code buildsexp(ξ̂₁q₁ + … + ξ̂₆q₆)·M, which is a legal pose, agrees with the correct product atq = 0, and drifts asO(q²)thereafter. Nothing raises, because the sum lives in the algebra where addition is defined, and the exponentiation conceals the substitution.
§L.1.5 — Consolidation checkpoint 1
(a) You should now be able to:
(i) state the tangent-space definition of a Lie algebra and the two structures it carries (addition and bracket);
(ii) state which operations are legal in so(3) and se(3) and which are not in SO(3) and SE(3), with the dimension of each;
(iii) state the BCH expansion’s first term and the exact condition under which exp(A+B) = exp(A)exp(B).
(b) Transfer probe. A colleague averages two measured poses as T = (T₁ + T₂)/2 and uses the result as a calibration. Does it live in SE(3)?
Answer sketch: No. The group is not closed under addition: the average of two rotation blocks has RᵀR ≠ I and det R ≠ 1 in general, and the bottom row is no longer (0, 0, 0, 1). The code runs because nothing checks the group axioms, and the matrix is a point in the ambient ℝ^{3×3}, not a rotation. The group-correct midpoint is the geodesic exp(½ log(R₂R₁ᵀ))R₁ from 0.2.2; the algebra-correct mean of two twists is (ξ₁ + ξ₂)/2 before a single exp. The learner who averages matrices has done the algebra’s operation in the group’s space.
(c) Remediation rule. If you cannot do (a)(ii) or (b), return to §L.1.2 and redo WE-L.1.d, then re-attempt (b) by asking which of the two spaces the expression (T₁ + T₂)/2 is an element of.
Part B — The Exponential Map on SE(3)
§L.2 — Objects: the twist, the left Jacobian, and the two forms
A twist is an element of se(3), written ξ = (ω, v) ∈ ℝ⁶, with ω the angular part and v the linear part. Its se(3) matrix is ξ̂ = [[ω̂, v], [0, 0]].
The exponential map exp : se(3) → SE(3) has two forms, which are the same formula viewed from different normalizations:
- Unit-twist form — for a twist
ξ = (ω, v)with‖ω‖ = 1and a joint angleq:
- Arbitrary-twist form — for a twist
ξ = (ω, v)withθ = ‖ω‖(the angle baked into the twist’s magnitude):
The two agree when ξ is the unit twist scaled by q, i.e., when ξ_arbitrary = q · ξ_unit (so that θ = q and ω̂ = q ω̂_unit). The unit-twist form is the right one for the product of exponentials (the joint’s twist is fixed, the angle varies); the arbitrary-twist form is the right one for learning (the network outputs a twist whose magnitude is the angle).
The matrix J(ω) (also called V(ω)) is the left Jacobian of SE(3), and it is the only correction to the naive “translation is v” formula. Its inverse is J(ω)^{-1} = I − ½ω̂ + (1/θ² − (1+cos θ)/(2θ sin θ))ω̂², used by the log map.
The logarithm log : SE(3) → se(3) is the inverse: for T = (R, t), log(T) = (ω, v) with ω = log_{SO(3)}(R) and v = J(ω)^{-1} t.
The special cases:
- Pure rotation (
v = 0):exp(ξ̂) = (exp(ω̂), 0)— the left Jacobian is irrelevant. - Pure translation (
ω = 0):exp(ξ̂) = (I, v)—J = I, and the translation is exactlyv. - General screw (
ω ≠ 0,v ≠ 0): the translation isJ·v ≠ v, and the coupling is visible.
§L.2.1 — Result: the exp map’s translation is J·v, not v, and the difference is the rotation–translation coupling
Claim. For ξ = (ω, v) with ω ≠ 0 and θ = ‖ω‖, exp(ξ̂) = (exp(ω̂), J(ω)v) with J the left Jacobian. The translation J·v equals v when ω = 0 (so J = I), when v = 0 (J·v = 0 = v), or when v lies along the rotation axis ω — the axial component is a fixed point of J, since J = I + a·\hat{u} + b·\hat{u}² acts as the identity on \hat{u} for any coefficients a, b. Outside those cases J ≠ I and J·v ≠ v.
Proof sketch. The matrix exponential exp(ξ̂) = Σ ξ̂ⁿ/n! splits into the top-left block (giving exp(ω̂)) and the top-right block Σ ω̂ⁿ⁻¹/n! · v = J(ω)·v. For ω = 0, all ω̂ terms vanish and J = I. For v = 0, J·v = 0. In the general case J ≠ I; but J is a polynomial in ω̂ with no ω̂⁰ term beyond the identity, so its action on ω̂’s own axis is the identity, and v ∥ ω is a third equality case that the “J ≠ I therefore J·v ≠ v” shorthand misses. The lab’s WE-L.2.a step 7 is exactly this case: ω = (0,0,0.7), v = (0,0,2), and J·v = (0,0,2) even though J ≠ I.
Corollary. J is the SE(3) analogue of the SO(3) exp’s sin θ / θ factor — it is the correction that accounts for the fact that the rotation and translation do not commute. In SO(3), the “spherical linear interpolation” correction is sin θ/θ; in SE(3), the correction is the J matrix. The correction is invisible on the axis and maximal perpendicular to it, which is the same geometry as the t − Rᵀt error in 0.2.1.
Corollary. J’s inverse is what the log map uses: v = J(ω)^{-1} t is the only way to recover the twist’s linear part from the pose’s translation. The inverse has the same 1/θ structure and the same π-singularity as the SO(3) log, so the SE(3) log is singular at ‖ω‖ = π.
§L.2.2 — Worked-example ladder
WE-L.2.a — Fully worked: exp of a pure-rotation twist and a general twist
Input (pure rotation). ξ = (0, 0, 1, 0, 0, 0), q = π/2. Input (general twist). ξ = (0, 0, 1, 0, 0, 1), q = π/2.
Trace (pure rotation).
ω = (0,0,1),v = (0,0,0),q = π/2.exp(q ω̂) = Rz(π/2) = [[0,-1,0],[1,0,0],[0,0,1]](from0.2.2).t = (q I + (1 − cos q)ẑ + (q − sin q)ẑ²) · 0 = 0.exp(ξ̂ q) = [[Rz(π/2), 0], [0, 1]]— a pure rotation.
Trace (general twist).
ω = (0,0,1),v = (0,0,1),q = π/2. Thevis parallel toω.ẑ·v = (0,0,1) × (0,0,1) = 0,ẑ²·v = 0.t = (π/2 I + 1·0 + (π/2 − 1)·0) · (0,0,1) = (π/2)(0,0,1) = (0, 0, π/2).exp(ξ̂ q) = [[Rz(π/2), (0,0,π/2)], [0, 1]].
Output and interpretation. The pure rotation’s exp has zero translation; the general twist’s exp has translation (0, 0, π/2). The translation is q·v (not v), because v is parallel to ω (so ẑ·v = 0 and the J matrix reduces to q I on the ω-parallel part of v). The translation is the screw’s pitch times the angle — π/2 units of translation for π/2 radians of rotation, giving a pitch of 1 per radian.
Self-explanation 3. Before reading on: in the general twist,
vis parallel toω, sot = q·v. What wouldtbe ifv = (1, 0, 0)(perpendicular toω)?Answer.
ẑ·(1,0,0) = (0,1,0),ẑ²·(1,0,0) = (-1,0,0), sot = (π/2)(1,0,0) + (1)(0,1,0) + (π/2 − 1)(-1,0,0) = (π/2, 0, 0) + (0, 1, 0) + (1 − π/2, 0, 0) = (1, 1, 0). The translation(1, 1, 0)is not(π/2)(1,0,0) = (π/2, 0, 0)— theJmatrix rotates the perpendicularvand adds a component alongy, the effect of the rotation on the translation.
Self-explanation 4. Before reading on: what is the screw interpretation of the general twist’s pose?
Answer. A rotation about
zbyπ/2plus a translation alongzofπ/2— a screw about thez-axis with pitch 1 (per radian), and the axis is thez-axis through the origin. The twist’svhas a component alongω(the pitch contribution) and a component perpendicular toω(the “moment,” which encodes the axis’s distance from the origin).
WE-L.2.b — Completion problem
Input. ξ = (0, 0, 1, 1, 0, 0), q = π/2. The v is perpendicular to ω.
Trace (final 25% as prompts).
ω = (0,0,1),v = (1,0,0),q = π/2.ẑ·v = (0,1,0),ẑ²·v = (-1,0,0). ⟨your turn: verify both cross products⟩t = (π/2 I + 1·ẑ + (π/2 − 1)·ẑ²) · (1,0,0). ⟨your turn: compute each term and sum⟩- Write the 4×4
exp(ξ̂ q). ⟨your turn⟩
Answers.
ẑ·(1,0,0) = (0,0,1) × (1,0,0) = (0·0 − 1·0, 1·1 − 0·0, 0·0 − 0·1) = (0, 1, 0).ẑ²·(1,0,0) = ẑ·(0,1,0) = (0,0,1) × (0,1,0) = (0·0 − 1·1, 1·0 − 0·0, 0·1 − 0·0) = (-1, 0, 0). ✓(π/2)(1,0,0) = (π/2, 0, 0);1·(0,1,0) = (0, 1, 0);(π/2 − 1)(-1,0,0) = (1 − π/2, 0, 0). Sum:(π/2 + 1 − π/2, 1, 0) = (1, 1, 0).exp(ξ̂ π/2) = [[Rz(π/2), (1, 1, 0)], [0, 1]].
The lesson. The J matrix’s effect is visible when v ⊥ ω: the translation (1, 1, 0) is not q·v = (π/2, 0, 0), and the difference is the J matrix’s rotation of v. The correct translation requires the J matrix; the naive q·v is wrong by (J − qI)v.
WE-L.2.c — Faded: the running case’s joint 2 twist
Setup. Joint 2 is a rotation about y at the point (a, 0, 0) at the home configuration, with a = 0.2. Its spatial twist is ξ₂ = (0, 1, 0, 0, 0, a) — angular ω = (0,1,0) and linear v = (0,0,a) (from v = −ω × q = −(0,1,0) × (0.2,0,0) = (0,0,0.2)).
Question. Compute exp(ξ̂₂ q₂) at q₂ = π/2 by hand, and verify it matches the chain FK’s T₁(0)·T₂(π/2) = Trans_x(a)·Rot_y(π/2)·Trans_x(a).
You must produce the intermediate steps. The setup and the answer are given.
Answer. ω = (0,1,0), v = (0,0,a), q = π/2.
exp(q ω̂) = Ry(π/2) = [[0,0,1],[0,1,0],[-1,0,0]].ŷ·vwithv = (0,0,a):ŷ = [[0,0,1],[0,0,0],[-1,0,0]], soŷ·(0,0,a) = (a, 0, 0); andŷ² = [[-1,0,0],[0,0,0],[0,0,-1]], soŷ²·(0,0,a) = (0, 0, -a).t = (π/2)(0,0,a) + (1)(a,0,0) + (π/2 − 1)(0,0,-a) = (0,0,πa/2) + (a,0,0) + (0,0,-a(π/2 − 1)) = (a, 0, πa/2 − a(π/2 − 1)) = (a, 0, a).exp(ξ̂₂ π/2) = [[Ry(π/2), (a, 0, a)], [0, 1]].
Chain FK: T₁(0)·T₂(π/2)·T₃(0)·…·T₆(0) = Trans_x(a)·Ry(π/2)·Trans_x(5a). Translation: (a,0,0) + Ry(π/2)·(5a,0,0) = (a,0,0) + (0,0,-5a) = (a, 0, -5a). PoE: exp(ξ̂₂ π/2)·M = [[Ry(π/2), (a,0,a)], [0,1]]·[[I, (6a,0,0)], [0,1]] = [[Ry(π/2), Ry(π/2)(6a,0,0) + (a,0,a)], [0,1]] = [[Ry(π/2), (0,0,-6a) + (a,0,a)], [0,1]] = [[Ry(π/2), (a, 0, -5a)], [0,1]]. ✓ Matches.
The lesson. The PoE’s unit-twist exp is not (exp(qω̂), q·v); the translation is (q I + (1−cos q)ω̂ + (q−sin q)ω̂²)v, and the (1−cos q) and (q−sin q) terms are what make the PoE match the chain FK. The near-miss that uses q·v gives (0,0,πa/2), which is wrong by (a, 0, a − πa/2).
WE-L.2.d — Contrastive near-miss: the exp without the left Jacobian
Correct solution. exp(ξ̂ q) = [[exp(q ω̂), (q I + (1−cos q)ω̂ + (q−sin q)ω̂²) v], [0, 1]].
Near-miss. exp(ξ̂ q) = [[exp(q ω̂), q·v], [0, 1]] — the translation is q·v (the “linear part times the angle”).
Why the near-miss produces a plausible but incorrect number. At v = 0 (pure rotation), q·v = 0, so the near-miss agrees. At v ∥ ω (pitch-only screw), J·v = q·v (the (1−cos) and (q−sin) terms vanish because ω̂·v = 0), so the near-miss agrees. At v ⊥ ω (the case in WE-L.2.b), J·v ≠ q·v, and the near-miss’s translation is wrong by (J − qI)v. The failure is silent for the pitch-only and pure-rotation cases, and visible for the perpendicular case — which is the common case in a robot arm (a joint’s link is perpendicular to the joint’s axis).
The discrepancy, stated. q·v vs. (q I + (1−cos q)ω̂ + (q−sin q)ω̂²)v differ by ((1−cos q)ω̂ + (q−sin q)ω̂²)v, which is the J matrix’s correction to the linear translation. The correction vanishes for ω̂·v = 0 (the pitch-only case) and is non-zero otherwise. For the running case’s joints, the links are perpendicular to the axes, so the correction is always non-zero, and the near-miss is always wrong.
Self-explanation 5. Before reading on: in the near-miss, if
vis perpendicular toω, what is the direction of the correction(J − qI)v?Answer.
ω̂·v = ω × vis perpendicular to bothωandv(the cross product’s direction);ω̂²·v = ω × (ω × v) = ω(ω·v) − v(ω·ω) = −vwhenv ⊥ ωand‖ω‖ = 1. So(J − qI)v = (1−cos q)(ω × v) + (q−sin q)(−v) = (1−cos q)(ω × v) − (q−sin q)v. The correction has two components: one alongω × v(perpendicular to bothωandv) and one along−v(opposite tov). The first component is the “rotation of the velocity” effect; the second is the “shortening” effect — the velocity’svcomponent is partially converted to rotation, so the net translation alongvis less thanq·v.
§L.2.3 — Comparative table: the forms of the SE(3) exponential
| Form | Translation block | Valid when | Where it is the right choice | Failure mode if misapplied |
|---|---|---|---|---|
Unit-twist, exp(ξ̂ q) with ‖ω‖ = 1 | (q I + (1−cos q)ω̂ + (q−sin q)ω̂²)v | The twist is normalized and the angle is the joint’s q | Product of exponentials: the twist is fixed, q varies | A non-unit twist silently rescales the angle and the pitch |
Arbitrary-twist, exp(ξ̂) with θ = ‖ω‖ | J(ω)v, J = I + (1−cos θ)/θ² ω̂ + (θ−sin θ)/θ³ ω̂² | The angle is baked into the twist’s magnitude | Learning: the network emits the twist | With a unit twist the angle is lost — the pose is always θ = 1 |
Naive linear, (exp(ω̂), q·v) | q·v | v = 0, or v ∥ ω | Never as a general formula | Wrong by (J − qI)v whenever v has a component off the axis |
Pure rotation (v = 0) | 0 | v = 0 | A revolute joint through the origin | J is irrelevant, so a missing-J bug hides here |
Pure translation (ω = 0) | v | ω = 0 | A prismatic joint | J = I, so a missing-J bug hides here too |
Log, v = J(ω)⁻¹ t | inverse map | ‖ω‖ < π | Recovering a twist from a pose | Using v = t is off by (J⁻¹ − I)t and inherits the π singularity |
Design principle (P7.5). Fix the normalization before you fix the formula. The unit-twist and arbitrary-twist forms are the same series under a rescaling of ξ; the only thing that distinguishes them is where the angle lives. Decide that first — a joint’s angle belongs in the exponent, a learned pose’s angle belongs in the twist’s magnitude — and the formula follows. A pipeline that mixes the two is not wrong in its algebra, it is wrong in its bookkeeping: the same twist read as arbitrary when it was unit produces a pose whose angle is 1 radian.
§L.2.4 — Concept check
Concept check (displaces M2). A learner says: “
expis applied blockwise, soexp((ω, v))is(exp(ω̂), v)— the rotation part exponentiates and the translation part is carried through.” What is the missing object, and what bug class does the omission produce?Answer. The missing object is the left Jacobian
J(ω): the translation block of the matrix exponential isΣ ω̂ⁿ⁻¹/n! · v = J(ω)v, notv, because theω̂powers in the series act onv.expis not a blockwise map — the off-diagonal block ofξ̂couples the two halves. The bug class is a translation that is right whenever the screw is degenerate: it is exact for a pure rotation (v = 0), for a pure translation (ω = 0), and for a pitch-only screw (v ∥ ω), so every unit test built on those three cases passes. It is wrong by(J − qI)vfor any link perpendicular to its joint’s axis, which in a serial arm is every link.
§L.2.5 — Consolidation checkpoint 2
(a) You should now be able to:
(i) write both forms of exp_se3 and state the normalization each assumes;
(ii) state the three cases in which J·v = v and why each one is degenerate;
(iii) state the log’s formula v = J(ω)⁻¹t and the domain on which it is defined.
(b) Transfer probe. A network outputs the arbitrary-form twist ξ = (0, 0, 2, 1, 0, 0), so θ = 2 rad about z with a linear part perpendicular to the axis. What translation does exp(ξ̂) produce, and what does the naive v predict?
Answer sketch: J·v = (0.4546, 0.7081, 0), of magnitude 0.8415, against the naive v = (1, 0, 0). The naive answer is wrong in direction as well as magnitude: J rotates the perpendicular component into y and shortens the x component, which is the same (1−cos θ)ω̂ + (θ−sin θ)ω̂² correction that WE-L.2.b exhibits at q = π/2. A learner who reports (1, 0, 0) has applied the blockwise exp; one who reports only the magnitude 0.8415 has found the correction but not its direction.
(c) Remediation rule. If you cannot do (a)(ii) or (b), return to §L.2.2 and redo WE-L.2.b, then re-attempt (b) by expanding (I + aω̂ + bω̂²)v term by term with ω̂v and ω̂²v written out.
Part C — Screw Motion: Axis, Pitch, and Classification
§L.3 — Objects: the screw parameters and the classification
A screw motion is a rigid motion that can be described as a rotation about an axis coupled with a translation along that axis. The four screw parameters are:
- Axis direction
u ∈ ℝ³,‖u‖ = 1— the direction of the screw’s axis. Ifω ≠ 0,u = ω/‖ω‖; ifω = 0, the “axis” is the direction ofv(a pure translation),u = v/‖v‖. - Point on the axis
q ∈ ℝ³— any point on the axis, computed asq = (ω × v)/‖ω‖²(forω ≠ 0), which is the point on the axis closest to the origin. - Pitch
h ∈ ℝ— the translation per radian along the axis,h = (ω · v)/‖ω‖²(forω ≠ 0); for a pure translation (ω = 0), the pitch is infinite (or, by convention,h = ∞). - Angle
θ = ‖ω‖— the rotation’s magnitude (for a pure translation,θ = ‖v‖is the translation’s magnitude, and the “rotation” is 0).
The classification of a twist:
- Pure rotation —
ω ≠ 0,v = 0(orω · v = 0andqsuch thatv = −ω × q): the pitch is 0, and the motion is a pure rotation about the axis throughq. - Pure translation —
ω = 0,v ≠ 0: the “axis” is in the direction ofv, and the motion is a pure translation. - General screw —
ω ≠ 0,v ≠ 0,ω · v ≠ 0: the motion is a rotation about an axis plus a translation along it, with a finite non-zero pitch.
The screw’s exp is the same SE(3) exp: exp(ξ̂) with ξ being the twist’s coordinates.
§L.3.1 — Result: the pitch and the point on the axis are the two numbers that classify a twist, and the pure cases are degenerate screws
Claim. Every twist ξ = (ω, v) ∈ se(3) is either a pure rotation (ω ≠ 0, v = 0), a pure translation (ω = 0), or a general screw with pitch h = (ω·v)/‖ω‖² and axis through q = (ω × v)/‖ω‖². The three cases are exhaustive and mutually exclusive (except at ξ = 0).
Proof sketch. If ω = 0, the twist is a translation. If ω ≠ 0 and v = 0, the twist is a pure rotation. If ω ≠ 0 and v ≠ 0, decompose v into its component along ω (which contributes a pitch h = (ω·v)/‖ω‖²) and its component perpendicular to ω (which, combined with ω, defines the axis’s point q by v_⊥ = −ω × q). The decomposition is unique, and the three cases correspond to which components are zero.
Corollary. The screw parameters (u, q, h, θ) are a minimal (4-number) description of a twist? No — a twist has 6 numbers, and the screw parameters (u, q, h, θ) have u (2 DOF, since ‖u‖ = 1), q (2 DOF, since q is constrained to the plane perpendicular to u), h (1 DOF), and θ (1 DOF) = 6 numbers. So the screw parameters are equivalent to the twist, but redundant in the sense that the axis point q has a free choice along the axis (which does not change the motion).
Corollary. The pitch is the only screw parameter that distinguishes a general screw from a pure rotation; a pure rotation has pitch 0, a pure translation has infinite pitch, and a general screw has a finite non-zero pitch. The point on the axis q is the “moment arm” of the twist — it is where the axis is, and it is what changes when the reference frame changes (the pitch and the axis direction are frame-invariant, but the point q is not).
§L.3.2 — Worked-example ladder
WE-L.3.a — Fully worked: classifying a twist, by hand
Input. ξ = (0, 0, 1, 2, 3, 4).
Trace.
ω = (0,0,1),v = (2,3,4),‖ω‖ = 1,‖v‖ = √29 ≈ 5.39.ω·v = 0·2 + 0·3 + 1·4 = 4. So the pitch ish = 4/1² = 4(translation of 4 per radian alongz).ω × v = (0·4 − 1·3, 1·2 − 0·4, 0·3 − 0·2) = (-3, 2, 0).q = (ω × v)/‖ω‖² = (-3, 2, 0).- So the screw is: axis along
zthroughq = (-3, 2, 0), pitch 4, angleθ = 1.
Output and interpretation. The twist is a general screw — rotation about z through (-3, 2, 0) with a pitch of 4 (translating 4 units along z per radian). The point q is the moment arm: the component of v perpendicular to ω is (2, 3, 0), and −ω × q = −(0,0,1) × (-3, 2, 0) = −(0·0 − 1·2, 1·(-3) − 0·0, 0·2 − 0·(-3)) = −(-2, -3, 0) = (2, 3, 0). So v_⊥ = (2, 3, 0) = −ω × q, which confirms q is correct.
Self-explanation 6. Before reading on: what is the physical motion of a point at the origin under this twist over a small time
dt?Answer. The point at the origin has velocity
v = (2, 3, 4). Overdt, it moves by(2dt, 3dt, 4dt)— thez-component is the pitch (translation along the axis), and thex- andy-components are the rotation’s effect at the origin (the origin is off-axis, at distance‖q‖ = √13 ≈ 3.61from the axis, so the rotation about the axis sweeps the origin in the(2, 3, 0)direction). Thev’s direction is(2, 3, 4)/5.39 ≈ (0.37, 0.56, 0.74), which is not along the axis(0, 0, 1)— it’s a combination of the pitch and the rotation’s contribution.
WE-L.3.b — Completion problem
Input. ξ = (0, 0, 0, 1, 2, 3) (pure translation).
Trace (final 25% as prompts).
ω = (0,0,0),v = (1,2,3),‖v‖ = √14. ⟨your turn: is this a pure translation?⟩- The “axis” is the direction of
v. ⟨your turn: what is the pitch? Is it finite, infinite, or undefined?⟩ - The angle
θ = ‖v‖(for the “translation magnitude”). ⟨your turn: what is the pointq?⟩
Answers.
- Yes —
ω = 0, so the twist is a pure translation. - The pitch is infinite (or “undefined” by convention): the motion has translation with no rotation, so the “translation per radian” is
‖v‖/0 = ∞. - The point
qis undefined — the axis of a pure translation is not a line but a direction (any line in the direction ofvhas the same screw motion). The “point on the axis” convention does not apply.
The lesson. A pure translation is the degenerate screw — it has an axis direction but no position, and its pitch is infinite. The screw parameters’ q and h are undefined (or infinite) in this case, and the twist’s classification is determined by the zero angular part, not by the formula’s limits.
WE-L.3.c — Faded: the running case’s joint 1 and joint 2 screws
Setup. Joint 1’s twist is ξ₁ = (0, 0, 1, 0, 0, 0); joint 2’s is ξ₂ = (0, 1, 0, 0, 0, a) with a = 0.2.
Question. Classify each twist (pure rotation, pure translation, or general screw), and compute each screw’s pitch.
You must produce the intermediate steps. The setup and the answer are given.
Answer. Joint 1: ω = (0,0,1), v = 0, so it is a pure rotation with pitch h = 0 (no translation along the axis). Joint 2: ω = (0,1,0), v = (0,0,0.2), ω·v = 0, so it is also a pure rotation with pitch h = 0 — the v is perpendicular to ω, so the v encodes the axis’s position (the axis is at q = (a, 0, 0)) but not a pitch.
The lesson. Both of the running case’s joints are pure rotations with zero pitch, because the link’s translation is perpendicular to the joint’s axis (the link is along x, the axis is z or y, so the cross product ω × v is the moment arm, not a pitch). The v’s role is to encode the position of the axis, not a pitch — the pitch is zero, and the v’s perpendicular component is what makes the twist a “rotation about an off-origin axis.”
WE-L.3.d — Contrastive near-miss: the wrong reference point
Correct solution. Joint 2’s twist at q_2 = 0 is ξ₂ = (0, 1, 0, 0, 0, a) — the axis is at (a, 0, 0) at the home configuration.
Near-miss. The learner uses ξ₂ = (0, 1, 0, 0, 0, 0) — the axis is at the origin, not at (a, 0, 0).
Why the near-miss produces a plausible but incorrect number. At q = 0, both twists give exp(0) = I, so the FK at q = 0 is the same. For q_2 ≠ 0, the near-miss’s twist is a rotation about y through the origin, which moves the end-effector to a different point from the correct twist (which is a rotation about y through (a, 0, 0)). The difference is (R_y(q_2) − I)(a, 0, 0) — the rotation’s effect on the moment arm — which is non-zero for q_2 ≠ 0 and grows with q_2. The failure is silent in the q = 0 test and visible in the q ≠ 0 test against the chain FK.
The discrepancy, stated. The correct twist has v = (0, 0, a) and the near-miss has v = 0. The difference is the moment arm −ω × q = (0, 0, a), which is what makes the rotation about (a, 0, 0) different from the rotation about the origin. The near-miss’s FK is the FK of a different arm — one whose joints are all at the origin — and the end-effector position differs by (R_y(q_2) − I)(a, 0, 0).
Self-explanation 7. Before reading on: in the near-miss, if the arm’s link lengths were zero (
a = 0), would the near-miss match the correct twist?Answer. Yes — if
a = 0, all the joints are at the origin (the arm is a “ball joint” with no links), and the correct twist isξ_i = (ω_i, 0), which is the near-miss’s twist. Theais the link length, and it is what makes the moment arm non-zero. The near-miss is the FK of the zero-link arm, and the correct twist is the FK of the a-link arm.
§L.3.3 — Comparative table: the screw parameters and their degenerate cases
| Parameter | Formula (for ω ≠ 0) | DOF | Value for a pure rotation | Value for a pure translation | What breaks if it is read as a velocity |
|---|---|---|---|---|---|
Axis direction u | ω/‖ω‖ | 2 | Defined | v/‖v‖ (a direction, not a line) | It is the rotation axis, not the direction of travel |
Point on the axis q | (ω × v)/‖ω‖² | 2 | The point nearest the origin on the axis | Undefined — any line along v serves | Reading q as the point that moves along v puts the axis in the wrong place |
Pitch h | (ω · v)/‖ω‖² | 1 | 0 | ∞ (‖v‖/0) | It is translation per radian, not a speed |
Angle θ | ‖ω‖ | 1 | The joint’s q | ‖v‖ by convention | It is a displacement, not an angular rate |
| Classification | which of ω, v, ω·v vanish | — | v = 0 | ω = 0 | A general screw misread as a rotation loses its pitch |
Design principle (P7.6). Classify the twist before you compute anything from it. The formulas for q and h divide by ‖ω‖², so they are defined on exactly the complement of the pure-translation case, and the “infinite pitch” of a prismatic joint is not a large number — it is the formula’s denominator being zero. Read the classification off the zeros first, then apply the formulas to the case that admits them. A pipeline that computes h unconditionally returns nan or an enormous number for a prismatic joint, and either value propagates into a controller as if it were a pitch.
§L.3.4 — Concept check
Concept check (displaces M3). A learner says: “A twist is
(ω, v)— the angular velocity and the linear velocity — sovis how fast the body’s origin is moving.” Which model is missing, and what bug class does it produce?Answer. The missing model is that
vis the velocity of a point on the screw axis, which equals the origin’s velocity only when the origin lies on that axis. For a joint atq ≠ 0, the origin’s velocity isv + ω × (0 − q) = v − ω × q, and the two differ by the moment arm. The bug class is a twist built from the wrong point: the learner writesξ = (ω, 0)for a joint whose axis is at(a, 0, 0), the FK is exact atq = 0becauseexp(0) = Ifor any twist, and it diverges from the chain FK for everyq ≠ 0by(R(q) − I)(a, 0, 0). Nothing raises:(ω, 0)is a perfectly legal twist, and it is the correct twist of a different arm whose joints all sit at the origin.
§L.3.5 — Consolidation checkpoint 3
(a) You should now be able to:
(i) compute the four screw parameters from a twist and state each one’s degree of freedom;
(ii) classify a twist as pure rotation, pure translation, or general screw from its zeros, and state which formulas are undefined in each degenerate case;
(iii) state why the pitch and the axis direction survive a change of frame while the point q does not.
(b) Transfer probe. Take joint 1’s twist ξ₁ = (0, 0, 1, 0, 0, 0) — a pure rotation about z through the origin — and move the frame by t = (2, 0, 0). What are the new screw parameters, and is the screw’s class unchanged?
Answer sketch: Ad_T ξ₁ = (0, 0, 1, 0, −2, 0), since R = I, Rv = 0 and t × (Rω) = (2,0,0) × (0,0,1) = (0,−2,0). The pitch is ω·v/‖ω‖² = 0 — unchanged — and q = (ω × v)/‖ω‖² = (2, 0, 0), which is exactly the translation t. The class is unchanged: still a pure rotation, now about the axis through (2, 0, 0). The pitch and the axis direction are frame-invariant; q moved with the frame, which is the sense in which q is a moment arm rather than a property of the motion.
(c) Remediation rule. If you cannot do (b), return to §L.3.2 and redo WE-L.3.d, then re-attempt (b) by asking which of the four parameters the adjoint is allowed to change.
Part D — The Adjoint Representation and Velocity Transformations
§L.4 — Objects: Ad_T, the body/spatial distinction, and velocity transformation
The adjoint representation of SE(3) is a map Ad : SE(3) → GL(6), defined for T = (R, t) by:
a 6×6 matrix. Its action on a twist ξ = (ω, v) is:
The angular part transforms as a direction (Rω); the linear part transforms as a velocity (Rv + t × (Rω)) — the Rv is the direction part, and the t × (Rω) is the cross-product term from the frame’s translation.
The body vs. spatial distinction:
- A spatial twist
$ξ_s$is expressed in the base frame, and it isAd_T · $ξ_b$whereTis the pose of the body frame relative to the base. - A body twist
$ξ_b$is expressed in the body’s own frame, and it is constant for a given joint. - The two are related by
$ξ_s$ = Ad_T $ξ_b$and$ξ_b$ = Ad_{T^{-1}} $ξ_s$.
For the running case, the body twists are constant (each joint’s axis in its own frame), and the spatial twists are $Ad_{T_i(0)}$ $ξ_b$,i where $T_i(0$) is the joint’s home pose — which is why the spatial twists depend on the home configuration’s $q_i$ and the body twists do not.
§L.4.1 — Result: the adjoint is the velocity’s transformation law, and it is not T
Claim. For T = (R, t) ∈ SE(3) and ξ = (ω, v) ∈ se(3), Ad_T ξ = (Rω, t × (Rω) + Rv), and this is not the same as T · [ω; v] (which is not a legal operation, because T is 4×4 and [ω; v] is 6×1). The adjoint’s linear part has a cross-product term t × (Rω) that a simple T-multiplication does not have.
Proof sketch. The adjoint is the derivative of the conjugation map T ↦ T T' T^{-1} at the identity, which is Ad_T ξ̂ = T ξ̂ T^{-1} for ξ̂ ∈ se(3). Computing T ξ̂ T^{-1} with T = [[R, t], [0, 1]] and ξ̂ = [[ω̂, v], [0, 0]] gives [[Rω̂R^{-1}, Rv − Rω̂R^{-1}t], [0, 0]] = [[\widehat{Rω}, Rv − (Rω) × t], [0, 0]], which is Ad_T ξ = (Rω, Rv + t × (Rω)). The cross-product term is the t × (Rω).
Corollary. The adjoint is the velocity’s transformation law, not the pose’s — it is what transforms a twist (a velocity) between frames, and it is different from T’s action on a point or a direction. The cross-product term is the “moment arm” of the translation on the angular velocity.
Corollary. The adjoint’s inverse is Ad_{T^{-1}} = [[Rᵀ, 0], [−Rᵀt̂, Rᵀ]], which is the inverse of the 6×6 matrix, and it satisfies Ad_T Ad_{T^{-1}} = $I_6$. The inverse’s cross-product term is −Rᵀt̂, which is the reverse transformation.
§L.4.2 — Worked-example ladder
WE-L.4.a — Fully worked: the adjoint of a specific transform, by hand
Input. T = [[0, -1, 0, 1], [1, 0, 0, 2], [0, 0, 1, 3], [0, 0, 0, 1]] (Rz(π/2) with t = (1, 2, 3)), ξ = (0, 0, 1, 0, 0, 0) (rotation about z).
Trace.
ω = (0,0,1),v = 0.Rω = Rz(π/2)·(0,0,1) = (0,0,1).Rv = 0.t × (Rω) = (1,2,3) × (0,0,1) = (2·1 − 3·0, 3·0 − 1·1, 1·0 − 2·0) = (2, -1, 0).Ad_T ξ = (0, 0, 1, 2, -1, 0).
Output and interpretation. The transformed twist is a rotation about z with a linear part (2, -1, 0) — the rotation is about the same axis, but the point of the rotation has shifted: the original twist is a rotation about z through the origin, and the transformed twist is a rotation about z through the point t = (1, 2, 3). The linear part (2, -1, 0) is −ω × q at q = (1, 2, 3): ω × q = (0,0,1) × (1,2,3) = (0·3 − 1·2, 1·1 − 0·3, 0·2 − 0·1) = (-2, 1, 0), so −ω × q = (2, -1, 0). The adjoint by T shifts the axis by t.
Self-explanation 8. Before reading on: in step 4, why is t × (Rω) = (2, -1, 0) and not (0, 0, 0)? What does it mean geometrically?
Answer.
t × (Rω)is non-zero becausetis not parallel toRω. Geometrically,t × (Rω)is the moment of the translation about the rotation axis — the component of the axis’s shift that is perpendicular to the axis. Iftwere along the axis (t ∥ Rω), the cross product would be zero, and the adjoint would only rotate the twist (no shift). Sincet = (1, 2, 3)is not alongz, the cross product is non-zero, and the transformed twist’s axis is shifted bytperpendicular toz— i.e., the axis moves from the origin to(1, 2, 0)(the perpendicular part oft).
WE-L.4.b — Completion problem
Input. Same T as above, ξ = (0, 0, 1, 0, 0, 1) (rotation about z with pitch 1).
Trace (final 25% as prompts).
ω = (0,0,1),v = (0,0,1).Rω = (0,0,1),Rv = (0,0,1). ⟨your turn: verifyRvby computingRz(π/2)·(0,0,1)⟩t × (Rω) = (1,2,3) × (0,0,1) = (2, -1, 0). ⟨your turn: verify⟩Ad_T ξ = (Rω, Rv + t × (Rω)) = (0,0,1, 2, -1, 1). ⟨your turn: sum the components⟩
Answers.
Rz(π/2)·(0,0,1) = (0,0,1)(thez-axis is fixed by az-rotation). ✓(2, -1, 0). ✓(0,0,1) + (2, -1, 0) + (0,0,1)— the third summand is wrong.Rv = (0,0,1)is the onlyRvcontribution; adding it twice counts it once asRvand once as if it were a second moment term. The correct sum isRv + t × (Rω) = (0,0,1) + (2, -1, 0) = (2, -1, 1), soAd_T ξ = (0, 0, 1, 2, -1, 1).
The lesson. The adjoint’s linear part has two contributions: Rv (the twist’s linear part, transformed as a direction) and t × (Rω) (the moment arm from the translation). Both are non-zero here (the pitch is v = (0,0,1) and the moment is (2, -1, 0)), and the total is (2, -1, 1). The pitch is the invariant that catches the error: a screw’s pitch ω·v / ‖ω‖² must survive the adjoint unchanged, and it does — (0,0,1)·(2,-1,1) = 1 — whereas the mis-summed (2, -1, 2) gives pitch 2. That is the H4 gate, and it is worth checking every adjoint against it.
WE-L.4.c — Faded: the running case’s velocity in two frames
Setup. The end-effector’s velocity in the spatial (base) frame is $v_s$ = (1, 0, 0, 0, 0, 0) (a pure translation along x), and the end-effector’s pose is T = [[0, -1, 0, 1], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] (a π/2 rotation about z at position (1, 0, 0)).
Question. What is the velocity in the body frame? Use $ξ_b$ = Ad_{T^{-1}} $ξ_s$.
You must produce the intermediate steps. The setup and the answer are given.
Answer. Rᵀ = [[0, 1, 0], [-1, 0, 0], [0, 0, 1]], so Rᵀt = Rᵀ(1,0,0) = (0, -1, 0) and −Rᵀt = (0, 1, 0). Hence T^{-1} = [[0, 1, 0, 0], [-1, 0, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]] — Rz(−π/2) at (0, 1, 0).
Ad_{T^{-1}} $ξ_s$ = (Rᵀ $ω_s$, Rᵀ $v_s$ + (−Rᵀt) × (Rᵀ $ω_s$)). $ω_s$ = 0, $v_s$ = (1,0,0), Rᵀ $v_s$ = Rᵀ(1,0,0) = (0, -1, 0). (−Rᵀt) × (Rᵀ $ω_s$) = (0,1,0) × 0 = 0. So $ξ_b$ = (0, 0, 0, 0, -1, 0).
The lesson. The spatial velocity (1, 0, 0) (translation along x in the base frame) becomes the body velocity (0, -1, 0) (translation along −y in the body frame), because the body frame is rotated π/2 about z. The magnitude is preserved (both are unit vectors), and the direction is rotated by Rᵀ — the adjoint’s R part for the angular part, and the Rᵀ part for the linear part (since the linear part of a pure translation is a direction, not a velocity, and transforms by Rᵀ in the inverse adjoint).
WE-L.4.d — Contrastive near-miss: the 4×4 T applied to a twist
Correct solution. The adjoint Ad_T (a 6×6 matrix) transforms the twist: $ξ_A$ = Ad_T $ξ_B$.
Near-miss. The learner applies the 4×4 T to the twist’s linear part and the 3×3 R to the twist’s angular part: $ω_A$ = R $ω_B$, $v_A$ = T · $v_B$ (treating $v_B$ as a point). This gives $v_A$ = R $v_B$ + t, missing the cross-product term (and adding a spurious t).
Why the near-miss produces a plausible but incorrect number. The near-miss’s $ω_A$ = R $ω_B$ is correct (the angular part transforms as a direction). The near-miss’s $v_A$ = R $v_B$ + t is wrong by the cross-product term t × (Rω_B) and has an extra t that shouldn’t be there (the twist’s v is a velocity, not a point, so it should not get the +t). The result is a “velocity” that is R $v_B$ + t — the true velocity plus a spurious translation, and missing the cross-product term. The failure is silent (the result is a 3-vector), and the end-effector’s velocity is systematically wrong.
The discrepancy, stated. The correct adjoint Ad_T ξ = (Rω, Rv + t × (Rω)) vs. the near-miss (Rω, Rv + t). The near-miss is missing the cross-product term t × (Rω) and adding the spurious t. The two are equal only when t × (Rω) = 0 (i.e., t is parallel to Rω) and t = 0 (which is contradictory unless t = 0), so the near-miss is essentially never correct.
Self-explanation 9. Before reading on: in the near-miss, why does the twist’s v not get the +t that a point gets?
Answer. Because
vis a velocity, not a point. A velocity’s transformation is$v_A$ = R $v_B$ + t × (Rω_B), which is the velocity of the point atB’s origin (inB’s frame, which is$v_B$), plus the velocity contributed by the rotation about the axis atA’s origin. Thet × (Rω_B)term is the moment arm of the rotation, not a translation of the velocity. The+tthat a point gets is because the point’s position includes the translation; the velocity’s value does not include a+t— it includes thet × (Rω)term. The near-miss’s+tis the mistake of treating a velocity like a point.
§L.4.3 — Comparative table: the transformation laws of the four objects
| Object | Size | Law | Cross-product term | Inverse | Symptom when the wrong law is used |
|---|---|---|---|---|---|
| Point | 3 | $p_A$ = R $p_B$ + t | none — the +t is the translation | $p_B$ = Rᵀ($p_A$ − t) | The point is placed correctly and the velocity is not |
| Direction | 3 | $d_A$ = R $d_B$ | none, no +t | $d_B$ = Rᵀ $d_A$ | A direction acquires a spurious translation |
| Pose | 4×4 | $T_A^B$ composed in chain order | none | (Rᵀ, −Rᵀt) | A pose is treated as a velocity and loses its frame |
| Twist | 6 | Ad_T ξ = (Rω, Rv + t × (Rω)) | t × (Rω) | Ad_{T⁻¹} = [[Rᵀ, 0], [−Rᵀt̂, Rᵀ]] | Missing t×(Rω) and adding +t — the near-miss of WE-L.4.d |
Design principle (P7.7). Identify the object before you pick its transformation law. The four laws share the R factor and differ in the translation handling: a point gets +t, a direction gets nothing, a pose is not transformed by a matrix-vector product at all, and a twist gets t × (Rω) and not +t. Because the rotation part is the same in all four, a wrong choice is invisible in the angular half of the answer and appears only in the linear half — which is why the discriminator is the pitch, the one scalar the adjoint must preserve.
§L.4.4 — Concept check
Concept check (displaces M4). A learner says: “The adjoint is just the pose applied to a twist —
Tis 4×4 and the twist is 6 numbers, so I pad the twist to a 4-vector and multiply.” What is the missing structure, and what bug class follows?Answer. The adjoint is a 6×6 matrix
[[R, 0], [t̂R, R]], and it is the derivative of conjugationT ξ̂ T⁻¹, not a matrix-vector product in the pose’s own dimension. Padding cannot work: a twist is not a homogeneous point, and there is no fourth coordinate to append. The bug class is the near-miss of WE-L.4.d —$ω_A$ = Rω_Bcorrect,$v_A$ = Rv_B + twrong both ways, missing the cross-product termt × (Rω_B)and adding a translation that a velocity does not receive. It is silent because the output is a well-formed 3-vector, and it is detectable only against a quantity the transform must leave alone: the pitchω·v/‖ω‖², whichRv + tchanges andRv + t × (Rω)does not.
§L.4.5 — Consolidation checkpoint 4
(a) You should now be able to:
(i) write Ad_T and its inverse in block form and state why the linear part carries t̂R and not R;
(ii) transform a twist between frames by hand and check the result against the pitch;
(iii) state the body/spatial distinction and which of the two is constant as the arm moves.
(b) Transfer probe. A body twist is $ξ_b$ = (0, 0, 1, 0, 0, 0) and the body’s pose is T = (I, (0, 0, 1)) — the body is one unit up the z-axis. Compute the spatial twist, then say whether the two are the same physical motion.
Answer sketch: t × (Rω) = (0,0,1) × (0,0,1) = 0, so $ξ_s$ = Ad_T $ξ_b$ = (0, 0, 1, 0, 0, 0) — identical to $ξ_b$. They are the same physical motion, and the adjoint is the identity on twists whose axis is parallel to the frame’s translation: translating a frame along the rotation axis does not move the axis, so nothing changes. The learner who expects every adjoint to change the twist has not modeled t × (Rω) as a moment arm — a moment arm is zero when the lever is parallel to the force.
(c) Remediation rule. If you cannot do (b), return to §L.4.2 and redo WE-L.4.a, then re-attempt (b) by asking which of the two terms in Rv + t × (Rω) can be non-zero when t ∥ ω.
Part E — Failure Taxonomy, Lab, Cheat Sheet, Exercises, Exit Test
§L.5 — Failure taxonomy
| Symptom | Three most likely causes, ranked | Discriminating test | Fix |
|---|---|---|---|
PoE FK matches at q = 0 but drifts at q ≠ 0 | (1) Missing J in exp (using q·v); (2) wrong twist (body vs. spatial); (3) exp composition in the wrong order | Compare against the chain FK at a specific non-zero q; if the drift is proportional to q, it is (1) | Add the (1−cos)ω̂ + (q−sin)ω̂² terms; use the correct twist; use the chain order |
Velocity transformed by Ad_T is the wrong magnitude | (1) Missing cross-product term t×(Rω); (2) using T instead of Ad_T; (3) the R vs. Rᵀ confusion | Compute the adjoint by hand and compare; if the result has t but no t×, it is (2) | Use Ad_T = [[R, 0], [t̂R, R]] |
| The screw’s pitch is wrong | (1) Pitch formula (ω·v)/‖ω‖² vs. (ω·v)/‖ω‖; (2) v vs. J·v confusion; (3) the axis direction is wrong | Compute the pitch from the twist and from the pose’s translation, and compare | Use (ω·v)/‖ω‖² (with the norm squared); check the twist |
| The body and spatial twists disagree | (1) The frame convention is confused; (2) the adjoint’s R is used instead of Rᵀ (or vice versa); (3) the twist’s reference point is wrong | Compute Ad_T $ξ_b$ and compare to the spatial twist directly | Use $ξ_s$ = Ad_T $ξ_b$ with the correct T = $T_body_to_spatial$ |
The chain FK and PoE FK differ at q = 0 | (1) The home configuration M is wrong; (2) the twists’ reference is wrong; (3) the product’s order is wrong | Check exp(0)·M = M and compare to $T_chain(0$) | Fix M (should be the chain FK at q = 0); fix the twists |
§L.6 — The lab
§L.6.1 — Task
Implement exp_so3, exp_se3 (both forms), and Ad_T, and use them to verify the PoE FK against the chain FK; then break the PoE three ways.
§L.6.2 — Conditions
Python 3.10+, PyTorch 2.2+, CPU only. dtype = float64. Seed 42. Runtime under 2 minutes.
§L.6.3 — Controls held fixed
Seed, dtype, link length a = 0.2, and the test configuration q = [0.3, 0.5, -0.2, 0.1, 0.4, -0.3] are fixed.
§L.6.4 — Metrics table
| Metric | Definition | How measured |
|---|---|---|
poe_vs_chain_error | ‖$T_poe(q$) − $T_chain(q$)‖_max at the test q | The max entry difference |
poe_vs_chain_q0 | The same at q = 0 | Both FK formulas at q = 0 |
exp_jacobian_effect | ‖$t_arbitrary$ − v‖ for a twist with v ⊥ ω | The difference between J·v and v |
adjoint_check | ‖Ad_T $ξ_b$ − $ξ_s$‖ where $ξ_s$ = Ad_T $ξ_b$ | The identity check |
adjoint_vs_T_error | ‖Ad_T ξ − (Rω, Rv + t)‖ | The near-miss’s error |
§L.6.5 — Pre-registered hypotheses
- H1.
poe_vs_chain_error < 1e-10at the testq— the PoE and the chain FK match exactly (up to floating point) when the twists andMare correct. The prediction is< 1e-10. - H2.
exp_jacobian_effect > 0.1for a twist withv ⊥ ωandq = π/2— theJmatrix’s effect is large, not negligible. The prediction is> 0.1. - H3.
adjoint_vs_T_error > 1.0— the 4×4T-multiplication’s result is very different from the adjoint’s. The prediction is> 1.0. - H4.
Ad_T ξpreserves the pitch — the pitch ofAd_T ξequals the pitch ofξ(the pitch is invariant under a change of frame). The prediction is equality within1e-10.
§L.6.6 — The break-it sequence
- Missing
J(silent). Replaceexp_se3’s translation withq·v. Predicted symptom: no exception; the PoE vs. chain FK drifts atq ≠ 0. Actual: as predicted — silent. - Wrong twist (silent). Use
ξ = (ω, 0)(origin-axis) instead ofξ = (ω, −ω×q)(off-origin). Predicted symptom: no exception; the FK differs by the moment arm. Actual: as predicted — silent. - Wrong adjoint (silent). Use
Tinstead ofAd_Tfor the velocity. Predicted symptom: no exception; the velocity is wrong by the cross-product term. Actual: as predicted — silent.
All three are silent.
§L.6.7 — Acceptance criteria
| Criterion | Threshold | Artefact it is tested on |
|---|---|---|
PoE vs. chain at test q | < 1e-10 | The lab’s error print |
J effect | > 0.1 | The lab’s exp_jacobian_effect |
Adjoint vs. T error | > 1.0 | The lab’s comparison |
| Pitch preserved under adjoint | < 1e-10 | The lab’s pitch check |
| Three break-its predicted | 3/3 | The prediction file |
§L.6.8 — What good looks like
A strong submission has five properties:
- The PoE and chain FKs match at the test
q(not justq = 0), with the error printed as a number, and the match is not taken for granted. - The
Jmatrix’s effect is shown numerically — the difference betweenJ·vandvis a specific number, not just “non-zero.” - The adjoint’s cross-product term is isolated — the submission shows
Ad_T ξ = (Rω, Rv + t×(Rω))andRv + tseparately, and the difference is the cross-product term. - The pitch’s invariance is verified — the pitch of the body twist and the spatial twist are equal, and the equality is printed.
- The three break-its are predicted before running, with numeric bounds, and the actuals are compared.
§L.6.9 — Runnable lab code
# lab_0_2_3.py — Twists, exp_se3, PoE FK, and the adjoint
# Requires: Python 3.10+, PyTorch 2.2+
import torch, math
torch.manual_seed(42)
dtype, device = torch.float64, torch.device("cpu")
def make_T(R, t):
T = torch.eye(4, dtype=dtype, device=device); T[:3, :3] = R; T[:3, 3] = t; return T
def skew(w):
return torch.tensor([[0, -w[2], w[1]], [w[2], 0, -w[0]], [-w[1], w[0], 0]], dtype=dtype, device=device)
def exp_so3(w):
th = w.norm()
if th < 1e-12: return torch.eye(3, dtype=dtype, device=device) + skew(w)
u = w / th; U = skew(u)
return torch.eye(3, dtype=dtype, device=device) + math.sin(th)*U + (1 - math.cos(th))*(U @ U)
def exp_se3_unit(xi, q):
"""Unit twist (||omega||=1) at joint angle q."""
w, v = xi[:3], xi[3:]
th = q; W = skew(w)
R = torch.eye(3, dtype=dtype, device=device) + math.sin(th)*W + (1 - math.cos(th))*(W @ W)
J = th*torch.eye(3, dtype=dtype, device=device) + (1 - math.cos(th))*W + (th - math.sin(th))*(W @ W)
return make_T(R, J @ v)
def exp_se3_arbitrary(xi):
"""Arbitrary twist, angle baked into the twist magnitude."""
w, v = xi[:3], xi[3:]
th = w.norm(); W = skew(w)
if th < 1e-12: return make_T(torch.eye(3, dtype=dtype, device=device), v)
R = exp_so3(w)
J = torch.eye(3, dtype=dtype, device=device) + ((1 - math.cos(th))/th**2)*W + ((th - math.sin(th))/th**3)*(W @ W)
return make_T(R, J @ v)
def adjoint(T):
R, t = T[:3, :3], T[:3, 3]
A = torch.zeros(6, 6, dtype=dtype, device=device)
A[:3, :3] = R; A[3:, 3:] = R; A[3:, :3] = skew(t) @ R
return A
# --- The running case: home configuration and spatial twists ---
a = 0.2
M = make_T(torch.eye(3, dtype=dtype, device=device), torch.tensor([6*a, 0., 0.], dtype=dtype, device=device))
# Spatial twists at home config: joint i is at (i-1)*a along x, axis alternates z, y, z, ...
axes = ['z', 'y', 'z', 'y', 'z', 'y']
twists = []
for i, ax in enumerate(axes):
$q_i$ = torch.tensor([(i)*a, 0., 0.], dtype=dtype, device=device)
w = torch.tensor([0., 0., 1.] if ax == 'z' else [0., 1., 0.], dtype=dtype, device=device)
v = -torch.cross(w, $q_i$) # v = -omega x q
twists.append(torch.cat([w, v]))
print("twists (spatial):")
for i, xi in enumerate(twists):
print(f" xi_{i+1}: {xi.tolist()}")
# --- Chain FK (from 0.2.1) ---
def rot_z(q): c, s = math.cos(q), math.sin(q); return torch.tensor([[c,-s,0],[s,c,0],[0,0,1]], dtype=dtype, device=device)
def rot_y(q): c, s = math.cos(q), math.sin(q); return torch.tensor([[c,0,s],[0,1,0],[-s,0,c]], dtype=dtype, device=device)
def chain_fk(q):
T = torch.eye(4, dtype=dtype, device=device)
for i, ax in enumerate(axes):
R = rot_z(q[i]) if ax == 'z' else rot_y(q[i])
# The link offset is expressed in the ROTATED frame: it is R·[a,0,0],
# not [a,0,0]. Getting this backwards is the bug H1 exists to catch.
T = T @ make_T(R, R @ torch.tensor([a, 0., 0.], dtype=dtype, device=device))
return T
def poe_fk(q):
T = torch.eye(4, dtype=dtype, device=device)
for i in range(6):
T = T @ exp_se3_unit(twists[i], q[i])
return T @ M
q = torch.tensor([0.3, 0.5, -0.2, 0.1, 0.4, -0.3], dtype=dtype, device=device)
$T_chain$ = chain_fk(q); $T_poe$ = poe_fk(q)
print$f"\nchain t: {T_chain[:3, 3].tolist()}"$
print(f"poe t: {T_poe[:3, 3].tolist()}")
print(f"poe_vs_chain_error: {(T_poe - $T_chain$).abs().max().item():.2e}")
# --- exp_jacobian_effect ---
xi = torch.tensor([0., 0., 1., 1., 0., 0.], dtype=dtype, device=device)
$q_test$ = math.pi / 2
$T_exp$ = exp_se3_unit(xi, $q_test$)
print$f"\nexp translation: {T_exp[:3, 3].tolist()}"$ # (1, 1, 0)
print(f"naive q*v: {(q_test * xi[3:]).tolist()}") # (π/2, 0, 0) — the translation without J
print(f"exp_jacobian_effect: {(T_exp[:3, 3] - $q_test$ * xi[3:]).norm().item():.4f}")
# --- adjoint ---
T = make_T(rot_z(math.pi/2), torch.tensor([1., 2., 3.], dtype=dtype, device=device))
xi_b = torch.tensor([0., 0., 1., 0., 0., 0.], dtype=dtype, device=device)
xi_s = adjoint(T) @ xi_b
print$f"\nAd_T xi_b: {xi_s.tolist()}"$
print(f"T (4x4) applied to (omega, v): {(T[:3, :3] @ xi_b[:3]).tolist()} (omega only)")
# --- pitch invariance ---
def pitch(xi):
w, v = xi[:3], xi[3:]
return (w @ v) / (w @ w).clamp(min=1e-12)
print$f"\npitch(xi_b): {pitch(xi_b).item():.6f}"$
print(f"pitch(Ad_T xi_b): {pitch(xi_s).item():.6f}") # should be 0
# xi_b has v = 0, so its pitch is 0 trivially -- a zero that any map would
# produce. Test the invariance on a twist with a NON-ZERO linear part, or the
# check cannot tell Ad_T from a map that discards v.
xi_b2 = torch.tensor([0., 0., 1., 0.3, 0.5, 0.7], dtype=dtype, device=device)
xi_s2 = adjoint(T) @ xi_b2
print(f"pitch(xi_b2) = {pitch(xi_b2).item():.6f}, "
f"pitch(Ad_T xi_b2) = {pitch(xi_s2).item():.6f} (non-degenerate: the two must match)")
# --- break-it #1: missing J ---
def exp_se3_missing_J(xi, q):
w, v = xi[:3], xi[3:]
W = skew(w)
R = torch.eye(3, dtype=dtype, device=device) + math.sin(q)*W + (1 - math.cos(q))*(W @ W)
return make_T(R, q * v) # missing the (1-cos) and (q-sin) terms
def poe_missing_J(q):
T = torch.eye(4, dtype=dtype, device=device)
for i in range(6):
T = T @ exp_se3_missing_J(twists[i], q[i])
return T @ M
print(f"\n[break-it #1] missing J: t = {poe_missing_J(q)[:3, 3].tolist()}")
print(f"[break-it #1] correct: t = {T_poe[:3, 3].tolist()}")
# --- break-it #2: wrong twist (origin axis) ---
twists_wrong = []
for i, ax in enumerate(axes):
w = torch.tensor([0., 0., 1.] if ax == 'z' else [0., 1., 0.], dtype=dtype, device=device)
twists_wrong.append(torch.cat([w, torch.zeros(3, dtype=dtype, device=device)]))
def poe_wrong_twists(q):
T = torch.eye(4, dtype=dtype, device=device)
for i in range(6):
T = T @ exp_se3_unit(twists_wrong[i], q[i])
return T @ M
print(f"\n[break-it #2] origin-axis: t = {poe_wrong_twists(q)[:3, 3].tolist()}")
print(f"[break-it #2] correct: t = {T_poe[:3, 3].tolist()}")
# --- break-it #3: wrong adjoint (4x4 T instead of 6x6 Ad) ---
xi_wrong = torch.cat([T[:3, :3] @ xi_b[:3], T[:3, 3] + T[:3, :3] @ xi_b[3:]])
print(f"\n[break-it #3] T (4x4) applied: {xi_wrong.tolist()}")
print(f"[break-it #3] Ad_T applied: {xi_s.tolist()}")
# --- summary ---
print("\n--- lab 0.2.3 summary ---")
print(f"H1: poe_vs_chain_error = {(T_poe - $T_chain$).abs().max().item():.2e}")
print(f"H2: exp_jacobian_effect = {(T_exp[:3, 3] - $q_test$ * xi[3:]).norm().item():.4f}")
print(f"H3: Ad_T vs T error = {(xi_s - xi_wrong).norm().item():.4f}")
print(f"H4: pitch(xi_b)={pitch(xi_b).item():.6f}, pitch(Ad_T xi_b)={pitch(xi_s).item():.6f}")
§L.7 — The cheat sheet
| Term or result | Statement or definition | Role |
|---|---|---|
so(3) | 3×3 skew-symmetric matrices; 3-dim vector space | Introduced in 0.2.2; restated here as the algebra half of the algebra-vs-group distinction |
se(3) | 4×4 matrices [[ω̂, v], [0, 0]]; 6-dim vector space | Introduced here; consumed by 1.1.1 |
| Bracket | [A, B] = AB − BA; on se(3), [(ω₁,v₁),(ω₂,v₂)] = (ω₁×ω₂, ω₁×v₂ − ω₂×v₁) | Introduced here; consumed by 1.1.1 |
| Twist | A 6-vector ξ = (ω, v) ∈ se(3) | Introduced here; consumed by 1.1.1, 1.1.2 |
| Left Jacobian | J(ω) = I + (1−cos θ)/θ² ω̂ + (θ−sin θ)/θ³ ω̂² | Introduced here; consumed by 1.1.1, 1.1.2 |
exp_se3 | exp(ξ̂ q) = [[exp(qω̂), (q I + (1−cos q)ω̂ + (q−sin q)ω̂²)v], [0, 1]] | Introduced here; consumed by 1.1.1 |
log_se3 | The inverse; v = J(ω)^{-1} t | Introduced here; consumed by 1.1.2 |
| Screw motion | Axis (u, q), pitch h, angle θ | Introduced here; consumed by 1.1.1 |
| Pitch | h = (ω·v)/‖ω‖² | Introduced here; consumed by 1.1.1 |
| Adjoint | Ad_T = [[R, 0], [t̂R, R]]; transforms twists | Introduced here; consumed by 1.1.1, 1.1.2 |
| Body vs. spatial | Body in joint’s frame (constant); spatial in base (constant at home) | Introduced here; consumed by 1.1.1 |
| PoE FK | T(q) = exp(ξ̂₁q₁)…exp(ξ̂₆q₆)·M | Introduced here; consumed by 1.1.1 |
| Design principle P7.1 | Use the unit-twist form for PoE, the arbitrary-twist form for learning | Introduced here; consumed by 1.1.1, 3.1.2 |
| Design principle P7.2 | Verify PoE against the chain at a non-zero q | Introduced here; consumed by 1.1.1 |
| Design principle P7.3 | The adjoint is the velocity’s law, not T | Introduced here; consumed by 1.1.1 |
| Design principle P7.4 | Add in the algebra, multiply in the group | Introduced here; consumed by 1.1.1’s PoE |
| Design principle P7.5 | Fix the normalization before you fix the formula | Introduced here; consumed by 1.1.1, 3.1.2 |
| Design principle P7.6 | Classify the twist before you compute anything from it | Introduced here; consumed by 1.1.1, 1.1.2 |
| Design principle P7.7 | Identify the object before you pick its transformation law | Introduced here; consumed by 1.1.1, 2.1.1 |
§L.8 — Exercises with solutions
E 0.2.3.1 — The bracket (numerical). ξ₁ = (0, 1, 0, 0, 0, 0), ξ₂ = (0, 0, 1, 0, 0, 0). Compute [ξ₁, ξ₂].
S 0.2.3.1. ω₁ = (0,1,0), ω₂ = (0,0,1), v₁ = v₂ = 0. ω₁ × ω₂ = (1·1 − 0·0, 0·0 − 0·1, 0·0 − 1·0) = (1, 0, 0). ω₁ × v₂ − ω₂ × v₁ = 0. So [ξ₁, ξ₂] = (1, 0, 0, 0, 0, 0). The lesson: the bracket of the y- and z-rotations is the x-rotation; the pattern x̂ → ŷ → ẑ → x̂ is the cyclic structure of so(3)’s bracket.
E 0.2.3.2 — exp of a pure rotation (numerical). ξ = (0, 0, 1, 0, 0, 0), q = π. Compute exp(ξ̂ q).
S 0.2.3.2. ω = (0,0,1), v = 0, q = π. exp(qω̂) = Rz(π) = [[-1, 0, 0], [0, -1, 0], [0, 0, 1]]. t = 0. So exp = [[Rz(π), 0], [0, 1]]. The lesson: a pure rotation about z by π gives Rz(π) with zero translation, and the π is the half-turn that 0.2.2 flagged as the log’s singularity.
E 0.2.3.3 — exp of a general twist (numerical). ξ = (0, 0, 1, 1, 0, 0), q = π/2. Compute exp(ξ̂ q).
S 0.2.3.3. From WE-L.2.b, t = (1, 1, 0), so exp = [[Rz(π/2), (1, 1, 0)], [0, 1]]. The lesson: the J matrix’s effect is (1, 1, 0) − (π/2, 0, 0) = (1 − π/2, 1, 0), the difference from the naive q·v.
E 0.2.3.4 — The adjoint (numerical). T = [[-1, 0, 0, 3], [0, -1, 0, 4], [0, 0, 1, 5], [0, 0, 0, 1]] (Rz(π) with t = (3,4,5)), ξ = (0, 0, 1, 0, 0, 0). Compute Ad_T ξ.
S 0.2.3.4. Rω = Rz(π)·(0,0,1) = (0,0,1). Rv = 0. t × (Rω) = (3,4,5) × (0,0,1) = (4·1 − 5·0, 5·0 − 3·1, 3·0 − 4·0) = (4, -3, 0). So Ad_T ξ = (0, 0, 1, 4, -3, 0). The lesson: the adjoint shifts the rotation’s axis from the origin to t, and the linear part is the moment arm t × (Rω).
E 0.2.3.5 — The pitch (diagnostic). A twist’s pitch is h = 0. What does this mean physically, and what class of twist is it?
S 0.2.3.5. h = (ω·v)/‖ω‖² = 0 means ω·v = 0, so the linear part v is perpendicular to the axis ω. Physically, this is a pure rotation — the motion has no translation along the axis, and the v is entirely the moment arm (the axis is off-origin, and the v encodes its position). The class is pure rotation, and it is the running case’s joints’ class. The lesson: h = 0 distinguishes a pure rotation from a general screw, and the v’s role in a pure rotation is to encode the axis’s position, not a pitch.
E 0.2.3.6 — The PoE vs. chain (diagnostic). A learner’s PoE FK matches the chain FK at q = 0 but not at q ≠ 0. What are the three likely causes, and the discriminating test?
S 0.2.3.6. Causes: (1) missing J in the exp (the naive q·v), which drifts proportionally to q; (2) wrong twist (the body twist instead of the spatial, or the wrong reference point), which drifts by the moment arm; (3) wrong composition order, which gives a different product. Discriminating tests: for (1), the drift’s dependence on q (linear for (1), non-linear for (2) and (3)); for (2), compare the twist’s v to −ω × $q_home$; for (3), compare the product to the chain’s order (joint 1 to joint 6, not reversed). The lesson: the q = 0 test is necessary and not sufficient; the q ≠ 0 test is the real test, and the drift’s shape distinguishes the causes.
E 0.2.3.7 — The adjoint vs. T (diagnostic). A learner transforms a velocity by applying the 4×4 T and gets a velocity that is R v + t. What is the error, and what is the correct velocity?
S 0.2.3.7. The error is: (a) missing the cross-product term t × (Rω), and (b) adding a spurious +t (the twist’s v is a velocity, not a point). The correct velocity is R v + t × (R ω), and the difference from the near-miss R v + t is (R v + t × Rω) − (R v + t) = t × Rω − t, the negation of t − t × Rω. For a pure translation velocity (ω = 0), the correct is R v (no +t, no cross-product) and the near-miss’s R v + t is wrong by +t. The lesson: the 4×4 T is the point/direction action, not the velocity action; the velocity’s action is the 6×6 adjoint, and the difference is the cross-product term. Write the subtraction in the order you computed it — the sign of a difference quoted backwards passes every magnitude check.
E 0.2.3.8 — The PoE implementation (constructive). Write pseudocode for poe_fk(q, twists, M) and state what must be true of twists and M.
S 0.2.3.8.
function poe_fk(q, twists, M):
T = identity(4)
for i in 0 .. len(twists)-1:
T = T @ exp_se3_unit(twists[i], q[i])
return T @ M
The twists[i] must be unit twists (‖ω‖ = 1) at the home configuration, and M must be the chain FK at q = 0. The exp_se3_unit must use the unit-twist form with the (q I + (1−cos q)ω̂ + (q−sin q)ω̂²)v translation, not the naive q·v. The lesson: the PoE’s correctness depends on three things: unit twists at home, the correct M, and the J matrix in the exp; any one wrong makes the FK wrong at q ≠ 0 and correct at q = 0.
§L.9 — Exit test
You are ready for Lesson 0.3.1 when you can, without notes:
- State what a Lie algebra is, distinguish
so(3)fromSO(3)andse(3)fromSE(3), and state the bracket’s role. - Compute the bracket of two twists by hand, and state the BCH formula’s first-order correction.
- State the
SE(3)exp’s unit-twist and arbitrary-twist forms, and explain why they are the same formula viewed differently. - Compute
exp(ξ̂ q)for a given twist and angle, showing theJmatrix’s contribution. - State the screw parameters (axis, point, pitch, angle) and classify a twist as pure rotation, pure translation, or general screw.
- State the running case’s spatial twists and home configuration, and write the PoE FK.
- Verify the PoE FK against the chain FK at a specific non-zero
q, and state the three conditions for the match. - State the adjoint
Ad_T = [[R, 0], [t̂R, R]]and transform a twist between frames, showing thet × (Rω)term. - Distinguish a body twist from a spatial twist, and state the frame convention for each.
- State why the adjoint is not the 4×4
Tapplied to a 6-vector, and the symptom of usingT. - Diagnose a PoE-vs-chain mismatch, a wrong adjoint, and a wrong pitch, and name the test for each.
- State the three conditions for the PoE’s correctness (unit twists, correct
M, correctJ), and the test that verifies each.
If any item is not answerable without notes, return to the section that introduced it: 1 to §L.1, 2 to §L.1.1, 3 to §L.2, 4 to §L.2.2, 5 to §L.3.1, 6 to §L.4.2, 7 to §L.4.2 and §L.6.9, 8 to §L.4.1, 9 to §L.4, 10 to §L.4.4, 11 to §L.5, 12 to E 0.2.3.8.
§L.10 — Provenance and evidence tiers
What this lesson reproduces. Every hypothesis in §L.6.5 is executed by the lab in §L.6.9. These claims are tier A, status R.
| Claim | Tier | Status | Where it is reproduced |
|---|---|---|---|
The PoE product and the chained homogeneous transforms agree at the test configuration: error 0.00e+00 | A | R | H1 |
The J matrix’s effect is large, not negligible: 1.1514 at q = π/2 | A | R | H2 |
A twist transformed by a 4×4 multiply differs from the adjoint’s result by 4.3589 — the wrong operation is not a near-miss | A | R | H3, and the break-it’s paired vectors |
The adjoint preserves pitch: both ξ and Ad_T ξ have pitch 0.000000 | A | R | H4 |
Applying T to a twist and applying Ad_T give visibly different 6-vectors | A | R | Break-it #3, printed as [0,0,1,1,2,3] against [0,0,1,2,-1,0] |
What this lesson does not reproduce. The claim that the pitch is invariant under a change of frame is checked only on a degenerate twist. H4’s xi_b = (0, 0, 1, 0, 0, 0) has v = 0, so its pitch is 0/|ω|² = 0 whatever T is — the test cannot distinguish an invariant map from one that destroys the linear part entirely. The invariance itself is true and was verified separately: with v = (0.3, 0.5, 0.7) the pitch goes 0.700000 → 0.700000 under Ad_T, so the claim is tier A, status S — reproduced, but not by the shipped test. The lab now prints both the degenerate and the non-degenerate case.
The 4.3589 is the lesson’s identification result. H3 exists to show that T·ξ is not a small perturbation of Ad_T·ξ — it is a different object. The break-it’s two printed vectors make the difference concrete: the first has a non-zero linear part in the last component (3), the second has a rotation-induced coupling (2, -1). A reader who thinks the adjoint is a notational convenience should compare those two lines.
No external sources are cited in this lesson. The PoE product, the exponential map, and the adjoint are built in Parts A–C and executed by the lab. No paper is named; the lesson defines every object it uses.