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
, , , , , x(0)=0.
Procedure
- Use and in turn to calculate unit-step responses on the same time axis. Record the responses and steady-state outputs side by side.
- For under , derive in terms of . Use to find satisfying , and show your calculations.
- Compare the directly calculated with the final values of both responses, and explain what the reference gain changed.
Results to retain
- Both step responses and steady-state values, and the equation used to obtain N.
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
- Do the directly calculated steady-state outputs match the final values of each simulation?
- Do closed-loop poles remain unchanged when N changes?
Trade-off between output error and input cost
Starting conditions
, , and . Use , .
Procedure
- Solve the CARE for each Q and calculate .
- Plot position and control input on the same time axis.
- Compare the integral of squared position error and the integral of squared input separately.
Results to retain
- Position and input plots and a comparison table of both integrals.
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
- Are both Q matrices positive definite, with ?
- Do all poles of have negative real parts?
Reference tracking with model error
Starting conditions
Design using nominal B, and change only the actual input gain to and . Use , , .
Procedure
- Find the poles and final step-response value for each actual model.
- Check whether N calculated from the nominal model also achieves the exact reference in every actual model.
- Distinguish the roles of reference-gain adjustment and integral-error feedback.
Results to retain
- Three response curves and a table of final values and errors.
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
- Were both the feedback and reference terms multiplied by the actual B_actual?
- Did you check stability before calculating final values?