Skip to content

Commit 906416d

Browse files
authored
Merge pull request #26 from rssalessio/iv
Iv
2 parents 46f8baf + 0214001 commit 906416d

21 files changed

+1213
-177
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
*.pyc
22
/dist/
33
/*.egg-info
4-
/build/
4+
/build/
5+
/.ipynb_checkpoints
6+
/examples/.ipynb_checkpoints

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# PythonVRFT Library - Version 0.0.4
1+
# PythonVRFT Library - Version 0.0.5
22
VRFT Adaptive Control Library written in Python. Aim of this library is to provide an implementation of the VRFT (Virtual Reference Feedback Tuning) algorithm.
33

44
You can find the package also at the following [link](https://pypi.org/project/pythonvrft/)
@@ -25,7 +25,7 @@ pip install .
2525
```
2626

2727
## Examples
28-
Examples are located in the examples/ folder. At the moment only 1 example is available.
28+
Examples are located in the examples/ folder. At the moment 2 examples are available. One without instrumental variables and one using instrumental variables.
2929

3030
## Tests
3131
To execute tests run the following command
@@ -34,12 +34,13 @@ python -m unittest
3434
```
3535

3636
## Changelog
37-
- [**DONE - V0.0.2**][26.03.2017] Implement the basic VRFT algorithm (1 DOF. offline, linear controller, controller expressed as scalar product theta*f(z))
38-
- [**DONE - V0.0.3**][05.01.2020] Code refactoring and conversion to Python 3; Removed support for Python Control library.
39-
- [**In Progress - V0.0.4**][07.01.2020-] Add Documentation and Latex formulas
37+
- [**V. 0.0.2**][26.03.2017] Implement the basic VRFT algorithm (1 DOF. offline, linear controller, controller expressed as scalar product theta*f(z))
38+
- [**V. 0.0.3**][05.01.2020] Code refactoring and conversion to Python 3; Removed support for Python Control library.
39+
- [**V. 0.0.5**][07.01.2020] Add Instrumental Variables (IVs) Support
40+
- [**In Progress**][07.01.2020-] Add Documentation and Latex formulas
4041
- [**TODO**] Add MIMO Support
41-
- [**TODO**] Add IV Support
4242
- [**TODO**] Generalize to other kind of controllers (e.g., neural nets)
43+
- [**TODO**] Add Cython support
4344

4445
## Citations
4546
If you find this code useful in your research, please, consider citing it:
@@ -50,3 +51,5 @@ If you find this code useful in your research, please, consider citing it:
5051
> doi = {},
5152
> url = { https://github.com/rssalessio/PythonVRFT }
5253
>}
54+
55+
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)

examples/example1.png

50.1 KB
Loading

examples/1_example.py renamed to examples/example1.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,12 @@
1919
import scipy.signal as scipysig
2020
from vrft import *
2121

22-
def generateNoise(t):
23-
omega = 2*np.pi*100
24-
xi = 0.9
25-
dt = t[1] - t[0]
26-
noise = np.random.normal(0,0.1,t.size)
27-
tf = scipysig.TransferFunction([10*omega**2], [1, 2*xi*omega, omega**2])
28-
# Second order system
29-
_, yn, _ = scipysig.lsim(tf, noise, t)
30-
return yn
22+
# Example 1
23+
# ------------
24+
# In this example we see how to apply VRFT to a simple SISO model
25+
# without any measurement noise.
26+
# Input data is generated using a square signal
27+
#
3128

3229
#Generate time and u(t) signals
3330
t_start = 0
@@ -43,8 +40,7 @@ def generateNoise(t):
4340
den = [1, -0.9]
4441
sys = ExtendedTF(num, den, dt=t_step)
4542
t, y = scipysig.dlsim(sys, u, t)
46-
y = y[:, 0]
47-
#y += generateNoise(t)
43+
y = y.flatten()
4844
data = iddata(y, u, t_step, [0])
4945

5046

@@ -58,7 +54,7 @@ def generateNoise(t):
5854
#Experiment filter
5955
L = refModel * (1 - refModel)
6056
#VRFT
61-
theta, r, _, _, C = compute_vrft(data, refModel, base, L)
57+
theta, r, loss, C = compute_vrft(data, refModel, base, L)
6258

6359
#Obtained controller
6460
print("Controller: {}".format(C))
@@ -78,7 +74,7 @@ def generateNoise(t):
7874
yr = np.array(yr).flatten()
7975
ys = np.array(ys).flatten()
8076
yc = np.array(yc).flatten()
81-
fig, ax = plt.subplots(4, sharex=True)
77+
fig, ax = plt.subplots(4, sharex=True, figsize=(12,8), dpi= 100, facecolor='w', edgecolor='k')
8278
ax[0].plot(t, yr,label='Ref System')
8379
ax[0].plot(t, yc, label='CL System')
8480
ax[0].set_title('Systems response')
File renamed without changes.

examples/example2.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Copyright [2020] [Alessio Russo - alessior@kth.se]
2+
# This file is part of PythonVRFT.
3+
# PythonVRFT is free software: you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation, version 3 of the License.
6+
# PythonVRFT is distributed in the hope that it will be useful,
7+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
8+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9+
# GNU General Public License for more details.
10+
# You should have received a copy of the GNU General Public License
11+
# along with PythonVRFT. If not, see <http://www.gnu.org/licenses/>.
12+
#
13+
# Code author: [Alessio Russo - alessior@kth.se]
14+
# Last update: 08th January 2020, by alessior@kth.se
15+
#
16+
17+
import numpy as np
18+
import matplotlib.pyplot as plt
19+
import scipy.signal as scipysig
20+
from vrft import *
21+
22+
# Example 2
23+
# ------------
24+
# In this example we see how to apply VRFT to a simple SISO model
25+
# with colored measurement noise (no instrumental variables)
26+
# Input data is generated using random normal noise
27+
#
28+
29+
def generateNoise(t):
30+
# Generate colored noise
31+
omega = 2*np.pi*100
32+
xi = 0.9
33+
dt = t[1] - t[0]
34+
noise = np.random.normal(0,0.1,t.size)
35+
tf = scipysig.TransferFunction([10*omega**2], [1, 2*xi*omega, omega**2])
36+
# Second order system
37+
_, yn, _ = scipysig.lsim(tf, noise, t)
38+
return yn
39+
40+
#Generate time and u(t) signals
41+
t_start = 0
42+
t_end = 10
43+
t_step = 1e-2
44+
t = np.arange(t_start, t_end, t_step)
45+
u = np.random.normal(size=t.size)
46+
47+
#Experiment
48+
num = [0.5]
49+
den = [1, -0.9]
50+
sys = ExtendedTF(num, den, dt=t_step)
51+
t, y = scipysig.dlsim(sys, u, t)
52+
y = y.flatten() + generateNoise(t)
53+
data = iddata(y, u, t_step, [0])
54+
55+
56+
#Reference Model
57+
refModel = ExtendedTF([0.6], [1, -0.4], dt=t_step)
58+
59+
#PI Controller
60+
base = [ExtendedTF([1], [1, -1], dt=t_step),
61+
ExtendedTF([1, 0], [1, -1], dt=t_step)]
62+
63+
#Experiment filter
64+
L = refModel * (1 - refModel)
65+
#VRFT
66+
theta, r, loss, C = compute_vrft(data, refModel, base, L)
67+
68+
#Obtained controller
69+
print("Controller: {}".format(C))
70+
71+
L = (C * sys).feedback()
72+
73+
print("Theta: {}".format(theta))
74+
print(scipysig.ZerosPolesGain(L))
75+
76+
#Analysis
77+
t = t[:len(r)]
78+
u = np.ones(len(t))
79+
_, yr = scipysig.dlsim(refModel, u, t)
80+
_, yc = scipysig.dlsim(L, u, t)
81+
_, ys = scipysig.dlsim(sys, u, t)
82+
83+
yr = np.array(yr).flatten()
84+
ys = np.array(ys).flatten()
85+
yc = np.array(yc).flatten()
86+
fig, ax = plt.subplots(4, sharex=True, figsize=(12,8), dpi= 100, facecolor='w', edgecolor='k')
87+
ax[0].plot(t, yr,label='Ref System')
88+
ax[0].plot(t, yc, label='CL System')
89+
ax[0].set_title('Systems response')
90+
ax[0].grid(True)
91+
ax[1].plot(t, ys, label='OL System')
92+
ax[1].set_title('OL Systems response')
93+
ax[1].grid(True)
94+
ax[2].plot(t, y[:len(r)])
95+
ax[2].grid(True)
96+
ax[2].set_title('Experiment data')
97+
ax[3].plot(t, r)
98+
ax[3].grid(True)
99+
ax[3].set_title('Virtual Reference')
100+
101+
# Now add the legend with some customizations.
102+
legend = ax[0].legend(loc='lower right', shadow=True)
103+
104+
# The frame is matplotlib.patches.Rectangle instance surrounding the legend.
105+
frame = legend.get_frame()
106+
frame.set_facecolor('0.90')
107+
108+
# Set the fontsize
109+
for label in legend.get_texts():
110+
label.set_fontsize('large')
111+
112+
for label in legend.get_lines():
113+
label.set_linewidth(1.5) # the legend line width
114+
plt.show()

examples/example3.png

122 KB
Loading

examples/example3.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Copyright [2020] [Alessio Russo - alessior@kth.se]
2+
# This file is part of PythonVRFT.
3+
# PythonVRFT is free software: you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation, version 3 of the License.
6+
# PythonVRFT is distributed in the hope that it will be useful,
7+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
8+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9+
# GNU General Public License for more details.
10+
# You should have received a copy of the GNU General Public License
11+
# along with PythonVRFT. If not, see <http://www.gnu.org/licenses/>.
12+
#
13+
# Code author: [Alessio Russo - alessior@kth.se]
14+
# Last update: 08th January 2020, by alessior@kth.se
15+
#
16+
17+
import numpy as np
18+
import matplotlib.pyplot as plt
19+
import scipy.signal as scipysig
20+
from vrft import *
21+
22+
# Example 3
23+
# ------------
24+
# In this example we see how to apply VRFT to a simple SISO model
25+
# with measurement noise using instrumental variables
26+
# Input data is generated using random normal noise
27+
#
28+
29+
#Generate time and u(t) signals
30+
t_start = 0
31+
t_end = 10
32+
dt = 1e-2
33+
t = np.array([i * dt for i in range(int(t_end/dt ))])
34+
35+
#Experiment
36+
num = [0.5]
37+
den = [1, -0.9]
38+
sys = ExtendedTF(num, den, dt=dt)
39+
40+
def generate_data(sys, u, t):
41+
t, y = scipysig.dlsim(sys, u, t)
42+
y = y.flatten() + 0.5 * np.random.normal(size = t.size)
43+
return iddata(y, u, dt, [0])
44+
45+
u = np.random.normal(size=t.size)
46+
data1 = generate_data(sys, u, t)
47+
data2 = generate_data(sys, u, t)
48+
data = [data1, data2]
49+
50+
#Reference Model
51+
refModel = ExtendedTF([0.6], [1, -0.4], dt=dt)
52+
53+
#PI Controller
54+
control = [ExtendedTF([1], [1, -1], dt=dt),
55+
ExtendedTF([1, 0], [1, -1], dt=dt)]
56+
57+
#Experiment filter
58+
prefilter = refModel * (1 - refModel)
59+
60+
# VRFT method with Instrumental variables
61+
theta_iv, r_iv, loss_iv, C_iv = compute_vrft(data, refModel, control, prefilter, iv=True)
62+
63+
# VRFT method without Instrumental variables
64+
theta_noiv, r_noiv, loss_noiv, C_noiv = compute_vrft(data1, refModel, control, prefilter, iv=False)
65+
66+
#Obtained controller
67+
print('------IV------')
68+
print("Loss: {}\nTheta: {}\nController: {}".format(loss_iv, theta_iv, C_iv))
69+
print('------No IV------')
70+
print("Loss: {}\nTheta: {}\nController: {}".format(loss_noiv, theta_noiv, C_noiv))
71+
72+
73+
# Closed loop system
74+
closed_loop_iv = (C_iv * sys).feedback()
75+
closed_loop_noiv = (C_noiv * sys).feedback()
76+
77+
t = t[:len(r_iv)]
78+
u = np.ones(len(t))
79+
80+
_, yr = scipysig.dlsim(refModel, u, t)
81+
_, yc_iv = scipysig.dlsim(closed_loop_iv, u, t)
82+
_, yc_noiv = scipysig.dlsim(closed_loop_noiv, u, t)
83+
_, ys = scipysig.dlsim(sys, u, t)
84+
85+
yr = yr.flatten()
86+
ys = ys.flatten()
87+
yc_noiv = yc_noiv.flatten()
88+
yc_iv = yc_iv.flatten()
89+
90+
fig, ax = plt.subplots(4, sharex=True, figsize=(12,8), dpi= 100, facecolor='w', edgecolor='k')
91+
ax[0].plot(t, yr,label='Ref System')
92+
ax[0].plot(t, yc_iv, label='CL System - IV')
93+
ax[0].plot(t, yc_noiv, label='CL System - No IV')
94+
ax[0].set_title('CL Systems response')
95+
ax[0].grid(True)
96+
ax[1].plot(t, ys, label='OL System')
97+
ax[1].set_title('OL Systems response')
98+
ax[1].grid(True)
99+
ax[2].plot(t, data1.y[:len(r_iv)])
100+
ax[2].grid(True)
101+
ax[2].set_title('Experiment data')
102+
ax[3].plot(t, r_iv)
103+
ax[3].grid(True)
104+
ax[3].set_title('Virtual Reference')
105+
106+
# Now add the legend with some customizations.
107+
legend = ax[0].legend(loc='lower right', shadow=True)
108+
109+
# The frame is matplotlib.patches.Rectangle instance surrounding the legend.
110+
frame = legend.get_frame()
111+
frame.set_facecolor('0.90')
112+
113+
114+
plt.show()

0 commit comments

Comments
 (0)