Chapter 2: Actuator-Space and Joint-Space Control Basics
Lesson 5: Lab: Joint Servoing on a Simulated/Real Arm
In this lab-style lesson we move from theory to practice: you will implement a joint-space servo loop on a simulated robotic arm (and, when available, on a real robot). We build on the PD/PID and gravity-compensation concepts from previous lessons and on the kinematics and dynamics of manipulators that you already know, but without introducing more advanced model-based controllers (such as computed-torque).
The focus is on: (i) deriving and understanding the closed-loop joint dynamics under PD + gravity compensation, (ii) implementing a discrete-time controller, and (iii) addressing practical issues such as saturation, sensor noise, and safety.
1. Joint Servoing Architecture
A joint servo is a feedback loop that regulates each joint angle \( q_i(t) \) around a desired joint trajectory \( q_{d,i}(t) \). At the core of the loop is a controller that converts the joint position/velocity error into an actuator command (typically torque or motor current).
For each joint \(i\), define the position error \( e_i(t) = q_{d,i}(t) - q_i(t) \) and velocity error \( \dot e_i(t) = \dot q_{d,i}(t) - \dot q_i(t) \). The joint servo loop then:
- measures \( q_i(t) \) and (possibly estimated) \( \dot q_i(t) \),
- computes the error \( e_i(t), \dot e_i(t) \),
- generates a torque command using a PD/PID law (plus gravity compensation),
- applies the torque to the actuator, closing the loop.
flowchart TD
R["Desired joint trajectory q_d(t)"] --> E["Error computation: e = q_d - q"]
E --> C["Controller: PD / PID + gravity term g(q)"]
C --> U["Actuator command tau"]
U --> P["Robot joint / arm dynamics"]
P --> S["Sensors: encoders, velocity estimate"]
S --> E
In the lab you will instantiate this loop for one joint (for detailed analysis) and then extend it to multiple joints, relying on approximate decoupling between joints using the diagonal inertia and damping approximations discussed in manipulator dynamics.
2. Continuous-Time Joint PD/PID with Gravity Compensation
Consider an \(n\)-DOF manipulator with dynamics (in joint coordinates)
\[ \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 positive-definite inertia matrix, \( \mathbf{C}(\mathbf{q}, \dot{\mathbf{q}})\dot{\mathbf{q}} \) collects Coriolis/centrifugal terms, and \( \mathbf{g}(\mathbf{q}) \) is the gravity torque vector.
A standard joint-space PD + gravity compensation law (per joint) is
\[ \tau_i(t) = K_{p,i}\big(q_{d,i}(t) - q_i(t)\big) + K_{d,i}\big(\dot q_{d,i}(t) - \dot q_i(t)\big) + g_i(\mathbf{q}(t)), \quad i = 1,\dots,n, \]
where \(K_{p,i} > 0\) and \(K_{d,i} \ge 0\) are scalar gains and \( g_i(\mathbf{q}) \) is the \(i\)-th component of the gravity torque vector.
For joints that require elimination of steady-state error under constant load or slowly varying commands, an integral term is often added:
\[ \tau_i(t) = K_{p,i} e_i(t) + K_{d,i} \dot e_i(t) + K_{i,i} \int_0^t e_i(\sigma)\, d\sigma + g_i(\mathbf{q}(t)). \]
In vector form (neglecting integral action for now),
\[ \boldsymbol{\tau}(t) = \mathbf{K}_p \mathbf{e}(t) + \mathbf{K}_d \dot{\mathbf{e}}(t) + \mathbf{g}(\mathbf{q}(t)), \]
where \( \mathbf{K}_p, \mathbf{K}_d \) are diagonal gain matrices, and \( \mathbf{e}(t) = \mathbf{q}_d(t) - \mathbf{q}(t) \). This is the control law you will implement in the lab.
3. One-DOF Closed-Loop Error Dynamics and Stability
For analysis, consider a single revolute joint with dynamics
\[ J \ddot q(t) + b \dot q(t) + g(q(t)) = \tau(t), \]
where \(J > 0\) is the equivalent inertia and \(b \ge 0\) is viscous friction. Assume a constant desired position \( q_d \) (\(\dot q_d = 0\)) and a PD + gravity law
\[ \tau(t) = K_p\big(q_d - q(t)\big) + K_d\big(0 - \dot q(t)\big) + g(q(t)). \]
Define the error \( e(t) = q_d - q(t) \), so that \( \dot e(t) = -\dot q(t) \) and \( \ddot e(t) = -\ddot q(t) \). Substituting the control law into the dynamics gives
\[ J \ddot q + b \dot q + g(q) = K_p e + K_d \dot e + g(q), \]
so the gravity term cancels and we obtain, after substituting \( \ddot q = -\ddot e \), \( \dot q = -\dot e \),
\[ J(-\ddot e) + b(-\dot e) = K_p e + K_d \dot e \quad \Rightarrow \quad J \ddot e + (b + K_d)\dot e + K_p e = 0. \]
The characteristic polynomial is \( J s^2 + (b+K_d)s + K_p = 0 \), with roots
\[ s_{1,2} = \frac{-(b+K_d) \pm \sqrt{(b+K_d)^2 - 4 J K_p}}{2 J}. \]
Closed-loop stability requires \( J > 0 \), \( K_p > 0 \), and \( b+K_d > 0 \), in which case both roots have negative real part (Routh conditions for a second-order polynomial).
It is convenient to rewrite the error dynamics in the standard second-order form
\[ \ddot e + 2\zeta\omega_n \dot e + \omega_n^2 e = 0, \]
where
\[ \omega_n^2 = \frac{K_p}{J}, \qquad 2\zeta\omega_n = \frac{b+K_d}{J}. \]
From this identification we can design gains for desired natural frequency \( \omega_n \) and damping ratio \( \zeta \in (0,1) \):
\[ K_p = J \omega_n^2, \qquad K_d = 2 J \zeta \omega_n - b. \]
Lyapunov proof sketch. Consider the Lyapunov candidate
\[ V(e,\dot e) = \tfrac{1}{2} J \dot e^2 + \tfrac{1}{2} K_p e^2, \]
which satisfies \( V \ge 0 \) and \( V = 0 \) only when \( e = \dot e = 0 \). Using the error dynamics \( J \ddot e + (b+K_d)\dot e + K_p e = 0 \), we obtain
\[ \dot V = J \dot e \ddot e + K_p e \dot e = \dot e \big(J \ddot e + K_p e\big) = -(b+K_d)\dot e^2 \le 0. \]
Thus the equilibrium \( e = \dot e = 0 \) is globally stable; with \(b+K_d > 0\) and the system being linear time-invariant, the origin is in fact globally asymptotically stable. This provides a theoretical guarantee that your joint servo will converge to the desired constant joint angle under the idealized assumptions (perfect gravity compensation, no saturation, no unmodeled couplings).
4. Discrete-Time Implementation of the Joint Servo
On real hardware (or in most simulators), the controller runs as a digital loop with sampling time \( T_s \). Let \( e[k] = q_d[k] - q[k] \) denote the sampled error at time \( t_k = k T_s \).
A common discrete-time PD + I implementation uses:
\[ \begin{aligned} e[k] &= q_d[k] - q[k], \\ \dot e[k] &\approx \frac{e[k] - e[k-1]}{T_s}, \\ I[k] &= I[k-1] + T_s\, e[k], \\ \tau[k] &= K_p e[k] + K_d \dot e[k] + K_i I[k] + g\big(q[k]\big), \end{aligned} \]
where \(I[k]\) is the discrete-time integral state. In actual implementation you will:
- choose \(T_s\) significantly smaller than the desired closed-loop time constants,
- limit \(I[k]\) to prevent integral windup under torque saturation,
- optionally filter the measured velocity (or the discrete derivative) to attenuate noise.
Note that even though the theoretical analysis in Section 3 was continuous-time, for sufficiently small \(T_s\) the discrete implementation approximates the continuous behavior well. A detailed discrete-time stability analysis can be performed using z-transform techniques (covered in digital control), but for this lesson we rely on the continuous-time gain design and then validate experimentally.
5. Practical Issues: Saturation, Velocity Estimation, and Safety
Actuator saturation. Motors and drives can only deliver torque within a bounded range \([\tau_{\min}, \tau_{\max}]\). In the controller you should constrain the commanded torque:
\[ \tau_{\text{cmd}}[k] = \min\big( \tau_{\max}, \max(\tau_{\min}, \tau[k]) \big). \]
If integral action is present, you must avoid integrating error when the actuator is saturated in a direction that would further increase saturation (anti-windup).
Velocity estimation. Encoders usually measure position only. A simple velocity approximation is a backward difference:
\[ \dot q[k] \approx \frac{q[k] - q[k-1]}{T_s}, \]
possibly passed through a low-pass filter to reduce quantization noise. Alternatively, one can use observers or filters (to be studied later), but for this lab a finite difference with mild filtering suffices.
Safety. Before running the controller on a real robot:
- verify software joint limits and hardware limit switches,
- start with very small gains and small motion ranges,
- ensure an emergency stop is reachable,
- test one joint at a time with the others locked or in a safe configuration.
6. Python Lab — Simulated One-Joint Servo
We first implement a simple one-DOF joint model with PD + gravity
compensation in Python. For a more complex arm, you could use a robotics
library such as
roboticstoolbox-python or a physics engine such as
pybullet, but here we keep the dynamics explicit.
import numpy as np
import matplotlib.pyplot as plt
# Physical parameters of a simple pendulum-like joint
J = 0.05 # inertia [kg m^2]
b = 0.01 # viscous friction [N m s/rad]
m = 1.0 # link mass [kg]
ell = 0.5 # link COM distance [m]
g = 9.81 # gravity [m/s^2]
def gravity_torque(q):
# simple planar joint: tau_g = m g l sin(q)
return m * g * ell * np.sin(q)
# Desired position: step from 0 to qd at t = 0
qd = np.deg2rad(45.0) # 45 degrees
# Continuous-time design: choose wn, zeta and derive Kp, Kd
wn = 8.0 # rad/s
zeta = 0.7
Kp = J * wn**2
Kd = 2.0 * J * zeta * wn - b
print("Kp =", Kp, "Kd =", Kd)
# Discrete simulation
Ts = 0.001 # controller and integration step [s]
t_final = 2.0
N = int(t_final / Ts)
q = 0.0 # initial position
dq = 0.0 # initial velocity
qs = np.zeros(N)
dqs = np.zeros(N)
taus = np.zeros(N)
ts = np.linspace(0.0, t_final, N)
for k in range(N):
# controller (no integral term)
e = qd - q
de = 0.0 - dq
tau = Kp * e + Kd * de + gravity_torque(q)
# simple saturation
tau_max = 10.0
tau_min = -10.0
tau = np.clip(tau, tau_min, tau_max)
# joint dynamics: J ddq + b dq + g(q) = tau
ddq = (tau - b * dq - gravity_torque(q)) / J
# Euler integration
dq = dq + Ts * ddq
q = q + Ts * dq
qs[k] = q
dqs[k] = dq
taus[k] = tau
# Plot results
plt.figure()
plt.subplot(3, 1, 1)
plt.plot(ts, qs, label="q")
plt.axhline(qd, linestyle="--", label="q_d")
plt.ylabel("position [rad]")
plt.legend()
plt.subplot(3, 1, 2)
plt.plot(ts, dqs)
plt.ylabel("velocity [rad/s]")
plt.subplot(3, 1, 3)
plt.plot(ts, taus)
plt.ylabel("tau [N m]")
plt.xlabel("time [s]")
plt.tight_layout()
plt.show()
In your lab report, you should compare the measured overshoot and settling time from this simulation with the values predicted from the second-order model parameters \( \omega_n, \zeta \), and discuss discrepancies due to model simplifications (e.g., Euler integration, unmodeled friction).
7. C++ Implementation Skeleton (Embedded / ROS-like Loop)
For deployment on embedded controllers or within a ROS control loop, a typical C++ implementation uses a class encapsulating the PD + gravity law, often with Eigen for linear algebra (for multi-DOF). Below is a minimal single-joint example without ROS-specific boilerplate:
#include <cmath>
#include <algorithm>
struct JointServo
{
double Kp;
double Kd;
double Ki;
double tau_min;
double tau_max;
double integral; // integral state
JointServo(double kp, double kd, double ki,
double tmin, double tmax)
: Kp(kp), Kd(kd), Ki(ki),
tau_min(tmin), tau_max(tmax),
integral(0.0)
{}
double gravity_torque(double q)
{
// Example: planar link with m g l sin(q)
const double m = 1.0;
const double ell = 0.5;
const double g = 9.81;
return m * g * ell * std::sin(q);
}
double update(double q, double dq,
double qd, double dqd,
double dt)
{
double e = qd - q;
double de = dqd - dq;
// integrate error with simple clamping
integral += e * dt;
const double Imax = 5.0;
if (integral > Imax) integral = Imax;
if (integral < -Imax) integral = -Imax;
double tau = Kp * e + Kd * de + Ki * integral
+ gravity_torque(q);
// saturation
tau = std::clamp(tau, tau_min, tau_max);
return tau;
}
};
// In your real-time loop:
// - dt is the measured control period
// - q, dq from encoders/velocity estimator
// - qd, dqd from trajectory generator
// JointServo servo(Kp, Kd, Ki, -10.0, 10.0);
// double tau_cmd = servo.update(q, dq, qd, dqd, dt);
For multi-DOF arms, you would typically replace the scalar quantities by Eigen vectors and matrices, and use a kinematics/dynamics library (e.g., KDL, RBDL) to compute \( \mathbf{g}(\mathbf{q}) \) for gravity compensation.
8. Java Implementation Skeleton (Simple Joint Servo)
Java is less common in industrial robot control, but it appears in educational platforms and some middleware. Here is a minimal Java implementation of the same joint servo (without any external robotics library):
public class JointServo {
private double Kp;
private double Kd;
private double Ki;
private double tauMin;
private double tauMax;
private double integral;
public JointServo(double kp, double kd, double ki,
double tauMin, double tauMax) {
this.Kp = kp;
this.Kd = kd;
this.Ki = ki;
this.tauMin = tauMin;
this.tauMax = tauMax;
this.integral = 0.0;
}
private double gravityTorque(double q) {
double m = 1.0;
double ell = 0.5;
double g = 9.81;
return m * g * ell * Math.sin(q);
}
public double update(double q, double dq,
double qd, double dqd,
double dt) {
double e = qd - q;
double de = dqd - dq;
integral += e * dt;
double Imax = 5.0;
if (integral > Imax) integral = Imax;
if (integral < -Imax) integral = -Imax;
double tau = Kp * e + Kd * de + Ki * integral
+ gravityTorque(q);
if (tau > tauMax) tau = tauMax;
if (tau < tauMin) tau = tauMin;
return tau;
}
}
On an actual robotic system, this class would be called from a high-priority control thread that reads joint states from the hardware interface and sends torque commands to the driver.
9. MATLAB/Simulink Implementation
In MATLAB, we can implement the continuous-time dynamics and controller
using
ode45, and in Simulink using standard blocks (Sum, Gain,
Integrator). Below is a script for simulating one joint with PD +
gravity compensation:
% Parameters
J = 0.05;
b = 0.01;
m = 1.0;
ell = 0.5;
g = 9.81;
qd = deg2rad(45); % desired angle
wn = 8.0;
zeta = 0.7;
Kp = J * wn^2;
Kd = 2 * J * zeta * wn - b;
% continuous dynamics with controller
dyn = @(t, x) [
x(2); % x(1) = q, x(2) = dq
( ...
Kp * (qd - x(1)) + ...
Kd * (0 - x(2)) + ...
m * g * ell * sin(x(1)) - ...
b * x(2) - ...
m * g * ell * sin(x(1)) ...
) / J % J ddq + b dq + g(q) = tau
];
x0 = [0; 0];
tspan = [0 2];
[t, x] = ode45(dyn, tspan, x0);
q = x(:,1);
dq = x(:,2);
figure;
subplot(3,1,1);
plot(t, q); hold on;
yline(qd, "--");
ylabel("q [rad]");
legend("q", "q_d");
subplot(3,1,2);
plot(t, dq);
ylabel("dq [rad/s]");
% compute torque trajectory
tau = Kp * (qd - q) + Kd * (0 - dq) + m * g * ell * sin(q);
subplot(3,1,3);
plot(t, tau);
ylabel("tau [N m]");
xlabel("time [s]");
In Simulink, you can construct an equivalent model as follows:
- Use an integrator chain to represent the plant: \( \ddot q \rightarrow \dot q \rightarrow q \).
- Compute \( e = q_d - q \) with a Sum block and feed it to proportional and derivative Gain blocks.
- Compute the gravity torque \( g(q) = m g \ell \sin(q) \) with a Trigonometric Function block and a Gain block.
- Sum the PD output with \( g(q) \), then feed into the plant dynamics.
- Set the solver step size small enough relative to the desired bandwidth.
10. Wolfram Mathematica Implementation
Mathematica is convenient for symbolic manipulation of the joint dynamics and for simulation. The snippet below simulates the same one-DOF joint under PD + gravity:
J = 0.05;
b = 0.01;
m = 1.0;
ell = 0.5;
g = 9.81;
qd = 45 Degree;
wn = 8.0;
zeta = 0.7;
Kp = J*wn^2;
Kd = 2*J*zeta*wn - b;
grav[q_] := m*g*ell*Sin[q];
eqs = {
q'[t] == dq[t],
J*dq'[t] + b*dq[t] + grav[q[t]] ==
Kp*(qd - q[t]) + Kd*(0 - dq[t]) + grav[q[t]]
};
ics = {q[0] == 0, dq[0] == 0};
sol = NDSolve[
Join[eqs, ics],
{q, dq},
{t, 0, 2}
][[1]];
Plot[
{q[t] /. sol, qd},
{t, 0, 2},
PlotLegends -> {"q(t)", "q_d"},
AxesLabel -> {"t [s]", "q [rad]"}
]
You can modify this notebook to explore the effect of varying \(K_p, K_d\) on the transient response and to symbolically verify the characteristic polynomial and its roots.
11. Lab Procedure for Simulated and Real Arms
A recommended sequence for the lab is:
flowchart TD
A["Start with one joint in simulation"] --> B["Choose wn, zeta and compute Kp, Kd"]
B --> C["Run step tests, measure overshoot and settling time"]
C --> D["Adjust gains if necessary (respect damping and bandwidth limits)"]
D --> E["Port controller to real arm with very small gains"]
E --> F["Perform safety checks and limit small motion tests"]
F --> G["Log joint trajectories and torques for analysis"]
On a simulated arm:
- Implement the dynamics and controller as in the Python or MATLAB examples.
- Run step commands in each joint separately (while holding others fixed).
- Record overshoot, rise time, settling time, and steady-state error.
On a real arm:
- Start with very low gains (e.g., \( \omega_n \) small, high damping), verify stability.
- Increase gains gradually to approach desired performance while monitoring motor currents and joint temperatures.
- Compare measured responses with the simulated ones and discuss the influence of unmodeled dynamics (Coulomb friction, flexibility, sensor noise).
12. Problems and Solutions
Problem 1 (Gain design from desired \( \omega_n, \zeta \)). A joint has equivalent inertia \( J = 0.05 \,\mathrm{kg\,m^2} \) and viscous friction \( b = 0.01 \,\mathrm{N\,m\,s/rad} \). You wish to design a PD + gravity controller for a step in position with desired closed-loop natural frequency \( \omega_n = 10\,\mathrm{rad/s} \) and damping ratio \( \zeta = 0.7 \). Compute \( K_p \) and \( K_d \).
Solution. The error dynamics are
\[ J \ddot e + (b+K_d)\dot e + K_p e = 0. \]
Matching to the canonical second-order form \( \ddot e + 2\zeta\omega_n \dot e + \omega_n^2 e = 0 \) yields the relations
\[ K_p = J \omega_n^2, \qquad b + K_d = 2 J \zeta \omega_n. \]
Hence,
\[ K_p = 0.05 \cdot 10^2 = 0.05 \cdot 100 = 5.0, \]
and
\[ 2 J \zeta \omega_n = 2 \cdot 0.05 \cdot 0.7 \cdot 10 = 0.1 \cdot 0.7 \cdot 10 = 0.07 \cdot 10 = 0.7, \]
so
\[ K_d = 2 J \zeta \omega_n - b = 0.7 - 0.01 = 0.69. \]
Using \( K_p = 5.0 \) and \( K_d = 0.69 \) should give a response close to the desired second-order behavior, subject to modeling errors.
Problem 2 (Steady-state error with PD + perfect gravity compensation). For the one-DOF joint with PD + perfect gravity compensation as in Section 3, show that the steady-state error for a constant desired angle \( q_d \) is zero.
Solution. At steady state, assuming the closed loop is stable, we have \( \dot q = \ddot q = 0 \), so the dynamics reduce to
\[ 0 + 0 + g(q_\infty) = \tau_\infty. \]
The control law at steady state is
\[ \tau_\infty = K_p (q_d - q_\infty) + K_d (0 - 0) + g(q_\infty) = K_p (q_d - q_\infty) + g(q_\infty). \]
Equating the plant and controller torque gives \( g(q_\infty) = K_p (q_d - q_\infty) + g(q_\infty) \), hence
\[ K_p (q_d - q_\infty) = 0. \]
Since \( K_p > 0 \), it follows that \( q_\infty = q_d \), so the steady-state error is zero. This holds for any constant \( q_d \) as long as the equilibrium is within the range where the model is valid.
Problem 3 (Discrete-time PD derivative approximation). Consider the discrete-time PD law
\[ \tau[k] = K_p e[k] + K_d \frac{e[k] - e[k-1]}{T_s}. \]
Show that, for sufficiently small \(T_s\), this implementation approximates the continuous-time law \( \tau(t) = K_p e(t) + K_d \dot e(t) \).
Solution. The backward difference approximation of the derivative is
\[ \dot e(t_k) \approx \frac{e(t_k) - e(t_{k-1})}{T_s} = \frac{e[k] - e[k-1]}{T_s}, \]
which is a first-order accurate approximation: the truncation error is \( \mathcal{O}(T_s) \). Therefore
\[ \tau[k] = K_p e[k] + K_d \frac{e[k] - e[k-1]}{T_s} \approx K_p e(t_k) + K_d \dot e(t_k), \]
so as \(T_s \to 0\), the discrete implementation converges to the continuous controller. In practice, \(T_s\) must be chosen small relative to the dominant time constants of the closed-loop system.
Problem 4 (Stability conditions via Routh for one-DOF PD). For the scalar error dynamics
\[ J \ddot e + (b+K_d)\dot e + K_p e = 0, \]
use the Routh stability criterion for second-order polynomials to derive the necessary and sufficient conditions on \(K_p, K_d\) for asymptotic stability.
Solution. The characteristic polynomial is
\[ p(s) = J s^2 + (b+K_d) s + K_p. \]
For a second-order polynomial \( a_2 s^2 + a_1 s + a_0 \) with real coefficients, Routh's conditions for all roots to have negative real parts reduce to \( a_2 > 0, a_1 > 0, a_0 > 0 \). Here we have \( a_2 = J \), \( a_1 = b+K_d \), \( a_0 = K_p \), so the conditions are
\[ J > 0, \quad b + K_d > 0, \quad K_p > 0. \]
The inertia \(J\) is strictly positive by physics; hence the controller design must satisfy \( K_p > 0 \) and \( K_d > -b \). In practice one chooses \( K_d \ge 0 \), so these conditions are easily met.
Problem 5 (Decoupled multi-joint servoing). Consider an \(n\)-DOF manipulator whose inertia and damping matrices can be approximated as diagonal: \( \mathbf{M}(\mathbf{q}) \approx \mathrm{diag}(J_1,\dots,J_n) \), \( \mathbf{B} \approx \mathrm{diag}(b_1,\dots,b_n) \), so the dynamics are
\[ J_i \ddot q_i + b_i \dot q_i + g_i(\mathbf{q}) = \tau_i, \quad i = 1,\dots,n. \]
With independent PD + gravity compensation per joint, \( \tau_i = K_{p,i}(q_{d,i} - q_i) + K_{d,i}(\dot q_{d,i} - \dot q_i) + g_i(\mathbf{q}) \), show that the closed-loop error dynamics are approximately decoupled across joints.
Solution. Substituting the control law in the \(i\)-th joint equation yields
\[ J_i \ddot q_i + b_i \dot q_i + g_i(\mathbf{q}) = K_{p,i}(q_{d,i} - q_i) + K_{d,i}(\dot q_{d,i} - \dot q_i) + g_i(\mathbf{q}). \]
Gravity cancels exactly, leaving
\[ J_i \ddot q_i + b_i \dot q_i = K_{p,i}(q_{d,i} - q_i) + K_{d,i}(\dot q_{d,i} - \dot q_i). \]
Defining \( e_i = q_{d,i} - q_i \) and assuming \( q_{d,i} \) is constant, the same argument as in Section 3 yields
\[ J_i \ddot e_i + (b_i + K_{d,i}) \dot e_i + K_{p,i} e_i = 0, \]
for each joint \(i\). Since the dynamics of \(e_i\) depend only on joint \(i\)'s parameters and gains (under the diagonal approximation), the closed-loop system splits into \(n\) independent second-order subsystems. The lab experiments with one joint at a time reflect precisely this approximate decoupling; deviations from ideal decoupling in a real arm are due to off-diagonal terms neglected in the model.
13. Summary
In this lab-focused lesson, you:
- reviewed the joint servo architecture and PD/PID + gravity compensation in joint space,
- derived and analyzed the closed-loop error dynamics for one joint, including Lyapunov stability and second-order design formulas,
- implemented the joint servo in multiple programming environments (Python, C++, Java, MATLAB/Simulink, Mathematica),
- outlined a practical procedure to transition from simulation to real hardware while respecting safety and saturation constraints,
- solved analytical problems that connect theoretical design parameters to observed transient performance.
These skills form the foundation for more advanced robot control structures in subsequent chapters, where models will be used more directly inside the controller, but the basic joint servo loop you implemented here will remain a building block.
14. References
- Takegaki, M., & Arimoto, S. (1981). A new feedback method for dynamic control of manipulators. Journal of Dynamic Systems, Measurement, and Control, 103(2), 119–125.
- Spong, M. W. (1987). Modeling and control of robot manipulators: A review. Proceedings of the IEEE International Conference on Robotics and Automation, 849–856.
- Slotine, J.-J. E., & Li, W. (1987). On the adaptive control of robot manipulators. International Journal of Robotics Research, 6(3), 49–59.
- Craig, J. J. (1989). The role of joint space and operational space in manipulator control theory. International Journal of Robotics Research, 8(6), 3–15.
- Ortega, R., Loria, A., Nicklasson, P. J., & Sira-Ramirez, H. (1998). Passivity-Based Control of Euler-Lagrange Systems: Mechanical, Electrical and Electromechanical Applications. Springer (selected chapters on mechanical joint regulation).
- Kelly, R. (1996). PD control with desired gravity compensation of robotic manipulators: A review. International Journal of Robotics Research, 15(5), 527–540.
- Kelly, R., Santibáñez, V., & Loria, A. (2005). Control of Robot Manipulators in Joint Space. Springer.
- Berghuis, H., & Nijmeijer, H. (1993). A passivity approach to controller-observer design for robots. IEEE Transactions on Robotics and Automation, 9(6), 740–754.
- Canudas de Wit, C., Siciliano, B., & Bastin, G. (1996). Theory of Robot Control. Springer (chapters on joint regulation and gravity compensation).
- Slotine, J.-J. E., & Li, W. (1991). Applied Nonlinear Control. Prentice Hall (sections on robot joint regulation).