Chapter 6: Actuation and Drive Systems

Lesson 5: Series Elastic and Variable Stiffness Actuators (overview)

This lesson introduces elastic actuation, where compliant elements are intentionally added to the drivetrain to improve force control, safety, and shock tolerance. We study Series Elastic Actuators (SEA) and Variable Stiffness Actuators (VSA) through simple dynamic models, torque/stiffness mappings, and control viewpoints aligned with students’ background in linear control.

1. Motivation and Big Picture

In previous lessons, we treated actuators as “rigid” sources of motion/torque, with gears, hydraulics, or pneumatics transmitting force to a joint. Real robots, however, interact with uncertain environments and humans. Purely rigid drives tend to transmit shocks and amplify torque errors. Elastic actuation inserts compliant elements to:

  • increase impact robustness and protect gearboxes/motors;
  • enable accurate force/torque estimation via spring deflection;
  • improve safety and allow soft interaction;
  • shape the mechanical impedance seen at the output.

Two primary families are:

  • Series Elastic Actuators (SEA): a fixed spring in series with the motor/load.
  • Variable Stiffness Actuators (VSA): mechanisms that adjust stiffness online.
flowchart TD
  M["Motor + drive"] --> G["Transmission (gear, etc.)"]
  G --> S["Elastic element"]
  S --> L["Load / joint"]
  S --> E["Deflection sensor"]
  E --> TC["Torque estimate & control"]
  TC --> M
        

The diagram emphasizes a key idea: elasticity provides a measurable deflection that can be mapped to output torque. The control loop can then regulate torque (inner loop) and position or interaction behavior (outer loop).

2. Series Elastic Actuator (SEA) Modeling

Consider a single rotational joint driven by a motor through a transmission with a torsional spring in series. Let:

  • \( \theta_m \): motor-side angle,
  • \( \theta_l \): load-side (joint) angle,
  • \( N \): gear ratio (load angle equals motor angle divided by \( N \)),
  • \( k_s \): spring stiffness (N·m/rad),
  • \( J_m, J_l \): motor-side and load-side inertias,
  • \( b_m, b_l \): viscous damping coefficients,
  • \( \tau_m \): motor torque command,
  • \( \tau_{ext} \): external torque on the load (from environment).

The spring deflection is the relative twist between motor and load:

\[ \theta_s = N\theta_m - \theta_l . \]

By Hooke’s law, the transmitted torque through the series spring is

\[ \tau_s = k_s \theta_s = k_s (N\theta_m - \theta_l). \]

A minimal linear rotational model is:

\[ \begin{aligned} J_m \ddot{\theta}_m + b_m \dot{\theta}_m + N \tau_s &= \tau_m, \\ J_l \ddot{\theta}_l + b_l \dot{\theta}_l + \tau_{ext} &= \tau_s . \end{aligned} \]

Substituting \( \tau_s \) yields two coupled second-order equations. A compact state-space form is obtained by choosing the state \( \mathbf{x}=[\theta_m,\dot{\theta}_m,\theta_l,\dot{\theta}_l]^T \). The system is linear time-invariant as long as \( k_s \) is constant.

Energy interpretation. The spring stores elastic energy:

\[ \mathcal{E}_s = \tfrac{1}{2}k_s \theta_s^2 . \]

This energy buffer decouples shocks: a sudden change in \( \tau_{ext} \) produces deflection instead of an impulsive motor torque.

3. Torque Estimation and Inner-Loop Torque Control

With a deflection sensor (encoder or strain gauge), SEA torque is directly measured: \( \hat{\tau}_s = k_s \hat{\theta}_s \). This yields a clean force/torque signal compared to estimating torque from motor current through unknown friction/backlash.

A standard torque controller uses motor torque \( \tau_m \) to track a desired output torque \( \tau_d \). A simple linear PID on torque error \( e_\tau = \tau_d - \hat{\tau}_s \) is:

\[ \tau_m(t) = K_P e_\tau(t) + K_I \int_0^t e_\tau(\xi)\,d\xi + K_D \dot{e}_\tau(t). \]

Closed-loop intuition (sketch). If motor dynamics are faster than load dynamics (common with high torque motors), then the inner loop makes \( \tau_s \approx \tau_d \). Hence, from the load equation:

\[ J_l \ddot{\theta}_l + b_l \dot{\theta}_l + \tau_{ext} \approx \tau_d. \]

This “near-ideal torque source” behavior is exactly what we want for safe and accurate interaction control in later chapters.

Stability note. For the linearized inner-loop plant \( \tau_s = k_s(N\theta_m-\theta_l) \), any stabilizing PID designed using classical linear control methods applies. The spring improves robustness by adding a phase-lag-free measurement of torque (no differentiation of noisy signals).

4. Variable Stiffness Actuators (VSA)

SEA uses a fixed spring stiffness \( k_s \). VSAs extend this by allowing stiffness to change based on task demands:

  • low stiffness for safe contact or energy efficiency,
  • high stiffness for precision or fast motions.

A general VSA torque law can be written as:

\[ \tau_s = k(\sigma)\, \theta_s, \]

where \( \sigma \) is an internal adjustment variable (e.g., spring preload, lever arm, cam position) and \( k(\sigma) \) is a tunable stiffness map.

Effective stiffness. The output stiffness is:

\[ K_{eff} = \frac{d\tau_s}{d\theta_s}. \]

For \( \tau_s = k(\sigma)\theta_s \) with fixed \( \sigma \) during small perturbations,

\[ K_{eff} = k(\sigma). \]

If \( \sigma \) depends on deflection (nonlinear mechanisms), then

\[ K_{eff} = k(\sigma) + \theta_s \frac{dk}{d\sigma}\frac{d\sigma}{d\theta_s}. \]

This term explains why some VSAs become stiffer as they deflect.

flowchart TD
  A1["Spring 1 preload"] --> T["Joint torque"]
  A2["Spring 2 preload"] --> T
  U1["Motor u1 sets equilibrium"] --> T
  U2["Motor u2 sets stiffness"] --> T
  T --> L["Load angle theta_l"]
        

The diagram captures a common “antagonistic” VSA idea: one actuation channel sets the equilibrium position while another changes the co-contraction/preload, thus varying stiffness.

5. Comparison, Trade-offs, and Design Guidelines

Using the SEA/VSA models, several key trade-offs follow.

Bandwidth vs. compliance. For SEA, the torsional spring forms a resonant pair of modes. The (approximate) natural frequency is

\[ \omega_n \approx \sqrt{k_s\left(\frac{N^2}{J_m} + \frac{1}{J_l}\right)} . \]

Lower \( k_s \) improves shock tolerance and safety, but reduces torque bandwidth. Higher \( k_s \) increases precision but approaches rigid-actuator risks.

Output impedance shaping. With inner-loop torque control making \( \tau_s \approx \tau_d \), the load “sees” an actuator whose mechanical impedance can be designed via control. For a desired linear impedance \( \tau_d = K_d(\theta_r - \theta_l) - B_d\dot{\theta}_l \), we obtain a standard second-order behavior:

\[ J_l\ddot{\theta}_l + (b_l + B_d)\dot{\theta}_l + K_d \theta_l = K_d \theta_r - \tau_{ext}. \]

This uses only linear control concepts already known to students.

VSA advantage. By changing \( k(\sigma) \), a single actuator can switch between compliant and stiff behaviors without redesigning control gains.

Engineering costs. VSAs add internal mechanisms (cams, antagonistic motors, adjustable linkages), increasing mass and complexity. SEA is simpler and more common in modern robots.

6. Programming Lab — Simple SEA Simulation and Torque Control

We build a minimal SEA model and close a torque PID loop. This is not tied to any specific robot; it’s a core concept demo.

6.1 Python (NumPy/SciPy)


import numpy as np

# SEA parameters
Jm, Jl = 0.01, 0.05     # inertias
bm, bl = 0.02, 0.05     # damping
N = 50                  # gear ratio
ks = 200.0              # spring stiffness

# Torque PID gains
Kp, Ki, Kd = 5.0, 40.0, 0.02

dt = 1e-3
T  = 2.0
steps = int(T/dt)

# States: theta_m, omega_m, theta_l, omega_l
x = np.zeros(4)
tau_int = 0.0
prev_e = 0.0

def spring_torque(theta_m, theta_l):
    theta_s = N*theta_m - theta_l
    return ks*theta_s

log = []

for k in range(steps):
    t = k*dt
    tau_d = 2.0 if t > 0.2 else 0.0   # desired torque step
    tau_s = spring_torque(x[0], x[2])

    # Torque PID at motor side
    e = tau_d - tau_s
    tau_int += e*dt
    de = (e - prev_e)/dt
    prev_e = e
    tau_m = Kp*e + Ki*tau_int + Kd*de

    # Dynamics
    tau_s = spring_torque(x[0], x[2])
    theta_m, omega_m, theta_l, omega_l = x

    domega_m = (tau_m - bm*omega_m - N*tau_s) / Jm
    domega_l = (tau_s - bl*omega_l) / Jl

    # Euler integration
    omega_m += dt*domega_m
    theta_m += dt*omega_m
    omega_l += dt*domega_l
    theta_l += dt*omega_l

    x[:] = [theta_m, omega_m, theta_l, omega_l]
    log.append([t, tau_d, tau_s, theta_l])

log = np.array(log)
print("Final torque tracking error:", log[-1,1] - log[-1,2])
      

6.2 C++ (discrete SEA class)


#include <iostream>
#include <array>

struct SEA {
    double Jm, Jl, bm, bl, N, ks;
    std::array<double,4> x; // [theta_m, omega_m, theta_l, omega_l]

    SEA(double Jm_, double Jl_, double bm_, double bl_, double N_, double ks_)
        : Jm(Jm_), Jl(Jl_), bm(bm_), bl(bl_), N(N_), ks(ks_), x{0,0,0,0} {}

    double springTorque() const {
        double theta_s = N*x[0] - x[2];
        return ks*theta_s;
    }

    void step(double tau_m, double dt) {
        double tau_s = springTorque();
        double domega_m = (tau_m - bm*x[1] - N*tau_s) / Jm;
        double domega_l = (tau_s - bl*x[3]) / Jl;

        x[1] += dt*domega_m;
        x[0] += dt*x[1];
        x[3] += dt*domega_l;
        x[2] += dt*x[3];
    }
};

int main(){
    SEA sea(0.01,0.05,0.02,0.05,50.0,200.0);

    double Kp=5.0, Ki=40.0, Kd=0.02;
    double dt=1e-3, T=2.0;
    int steps = (int)(T/dt);
    double eint=0, eprev=0;

    for(int k=0;k<steps;k++){
        double t=k*dt;
        double tau_d = (t>0.2)?2.0:0.0;
        double tau_s = sea.springTorque();
        double e = tau_d - tau_s;
        eint += e*dt;
        double de = (e-eprev)/dt; eprev=e;
        double tau_m = Kp*e + Ki*eint + Kd*de;
        sea.step(tau_m, dt);
    }
    std::cout << "Final tau_s=" << sea.springTorque() << std::endl;
}
      

6.3 Java (using simple arrays)


public class SEA {
    double Jm, Jl, bm, bl, N, ks;
    // x = [theta_m, omega_m, theta_l, omega_l]
    double[] x = new double[4];

    public SEA(double Jm, double Jl, double bm, double bl, double N, double ks){
        this.Jm=Jm; this.Jl=Jl; this.bm=bm; this.bl=bl; this.N=N; this.ks=ks;
    }

    public double springTorque(){
        double theta_s = N*x[0] - x[2];
        return ks*theta_s;
    }

    public void step(double tau_m, double dt){
        double tau_s = springTorque();

        double domega_m = (tau_m - bm*x[1] - N*tau_s) / Jm;
        double domega_l = (tau_s - bl*x[3]) / Jl;

        x[1] += dt*domega_m;
        x[0] += dt*x[1];
        x[3] += dt*domega_l;
        x[2] += dt*x[3];
    }

    public static void main(String[] args){
        SEA sea = new SEA(0.01,0.05,0.02,0.05,50.0,200.0);
        double Kp=5, Ki=40, Kd=0.02;
        double dt=1e-3, T=2.0;
        int steps = (int)(T/dt);
        double eint=0, eprev=0;

        for(int k=0;k<steps;k++){
            double t=k*dt;
            double tau_d = (t>0.2)?2.0:0.0;
            double tau_s = sea.springTorque();
            double e = tau_d - tau_s;
            eint += e*dt;
            double de = (e-eprev)/dt; eprev=e;
            double tau_m = Kp*e + Ki*eint + Kd*de;
            sea.step(tau_m, dt);
        }
        System.out.println("Final torque: " + sea.springTorque());
    }
}
      

6.4 MATLAB / Simulink-ready script


% SEA linear simulation using state-space
Jm=0.01; Jl=0.05; bm=0.02; bl=0.05; N=50; ks=200;

% State x = [theta_m; omega_m; theta_l; omega_l]
A = [ 0 1 0 0;
     -N^2*ks/Jm -bm/Jm  N*ks/Jm 0;
      0 0 0 1;
      N*ks/Jl 0 -ks/Jl -bl/Jl];

B = [0; 1/Jm; 0; 0];
C_tau = [N*ks 0 -ks 0];  % tau_s = ks*(N*theta_m - theta_l)
D = 0;

sys = ss(A,B,C_tau,D);

t = 0:1e-3:2;
tau_d = 2*(t>0.2);

% Simple torque PID (discrete)
Kp=5; Ki=40; Kd=0.02;
x = zeros(4,1); eint=0; eprev=0;
tau_s_log=zeros(size(t));

for k=1:length(t)
    tau_s = C_tau*x;
    e = tau_d(k) - tau_s;
    eint = eint + e*1e-3;
    de = (e-eprev)/1e-3; eprev=e;
    tau_m = Kp*e + Ki*eint + Kd*de;

    xdot = A*x + B*tau_m;
    x = x + 1e-3*xdot;
    tau_s_log(k)=tau_s;
end

plot(t,tau_d,'--',t,tau_s_log,'LineWidth',1.2);
xlabel('Time (s)'); ylabel('Torque (N*m)'); grid on;
legend('Desired','SEA torque');
      

A Simulink model can be built directly from the state-space matrices: use a “State-Space” block for \( (A,B,C_\tau,D) \), a PID block to compute \( \tau_m \), and a step signal for \( \tau_d \).

7. Problems and Solutions

Problem 1 (SEA torque mapping): Show that if \( \theta_s = N\theta_m - \theta_l \) and the spring is linear, then power transmitted through the spring equals the time-derivative of stored energy.

Solution:

Spring energy is \( \mathcal{E}_s = \tfrac{1}{2}k_s\theta_s^2 \). Differentiate:

\[ \dot{\mathcal{E}}_s = k_s\theta_s \dot{\theta}_s = \tau_s \dot{\theta}_s . \]

The relative angular velocity across the spring is \( \dot{\theta}_s \), hence the instantaneous power through the spring is \( P_s=\tau_s\dot{\theta}_s \), which equals \( \dot{\mathcal{E}}_s \). This confirms the spring acts as an energy buffer.

Problem 2 (SEA resonance): Assume \( b_m=b_l=0 \) and \( \tau_{ext}=0 \). Derive the natural frequency of small oscillations for the SEA model.

Solution:

With zero damping and no external torque,

\[ \begin{aligned} J_m \ddot{\theta}_m + N\tau_s &= 0,\\ J_l \ddot{\theta}_l - \tau_s &= 0,\\ \tau_s &= k_s(N\theta_m - \theta_l). \end{aligned} \]

Differentiate \( \theta_s=N\theta_m-\theta_l \) twice: \( \ddot{\theta}_s = N\ddot{\theta}_m-\ddot{\theta}_l \). Substitute from the first two equations:

\[ \ddot{\theta}_m = -\frac{N}{J_m}\tau_s,\qquad \ddot{\theta}_l = \frac{1}{J_l}\tau_s. \]

Thus

\[ \ddot{\theta}_s = N\left(-\frac{N}{J_m}\tau_s\right) - \frac{1}{J_l}\tau_s = -\left(\frac{N^2}{J_m}+\frac{1}{J_l}\right)\tau_s . \]

Using \( \tau_s = k_s \theta_s \):

\[ \ddot{\theta}_s + k_s\left(\frac{N^2}{J_m}+\frac{1}{J_l}\right)\theta_s = 0. \]

This is a simple harmonic oscillator with \( \omega_n = \sqrt{k_s\left(\frac{N^2}{J_m}+\frac{1}{J_l}\right)} \).

Problem 3 (VSA effective stiffness): For a VSA with \( \tau_s = k(\sigma)\theta_s \), compute \( K_{eff} \) if \( \sigma = c\,\theta_s \) and \( k(\sigma)=k_0 + k_1\sigma \).

Solution:

Substitute \( \sigma=c\theta_s \): \( k(\sigma)=k_0+k_1 c\theta_s \). Then

\[ \tau_s = (k_0+k_1 c\theta_s)\theta_s = k_0\theta_s + k_1 c\theta_s^2. \]

Therefore

\[ K_{eff}=\frac{d\tau_s}{d\theta_s}=k_0 + 2k_1 c\theta_s. \]

The stiffness increases linearly with deflection, a typical nonlinear VSA behavior.

Problem 4 (Torque PID robustness): Suppose torque measurement is corrupted by additive noise \( n(t) \), so \( \hat{\tau}_s=\tau_s + n(t) \). Explain why inner-loop torque control with SEA is often more robust than estimating torque from motor current.

Solution:

SEA measures torque via spring deflection: \( \hat{\tau}_s = k_s\hat{\theta}_s \). The noise enters additively at the measurement and can be filtered without affecting the physical spring law. By contrast, current-based torque estimation requires: \( \tau \approx K_t i - \tau_{fric} \), where \( \tau_{fric} \) is uncertain and nonlinear (backlash, stiction). Thus SEA converts torque estimation into a direct mechanical measurement, reducing model uncertainty; linear filtering and PID design from linear control remain valid.

8. Summary

Series Elastic Actuators add a fixed compliance in series, enabling direct torque measurement \( \tau_s=k_s(N\theta_m-\theta_l) \), shock buffering, and reliable inner-loop torque control. Variable Stiffness Actuators generalize this by making stiffness tunable \( \tau_s=k(\sigma)\theta_s \), allowing a robot to switch between soft and stiff behaviors as tasks require. These actuation paradigms are foundations for safe interaction and impedance shaping in later robotics courses.

9. References

  1. Pratt, G.A., & Williamson, M.M. (1995). Series elastic actuators. Proceedings of the IEEE/RSJ International Conference on Intelligent Robots and Systems, 399–406.
  2. Robinson, D.W. (2000). Design and analysis of series elasticity in closed-loop actuator force control. Ph.D. Thesis, MIT.
  3. Hogan, N. (1985). Impedance control: An approach to manipulation. ASME Journal of Dynamic Systems, Measurement, and Control, 107(1), 1–24.
  4. Bicchi, A., & Tonietti, G. (2004). Fast and “soft-arm” tactics: Dealing with the safety–performance trade-off in robot arms design and control. IEEE Robotics & Automation Magazine, 11(2), 22–33.
  5. Vanderborght, B., et al. (2013). Variable impedance actuators: A review. Robotics and Autonomous Systems, 61(12), 1601–1614.
  6. Albu-Schäffer, A., Haddadin, S., Ott, C., Stemmer, A., Wimböck, T., & Hirzinger, G. (2007). The DLR lightweight robot: Design and control concepts for robots in human environments. Industrial Robot, 34(5), 376–385.
  7. Maksimovic, F., & Grioli, G. (2019). On the passivity and stability of variable stiffness actuators. IEEE Transactions on Robotics, 35(6), 1460–1474.