Model-Reference Adaptive Control (MRAC) SISO, direct MIT rule

Code

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

Notes

1. The environment variable PYCONTROL_TEST_EXAMPLES is used for testing to turn off plotting of the outputs.0