Part II · Articulated systems · Chapter 04
available

Generalized coordinates

A robot may contain dozens of Cartesian bodies but only a handful of independent motions. Generalized coordinates name those freedoms directly.

Level
Intermediate
Hands-on
100–140 minutes
Before you start
Chapters 2–3, homogeneous transforms, Jacobians

By the end, you can…

  • Count mechanism degrees of freedom independently of state-storage size.
  • Distinguish minimal and redundant coordinate representations.
  • Compose parent-to-child transforms along a kinematic tree.
  • Derive pose and velocity for a two-revolute-link arm.
  • Detect singular configurations through Jacobian rank.
  • Explain how LavenderSim's Cartesian joint solver differs from generalized-coordinate engines.

Coordinates are a description, not the mechanism

A degree of freedom is one independent way a system can move. A free rigid body in 3D has six: three translations and three rotations. A revolute joint permits one relative rotation; a prismatic joint permits one relative translation.

Generalized coordinates collect independent configuration variables:

q=[q1qn]T,q˙=dqdtq=\begin{bmatrix}q_1&\cdots&q_n\end{bmatrix}^{\mathsf T},\qquad \dot q=\frac{dq}{dt}

The word “generalized” means the entries need not be Cartesian positions. A coordinate may be a hinge angle, slider distance, or any scalar that locally identifies configuration.

A minimal representation uses exactly one coordinate per degree of freedom. A redundant representation stores extra variables and equations that keep them consistent. For a two-link planar arm:

2mechanism DoF
2minimal positions q
26LavenderSim body-state scalars

The 26 scalars are two records of position (3), quaternion (4), linear velocity (3), and angular velocity (3). They are useful for direct collision and rendering, but joint and unit-quaternion constraints make them non-independent.

Joints remove relative motion

Two unconstrained rigid bodies have twelve relative-plus-global degrees of freedom. An ideal revolute joint permits one relative rotation, so it removes five relative directions. A prismatic joint similarly retains one translation and removes the other five relative motions.

JointCoordinateAllowed relative motion
FixednoneNo translation or rotation
Revoluteangle in radiansRotation about one axis
Prismaticdistance in metresTranslation along one axis
Sphericalthree local DoFRotation about a shared anchor
Coordinate count is local. No smooth three-number orientation chart covers every 3D rotation without a singularity or discontinuity. “Minimal” does not automatically mean “globally convenient.”

A kinematic tree carries transforms outward

A robot description names a root and assigns every non-root link one parent joint. The unique path from root to link lets us multiply transforms in order. For the two-link arm:

WTtip=WT0  0T1(q1)  1T2(q2)  2Ttip{}^WT_{tip}={}^WT_0\;{}^0T_1(q_1)\;{}^1T_2(q_2)\;{}^2T_{tip}

Each planar homogeneous transform combines a 2×2 rotation and a translation:

T(θ,t)=[cosθsinθtxsinθcosθty001]T(\theta,t)=\begin{bmatrix}\cos\theta&-\sin\theta&t_x\\\sin\theta&\cos\theta&t_y\\0&0&1\end{bmatrix}

Multiplication order matters. Transforms are functions between named frames; writing the superscripts is a useful defense against accidentally reversing a mapping.

Experiment 1: build forward kinematics

Experiment 01verified

Compose a two-link arm from transforms

How can two joint angles determine every link and site pose?

With lengths l1,l2l_1,l_2, the tip position is:

p(q)=[l1cosq1+l2cos(q1+q2)l1sinq1+l2sin(q1+q2)]p(q)=\begin{bmatrix}l_1\cos q_1+l_2\cos(q_1+q_2)\\l_1\sin q_1+l_2\sin(q_1+q_2)\end{bmatrix}
python
elbow = transform_z(q1) @ translation_x(l1)
tip = elbow @ transform_z(q2) @ translation_x(l2)
tip_position = tip[:2, 2]
Three poses of a two-link planar arm generated from two joint coordinates
The same tree and link lengths produce different Cartesian geometry as q changes. Pink marks the elbow; mint marks the named tip site.

Expected result: at q=[0,0]q=[0,0], the tip is [l1+l2,0][l_1+l_2,0]. Failure modes: omit the cumulative q1+q2q_1+q_2, reverse transform order, or confuse a link-center transform with its endpoint.

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.

Differentiate the tree, not the renderer

The geometric Jacobian maps generalized velocity into tip velocity:

p˙=J(q)q˙,J=pq\dot p=J(q)\dot q,\qquad J=\frac{\partial p}{\partial q}
J(q)=[l1sinq1l2sin(q1+q2)l2sin(q1+q2)l1cosq1+l2cos(q1+q2)l2cos(q1+q2)]J(q)=\begin{bmatrix}-l_1\sin q_1-l_2\sin(q_1+q_2)&-l_2\sin(q_1+q_2)\\l_1\cos q_1+l_2\cos(q_1+q_2)&l_2\cos(q_1+q_2)\end{bmatrix}

Column ii answers: “if only coordinate qiq_i moves at one unit per second, which way does the tip move?” Upstream joints influence more descendants, which gives tree Jacobians their structure.

Experiment 2: find where q loses leverage

Experiment 02verified

Sweep the elbow through its workspace

When do two independent joints produce only one instantaneous tip direction?

At q2=0q_2=0 the arm is straight; at q2=±πq_2=\pm\pi it is fully folded. In both cases the Jacobian columns are parallel and rank falls from two to one.

Smallest Jacobian singular value and condition number versus elbow coordinate
The smallest singular value reaches zero at straight and folded configurations. The condition number diverges because one Cartesian direction has vanished.

Expected result: rank one at all three marked angles and rank two away from them. Failure modes: use a loose rank tolerance, divide by the zero singular value, or interpret a damped inverse as restoring a physically unavailable motion.

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 3: map q into LavenderSim

Experiment 03verified

Predict native named-site telemetry

Can minimal-coordinate kinematics reproduce a Cartesian rigid-body scene exactly?

The lab creates a world-fixed base and two revolute links with LavenderSim's scene DSL. It then expands selected q,q˙q,\dot q values into consistent body positions, quaternions, linear velocities, and angular velocities. The named tip site is measured through the normal native telemetry path.

python
scene.revolute(
    "arm.elbow",
    upper,
    forearm,
    anchor=(l1, 0, 0),
    axis=(0, 0, 1),
)
scene.site("arm.tip.site", body=forearm, position=(l2 / 2, 0, 0))
Log-scale position and velocity errors between analytic kinematics and LavenderSim site telemetry
Across four configurations, position and velocity agreement is at float32 telemetry precision: below 4e-8 m and 2e-8 m/s.
What this verifies: the scene DSL, body-frame site offset, Cartesian body state, and analytic generalized kinematics agree. The lab intentionally imposes a consistent state and does not claim a long dynamic rollout has zero joint-constraint error.

Failure modes to try: give the forearm orientation q2q_2 instead of q1+q2q_1+q_2; omit the moving elbow's velocity; place the site at a world coordinate instead of a body-local offset.

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.

Two valid simulator architectures

A generalized-coordinate engine stores q,q˙q,\dot q for a kinematic tree and derives link poses. Joint constraints are satisfied by construction, while contacts and closed loops still introduce constraints.

LavenderSim currently stores every body's Cartesian pose and twist. Its revolute solver preserves a common anchor, aligns two axes, and leaves rotation about that axis free. This makes the compact engine and collision pipeline direct, but articulation accuracy depends on timestep, projection, and solver iterations.

QuestionGeneralized-coordinate treeLavenderSim today
Primary dynamic stateq,q˙q,\dot qBody poses and twists
Tree-joint satisfactionBy constructionNumerical constraints
Body poseForward kinematicsStored directly
Joint coordinateState variableMeasured from body frames
Implemented by LavenderSim: Scene.revolute() defines the Python-side joint, while measure_joint_coordinate() reconstructs its scalar coordinate from Cartesian body frames.

Paw-check: choose coordinates deliberately

  1. Add a third link. Write its tip transform before expanding the trigonometry.
  2. Derive the two-link Jacobian by treating each revolute column as z^×(ptippjoint)\hat z\times(p_{tip}-p_{joint}).
  3. Plot the reachable annulus and identify its inner and outer radii.
  4. Add a prismatic first joint and decide its coordinate unit and Jacobian column.
  5. Step the native chain dynamically instead of imposing state. Sweep solver iterations and measure anchor error.
  6. Explain why a closed four-bar linkage cannot be represented by a simple tree without an extra closure constraint.
What should I be able to say now?

Degrees of freedom belong to the mechanism, while coordinate and storage counts belong to a representation. Generalized coordinates directly name independent motions. Forward kinematics composes transforms along a tree, and the Jacobian maps generalized rates to Cartesian velocity while revealing singularities. LavenderSim instead stores redundant Cartesian body state and enforces joints numerically; its sites provide a clean bridge for testing both descriptions against each other.