Chapter 20: Two-Degree-of-Freedom (2-DOF) Linear Controllers
Lesson 5: Practical Applications of 2-DOF Controllers
This lesson connects the abstract 2-DOF feedback structures from previous lessons to concrete engineering applications such as robot joint servos and process control loops. We derive closed-loop transfer functions for a general 2-DOF architecture, interpret how independent shaping of reference tracking and disturbance rejection is realized, and implement 2-DOF PID controllers in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica, with a particular emphasis on robotics-related scenarios.
1. 2-DOF Architecture in Practice
In previous lessons you have seen that a one-degree-of-freedom (1-DOF) controller uses the same transfer function to process both the reference signal and the measured output. A 2-DOF controller separates these roles by introducing two designable transfer functions:
- \( C_r(s) \): reference path (or pre-filter path) that shapes the response to set-point changes.
- \( C_y(s) \): feedback path that mainly determines stability and disturbance rejection.
A common implementation is \( u(s) = C_r(s) r(s) - C_y(s) y(s) \), where \( G(s) \) denotes the plant transfer function. In industrial practice:
- Robot joint servos use 2-DOF PID to reduce overshoot for reference steps while keeping strong disturbance rejection against load torques.
- Process control (temperature, level, flow) uses reference pre-filters to limit overshoot and valve wear when the set-point changes.
- Motion controllers in mechatronic stages use 2-DOF designs to attain aggressive disturbance rejection without exciting resonances during command changes.
The following diagram summarizes a widely used 2-DOF structure:
flowchart TD
R["Reference r"] --> CR["Reference path Cr(s)"]
CR --> SUM1["+ node"]
Y["Output y"] --> CY["Feedback path Cy(s)"]
CY -->|subtract| SUM1
SUM1 --> U["Control input u"]
U --> G["Plant G(s)"]
G --> Y
D["Disturbance d"] --> G
Because \( C_r(s) \) and \( C_y(s) \) are independent, we can tune:
- tracking dynamics (mostly via \( C_r(s) \)), and
- disturbance/noise behavior (mostly via \( C_y(s) \)),
largely without redoing the entire loop design each time we change tracking requirements.
2. Closed-Loop Transfer Functions for a General 2-DOF Controller
We now derive the closed-loop relations for the 2-DOF structure in the Laplace domain, assuming a disturbance signal \( d(s) \) acts at the plant input. Let
\[ \begin{aligned} y(s) &= G(s)\bigl(u(s) + d(s)\bigr), \\ u(s) &= C_r(s) r(s) - C_y(s) y(s). \end{aligned} \]
Substituting \( u(s) \) into the plant equation gives
\[ \begin{aligned} y(s) &= G(s)\bigl(C_r(s) r(s) - C_y(s) y(s) + d(s)\bigr) \\ &= G(s) C_r(s) r(s) - G(s) C_y(s) y(s) + G(s) d(s). \end{aligned} \]
Rearrange terms in \( y(s) \):
\[ \bigl(1 + G(s) C_y(s)\bigr) y(s) = G(s) C_r(s) r(s) + G(s) d(s). \]
Hence the closed-loop transfer functions from reference to output and from disturbance to output are
\[ \frac{Y(s)}{R(s)} = \frac{G(s) C_r(s)}{1 + G(s) C_y(s)}, \qquad \frac{Y(s)}{D(s)} = \frac{G(s)}{1 + G(s) C_y(s)}. \]
Two crucial conclusions:
- The denominator \( 1 + G(s) C_y(s) \) is independent of \( C_r(s) \). Thus stability and disturbance rejection characteristics are determined entirely by \( C_y(s) \) (together with \( G(s) \)).
- The numerator for reference tracking can be shaped via \( C_r(s) \) without altering the closed-loop poles. This allows us to reduce overshoot or attenuate high-frequency components of the reference, while preserving the disturbance rejection behavior.
In the special 1-DOF case \( C_r(s) = C_y(s) = C(s) \), the usual closed-loop formula is recovered:
\[ \frac{Y(s)}{R(s)} = \frac{G(s) C(s)}{1 + G(s) C(s)}, \qquad \frac{Y(s)}{D(s)} = \frac{G(s)}{1 + G(s) C(s)}. \]
3. 2-DOF PID for Robot Joint Position Control
Consider a single robot joint driven by a motor. Around a fixed operating point, the joint position dynamics can often be approximated by a second-order model
\[ G(s) = \frac{K}{(T_m s + 1)s}, \]
where \( K > 0 \) is an effective gain and \( T_m > 0 \) is a motor time constant. We want:
- fast set-point tracking for reference changes in joint angle,
- strong rejection of load torque disturbances,
- limited overshoot and smooth actuator effort.
A widely used 2-DOF PID structure for such servo tasks introduces weighting coefficients \( b \) and \( c \) on the proportional and derivative parts of the reference, respectively. In the time domain:
\[ \begin{aligned} u(t) =&\; K_p \bigl(b\,r(t) - y(t)\bigr) + K_i \int_0^t \bigl(r(\sigma) - y(\sigma)\bigr)\,d\sigma \\ &\; + K_d \bigl(c\,\dot r(t) - \dot y(t)\bigr), \end{aligned} \]
with design parameters \( K_p, K_i, K_d, b, c \). The integral term always acts on the true error to preserve steady-state accuracy; the proportional and derivative actions receive weighted reference signals.
Taking Laplace transforms (assuming zero initial conditions):
\[ \begin{aligned} u(s) =&\; K_p \bigl(b R(s) - Y(s)\bigr) + \frac{K_i}{s} \bigl(R(s) - Y(s)\bigr) + K_d \bigl(c s R(s) - s Y(s)\bigr) \\ =&\; \underbrace{\Bigl( K_p b + \frac{K_i}{s} + K_d c s \Bigr)}_{C_r(s)} R(s) - \underbrace{\Bigl( K_p + \frac{K_i}{s} + K_d s \Bigr)}_{C_y(s)} Y(s). \end{aligned} \]
Thus this 2-DOF PID can be seen as a special case of the general structure with
\[ C_r(s) = K_p b + \frac{K_i}{s} + K_d c s, \qquad C_y(s) = K_p + \frac{K_i}{s} + K_d s. \]
Note that the closed-loop denominator \( 1 + G(s) C_y(s) \) does not depend on \( b \) or \( c \). Therefore:
- First design \( K_p, K_i, K_d \) (e.g., via root-locus or frequency-domain methods) to obtain good stability margins and disturbance rejection.
- Then tune \( b \) and \( c \) to modify the set-point response (overshoot, speed, smoothness) while keeping the disturbance behavior essentially unchanged.
For a pure step reference \( r(t) = r_0 \mathbf{1}_{t \ge 0} \), we have \( \dot r(t) = 0 \) for \( t > 0 \), so the derivative weight \( c \) primarily affects the control effort at the instant of the step; the steady-state error and overshoot depend mainly on \( b \) and the closed-loop poles.
4. Reference Pre-Filtering in Process Control
In process control (e.g., temperature or liquid level), the plant is often slow and subject to persistent disturbances. A typical design procedure is:
- Design a 1-DOF feedback controller \( C(s) \) for good disturbance rejection and robust stability.
- Add a reference pre-filter \( F(s) \) to shape the tracking dynamics without changing the closed-loop poles.
When the reference is filtered before entering the standard error computation, the effective reference path becomes \( C_r(s) = C(s) F(s) \) and \( C_y(s) = C(s) \). Then
\[ \frac{Y(s)}{R(s)} = F(s)\,\frac{G(s) C(s)}{1 + G(s) C(s)}, \qquad \frac{Y(s)}{D(s)} = \frac{G(s)}{1 + G(s) C(s)}. \]
A simple and effective choice for \( F(s) \) is a first-order low-pass filter
\[ F(s) = \frac{1}{T_f s + 1}, \qquad T_f > 0. \]
The parameter \( T_f \) limits how quickly the set-point can change from the controller's perspective, thereby:
- reducing overshoot and actuator saturation risks,
- leaving disturbance rejection properties almost unchanged, because \( F(s) \) is not in the disturbance path.
The design workflow can be summarized as:
flowchart TD
A["Design feedback C(s) for stability and disturbance rejection"]
A --> B["Analyze closed-loop poles and margins"]
B --> C["Choose prefilter F(s) for desired rise time / overshoot"]
C --> D["Check reference step response y(t)"]
D --> E["Adjust F(s) until tracking specs satisfied"]
5. Python Implementation for a 2-DOF PID Servo (Robot Joint)
We now implement the 2-DOF PID structure for a robot joint model in
Python. We use the control library for transfer-function
modeling and simulation. Such code can be integrated into robotics
frameworks (for example as a joint-level controller in a ROS-based
simulation).
import numpy as np
import control as ctrl
import matplotlib.pyplot as plt
# Plant: G(s) = K / ((T_m s + 1) s)
K = 50.0
T_m = 0.05
G = ctrl.TransferFunction([K], [T_m, 1.0, 0.0])
# Feedback-side PID gains (designed e.g. by root locus or frequency methods)
Kp = 20.0
Ki = 200.0
Kd = 0.02
# 2-DOF weights
b = 0.3 # proportional weight on reference
c = 0.0 # derivative weight on reference (often 0 for step commands)
# Cy(s) = Kp + Ki/s + Kd s
s = ctrl.TransferFunction([1, 0], [1])
Cy = Kp + Ki/s + Kd * s
# Cr(s) = Kp b + Ki/s + Kd c s
Cr = Kp * b + Ki/s + Kd * c * s
# Closed-loop transfer functions
T_r = ctrl.minreal(G * Cr / (1 + G * Cy))
T_d = ctrl.minreal(G / (1 + G * Cy))
# Simulate step in reference with no disturbance
t = np.linspace(0, 0.5, 2000)
t_out, y_r = ctrl.step_response(T_r, T=t)
# Simulate disturbance step (negative sign: load torque opposing motion)
Td = -T_d
t_out2, y_d = ctrl.step_response(Td, T=t)
plt.figure()
plt.plot(t_out, y_r, label="response to reference step")
plt.plot(t_out2, y_d, label="response to disturbance step")
plt.xlabel("time [s]")
plt.ylabel("joint angle [rad]")
plt.grid(True)
plt.legend()
plt.show()
By changing \( b \) while keeping \( K_p, K_i, K_d \) fixed, you can observe how the overshoot to a reference step changes, while the disturbance response (given by \( T_d(s) \)) remains almost unchanged.
6. C++ Implementation for Discrete-Time 2-DOF PID (Robotics Context)
In embedded and robotic systems, PID controllers are usually implemented in discrete time. The 2-DOF PID law can be discretized using, for example, a backward Euler approximation for the integrator and a filtered derivative. The following C++ code illustrates a self-contained 2-DOF PID class appropriate for integration into a robot joint controller node (e.g., within a ROS or ROS 2 control loop).
#include <cmath>
#include <algorithm>
class TwoDofPID {
public:
TwoDofPID(double Kp, double Ki, double Kd,
double b, double c,
double Ts, double N)
: Kp_(Kp), Ki_(Ki), Kd_(Kd),
b_(b), c_(c), Ts_(Ts), N_(N),
integ_(0.0), prev_y_(0.0), prev_r_(0.0), d_state_(0.0)
{}
double step(double r, double y) {
// error signal
double e = r - y;
// Proportional term with reference weighting b
double up = Kp_ * (b_ * r - y);
// Integral term (backward Euler)
integ_ += Ki_ * Ts_ * e;
double ui = integ_;
// Derivative term with reference weighting c and filtering
double dr = (r - prev_r_) / Ts_;
double dy = (y - prev_y_) / Ts_;
double v = c_ * dr - dy; // "virtual" derivative input
// First-order filter on derivative: D(s) = Kd * N / (1 + N / s)
double alpha = Ts_ * N_;
d_state_ = (d_state_ + alpha * v) / (1.0 + alpha);
double ud = Kd_ * d_state_;
// Update stored values
prev_r_ = r;
prev_y_ = y;
return up + ui + ud;
}
void reset() {
integ_ = 0.0;
d_state_ = 0.0;
prev_y_ = 0.0;
prev_r_ = 0.0;
}
private:
double Kp_, Ki_, Kd_;
double b_, c_;
double Ts_, N_;
double integ_;
double prev_y_, prev_r_;
double d_state_;
};
In a robotic joint control loop, one would call
step(r, y) at each sampling instant using the desired joint
position \( r \) from a trajectory generator and the
measured joint angle \( y \). The resulting control
signal can be applied to a low-level motor driver or published as a
torque/effort command through a robotics middleware interface.
7. Java Implementation Example (Motion Control / Robotics Library Context)
Java is widely used in educational and industrial robotics frameworks (for example, mobile robots or competition robots). The 2-DOF PID structure can be implemented similarly to the C++ case. Below, we show a simple discrete-time implementation that could be integrated into a real-time loop provided by a robotics library.
public class TwoDofPID {
private double Kp, Ki, Kd;
private double b, c;
private double Ts, N;
private double integ;
private double prevY, prevR;
private double dState;
public TwoDofPID(double Kp, double Ki, double Kd,
double b, double c,
double Ts, double N) {
this.Kp = Kp;
this.Ki = Ki;
this.Kd = Kd;
this.b = b;
this.c = c;
this.Ts = Ts;
this.N = N;
this.integ = 0.0;
this.prevY = 0.0;
this.prevR = 0.0;
this.dState = 0.0;
}
public double step(double r, double y) {
double e = r - y;
// Proportional term with weighting
double up = Kp * (b * r - y);
// Integral term
integ += Ki * Ts * e;
double ui = integ;
// Derivative term with simple filter
double dr = (r - prevR) / Ts;
double dy = (y - prevY) / Ts;
double v = c * dr - dy;
double alpha = Ts * N;
dState = (dState + alpha * v) / (1.0 + alpha);
double ud = Kd * dState;
prevR = r;
prevY = y;
return up + ui + ud;
}
public void reset() {
integ = 0.0;
dState = 0.0;
prevR = 0.0;
prevY = 0.0;
}
}
A robotics framework (e.g., one providing periodic callbacks at a fixed control period) can instantiate this class and use it as a joint or wheel controller, tuning the weights \( b \) and \( c \) differently for tracking-dominant or disturbance-dominant tasks.
8. MATLAB/Simulink Implementation
MATLAB and Simulink provide built-in tools for 2-DOF PID design. Here we illustrate a script-level implementation of a 2-DOF PID for the robot joint plant, followed by comments on Simulink modeling.
% Robot joint plant: G(s) = K / ((T_m s + 1) s)
K = 50;
Tm = 0.05;
s = tf('s');
G = K / ((Tm * s + 1) * s);
% Feedback-side PID
Kp = 20;
Ki = 200;
Kd = 0.02;
Cy = Kp + Ki / s + Kd * s;
% 2-DOF weights
b = 0.3;
c = 0.0;
Cr = Kp * b + Ki / s + Kd * c * s;
% Closed-loop from reference and from disturbance
Tr = minreal(G * Cr / (1 + G * Cy));
Td = minreal(G / (1 + G * Cy));
t = linspace(0, 0.5, 2000);
figure; step(Tr, t); grid on;
title('2-DOF PID: response to reference step');
figure; step(-Td, t); grid on;
title('2-DOF PID: response to disturbance step');
% In Simulink:
% - Use a PID Controller block configured for 2-DOF (if available),
% or implement b and c weighting using Sum and Gain blocks.
% - Place a prefilter block F(s) in front of the reference if needed.
In Simulink, the 2-DOF PID is implemented by feeding \( b r \) to the proportional term and \( c \dot r \) to the derivative term, while the integral term always integrates the true error \( r - y \). This block diagram is directly compatible with the mathematical structure given earlier.
9. Wolfram Mathematica Implementation
Mathematica allows analytical and numerical exploration of 2-DOF structures. The following code constructs the plant, the 2-DOF PID controller, and the closed-loop transfer functions.
(* Plant: G(s) = K / ((T_m s + 1) s) *)
K = 50.0;
Tm = 0.05;
s = LaplaceTransformVariable;
G = TransferFunctionModel[K/((Tm*s + 1)*s), s];
(* PID parameters *)
Kp = 20.0;
Ki = 200.0;
Kd = 0.02;
(* 2-DOF weights *)
b = 0.3;
c = 0.0;
(* Cy(s) and Cr(s) *)
Cy = TransferFunctionModel[Kp + Ki/s + Kd*s, s];
Cr = TransferFunctionModel[Kp*b + Ki/s + Kd*c*s, s];
(* Closed-loop from reference to output *)
Tr = SystemsModelSeriesConnect[Cr, G];
Den = SystemsModelSeriesConnect[Cy, G];
Tcl = SystemsModelFeedbackConnect[Tr, Den];
(* For disturbance at plant input: Y/D = G / (1 + G Cy) *)
Td = SystemsModelFeedbackConnect[G, Cy];
(* Step responses *)
tmax = 0.5;
refResp = OutputResponse[Tcl, UnitStep[t], {t, 0, tmax}];
distResp = OutputResponse[-Td, UnitStep[t], {t, 0, tmax}];
Plot[{refResp, distResp}, {t, 0, tmax},
PlotLegends -> {"reference step", "disturbance step"},
AxesLabel -> {"t", "y(t)"},
GridLines -> Automatic]
Mathematica can also be used to symbolically analyze how the weights \( b \) and \( c \) affect the numerator polynomials of \( Y(s)/R(s) \), thereby connecting algebraic structure to time-domain performance constraints.
10. Problems and Solutions
Problem 1 (Derive General 2-DOF Closed-Loop Expressions): For the 2-DOF architecture with plant \( G(s) \) and controller \( u(s) = C_r(s) r(s) - C_y(s) y(s) \), derive the closed-loop transfer functions \( Y(s)/R(s) \) and \( Y(s)/D(s) \) when the disturbance \( d(s) \) is added at the plant input.
Solution:
The plant equation is \( y(s) = G(s)\bigl(u(s) + d(s)\bigr) \). Substituting \( u(s) = C_r(s) r(s) - C_y(s) y(s) \) gives
\[ \begin{aligned} y(s) &= G(s)\bigl(C_r(s) r(s) - C_y(s) y(s) + d(s)\bigr) \\ &= G(s) C_r(s) r(s) - G(s) C_y(s) y(s) + G(s) d(s). \end{aligned} \]
Rearranging terms in \( y(s) \):
\[ \bigl(1 + G(s) C_y(s)\bigr) y(s) = G(s) C_r(s) r(s) + G(s) d(s). \]
Thus, dividing by \( 1 + G(s) C_y(s) \) and isolating contributions from \( r(s) \) and \( d(s) \), we obtain
\[ \frac{Y(s)}{R(s)} = \frac{G(s) C_r(s)}{1 + G(s) C_y(s)}, \qquad \frac{Y(s)}{D(s)} = \frac{G(s)}{1 + G(s) C_y(s)}. \]
The first depends on \( C_r(s) \) and \( C_y(s) \), while the second depends only on \( C_y(s) \).
Problem 2 (1-DOF as a Special Case): Show that if \( C_r(s) = C_y(s) = C(s) \), the general 2-DOF closed-loop reduces to the standard 1-DOF structure. Interpret the roles of numerator and denominator in that case.
Solution:
Substituting \( C_r(s) = C_y(s) = C(s) \) into the result of Problem 1 yields
\[ \frac{Y(s)}{R(s)} = \frac{G(s) C(s)}{1 + G(s) C(s)}, \qquad \frac{Y(s)}{D(s)} = \frac{G(s)}{1 + G(s) C(s)}. \]
Both reference tracking and disturbance rejection are governed by the same loop transfer function \( G(s) C(s) \). Therefore, any change in the controller to improve tracking also changes disturbance rejection and stability margins, and vice versa. The denominator \( 1 + G(s) C(s) \) determines the closed-loop poles (stability and transient speed), while the numerator \( G(s) C(s) \) shapes the steady-state gain and zero locations.
Problem 3 (Static Gains for a 2-DOF PID): Consider the 2-DOF PID of Section 3 with \( C_r(s) = K_p b + K_i/s + K_d c s \) and \( C_y(s) = K_p + K_i/s + K_d s \). Assume the robot joint plant \( G(s) \) is asymptotically stable and has nonzero DC gain \( G(0) \). Compute the steady-state gain \( \lim_{s \to 0} Y(s)/R(s) \) for a step reference, and show that it is independent of \( b \) and \( c \).
Solution:
For a step reference of amplitude \( r_0 \), the Laplace transform is \( R(s) = r_0/s \). The steady-state gain is
\[ \lim_{s \to 0} \frac{Y(s)}{R(s)} = \lim_{s \to 0} \frac{G(s) C_r(s)}{1 + G(s) C_y(s)}. \]
As \( s \to 0 \), the integral terms dominate: \( K_i/s \) is unbounded, while the proportional and derivative terms remain bounded. Hence
\[ C_r(s) \approx \frac{K_i}{s}, \qquad C_y(s) \approx \frac{K_i}{s}. \]
Therefore
\[ \frac{G(s) C_r(s)}{1 + G(s) C_y(s)} \approx \frac{G(s) \frac{K_i}{s}}{1 + G(s) \frac{K_i}{s}}. \]
Assuming \( G(s) \) has finite nonzero DC gain \( G(0) \), we obtain
\[ \lim_{s \to 0} \frac{G(s) \frac{K_i}{s}}{1 + G(s) \frac{K_i}{s}} = 1. \]
Hence \( \lim_{t \to \infty} y(t) = r_0 \) (zero steady-state error for a step), and the steady-state gain is independent of \( b \) and \( c \). These parameters affect transients but not the final value.
Problem 4 (Effect of Reference Prefilter on Overshoot): Let a stable closed-loop system without prefilter have step response \( y_0(t) \). Suppose we introduce a prefilter \( F(s) = 1/(T_f s + 1) \) in front of the reference. Show that the new step response \( y(t) \) can be written as the convolution of \( y_0(t) \) with the impulse response of the prefilter, and explain qualitatively why increasing \( T_f \) reduces overshoot.
Solution:
Denote the original closed-loop transfer function from reference to output by \( T_0(s) \), i.e., \( Y_0(s) = T_0(s) R(s) \). With prefilter, the new closed-loop is \( T(s) = F(s) T_0(s) \), so the step response is
\[ y(t) = \mathcal{L}^{-1}\bigl\{ T(s) \frac{1}{s} \bigr\} = \mathcal{L}^{-1}\bigl\{ F(s) \bigr\} * \mathcal{L}^{-1}\bigl\{ T_0(s) \frac{1}{s} \bigr\} = f(t) * y_0(t), \]
where \( f(t) = \frac{1}{T_f} e^{-t/T_f} \mathbf{1}_{t \ge 0} \) is the impulse response of the prefilter and \( * \) denotes convolution. As \( T_f \) increases, \( f(t) \) spreads out in time, effectively smoothing the rapid changes in \( y_0(t) \). This smoothing reduces the maximum slope and peak amplitude of the step response, thereby reducing overshoot at the price of a slower rise time.
Problem 5 (Design Flow for 2-DOF PID in a Robot Joint): Sketch a high-level design flow for tuning a 2-DOF PID controller for a robot joint position servo. Indicate where plant modeling, feedback tuning, and 2-DOF adjustment occur.
Solution (conceptual flow):
flowchart TD
M["Model or identify G(s) for joint"] --> FB["Tune feedback PID Cy(s) for stability and disturbance rejection"]
FB --> ANA["Check time and frequency responses"]
ANA --> ADJ["Adjust PID gains until margins and regulation specs are met"]
ADJ --> TD["Introduce 2-DOF weights b, c or prefilter F(s)"]
TD --> SIM["Simulate setpoint and disturbance scenarios"]
SIM --> FIN["Finalize parameters and implement on robot"]
The essential idea is to treat the feedback loop design as the primary step (stability and robustness), and the 2-DOF adjustment as a secondary step that fine-tunes tracking without compromising the achieved disturbance rejection.
11. Summary
In this lesson we connected two-degree-of-freedom controller structures to practical servo and process control applications. We derived the general closed-loop expressions, highlighting how \( C_y(s) \) determines stability and disturbance rejection while \( C_r(s) \) shapes the reference response. We studied 2-DOF PID designs for robot joint control, showed how reference prefilters are used in process control to reduce overshoot, and implemented these concepts in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica. The problems at the end reinforced the algebraic structure of 2-DOF systems and clarified the qualitative impact of prefilters and set-point weighting on performance.
12. References
- Horowitz, I. M., & Sidi, M. (1972). Synthesis of feedback systems with two degrees of freedom. International Journal of Control, 16(2), 287–309.
- Manabe, S. (1998). Two-degree-of-freedom PID controllers. International Journal of Control, 68(4), 741–756.
- Åström, K. J., & Hägglund, T. (1995). PID controllers: Theory, design, and tuning. ISA Transactions (selected theoretical papers).
- Kwon, W. H., & Han, S. H. (1999). A practical design method of two-degree-of-freedom control systems. Automatica, 35(6), 1125–1134.
- Chen, T., & Francis, B. A. (1995). Optimal sampled-data control systems. IEEE Transactions on Automatic Control, 40(3), 450–461.
- Middleton, R. H., & Goodwin, G. C. (1988). Adaptive control of time-varying systems. IEEE Transactions on Automatic Control, 33(2), 150–163.