Reference Tracking and Optimal Control

English translation

Learning objectives

Examine why reference error remains even with a stable controller, and compare tracking performance with control-input cost.

Choosing reference gain for reference tracking

Starting conditions

A=[01−2−3]A=\begin{bmatrix}0&1\\-2&-3\end{bmatrix}, B=[01]B=\begin{bmatrix}0\\1\end{bmatrix}, C=[10]C=\begin{bmatrix}1&0\end{bmatrix}, K=[63]K=\begin{bmatrix}6&3\end{bmatrix}, r=1r=1, x(0)=0.

Procedure

  1. Use N=1N=1 and N=8N=8 in turn to calculate unit-step responses on the same time axis. Record the responses and steady-state outputs side by side.
  2. For 0=(A−BK)xss+BNr0=(A-BK)x_{ss}+BNr under r=1r=1, derive xssx_{ss} in terms of NN. Use yss=Cxssy_{ss}=Cx_{ss} to find NN satisfying yss=1y_{ss}=1, and show your calculations.
  3. Compare the directly calculated NN with the final values of both responses, and explain what the reference gain changed.

Results to retain

import numpy as np
from scipy.signal import place_poles,StateSpace,lsim
from scipy.linalg import solve_continuous_are
import matplotlib.pyplot as plt
A=np.array([[0.,1.],[-2.,-3.]])
B=np.array([[0.],[1.]]);C=np.array([[1.,0.]])
K=place_poles(A,B,[-2.,-4.]).gain_matrix
N=float((-1/(C@np.linalg.solve(A-B@K,B))).item())
print('K:',K,'N:',N);assert np.isclose(N,8)
t=np.linspace(0,5,1001)
for gain in [1.,N]:
    _,y,_=lsim(StateSpace(A-B@K,B*gain,C,0),np.ones(len(t)),t)
    plt.plot(t,y,label=f'N={gain:g}')
plt.xlabel('Time [s]');plt.ylabel('Position');plt.legend();plt.grid()

Record results and self-check

N:

Steady-state value:

Closed-loop Pole:

Points to check

Trade-off between output error and input cost

Starting conditions

Q=diag⁡(q,1)Q=\operatorname{diag}(q,1), q∈{1,25}q\in\{1,25\}, and R=[1]R=\begin{bmatrix}1\end{bmatrix}. Use x(0)=[10]x(0)=\begin{bmatrix}1\\0\end{bmatrix}, r=0r=0.

Procedure

  1. Solve the CARE for each Q and calculate K=R−1B⊤PK=R^{-1}B^\top P.
  2. Plot position and control input on the same time axis.
  3. Compare the integral of squared position error and the integral of squared input separately.

Results to retain

from scipy.linalg import solve_continuous_are
for q in [1.,25.]:
    Q=np.diag([q,1.]);P=solve_continuous_are(A,B,Q,[[1.]])
    Kq=B.T@P
    _,x,_=lsim(StateSpace(A-B@Kq,np.zeros((2,1)),np.eye(2),np.zeros((2,1))),np.zeros(len(t)),t,X0=[1,0])
    u=-(x@Kq.T).ravel()
    print(q,Kq,np.trapezoid(x[:,0]**2,t),np.trapezoid(u**2,t))
    plt.plot(t,x[:,0],label=f'q={q:g}')
plt.xlabel('Time [s]');plt.ylabel('Position');plt.legend();plt.grid()
# TODO: Add control-input plots and pole checks.

Record results and self-check

q:

K:

Position-error integral:

Input cost:

Points to check

Reference tracking with model error

Starting conditions

Design using nominal B, and change only the actual input gain to Bactual=gBB_{\mathrm{actual}}=gB and g∈{.8,1,1.2}g\in\{.8,1,1.2\}. Use K=[63]K=\begin{bmatrix}6&3\end{bmatrix}, N=8N=8, r=1r=1.

Procedure

  1. Find the poles and final step-response value for each actual model.
  2. Check whether N calculated from the nominal model also achieves the exact reference in every actual model.
  3. Distinguish the roles of reference-gain adjustment and integral-error feedback.

Results to retain

K=np.array([[6.,3.]])
for g in [.8,1.,1.2]:
    Ba=g*B;Acl=A-Ba@K
    steady=float((-C@np.linalg.solve(Acl,Ba*8)).item())
    _,y,_=lsim(StateSpace(Acl,Ba*8,C,0),np.ones(len(t)),t)
    print(g,np.linalg.eigvals(Acl),steady);plt.plot(t,y,label=str(g))
plt.xlabel('Time [s]');plt.ylabel('Position');plt.legend();plt.grid()
# TODO: Explain why error remains and what integral feedback would additionally address.

Record results and self-check

Input gain:

Pole:

Final value:

Tracking error:

Points to check

Official examples and data