|
| 1 | +# mrac_siso_lyapunov.py |
| 2 | +# Johannes Kaisinger, 3 July 2023 |
| 3 | +# |
| 4 | +# Demonstrate a MRAC example for a SISO plant using Lyapunov rule. |
| 5 | +# Based on [1] Ex 5.7, Fig 5.12 & 5.13. |
| 6 | +# Notation as in [2]. |
| 7 | +# |
| 8 | +# [1] K. J. Aström & B. Wittenmark "Adaptive Control" Second Edition, 2008. |
| 9 | +# |
| 10 | +# [2] Nhan T. Nguyen "Model-Reference Adaptive Control", 2018. |
| 11 | + |
| 12 | +import numpy as np |
| 13 | +import scipy.signal as signal |
| 14 | +import matplotlib.pyplot as plt |
| 15 | + |
| 16 | +import control as ct |
| 17 | + |
| 18 | +# Plant model as linear state-space system |
| 19 | +A = -1 |
| 20 | +B = 0.5 |
| 21 | +C = 1 |
| 22 | +D = 0 |
| 23 | + |
| 24 | +io_plant = ct.ss(A, B, C, D, |
| 25 | + inputs=('u'), outputs=('x'), states=('x'), name='plant') |
| 26 | + |
| 27 | +# Reference model as linear state-space system |
| 28 | +Am = -2 |
| 29 | +Bm = 2 |
| 30 | +Cm = 1 |
| 31 | +Dm = 0 |
| 32 | + |
| 33 | +io_ref_model = ct.ss(Am, Bm, Cm, Dm, |
| 34 | + inputs=('r'), outputs=('xm'), states=('xm'), name='ref_model') |
| 35 | + |
| 36 | +# Adaptive control law, u = kx*x + kr*r |
| 37 | +kr_star = (Bm)/B |
| 38 | +print(f"Optimal value for {kr_star = }") |
| 39 | +kx_star = (Am-A)/B |
| 40 | +print(f"Optimal value for {kx_star = }") |
| 41 | + |
| 42 | +def adaptive_controller_state(_t, xc, uc, params): |
| 43 | + """Internal state of adaptive controller, f(t,x,u;p)""" |
| 44 | + |
| 45 | + # Parameters |
| 46 | + gam = params["gam"] |
| 47 | + signB = params["signB"] |
| 48 | + |
| 49 | + # Controller inputs |
| 50 | + r = uc[0] |
| 51 | + xm = uc[1] |
| 52 | + x = uc[2] |
| 53 | + |
| 54 | + # Controller states |
| 55 | + # x1 = xc[0] # kr |
| 56 | + # x2 = xc[1] # kx |
| 57 | + |
| 58 | + # Algebraic relationships |
| 59 | + e = xm - x |
| 60 | + |
| 61 | + # Controller dynamics |
| 62 | + d_x1 = gam*r*e*signB |
| 63 | + d_x2 = gam*x*e*signB |
| 64 | + |
| 65 | + return [d_x1, d_x2] |
| 66 | + |
| 67 | +def adaptive_controller_output(_t, xc, uc, params): |
| 68 | + """Algebraic output from adaptive controller, g(t,x,u;p)""" |
| 69 | + |
| 70 | + # Controller inputs |
| 71 | + r = uc[0] |
| 72 | + #xm = uc[1] |
| 73 | + x = uc[2] |
| 74 | + |
| 75 | + # Controller state |
| 76 | + kr = xc[0] |
| 77 | + kx = xc[1] |
| 78 | + |
| 79 | + # Control law |
| 80 | + u = kx*x + kr*r |
| 81 | + |
| 82 | + return [u] |
| 83 | + |
| 84 | +params={"gam":1, "Am":Am, "Bm":Bm, "signB":np.sign(B)} |
| 85 | + |
| 86 | +io_controller = ct.nlsys( |
| 87 | + adaptive_controller_state, |
| 88 | + adaptive_controller_output, |
| 89 | + inputs=('r', 'xm', 'x'), |
| 90 | + outputs=('u'), |
| 91 | + states=2, |
| 92 | + params=params, |
| 93 | + name='control', |
| 94 | + dt=0 |
| 95 | +) |
| 96 | + |
| 97 | +# Overall closed loop system |
| 98 | +io_closed = ct.interconnect( |
| 99 | + [io_plant, io_ref_model, io_controller], |
| 100 | + connections=[ |
| 101 | + ['plant.u', 'control.u'], |
| 102 | + ['control.xm', 'ref_model.xm'], |
| 103 | + ['control.x', 'plant.x'] |
| 104 | + ], |
| 105 | + inplist=['control.r', 'ref_model.r'], |
| 106 | + outlist=['plant.x', 'control.u'], |
| 107 | + dt=0 |
| 108 | +) |
| 109 | + |
| 110 | +# Set simulation duration and time steps |
| 111 | +Tend = 100 |
| 112 | +dt = 0.1 |
| 113 | + |
| 114 | +# Define simulation time |
| 115 | +t_vec = np.arange(0, Tend, dt) |
| 116 | + |
| 117 | +# Define control reference input |
| 118 | +r_vec = np.zeros((2, len(t_vec))) |
| 119 | +rect = signal.square(2 * np.pi * 0.05 * t_vec) |
| 120 | +r_vec[0, :] = rect |
| 121 | +r_vec[1, :] = r_vec[0, :] |
| 122 | + |
| 123 | +plt.figure(figsize=(16,8)) |
| 124 | +plt.plot(t_vec, r_vec[0,:]) |
| 125 | +plt.title(r'reference input $r$') |
| 126 | +plt.show() |
| 127 | + |
| 128 | +# Set initial conditions, io_closed |
| 129 | +X0 = np.zeros((4, 1)) |
| 130 | +X0[0] = 0 # state of plant, (x) |
| 131 | +X0[1] = 0 # state of ref_model, (xm) |
| 132 | +X0[2] = 0 # state of controller, (kr) |
| 133 | +X0[3] = 0 # state of controller, (kx) |
| 134 | + |
| 135 | +# Simulate the system with different gammas |
| 136 | +tout1, yout1, xout1 = ct.input_output_response(io_closed, t_vec, r_vec, X0, |
| 137 | + return_x=True, params={"gam":0.2}) |
| 138 | +tout2, yout2, xout2 = ct.input_output_response(io_closed, t_vec, r_vec, X0, |
| 139 | + return_x=True, params={"gam":1.0}) |
| 140 | +tout3, yout3, xout3 = ct.input_output_response(io_closed, t_vec, r_vec, X0, |
| 141 | + return_x=True, params={"gam":5.0}) |
| 142 | + |
| 143 | +plt.figure(figsize=(16,8)) |
| 144 | +plt.subplot(2,1,1) |
| 145 | +plt.plot(tout1, yout1[0,:], label=r'$x_{\gamma = 0.2}$') |
| 146 | +plt.plot(tout2, yout2[0,:], label=r'$x_{\gamma = 1.0}$') |
| 147 | +plt.plot(tout2, yout3[0,:], label=r'$x_{\gamma = 5.0}$') |
| 148 | +plt.plot(tout1, xout1[1,:] ,label=r'$x_{m}$', color='black', linestyle='--') |
| 149 | +plt.legend(fontsize=14) |
| 150 | +plt.title(r'system response $x, (x_m)$') |
| 151 | +plt.subplot(2,1,2) |
| 152 | +plt.plot(tout1, yout1[1,:], label=r'$u_{\gamma = 0.2}$') |
| 153 | +plt.plot(tout2, yout2[1,:], label=r'$u_{\gamma = 1.0}$') |
| 154 | +plt.plot(tout3, yout3[1,:], label=r'$u_{\gamma = 5.0}$') |
| 155 | +plt.legend(loc=4, fontsize=14) |
| 156 | +plt.title(r'control $u$') |
| 157 | +plt.show() |
| 158 | + |
| 159 | +plt.figure(figsize=(16,8)) |
| 160 | +plt.subplot(2,1,1) |
| 161 | +plt.plot(tout1, xout1[2,:], label=r'$k_{r, \gamma = 0.2}$') |
| 162 | +plt.plot(tout2, xout2[2,:], label=r'$k_{r, \gamma = 1.0}$') |
| 163 | +plt.plot(tout3, xout3[2,:], label=r'$k_{r, \gamma = 5.0}$') |
| 164 | +plt.hlines(kr_star, 0, Tend, label=r'$k_r^{\ast}$', color='black', linestyle='--') |
| 165 | +plt.legend(loc=4, fontsize=14) |
| 166 | +plt.title(r'control gain $k_r$ (feedforward)') |
| 167 | +plt.subplot(2,1,2) |
| 168 | +plt.plot(tout1, xout1[3,:], label=r'$k_{x, \gamma = 0.2}$') |
| 169 | +plt.plot(tout2, xout2[3,:], label=r'$k_{x, \gamma = 1.0}$') |
| 170 | +plt.plot(tout3, xout3[3,:], label=r'$k_{x, \gamma = 5.0}$') |
| 171 | +plt.hlines(kx_star, 0, Tend, label=r'$k_x^{\ast}$', color='black', linestyle='--') |
| 172 | +plt.legend(loc=4, fontsize=14) |
| 173 | +plt.title(r'control gain $k_x$ (feedback)') |
| 174 | +plt.show() |
0 commit comments