Back to Blog

Sliding Mode Control for Robot Arm Joints: A Worked Numeric Example

Sizing the sliding surface, the switching gain, and the boundary layer for a real joint, with actual numbers instead of a Lyapunov proof

Sliding mode control (SMC) shows up in robotics papers as a Lyapunov stability proof and a block diagram with a sign function in it, and then it disappears. Most tutorials stop at "the control law drives the system onto a sliding surface and keeps it there" without ever computing a sliding surface, a switching gain, or a boundary layer width for an actual joint. This article does that: one revolute joint, real inertia and friction numbers, and the arithmetic that turns the abstract theory into a controller you could actually code.

Why sliding mode control for a robot joint

A PID loop tunes gains around a linear model of the joint. If the load inertia changes (the arm picks up a payload, or extends to a different configuration), or friction is only approximately known, a fixed-gain PID controller can lose tracking accuracy. Sliding mode control is attractive here because it is a form of robust control: instead of relying on an exact model, it forces the tracking error onto a surface where the error dynamics are fixed by design, and it stays there even when the model is wrong by a bounded amount. That robustness guarantee is the entire point of sliding mode control robot arm applications, and it is also why the controller looks intimidating: the robustness comes from a discontinuous switching term, and that switching term is exactly what causes chattering.

Step 1: the joint model

Take a single revolute joint driven through a gearbox, modeled at the motor/output shaft as a second-order system:

J * theta_ddot + b * theta_dot = tau - tau_d

where:

The nominal inertia used by the controller is J_hat = 0.04 kg*m^2 and the nominal friction is b_hat = 0.018 N*m*s/rad. The 12.5% inertia error and unknown disturbance are exactly the kind of model mismatch SMC is meant to survive.

Step 2: define the sliding surface

Let e = theta - theta_d be the tracking error (actual minus desired joint angle). The sliding surface for a second-order system is:

s = e_dot + lambda * e

lambda is a positive constant that sets how fast the error collapses once the system is on the surface. On the surface (s = 0), the error dynamics become e_dot = -lambda * e, a stable first-order decay with time constant 1/lambda. Pick lambda based on how fast you want tracking error to die out. For a joint that should recover from a disturbance in about 0.2 seconds, use the 1/e settling time relationship:

lambda = 1 / 0.2 = 5 rad/s

So s = e_dot + 5*e. This is the actual number you compute every control cycle from the encoder-derived position and velocity error.

Step 3: derive the control law

Differentiate s and substitute the joint dynamics to get s_dot in terms of tau:

s_dot = e_ddot + lambda * e_dot = (theta_ddot - theta_d_ddot) + lambda * e_dot

Using theta_ddot = (tau - tau_d - b*theta_dot) / J, the control law that drives s_dot toward zero has two parts: an equivalent control term (the nominal model-based feedforward that would hold s_dot = 0 with no disturbance) and a switching term that rejects the disturbance:

tau = J_hat * (theta_d_ddot - lambda*e_dot + b_hat/J_hat * theta_dot) - K * sign(s)

The first bracketed term is tau_eq, the equivalent control. K is the switching gain, and sign(s) is +1 when s > 0 and -1 when s < 0. This discontinuous term is what makes the controller robust to the 0.8 N*m disturbance and the 12.5% inertia error the nominal model does not capture, and it is also what makes the joint buzz if you size K carelessly.

Step 4: size the switching gain K

K has to be large enough to overcome the worst-case combination of unmodeled disturbance and model mismatch, or the surface is never actually reached. The standard sizing condition (from the reaching condition s * s_dot <= -eta * |s| for some eta > 0) reduces to:

K >= (max disturbance torque effect) + (inertia mismatch effect) + eta

Plugging in numbers: the disturbance torque of 0.8 N*m maps directly since it enters s_dot divided by J_hat, so its contribution is 0.8 / 0.04 = 20 rad/s^2 equivalent. The inertia mismatch (J = 0.045 vs J_hat = 0.04, an 11% error) contributes roughly |1 - J_hat/J| * |theta_d_ddot_max|, and for a trajectory with a peak commanded acceleration of 6 rad/s^2 that is 0.11 * 6 = 0.66 rad/s^2. Choosing eta = 2 rad/s^2 as a margin for a reasonably fast reaching phase:

K_required (in rad/s^2 units) = 20 + 0.66 + 2 = 22.66

Converting back to a torque-domain switching gain by multiplying by J_hat: K = 22.66 * 0.04 = 0.906 N*m. Round up for margin: K = 1.0 N*m. That is the actual number that goes into the -K*sign(s) term. Undersize K and the trajectory never truly reaches the surface under the full 0.8 N*m disturbance; oversize it far beyond this and you get unnecessary chattering amplitude for no robustness benefit.

Step 5: kill the chattering with a boundary layer

With K = 1.0 N*m and a true discontinuous sign(s), the motor torque command switches sign every time s crosses zero. In a real system with any actuator or sensor time delay, this produces high-frequency torque chatter, which is audible, wastes power, and fatigues gearboxes. The standard fix is to replace sign(s) with a saturation function inside a boundary layer of width phi:

sat(s/phi) = s/phi, if |s| <= phi; sign(s), if |s| > phi

Inside the boundary layer the control becomes continuous (effectively a high-gain linear term), and outside it the full switching authority still applies. Sizing phi trades off chattering amplitude against steady-state tracking error: a wider boundary layer smooths the torque command but lets the tracking error settle to a small nonzero band instead of exactly zero.

A practical starting point is to size phi so the resulting steady-state position error stays under your encoder resolution. If the boundary layer replaces exact sliding with a first-order error bound of roughly phi/lambda, and the joint has an effective resolution of 0.001 rad after gearing, set:

phi = lambda * 0.001 = 5 * 0.001 = 0.005

With phi = 0.005, s values inside +/-0.005 use the linear ramp (torque scales smoothly to zero at s = 0), and anything outside that band still gets the full K = 1.0 N*m switching torque. In practice this single change, sat(s/phi) instead of sign(s), is the difference between a joint that hums quietly and one that audibly buzzes at the actuator's control loop rate.

Putting it together

The full controller computed every cycle is:

tau = J_hat*(theta_d_ddot - lambda*e_dot) + b_hat*theta_dot - K*sat(s/phi), with s = e_dot + lambda*e

Using the numbers above: J_hat = 0.04, b_hat = 0.018, lambda = 5, K = 1.0, phi = 0.005. If at some instant e = 0.02 rad and e_dot = -0.03 rad/s, then s = -0.03 + 5*0.02 = 0.07, which is well outside the boundary layer, so sat(s/phi) = sign(s) = 1 and the full switching torque of -1.0 N*m is added to the feedforward term. As the joint converges and e, e_dot shrink toward zero, s eventually falls inside +/-0.005 and the switching term smoothly tapers to zero.

This is the same overall structure as computed torque control: a model-based feedforward term plus a correction term. The difference is that computed torque control relies entirely on an accurate model, while sliding mode control adds the K*sat(s/phi) term specifically to absorb the part of the model that is wrong or unpredictable, which is why it tolerates the kind of friction uncertainty discussed in the Coulomb and viscous friction compensation article without needing the friction coefficients to be exact.

When to reach for this over PID or computed torque

The tradeoff is real: sliding mode control needs a bound on the disturbance and model error to size K correctly. If you underestimate that bound, the controller will not actually reach the sliding surface and you lose the robustness guarantee that was the entire reason to use it.

Building or studying robotics?

Arcbotix publishes practical guides on robotics engineering, control systems and real-world builds. Explore more articles on the blog.