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:
Linearize a nonlinear Simulink® model on a grid of trim points.
Construct an LPV model from the gridded array of LTI models and trim offsets.
Design a controller on a grid of trim points. This grid may be different from the grid used for the linearization.
Test the controller using command-line LTI simulations (at frozen operating points) and LPV simulations (along a specified parameter trajectory).
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 (,), the body coordinates (,), the pitch angle , and the pitch rate . This figure summarizes the relationship between the inertial and body frames, the flight path angle , the incidence angle , and the pitch angle .
The airframe dynamics are nonlinear and the aerodynamic forces and moments depend on speed 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 varies between 700 and 1400 m/s. Use a 15-by-12 grid of linearly spaced (,) 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 (,), linearize the airframe dynamics at trim (zero normal acceleration and pitching moment). Doing so requires computing the elevator deflection and pitch rate that result in steady and .
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 (,).
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 (,) 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 with a proportional term on .
The tunable gains and are defined as gain surfaces with a linear dependence on (,).
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 .
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 (,) grids.
Tlpv = feedback(Glpv*Klpv,1,2,3,+1);
Tlpv = Tlpv(:,1); % from qref to plant outputs
Plot the closed-loop responses from to 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 of the LPV controller to deliver the trim deflection for the selected operating point. Since the LPV controller output is
must satisfy
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 trajectory. A first option is to use the approximate trajectory from the LTI simulation above (recall that and 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 , depend on the states , according to
For more accurate results, you can describe this dependency using the function .
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
.
Close the models.
bdclose("scdairframeOpenLoop") bdclose("scdairframeClosedLoop")
See Also
ssInterpolant
| sample
| linearize
(Simulink Control Design) | systune
| ndgrid