Skip to content

Commit a8658e4

Browse files
authored
Merge pull request #914 from KybernetikJo/mrac-examples
Add two MRAC siso examples
2 parents ad1af2f + f3713f1 commit a8658e4

File tree

7 files changed

+390
-0
lines changed

7 files changed

+390
-0
lines changed

doc/examples.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ other sources.
3333
steering-gainsched
3434
steering-optimal
3535
kincar-flatsys
36+
mrac_siso_mit
37+
mrac_siso_lyapunov
3638

3739
Jupyter notebooks
3840
=================

doc/mrac_siso_lyapunov.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../examples/mrac_siso_lyapunov.py

doc/mrac_siso_lyapunov.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Model-Reference Adaptive Control (MRAC) SISO, direct Lyapunov rule
2+
------------------------------------------------------------------
3+
4+
Code
5+
....
6+
.. literalinclude:: mrac_siso_lyapunov.py
7+
:language: python
8+
:linenos:
9+
10+
11+
Notes
12+
.....
13+
14+
1. The environment variable `PYCONTROL_TEST_EXAMPLES` is used for
15+
testing to turn off plotting of the outputs.

doc/mrac_siso_mit.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../examples/mrac_siso_mit.py

doc/mrac_siso_mit.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Model-Reference Adaptive Control (MRAC) SISO, direct MIT rule
2+
-------------------------------------------------------------
3+
4+
Code
5+
....
6+
.. literalinclude:: mrac_siso_mit.py
7+
:language: python
8+
:linenos:
9+
10+
11+
Notes
12+
.....
13+
14+
1. The environment variable `PYCONTROL_TEST_EXAMPLES` is used for
15+
testing to turn off plotting of the outputs.0

examples/mrac_siso_lyapunov.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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

Comments
 (0)