Skip to content

Commit 5b8ee85

Browse files
committed
iv part 2
1 parent 8f21c0c commit 5b8ee85

12 files changed

Lines changed: 476 additions & 109 deletions

.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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ python -m unittest
4040
- [**In Progress**][07.01.2020-] Add Documentation and Latex formulas
4141
- [**TODO**] Add MIMO 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:

examples/1_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def generateNoise(t):
5858
#Experiment filter
5959
L = refModel * (1 - refModel)
6060
#VRFT
61-
theta, r, _, _, C = compute_vrft(data, refModel, base, L)
61+
theta, r, loss, C = compute_vrft(data, refModel, base, L)
6262

6363
#Obtained controller
6464
print("Controller: {}".format(C))

examples/2_example.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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: 07th 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+
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
31+
32+
#Generate time and u(t) signals
33+
t_start = 0
34+
t_end = 10
35+
t_step = 1e-2
36+
t = np.arange(t_start, t_end, t_step)
37+
u = np.ones(len(t))
38+
u[200:400] = np.zeros(200)
39+
u[600:800] = np.zeros(200)
40+
41+
#Experiment
42+
num = [0.5]
43+
den = [1, -0.9]
44+
sys = ExtendedTF(num, den, dt=t_step)
45+
t, y = scipysig.dlsim(sys, u, t)
46+
y = y.flatten() + 0.5 * np.random.normal(size = t.size)
47+
#y += generateNoise(t)
48+
data = iddata(y, u, t_step, [0])
49+
50+
51+
#Reference Model
52+
refModel = ExtendedTF([0.6], [1, -0.4], dt=t_step)
53+
54+
#PI Controller
55+
base = [ExtendedTF([1], [1, -1], dt=t_step),
56+
ExtendedTF([1, 0], [1, -1], dt=t_step)]
57+
58+
#Experiment filter
59+
L = refModel * (1 - refModel)
60+
61+
#VRFT
62+
theta, r, loss, C = compute_vrft(data, refModel, base, L, iv=True)
63+
64+
#Obtained controller
65+
print("Controller: {}".format(C))
66+
67+
L = (C * sys).feedback()
68+
69+
print("Theta: {}".format(theta))
70+
print(scipysig.ZerosPolesGain(L))
71+
72+
#Analysis
73+
t = t[:len(r)]
74+
u = np.ones(len(t))
75+
_, yr = scipysig.dlsim(refModel, u, t)
76+
_, yc = scipysig.dlsim(L, u, t)
77+
_, ys = scipysig.dlsim(sys, u, t)
78+
79+
yr = np.array(yr).flatten()
80+
ys = np.array(ys).flatten()
81+
yc = np.array(yc).flatten()
82+
fig, ax = plt.subplots(4, sharex=True)
83+
ax[0].plot(t, yr,label='Ref System')
84+
ax[0].plot(t, yc, label='CL System')
85+
ax[0].set_title('Systems response')
86+
ax[0].grid(True)
87+
ax[1].plot(t, ys, label='OL System')
88+
ax[1].set_title('OL Systems response')
89+
ax[1].grid(True)
90+
ax[2].plot(t, y[:len(r)])
91+
ax[2].grid(True)
92+
ax[2].set_title('Experiment data')
93+
ax[3].plot(t, r)
94+
ax[3].grid(True)
95+
ax[3].set_title('Virtual Reference')
96+
97+
# Now add the legend with some customizations.
98+
legend = ax[0].legend(loc='lower right', shadow=True)
99+
100+
# The frame is matplotlib.patches.Rectangle instance surrounding the legend.
101+
frame = legend.get_frame()
102+
frame.set_facecolor('0.90')
103+
104+
# Set the fontsize
105+
for label in legend.get_texts():
106+
label.set_fontsize('large')
107+
108+
for label in legend.get_lines():
109+
label.set_linewidth(1.5) # the legend line width
110+
plt.show()

examples/notebook_example_1.ipynb

Lines changed: 18 additions & 10 deletions
Large diffs are not rendered by default.

examples/notebook_example_2.ipynb

Lines changed: 247 additions & 0 deletions
Large diffs are not rendered by default.

test/test_iddata.py

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,62 +24,62 @@
2424

2525
class TestIDData(TestCase):
2626
def test_type(self):
27-
a = iddata(0.0, 0.0, 0.0)
27+
a = iddata(0.0, 0.0, 0.0, [0])
2828
with self.assertRaises(ValueError):
2929
a.check()
3030

31-
a = iddata(0.0, [1], 0.0)
31+
a = iddata(0.0, [1], 0.0, [0])
3232
with self.assertRaises(ValueError):
3333
a.check()
3434

35-
a = iddata(np.zeros(10), 1, 0.0)
35+
a = iddata(np.zeros(10), 1, 0.0, [0])
3636
with self.assertRaises(ValueError):
3737
a.check()
3838

39-
a = iddata([0 for i in range(10)], [0 for i in range(10)], 1.0)
39+
a = iddata([0 for i in range(10)], [0 for i in range(10)], 1.0, [0])
4040
self.assertTrue(a.check())
4141

42-
a = iddata(np.zeros(10), np.zeros(10), 1.0)
42+
a = iddata(np.zeros(10), np.zeros(10), 1.0, [0])
4343
self.assertTrue(a.check())
4444

4545
def test_size(self):
46-
a = iddata(np.zeros(10), np.zeros(10), 0.0)
46+
a = iddata(np.zeros(10), np.zeros(10), 0.0, [0])
4747
self.assertEqual(len(a.y), 10)
4848
self.assertEqual(len(a.u), 10)
4949
self.assertEqual(len(a.y), len(a.u))
5050

51-
a = iddata([0 for i in range(10)], [1 for i in range(0,10)], 0.0)
51+
a = iddata([0 for i in range(10)], [1 for i in range(0,10)], 0.0, [0])
5252
self.assertEqual(len(a.y), 10)
5353
self.assertEqual(len(a.u), 10)
5454
self.assertEqual(len(a.y), len(a.u))
5555

56-
a = iddata(np.zeros(10), np.zeros(9), 0.0)
56+
a = iddata(np.zeros(10), np.zeros(9), 0.0, [0])
5757
with self.assertRaises(ValueError):
5858
a.check()
5959

60-
a = iddata(np.zeros(8), np.zeros(9), 0.0)
60+
a = iddata(np.zeros(8), np.zeros(9), 0.0, [0])
6161
with self.assertRaises(ValueError):
6262
a.check()
6363

6464

6565
def test_sampling_time(self):
66-
a = iddata(np.zeros(10), np.zeros(10), 0.0)
66+
a = iddata(np.zeros(10), np.zeros(10), 0.0, [0])
6767
with self.assertRaises(ValueError):
6868
a.check()
6969

70-
a = iddata(np.zeros(10), np.zeros(10), 1e-9)
70+
a = iddata(np.zeros(10), np.zeros(10), 1e-9, [0])
7171
with self.assertRaises(ValueError):
7272
a.check()
7373

74-
a = iddata(np.zeros(10), np.zeros(10), -0.1)
74+
a = iddata(np.zeros(10), np.zeros(10), -0.1, [0])
7575
with self.assertRaises(ValueError):
7676
a.check()
7777

78-
a = iddata(np.zeros(10), np.zeros(10), 0.1)
78+
a = iddata(np.zeros(10), np.zeros(10), 0.1, [0])
7979
self.assertTrue(a.check())
8080

8181
def test_copy(self):
82-
a = iddata(np.zeros(10), np.zeros(10), 0.1)
82+
a = iddata(np.zeros(10), np.zeros(10), 0.1, [0])
8383
b = a.copy()
8484
self.assertTrue(a.check())
8585
self.assertTrue(b.check())
@@ -90,7 +90,7 @@ def test_copy(self):
9090
self.assertTrue(a.ts == b.ts)
9191

9292
def test_filter(self):
93-
a = iddata(np.zeros(10), np.zeros(10), 0.1)
93+
a = iddata(np.zeros(10), np.zeros(10), 0.1, [0])
9494
L = scipysig.dlti([1], [1], dt=0.1)
9595
b = a.copy()
9696
a.filter(L)
@@ -100,23 +100,24 @@ def test_filter(self):
100100
self.assertTrue(a.ts == b.ts)
101101

102102
def test_split(self):
103-
n = 10
104-
a = iddata(np.random.normal(size=n), np.random.normal(size=n), 0.1)
103+
n = 9
104+
a = iddata(np.random.normal(size=n), np.random.normal(size=n), 0.1, [0])
105105

106106
b, c = a.split()
107+
n0 = len(a.y0)
108+
n1 = (n + n0) // 2
107109

108110
self.assertTrue(b.y.size == c.y.size)
109111
self.assertTrue(b.u.size == c.u.size)
110-
self.assertTrue(b.y0 == c.y0)
111112
self.assertTrue(b.ts == c.ts)
112113
self.assertTrue(b.ts == a.ts)
113-
self.assertTrue(np.all(b.y == a.y[:n//2]))
114-
self.assertTrue(np.all(b.u == a.u[:n//2]))
115-
self.assertTrue(b.y0 == a.y0)
114+
self.assertTrue(np.all(b.y == a.y[:n1 - n0]))
115+
self.assertTrue(np.all(b.u == a.u[:n1 - n0]))
116+
self.assertTrue(np.all(b.y0 == a.y0))
116117

117-
self.assertTrue(np.all(c.y == a.y[n//2:]))
118-
self.assertTrue(np.all(c.u == a.u[n//2:]))
119-
self.assertTrue(c.y0 == a.y0)
118+
self.assertTrue(np.all(c.y == a.y[n1:n]))
119+
self.assertTrue(np.all(c.u == a.u[n1:n]))
120+
self.assertTrue(np.all(c.y0 == a.y[n1 - n0:n1]))
120121

121122
y0 = [-1, 2]
122123
a = iddata(np.random.normal(size=n), np.random.normal(size=n), 0.1, y0)
@@ -132,8 +133,8 @@ def test_split(self):
132133
self.assertTrue(np.all(b.u == a.u[:n1 - n0]))
133134
self.assertTrue(np.all(b.y0 == a.y0))
134135

135-
self.assertTrue(np.all(c.y == a.y[n1:]))
136-
self.assertTrue(np.all(c.u == a.u[n1:]))
136+
self.assertTrue(np.all(c.y == a.y[n1:n-1]))
137+
self.assertTrue(np.all(c.u == a.u[n1:n-1]))
137138
self.assertTrue(np.all(c.y0 == a.y[n1 - n0:n1]))
138139

139140

test/test_reference.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,10 @@ def test_virtualReference(self):
5353
y = y[:,0]
5454
data = iddata(y,u,t_step,[0,0])
5555

56-
r, _ = virtualReference(data, num, den)
57-
for i in range(len(r)):
58-
self.assertTrue(np.isclose(r[i], u[i]))
59-
60-
data = iddata(y,u,t_step,[0,0,0])
61-
r, _ = virtualReference(data, num, den)
62-
for i in range(len(r)):
63-
self.assertTrue(np.isclose(r[i], u[i]))
64-
56+
# wrong initial conditions
57+
with self.assertRaises(ValueError):
58+
r, _ = virtualReference(data, num, den)
59+
6560
#test good data, first order
6661
data = iddata(y,u,t_step,[0])
6762

test/test_vrft.py

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,61 +27,66 @@
2727
class TestVRFT(TestCase):
2828
def test_vrft(self):
2929
t_start = 0
30-
t_end = 10
3130
t_step = 1e-2
32-
t = np.arange(t_start, t_end, t_step)
33-
u = np.ones(len(t)).tolist()
31+
t_ends = [10, 10 + t_step]
3432

35-
num = [0.1]
36-
den = [1, -0.9]
37-
sys = scipysig.TransferFunction(num, den, dt=t_step)
38-
t, y = scipysig.dlsim(sys, u, t)
39-
y = y[:,0]
40-
data = iddata(y,u,t_step,[0])
33+
expected_theta = np.array([1.93220784, -1.05808206, 1.26623764, 0.0088772])
34+
expected_loss = 0.00064687904235295
35+
36+
for t_end in t_ends:
37+
t = np.arange(t_start, t_end, t_step)
38+
u = np.ones(len(t)).tolist()
4139

42-
refModel = ExtendedTF([0.2], [1, -0.8], dt=t_step)
43-
prefilter = refModel * (1-refModel)
40+
num = [0.1]
41+
den = [1, -0.9]
42+
sys = scipysig.TransferFunction(num, den, dt=t_step)
43+
t, y = scipysig.dlsim(sys, u, t)
44+
y = y[:,0]
45+
data = iddata(y,u,t_step,[0])
4446

45-
control = [ExtendedTF([1], [1,0], dt=t_step),
46-
ExtendedTF([1], [1,0,0], dt=t_step),
47-
ExtendedTF([1], [1,0,0,0], dt=t_step),
48-
ExtendedTF([1, 0], [1,1], dt=t_step)]
47+
refModel = ExtendedTF([0.2], [1, -0.8], dt=t_step)
48+
prefilter = refModel * (1-refModel)
4949

50-
theta1, _, loss1, _ = compute_vrft(data, refModel, control, prefilter)
51-
theta2, _, loss2, _ = compute_vrft([data], refModel, control, prefilter)
52-
theta3, _, loss3, _ = compute_vrft([data, data], refModel, control, prefilter)
53-
54-
self.assertTrue(np.isclose(loss1, loss2))
55-
self.assertTrue(np.isclose(loss1, loss3))
56-
self.assertTrue(np.linalg.norm(theta1-theta2)<1e-15)
57-
self.assertTrue(np.linalg.norm(theta1-theta3)<1e-15)
50+
control = [ExtendedTF([1], [1,0], dt=t_step),
51+
ExtendedTF([1], [1,0,0], dt=t_step),
52+
ExtendedTF([1], [1,0,0,0], dt=t_step),
53+
ExtendedTF([1, 0], [1,1], dt=t_step)]
54+
55+
theta1, _, loss1, _ = compute_vrft(data, refModel, control, prefilter)
56+
theta2, _, loss2, _ = compute_vrft([data], refModel, control, prefilter)
57+
theta3, _, loss3, _ = compute_vrft([data, data], refModel, control, prefilter)
58+
59+
self.assertTrue(np.isclose(loss1, loss2))
60+
self.assertTrue(np.isclose(loss1, loss3))
61+
self.assertTrue(np.linalg.norm(theta1-theta2)<1e-15)
62+
self.assertTrue(np.linalg.norm(theta1-theta3)<1e-15)
63+
self.assertTrue(np.linalg.norm(theta1-expected_theta, np.infty) < 1e-5)
64+
self.assertTrue(abs(expected_loss - loss1) < 1e-5)
5865

5966
def test_iv(self):
6067
t_start = 0
61-
t_end = 10
6268
t_step = 1e-2
63-
t = np.arange(t_start, t_end, t_step)
64-
u = np.ones(len(t)).tolist()
69+
t_ends = [10, 10 + t_step]
6570

66-
num = [0.1]
67-
den = [1, -0.9]
68-
sys = scipysig.TransferFunction(num, den, dt=t_step)
69-
t, y = scipysig.dlsim(sys, u, t)
70-
y = y.flatten() + np.random.normal(size=t.size)
71-
data = iddata(y,u,t_step,[0])
72-
73-
refModel = ExtendedTF([0.2], [1, -0.8], dt=t_step)
74-
prefilter = refModel * (1-refModel)
71+
72+
for t_end in t_ends:
73+
t = np.arange(t_start, t_end, t_step)
74+
u = np.ones(len(t)).tolist()
7575

76-
control = [ExtendedTF([1], [1,0], dt=t_step),
77-
ExtendedTF([1], [1,0,0], dt=t_step),
78-
ExtendedTF([1], [1,0,0,0], dt=t_step),
79-
ExtendedTF([1, 0], [1,1], dt=t_step)]
76+
num = [0.1]
77+
den = [1, -0.9]
78+
sys = scipysig.TransferFunction(num, den, dt=t_step)
79+
t, y = scipysig.dlsim(sys, u, t)
80+
y = y.flatten() + 1e-2 * np.random.normal(size=t.size)
81+
data = iddata(y,u,t_step,[0])
8082

81-
#import pdb
82-
#pdb.set_trace()
83-
84-
#theta, _, loss, _ = compute_vrft(data, refModel, control, prefilter, iv=True)
83+
refModel = ExtendedTF([0.2], [1, -0.8], dt=t_step)
84+
prefilter = refModel * (1-refModel)
8585

86+
control = [ExtendedTF([1], [1,0], dt=t_step),
87+
ExtendedTF([1], [1,0,0], dt=t_step),
88+
ExtendedTF([1], [1,0,0,0], dt=t_step),
89+
ExtendedTF([1, 0], [1,1], dt=t_step)]
8690

91+
theta, _, loss, _ = compute_vrft(data, refModel, control, prefilter, iv=True)
8792

0 commit comments

Comments
 (0)