Chapter 11: State Estimation for Control (Control-Facing Only)
Lesson 5: Lab: Observer-Based Control
In this lab we implement an observer-based state-feedback controller for a robot joint modeled in state space. Building on previous lessons on observer design and state estimation, we will (i) specify a simplified joint model, (ii) design a full-order observer and state-feedback gains, and (iii) implement and simulate the resulting dynamic compensator in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica. The focus is on understanding how estimation and control interact in closed loop.
1. Lab Objectives and Prerequisites
We consider the standard linear time-invariant (LTI) state-space model of a robot joint (derived from the manipulator dynamics and linearization tools from earlier chapters):
\[ \dot{\mathbf{x}}(t) = A \mathbf{x}(t) + B u(t), \quad y(t) = C \mathbf{x}(t), \]
where \(\mathbf{x}(t)\) collects joint angles and velocities, \(u(t)\) is the commanded joint torque, and \(y(t)\) is the measured output (e.g., encoder position).
The observer-based controller uses a full-order observer to reconstruct the state from noisy and partial measurements:
\[ \dot{\hat{\mathbf{x}}}(t) = A \hat{\mathbf{x}}(t) + B u(t) + L\big(y(t) - C \hat{\mathbf{x}}(t)\big), \]
which is then used inside a state-feedback law
\[ u(t) = -K \hat{\mathbf{x}}(t). \]
In this lesson we:
- Choose a physically motivated 2-state joint model suitable for observer-based control.
- Derive and design gains \(K\) and \(L\) via pole placement.
- Implement the dynamic compensator in several languages using simple numerical integration.
- Analyze the combined plant–observer closed-loop dynamics (separation principle in practice).
We assume familiarity with state-space models for mechanical systems, linearization around operating points, state-feedback design, and the observer design concepts developed in earlier lessons of this chapter.
2. Robot Joint Model for the Lab
Consider a single revolute joint of a manipulator around an operating point (e.g., gravity-compensated posture). Neglecting strong nonlinear couplings, a common local model is a damped second-order system driven by joint torque \(\tau(t)\):
\[ M \ddot{q}(t) + b \dot{q}(t) + k q(t) = \tau(t), \]
where \(M > 0\) is the equivalent inertia at the joint, \(b \ge 0\) is viscous damping, and \(k \ge 0\) is an effective stiffness term arising from linearization of gravity and elastic elements.
Define the state \(\mathbf{x} = [x_1\; x_2]^\top = [q\; \dot{q}]^\top\) and input \(u = \tau\). Then:
\[ \dot{x}_1 = x_2, \quad \dot{x}_2 = -\frac{k}{M} x_1 - \frac{b}{M} x_2 + \frac{1}{M} u. \]
In matrix form:
\[ A = \begin{bmatrix} 0 & 1 \\ -\dfrac{k}{M} & -\dfrac{b}{M} \end{bmatrix},\quad B = \begin{bmatrix} 0 \\[4pt] \dfrac{1}{M} \end{bmatrix},\quad C = \begin{bmatrix} 1 & 0 \end{bmatrix},\quad y = C \mathbf{x} = q. \]
This model is controllable and observable for all \(M > 0\) and generic \(b,k \ge 0\), so both state-feedback and observer design are feasible.
3. Observer-Based Control and Closed-Loop Structure
We use a full-order observer with gain matrix \(L \in \mathbb{R}^{2\times 1}\):
\[ \dot{\hat{\mathbf{x}}} = A \hat{\mathbf{x}} + B u + L\big(y - C \hat{\mathbf{x}}\big). \]
The control law is state feedback using the estimated state:
\[ u = -K \hat{\mathbf{x}}, \]
with \(K \in \mathbb{R}^{1\times 2}\). Define the estimation error \(\mathbf{e} = \mathbf{x} - \hat{\mathbf{x}}\). Subtracting the observer equation from the plant equation gives
\[ \dot{\mathbf{e}} = \dot{\mathbf{x}} - \dot{\hat{\mathbf{x}}} = \big(A\mathbf{x} + Bu\big) - \big(A \hat{\mathbf{x}} + Bu + L(y - C \hat{\mathbf{x}})\big) = (A - LC)\mathbf{e}. \]
Thus the error dynamics depend only on \(A - LC\) and are independent of \(K\). The state dynamics with the control substitution are
\[ \dot{\mathbf{x}} = A\mathbf{x} + B(-K\hat{\mathbf{x}}) = (A - BK)\mathbf{x} + BK\mathbf{e}. \]
In the combined state \(\mathbf{z} = [\mathbf{x}^\top\; \mathbf{e}^\top]^\top\), we obtain
\[ \begin{bmatrix} \dot{\mathbf{x}} \\[4pt] \dot{\mathbf{e}} \end{bmatrix} = \begin{bmatrix} A - BK & BK \\ 0 & A - LC \end{bmatrix} \begin{bmatrix} \mathbf{x} \\[4pt] \mathbf{e} \end{bmatrix}. \]
The closed-loop matrix is block upper-triangular, so its eigenvalues are precisely the union of the eigenvalues of \(A - BK\) (state-feedback dynamics) and \(A - LC\) (observer error dynamics). This is the separation principle in action.
flowchart TD
R["Reference joint position q_ref"] --> C["State-feedback controller (uses x_hat)"]
C --> U["Torque command u"]
U --> P["Robot joint (plant): x_dot = A x + B u"]
P --> Y["Encoder measurement y = q"]
Y --> O["Observer: x_hat_dot = A x_hat + B u + L (y - C x_hat)"]
O --> C
4. Numerical Design of \(K\) and \(L\) for a Joint
Choose representative physical parameters for a moderately heavy joint:
- \(M = 2.0\) (kg·m\(^2\))
- \(b = 0.8\) (N·m·s/rad)
- \(k = 5.0\) (N·m/rad)
Then
\[ A = \begin{bmatrix} 0 & 1 \\ -\dfrac{5}{2} & -\dfrac{0.8}{2} \end{bmatrix} = \begin{bmatrix} 0 & 1 \\ -2.5 & -0.4 \end{bmatrix}, \quad B = \begin{bmatrix} 0 \\[4pt] \dfrac{1}{2} \end{bmatrix}, \quad C = \begin{bmatrix} 1 & 0 \end{bmatrix}. \]
4.1 State-Feedback Gain \(K\)
Let \(K = \begin{bmatrix} k_1 & k_2 \end{bmatrix}\). The closed-loop matrix is
\[ A_{\text{cl}} = A - BK = \begin{bmatrix} 0 & 1 \\ -2.5 & -0.4 \end{bmatrix} - \begin{bmatrix} 0 \\[4pt] \dfrac{1}{2} \end{bmatrix} \begin{bmatrix} k_1 & k_2 \end{bmatrix} = \begin{bmatrix} 0 & 1 \\ -2.5 - \dfrac{k_1}{2} & -0.4 - \dfrac{k_2}{2} \end{bmatrix}. \]
The characteristic polynomial of \(A_{\text{cl}}\) is
\[ s^2 + \frac{0.4 + \dfrac{k_2}{2}}{} s + \Big(2.5 + \dfrac{k_1}{2}\Big) = s^2 + \frac{0.4 + \dfrac{k_2}{2}}{1} s + \Big(2.5 + \dfrac{k_1}{2}\Big). \]
We want a well-damped, faster response than the open loop. Choose a desired closed-loop natural frequency \(\omega_c = 4\) rad/s and damping ratio \(\zeta_c = 0.9\). The desired characteristic polynomial is
\[ s^2 + 2 \zeta_c \omega_c s + \omega_c^2 = s^2 + (2 \cdot 0.9 \cdot 4) s + 4^2 = s^2 + 7.2 s + 16. \]
Matching coefficients gives
\[ \frac{0.4 + \dfrac{k_2}{2}}{1} = 7.2, \quad 2.5 + \dfrac{k_1}{2} = 16. \]
Solving:
\[ \dfrac{k_2}{2} = 7.2 - 0.4 = 6.8 \Rightarrow k_2 = 13.6, \quad \dfrac{k_1}{2} = 16 - 2.5 = 13.5 \Rightarrow k_1 = 27.0. \]
Thus we use \(K = \begin{bmatrix} 27.0 & 13.6 \end{bmatrix}\).
4.2 Observer Gain \(L\)
Let \(L = \begin{bmatrix} \ell_1 \\ \ell_2 \end{bmatrix}\). The observer error dynamics are governed by \(A - LC\). With \(C = \begin{bmatrix} 1 & 0 \end{bmatrix}\),
\[ LC = \begin{bmatrix} \ell_1 \\ \ell_2 \end{bmatrix} \begin{bmatrix} 1 & 0 \end{bmatrix} = \begin{bmatrix} \ell_1 & 0 \\ \ell_2 & 0 \end{bmatrix}, \quad A - LC = \begin{bmatrix} -\ell_1 & 1 \\ -2.5 - \ell_2 & -0.4 \end{bmatrix}. \]
The characteristic polynomial of \(A - LC\) is
\[ s^2 + (\ell_1 + 0.4) s + \big(2.5 + \ell_2 + 0.4 \ell_1\big). \]
We choose observer poles approximately four times faster than the controller dynamics to ensure rapid convergence of the estimate without excessive sensitivity to noise. Let \(\omega_o = 16\) rad/s and \(\zeta_o = 0.9\), so the desired observer characteristic polynomial is
\[ s^2 + 2 \zeta_o \omega_o s + \omega_o^2 = s^2 + (2 \cdot 0.9 \cdot 16) s + 16^2 = s^2 + 28.8 s + 256. \]
Matching coefficients:
\[ \ell_1 + 0.4 = 28.8 \Rightarrow \ell_1 = 28.4, \]
\[ 2.5 + \ell_2 + 0.4 \ell_1 = 256 \Rightarrow \ell_2 = 256 - 2.5 - 0.4 \cdot 28.4 = 242.14. \]
So we choose \(L = \begin{bmatrix} 28.4 \\ 242.14 \end{bmatrix}\).
5. Implementation Algorithm (Simulation Loop)
In all implementations, we simulate the continuous-time dynamics with a fixed integration step \( \Delta t \) using an explicit Euler scheme. Let \(\mathbf{x}(t)\) be the plant state and \(\hat{\mathbf{x}}(t)\) the observer state. For a regulation task (equilibrium at \(\mathbf{x}_{\text{ref}} = 0\)), the continuous-time equations are
\[ u(t) = -K \hat{\mathbf{x}}(t), \quad \dot{\mathbf{x}}(t) = A \mathbf{x}(t) + B u(t), \quad \dot{\hat{\mathbf{x}}}(t) = A \hat{\mathbf{x}}(t) + B u(t) + L \big( C \mathbf{x}(t) - C \hat{\mathbf{x}}(t) \big). \]
The Euler discretization with step \(\Delta t\) is
\[ \mathbf{x}_{k+1} = \mathbf{x}_k + \Delta t\,\dot{\mathbf{x}}_k, \quad \hat{\mathbf{x}}_{k+1} = \hat{\mathbf{x}}_k + \Delta t\,\dot{\hat{\mathbf{x}}}_k, \]
where all derivatives are evaluated at time step \(k\).
flowchart TD
S["Start: choose A, B, C, K, L, dt, T_end"] --> I["Initialize x, x_hat, t = 0"]
I --> L1["Loop while t is below T_end"]
L1 --> C1["Compute control u = -K * x_hat"]
C1 --> P1["Compute x_dot = A * x + B * u"]
P1 --> O1["Compute x_hat_dot = A * x_hat + B * u + L * (y - C * x_hat)"]
O1 --> U1["Update x and x_hat with Euler step"]
U1 --> T1["Increase t by dt, log trajectories"]
T1 --> L1
L1 --> E["Stop loop and analyze plots"]
6. Python Implementation (NumPy + Matplotlib)
The following Python script simulates observer-based control of the
joint. It uses numpy for linear algebra and
matplotlib
for plotting. The integration method is explicit Euler for clarity.
import numpy as np
import matplotlib.pyplot as plt
# Physical and model parameters
M = 2.0
b = 0.8
k = 5.0
A = np.array([[0.0, 1.0],
[-k / M, -b / M]])
B = np.array([[0.0],
[1.0 / M]])
C = np.array([[1.0, 0.0]])
# State-feedback and observer gains from Section 4
K = np.array([[27.0, 13.6]]) # 1x2
L = np.array([[28.4],
[242.14]]) # 2x1
# Simulation settings
dt = 1e-3
T_end = 5.0
N = int(T_end / dt)
# Reference (regulation around zero; you can change q_ref to test tracking)
q_ref = 0.5
x_ref = np.array([[q_ref],
[0.0]])
# State initialization (true and estimated)
x = np.array([[0.0],
[0.0]])
x_hat = np.array([[0.0],
[0.0]])
t_vec = np.zeros(N)
q_vec = np.zeros(N)
q_hat_vec = np.zeros(N)
u_vec = np.zeros(N)
for k_step in range(N):
t = k_step * dt
t_vec[k_step] = t
# Output and estimation error in output
y = C @ x
y_hat = C @ x_hat
y_err = y - y_hat
# Control law (reference in state coordinates)
u = -K @ (x_hat - x_ref)
# Store for plotting
q_vec[k_step] = y.item()
q_hat_vec[k_step] = y_hat.item()
u_vec[k_step] = u.item()
# Continuous-time derivatives
x_dot = A @ x + B * u
x_hat_dot = A @ x_hat + B * u + L * y_err
# Euler integration
x = x + dt * x_dot
x_hat = x_hat + dt * x_hat_dot
# Plot results
plt.figure()
plt.plot(t_vec, q_vec, label="q (true)")
plt.plot(t_vec, q_hat_vec, linestyle="--", label="q_hat (estimate)")
plt.axhline(q_ref, linestyle=":", label="q_ref")
plt.xlabel("Time [s]")
plt.ylabel("Joint position [rad]")
plt.legend()
plt.grid(True)
plt.figure()
plt.plot(t_vec, u_vec)
plt.xlabel("Time [s]")
plt.ylabel("Torque command u [N*m]")
plt.grid(True)
plt.show()
You can vary the observer pole locations (by changing \(L\)) and the state-feedback poles (by changing \(K\)) and observe how estimation speed and control aggressiveness affect the transient.
7. C++ Implementation (Eigen-Based)
In C++, a convenient way to handle state-space computations is to use
the Eigen library. The snippet below simulates the same
observer-based controller using Euler integration.
#include <iostream>
#include <vector>
#include <Eigen/Dense>
int main() {
using Eigen::Matrix2d;
using Eigen::Vector2d;
using Eigen::RowVector2d;
// Model parameters
double M = 2.0;
double b = 0.8;
double k = 5.0;
Matrix2d A;
A << 0.0, 1.0,
-k / M, -b / M;
Vector2d B;
B << 0.0,
1.0 / M;
RowVector2d C;
C << 1.0, 0.0;
RowVector2d K;
K << 27.0, 13.6;
Vector2d L;
L << 28.4,
242.14;
double dt = 1e-3;
double T_end = 5.0;
int N = static_cast<int>(T_end / dt);
double q_ref = 0.5;
Vector2d x_ref;
x_ref << q_ref, 0.0;
Vector2d x; // true state
Vector2d x_hat; // estimated state
x.setZero();
x_hat.setZero();
std::vector<double> t_log;
std::vector<double> q_log;
std::vector<double> q_hat_log;
std::vector<double> u_log;
t_log.reserve(N);
q_log.reserve(N);
q_hat_log.reserve(N);
u_log.reserve(N);
for (int k_step = 0; k_step != N; ++k_step) {
double t = k_step * dt;
double y = C * x;
double y_hat = C * x_hat;
double y_err = y - y_hat;
Vector2d x_hat_minus_ref = x_hat - x_ref;
double u = -(K * x_hat_minus_ref)(0);
t_log.push_back(t);
q_log.push_back(y);
q_hat_log.push_back(y_hat);
u_log.push_back(u);
Vector2d x_dot = A * x + B * u;
Vector2d x_hat_dot = A * x_hat + B * u + L * y_err;
x += dt * x_dot;
x_hat += dt * x_hat_dot;
}
// Example: print final state
std::cout << "Final q = " << q_log.back()
<< ", final q_hat = " << q_hat_log.back() << std::endl;
return 0;
}
These arrays t_log, q_log, and
q_hat_log can be exported to CSV for plotting in a
scientific plotting tool or integrated into a real-time robot control
loop.
8. Java Implementation (Plain Arrays)
Java does not provide built-in matrix types, but for a 2-state system we can implement the dynamics explicitly via doubles and small arrays.
public class ObserverBasedControlLab {
public static void main(String[] args) {
double M = 2.0;
double b = 0.8;
double k = 5.0;
// A matrix entries
double a11 = 0.0;
double a12 = 1.0;
double a21 = -k / M;
double a22 = -b / M;
// B vector
double b1 = 0.0;
double b2 = 1.0 / M;
// C row vector
double c1 = 1.0;
double c2 = 0.0;
// State-feedback gains K = [k1 k2]
double k1 = 27.0;
double k2 = 13.6;
// Observer gains L = [l1; l2]
double l1 = 28.4;
double l2 = 242.14;
double dt = 1e-3;
double T_end = 5.0;
int N = (int) (T_end / dt);
double qRef = 0.5;
double xRef1 = qRef;
double xRef2 = 0.0;
double x1 = 0.0; // q
double x2 = 0.0; // q_dot
double xhat1 = 0.0; // q_hat
double xhat2 = 0.0; // q_dot_hat
for (int kStep = 0; kStep != N; ++kStep) {
double y = c1 * x1 + c2 * x2;
double yHat = c1 * xhat1 + c2 * xhat2;
double yErr = y - yHat;
double u = -(k1 * (xhat1 - xRef1) + k2 * (xhat2 - xRef2));
double xdot1 = a11 * x1 + a12 * x2 + b1 * u;
double xdot2 = a21 * x1 + a22 * x2 + b2 * u;
double xhatdot1 = a11 * xhat1 + a12 * xhat2 + b1 * u + l1 * yErr;
double xhatdot2 = a21 * xhat1 + a22 * xhat2 + b2 * u + l2 * yErr;
x1 += dt * xdot1;
x2 += dt * xdot2;
xhat1 += dt * xhatdot1;
xhat2 += dt * xhatdot2;
}
System.out.println("Final q = " + x1);
System.out.println("Final q_hat = " + xhat1);
}
}
This style maps directly onto embedded Java environments or higher-level robot middleware layers where a minimal dependency set is desired.
9. MATLAB / Simulink Implementation
In MATLAB, we can simulate the observer-based controller using simple matrix operations. This code can also be wrapped in an S-Function or implemented directly in Simulink using blocks for the plant, controller, and observer.
% Model parameters
M = 2.0;
b = 0.8;
k = 5.0;
A = [0 1;
-k/M -b/M];
B = [0;
1/M];
C = [1 0];
K = [27.0 13.6];
L = [28.4;
242.14];
dt = 1e-3;
T_end = 5.0;
N = floor(T_end / dt);
q_ref = 0.5;
x_ref = [q_ref; 0];
x = [0; 0];
x_hat = [0; 0];
t_log = zeros(N, 1);
q_log = zeros(N, 1);
q_hat_log = zeros(N, 1);
u_log = zeros(N, 1);
for k_step = 1:N
t = (k_step - 1) * dt;
t_log(k_step) = t;
y = C * x;
y_hat = C * x_hat;
y_err = y - y_hat;
u = -K * (x_hat - x_ref);
q_log(k_step) = y;
q_hat_log(k_step) = y_hat;
u_log(k_step) = u;
x_dot = A * x + B * u;
x_hat_dot = A * x_hat + B * u + L * y_err;
x = x + dt * x_dot;
x_hat = x_hat + dt * x_hat_dot;
end
figure;
plot(t_log, q_log, t_log, q_hat_log, '--', ...
t_log, q_ref * ones(size(t_log)), ':');
xlabel('Time [s]');
ylabel('Joint position [rad]');
legend('q (true)', 'q_hat (estimate)', 'q_{ref}');
grid on;
figure;
plot(t_log, u_log);
xlabel('Time [s]');
ylabel('Torque u [N*m]');
grid on;
In Simulink, you can implement the same structure by:
- Using a State-Space block for the plant with matrices \(A,B,C,0\).
- Implementing the observer equations in a separate State-Space block with state \(\hat{\mathbf{x}}\).
- Feeding back \(\hat{\mathbf{x}}\) through a Gain block set to \(K\) to compute \(u\).
- Connecting the measured output \(y\) and estimated output \(C\hat{\mathbf{x}}\) to the observer input.
10. Wolfram Mathematica Implementation
Mathematica can simulate the coupled plant and observer via symbolic or
numeric differential equations. The following example uses
NDSolve
to integrate the dynamics with the same gains.
M = 2.0;
b = 0.8;
k = 5.0;
A = { {0, 1}, {-k/M, -b/M} };
B = { {0}, {1/M} };
C = { {1, 0} };
K = {27.0, 13.6} }; (* 1x2 *)
L = { {28.4}, {242.14} }; (* 2x1 *)
qRef = 0.5;
(* State variables: q[t], qd[t], qhat[t], qdhat[t] *)
vars = {q, qd, qhat, qdhat};
u[t_] := - (K . { {qhat[t]}, {qdhat[t]} } - K . { {qRef}, {0} })[[1, 1]];
y[t_] := q[t];
yHat[t_] := qhat[t];
eqs = {
q'[t] == qd[t],
qd'[t] == (-k/M) q[t] + (-b/M) qd[t] + (1/M) u[t],
qhat'[t] == 0*qhat[t] + 1*qdhat[t] + L[[1, 1]] (y[t] - yHat[t]),
qdhat'[t] == (-k/M) qhat[t] + (-b/M) qdhat[t] + (1/M) u[t] +
L[[2, 1]] (y[t] - yHat[t]),
q[0] == 0, qd[0] == 0,
qhat[0] == 0, qdhat[0] == 0
};
sol = NDSolve[eqs, vars, {t, 0, 5.0}][[1]];
Plot[
Evaluate[{q[t], qhat[t], qRef} /. sol],
{t, 0, 5},
PlotLegends -> {"q(t)", "q_hat(t)", "q_ref"},
AxesLabel -> {"t [s]", "position [rad]"}
]
Plot[
Evaluate[u[t] /. sol],
{t, 0, 5},
AxesLabel -> {"t [s]", "u(t) [N*m]"}
]
The structure mirrors previous implementations, highlighting how observer-based control can be ported between environments while keeping the underlying mathematics unchanged.
11. Problems and Solutions
Problem 1 (Error Dynamics and Separation Principle): For the general LTI system \(\dot{\mathbf{x}} = A \mathbf{x} + B u\), \(y = C \mathbf{x}\), consider the observer-based controller \(u = -K \hat{\mathbf{x}}\), \(\dot{\hat{\mathbf{x}}} = A \hat{\mathbf{x}} + B u + L(y - C \hat{\mathbf{x}})\). Derive the dynamics of the estimation error \(\mathbf{e} = \mathbf{x} - \hat{\mathbf{x}}\) and show that the eigenvalues of the closed-loop system are the union of the eigenvalues of \(A - BK\) and \(A - LC\).
Solution: The error dynamics satisfy
\[ \dot{\mathbf{e}} = \dot{\mathbf{x}} - \dot{\hat{\mathbf{x}}} = (A \mathbf{x} + B u) - (A \hat{\mathbf{x}} + B u + L(y - C \hat{\mathbf{x}})). \]
Since \(y = C \mathbf{x}\), we have
\[ \dot{\mathbf{e}} = A(\mathbf{x} - \hat{\mathbf{x}}) - L(C \mathbf{x} - C \hat{\mathbf{x}}) = (A - LC)\mathbf{e}. \]
For the state \(\mathbf{x}\), substituting \(u = -K \hat{\mathbf{x}}\) gives
\[ \dot{\mathbf{x}} = A \mathbf{x} + B(-K \hat{\mathbf{x}}) = (A - BK)\mathbf{x} + BK(\mathbf{x} - \hat{\mathbf{x}}) = (A - BK)\mathbf{x} + BK \mathbf{e}. \]
In augmented coordinates \(\mathbf{z} = [\mathbf{x}^\top\; \mathbf{e}^\top]^\top\),
\[ \begin{bmatrix} \dot{\mathbf{x}} \\[4pt] \dot{\mathbf{e}} \end{bmatrix} = \begin{bmatrix} A - BK & BK \\ 0 & A - LC \end{bmatrix} \begin{bmatrix} \mathbf{x} \\[4pt] \mathbf{e} \end{bmatrix}. \]
This matrix is block upper-triangular, so its eigenvalues are the union of the eigenvalues of \(A - BK\) and \(A - LC\). Hence controller and observer can be designed independently, which is the separation principle.
Problem 2 (State-Feedback Design for the Joint): Using the joint model of Section 2 with \(M = 2.0\), \(b = 0.8\), and \(k = 5.0\), design a state-feedback gain \(K = [k_1\; k_2]\) that yields the closed-loop characteristic polynomial \(s^2 + 7.2 s + 16\).
Solution: From Section 4 we computed
\[ A_{\text{cl}} = A - BK = \begin{bmatrix} 0 & 1 \\ -2.5 - \dfrac{k_1}{2} & -0.4 - \dfrac{k_2}{2} \end{bmatrix}, \]
whose characteristic polynomial is
\[ s^2 + \Big(0.4 + \dfrac{k_2}{2}\Big) s + \Big(2.5 + \dfrac{k_1}{2}\Big). \]
Matching to \(s^2 + 7.2 s + 16\) gives the equations
\[ 0.4 + \dfrac{k_2}{2} = 7.2, \quad 2.5 + \dfrac{k_1}{2} = 16. \]
Solving yields \(k_2 = 13.6\), \(k_1 = 27.0\), so \(K = [27.0\; 13.6]\).
Problem 3 (Observer Gain Calculation): For the same joint model, design an observer gain \(L\) such that the observer error dynamics have characteristic polynomial \(s^2 + 28.8 s + 256\). Use \(C = [1\; 0]\).
Solution: With \(L = [\ell_1\; \ell_2]^\top\), the error dynamics matrix is
\[ A - LC = \begin{bmatrix} -\ell_1 & 1 \\ -2.5 - \ell_2 & -0.4 \end{bmatrix}, \]
whose characteristic polynomial is
\[ s^2 + (\ell_1 + 0.4) s + \big(2.5 + \ell_2 + 0.4 \ell_1\big). \]
Matching to \(s^2 + 28.8 s + 256\) gives
\[ \ell_1 = 28.8 - 0.4 = 28.4, \]
\[ 2.5 + \ell_2 + 0.4 \ell_1 = 256 \Rightarrow \ell_2 = 256 - 2.5 - 0.4 \cdot 28.4 = 242.14. \]
Therefore \(L = [28.4\; 242.14]^\top\).
Problem 4 (Effect of Observer Speed): Consider keeping \(K\) fixed as in Problem 2, but placing the observer poles only slightly faster than the controller (e.g., at the poles of \(s^2 + 10 s + 25\)). Discuss qualitatively what happens to the transient response and robustness to measurement noise compared to the design in Problem 3.
Solution: With slower observer poles (closer to the controller poles), the estimation error decays more slowly. This leads to:
- Slower convergence of \(\hat{\mathbf{x}}\) to \(\mathbf{x}\), so the early closed-loop behavior more strongly reflects the initial estimation error.
- The effective input to the plant is based on a less accurate state estimate, producing larger overshoot or longer settling time compared to a faster observer.
- However, slower observer poles reduce amplification of sensor noise, because the observer acts as a lower-bandwidth filter on measurement errors.
In practice, observers are placed a few times faster than the controller but not arbitrarily fast, balancing estimation speed against noise amplification and numerical sensitivity.
Problem 5 (Closed-Loop Eigenvalues): Using the gains \(K\) and \(L\) found above, compute the eigenvalues of \(A - BK\) and \(A - LC\), and explain why the eigenvalues of the \(4 \times 4\) augmented closed-loop matrix must equal this combined set.
Solution: By construction, the characteristic polynomial of \(A - BK\) is \(s^2 + 7.2 s + 16\), so its eigenvalues are the roots of this polynomial (a pair of complex conjugates with real part \(-3.6\)). The characteristic polynomial of \(A - LC\) is \(s^2 + 28.8 s + 256\), whose roots are a pair of complex conjugates with real part \(-14.4\). The augmented closed-loop matrix
\[ \begin{bmatrix} A - BK & BK \\ 0 & A - LC \end{bmatrix} \]
is block upper-triangular, so its characteristic polynomial is the product of the characteristic polynomials of the diagonal blocks. Therefore the full eigenvalue set consists of the two eigenvalues of \(A - BK\) together with the two eigenvalues of \(A - LC\), confirming the separation principle.
12. Summary
In this lab we implemented a full-order observer-based state-feedback controller for a simplified robot joint model. Starting from the linearized second-order dynamics, we designed state-feedback and observer gains via pole placement, examined the resulting closed-loop and error dynamics, and implemented the dynamic compensator in several programming environments. The key structural insight is the separation of controller and observer design through the block upper-triangular closed-loop matrix. This lab provides a practical bridge between the theoretical observer design of earlier lessons and real-time control implementations that will be refined in subsequent chapters on digital control and real-time scheduling.
13. References
- Luenberger, D.G. (1964). Observers for multivariable systems. IEEE Transactions on Automatic Control, 9(2), 190–197.
- Kalman, R.E. (1960). Contributions to the theory of optimal control. Bol. Soc. Mat. Mexicana, 5, 102–119.
- Wonham, W.M. (1968). On pole assignment in multi-input controllable linear systems. IEEE Transactions on Automatic Control, 13(6), 660–665.
- Anderson, B.D.O., & Moore, J.B. (1971). Linear optimal control. Prentice-Hall (selected chapters on observers and separation).
- Kailath, T. (1968). An innovations approach to least-squares estimation — Part I: Linear filtering in additive white noise. IEEE Transactions on Automatic Control, 13(6), 646–655.
- Triggiani, R. (1975). On the stabilizability problem in Banach space. Journal of Mathematical Analysis and Applications, 52(3), 383–403. (Abstract treatment of stabilizability and detectability.)
- Zames, G. (1966). On the input-output stability of time-varying nonlinear feedback systems. IEEE Transactions on Automatic Control, 11(2), 228–238. (Foundational stability ideas underlying observer-based designs.)