Main Content

Design and Validate Gain-Scheduled Controller for Nonlinear Aircraft Pitch Dynamics

This example shows how to design and validate gain-scheduled controller for a nonlinear system using a linear parameter-varying state-space model. This example is based on the Approximate Nonlinear Behavior Using Array of LTI Systems (Simulink Control Design) example, and shows how to approximate the nonlinear behavior at the command line.

In this example, you:

  1. Linearize a nonlinear Simulink® model on a grid of trim points.

  2. Construct an LPV model from the gridded array of LTI models and trim offsets.

  3. Design a controller on a grid of trim points. This grid may be different from the grid used for the linearization.

  4. Test the controller using command-line LTI simulations (at frozen operating points) and LPV simulations (along a specified parameter trajectory).

  5. Test the controller on the nonlinear plant in Simulink.

Aircraft Model

Consider a three-degree-of-freedom model of the pitch axis dynamics of an airframe. The states are the Earth coordinates (Xe,Ze), the body coordinates (u,w), the pitch angle θ, and the pitch rate q=θ˙. This figure summarizes the relationship between the inertial and body frames, the flight path angle γ, the incidence angle α, and the pitch angle θ.

gsautopilot.png

The airframe dynamics are nonlinear and the aerodynamic forces and moments depend on speed V and incidence α. The scdairframeOpenLoop model describes these dynamics.

open_system("scdairframeOpenLoop")

Range Of Operating Conditions

Assume that the incidence α varies between –20 and 20 degrees and that the speed V varies between 700 and 1400 m/s. Use a 15-by-12 grid of linearly spaced (α,V) pairs for scheduling.

nA = 15;   % number of alpha values
nV = 12;   % number of V values
alphaRange = linspace(-20,20,nA)*pi/180;
VRange = linspace(700,1400,nV);
[alpha,V] = ndgrid(alphaRange,VRange);

Batch Trimming

For each flight condition (α,V), linearize the airframe dynamics at trim (zero normal acceleration and pitching moment). Doing so requires computing the elevator deflection δ and pitch rate q that result in steady w and q.

for ct = 1:nA*nV
   alpha_ini = alpha(ct);      % Incidence [rad]
   v_ini = V(ct);              % Speed [m/s]

   % Specify trim condition
   opspec(ct) = operspec("scdairframeOpenLoop");

   % Xe,Ze: known, not steady.
   opspec(ct).States(1).Known = [1;1];
   opspec(ct).States(1).SteadyState = [0;0];

   % u,w: known, w steady
   opspec(ct).States(3).Known = [1 1];
   opspec(ct).States(3).SteadyState = [0 1];

   % theta: known, not steady
   opspec(ct).States(2).Known = 1;
   opspec(ct).States(2).SteadyState = 0;

   % q: unknown, steady
   opspec(ct).States(4).Known = 0;
   opspec(ct).States(4).SteadyState = 1;

end
opspec = reshape(opspec,[nA nV]);

Trim the model for all of the operating point specifications.

opt = findopOptions("DisplayReport","off","OptimizerType","lsqnonlin");
opt.OptimizationOptions.Algorithm = "trust-region-reflective";
[op,report] = findop("scdairframeOpenLoop",opspec,opt);

Batch Linearization

To linearize the model, first specify linearization input and output points.

io = [linio("scdairframeOpenLoop/delta",1,"in");            % delta
      linio("scdairframeOpenLoop/Airframe Model",1,"out");  % alpha
      linio("scdairframeOpenLoop/Airframe Model",2,"out");  % V
      linio("scdairframeOpenLoop/Airframe Model",3,"out");  % q
      linio("scdairframeOpenLoop/Airframe Model",4,"out");  % az
      linio("scdairframeOpenLoop/Airframe Model",5,"out")]; % gamma

Linearize the model for each of the trim conditions. Store linearization offset information in the info structure.

linOpt = linearizeOptions("StoreOffsets",true);
[G,~,info] = linearize("scdairframeOpenLoop",op,io,linOpt);
G = reshape(G,[nA nV]);
G.u = 'delta';
G.y = {'alpha','V','q','az','gamma'};
G.SamplingGrid = struct("alpha",alpha,"V",V);

G is a 15-by-12 array of linearized plant models at the 180 flight conditions (α,V).

bodemag(G(3:5,:,:,:))
title("Variations in airframe dynamics")

Use ssInterpolant to construct an LPV model that interpolates these linearized models between grid points.

offsets = info.Offsets;
Glpv = ssInterpolant(G,offsets);

Control Design: Pitch-Axis Stability Augmentation

The (α,V) grid for control design is coarser and on a smaller range than for the plant model.

nAcd = 5;   % number of alpha values
nVcd = 3;   % number of V values
alphacd = linspace(-10,10,nAcd)*pi/180;
Vcd = linspace(900,1200,nVcd);
[alphacd,Vcd] = ndgrid(alphacd,Vcd);
Ncd = nAcd*nVcd;

Use a 2-DOF gain-scheduled architecture for the pitch rate loop. This combines an I-only term on the error signal e=qref-q with a proportional term on q.

δ=Kis(qref-q)+Kpq

The tunable gains Kp and Ki are defined as gain surfaces with a linear dependence on (α,V).

s = tf("s");
Domain = struct("alpha",alphacd,"V",Vcd);
ShapeFcn = @(x,y) [x y];
Ki = tunableSurface("Ki",-1,Domain,ShapeFcn);
Kp = tunableSurface("Kp",1,Domain,ShapeFcn);
K = [Ki/s Kp] * [1 -1;0 1];
K.InputName = {'qref';'q'};
K.OutputName = {'delta'};

Sample the LPV model of the airframe over the tuning grid (alphacd,Vcd) and build the closed-loop model.

[Ga,Goffsets] = sample(Glpv,[],alphacd,Vcd);
T0 = connect(Ga,K,'qref','q','delta');
Warning: The following block outputs are not used: alpha,V,az,gamma.

Tune the gain surfaces subject to step response and loop-shaping requirements.

R1 = TuningGoal.StepTracking("qref","q",1/2);
R2 = TuningGoal.MaxLoopGain("delta",50,1);
R3 = TuningGoal.MaxGain("qref","q",tf(15,[1 0.01]));
T = systune(T0,[R1 R2],R3);
Final: Soft = 4.94, Hard = 0.19413, Iterations = 30
Warning: StepTracking goal: Feedback configuration has fixed integrators that cannot be stabilized with available tuning parameters. Make sure these are modeling artifacts rather than physical instabilities.
showTunable(T)
Ki =
   -4.4209   -0.0712    1.2825
-----------------------------------
Kp =
    1.1167   -0.0000   -0.2609

View the tuning results.

viewGoal([R1 R2 R3],T)

The tuned gain surfaces show a weak dependence on α and a stronger dependence on V.

Ki = setBlockValue(Ki,T);
clf
subplot(211)
viewSurf(Ki)
Kp = setBlockValue(Kp,T);
subplot(212)
viewSurf(Kp)

Build the LPV controller. Include the trim deflection as output offset so that the controller performs an incremental correction around trim.

Ka = ss(setBlockValue(K,T));
Koffsets = reshape(struct("y",{Goffsets.u}),size(Goffsets));
Klpv = ssInterpolant(Ka,Koffsets);

Build the closed-loop LPV model. Note that the plant and controller are sampled and interpolated on two different (α,V) grids.

Tlpv = feedback(Glpv*Klpv,1,2,3,+1);
Tlpv = Tlpv(:,1); % from qref to plant outputs

Plot the closed-loop responses from qref to q over the design grid.

clf
step(sample(Tlpv(3,:),[],alphacd,Vcd),6);
grid
title("Closed-loop response qref to q on design grid")

Nonlinear Simulation

To compare the nonlinear and LPV simulation, simulate the step response of the pitch rate loop initialized from one of the trim operating points. This trim condition is chosen on the denser grid and does not belong to the design grid.

aidx = 9;
Vidx = 6;
alpha0 = alphaRange(aidx);
V0 = VRange(Vidx);
q0 = offsets(aidx,Vidx).x(4);

Pick the initial state xK0 of the LPV controller to deliver the trim deflection δ0 for the selected operating point. Since the LPV controller output is

δ=δ0+CKxK+DK,2q,

xK0 must satisfy

CKxK0+DK,2q0=0.

K0 = sample(Klpv,[],alpha0,V0);
xK0 = -K0.C\K0.D(2)*q0;

Starting from the trim condition for (alpha0,V0), apply a step change at t=0 from q0 to q0+0.05.

tstep = 0;
rstep0 = q0;
rStepAmp = 0.05;
rstepf = rstep0+rStepAmp;
Tf = 5;

Open the closed-loop Simulink model and initialize the nonlinear simulation.

open_system("scdairframeClosedLoop")

alpha_ini = alpha0;
v_ini = V0;
q_ini = q0;
yK0 = reshape([Koffsets.y],[1 1 nAcd nVcd]);

Acquire the nonlinear simulation results.

sim("scdairframeClosedLoop",[0 tstep+Tf]);
tsim = y.Time;
ysim = y.Data;

For comparison, first compute the LTI response at this operating condition.

t = linspace(0,Tf,250);
y = step(sample(Tlpv,[],alpha0,V0),t);
ylin = [alpha0,V0,q0] + rStepAmp * y(:,1:3);

LPV Simulation

Compute the response with the LPV approximation Glpv of the nonlinear airframe model. To do this you need the p=(α,V) trajectory. A first option is to use the approximate trajectory from the LTI simulation above (recall that α and V are the first two outputs of Tlpv).

p_ideal = ylin(:,1:2);

The initial state of the plant is available from the trim analysis. You obtain overall initial state xinit by appending the initial state xK0 of Klpv.

xinit = [offsets(aidx,Vidx).x ; xK0];

Simulate the LPV response with the approximate parameter trajectory p_ideal.

qref = rstepf*ones(1,numel(t));
y1 = lsim(Tlpv,qref,t,xinit,p_ideal);

The true parameter trajectory is endogenous since α,V depend on the states u,w according to

tan(α)=wu,

V=u2+w2.

For more accurate results, you can describe this dependency using the function p=F(t,x,u).

pFcn = @(t,x,u) [atan2(x(3),x(2)) ; sqrt(x(2)^2+x(3)^2)];
[y2,~,~,p2] = lsim(Tlpv,qref,t,xinit,pFcn);

Compare the simulation results.

figure(1)
clf
subplot(411)
plot(t,y1(:,1),t,y2(:,1),t,ylin(:,1),tsim,ysim(:,1),"k--")
ylabel("alpha")
grid on
title("Linear, LPV, and Nonlinear Simulations")
legend("p ideal","p true","Linear","Nonlinear","location","southeast")
subplot(412)
plot(t,y1(:,2),t,y2(:,2),t,ylin(:,2),tsim,ysim(:,2),"k--")
ylabel("V")
grid on
subplot(413)
plot(t,y1(:,3),t,y2(:,3),t,ylin(:,3),tsim,ysim(:,3),"k--")
ylabel("q")
grid on
subplot(414)
plot(t,y1(:,5),t,y2(:,5),tsim,ysim(:,5),"k--")
ylabel("gamma")
grid on

The LPV simulations are very close to the nonlinear simulation, confirming that the LPV model of the airframe is an effective surrogate and that the gain-scheduled LPV controller is performing well. As expected, the LPV simulation using the true parameter trajectory is slightly more accurate than its surrogate using p_ideal.

See Also

| | (Simulink Control Design) | |

Related Topics