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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy