Chapter 18: Frequency-Domain Performance Specifications
Lesson 2: Disturbance Rejection in the Frequency Domain
In this lesson we study how feedback attenuates disturbances as a function of frequency. Starting from closed-loop transfer functions, we derive disturbance-to-output gains for different disturbance locations, interpret them in the frequency domain, and connect them to design specifications. We illustrate the ideas on a robot joint model and provide multi-language implementations that compute and visualize disturbance rejection.
1. Conceptual Overview of Disturbance Rejection
A disturbance is any exogenous signal that perturbs a control system away from its desired behavior. Typical examples include load torques in robot joints, ground irregularities in vehicle suspensions, or unmodeled friction in actuators. In a unity-feedback configuration with reference input \( R(s) \), plant \( P(s) \), and controller \( C(s) \), disturbance rejection refers to keeping the output \( Y(s) \) close to \( R(s) \) despite the presence of disturbance signals \( D(s) \).
In the frequency domain, disturbance rejection is quantified by the magnitude of the disturbance-to-output transfer function. For a disturbance with frequency content concentrated near some band \( \omega \in [0,\omega_d] \), good disturbance rejection means that the transfer function magnitude from disturbance to output is small in that band.
We distinguish several disturbance locations:
- Plant input disturbance: additive torque or force before the plant.
- Plant output disturbance: additive disturbance directly added to the output signal.
- Measurement disturbance (sensor noise): added to the measured output (treated in the next lesson).
The following diagram summarizes the signal paths for reference and disturbances in a unity-feedback loop with a plant-input disturbance.
flowchart TD
R["Reference R(s)"] --> E["Sum: R - Y"]
Y["Output Y(s)"] --> E
E --> C["Controller C(s)"]
C --> U["Control signal U(s)"]
D["Disturbance D(s) at plant input"] --> SUM["Sum: U + D"]
U --> SUM
SUM --> P["Plant P(s)"]
P --> Y
Disturbance rejection specifications in the frequency domain will ultimately constrain the loop transfer function \( L(s) = C(s)P(s) \) over given frequency ranges.
2. Closed-Loop Transfer Functions for Disturbances
We start with algebraic derivations of disturbance-to-output transfer functions for the standard unity-feedback configuration. Throughout this lesson, we write the loop transfer function as
\[ L(s) = C(s)P(s). \]
2.1 Plant-input disturbance
Consider a disturbance \( D(s) \) added at the plant input as shown in the previous diagram. The equations in the Laplace domain are
\[ \begin{aligned} E(s) &= R(s) - Y(s), \\ U(s) &= C(s)E(s), \\ Y(s) &= P(s)\bigl(U(s) + D(s)\bigr). \end{aligned} \]
Eliminating \( E(s) \) and \( U(s) \), we obtain
\[ Y(s) = P(s)\bigl(C(s)(R(s) - Y(s)) + D(s)\bigr). \]
Collect the terms in \( Y(s) \):
\[ Y(s)\bigl(1 + C(s)P(s)\bigr) = C(s)P(s)R(s) + P(s)D(s). \]
Using \( L(s) = C(s)P(s) \), we get
\[ Y(s) = \frac{L(s)}{1 + L(s)}R(s) + \frac{P(s)}{1 + L(s)}D(s). \]
Thus, the transfer function from plant-input disturbance to output is
\[ G_{yd}^{\text{in}}(s) = \frac{Y(s)}{D(s)}\Bigg|_{R(s)=0} = \frac{P(s)}{1 + L(s)}. \]
2.2 Plant-output disturbance
Next, consider a disturbance \( D(s) \) added directly at the plant output, after the plant. The equations become
\[ \begin{aligned} E(s) &= R(s) - Y(s), \\ U(s) &= C(s)E(s), \\ Y(s) &= P(s)U(s) + D(s) = P(s)C(s)(R(s) - Y(s)) + D(s). \end{aligned} \]
Rearranging terms yields
\[ Y(s)\bigl(1 + L(s)\bigr) = L(s)R(s) + D(s). \]
Therefore,
\[ Y(s) = \frac{L(s)}{1 + L(s)}R(s) + \frac{1}{1 + L(s)}D(s), \]
and the disturbance-to-output transfer function is
\[ G_{yd}^{\text{out}}(s) = \frac{Y(s)}{D(s)}\Bigg|_{R(s)=0} = \frac{1}{1 + L(s)}. \]
We see that:
- For plant-output disturbance, rejection depends directly on \( 1 + L(s) \). If \(|L(j\omega)|\) is large, \(|G_{yd}^{\text{out}}(j\omega)| \approx 1/\lvert L(j\omega)\rvert\) is small.
- For plant-input disturbance, the factor \( P(s) \) also appears in the numerator; at low frequencies many plants behave like integrators or low-pass systems, which changes how loop gain affects disturbance rejection.
3. Frequency-Domain Disturbance Rejection Specifications
In frequency-domain design, we specify disturbance rejection by bounding the magnitude of the transfer function from disturbance to output in frequency ranges where the disturbance has significant energy.
3.1 Magnitude bounds
For a plant-output disturbance, a typical requirement is
\[ \bigl|G_{yd}^{\text{out}}(j\omega)\bigr| = \left|\frac{1}{1 + L(j\omega)}\right| \le \gamma_d, \quad \omega \in [0,\omega_d]. \]
Equivalently, in decibels:
\[ 20\log_{10}\bigl|G_{yd}^{\text{out}}(j\omega)\bigr| \le -A_d, \quad A_d = -20\log_{10}(\gamma_d). \]
For example, \( A_d = 20\ \text{dB} \) means that the disturbance is attenuated by at least a factor of 10 in amplitude within the specified band.
For plant-input disturbances, we similarly constrain
\[ \bigl|G_{yd}^{\text{in}}(j\omega)\bigr| = \left|\frac{P(j\omega)}{1 + L(j\omega)}\right| \le \gamma_d^{\text{in}}, \quad \omega \in [0,\omega_d^{\text{in}}]. \]
The loop-shaping intuition remains the same: high loop gain at low frequencies improves disturbance rejection, but excessive gain near or beyond the crossover frequency compromises stability margins (see Chapter 17).
3.2 Loop-shaping workflow
The following flow diagram summarizes a typical design workflow for disturbance rejection in the frequency domain.
flowchart TD
A["Identify disturbance bandwidth and spectrum"] --> B["Choose plant model P(s) and controller structure C(s)"]
B --> C["Compute loop L(jw) and Gyd(jw)"]
C --> D["Impose bounds on |Gyd(jw)| in low frequency band"]
D --> E["Adjust controller gains and zeros for desired rejection"]
E --> F["Check stability margins and high frequency behavior"]
F --> G["Iterate until disturbance and stability specs are both satisfied"]
Thus, disturbance rejection requirements translate into inequalities on \(|L(j\omega)|\) and on the shape of the closed-loop functions \(\frac{L}{1+L}\) and \(\frac{1}{1+L}\).
4. Example — Robot Joint with Load Torque Disturbance
Consider a single revolute robot joint with inertia \( J \) and viscous friction \( b \). The equation of motion (for small angles) is
\[ J\ddot{q}(t) + b\dot{q}(t) = u(t) + d(t), \]
where \( q(t) \) is the joint angle, \( u(t) \) is the control torque, and \( d(t) \) is a load disturbance torque (e.g., from contact with the environment).
Taking Laplace transforms (with zero initial conditions) gives
\[ J s^2 Q(s) + b s Q(s) = U(s) + D(s). \]
The plant from torque to position is
\[ P(s) = \frac{Q(s)}{U(s)}\Bigg|_{D(s)=0} = \frac{1}{Js^2 + b s}. \]
With a unity-feedback controller \( C(s) \) and disturbance at the plant input, the disturbance-to-output transfer function is
\[ G_{qd}(s) = \frac{Q(s)}{D(s)}\Bigg|_{R(s)=0} = \frac{P(s)}{1 + C(s)P(s)}. \]
4.1 PI controller and low-frequency disturbance rejection
Suppose we use a proportional-integral (PI) controller
\[ C(s) = K_p + \frac{K_i}{s}. \]
The loop transfer function is
\[ L(s) = C(s)P(s) = \left(K_p + \frac{K_i}{s}\right)\frac{1}{Js^2 + b s}. \]
For very low frequencies (\( s = j\omega \) with \( \omega \) small), the integrator term \( K_i/s \) dominates, so approximately
\[ L(j\omega) \approx \frac{K_i}{j\omega}\frac{1}{J(j\omega)^2 + b j\omega} = \frac{K_i}{j\omega}\frac{1}{-J\omega^2 + b j\omega}. \]
For sufficiently small \( \omega \), the viscous term \( b j\omega \) dominates the inertial term, and we obtain roughly
\[ L(j\omega) \approx \frac{K_i}{j\omega}\frac{1}{b j\omega} = \frac{K_i}{-b\omega^2}. \]
Hence \(|L(j\omega)|\) becomes very large as \(\omega \to 0\). Consequently,
\[ \bigl|G_{qd}(j\omega)\bigr| = \left|\frac{P(j\omega)}{1 + L(j\omega)}\right| \approx \frac{|P(j\omega)|}{|L(j\omega)|} = \frac{1}{|C(j\omega)|}, \quad \omega \text{ small}. \]
Since \(|C(j\omega)|\) grows like \(K_i/\omega\) at low frequencies, the magnitude of \( G_{qd}(j\omega) \) tends to zero, meaning that the PI controller yields excellent rejection of low-frequency (quasi-static) load torques.
In the time domain, this manifests as zero steady-state error in angle \( q(t) \) when the disturbance is a constant torque step, provided the closed-loop system is stable.
5. Python Lab — Disturbance Rejection for a Robot Joint
We now compute the disturbance-to-output transfer function and its Bode
magnitude using Python. We use the python-control library,
which is also used in robotics contexts (e.g., for analyzing linearized
models of robot manipulators).
import numpy as np
import matplotlib.pyplot as plt
# Control toolbox for LTI systems
import control # pip install control
# Robot joint parameters
J = 0.01 # kg*m^2 (effective inertia)
b = 0.1 # N*m*s/rad (viscous friction)
# Plant: torque -> angle
# P(s) = 1 / (J s^2 + b s) = 1 / (s (J s + b))
P = control.tf([1.0], [J, b, 0.0])
# PI controller
Kp = 5.0
Ki = 10.0
C = control.tf([Kp, Ki], [1.0, 0.0]) # Kp + Ki/s
# Loop transfer function L(s) = C(s) P(s)
L = control.series(C, P)
# Disturbance-to-output for plant-input disturbance:
# G_d(s) = P(s) / (1 + L(s))
Gd = control.minreal(P / (1 + L))
# Frequency grid
w = np.logspace(-1, 2, 400) # rad/s
# Bode magnitude of Gd(jw)
mag, phase, wout = control.bode(Gd, w, Plot=False)
mag_db = 20 * np.log10(mag)
plt.figure()
plt.semilogx(wout, mag_db)
plt.xlabel("Frequency [rad/s]")
plt.ylabel("Magnitude of Gd(jw) [dB]")
plt.title("Disturbance-to-output magnitude for plant-input disturbance")
plt.grid(True, which="both")
plt.show()
In a robotics workflow (e.g., with ROS), a similar frequency-domain
analysis can be performed on linearized models extracted from a robot
dynamics library such as
roboticstoolbox or from URDF-based models, then analyzed
using python-control.
6. C++ Example — Evaluating \(|G_d(j\omega)|\) Numerically
C++ is widely used in robotics for real-time control (e.g., via ROS controllers). Below we implement a simple numerical evaluation of the disturbance-to-output magnitude for the same robot joint model and PI controller.
#include <iostream>
#include <complex>
#include <vector>
#include <cmath>
int main() {
using std::complex;
using std::cout;
using std::endl;
const double J = 0.01;
const double b = 0.1;
const double Kp = 5.0;
const double Ki = 10.0;
// Frequency grid (rad/s)
std::vector<double> w;
for (int i = 0; i <= 20; ++i) {
double wi = std::pow(10.0, -1.0 + 0.15 * i); // 0.1 ... about 31.6
w.push_back(wi);
}
cout << "# w, |Gd(jw)|" << endl;
for (double wi : w) {
complex<double> j(0.0, 1.0);
complex<double> s = j * wi;
// P(s) = 1 / (J s^2 + b s)
complex<double> denomP = J * s * s + b * s;
complex<double> P = 1.0 / denomP;
// C(s) = Kp + Ki/s
complex<double> C = Kp + Ki / s;
complex<double> L = C * P;
complex<double> Gd = P / (complex<double>(1.0, 0.0) + L);
double mag = std::abs(Gd);
cout << wi << " " << mag << endl;
}
return 0;
}
In a ROS-based robot controller, such a routine could be used offline to tune gains so that the computed \(|G_d(j\omega)|\) satisfies specified disturbance rejection bounds over a target frequency range.
7. Java Example — Disturbance Rejection Computation
Java is used in some robotics frameworks (e.g., FRC robots via WPILib). The following Java code computes the disturbance-to-output magnitude for the same system using basic complex arithmetic.
public class DisturbanceRejection {
public static void main(String[] args) {
double J = 0.01;
double b = 0.1;
double Kp = 5.0;
double Ki = 10.0;
// Frequency grid (rad/s)
int N = 21;
double[] w = new double[N];
for (int i = 0; i < N; i++) {
w[i] = Math.pow(10.0, -1.0 + 0.15 * i); // 0.1 ... about 31.6
}
System.out.println("# w, |Gd(jw)|");
for (int i = 0; i < N; i++) {
double wi = w[i];
// s = j*wi
double sr = 0.0;
double si = wi;
// P(s) = 1 / (J s^2 + b s)
// s^2
double s2r = sr * sr - si * si;
double s2i = 2 * sr * si;
double denomPr = J * s2r + b * sr;
double denomPi = J * s2i + b * si;
double denomPabs2 = denomPr * denomPr + denomPi * denomPi;
double Pr = denomPr / denomPabs2;
double Pi = -denomPi / denomPabs2;
// C(s) = Kp + Ki/s
// 1/s
double sabs2 = sr * sr + si * si;
double invsr = sr / sabs2;
double invsi = -si / sabs2;
double Cr = Kp + Ki * invsr;
double Ci = Ki * invsi;
// L = C * P
double Lr = Cr * Pr - Ci * Pi;
double Li = Cr * Pi + Ci * Pr;
// Gd = P / (1 + L)
double onePlusLr = 1.0 + Lr;
double onePlusLi = Li;
double denomGabs2 = onePlusLr * onePlusLr + onePlusLi * onePlusLi;
// P / (1 + L)
double Gdr = (Pr * onePlusLr + Pi * onePlusLi) / denomGabs2;
double Gdi = (Pi * onePlusLr - Pr * onePlusLi) / denomGabs2;
double mag = Math.hypot(Gdr, Gdi);
System.out.println(wi + " " + mag);
}
}
}
Libraries such as Apache Commons Math offer higher-level complex number and polynomial utilities, but the fundamental computation is the evaluation of the transfer function \( G_d(j\omega) \) on a frequency grid.
8. MATLAB / Simulink Implementation
MATLAB and Simulink are standard tools in control and robotics. The
Control System Toolbox and
Robotics System Toolbox
can be combined to linearize robot models and analyze disturbance
rejection.
% Robot joint parameters
J = 0.01;
b = 0.1;
% Plant: torque -> angle
P = tf(1, [J b 0]);
% PI controller
Kp = 5;
Ki = 10;
C = tf([Kp Ki], [1 0]); % Kp + Ki/s
L = series(C, P);
% Disturbance transfer function (plant-input disturbance)
Gd = minreal(P / (1 + L));
w = logspace(-1, 2, 400);
[mag, phase, wout] = bode(Gd, w);
mag = squeeze(mag);
figure;
semilogx(wout, 20*log10(mag));
grid on;
xlabel('Frequency [rad/s]');
ylabel('Magnitude of Gd(jw) [dB]');
title('Disturbance rejection for plant-input disturbance');
% In a robotics workflow:
% * Build a rigidBodyTree robot model.
% * Use linearize() around an operating point to get P(s).
% * Design C(s) and compute Gd(s) as above.
In Simulink, one can explicitly model disturbances as additive signals
and use the Frequency Response Estimator or linear analysis
tools to verify disturbance rejection specifications.
9. Wolfram Mathematica Implementation
Mathematica provides symbolic and numerical tools for transfer-function analysis, making it convenient to derive and plot disturbance rejection characteristics.
(* Parameters *)
J = 0.01;
b = 0.1;
Kp = 5.;
Ki = 10.;
s = ComplexExpand[I*ω] /. ω -> Symbol["w"]; (* symbolic frequency variable not strictly needed *)
(* Define transfer functions as functions of s *)
P[s_] := 1/(J s^2 + b s);
C[s_] := Kp + Ki/s;
L[s_] := C[s] P[s];
Gd[s_] := P[s]/(1 + L[s]);
(* Bode-like magnitude plot for Gd(jω) *)
GdFreq[w_] := Gd[I w];
Plot[20 Log10[Abs[GdFreq[w]]], {w, 0.1, 100},
AxesLabel -> {"w [rad/s]", "20 log10 |Gd(j w)| [dB]"},
PlotRange -> All,
PlotPoints -> 200,
GridLines -> Automatic,
ScalingFunctions -> {"Log10", None}]
For more complex robotic models, one can build state-space or
transfer-function models using StateSpaceModel or
TransferFunctionModel and then compute the disturbance
transfer function symbolically before evaluating it numerically.
10. Problems and Solutions
Problem 1 (Plant-input disturbance transfer function):
Consider a unity-feedback loop with plant \( P(s) \), controller \( C(s)
\), reference \( R(s) \), and a disturbance \( D(s) \) added at the
plant input. Derive the disturbance-to-output transfer function \(
G_{yd}^{\text{in}}(s) = Y(s)/D(s) \) under the assumption \( R(s) = 0
\).
Solution:
From Section 2, the equations are
\[ \begin{aligned} E(s) &= R(s) - Y(s), \\ U(s) &= C(s)E(s), \\ Y(s) &= P(s)\bigl(U(s) + D(s)\bigr). \end{aligned} \]
Substituting \( U(s) = C(s)(R(s) - Y(s)) \) and setting \( R(s) = 0 \) gives
\[ Y(s) = P(s)\bigl(C(s)(-Y(s)) + D(s)\bigr) = -C(s)P(s)Y(s) + P(s)D(s). \]
Rearranging:
\[ Y(s)\bigl(1 + C(s)P(s)\bigr) = P(s)D(s). \]
Thus
\[ G_{yd}^{\text{in}}(s) = \frac{Y(s)}{D(s)} = \frac{P(s)}{1 + C(s)P(s)}. \]
Problem 2 (Plant-output disturbance transfer function):
For a unity-feedback loop with disturbance \( D(s) \) added at the plant
output, derive the disturbance-to-output transfer function \(
G_{yd}^{\text{out}}(s) \).
Solution:
The equations are
\[ \begin{aligned} E(s) &= R(s) - Y(s), \\ U(s) &= C(s)E(s), \\ Y(s) &= P(s)U(s) + D(s). \end{aligned} \]
Substitute \( U(s) = C(s)(R(s) - Y(s)) \):
\[ Y(s) = P(s)C(s)(R(s) - Y(s)) + D(s) = C(s)P(s)R(s) - C(s)P(s)Y(s) + D(s). \]
Collecting \( Y(s) \) terms:
\[ Y(s)\bigl(1 + C(s)P(s)\bigr) = C(s)P(s)R(s) + D(s). \]
With \( R(s) = 0 \), we obtain
\[ Y(s) = \frac{1}{1 + C(s)P(s)}D(s), \]
hence
\[ G_{yd}^{\text{out}}(s) = \frac{Y(s)}{D(s)} = \frac{1}{1 + C(s)P(s)}. \]
Problem 3 (Low-frequency disturbance rejection with integral
control):
Consider a stable plant \( P(s) \) such that \( P(0) \) is finite and
nonzero. Suppose the controller has an integrator, i.e., \( C(s) =
\tilde{C}(s)/s \), where \( \tilde{C}(s) \) is proper and nonzero at \(
s = 0 \). Show that for a constant plant-output disturbance (step
disturbance), the steady-state output error can be driven to zero under
closed-loop stability.
Solution:
With disturbance at the output and reference \( R(s) = 0 \), the output is
\[ Y(s) = \frac{1}{1 + C(s)P(s)}D(s). \]
For a step disturbance of magnitude \( d_0 \), we have \( D(s) = d_0/s \). The steady-state value of \( y(t) \) is given by the final value theorem:
\[ \lim_{t \to \infty} y(t) = \lim_{s \to 0} s Y(s) = \lim_{s \to 0} \frac{s}{1 + C(s)P(s)} \frac{d_0}{s} = \lim_{s \to 0} \frac{d_0}{1 + C(s)P(s)}. \]
Because \( C(s) = \tilde{C}(s)/s \) and \( P(0) \) is finite and nonzero, we have
\[ C(s)P(s) \approx \frac{\tilde{C}(0)P(0)}{s} \]
near \( s = 0 \), so \( |C(s)P(s)| \to \infty \) as \( s \to 0 \). Therefore,
\[ \frac{1}{1 + C(s)P(s)} \to 0, \]
and the steady-state output tends to zero. Thus, an integrator in the controller can eliminate steady-state error due to constant output disturbances, provided the closed-loop system remains stable.
Problem 4 (Frequency-domain bound for disturbance
attenuation):
Let \( G_{yd}^{\text{out}}(s) = 1/(1 + L(s)) \) for a plant-output
disturbance. Suppose we want at least 40 dB attenuation of disturbances
in the band \( \omega \in [0,\omega_0] \). Express a sufficient
condition on \(|L(j\omega)|\) in this band.
Solution:
The requirement is
\[ 20\log_{10}\left|G_{yd}^{\text{out}}(j\omega)\right| \le -40 \quad \text{for } \omega \in [0,\omega_0], \]
which is equivalent to
\[ \left|\frac{1}{1 + L(j\omega)}\right| \le 10^{-2}. \]
A sufficient condition is that \(|L(j\omega)| \ge 99\) because
\[ \left|\frac{1}{1 + L(j\omega)}\right| \le \frac{1}{|L(j\omega)| - 1} \le \frac{1}{99} \approx 0.0101, \]
which is slightly tighter than \( 10^{-2} \). In practice, designers ensure \(|L(j\omega)|\) is well above \( 100 \) over \( [0,\omega_0] \) to maintain a margin.
Problem 5 (Design flow for disturbance rejection and
stability):
Sketch a high-level design flow that starts from disturbance
characteristics and ends with a controller \( C(s) \) that satisfies
both disturbance rejection and stability margin requirements.
Solution:
A suitable flow is:
flowchart TD
S["Characterize disturbances (bandwidth, level)"] --> M["Select plant model P(s)"]
M --> C1["Choose controller structure (e.g., PI, PID, lead-lag)"]
C1 --> L["Shape loop L(s) on Bode plot"]
L --> D1["Check |Gyd(jw)| vs disturbance specs"]
D1 --> ST["Check gain and phase margins"]
ST --> DEC["Adjust controller until both disturbance and stability specs are met"]
This flow emphasizes that disturbance rejection requirements and stability margins must be satisfied simultaneously through appropriate loop shaping.
11. Summary
- Disturbances can enter at different locations in a feedback loop; their impact on the output is quantified by disturbance-to-output transfer functions.
- For unity feedback, plant-input disturbances satisfy \( G_{yd}^{\text{in}}(s) = P(s)/(1 + C(s)P(s)) \), while plant-output disturbances satisfy \( G_{yd}^{\text{out}}(s) = 1/(1 + C(s)P(s)) \).
- Frequency-domain disturbance rejection specs constrain the magnitude of these transfer functions over frequency ranges where disturbances have significant energy.
- High loop gain at low frequencies enhances disturbance rejection but must be balanced against stability margins and noise sensitivity.
- For robot joints with PI controllers, the integrator dramatically improves low-frequency disturbance rejection, leading to small steady-state errors for constant torques.
- The concepts can be implemented and visualized using Python, C++, Java, MATLAB/Simulink, and Mathematica, which are common in control and robotics workflows.
12. References
- Bode, H.W. (1945). Network Analysis and Feedback Amplifier Design. Van Nostrand, New York.
- Horowitz, I.M. (1963). Synthesis of feedback systems. Academic Press, New York.
- Zames, G. (1981). Feedback and optimal sensitivity: Model reference transformations, multiplicative seminorms, and approximate inverses. IEEE Transactions on Automatic Control, 26(2), 301–320.
- Doyle, J.C., Francis, B.A., & Tannenbaum, A.R. (1992). Feedback Control Theory. Macmillan.
- Freudenberg, J.S., & Looze, D.P. (1985). Right half plane poles and zeros and design tradeoffs in feedback systems. IEEE Transactions on Automatic Control, 30(6), 555–565.
- Middleton, R.H., & Goodwin, G.C. (1990). Improved finite word length characteristics in digital control using delta operators. IEEE Transactions on Automatic Control, 35(8), 917–924.
- Åström, K.J., & Wittenmark, B. (1972). On the limitations of linear control. Automatica, 8(3), 287–304.