Part I · Foundations · Chapter 03
available

3D rigid-body mechanics

A rigid body can translate and rotate, but it cannot deform. That compact promise turns many particles into twelve state numbers—and makes orientation the interesting part.

Level
Intermediate
Hands-on
110–150 minutes
Before you start
Chapters 1–2, cross products, matrix multiplication

By the end, you can…

  • Compute center of mass and linear and angular momentum.
  • Predict the torque produced by an off-center force.
  • Transform inertia between body and world coordinates.
  • Relate rotation matrices, axis-angle, quaternions, and angular velocity.
  • Explain why naïvely adding angular velocity to Euler angles fails.
  • Interpret torque-free tumbling and reaction-wheel momentum exchange.

From many particles to one rigid body

Imagine a body as particles with masses mim_i, world positions rir_i, and velocities viv_i. Its total mass and center of mass are:

m=imi,rCOM=1mimirim=\sum_i m_i,\qquad r_{COM}=\frac{1}{m}\sum_i m_i r_i

Linear momentum compresses all translational motion into one vector:

p=imivi=mvCOMp=\sum_i m_i v_i=m v_{COM}

Angular momentum about the center of mass records rotational motion:

L=i(rirCOM)×mi(vivCOM)=IωL=\sum_i (r_i-r_{COM})\times m_i(v_i-v_{COM})=I\omega

The last equality is frame-sensitive: II and ω\omega must be expressed in the same frame. With no external force, pp is constant. With no external torque, world-frame LL is constant—even though its body-frame components may dance.

A force changes translation; its lever arm changes rotation

Newton's translational and rotational laws have matching shapes:

p˙=Fexternal,L˙=τexternal\dot p=F_{external},\qquad \dot L=\tau_{external}

A force FF applied at an offset rr from the center of mass produces torque:

τ=r×F\tau=r\times F

Only the perpendicular component contributes. In the reference lab, r=[0.3,0,0]mr=[0.3,0,0]\,m and F=[0,5,0]NF=[0,5,0]\,N yield τ=[0,0,1.5]N ⁣ ⁣m\tau=[0,0,1.5]\,N\!\cdot\!m. Point the force along the lever arm and the torque becomes zero.

off-center force
offset = np.array([0.3, 0.0, 0.0])
force = np.array([0.0, 5.0, 0.0])
torque = np.cross(offset, force)

assert np.allclose(torque, [0.0, 0.0, 1.5])

Experiment 1: equal torque, unequal response

Mass measures resistance to translational acceleration. The inertia tensor measures resistance to angular acceleration and also depends on how mass is distributed.

IB=V(r21rrT)dmI_B=\int_V \left(\lVert r\rVert^2\mathbf{1}-rr^\mathsf{T}\right)\,dm

For a box of full side lengths w,h,dw,h,d aligned with its principal body axes:

IB=m12diag(h2+d2,  w2+d2,  w2+h2)I_B=\frac{m}{12}\operatorname{diag}(h^2+d^2,\;w^2+d^2,\;w^2+h^2)

At rest, Euler's rotation equation reduces to α=I1τ\alpha=I^{-1}\tau. Away from rest, the coupling term matters:

Iω˙+ω×(Iω)=τI\dot\omega+\omega\times(I\omega)=\tau
Experiment 01verified

Push a cube and a rod with the same torque

If mass and torque match, how much does shape alone change angular acceleration?

A 0.2 m cube and a 0.8 × 0.2 × 0.2 m rod each have mass 1 kg. LavenderSim applies the same 0.02 N·m world-z torque. The rod's z inertia is 8.5 times larger, so the cube's angular acceleration is 8.5 times larger.

Predicted and measured angular acceleration for a cube and long rod under equal torque
The native result overlays the analytical prediction. After 0.1 s, the measured angular-velocity ratio is 8.499998.
3.000 rad/s²cube prediction
0.353 rad/s²rod prediction
8.5×measured ratio

Failure modes to try: increase the timestep until integration error is visible; add damping and notice that the velocity ratio is no longer exactly constant; rotate a non-symmetric body and remember that diagonal body inertia is not diagonal in world coordinates.

Expected result
The command exits successfully and reproduces the numerical or visual relationship described in this card.
Failure modes
Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.

For body-to-world rotation RWBR_{WB}, the same physical inertia written in world coordinates is:

IW=RWBIBRWBTI_W=R_{WB}I_BR_{WB}^{\mathsf T}
Implemented by LavenderSim: apply_inv_inertia() rotates a world vector into the body frame, applies diagonal inverse inertia, then rotates the result back. This is the compact equivalent of multiplying by IW1I_W^{-1}.

Orientation is not three independent numbers

A rotation matrix RSO(3)R\in SO(3) has nine entries but only three degrees of freedom. It must satisfy RTR=IR^\mathsf{T}R=I and detR=1\det R=1. Axis-angle stores a unit axis u^\hat u and one angle θ\theta. A unit quaternion stores the same rotation as:

q=[u^sin(θ/2)cos(θ/2)],q=1q=\begin{bmatrix}\hat u\sin(\theta/2)\\\cos(\theta/2)\end{bmatrix},\qquad \lVert q\rVert=1

LavenderSim's public convention is (x, y, z, w), with the scalar component last. Quaternions qq and q-q describe the same orientation. They avoid Euler-angle singularities, but their unit-length constraint must still be maintained numerically.

Angular velocity is physical; Euler angles are coordinates. The vector ω\omega describes the instantaneous rotation axis and speed. Except in special cases, it is not equal to roll, pitch, and yaw rates.

Experiment 2: Euler-angle addition versus quaternions

Experiment 02verified

Integrate a constant world angular velocity

What breaks if we treat angular velocity as three Euler-angle derivatives?

For a world-frame angular velocity, quaternion kinematics are:

q˙=12[ωW,0]q\dot q=\frac{1}{2}\,[\omega_W,0]\otimes q

The lab integrates this equation while a deliberately incorrect method adds Δtω\Delta t\,\omega directly to XYZ Euler angles. It also integrates a second quaternion without normalization.

Orientation error for naïve Euler angle addition and quaternion integration, with unnormalized quaternion norm drift
Mixed-axis rotation exposes the Euler-rate mistake. Quaternion error remains small, while omitting normalization lets the state drift off the unit 3-sphere.
87.6°naïve Euler error at 4 s
0.0087°quaternion error at 4 s
|q| = 1.0138without normalization

Failure modes to try: use a single-axis rotation, which can hide the Euler-angle bug; enlarge dt, which increases quaternion truncation error; swap world- and body-frame multiplication order.

Expected result
The command exits successfully and reproduces the numerical or visual relationship described in this card.
Failure modes
Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.

If ω\omega is expressed in the body frame instead, multiplication moves to the other side:

q˙=12q[ωB,0]\dot q=\frac{1}{2}\,q\otimes[\omega_B,0]
Implemented by LavenderSim: integrate_orientation() uses the world-frame form, takes an explicit quaternion step, and normalizes after every substep.

Experiment 3: steady spin and tumbling

In principal body axes, torque-free motion follows Euler's equations:

Ixω˙x=(IyIz)ωyωzand cyclic permutationsI_x\dot\omega_x=(I_y-I_z)\omega_y\omega_z\quad\text{and cyclic permutations}

Spin exactly around a principal axis and the other components stay zero. Start with components on several axes and the body-frame angular velocity evolves, even though world angular momentum and rotational kinetic energy remain constant:

LW=RWBIBωB=constant,T=12ωBTIBωB=constantL_W=R_{WB}I_B\omega_B=\text{constant},\qquad T=\tfrac{1}{2}\omega_B^\mathsf{T}I_B\omega_B=\text{constant}
Experiment 03verified

Integrate torque-free Euler equations

How can angular velocity change when no external torque acts?

Body-frame angular velocity for principal-axis and mixed-axis torque-free rotation
The upper body spins steadily about a principal axis. The lower body's coordinate axes move underneath the conserved world angular-momentum vector, so its body-frame components vary.
0 changeprincipal-axis body ω
3.30 rad/smixed-axis body ω change
< 4e-7world momentum error

Failure modes to try: replace RK4 with explicit Euler and plot energy drift; forget whether ω\omega is in the body or world frame; test rotation near the intermediate principal axis.

Expected result
The command exits successfully and reproduces the numerical or visual relationship described in this card.
Failure modes
Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.

Experiment 4: trade angular momentum internally

A reaction wheel motor exerts equal and opposite torques on its rotor and housing. In an ideal isolated system:

τcube=τwheel,ΔLcube=ΔLwheel\tau_{cube}=-\tau_{wheel},\qquad \Delta L_{cube}=-\Delta L_{wheel}
Experiment 04verified

Command ReactionWheelCube-v0

Can an internal actuator rotate a body without pushing on the outside world?

A positive x-wheel command makes every recorded wheel sample positive and every cube sample negative.

Cube and internal wheel spinning in opposite x directions in ReactionWheelCube
The small cube motion and fast wheel motion have opposite signs because the wheel has much lower rotational inertia.
Scope of the claim: the current environment contains damping and a world-anchored spherical joint. It demonstrates opposite angular-velocity response and useful control physics, not exact conservation for a perfectly isolated spacecraft.

Failure modes to try: saturate the actuator, reverse the command, or command two wheels simultaneously and inspect cross-axis coupling.

Expected result
The command exits successfully and reproduces the numerical or visual relationship described in this card.
Failure modes
Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.

Let Python choose what the browser explains

Live frames now accept Python-defined body axes and vectors. That makes the visualization part of an experiment rather than a hard-coded renderer feature.

live mechanics overlays
viewer.publish_frame(
    sim,
    overlays={
        "axes": [{"body": "cube", "scale": 0.34}],
        "vectors": [
            {
                "label": "angular velocity",
                "origin": cube_position.tolist(),
                "vector": cube_omega.tolist(),
                "scale": 0.12,
                "color": [0.51, 0.87, 0.76, 0.95],
            },
            {
                "label": "torque",
                "origin": cube_position.tolist(),
                "vector": commanded_torque.tolist(),
                "scale": 3.0,
                "color": [0.95, 0.66, 0.77, 0.95],
            },
        ],
    },
)

The browser rotates the three body axes using authoritative body transforms and draws vector arrows in world coordinates. The same payload path can display angular momentum or any diagnostic vector computed by a Python controller.

Paw-check: reason in the right frame

  1. Double every mass in the center-of-mass example. Which quantities change, and which remain fixed?
  2. Move the force application point from 0.3 m to −0.3 m. Predict the torque before running the code.
  3. Rotate the rod 90° about y, apply world-z torque, and compute the relevant world inertia.
  4. Modify the orientation lab to use body-frame angular velocity and the corresponding quaternion multiplication order.
  5. Start torque-free motion almost—but not exactly—on each principal axis. Which axis is least forgiving?
  6. Reverse the reaction-wheel command and assert that both signs reverse.
What should I be able to say now?

A rigid body is summarized by center-of-mass translation and orientation. Forces change linear momentum; torques change angular momentum. Inertia depends on mass distribution and coordinate frame. Angular velocity is not an Euler-angle derivative, while unit quaternions provide a nonsingular orientation representation that must be integrated with the correct frame convention and renormalized. Torque-free body-frame motion can tumble even while world angular momentum is conserved.