Robot Dynamics
The Lagrangian route from masses and lever arms to M(q) q̈ + C(q, q̇) q̇ + g(q) = τ — Christoffel symbols as the geometry of unforced motion, Pfaffian constraints with Lagrange multipliers, rigid-body rotation and Euler's equation, and an integrator whose energy bookkeeping is a test.
A path is not a complete description of the motion of a robot system, however, as the timing of the motion is not specified.
In this chapter
Parts I through IV planned paths: curves in with no clock attached. A real motor does not execute a curve. It applies a torque, and the configuration that results depends on masses, lever arms, and how fast everything else is already moving. This chapter builds the model that turns geometry into motion for the rest of Part V.
The idea the chapter unpacks fits in one sentence. The entire dynamics of an arm is encoded in one configuration-dependent symmetric matrix . Kinetic energy is ; the Coriolis and centrifugal forces are nothing but derivatives of — the Christoffel symbols; gravity is the gradient of a scalar. The reader who watched Reach's Jacobian ellipse collapse in Chapter 4 now watches a second ellipse — the inertia ellipse — breathe as the elbow folds, and learns why "force equals mass times acceleration" is false, term by term, in joint coordinates.
Everything here is Choset's Chapter 10, with one addition the 2005 text did not need because it assumed a Mathematica pipeline and never integrated anything: the skew-symmetry of is turned into a test oracle, and the integrator that powers every widget on this page is required to conserve energy to a stated tolerance. If the code and the prose ever disagree, the energy bar settles it.
The problem: a path says nothing about time
Take a path for Reach — a smooth curve on the torus, the kind every planner in Part III hands back — and replay it. Then replay it twice as fast.
Nothing about the curve changed. What changed is and , and the torque a joint must supply depends on both — quadratically on , which is why doubling the speed does not double the torque but quadruples the part of it that comes from motion. A path is a set of configurations; a trajectory is that set with a clock, and only the clocked version can be feasible or infeasible. Chapter 18 chooses the clock. This chapter builds the thing the clock is measured against: the map from to the torques that produce them.
Building intuition
The arm is a different machine in every configuration
Before any formalism, watch Reach fall. The Lagrangian Lab hangs the arm on a vertical Workbench, releases it from a configuration you choose, and keeps the books.
Three things are worth noticing, and each becomes a theorem in the next section.
Energy is conserved, exactly, by construction. The total sits on its dashed reference line while kinetic and potential energy trade places violently. There is no friction in the default model and no torque, so this is what the physics must do — and the integrator reproduces it to about one part in over ten seconds. That number is not an accident; it is a unit test, and the end of the chapter prints it.
Switch Coriolis off and the physics lies. The toggle hands the same integrator a model in which the velocity-product vector has been zeroed — "a small correction," the folklore says. Within two swings the energy bar leaves its line. The arm gains or loses energy from nowhere, by more than twenty percent of what it started with. Those terms are not a correction. They are the only thing that makes joint-space Newton's law consistent with workspace Newton's law.
Neither nor is a force. Watch the torque gauge. Under zero applied torque the three stacks satisfy at every instant, and as the arm swings through different configurations the share carried by and by changes while their sum does not. Choset says this precisely: "neither nor individually should be thought of as a generalized force; only their sum is a force."
Inertia is a metric
The second widget throws the arm away and keeps the matrix.
The orange curve is the set of joint velocities that carry exactly one joule of kinetic energy, . If inertia were a number the curve would be a circle. It is an ellipse, its semi-axes are for the eigenvalues of , and as sweeps from to the entry reads , then , then : the stretched arm is three times harder to swing about the shoulder than the folded one. Move the shoulder instead and nothing happens — there is no anywhere in , and the text will ask you to say why.
| Symbol | Meaning | Note |
|---|---|---|
| Generalized coordinates and generalized forces, paired so that uᵀq̇ is power. For Reach u = τ, the joint torques. | Chs. 18–19 keep u for controls. | |
| The Lagrangian: kinetic minus potential energy. | ||
| Inertia matrix, a Coriolis matrix, gravity vector, dissipation — the standard form (10.7). | Book-wide (TOC §2). | |
| Christoffel symbols of M; the i-th symmetric n×n slice; the velocity-product vector (10.10). | ||
| Actuation matrix and raw actuator forces; T = Jᵀ for forces applied at a body point (10.12). | ||
| k Pfaffian constraints (rows ω_j(q)) and their Lagrange multipliers. | ||
| Projections onto motions that satisfy the constraints / forces that do work (10.18–10.19). | ||
| Center of mass; planar inertia about z; body-frame and spatial inertia tensors. | ||
| Body angular velocity, its skew matrix, the orientation. | ||
| Total mechanical energy; the integrator step. |
The mathematics
Definitions
Generalized coordinates and forces. A configuration is locally — joint angles for Reach, for a planar body — and a generalized force is anything that pairs with to give power: is the rate at which work is done on the system. Torques pair with joint rates; a force through the center of mass pairs with its velocity.
The Lagrangian is with kinetic energy quadratic in the velocities and potential energy a function of configuration alone. The Euler–Lagrange equations (Choset eq. 10.1)
are the equations of motion. Their derivation from the principle of least action is in any mechanics text; this chapter takes them as the recipe.
The inertia matrix , so that (eqs. 10.13–10.14). It is symmetric by construction and positive definite because kinetic energy is positive for every nonzero velocity.
Christoffel symbols. For each , the matrix with entries
(eq. 10.9). Terms with are centrifugal; terms with are Coriolis. There are of them, symmetric in the lower pair.
A Pfaffian constraint is for a matrix of full row rank. It is holonomic if each row is for some — the constraint is really a constraint on configurations in disguise — and nonholonomic otherwise. Chapter 20 decides which; this chapter only imposes them.
The inertia tensor of a rigid body with density occupying , in a frame at its center of mass, is (eq. 10.42). Its eigenvectors are the principal axes; its eigenvalues the principal moments.
Euler–Lagrange gives the standard form
DerivationFrom the Lagrangian to M q̈ + C q̇ + g = u
Step 1 — the momentum. , because is a quadratic form and has no in it.
Step 2 — its time derivative. , and since depends on only through , .
Step 3 — the configuration derivative. .
Step 4 — subtract and symmetrize. The -th equation reads
The middle sum is a quadratic form in whose coefficient matrix is not symmetric in . A quadratic form only sees the symmetric part, so replace it by . Together with the third sum that is exactly — the symmetrization is where the three-term Christoffel formula comes from.
Step 5 — read off the pieces. Define (eq. 10.8), , and any matrix with ; the choice is the one the rest of the chapter uses.
The RP arm as a check (Choset Examples 10.1.2, 10.2.1). With , the only nonzero symbols are and , and the velocity-product vector is — eqs. (10.5)–(10.6) exactly. The cast of this book is revolute, so the RP arm survives only here.
is not unique; is. Add to any matrix with — for , for any scalar — and the equations of motion are unchanged. What is changed is the property the next derivation proves, which is why the Christoffel choice is the one worth naming.
Ṁ − 2C is skew-symmetric, so energy is conserved exactly
DerivationWhy the Christoffel choice conserves energy
Step 1 — write both pieces with the same partials. From Step 2 above, . From the definition, .
Step 2 — the first terms cancel. Subtracting leaves .
Step 3 — what remains is antisymmetric. Swap and and the bracket changes sign: . So for every , since a quadratic form of a skew matrix vanishes identically.
Step 4 — differentiate the energy. , so . Substitute :
Why this is the test the dynamics crate runs. The derivation used only (i) the correct ,
(ii) the correct obtained from it, and (iii) . An error in any of
the three breaks the identity, and an integrator run from rest with will show it as
energy drift. That is one assertion covering the whole derivation. The library check
dynamics: released from rest … E stays within 1e-6 relative under RK4 is exactly this.
Why dropping breaks it. With the Step 4 remainder is , which is not zero — it is the rate at which the configuration-dependence of pumps energy in or out. That is the Lagrangian Lab's lie, and the sign of the drift depends on whether is positive or negative along the motion (Exercise 4).
The 2R arm, worked in full
This is Choset's Problem 10.2 — Reach in a vertical plane — and the model every later chapter in Part V runs.
DerivationThe Lagrange recipe applied to Reach
Step 1 — centers of mass by forward kinematics. Chapter 2's evaluated at instead of : and .
Step 2 — center-of-mass Jacobians. Differentiating, and
These are Chapter 4's Jacobian construction applied to interior points of the links.
Step 3 — kinetic energy. Each link contributes translation of its center of mass plus rotation about it: with and , i.e. , . Hence
Multiplying out: , using . Collect by and the three entries of in the statement appear.
Step 4 — differentiate for . Only appears in , and only through : , , every other partial is zero. Feeding these into the three-term formula: ; ; ; . Contracting, and (Problem 10.3).
Step 5 — gravity. , and is the statement.
The same recipe for 3R. Nothing in Steps 1–5 used except the trigonometric collapse in Step 3. For Reach's three-link variant there are three center-of-mass Jacobians, nine entries of and 27 symbols — too many to differentiate by hand without error, so the library assembles from the Jacobians and takes the Christoffel symbols by central differences. The 2R case is the check: the finite-difference symbols agree with the closed form above to at fifty random states.
The numbers
Fix the micro-example that every later chapter in Part V reuses: m, uniform rods so , kg, kg, hence and kg·m², m/s², at Choset's worked pose from Example 3.8.1 — the configuration the reader already knows from Chapter 2. Then and
with eigenvalues and . Gravity: N·m and N·m. With rad/s the kinetic energy is J, , and N·m: joint 2 must push outward at half a newton-meter just to keep the elbow angle fixed while the shoulder turns. Released from rest at this pose with , J and must stay there.
Sweep q₂ through 0, π/2, π at the same masses. What is M₁₁ at q₂ = 0 and at q₂ = π?
Constrained dynamics and the projection P
Rusty's axle, Hitch's wheels, a knife-edge on ice: each may move along its heading and spin but not slide sideways. That is a constraint on velocities, and it enters the equations of motion through a Lagrange multiplier.
DerivationEliminating the multipliers
Step 1 — differentiate the constraint. holds for all time, so (eq. 10.17).
Step 2 — solve the force balance for . From (10.16), .
Step 3 — substitute into the differentiated constraint. .
Step 4 — solve for . The matrix is invertible because has full row rank and : .
Step 5 — substitute back and factor. Using and collecting, . Call the bracket (eq. 10.18); then (eq. 10.19) and rearranging gives the statement (eq. 10.20).
Orthogonality with respect to . and split a velocity into a part that satisfies the constraints and a part in the constrained directions, and for every . The inner product is , not the identity: Choset is explicit that this is "the appropriate one when discussing dynamics," because carries the metric of coordinates that mix lengths and angles. Also , which the library checks numerically alongside the rank.
The knife-edge (Example 10.3.1). is contact point and heading, , , . Then , rank 2, and the closed forms (10.25)–(10.28) follow — including the constraint force . The library solves the system directly and reproduces those four formulas to at thirty random states. This is Rusty's no-side-slip constraint, and Chapter 20 will show it is nonholonomic.
Euler's equation in the body frame
For a spinning rigid body no choice of three angles gives a smooth global coordinate system — the same fact Chapter 5 made about — so Choset does not pick one. He takes as the orientation and the body-frame angular velocity as the velocity, and derives the dynamics directly.
DerivationFrom the spatial frame to the body frame
Step 1 — kinetic energy in the spatial frame. A point at moves at , so with (eqs. 10.34–10.36). Because it is written in a fixed frame, changes as the body turns.
Step 2 — angular momentum and its rate. and . The density does not change, so comes only from the rotation, giving — Euler's equation in the inertial frame (10.37), or in matrix form (10.38).
Step 3 — change frames. , , , , and (10.39, 10.41).
Step 4 — substitute. .
Step 5 — kill one term. .
Step 6 — premultiply by . .
The principal-axis form (10.45). Align the body frame with the eigenvectors of and , with the two cyclic permutations. Set : is zero only if two of the components vanish. Spin about the axis of intermediate inertia and a small perturbation in the other two components grows exponentially — the flip the next widget shows. The parallel-axis theorem (10.33 planar, 10.46 spatial) and the composite-body rule are functions in the library, not sections here.
Angular momentum and kinetic energy are conserved — the readout holds both to five decimals over the whole run — while the angular velocity is not. That is not a numerical artifact; it is what the equation says. Choset: "ω̇ may not be zero even if τ is zero. Although the angular momentum and kinetic energy of a rotating body are constant when no external torques are applied, the angular velocity of the body may not be constant."
RK4 on the first-order state, and what its energy drift means
Write and . Classical RK4 has local error and global error , and the energy error inherits that order. Three honesty items the prose must keep:
- RK4 is not symplectic. Its energy error is bounded by over the horizons we use because the global error is, not because of any structural guarantee. Run it for an hour and it will drift.
- The bound is empirical. The test asserts for ms over 10 s on the micro-example released from rest; the measured value is about .
- Explicit Euler drifts linearly and is kept only as the bad example: on the same run it loses track of the energy by more than ten percent. Semi-implicit (symplectic) Euler keeps the error bounded, but at first order the bound is also around ten percent on this violently swinging arm — cheap, not accurate.
The algorithm
Choset numbers none of these; the names follow his text.
- In
- link parameters (L_i, r_i, m_i, I_i), forward kinematics, gravitational acceleration
- Out
- M(q), Γ(q), g(q) as callables
- for each link : position of its center of mass by at ; ; with ones
- ;
- , by closed form or central differences
- return
- In
- state and generalized force
- Out
- q̈
- ;
- return by Cholesky or pivoted elimination — Choset eq. (11.46)
- In
- state, force, constraint matrix and its rate
- Out
- (q̈, λ)
- assemble — eqs. (10.16)–(10.17)
- solve; return . The constraint force on the system is .
- In
- ẋ = f(x), state, step
- Out
- x(t + h)
- ; ; ;
- return
- In
- orientation as a unit quaternion, body angular velocity, body torque, body inertia tensor, step
- Out
- (R, ω) after h
- state with and — eqs. (10.43)–(10.44)
- one
rk4_stepon ; renormalize
Implementation in Rust
The dynamics crate is introduced here and imported by Chapters 18–21 and 23. Its surface is a trait
with four physical callbacks and two derived operations.
use nalgebra::{SMatrix, SVector};
/// A fully actuated second-order mechanical system in generalized coordinates.
/// Everything downstream (time scaling, iLQR, kinodynamic RRT) talks to this trait.
pub trait Dynamics<const N: usize> {
/// M(q): symmetric positive definite. We return the full matrix — the
/// Cholesky factorization is cheap for N ≤ 3 and keeps the code readable.
fn mass(&self, q: &SVector<f64, N>) -> SMatrix<f64, N, N>;
/// The velocity-product vector C(q, q̇) q̇ = q̇ᵀ Γ(q) q̇ — returned as a vector,
/// because the matrix C is not unique and only the product is a generalized
/// force (Choset §10.2).
fn velocity_product(&self, q: &SVector<f64, N>, qd: &SVector<f64, N>) -> SVector<f64, N>;
/// g(q) = ∂V/∂q.
fn gravity(&self, q: &SVector<f64, N>) -> SVector<f64, N>;
/// V(q), so E = K + V can be tracked. K = ½ q̇ᵀ M q̇ is provided below.
fn potential_energy(&self, q: &SVector<f64, N>) -> f64;
/// Dissipation b(q, q̇); default: none.
fn dissipation(&self, _q: &SVector<f64, N>, _qd: &SVector<f64, N>) -> SVector<f64, N> {
SVector::zeros()
}
/// q̈ = M⁻¹(u − C q̇ − g − b). Default impl; override only for a faster recursive form.
fn forward(&self, q: &SVector<f64, N>, qd: &SVector<f64, N>, u: &SVector<f64, N>)
-> SVector<f64, N>
{
let rhs = u - self.velocity_product(q, qd) - self.gravity(q) - self.dissipation(q, qd);
self.mass(q).cholesky().expect("M(q) must be positive definite").solve(&rhs)
}
/// u = M q̈ + C q̇ + g + b — the torque that produces a given acceleration.
fn inverse(&self, q: &SVector<f64, N>, qd: &SVector<f64, N>, qdd: &SVector<f64, N>)
-> SVector<f64, N>
{
self.mass(q) * qdd + self.velocity_product(q, qd) + self.gravity(q) + self.dissipation(q, qd)
}
fn kinetic_energy(&self, q: &SVector<f64, N>, qd: &SVector<f64, N>) -> f64 {
0.5 * qd.dot(&(self.mass(q) * qd))
}
fn energy(&self, q: &SVector<f64, N>, qd: &SVector<f64, N>) -> f64 {
self.kinetic_energy(q, qd) + self.potential_energy(q)
}
}The 2R model is Derivation 3 typed in. Every expression is a line of the derivation, which is the point: a reader who has done the algebra should recognize the file.
use nalgebra::{Matrix2, Vector2};
use crate::Dynamics;
/// Choset Problem 10.2: planar 2R arm in a vertical plane. Units: kg, m, s.
pub struct Reach2R {
pub l1: f64, pub r1: f64, pub m1: f64, pub i1: f64,
pub l2: f64, pub r2: f64, pub m2: f64, pub i2: f64,
pub a_g: f64, // 0.0 lays the Workbench flat
}
impl Reach2R {
/// Uniform rods: r_i = L_i/2, I_i = m_i L_i²/12.
pub fn uniform(l1: f64, m1: f64, l2: f64, m2: f64, a_g: f64) -> Self {
Self {
l1, r1: l1 / 2.0, m1, i1: m1 * l1 * l1 / 12.0,
l2, r2: l2 / 2.0, m2, i2: m2 * l2 * l2 / 12.0,
a_g,
}
}
/// h(q) = m₂ L₁ r₂ sin q₂ — every velocity-product term is built from it.
fn h(&self, q: &Vector2<f64>) -> f64 {
self.m2 * self.l1 * self.r2 * q[1].sin()
}
/// Closed form: the four nonzero symbols Γ¹₁₂ = Γ¹₂₁ = Γ¹₂₂ = −h, Γ²₁₁ = h.
pub fn christoffel(&self, q: &Vector2<f64>) -> [Matrix2<f64>; 2] {
let h = self.h(q);
[Matrix2::new(0.0, -h, -h, -h), Matrix2::new(h, 0.0, 0.0, 0.0)]
}
}
impl Dynamics<2> for Reach2R {
fn mass(&self, q: &Vector2<f64>) -> Matrix2<f64> {
let c2 = q[1].cos();
let m22 = self.i2 + self.m2 * self.r2 * self.r2;
let m12 = m22 + self.m2 * self.l1 * self.r2 * c2;
let m11 = self.i1 + self.i2 + self.m1 * self.r1 * self.r1
+ self.m2 * (self.l1 * self.l1 + self.r2 * self.r2 + 2.0 * self.l1 * self.r2 * c2);
Matrix2::new(m11, m12, m12, m22)
}
fn velocity_product(&self, q: &Vector2<f64>, qd: &Vector2<f64>) -> Vector2<f64> {
let h = self.h(q);
Vector2::new(-h * qd[1] * qd[1] - 2.0 * h * qd[0] * qd[1], h * qd[0] * qd[0])
}
fn gravity(&self, q: &Vector2<f64>) -> Vector2<f64> {
let (c1, c12) = (q[0].cos(), (q[0] + q[1]).cos());
Vector2::new(
(self.m1 * self.r1 + self.m2 * self.l1) * self.a_g * c1 + self.m2 * self.r2 * self.a_g * c12,
self.m2 * self.r2 * self.a_g * c12,
)
}
fn potential_energy(&self, q: &Vector2<f64>) -> f64 {
self.m1 * self.a_g * self.r1 * q[0].sin()
+ self.m2 * self.a_g * (self.l1 * q[0].sin() + self.r2 * (q[0] + q[1]).sin())
}
}For arms longer than two links the symbols are differenced rather than derived. The function is generic over anything that can produce , and the 2R closed form is what it is tested against.
use nalgebra::{SMatrix, SVector};
/// Γ^i_jk = ½(∂M_ij/∂q_k + ∂M_ik/∂q_j − ∂M_jk/∂q_i) (Choset eq. 10.9), with the
/// partials by central differences. O(n³) symbols from 2n evaluations of M.
/// The 3R arm has no other path; the 2R arm's closed form checks this one.
pub fn christoffel<const N: usize>(
mass: impl Fn(&SVector<f64, N>) -> SMatrix<f64, N, N>,
q: &SVector<f64, N>,
step: f64,
) -> [SMatrix<f64, N, N>; N] {
let mut d_m = [SMatrix::<f64, N, N>::zeros(); N]; // d_m[k] = ∂M/∂q_k
for k in 0..N {
let (mut qp, mut qm) = (*q, *q);
qp[k] += step;
qm[k] -= step;
d_m[k] = (mass(&qp) - mass(&qm)) / (2.0 * step);
}
let mut gamma = [SMatrix::<f64, N, N>::zeros(); N];
for i in 0..N {
for j in 0..N {
for k in 0..N {
gamma[i][(j, k)] = 0.5 * (d_m[k][(i, j)] + d_m[j][(i, k)] - d_m[i][(j, k)]);
}
}
}
gamma
}
/// max |(Ṉ − 2C) + (Ṉ − 2C)ᵀ| — zero when C is the Christoffel choice. A
/// diagnostic for a model, not a step of any algorithm.
pub fn skew_symmetry_defect<const N: usize>(
mass: impl Fn(&SVector<f64, N>) -> SMatrix<f64, N, N> + Copy,
q: &SVector<f64, N>,
qd: &SVector<f64, N>,
) -> f64 {
let gamma = christoffel(mass, q, 1e-6);
let mut c = SMatrix::<f64, N, N>::zeros();
let mut m_dot = SMatrix::<f64, N, N>::zeros();
for i in 0..N {
for j in 0..N {
for k in 0..N {
c[(i, j)] += gamma[i][(j, k)] * qd[k];
}
}
}
// Ṁ = Σ_k ∂M/∂q_k q̇_k, recomputed here so the two sides share no code path.
for k in 0..N {
let (mut qp, mut qm) = (*q, *q);
qp[k] += 1e-6;
qm[k] -= 1e-6;
m_dot += (mass(&qp) - mass(&qm)) / 2e-6 * qd[k];
}
let s = m_dot - 2.0 * c;
(s + s.transpose()).abs().max()
}The integrator is thirty lines, and the test next to it is the chapter's contract.
use nalgebra::SVector;
use crate::Dynamics;
/// One classical RK4 step on x = (q, q̇) with u held over the step.
pub fn rk4_step<const N: usize>(
dyn_: &impl Dynamics<N>,
q: &SVector<f64, N>, qd: &SVector<f64, N>, u: &SVector<f64, N>, h: f64,
) -> (SVector<f64, N>, SVector<f64, N>) {
let f = |q: &SVector<f64, N>, qd: &SVector<f64, N>| (*qd, dyn_.forward(q, qd, u));
let (k1q, k1v) = f(q, qd);
let (k2q, k2v) = f(&(q + k1q * (h / 2.0)), &(qd + k1v * (h / 2.0)));
let (k3q, k3v) = f(&(q + k2q * (h / 2.0)), &(qd + k2v * (h / 2.0)));
let (k4q, k4v) = f(&(q + k3q * h), &(qd + k3v * h));
(
q + (k1q + k2q * 2.0 + k3q * 2.0 + k4q) * (h / 6.0),
qd + (k1v + k2v * 2.0 + k3v * 2.0 + k4v) * (h / 6.0),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reach2r::Reach2R;
use nalgebra::Vector2;
use std::f64::consts::PI;
/// Zero torque, zero friction ⇒ E constant. The tolerance is empirical
/// (RK4 is not symplectic), calibrated once and never loosened.
#[test]
fn energy_drift_rk4() {
let arm = Reach2R::uniform(1.0, 2.0, 1.0, 1.0, 9.81);
let (mut q, mut qd) = (Vector2::new(PI / 4.0, PI / 2.0), Vector2::zeros());
let e0 = arm.energy(&q, &qd);
let mut worst = 0.0_f64;
for _ in 0..10_000 {
(q, qd) = rk4_step(&arm, &q, &qd, &Vector2::zeros(), 1e-3);
worst = worst.max(((arm.energy(&q, &qd) - e0) / e0).abs());
}
assert!(worst < 1e-6, "RK4 energy drift {worst:e}");
}
}The worked example, printed
cargo run --example two_r_numbers -p dynamics builds Reach2R::uniform(1.0, 2.0, 1.0, 1.0, 9.81),
evaluates it at and prints
M = [[2.0000, 0.3333], [0.3333, 0.3333]] det 0.5556 eig 2.0642 0.2691
g = [10.4051, -3.4684]
Cq̇ = [0.0000, 0.5000] for q̇ = (1, 0)
K = 1.0000 J V = 17.3418 J E = 18.3418 J
M11 sweep over q2 ∈ {0, π/2, π}: 3.0000 2.0000 1.0000
energy drift, τ = 0, h = 1 ms, 10 s: rk4 9.4e-9 semi-implicit Euler 1.1e-1 explicit Euler 1.4e-1#[test] fn reproduces_micro_example() asserts each line to . Three more tests pin the
chapter's claims: christoffel_closed_form_matches_finite_differences (2R closed form against central
differences with step , agreement to ), energy_drift_rk4 above (with the explicit
Euler run asserted to be worse than , so the comparison in the widget is honest), and
rapier_agrees, which assembles Reach in rapier2d as two rigid bodies joined by revolute joints and
checks that one engine step at agrees with forward to relative, and
2 s trajectories to — the engine's own integrator sets that tolerance, and the text says so.
The widgets on this page run the TypeScript port in lib/dynamics/, a line-for-line translation of
the Rust above. Its self-checks reproduce every number in the printout: M = [[2, 1/3], [1/3, 1/3]],
g = (10.4051, −3.4684), C q̇ = (0, 0.5), the sweep, the eigenvalues, the energy
drift, the knife-edge closed forms, and the brick that flips.
Putting it together
The integration lab drops the same Reach2R into Chapter 2's Workbench with gravity on and a PD
controller holding a planned waypoint:
Hold the micro-example pose. The gravity compensation term alone is N·m — more than half the shoulder motor's 20 N·m budget just to stand still. Raise and the arm overshoots on release; the overshoot is governed by , which is why a gain that is crisp with the arm folded () rings with it stretched (). The torque trace this lab produces, peak and all, is the limit Chapter 18's time scaler inherits.
Here is where the chapter closes and the next one opens. Take any planned path , substitute and into the standard form, and the equations collapse to
A path plus these equations is a one-dimensional dynamics in the path parameter. The whole of
Chapter 18 lives in the plane that equation
defines. Chapter 19 linearizes forward along a trajectory
for iLQR. Chapter 20 reuses and asks whether the
knife-edge constraint can be integrated. All three import this crate and none of them re-derives a
line of it.
Exercises
- Foundation exerciseDifficulty 2 of 3The three-term formula, and what breaks without it
Starting from , carry out Step 4 of the first derivation and show that the symmetrization produces exactly . Then take with — for , with — and show the equations of motion are unchanged while is skew only if .
- Foundation exerciseDifficulty 1 of 3Choset Problem 10.1: a point mass in polar coordinates
With and , write , compute the Christoffel symbols, and verify and . Then show that the unforced motions are straight lines in the Cartesian plane — so the symbols are not describing a force but the bending of straight lines in these coordinates.
For m = 2 kg at r = 1.5 m, what is Γ²₁₂?
kg·m - Conceptual exerciseDifficulty 2 of 3Predict the ellipsePredict first
In the Inertia Ellipsoid the semi-axes at q₂ = π/2 are 1.928 and 0.696. At q₂ = π, where M₁₁ = 1 and M₁₂ = 1/3 − 1/2 = −1/6, what happens to the two semi-axes?
- Conceptual exerciseDifficulty 2 of 3Which way does the lie go?
In the Lagrangian Lab switch Coriolis off and release from . Does increase or decrease first, and why does the sign depend on the release configuration? Confirm with the torque-gauge stacks.
- Practical exerciseDifficulty 2 of 3Reach3R from three Jacobians
Implement
Reach3Rby assembling from three center-of-mass Jacobians, and from the -rows of the same Jacobians. Add a property test that is positive definite on 1000 seeded configurations (SmallRng), thatskew_symmetry_defectis below with finite-difference symbols, and thatinverse(forward(q, q̇, τ))returns to . The TypeScriptPlanarArm.reach3R()does the same with links of 0.6, 0.5, 0.4 m and 1.2, 1.0, 0.8 kg; its checkM(q) is positive definite at 200 seeded configurations of each armis the one to match. - Practical exerciseDifficulty 3 of 3Rusty's chassis as a knife-edge (stretch)
Implement Rusty's chassis as a planar rigid body with the no-side-slip Pfaffian constraint of Example 10.3.1 using
constrained_forward_dynamics. Verify against eq. (10.28) and show that the reduced two-equation form of Choset p. 361, and , emerges from . Then drive it: with and the body turns in place, and the constraint force is the centripetal force the floor supplies. Chapter 20 will show this constraint cannot be integrated to a constraint on .
References
- Choset, H., Lynch, K. M., Hutchinson, S., Kantor, G., Burgard, W., Kavraki, L. E., and Thrun, S. (2005) Principles of Robot Motion: Theory, Algorithms, and Implementations. MIT Press.link to Principles of Robot Motion: Theory, Algorithms, and Implementations (opens in a new tab)
Chapter 10 is the source of everything here: the Lagrangian recipe, the standard form and Christoffel symbols (§10.2), Pfaffian constraints and the projections P and P_u (§10.3), and Euler's equation in both frames (§10.4). Our 2R arm is Problems 10.2–10.3 worked in full.
- Murray, R. M., Li, Z., and Sastry, S. S. (1994) A Mathematical Introduction to Robotic Manipulation. CRC Press.link to A Mathematical Introduction to Robotic Manipulation (opens in a new tab)
Chapter 4 derives the manipulator equations with the Christoffel symbols in the index convention used here, and proves the skew-symmetry of Ṁ − 2C that this chapter turns into a test oracle.
- Lynch, K. M. and Park, F. C. (2017) Modern Robotics: Mechanics, Planning, and Control. Cambridge University Press.link to Modern Robotics: Mechanics, Planning, and Control (opens in a new tab)
Chapter 8 covers the same dynamics with the Newton–Euler recursion as the computational alternative to the Lagrangian route taken here; written by one of Choset's co-authors.
- Featherstone, R. (2008) Rigid Body Dynamics Algorithms. Springer.doi:10.1007/978-1-4899-7560-7 (opens in a new tab)
The composite-rigid-body and articulated-body algorithms that build M(q) and solve forward dynamics in O(n) — what replaces our O(n³) Jacobian assembly once an arm has more than a handful of joints.
- Carpentier, J., Saurel, G., Buondonno, G., Mirabel, J., Lamiraux, F., Stasse, O., and Mansard, N. (2019) The Pinocchio C++ library: A fast and flexible implementation of rigid body dynamics algorithms and their analytical derivatives. IEEE/SICE International Symposium on System Integration (SII).doi:10.1109/SII.2019.8700380 (opens in a new tab)
A production rigid-body library that assembles the same quantities this chapter hand-rolls, with analytic derivatives — the modern form of the 'build M from link Jacobians' pattern behind Reach3R.
- Hairer, E., Lubich, C., and Wanner, G. (2006) Geometric Numerical Integration: Structure-Preserving Algorithms for Ordinary Differential Equations. Springer Series in Computational Mathematics 31, 2nd edition.doi:10.1007/3-540-30666-8 (opens in a new tab)
Why RK4 is not symplectic and what symplectic integrators preserve instead — the theory behind the honesty item that the energy-drift bound is empirical.
