Main Content

Loop-Shaping Controller Design

This example shows how to design a controller by specifying a desired shape for the open-loop response of the plant with the controller. The loopsyn command designs a controller that shapes the open-loop response to approximately match the target loop shape you provide. loopsyn lets you adjust the tradeoff between performance and robustness to obtain satisfactory time-domain responses while avoiding fragile designs with plant inversion or flexible mode cancellation.

In this example, you design a controller for an aircraft model.The example shows how varying the balance between performance and robustness affects loop shape and closed-loop response. The example then shows how to reduce the controller order while preserving desirable characteristics of the response.

Plant Model

This example uses the two-input, two-output NASA HiMAT aircraft model [1]. The aircraft is shown in the following diagram.

The control variables are the elevon and canard actuators (δe and δc). The output variables are the angle of attack (α) and attitude angle (θ). The model has six states, given by

x=[x1x2x3x4x5x6]=[α˙αθ˙θxexc],

where xe and xc are the elevator and canard actuator states, respectively. Using the following state-space matrices, create the model of this plant.

A = [ -2.2567e-02  -3.6617e+01  -1.8897e+01  -3.2090e+01   3.2509e+00  -7.6257e-01;
       9.2572e-05  -1.8997e+00   9.8312e-01  -7.2562e-04  -1.7080e-01  -4.9652e-03;
       1.2338e-02   1.1720e+01  -2.6316e+00   8.7582e-04  -3.1604e+01   2.2396e+01;
       0            0            1.0000e+00   0            0            0;
       0            0            0            0           -3.0000e+01   0;
       0            0            0            0            0           -3.0000e+01];
B = [0     0;
     0     0;
     0     0;
     0     0;
    30     0;
     0    30];
C = [0     1     0     0     0     0;
     0     0     0     1     0     0];
D = [0     0;
     0     0];

G = ss(A,B,C,D);
G.InputName = {'elevon','canard'};
G.OutputName = {'attack','attitude'};

Examine the singular values of the model.

sigma(G)

Figure contains an axes object. The axes object contains 2 objects of type line. This object represents G.

This plant is ill-conditioned, in the sense that there is a gap of about 40 dB between the largest and smallest singular values in the vicinity of the desired control bandwidth of 8 rad/s. Further, as a step plot shows, the open-loop response of this plant is unstable.

step(G)

Figure contains 4 axes objects. Axes object 1 with title From: elevon contains an object of type line. This object represents G. Axes object 2 contains an object of type line. This object represents G. Axes object 3 with title From: canard contains an object of type line. This object represents G. Axes object 4 contains an object of type line. This object represents G.

Initial Controller Design

To design a stabilizing controller for this plant, select a target loop shape. A typical loop shape has low gain at high frequencies for robustness, and high gain at low frequencies for performance. For the desired crossover frequency of 8 rad/s, a simple target loop shape that meets these requirements is Gd=8/s.

Gd = tf(8,[1 0]);
sigma(Gd,{0.1 100})
grid on

Figure contains an axes object. The axes object contains an object of type line. This object represents Gd.

Design the initial controller with loopsyn.

[K0,CL0,gamma0,info0] = loopsyn(G,Gd);
gamma0
gamma0 = 1.2849

The performance gamma is a measure of how well the loop shape with K0 matches the desired loop shape. Values near or below 1 indicate that G*K0 is close to Gd. Compare the achieved loop shape with the target.

L0 = G*K0;              
sigma(L0,"b",Gd,"r--",{.1,100});
grid
legend("L0 (actual loop shape)","Gd (target loop shape)");

Figure contains an axes object. The axes object contains 3 objects of type line. These objects represent L0 (actual loop shape), Gd (target loop shape).

The match is not very close at low frequencies, though it improves near crossover. Moreover, the two singular values are still somewhat far apart around crossover, such that there are effectively two crossover frequencies. Examine how this open-loop shape affects the closed-loop step response.

step(CL0,5)

Figure contains 4 axes objects. Axes object 1 with title From: In(1) contains an object of type line. This object represents CL0. Axes object 2 contains an object of type line. This object represents CL0. Axes object 3 with title From: In(2) contains an object of type line. This object represents CL0. Axes object 4 contains an object of type line. This object represents CL0.

The bump in attitude tracking (lower-right plot) is the result of the separation of the two singular values, leading to a response with two time constants. Also, there is significant coupling between attack and attitude. It is desirable to adjust the controller to reduce the bump in attitude tracking, reduce the coupling, and if possible reduce the overshoot in the attack response.

Design Controller for Performance

To improve the design, you can try changing the balance that loopsyn strikes between performance and robustness. To do so, use the alpha input argument to loopsyn. By default, loopsyn uses alpha = 0.5, which optimizes performance subject to the robustness being no worse than half the maximum achievable robustness. alpha = 0 optimizes for performance (mixsyn design). Setting alpha = 1 uses the robustness-maximizing ncfsyn design. First, consider the pure mixsyn design.

alpha = 0;
[K_mix,CL_mix,gamma_mix,info_mix] = loopsyn(G,Gd,alpha);
gamma_mix
gamma_mix = 0.7723

The gamma value indicates a much closer match to the target loop shape, which you can confirm by plotting the open-loop responses.

L_mix = G*K_mix;              
sigma(L0,"b",L_mix,"g",Gd,"r--",{.1,100});
grid
legend("L0 (inital design)","L_mix (mixsyn design)","Gd (target loop shape)");

Figure contains an axes object. The axes object contains 5 objects of type line. These objects represent L0 (inital design), L\_mix (mixsyn design), Gd (target loop shape).

This design roughly inverts the plant. As a result, the singular values of of L_mix converge near the crossover frequency and are generally much closer together than in the original plant. With this plant-inverting controller, the closed-loop response shows good performance, with minimal overshoot and cross-coupling.

step(CL0,CL_mix,5)
legend("Initial design","mixsyn design","Location","southeast")

Figure contains 4 axes objects. Axes object 1 with title From: In(1) contains 2 objects of type line. These objects represent Initial design, mixsyn design. Axes object 2 contains 2 objects of type line. These objects represent Initial design, mixsyn design. Axes object 3 with title From: In(2) contains 2 objects of type line. These objects represent Initial design, mixsyn design. Axes object 4 contains 2 objects of type line. These objects represent Initial design, mixsyn design.

However, this performance comes at the cost of robustness. Compare the stability margins of the system with the initial design and the mixsyn design.

DM0 = diskmargin(G,K0);
DM_mix = diskmargin(G,K_mix);
DM0.DiskMargin
ans = 0.1215
DM_mix.DiskMargin
ans = 0.0517

The plant-inverting design has poor robustness. For instance, if the smallest singular value of the plant model is 1% of the largest singular value, inverting the plant amplifies model errors by a factor of 100 in the direction of the smallest singular value. Thus, unless you have a highly accurate model, it is preferable to use a design with better robustness.

Design Controller for Robustness

At the opposite extreme is the pure ncfsyn design, optimized for robustness. Compute such a controller using alpha = 1, and examine the resulting stability, loop shape, and responses.

alpha = 1;
[K_ncf,CL_ncf,gamma_ncf,info_ncf] = loopsyn(G,Gd,alpha);
gamma_ncf
gamma_ncf = 2.8360
DM_ncf = diskmargin(G,K_ncf);
DM_ncf.DiskMargin
ans = 0.2201
L_ncf = G*K_ncf; 
sigma(L0,L_mix,L_ncf,Gd,"k--",{.1,100});
grid
legend("L0 (inital design)","L_mix (mixsyn design)","L_ncf (ncfsyn design)","Gd (target loop shape)");

Figure contains an axes object. The axes object contains 7 objects of type line. These objects represent L0 (inital design), L\_mix (mixsyn design), L\_ncf (ncfsyn design), Gd (target loop shape).

The increased value of gamma indicates poor performance, though the stability margin is improved, as expected. The singular-value plot shows that this controller inverts the plant even less than the initial, which is evident in that the separation of the singular values is roughly the same as it was for the open-loop plant. The separation of crossover frequencies results in slow and fast time constants in the step response, which is even poorer than the initial design. The kick resulting from the wide crossover region is now apparent in all four I/O channels.

step(CL0,CL_mix,CL_ncf,5)
legend("Initial design","mixsyn design","ncfsyn design","Location","southeast")

Figure contains 4 axes objects. Axes object 1 with title From: In(1) contains 3 objects of type line. These objects represent Initial design, mixsyn design, ncfsyn design. Axes object 2 contains 3 objects of type line. These objects represent Initial design, mixsyn design, ncfsyn design. Axes object 3 with title From: In(2) contains 3 objects of type line. These objects represent Initial design, mixsyn design, ncfsyn design. Axes object 4 contains 3 objects of type line. These objects represent Initial design, mixsyn design, ncfsyn design.

Choosing a Satisfactory Design

Thus, to improve on the default design, slightly favoring the mixsyn design without throwing away too much stability margin might yield a suitable design for this plant. You can control how much loopsyn favors performance or robustness by setting alpha to any value between 0 and 1. The default value used in the initial controller is alpha = 0.5. Try a value that slightly favors performance, and compare the results with the initial design.

alpha = 0.25;
[K,CL,gamma,info] = loopsyn(G,Gd,alpha);
gamma
gamma = 1.0147
L = G*K;            
sigma(L0,L,Gd,"k--",{.1,100});
grid
legend("L0 (inital design)","L (final design)","Gd (target loop shape)");

Figure contains an axes object. The axes object contains 5 objects of type line. These objects represent L0 (inital design), L (final design), Gd (target loop shape).

DM = diskmargin(G,K);
DM.DiskMargin
ans = 0.0928
step(CL0,CL,5)
legend("initial (alpha = 0.5)","final (alpha = 0.25)","Location","southeast")

Figure contains 4 axes objects. Axes object 1 with title From: In(1) contains 2 objects of type line. These objects represent initial (alpha = 0.5), final (alpha = 0.25). Axes object 2 contains 2 objects of type line. These objects represent initial (alpha = 0.5), final (alpha = 0.25). Axes object 3 with title From: In(2) contains 2 objects of type line. These objects represent initial (alpha = 0.5), final (alpha = 0.25). Axes object 4 contains 2 objects of type line. These objects represent initial (alpha = 0.5), final (alpha = 0.25).

The alpha = 0.25 design yields reasonably good performance, reducing coupling and eliminating the bump in the attitude response. It has a slightly smaller stability margin (disk margin of about 0.09, compared to about 0.125 for the initial design). For your application, you can select whatever value of alpha between 0 and 1 achieves an acceptable balance between performance and robustness.

Reduce Controller Order

It is sometimes possible to simplify the controller returned by loopsyn while preserving desirable characteristics of the system response. In this example, the controller K is ninth order.

order(K)
ans = 9

To see whether it is possible to simplify K, use the balred command.

balred(K)

Figure contains an axes object. The axes object with title Hankel Singular Values and Approximation Error contains 3 objects of type bar, line. These objects represent Unstable modes, Stable modes, Absolute error bound.

The plot shows the Hankel singular values of the controller, which indicates the relative energy contribution of each mode. The Hankel Singular value decreases sharply after sixth order, so try reducing the controller accordingly.

Kr = balred(K,6);
order(Kr)
ans = 6

Compare the singular values of the reduced and full-order controllers to confirm that the difference between them is small.

sigma(K,K-Kr,{1e-4,1e6})
legend("K (ninth order)","difference K-Kr")

Figure contains an axes object. The axes object contains 4 objects of type line. These objects represent K (ninth order), difference K-Kr.

You can also confirm that the reduced-order controller produces a virtually identical closed-loop response.

CLr = feedback(G*Kr,eye(2));
step(CL,CLr,5)
legend("K (ninth order)","Kr (sixth order)","Location","southeast")

Figure contains 4 axes objects. Axes object 1 with title From: In(1) contains 2 objects of type line. These objects represent K (ninth order), Kr (sixth order). Axes object 2 contains 2 objects of type line. These objects represent K (ninth order), Kr (sixth order). Axes object 3 with title From: In(2) contains 2 objects of type line. These objects represent K (ninth order), Kr (sixth order). Axes object 4 contains 2 objects of type line. These objects represent K (ninth order), Kr (sixth order).

Design Controller with Reduced Order

Knowing that a sixth-order controller is sufficient to achieve the desired responses, you can use loopsyn to design a new controller, specifying the target order with the ord input argument. This approach is an alternative to the previous approach of designing and a full-order controller followed by reduction.

Design a new sixth-order controller with alpha = 0.25 and compare the responses to the response obtained with the reduced controller.

alpha = 0.25;
[K6,CL6,gamma6,info6] = loopsyn(G,Gd,alpha,6);
step(CLr,CL6,5)
legend("Kr (reduced to 6th order)","K6 (computed at 6th order)","Location","southeast")

Figure contains 4 axes objects. Axes object 1 with title From: In(1) contains 2 objects of type line. These objects represent Kr (reduced to 6th order), K6 (computed at 6th order). Axes object 2 contains 2 objects of type line. These objects represent Kr (reduced to 6th order), K6 (computed at 6th order). Axes object 3 with title From: In(2) contains 2 objects of type line. These objects represent Kr (reduced to 6th order), K6 (computed at 6th order). Axes object 4 contains 2 objects of type line. These objects represent Kr (reduced to 6th order), K6 (computed at 6th order).

Designing the sixth-order controller directly yields a similar step response, although for this particular system this approach leads to some reduction in performance (gamma6 = 1.4, compared to gamma = 1.0 for the full-order, alpha = 0.25 controller K). However, for some systems, this approach can be better because it optimizes the lower-order controller itself, rather than removing potentially important dynamics from an optimized controller.

Conclusion

loopsyn lets you adjust the tradeoff between performance and robustness to strike a suitable balance for your application. You can try different values of alpha to find a controller that works for your requirements. You can then reduce controller order with balred, or use the ord argument of loopsyn to synthesize a lower-order controller directly.

References

[1] Safonov, M., A. Laub, and G. Hartmann. “Feedback Properties of Multivariable Systems: The Role and Use of the Return Difference Matrix.” IEEE Transactions on Automatic Control 26, no. 1 (February 1981): 47–65.

See Also

Related Topics