Chapter 11: Lagrange–Euler Dynamics for Manipulators
Lesson 4: Examples — 2-DOF and 3-DOF Arms
This lesson applies the general Lagrange–Euler formulation developed in previous lessons to concrete serial manipulators: a 2-DOF planar R–R arm and a 3-DOF planar R–R–R arm. We derive explicit expressions for the inertia matrix, Coriolis/centrifugal terms, and gravity vector, and then implement these models in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica.
1. Conceptual Overview
From Lesson 1 we know that an n-DOF rigid manipulator admits equations of motion of the form
\[ \mathbf{M}(\mathbf{q})\,\ddot{\mathbf{q}} + \mathbf{C}(\mathbf{q},\dot{\mathbf{q}})\,\dot{\mathbf{q}} + \mathbf{g}(\mathbf{q}) = \boldsymbol{\tau}, \]
where \( \mathbf{M}(\mathbf{q}) \) is the symmetric positive definite inertia matrix, \( \mathbf{C}(\mathbf{q},\dot{\mathbf{q}}) \) collects Coriolis and centrifugal terms, \( \mathbf{g}(\mathbf{q}) \) is the gravity torque vector, and \( \boldsymbol{\tau} \) contains joint input torques.
In this lesson we specialize this general structure to:
- A 2-DOF planar R–R arm (two revolute joints in a plane).
- A 3-DOF planar R–R–R arm.
This illustrates how geometry, mass distribution, and joint coordinates appear explicitly in \( \mathbf{M},\mathbf{C},\mathbf{g} \), and prepares us for symbolic and numeric derivation workflows in Lesson 5.
flowchart TD
A["Geometric model (links, joints, COMs)"] --> B["Kinematics: positions, velocities"]
B --> C["Energies: T(q,qdot), V(q)"]
C --> D["Lagrangian L = T - V"]
D --> E["Compute d/dt(dL/dqdot) - dL/dq"]
E --> F["Arrange into M(q) qddot + C(q,qdot) qdot + g(q) = tau"]
2. 2-DOF Planar R–R Manipulator — Geometry and Parameters
Consider a 2-DOF planar manipulator with two revolute joints, joint angles \( q_1, q_2 \), and link lengths \( \ell_1, \ell_2 \). Let each link be a rigid body with:
- Masses \( m_1, m_2 \).
- Center-of-mass (COM) distances along each link: \( c_1 \) from joint 1 to the COM of link 1, \( c_2 \) from joint 2 to the COM of link 2.
- Planar rotational inertias about the COM: \( I_1, I_2 \).
Using the base frame at joint 1, with the x-axis along the first link when \( q_1 = 0 \) and gravity pointing in the negative y-direction (for convenience), the COM positions are:
\[ \begin{aligned} x_1(q_1) &= c_1 \cos q_1, & y_1(q_1) &= c_1 \sin q_1, \\ x_2(q_1,q_2) &= \ell_1 \cos q_1 + c_2 \cos(q_1 + q_2), & y_2(q_1,q_2) &= \ell_1 \sin q_1 + c_2 \sin(q_1 + q_2). \end{aligned} \]
Differentiating with respect to time gives COM velocities \( \mathbf{v}_i = \dot{\mathbf{p}}_i(q,\dot{q}) \) as functions of \( q_1,q_2,\dot{q}_1,\dot{q}_2 \). These feed into the kinetic energy.
3. 2-DOF Arm — Kinetic and Potential Energy
The total kinetic energy is the sum of translational and rotational contributions:
\[ T = \frac{1}{2} m_1 \|\mathbf{v}_1\|^2 + \frac{1}{2} I_1 \dot{q}_1^2 + \frac{1}{2} m_2 \|\mathbf{v}_2\|^2 + \frac{1}{2} I_2 (\dot{q}_1 + \dot{q}_2)^2 . \]
Using the expressions for \( \mathbf{v}_1,\mathbf{v}_2 \), one obtains the standard quadratic form:
\[ T = \tfrac{1}{2} \begin{bmatrix} \dot{q}_1 & \dot{q}_2 \end{bmatrix} \mathbf{M}(q_2) \begin{bmatrix} \dot{q}_1 \\ \dot{q}_2 \end{bmatrix}, \]
with inertia matrix entries
\[ \begin{aligned} M_{11}(q_2) &= I_1 + I_2 + m_1 c_1^2 + m_2\big(\ell_1^2 + c_2^2 + 2 \ell_1 c_2 \cos q_2\big), \\ M_{12}(q_2) &= M_{21}(q_2) = I_2 + m_2\big(c_2^2 + \ell_1 c_2 \cos q_2\big), \\ M_{22}(q_2) &= I_2 + m_2 c_2^2. \end{aligned} \]
The potential energy (with gravity magnitude \( g > 0 \) pointing in negative y) is
\[ V(q_1,q_2) = m_1 g c_1 \sin q_1 + m_2 g\big(\ell_1 \sin q_1 + c_2 \sin(q_1 + q_2)\big). \]
The Lagrangian is \( L(q,\dot{q}) = T(q,\dot{q}) - V(q) \) and feeds directly into the Euler–Lagrange equations.
4. 2-DOF Arm — Explicit Dynamics: \( \mathbf{M}, \mathbf{C}, \mathbf{g} \)
Applying the Euler–Lagrange equation from Lesson 1, \( \frac{\mathrm{d}}{\mathrm{d}t}\frac{\partial L}{\partial \dot{q}_i} - \frac{\partial L}{\partial q_i} = \tau_i \), we obtain the standard dynamic model:
\[ \mathbf{M}(q_2)\,\ddot{\mathbf{q}} + \mathbf{C}(q_2,\dot{\mathbf{q}})\,\dot{\mathbf{q}} + \mathbf{g}(q_1,q_2) = \boldsymbol{\tau}, \quad \mathbf{q} = \begin{bmatrix} q_1 \\ q_2 \end{bmatrix}. \]
Define the coupling term \( h(q_2) = m_2 \ell_1 c_2 \sin q_2 \). A widely used (and energy-consistent) choice for \( \mathbf{C} \) is
\[ \mathbf{C}(q_2,\dot{\mathbf{q}}) = \begin{bmatrix} -h(q_2)\,\dot{q}_2 & -h(q_2)\,(\dot{q}_1 + \dot{q}_2) \\ h(q_2)\,\dot{q}_1 & 0 \end{bmatrix}. \]
The gravity vector is
\[ \mathbf{g}(q_1,q_2) = \begin{bmatrix} (m_1 c_1 + m_2 \ell_1) g \cos q_1 + m_2 c_2 g \cos(q_1 + q_2) \\ m_2 c_2 g \cos(q_1 + q_2) \end{bmatrix}. \]
The following structural properties (from Lesson 3) hold:
- Symmetry and positive definiteness: \( \mathbf{M}(q_2) = \mathbf{M}(q_2)^\top \) and \( \mathbf{M}(q_2) \) is positive definite for all configurations.
- Skew-symmetry: the matrix \( \dot{\mathbf{M}}(\mathbf{q}) - 2\mathbf{C}(\mathbf{q},\dot{\mathbf{q}}) \) is skew-symmetric, which underpins passivity of the manipulator dynamics.
5. 3-DOF Planar R–R–R Arm — Structural Dynamics
Consider now a 3-DOF planar manipulator with revolute joints \( q_1,q_2,q_3 \), link lengths \( \ell_1,\ell_2,\ell_3 \), COM distances \( c_1,c_2,c_3 \), masses \( m_1,m_2,m_3 \), and inertias \( I_1,I_2,I_3 \) about the COMs.
The kinetic energy again has quadratic form
\[ T = \tfrac{1}{2} \dot{\mathbf{q}}^\top \mathbf{M}(\mathbf{q}) \dot{\mathbf{q}}, \quad \mathbf{q} = \begin{bmatrix} q_1 \\ q_2 \\ q_3 \end{bmatrix}, \]
with \( \mathbf{M}(\mathbf{q}) \) a \( 3\times 3 \) symmetric matrix. Instead of writing all entries explicitly (which is algebraically long), it is useful to recognize that every entry is a linear combination of a small set of trigonometric functions:
\[ \mathbf{M}(\mathbf{q}) = \mathbf{M}_0 + \mathbf{M}_1 \cos q_2 + \mathbf{M}_2 \cos q_3 + \mathbf{M}_3 \cos(q_2 + q_3), \]
where \( \mathbf{M}_0,\dots,\mathbf{M}_3 \) are constant symmetric matrices determined by \( m_i, I_i, \ell_i, c_i \). This parametric structure is heavily used in identification (Chapter 19).
The gravity vector has the generic structure
\[ \mathbf{g}(\mathbf{q}) = \mathbf{g}_1 \cos q_1 + \mathbf{g}_2 \cos(q_1 + q_2) + \mathbf{g}_3 \cos(q_1 + q_2 + q_3), \]
where the constant vectors \( \mathbf{g}_i \) encode the vertical projection of each link's COM. The Coriolis/centrifugal terms are constructed from the Christoffel symbols (Lesson 2); they can be expressed as bilinear forms in \( \dot{\mathbf{q}} \) with coefficients depending on \( \sin q_2, \sin q_3, \sin(q_2 + q_3) \).
Despite the more complex expressions, all structural properties (symmetry and positive definiteness of \( \mathbf{M} \), skew-symmetry of \( \dot{\mathbf{M}} - 2\mathbf{C} \), passivity) remain valid.
flowchart TD
Q["q = [q1,q2,q3]"] --> T["Compute link COM speeds: v1,v2,v3"]
T --> K["Form T = 0.5 * sum( mi * |vi|^2 + Ii * wi^2 )"]
K --> M["Identify M(q) by collecting terms in qdot"]
M --> Cmat["Compute Christoffel coefficients for C(q,qdot)"]
Cmat --> G["Take V(q) from COM heights, then g(q) = dV/dq"]
G --> FINAL["Assemble 3x3 dynamics: M(q) qddot + C(q,qdot) qdot + g(q) = tau"]
6. Python Implementation — 2-DOF and 3-DOF Dynamics
We now implement the 2-DOF planar dynamics explicitly in Python using
numpy. Symbolic derivation can be performed with
sympy, but here we assume the closed-form expressions from
Sections 3 and 4.
import numpy as np
class TwoLinkParams:
def __init__(self, l1, l2, c1, c2, m1, m2, I1, I2, g=9.81):
self.l1 = l1
self.l2 = l2
self.c1 = c1
self.c2 = c2
self.m1 = m1
self.m2 = m2
self.I1 = I1
self.I2 = I2
self.g = g
def M_2R(q, params):
q1, q2 = q
l1 = params.l1
c1 = params.c1
c2 = params.c2
m1 = params.m1
m2 = params.m2
I1 = params.I1
I2 = params.I2
cos2 = np.cos(q2)
M11 = I1 + I2 + m1 * c1**2 + m2 * (l1**2 + c2**2 + 2.0 * l1 * c2 * cos2)
M12 = I2 + m2 * (c2**2 + l1 * c2 * cos2)
M22 = I2 + m2 * c2**2
return np.array([[M11, M12],
[M12, M22]])
def C_2R(q, qdot, params):
q1, q2 = q
q1dot, q2dot = qdot
l1 = params.l1
c2 = params.c2
m2 = params.m2
sin2 = np.sin(q2)
h = m2 * l1 * c2 * sin2
C11 = -h * q2dot
C12 = -h * (q1dot + q2dot)
C21 = h * q1dot
C22 = 0.0
return np.array([[C11, C12],
[C21, C22]])
def g_2R(q, params):
q1, q2 = q
c1 = params.c1
c2 = params.c2
l1 = params.l1
m1 = params.m1
m2 = params.m2
g = params.g
g1 = (m1 * c1 + m2 * l1) * g * np.cos(q1) + m2 * c2 * g * np.cos(q1 + q2)
g2 = m2 * c2 * g * np.cos(q1 + q2)
return np.array([g1, g2])
def forward_dynamics_2R(q, qdot, tau, params):
M = M_2R(q, params)
C = C_2R(q, qdot, params)
g_vec = g_2R(q, params)
rhs = tau - C @ qdot - g_vec
qddot = np.linalg.solve(M, rhs)
return qddot
# Example usage
if __name__ == "__main__":
params = TwoLinkParams(
l1=1.0, l2=1.0,
c1=0.5, c2=0.5,
m1=1.0, m2=1.0,
I1=0.1, I2=0.1,
g=9.81
)
q = np.array([0.3, 0.4])
qdot = np.array([0.2, -0.1])
tau = np.array([0.5, 0.2])
qddot = forward_dynamics_2R(q, qdot, tau, params)
print("qddot =", qddot)
For a 3-DOF planar arm, one practical approach is to precompute constant matrices \( \mathbf{M}_0,\dots,\mathbf{M}_3 \) and implement
def M_3R(q, M0, M1, M2, M3):
q1, q2, q3 = q
return (M0
+ np.cos(q2) * M1
+ np.cos(q3) * M2
+ np.cos(q2 + q3) * M3)
where M0,...,M3 are 3x3 symmetric
numpy arrays determined once from symbolic derivation
(Lesson 5).
7. C++ Implementation — 2-DOF Dynamics with Eigen
In C++, a common choice for small dense linear algebra is the
Eigen library. Below we implement
\( \mathbf{M}, \mathbf{C}, \mathbf{g} \) and the
forward dynamics map
\( (\mathbf{q},\dot{\mathbf{q}},\boldsymbol{\tau}) \mapsto
\ddot{\mathbf{q}} \).
#include <Eigen/Dense>
struct TwoLinkParams {
double l1, l2;
double c1, c2;
double m1, m2;
double I1, I2;
double g;
};
Eigen::Matrix2d M_2R(const Eigen::Vector2d& q, const TwoLinkParams& p) {
double q2 = q(1);
double cos2 = std::cos(q2);
double M11 = p.I1 + p.I2
+ p.m1 * p.c1 * p.c1
+ p.m2 * (p.l1 * p.l1 + p.c2 * p.c2 + 2.0 * p.l1 * p.c2 * cos2);
double M12 = p.I2 + p.m2 * (p.c2 * p.c2 + p.l1 * p.c2 * cos2);
double M22 = p.I2 + p.m2 * p.c2 * p.c2;
Eigen::Matrix2d M;
M(0,0) = M11; M(0,1) = M12;
M(1,0) = M12; M(1,1) = M22;
return M;
}
Eigen::Matrix2d C_2R(const Eigen::Vector2d& q,
const Eigen::Vector2d& qdot,
const TwoLinkParams& p) {
double q2 = q(1);
double q1dot = qdot(0);
double q2dot = qdot(1);
double sin2 = std::sin(q2);
double h = p.m2 * p.l1 * p.c2 * sin2;
Eigen::Matrix2d C;
C(0,0) = -h * q2dot;
C(0,1) = -h * (q1dot + q2dot);
C(1,0) = h * q1dot;
C(1,1) = 0.0;
return C;
}
Eigen::Vector2d g_2R(const Eigen::Vector2d& q, const TwoLinkParams& p) {
double q1 = q(0);
double q2 = q(1);
double g1 = (p.m1 * p.c1 + p.m2 * p.l1) * p.g * std::cos(q1)
+ p.m2 * p.c2 * p.g * std::cos(q1 + q2);
double g2 = p.m2 * p.c2 * p.g * std::cos(q1 + q2);
Eigen::Vector2d gv;
gv(0) = g1;
gv(1) = g2;
return gv;
}
Eigen::Vector2d forward_dynamics_2R(const Eigen::Vector2d& q,
const Eigen::Vector2d& qdot,
const Eigen::Vector2d& tau,
const TwoLinkParams& p) {
Eigen::Matrix2d M = M_2R(q, p);
Eigen::Matrix2d C = C_2R(q, qdot, p);
Eigen::Vector2d gvec = g_2R(q, p);
Eigen::Vector2d rhs = tau - C * qdot - gvec;
Eigen::Vector2d qddot = M.ldlt().solve(rhs);
return qddot;
}
For larger DOF counts or dynamic parameters, one typically organizes the
code in classes (e.g., a RigidBody hierarchy) but the
structural form remains the same.
8. Java Implementation — 2-DOF Dynamics
In Java, we can implement the same model using primitive arrays without external libraries. The inertia matrix inversion is coded explicitly for the 2x2 case.
public class TwoLinkDynamics {
public static class Params {
public double l1, l2;
public double c1, c2;
public double m1, m2;
public double I1, I2;
public double g;
public Params(double l1, double l2,
double c1, double c2,
double m1, double m2,
double I1, double I2,
double g) {
this.l1 = l1;
this.l2 = l2;
this.c1 = c1;
this.c2 = c2;
this.m1 = m1;
this.m2 = m2;
this.I1 = I1;
this.I2 = I2;
this.g = g;
}
}
public static double[][] M(double[] q, Params p) {
double q2 = q[1];
double cos2 = Math.cos(q2);
double M11 = p.I1 + p.I2
+ p.m1 * p.c1 * p.c1
+ p.m2 * (p.l1 * p.l1 + p.c2 * p.c2 + 2.0 * p.l1 * p.c2 * cos2);
double M12 = p.I2 + p.m2 * (p.c2 * p.c2 + p.l1 * p.c2 * cos2);
double M22 = p.I2 + p.m2 * p.c2 * p.c2;
return new double[][] {
{ M11, M12 },
{ M12, M22 }
};
}
public static double[][] C(double[] q, double[] qdot, Params p) {
double q2 = q[1];
double q1dot = qdot[0];
double q2dot = qdot[1];
double sin2 = Math.sin(q2);
double h = p.m2 * p.l1 * p.c2 * sin2;
return new double[][] {
{ -h * q2dot, -h * (q1dot + q2dot) },
{ h * q1dot, 0.0 }
};
}
public static double[] g(double[] q, Params p) {
double q1 = q[0];
double q2 = q[1];
double g1 = (p.m1 * p.c1 + p.m2 * p.l1) * p.g * Math.cos(q1)
+ p.m2 * p.c2 * p.g * Math.cos(q1 + q2);
double g2 = p.m2 * p.c2 * p.g * Math.cos(q1 + q2);
return new double[] { g1, g2 };
}
public static double[] forwardDynamics(double[] q,
double[] qdot,
double[] tau,
Params p) {
double[][] M = M(q, p);
double[][] C = C(q, qdot, p);
double[] g = g(q, p);
// rhs = tau - C qdot - g
double[] rhs = new double[2];
rhs[0] = tau[0]
- (C[0][0] * qdot[0] + C[0][1] * qdot[1])
- g[0];
rhs[1] = tau[1]
- (C[1][0] * qdot[0] + C[1][1] * qdot[1])
- g[1];
// Invert 2x2 inertia matrix
double det = M[0][0] * M[1][1] - M[0][1] * M[1][0];
double inv00 = M[1][1] / det;
double inv01 = -M[0][1] / det;
double inv10 = -M[1][0] / det;
double inv11 = M[0][0] / det;
double[] qddot = new double[2];
qddot[0] = inv00 * rhs[0] + inv01 * rhs[1];
qddot[1] = inv10 * rhs[0] + inv11 * rhs[1];
return qddot;
}
}
For a 3-DOF arm, similar array-based code can be used with a generic 3x3 matrix inversion routine.
9. MATLAB/Simulink Implementation — 2-DOF Dynamics
MATLAB offers both symbolic and numeric workflows. A compact numeric function for the 2-DOF dynamics is:
function qddot = two_link_forward_dynamics(t, state, tau, p)
% state = [q1; q2; q1dot; q2dot]
q1 = state(1); q2 = state(2);
q1dot = state(3); q2dot = state(4);
l1 = p.l1; c1 = p.c1; c2 = p.c2;
m1 = p.m1; m2 = p.m2;
I1 = p.I1; I2 = p.I2;
g = p.g;
cos2 = cos(q2);
sin2 = sin(q2);
M11 = I1 + I2 + m1 * c1^2 + m2 * (l1^2 + c2^2 + 2 * l1 * c2 * cos2);
M12 = I2 + m2 * (c2^2 + l1 * c2 * cos2);
M22 = I2 + m2 * c2^2;
M = [M11, M12; M12, M22];
h = m2 * l1 * c2 * sin2;
C = [-h * q2dot, -h * (q1dot + q2dot);
h * q1dot, 0];
g1 = (m1 * c1 + m2 * l1) * g * cos(q1) + m2 * c2 * g * cos(q1 + q2);
g2 = m2 * c2 * g * cos(q1 + q2);
gvec = [g1; g2];
rhs = tau - C * [q1dot; q2dot] - gvec;
qdd = M \ rhs;
qddot = qdd;
end
In Simulink, one typical approach is:
-
Use a
MATLAB Functionblock that encapsulatestwo_link_forward_dynamics. -
Integrate the joint accelerations using two cascaded
Integratorblocks to obtain \( \dot{\mathbf{q}} \) and \( \mathbf{q} \). - Feed commanded \( \boldsymbol{\tau} \) as an input signal to the function block.
For a 3-DOF arm, a 3x3 inertia matrix and 3x1 vectors are used, with the same signal-flow structure.
10. Wolfram Mathematica — Symbolic Derivation Template
Wolfram Mathematica is very convenient for symbolic Lagrangian derivations. Below is a template for the 2-DOF planar arm.
(* Define symbols *)
ClearAll["Global`*"];
q1[t_]; q2[t_];
m1 = Symbol["m1"]; m2 = Symbol["m2"];
l1 = Symbol["l1"]; c1 = Symbol["c1"]; c2 = Symbol["c2"];
I1 = Symbol["I1"]; I2 = Symbol["I2"];
g = Symbol["g"];
q1t = q1[t]; q2t = q2[t];
q1d = D[q1t, t]; q2d = D[q2t, t];
(* COM positions *)
x1 = c1 Cos[q1t];
y1 = c1 Sin[q1t];
x2 = l1 Cos[q1t] + c2 Cos[q1t + q2t];
y2 = l1 Sin[q1t] + c2 Sin[q1t + q2t];
(* Velocities *)
vx1 = D[x1, t]; vy1 = D[y1, t];
vx2 = D[x2, t]; vy2 = D[y2, t];
(* Kinetic and potential energy *)
T = 1/2 m1 (vx1^2 + vy1^2) + 1/2 I1 q1d^2
+ 1/2 m2 (vx2^2 + vy2^2) + 1/2 I2 (q1d + q2d)^2;
V = m1 g y1 + m2 g y2;
L = T - V;
(* Euler-Lagrange equations *)
eq1 = D[D[L, q1d], t] - D[L, q1t] == Symbol["tau1"];
eq2 = D[D[L, q2d], t] - D[L, q2t] == Symbol["tau2"];
(* Expand and collect with respect to accelerations q1dd, q2dd *)
q1dd = D[q1d, t]; q2dd = D[q2d, t];
eq1exp = Expand[eq1];
eq2exp = Expand[eq2];
(* Coefficients of q1dd, q2dd provide inertia matrix entries *)
M11 = Coefficient[eq1exp, q1dd];
M12 = Coefficient[eq1exp, q2dd];
M21 = Coefficient[eq2exp, q1dd];
M22 = Coefficient[eq2exp, q2dd];
Mmat = { {M11, M12}, {M21, M22} } // Simplify;
(* Residual terms yield C(q,qdot) qdot + g(q) *)
rhs1 = (Symbol["tau1"] - (eq1exp /. {q1dd -> 0, q2dd -> 0})) // Simplify;
rhs2 = (Symbol["tau2"] - (eq2exp /. {q1dd -> 0, q2dd -> 0})) // Simplify;
rhs = {rhs1, rhs2};
The resulting Mmat and rhs can be further
processed to identify
\( \mathbf{C}(\mathbf{q},\dot{\mathbf{q}})\dot{\mathbf{q}} \)
and \( \mathbf{g}(\mathbf{q}) \), and exported as C or
C++ code.
11. Problems and Solutions
Problem 1 (Deriving \( \mathbf{M} \) for a Simplified 2-DOF Arm): Consider a 2-DOF planar R–R arm where link 1 and link 2 are modeled as point masses at distances \( c_1 \) and \( c_2 \) from their proximal joints with zero rotational inertia (i.e., \( I_1 = I_2 = 0 \)). Derive the inertia matrix \( \mathbf{M}(q_2) \).
Solution:
For point masses, the kinetic energy is purely translational:
\[ T = \tfrac{1}{2} m_1 \|\mathbf{v}_1\|^2 + \tfrac{1}{2} m_2 \|\mathbf{v}_2\|^2. \]
Using the COM positions from Section 2, one finds
\[ \|\mathbf{v}_1\|^2 = c_1^2 \dot{q}_1^2, \quad \|\mathbf{v}_2\|^2 = \ell_1^2 \dot{q}_1^2 + c_2^2 (\dot{q}_1 + \dot{q}_2)^2 + 2 \ell_1 c_2 \cos q_2\,\dot{q}_1(\dot{q}_1 + \dot{q}_2). \]
Expanding and collecting terms in \( \dot{q}_1^2, \dot{q}_1 \dot{q}_2, \dot{q}_2^2 \), one obtains
\[ \begin{aligned} T &= \tfrac{1}{2}\big( [m_1 c_1^2 + m_2(\ell_1^2 + c_2^2 + 2\ell_1 c_2 \cos q_2)] \dot{q}_1^2 \\ &\quad + 2 m_2(c_2^2 + \ell_1 c_2 \cos q_2)\dot{q}_1 \dot{q}_2 + m_2 c_2^2 \dot{q}_2^2\big). \end{aligned} \]
Matching this to \( T = \tfrac{1}{2} \dot{\mathbf{q}}^\top \mathbf{M} \dot{\mathbf{q}} \) yields exactly the same formulas as in Section 3 but with \( I_1 = I_2 = 0 \).
Problem 2 (Skew-Symmetry Check for the 2-DOF Arm): For the 2-DOF model in Section 4, verify that \( \dot{\mathbf{M}}(\mathbf{q}) - 2\mathbf{C}(\mathbf{q},\dot{\mathbf{q}}) \) is skew-symmetric by explicitly computing its (1,1) entry.
Solution:
We only need the (1,1) entry. Since \( M_{11} \) depends on \( q_2 \) but not on \( q_1 \), we have
\[ \dot{M}_{11} = \frac{\partial M_{11}}{\partial q_2} \dot{q}_2 = -2 m_2 \ell_1 c_2 \sin q_2\,\dot{q}_2 = -2 h(q_2)\,\dot{q}_2. \]
From the definition of \( \mathbf{C} \), \( C_{11} = -h(q_2)\dot{q}_2 \), so
\[ \big(\dot{\mathbf{M}} - 2\mathbf{C}\big)_{11} = \dot{M}_{11} - 2 C_{11} = -2 h(q_2)\dot{q}_2 - 2(-h(q_2)\dot{q}_2) = 0. \]
Thus the diagonal element is zero; similar computations for off-diagonal entries verify skew-symmetry.
Problem 3 (Numerical Torque Computation): For a 2-DOF arm with \( \ell_1 = \ell_2 = 1 \), \( c_1 = c_2 = 0.5 \), \( m_1 = m_2 = 1 \), \( I_1 = I_2 = 0.1 \), \( g = 9.81 \), compute \( \boldsymbol{\tau} \) required to hold the arm at the static configuration \( q_1 = \pi/4, q_2 = -\pi/6 \) with \( \dot{\mathbf{q}} = \ddot{\mathbf{q}} = \mathbf{0} \).
Solution:
At static equilibrium with zero velocities and accelerations, the dynamics reduce to \( \mathbf{g}(q_1,q_2) = \boldsymbol{\tau} \). Therefore \( \boldsymbol{\tau} \) equals the gravity vector evaluated at the given configuration:
\[ \begin{aligned} &g_1 = (m_1 c_1 + m_2 \ell_1) g \cos q_1 + m_2 c_2 g \cos(q_1 + q_2), \\ &g_2 = m_2 c_2 g \cos(q_1 + q_2). \end{aligned} \]
Substituting the numeric values yields specific real numbers for \( g_1, g_2 \), which are the required holding torques. Students are encouraged to compute these numerically using any of the code implementations above.
Problem 4 (3-DOF Structural Form): Show that for any 3-DOF planar R–R–R arm with COMs in the plane, each entry of \( \mathbf{M}(\mathbf{q}) \) can be expressed as a finite linear combination of \( \{1, \cos q_2, \cos q_3, \cos(q_2 + q_3)\} \).
Solution:
Each COM position is obtained by composing planar rotations of the form \( R_z(q_i) \), so the squared speeds \( \|\mathbf{v}_i\|^2 \) are sums of terms like \( \dot{q}_j \dot{q}_k (\cos(\cdot), \sin(\cdot)) \) for \( j,k \in \{1,2,3\} \). Because the sum of joint angles in the planar setting always appears as \( q_1 \), \( q_1 + q_2 \), or \( q_1 + q_2 + q_3 \), the dot products of velocity vectors produce only cosines of differences between these angles, specifically functions of \( q_2 \), \( q_3 \), and \( q_2 + q_3 \). Collecting terms in \( \dot{\mathbf{q}} \) yields an inertia matrix whose entries depend only on \( 1, \cos q_2, \cos q_3, \cos(q_2 + q_3) \). The coefficients are determined by link masses, COM distances, and inertias, but no additional trigonometric functions are needed.
Problem 5 (Simple Forward Simulation Step): Using the 2-DOF dynamics \( \mathbf{M}(\mathbf{q})\ddot{\mathbf{q}} + \mathbf{C}(\mathbf{q},\dot{\mathbf{q}})\dot{\mathbf{q}} + \mathbf{g}(\mathbf{q}) = \boldsymbol{\tau} \), write an explicit Euler integration scheme for a time step \( h > 0 \) that updates \( (\mathbf{q}_k,\dot{\mathbf{q}}_k) \) to \( (\mathbf{q}_{k+1},\dot{\mathbf{q}}_{k+1}) \).
Solution:
Given \( \mathbf{q}_k,\dot{\mathbf{q}}_k,\boldsymbol{\tau}_k \), compute \( \ddot{\mathbf{q}}_k \) by solving \( \mathbf{M}(\mathbf{q}_k)\ddot{\mathbf{q}}_k = \boldsymbol{\tau}_k - \mathbf{C}(\mathbf{q}_k,\dot{\mathbf{q}}_k)\dot{\mathbf{q}}_k - \mathbf{g}(\mathbf{q}_k) \). Then set
\[ \dot{\mathbf{q}}_{k+1} = \dot{\mathbf{q}}_k + h\,\ddot{\mathbf{q}}_k, \quad \mathbf{q}_{k+1} = \mathbf{q}_k + h\,\dot{\mathbf{q}}_k. \]
This explicit Euler scheme is only first-order accurate and may require small \( h \) for stable simulations, but it is conceptually simple and matches the forward-dynamics implementations developed earlier.
12. Summary
In this lesson we instantiated the Lagrange–Euler framework on concrete serial manipulators: a 2-DOF planar R–R arm and a 3-DOF planar R–R–R arm. We derived explicit expressions for the inertia matrix, Coriolis/centrifugal terms, and gravity vector, and emphasized the structural properties (symmetry, positive definiteness, skew-symmetry of \( \dot{\mathbf{M}} - 2\mathbf{C} \), and passivity).
We then implemented these models in several programming environments
that are central in robotics: Python with numpy, C++ with
Eigen, Java with primitive arrays, MATLAB/Simulink, and
Wolfram Mathematica. These concrete examples will be the basis for the
symbolic and numeric derivation pipelines developed in Lesson 5.
13. References
- Luh, J.Y.S., Walker, M.W., & Paul, R.P. (1980). On-line computational scheme for mechanical manipulators. Journal of Dynamic Systems, Measurement, and Control, 102(2), 69–76.
- Hollerbach, J.M. (1980). A recursive Lagrangian formulation of manipulator dynamics and a comparative study of dynamics formulation complexity. IEEE Transactions on Systems, Man, and Cybernetics, 10(11), 730–736.
- Asada, H., & Slotine, J.-J.E. (1986). Robot analysis and control: The Lagrangian formulation. Various journal and conference contributions.
- Khatib, O. (1987). A unified approach for motion and force control of robot manipulators: The operational space formulation. IEEE Journal of Robotics and Automation, 3(1), 43–53.
- Craig, J.J. (1989). Dynamic modeling of robot manipulators. Numerous journal papers complementing textbook expositions.