Chapter 11: PID Control Basics
Lesson 5: Classical Heuristic PID Tuning (e.g., Ziegler–Nichols)
This lesson presents classical heuristic rules for tuning PID controllers, with emphasis on the Ziegler–Nichols methods. We connect these rules to the closed-loop dynamics of unity-feedback systems, explain the quarter-amplitude damping concept, and provide implementations in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica with remarks on their use in robotic control software.
1. Conceptual Overview of Heuristic PID Tuning
In previous lessons, PID control has been introduced as a ubiquitous feedback strategy. The remaining practical question is: how do we choose the gains \( K_p \), \( K_i \), \( K_d \)?
Classical heuristic tuning rules use simple experiments on the real plant (or a high-fidelity simulator) rather than explicit analytical models. The two most famous sets of rules are those proposed by Ziegler and Nichols (1942):
- Closed-loop (ultimate gain) method: increase a proportional gain until sustained oscillations appear; the resulting ultimate gain and ultimate period are plugged into algebraic formulas for PID gains.
- Open-loop (process reaction curve) method: approximate the plant by a first-order plus dead-time (FOPDT) model using a step response; again, algebraic formulas map the model parameters to PID gains.
Consider a unity-feedback loop with plant transfer function \( G(s) \) and PID controller \( C(s) \). The closed-loop transfer function from reference \( R(s) \) to output \( Y(s) \) is
\[ T(s) \;=\; \frac{Y(s)}{R(s)} \;=\; \frac{C(s)\,G(s)}{1 + C(s)\,G(s)}. \]
Heuristic tuning rules implicitly shape the denominator \( 1 + C(s)\,G(s) \) so that the time response exhibits a desired “typical” behavior (for example, overshoot and damping), without solving for the poles analytically.
2. PID Controller Model in Time and Laplace Domains
We work with the parallel (or “standard”) PID structure. With control error \( e(t) = r(t) - y(t) \), the control input \( u(t) \) is
\[ u(t) \;=\; K_p\,e(t) \;+\; K_i \int_0^t e(\xi)\,d\xi \;+\; K_d\,\frac{d}{dt}e(t). \]
Taking Laplace transforms (all signals assumed to be zero for \( t < 0 \)) gives
\[ U(s) \;=\; \left( K_p \;+\; \frac{K_i}{s} \;+\; K_d s \right) E(s) \;\equiv\; C(s)\,E(s), \]
where
\[ C(s) \;=\; K_p \;+\; \frac{K_i}{s} \;+\; K_d s. \]
In many tuning rules it is convenient to parameterize the controller using a proportional gain \( K_p \), an integral time \( T_i \), and a derivative time \( T_d \):
\[ C(s) \;=\; K_p \left( 1 \;+\; \frac{1}{T_i s} \;+\; T_d s \right). \]
Comparing the two forms,
\[ K_i \;=\; \frac{K_p}{T_i}, \qquad K_d \;=\; K_p\,T_d. \]
Ziegler–Nichols formulas are traditionally expressed in terms of \( K_p \), \( T_i \), \( T_d \); we will later convert them to \( K_i, K_d \) where needed for implementation.
For illustration, consider the common first-order plant
\[ G_p(s) \;=\; \frac{K}{(\tau s + 1)}, \]
with static gain \( K \) and time constant \( \tau \). Using the PID controller above, the closed-loop characteristic equation (unity feedback) is obtained by clearing denominators in
\[ 1 \;+\; C(s)G_p(s) \;=\; 1 \;+\; \frac{K\left( K_p + \frac{K_i}{s} + K_d s \right)}{(\tau s + 1)} \;=\; 0. \]
After multiplying by \( s(\tau s + 1) \), one obtains
\[ (\tau s + 1)s \;+\; K\left(K_d s^2 + K_p s + K_i\right) \;=\; 0, \]
which is a third-order polynomial in \( s \). Exact analytical tuning (choosing \( K_p, K_i, K_d \) to place the poles) is possible but cumbersome for higher-order or time-delay plants. Heuristic rules bypass this by directly encoding “good” pole locations in simple tuning formulas.
3. Ziegler–Nichols Closed-Loop (Ultimate Gain) Method
The closed-loop method is performed directly on the feedback system. The procedure is as follows:
- Configure the controller as P-only: set \( K_i = 0 \), \( K_d = 0 \).
- Apply a step reference and increase \( K_p \) from a low value until the closed-loop output exhibits sustained oscillations (neither decaying nor growing, approximately).
- Record the value of \( K_p \) at this condition as the ultimate gain \( K_u \).
- Measure the period of the sustained oscillation, the ultimate period \( T_u \).
- Compute the PID parameters from \( K_u, T_u \) using the Ziegler–Nichols formulas.
flowchart TD
A["Start with P-only controller"] --> B["Increase Kp until sustained oscillation"]
B --> C["Record Ku (ultimate gain) and Tu (ultimate period)"]
C --> D["Choose mode: P / PI / PID"]
D --> E["Compute Kp, Ti, Td from ZN table"]
E --> F["Convert to Kp, Ki, Kd and test"]
The Ziegler–Nichols recommended settings (closed-loop method) in terms of \( K_p, T_i, T_d \) are
\[ \begin{array}{c|c|c|c} \text{Controller} & K_p & T_i & T_d \\ \hline \text{P} & 0.50\,K_u & \infty & 0 \\ \text{PI} & 0.45\,K_u & \dfrac{T_u}{1.2} & 0 \\ \text{PID} & 0.60\,K_u & \dfrac{T_u}{2} & \dfrac{T_u}{8} \end{array} \]
For implementation in the parallel form \( C(s) = K_p + K_i/s + K_d s \), we use the relations
\[ K_i \;=\; \frac{K_p}{T_i}, \qquad K_d \;=\; K_p T_d. \]
The method assumes that for some finite \( K_u \) the closed-loop system exhibits a stable limit cycle. This is common for higher-order or time-delay plants but does not occur for simple first-order plants without delay (we will analyze this in a later section).
4. Ziegler–Nichols Open-Loop (Process Reaction Curve) Method
The second Ziegler–Nichols method uses an open-loop step response of the plant. The key idea is to approximate the plant by a first-order plus dead-time (FOPDT) model:
\[ G_p(s) \;\approx\; \frac{K}{(\tau s + 1)}\,e^{-Ls}, \]
where
- \( K \) is the static gain.
- \( L \) is an effective pure delay (dead time).
- \( \tau \) is an effective time constant.
From an open-loop step test of amplitude \( \Delta u \), we obtain the output change \( \Delta y \) and estimate
\[ K \;\approx\; \frac{\Delta y}{\Delta u}. \]
The parameters \( L \) and \( \tau \) are obtained from the process reaction curve (the S-shaped step response) by drawing a tangent at its inflection point and identifying the intercepts with the time axis.
flowchart TD
S["Apply open-loop step input"] --> R["Record process reaction curve y(t)"]
R --> ID["Identify K, L, tau from curve"]
ID --> TBL["Select P / PI / PID ZN open-loop formula"]
TBL --> GNS["Compute Kp, Ti, Td; then Ki, Kd"]
The Ziegler–Nichols open-loop formulas (for the FOPDT model above) are
\[ \begin{array}{c|c|c|c} \text{Controller} & K_p & T_i & T_d \\ \hline \text{P} & \dfrac{\tau}{K L} & \infty & 0 \\ \text{PI} & 0.9\,\dfrac{\tau}{K L} & 3L & 0 \\ \text{PID} & 1.2\,\dfrac{\tau}{K L} & 2L & 0.5L \end{array} \]
Again, for implementation we convert
\[ K_i \;=\; \frac{K_p}{T_i}, \qquad K_d \;=\; K_p T_d. \]
These formulas were obtained by Ziegler and Nichols using extensive simulations and experiments, aiming at a “standard” closed-loop response with about quarter-amplitude damping (successive peaks reduced to approximately one quarter of the previous one).
5. Quarter-Amplitude Damping and Damping Ratio
In Chapter 6, the underdamped second-order closed-loop model was studied:
\[ T(s) \;=\; \frac{\omega_n^2}{s^2 + 2\zeta\omega_n s + \omega_n^2}, \]
where \( \zeta \) is the damping ratio and \( \omega_n \) is the natural frequency. For a unit step input, the response has decaying oscillations with peak amplitudes
\[ y_k \;=\; 1 + A\,\exp\!\left( -\zeta\omega_n t_k \right), \]
at times
\[ t_k \;=\; \frac{\pi}{\omega_d}(2k+1), \qquad \omega_d \;=\; \omega_n\sqrt{1 - \zeta^2}, \quad k = 0,1,2,\dots \]
The ratio of successive peak deviations from the final value (the amplitude ratio) is
\[ R \;=\; \frac{y_{k+1} - 1}{y_k - 1} \;=\; \exp\!\left( -\frac{2\pi\zeta}{\sqrt{1 - \zeta^2}} \right). \]
Quarter-amplitude damping corresponds to \( R = 0.25 \), so we solve
\[ \exp\!\left( -\frac{2\pi\zeta}{\sqrt{1 - \zeta^2}} \right) \;=\; 0.25. \]
Taking natural logarithms on both sides gives the implicit equation
\[ -\frac{2\pi\zeta}{\sqrt{1 - \zeta^2}} \;=\; \ln(0.25). \]
Numerical solution yields approximately
\[ \zeta \;\approx\; 0.215. \]
Thus, the Ziegler–Nichols rules approximately place the dominant closed-loop poles so that the effective damping ratio is about \( 0.2 \), leading to relatively fast but oscillatory responses with substantial overshoot. This is acceptable in some applications but too aggressive in many precision motion or robotic tasks, where further fine-tuning or more conservative rules are often used.
6. Theoretical Limitations and Modifications of ZN Tuning
Ziegler–Nichols rules are heuristic, not optimal in any strict mathematical sense. Some theoretical observations:
- Dependence on plant order and delay. For a simple first-order plant without delay, e.g. \( G_p(s) = \dfrac{K}{(\tau s + 1)} \), a proportional controller with gain \( K_p > 0 \) yields the characteristic equation \( \tau s + 1 + K K_p = 0 \), whose unique root always lies in the left half-plane. In such a case, there is no finite \( K_u \) that produces sustained oscillations; the closed-loop always remains stable and the ZN closed-loop method is not applicable.
- High overshoot and actuator stress. The quarter-amplitude design objective corresponds to relatively small damping ratio and can induce large overshoot and control effort, which may saturate actuators.
- Sensitivity to measurement noise. The derivative term \( K_d s \) amplifies high-frequency noise; ZN rules for \( T_d \) may be too aggressive when sensor noise is significant. Later lessons (Chapter 12) will discuss filtered derivative action.
A simple way to obtain more conservative settings is to introduce a scaling factor \( \alpha \in (0,1) \) and use
\[ K_p^{\text{new}} \;=\; \alpha\,K_p^{\text{ZN}}, \qquad K_i^{\text{new}} \;=\; \alpha\,K_i^{\text{ZN}}, \qquad K_d^{\text{new}} \;=\; \alpha\,K_d^{\text{ZN}}, \]
where the superscript “ZN” denotes the original Ziegler–Nichols gains. This preserves the ratios among \( K_p, K_i, K_d \), hence approximate shape of the response, while increasing damping and reducing overshoot.
7. Python Implementation for ZN PID Tuning (Robotics-Oriented)
We implement a small Python utility that converts Ziegler–Nichols parameters into a discrete-time PID controller suitable for running in a real-time loop (for example, a robot joint controller implemented in a ROS node). The code uses the parallel form and a simple rectangular rule for integration and backward difference for differentiation.
from dataclasses import dataclass
from enum import Enum
import math
class ZNMode(str, Enum):
P = "P"
PI = "PI"
PID = "PID"
def zn_closed_loop(Ku: float, Tu: float, mode: ZNMode = ZNMode.PID):
"""
Ziegler-Nichols closed-loop tuning.
Returns (Kp, Ki, Kd) for the parallel PID form.
"""
if mode == ZNMode.P:
Kp = 0.5 * Ku
Ti = math.inf
Td = 0.0
elif mode == ZNMode.PI:
Kp = 0.45 * Ku
Ti = Tu / 1.2
Td = 0.0
else: # PID
Kp = 0.60 * Ku
Ti = Tu / 2.0
Td = Tu / 8.0
if math.isinf(Ti):
Ki = 0.0
else:
Ki = Kp / Ti
Kd = Kp * Td
return Kp, Ki, Kd
@dataclass
class PIDController:
Kp: float
Ki: float
Kd: float
dt: float # sampling period
u_min: float = -float("inf")
u_max: float = float("inf")
# internal state
integral: float = 0.0
prev_error: float = 0.0
def reset(self):
self.integral = 0.0
self.prev_error = 0.0
def step(self, error: float) -> float:
"""
One control step. For a robot joint, 'error' can be the difference
between desired and measured joint position.
"""
# Integrate error
self.integral += error * self.dt
# Derivative of error
derivative = (error - self.prev_error) / self.dt
# PID law
u = self.Kp * error + self.Ki * self.integral + self.Kd * derivative
# Saturation with simple integral anti-windup
if u > self.u_max:
u = self.u_max
# anti-windup: do not integrate further when saturated high
self.integral -= error * self.dt
elif u < self.u_min:
u = self.u_min
# anti-windup: do not integrate further when saturated low
self.integral -= error * self.dt
self.prev_error = error
return u
# Example usage:
if __name__ == "__main__":
Ku = 6.0 # measured ultimate gain
Tu = 1.2 # measured ultimate period (seconds)
Kp, Ki, Kd = zn_closed_loop(Ku, Tu, ZNMode.PID)
print("ZN PID gains:", Kp, Ki, Kd)
pid = PIDController(Kp=Kp, Ki=Ki, Kd=Kd, dt=0.001,
u_min=-10.0, u_max=10.0)
# In a robotics control loop (e.g., with ROS),
# pid.step(error) would be called every dt seconds.
In a robotics context, such a PID can be integrated into a Python-based
control node (for example, a ROS2 node using rclpy) or used
to prototype tuning before porting the gains to a C++
ros_control controller. The Ziegler–Nichols rules
provide initial guesses that can be refined experimentally.
8. C++ Implementation and Robotics Libraries
Many robotics middlewares (e.g. ROS ros_control) implement
PID controllers in C++. Below is a minimal C++ class that accepts
Ziegler–Nichols closed-loop parameters and can be used in a
low-level joint controller.
#include <algorithm>
#include <limits>
enum class ZNMode { P, PI, PID };
struct ZNParams {
double Kp;
double Ki;
double Kd;
};
inline ZNParams znClosedLoop(double Ku, double Tu, ZNMode mode) {
double Kp, Ti, Td;
if (mode == ZNMode::P) {
Kp = 0.5 * Ku;
Ti = std::numeric_limits<double>::infinity();
Td = 0.0;
} else if (mode == ZNMode::PI) {
Kp = 0.45 * Ku;
Ti = Tu / 1.2;
Td = 0.0;
} else { // PID
Kp = 0.60 * Ku;
Ti = Tu / 2.0;
Td = Tu / 8.0;
}
double Ki = std::isinf(Ti) ? 0.0 : Kp / Ti;
double Kd = Kp * Td;
return {Kp, Ki, Kd};
}
class PID {
public:
PID(double Kp, double Ki, double Kd, double dt,
double u_min = -std::numeric_limits<double>::infinity(),
double u_max = std::numeric_limits<double>::infinity())
: Kp_(Kp), Ki_(Ki), Kd_(Kd), dt_(dt),
u_min_(u_min), u_max_(u_max),
integral_(0.0), prev_error_(0.0) {}
void reset() {
integral_ = 0.0;
prev_error_ = 0.0;
}
double step(double error) {
integral_ += error * dt_;
double derivative = (error - prev_error_) / dt_;
double u = Kp_ * error + Ki_ * integral_ + Kd_ * derivative;
if (u > u_max_) {
u = u_max_;
integral_ -= error * dt_;
} else if (u < u_min_) {
u = u_min_;
integral_ -= error * dt_;
}
prev_error_ = error;
return u;
}
private:
double Kp_, Ki_, Kd_;
double dt_;
double u_min_, u_max_;
double integral_;
double prev_error_;
};
// Example usage in a joint position controller:
// ZNParams p = znClosedLoop(Ku, Tu, ZNMode::PID);
// PID pid(p.Kp, p.Ki, p.Kd, dt, -10.0, 10.0);
// double torque = pid.step(position_error);
In ROS, similar logic is implemented by the
control_toolbox::Pid
class. Ziegler–Nichols formulas can be used offline to compute
gains, which are then placed into a YAML configuration file loaded by
ros_control or ros2_control for robot joint
controllers.
9. Java Implementation and Robotics Context
Java is widely used in educational and industrial robotics, notably with the WPILib framework for mobile robots. The following example shows a simple PID controller tuned via Ziegler–Nichols, which could be used for, e.g., a wheeled robot velocity loop.
public enum ZNMode {
P, PI, PID
}
public final class ZNTuning {
public static double[] znClosedLoop(double Ku, double Tu, ZNMode mode) {
double Kp, Ti, Td;
switch (mode) {
case P:
Kp = 0.5 * Ku;
Ti = Double.POSITIVE_INFINITY;
Td = 0.0;
break;
case PI:
Kp = 0.45 * Ku;
Ti = Tu / 1.2;
Td = 0.0;
break;
default: // PID
Kp = 0.60 * Ku;
Ti = Tu / 2.0;
Td = Tu / 8.0;
break;
}
double Ki = Double.isInfinite(Ti) ? 0.0 : Kp / Ti;
double Kd = Kp * Td;
return new double[]{Kp, Ki, Kd};
}
}
public class PIDControllerZN {
private final double Kp;
private final double Ki;
private final double Kd;
private final double dt;
private final double uMin;
private final double uMax;
private double integral = 0.0;
private double prevError = 0.0;
public PIDControllerZN(double Kp, double Ki, double Kd,
double dt, double uMin, double uMax) {
this.Kp = Kp;
this.Ki = Ki;
this.Kd = Kd;
this.dt = dt;
this.uMin = uMin;
this.uMax = uMax;
}
public void reset() {
integral = 0.0;
prevError = 0.0;
}
public double step(double error) {
integral += error * dt;
double derivative = (error - prevError) / dt;
double u = Kp * error + Ki * integral + Kd * derivative;
if (u > uMax) {
u = uMax;
integral -= error * dt;
} else if (u < uMin) {
u = uMin;
integral -= error * dt;
}
prevError = error;
return u;
}
}
// Example:
// double[] gains = ZNTuning.znClosedLoop(6.0, 1.2, ZNMode.PID);
// PIDControllerZN pid = new PIDControllerZN(gains[0], gains[1], gains[2],
// 0.02, -12.0, 12.0);
// double u = pid.step(setpoint - measuredVelocity);
In WPILib, one could similarly compute Ziegler–Nichols gains and
pass them to the built-in
edu.wpi.first.math.controller.PIDController
for robot drive or arm control.
10. MATLAB/Simulink and Wolfram Mathematica Implementations
10.1 MATLAB Script and Simulink Integration
The MATLAB script below computes Ziegler–Nichols closed-loop PID gains and simulates the step response of a first-order plant, which could represent, for example, a robot joint driven by a DC motor.
% Ziegler-Nichols closed-loop PID tuning in MATLAB
Ku = 6.0; % ultimate gain (measured experimentally)
Tu = 1.2; % ultimate period (seconds)
% PID controller parameters (closed-loop ZN)
Kp = 0.60 * Ku;
Ti = Tu / 2.0;
Td = Tu / 8.0;
Ki = Kp / Ti;
Kd = Kp * Td;
fprintf('Kp = %.3f, Ki = %.3f, Kd = %.3f\n', Kp, Ki, Kd);
% Example plant: DC motor-like first-order model
Kplant = 1.5;
tau = 0.4;
s = tf('s');
Gp = Kplant / (tau * s + 1);
C = Kp + Ki / s + Kd * s; % PID in parallel form
Tcl = feedback(C*Gp, 1); % unity-feedback closed loop
figure;
step(Tcl);
grid on;
title('Ziegler-Nichols PID step response');
% Simulink:
% - Use a "PID Controller" block in parallel form.
% - Enter Kp, Ki, Kd computed above.
% - Connect plant model Gp (e.g., using a Transfer Fcn block).
In robotic applications using MATLAB Robotics System Toolbox, the same
gains can be attached to joint controllers acting on a
rigidBodyTree model, or exported to embedded hardware.
10.2 Wolfram Mathematica Example
Wolfram Mathematica offers symbolic and numeric tools for control design. The following example computes Ziegler–Nichols PID gains and simulates the closed-loop response of a simple plant:
(* Ziegler-Nichols closed-loop PID tuning in Mathematica *)
Ku = 6.0;
Tu = 1.2;
Kp = 0.60 Ku;
Ti = Tu/2.0;
Td = Tu/8.0;
Ki = Kp/Ti;
Kd = Kp Td;
Print["Kp = ", Kp, ", Ki = ", Ki, ", Kd = ", Kd];
s = LaplaceTransformVariable;
(* First-order plant model *)
Kplant = 1.5;
tau = 0.4;
Gp[s_] := Kplant/(tau s + 1);
C[s_] := Kp + Ki/s + Kd s;
Tcl[s_] := (C[s] Gp[s])/(1 + C[s] Gp[s]);
(* Step response in time domain *)
TclTime[t_] = InverseLaplaceTransform[Tcl[s]/s, s, t];
Plot[TclTime[t], {t, 0, 5},
AxesLabel -> {"t", "y(t)"},
PlotRange -> All,
GridLines -> Automatic,
PlotLabel -> "ZN PID Step Response"];
Mathematica and Wolfram SystemModeler also include multibody and robotics libraries; Ziegler–Nichols tuning can be used to obtain initial PID gains for joint actuators in such models.
11. Problems and Solutions
Problem 1 (Amplitude Ratio in a Second-Order System): Consider the underdamped second-order closed-loop transfer function
\[ T(s) \;=\; \frac{\omega_n^2}{s^2 + 2\zeta\omega_n s + \omega_n^2}. \]
For a unit step input, derive the expression for the ratio \( R \) between successive peak deviations from the steady-state value and show that
\[ R \;=\; \exp\!\left( -\frac{2\pi\zeta}{\sqrt{1 - \zeta^2}} \right). \]
Solution:
The step response of the underdamped second-order system is
\[ y(t) \;=\; 1 - \frac{1}{\sqrt{1 - \zeta^2}} \exp\!\big(-\zeta\omega_n t\big) \sin\!\left( \omega_d t + \phi \right), \]
where \( \omega_d = \omega_n\sqrt{1 - \zeta^2} \) and \( \phi = \arctan\left( \dfrac{\sqrt{1 - \zeta^2}}{\zeta} \right) \). The peaks occur at times
\[ t_k \;=\; \frac{\pi}{\omega_d}(2k + 1),\quad k = 0,1,2,\dots \]
The deviation from steady state at peak \( k \) is proportional to \( \exp(-\zeta\omega_n t_k) \). Thus
\[ R \;=\; \frac{\exp(-\zeta\omega_n t_{k+1})} {\exp(-\zeta\omega_n t_k)} \;=\; \exp\big(-\zeta\omega_n (t_{k+1} - t_k)\big). \]
Using \( t_{k+1} - t_k = \dfrac{2\pi}{\omega_d} \) and \( \omega_d = \omega_n\sqrt{1 - \zeta^2} \), we obtain
\[ R \;=\; \exp\!\left( -\frac{2\pi\zeta}{\sqrt{1 - \zeta^2}} \right), \]
which proves the desired expression.
Problem 2 (ZN Open-Loop Tuning for an FOPDT Plant): A plant is approximated by the FOPDT model
\[ G_p(s) \;\approx\; \frac{K}{(\tau s + 1)}e^{-Ls} \]
with \( K = 2 \), \( L = 0.5 \) s, \( \tau = 1.5 \) s. Compute Ziegler–Nichols open-loop PID parameters \( K_p, K_i, K_d \).
Solution:
For the PID controller, the open-loop ZN formulas give
\[ K_p \;=\; 1.2\,\frac{\tau}{K L}, \qquad T_i \;=\; 2L, \qquad T_d \;=\; 0.5L. \]
Substituting \( K = 2 \), \( L = 0.5 \), \( \tau = 1.5 \):
\[ K_p \;=\; 1.2\,\frac{1.5}{2\cdot 0.5} \;=\; 1.8, \]
\[ T_i \;=\; 2L \;=\; 1.0\ \text{s}, \qquad T_d \;=\; 0.5L \;=\; 0.25\ \text{s}. \]
Converting to \( K_i, K_d \):
\[ K_i \;=\; \frac{K_p}{T_i} \;=\; \frac{1.8}{1.0} \;=\; 1.8, \qquad K_d \;=\; K_p T_d \;=\; 1.8 \cdot 0.25 \;=\; 0.45. \]
Thus, \( K_p = 1.8 \), \( K_i = 1.8 \), \( K_d = 0.45 \).
Problem 3 (ZN Closed-Loop Gains from \( K_u, T_u \)): A closed-loop experiment with P-only control yields an ultimate gain \( K_u = 6 \) and ultimate period \( T_u = 1.2 \) s. Compute the Ziegler–Nichols closed-loop gains \( K_p, K_i, K_d \) for P, PI, and PID.
Solution:
P controller:
\[ K_p^{\text{P}} \;=\; 0.5\,K_u \;=\; 0.5 \cdot 6 \;=\; 3.0, \quad K_i^{\text{P}} \;=\; 0, \quad K_d^{\text{P}} \;=\; 0. \]
PI controller:
\[ K_p^{\text{PI}} \;=\; 0.45\,K_u \;=\; 2.7, \qquad T_i^{\text{PI}} \;=\; \frac{T_u}{1.2} \;=\; 1.0\ \text{s}. \]
\[ K_i^{\text{PI}} \;=\; \frac{K_p^{\text{PI}}}{T_i^{\text{PI}}} \;=\; \frac{2.7}{1.0} \;=\; 2.7, \qquad K_d^{\text{PI}} \;=\; 0. \]
PID controller:
\[ K_p^{\text{PID}} \;=\; 0.60\,K_u \;=\; 3.6, \qquad T_i^{\text{PID}} \;=\; \frac{T_u}{2} \;=\; 0.6\ \text{s}, \qquad T_d^{\text{PID}} \;=\; \frac{T_u}{8} \;=\; 0.15\ \text{s}. \]
\[ K_i^{\text{PID}} \;=\; \frac{3.6}{0.6} \;=\; 6.0, \qquad K_d^{\text{PID}} \;=\; 3.6 \cdot 0.15 \;=\; 0.54. \]
Problem 4 (Nonexistence of Ultimate Gain for a First-Order Plant): Consider a first-order plant without delay,
\[ G_p(s) \;=\; \frac{K}{(\tau s + 1)}, \]
and a proportional controller \( C(s) = K_p \) in unity feedback. Show that the closed-loop system is stable for all \( K_p > 0 \) and therefore no finite ultimate gain \( K_u \) exists for the Ziegler–Nichols closed-loop method.
Solution:
The closed-loop characteristic equation is obtained from
\[ 1 + C(s)G_p(s) \;=\; 1 + \frac{K_p K}{(\tau s + 1)} \;=\; 0. \]
Multiplying by \( \tau s + 1 \) yields
\[ \tau s + 1 + K K_p \;=\; 0. \]
This is a first-order polynomial with root
\[ s \;=\; -\frac{1 + K K_p}{\tau}. \]
For \( K > 0 \), \( \tau > 0 \), and any \( K_p > 0 \), we have \( 1 + K K_p > 0 \), so the pole always lies on the negative real axis and the closed-loop system is asymptotically stable. It never exhibits a transition from stability to instability via a pair of imaginary-axis poles. Therefore there is no finite ultimate gain \( K_u \), and the Ziegler–Nichols closed-loop method cannot be applied.
Problem 5 (Discrete-Time PID Difference Equation): Starting from the continuous-time PID law
\[ u(t) \;=\; K_p\,e(t) \;+\; K_i \int_0^t e(\xi)\,d\xi \;+\; K_d\,\frac{d}{dt}e(t), \]
derive a simple discrete-time implementation with sampling period \( h \), using a rectangular rule for the integral and a backward difference for the derivative. Express \( u[k] \) in terms of \( u[k-1] \), \( e[k] \), and \( e[k-1] \).
Solution:
Approximate the integral at time \( t_k = kh \) by
\[ \int_0^{t_k} e(\xi)\,d\xi \;\approx\; h\sum_{i=0}^{k} e[i], \]
and the derivative by
\[ \frac{d}{dt}e(t_k) \;\approx\; \frac{e[k] - e[k-1]}{h}. \]
Then the control input at step \( k \) is
\[ u[k] \;=\; K_p e[k] \;+\; K_i h\sum_{i=0}^{k} e[i] \;+\; K_d \frac{e[k] - e[k-1]}{h}. \]
Introducing the integral state
\[ I[k] \;=\; I[k-1] + h\,e[k], \]
we can write the recursive implementation as
\[ \begin{aligned} I[k] &=\; I[k-1] + h\,e[k], \\ u[k] &=\; K_p e[k] + K_i I[k] + K_d \frac{e[k] - e[k-1]}{h}, \end{aligned} \]
which matches the structure of the discrete PID implementations shown in the Python, C++, and Java examples of this lesson.
12. Summary
This lesson introduced classical heuristic PID tuning with emphasis on the Ziegler–Nichols methods. We expressed the PID controller in parallel form, related the tuning objective to the closed-loop transfer function and damping ratio, and discussed quarter-amplitude damping as a design heuristic. The closed-loop ZN method uses ultimate gain \( K_u \) and period \( T_u \), while the open-loop method relies on FOPDT parameters \( K, L, \tau \). We also highlighted theoretical limitations (nonexistence of \( K_u \) for some plants, high overshoot, sensitivity to noise) and provided multi-language implementations suitable for robotic control software.
In subsequent chapters, more systematic time-domain and frequency-domain design methods will be developed, allowing PID gains to be chosen to satisfy explicit performance and robustness specifications rather than heuristic norms.
13. References
- Ziegler, J. G., & Nichols, N. B. (1942). Optimum settings for automatic controllers. Transactions of the ASME, 64, 759–768.
- Cohen, G. H., & Coon, G. A. (1953). Theoretical consideration of retarded control. Transactions of the ASME, 75, 827–834.
- Åström, K. J., & Hägglund, T. (1984). Automatic tuning of simple regulators with specifications on phase and amplitude margins. Automatica, 20(5), 645–651.
- Åström, K. J., & Hägglund, T. (1995). PID Controllers: Theory, Design, and Tuning. Instrument Society of America.
- Ho, W. K., Hang, C. C., & Cao, L. S. (1995). Tuning of PID controllers based on gain and phase margin specifications. Automatica, 31(3), 497–502.
- Middleton, R. H., & Goodwin, G. C. (1988). Improved deadbeat control design for uncertain systems. IEEE Transactions on Automatic Control, 33(10), 966–974.
- Hägglund, T. (1991). A friction compensator for industrial regulators. Journal of Process Control, 1(2), 93–100.
- Ho, W. K., Hang, C. C., & Zhou, J. H. (1995). Self-tuning PID control of a class of processes. Automatica, 31(10), 1571–1579.
- Skogestad, S. (2003). Simple analytic rules for model reduction and PID controller tuning. Journal of Process Control, 13(4), 291–309.
- Hwang, C., & Hsu, P. L. (1992). A new PID tuning method for single-input single-output systems. Automatica, 28(1), 45–53.