1 | #define BDMLIB |
---|
2 | #include "../mat_checks.h" |
---|
3 | #include "design/lq_ctrl.h" |
---|
4 | |
---|
5 | using namespace bdm; |
---|
6 | |
---|
7 | TEST ( LQG_test ) { |
---|
8 | LQG reg; |
---|
9 | shared_ptr<StateSpace<chmat> > stsp = new StateSpace<chmat>; |
---|
10 | // 2 x 1 x 1 |
---|
11 | stsp-> set_parameters ( eye ( 2 ), ones ( 2, 1 ), ones ( 1, 2 ), ones ( 1, 1 ), /* Q,R */ eye ( 2 ), eye ( 1 ) ); |
---|
12 | reg.set_system ( stsp ); // A, B, C |
---|
13 | reg.set_control_parameters ( eye ( 1 ), eye ( 1 ), vec_1 ( 1.0 ), 6 ); //Qy, Qu, horizon |
---|
14 | reg.validate(); |
---|
15 | |
---|
16 | reg.redesign(); |
---|
17 | double reg_apply = reg.ctrlaction ( "0.5, 1.1", "0.0" ) ( 0 ); /*convert vec to double*/ |
---|
18 | CHECK_CLOSE ( reg_apply, -0.248528137234392, 0.0001 ); |
---|
19 | } |
---|
20 | |
---|
21 | TEST ( to_state_test ) { |
---|
22 | mlnorm<fsqmat> ml; |
---|
23 | mat A = "1.1, 2.3"; |
---|
24 | ml.set_parameters ( A, vec_1 ( 1.3 ), eye ( 1 ) ); |
---|
25 | RV yr = RV ( "y", 1 ); |
---|
26 | RV ur = RV ( "u", 1 ); |
---|
27 | ml.set_rv ( yr ); |
---|
28 | yr.t_plus ( -1 ); |
---|
29 | ml.set_rvc ( concat ( yr, ur ) ); |
---|
30 | |
---|
31 | shared_ptr<StateCanonical > Stsp = new StateCanonical; |
---|
32 | Stsp->connect_mlnorm ( ml ); |
---|
33 | |
---|
34 | /* results from |
---|
35 | [A,B,C,D]=tf2ss([2.3 0],[1 -1.1]) |
---|
36 | */ |
---|
37 | CHECK_CLOSE_EX ( Stsp->_A().get_row ( 0 ), vec ( "1.1" ), 0.0001 ); |
---|
38 | CHECK_CLOSE_EX ( Stsp->_C().get_row ( 0 ), vec ( "2.53" ), 0.0001 ); |
---|
39 | CHECK_CLOSE_EX ( Stsp->_D().get_row ( 0 ), vec ( "2.30" ), 0.0001 ); |
---|
40 | } |
---|
41 | |
---|
42 | TEST ( to_state_arx_test ) { |
---|
43 | mlnorm<chmat> ml; |
---|
44 | mat A = "1.1, 2.3, 3.4"; |
---|
45 | ml.set_parameters ( A, vec_1 ( 1.3 ), eye ( 1 ) ); |
---|
46 | RV yr = RV ( "y", 1 ); |
---|
47 | RV ur = RV ( "u", 1 ); |
---|
48 | ml.set_rv ( yr ); |
---|
49 | ml.set_rvc ( concat ( yr.copy_t ( -1 ), concat ( ur, ur.copy_t ( -1 ) ) ) ); |
---|
50 | |
---|
51 | shared_ptr<StateFromARX> Stsp = new StateFromARX; |
---|
52 | RV xrv; |
---|
53 | RV urv; |
---|
54 | Stsp->connect_mlnorm ( ml, xrv, urv ); |
---|
55 | |
---|
56 | /* results from |
---|
57 | [A,B,C,D]=tf2ss([2.3 0],[1 -1.1]) |
---|
58 | */ |
---|
59 | CHECK_CLOSE_EX ( Stsp->_A().get_row ( 0 ), vec ( "1.1, 3.4, 1.3" ), 0.0001 ); |
---|
60 | CHECK_CLOSE_EX ( Stsp->_B().get_row ( 0 ), vec ( "2.3" ), 0.0001 ); |
---|
61 | CHECK_CLOSE_EX ( Stsp->_C().get_row ( 0 ), vec ( "1, 0, 0" ), 0.0001 ); |
---|
62 | } |
---|
63 | |
---|
64 | TEST ( arx_LQG_test ) { |
---|
65 | mlnorm<chmat> ml; |
---|
66 | mat A = "1.81, -.81, .00468, .00438"; |
---|
67 | ml.set_parameters ( A, vec_1 ( 0.0 ), 0.00001*eye ( 1 ) ); |
---|
68 | RV yr = RV ( "y", 1 ); |
---|
69 | RV ur = RV ( "u", 1 ); |
---|
70 | RV rgr = yr.copy_t ( -1 ); |
---|
71 | rgr.add ( yr.copy_t ( -2 ) ); |
---|
72 | rgr.add ( yr.copy_t ( -2 ) ); |
---|
73 | rgr.add ( ur.copy_t ( -1 ) ); |
---|
74 | rgr.add ( ur ); |
---|
75 | |
---|
76 | ml.set_rv ( yr ); |
---|
77 | ml.set_rvc ( rgr ); |
---|
78 | ml.validate(); |
---|
79 | |
---|
80 | shared_ptr<StateFromARX> Stsp = new StateFromARX; |
---|
81 | RV xrv; |
---|
82 | RV urv; |
---|
83 | Stsp->connect_mlnorm ( ml, xrv, urv ); |
---|
84 | |
---|
85 | LQG L; |
---|
86 | L.set_system ( Stsp ); |
---|
87 | L.set_control_parameters ( eye ( 1 ), sqrt ( 1.0 / 1000 ) *eye ( 1 ), vec_1 ( 0.0 ), 100 ); |
---|
88 | L.validate(); |
---|
89 | |
---|
90 | L.redesign(); |
---|
91 | cout << L.to_string() << endl; |
---|
92 | } |
---|
93 | |
---|
94 | TEST (quadratic_test){ |
---|
95 | /*quadraticfn qf; |
---|
96 | qf.Q = chmat(2); |
---|
97 | qf.Q._Ch() = mat("1 -1 0; 0 0 0; 0 0 0"); |
---|
98 | CHECK_CLOSE_EX(qf.eval(vec("1 2")), vec_1(1.0), 0.0000000000000001); |
---|
99 | |
---|
100 | LQG_universal lq; |
---|
101 | lq.Losses = Array<quadraticfn>(1); |
---|
102 | lq.Losses(0) = quadraticfn(); |
---|
103 | lq.Losses(0).Q._Ch() = mat("1 -1 0; 0 0 0; 0 0 0"); |
---|
104 | lq.Losses(0).rv = RV("{u up }"); |
---|
105 | |
---|
106 | lq.Models = Array<linfnEx>(1); |
---|
107 | lq.Models(0) = linfnEx(mat("1"),vec("1")); |
---|
108 | lq.Models(0).rv = RV("{x }"); |
---|
109 | |
---|
110 | lq.rv = RV("u",1); |
---|
111 | |
---|
112 | lq.redesign(); |
---|
113 | CHECK_CLOSE_EX(lq.ctrlaction(vec("1,0")), vec("1.24, -5.6"), 0.0000001);*/ |
---|
114 | } |
---|
115 | |
---|
116 | TEST (lqguniversal_test){ |
---|
117 | //test of universal LQG controller |
---|
118 | LQG_universal lq; |
---|
119 | lq.rv = RV("u", 2, 0); |
---|
120 | lq.set_rvc(RV("x", 2, 0)); |
---|
121 | lq.horizon = 10; |
---|
122 | |
---|
123 | /* |
---|
124 | model: x = Ax + Bu time: 0..horizon |
---|
125 | loss: l = x'Q'Qx + u'R'Ru time: 0..horizon-1 |
---|
126 | final loss: l = x'S'Sx time: horizon |
---|
127 | |
---|
128 | dim: x: 2 |
---|
129 | u: 2 |
---|
130 | |
---|
131 | A = [ 2 -1 ] |
---|
132 | [ 0 0.5 ] |
---|
133 | |
---|
134 | B = [ 1 -0.1 ] |
---|
135 | [ -0.2 2 ] |
---|
136 | |
---|
137 | Q = [ 5 0 ] |
---|
138 | [ 0 1 ] |
---|
139 | |
---|
140 | R = [ 0.01 0 ] |
---|
141 | [ 0 0.1 ] |
---|
142 | |
---|
143 | S = Q |
---|
144 | */ |
---|
145 | |
---|
146 | //mat mA("2 -1;0 0.5"); |
---|
147 | //mat mB("1 -0.1;-0.2 2"); |
---|
148 | //mat mQ("5 0;0 1"); |
---|
149 | //mat mR("0.01 0;0 0.1"); |
---|
150 | //mat mS = mQ; |
---|
151 | |
---|
152 | ////starting configuration |
---|
153 | //vec x0("6 3"); |
---|
154 | |
---|
155 | //uniform random generator |
---|
156 | Uniform_RNG urng; |
---|
157 | double maxmult = 10; |
---|
158 | vec tmpdiag; |
---|
159 | |
---|
160 | mat mA; |
---|
161 | urng.sample_matrix(2, 2, mA); |
---|
162 | mA *= maxmult; |
---|
163 | mat mB; |
---|
164 | urng.sample_matrix(2, 2, mB); |
---|
165 | mB *= maxmult; |
---|
166 | mat mQ(2, 2); |
---|
167 | urng.sample_vector(2, tmpdiag); |
---|
168 | tmpdiag *= maxmult; |
---|
169 | mQ.zeros(); |
---|
170 | mQ.set(0, 0, tmpdiag.get(0)); |
---|
171 | mQ.set(1, 1, tmpdiag.get(1)); |
---|
172 | mat mR(2, 2); |
---|
173 | urng.sample_vector(2, tmpdiag); |
---|
174 | tmpdiag *= maxmult; |
---|
175 | mR.zeros(); |
---|
176 | mR.set(0, 0, tmpdiag.get(0)); |
---|
177 | mR.set(1, 1, tmpdiag.get(1)); |
---|
178 | mat mS(2, 2); |
---|
179 | urng.sample_vector(2, tmpdiag); |
---|
180 | tmpdiag *= maxmult; |
---|
181 | mS.zeros(); |
---|
182 | mS.set(0, 0, tmpdiag.get(0)); |
---|
183 | mS.set(1, 1, tmpdiag.get(1)); |
---|
184 | |
---|
185 | //starting configuration |
---|
186 | vec x0; |
---|
187 | urng.sample_vector(2, x0); |
---|
188 | x0 *= maxmult; |
---|
189 | |
---|
190 | /*cout << "configuration:" << endl |
---|
191 | << "mA:" << endl << mA << endl |
---|
192 | << "mB:" << endl << mB << endl |
---|
193 | << "mQ:" << endl << mQ << endl |
---|
194 | << "mR:" << endl << mR << endl |
---|
195 | << "mS:" << endl << mS << endl |
---|
196 | << "x0:" << endl << x0 << endl;*/ |
---|
197 | |
---|
198 | //model |
---|
199 | Array<linfnEx> model(2); |
---|
200 | //model Ax part |
---|
201 | model(0).A = mA; |
---|
202 | model(0).B = vec("0 0"); |
---|
203 | model(0).rv = RV("x", 2, 0); |
---|
204 | model(0).rv_ret = RV("x", 2, 1); |
---|
205 | //model Bu part |
---|
206 | model(1).A = mB; |
---|
207 | model(1).B = vec("0 0"); |
---|
208 | model(1).rv = RV("u", 2, 0); |
---|
209 | model(1).rv_ret = RV("x", 2, 1); |
---|
210 | //setup |
---|
211 | lq.Models = model; |
---|
212 | |
---|
213 | //loss |
---|
214 | Array<quadraticfn> loss(2); |
---|
215 | //loss x'Qx part |
---|
216 | loss(0).Q.setCh(mQ); |
---|
217 | loss(0).rv = RV("x", 2, 0); |
---|
218 | //loss u'Ru part |
---|
219 | loss(1).Q.setCh(mR); |
---|
220 | loss(1).rv = RV("u", 2, 0); |
---|
221 | //setup |
---|
222 | lq.Losses = loss; |
---|
223 | |
---|
224 | //finalloss setup |
---|
225 | lq.finalLoss.Q.setCh(mS); |
---|
226 | lq.finalLoss.rv = RV("x", 2, 1); |
---|
227 | |
---|
228 | //default L |
---|
229 | //cout << "default L matrix:" << endl << lq.getL() << endl; |
---|
230 | |
---|
231 | //produce last control matrix L |
---|
232 | lq.redesign(); |
---|
233 | |
---|
234 | //verification via Riccati LQG version |
---|
235 | mat mK = mS; |
---|
236 | mat mL = - inv(mR + mB.transpose() * mK * mB) * mB.transpose() * mK * mA; |
---|
237 | |
---|
238 | //cout << "L matrix LQG_universal:" << endl << lq.getL() << endl << |
---|
239 | //"L matrix LQG Riccati:" << endl << mL << endl; |
---|
240 | |
---|
241 | //checking L matrix (universal vs Riccati), tolerance is high, but L is not main criterion |
---|
242 | //more important is reached loss compared in the next part |
---|
243 | double tolerr = 1;//0.01; //0.01 OK x 0.001 NO OK |
---|
244 | |
---|
245 | //check last time L matrix |
---|
246 | CHECK_CLOSE_EX(lq.getL().get_cols(0,1), mL, tolerr); |
---|
247 | |
---|
248 | mat oldK; |
---|
249 | int i; |
---|
250 | |
---|
251 | //produce next control matrix L |
---|
252 | for(i = 0; i < lq.horizon - 1; i++) { |
---|
253 | lq.redesign(); |
---|
254 | oldK = mK; |
---|
255 | mK = mA.transpose() * (oldK - oldK * mB * inv(mR + mB.transpose() * oldK * mB) * mB.transpose() * oldK) * mA + mQ; |
---|
256 | mL = - inv(mR + mB.transpose() * mK * mB) * mB.transpose() * mK * mA; |
---|
257 | |
---|
258 | //cout << "L matrix LQG_universal:" << endl << lq.getL() << endl << |
---|
259 | //"L matrix LQG Riccati:" << endl << mL << endl; |
---|
260 | |
---|
261 | //check other times L matrix |
---|
262 | CHECK_CLOSE_EX(lq.getL().get_cols(0,1), mL, tolerr); |
---|
263 | } |
---|
264 | |
---|
265 | //check losses of LQG control - compare LQG_universal and Riccati version, no noise |
---|
266 | |
---|
267 | //loss of LQG_universal |
---|
268 | /*double*/vec loss_uni("0"); |
---|
269 | |
---|
270 | //setup |
---|
271 | vec x = x0; |
---|
272 | vec xold = x; |
---|
273 | vec u; |
---|
274 | //vec tmploss; |
---|
275 | |
---|
276 | //iteration |
---|
277 | for(i = 0; i < lq.horizon - 1; i++){ |
---|
278 | u = lq.getL().get_cols(0,1) * xold; |
---|
279 | x = mA * xold + mB * u; |
---|
280 | /*tmploss*/loss_uni = x.transpose() * mQ * x + u.transpose() * mR * u; |
---|
281 | //loss_uni += tmploss.get(0); |
---|
282 | xold = x; |
---|
283 | } |
---|
284 | /*tmploss*/ loss_uni = x.transpose() * mS * x; |
---|
285 | //loss_uni += tmploss.get(0); |
---|
286 | |
---|
287 | //loss of LQG Riccati version |
---|
288 | /*double*/ vec loss_rct("0"); |
---|
289 | |
---|
290 | //setup |
---|
291 | x = x0; |
---|
292 | xold = x; |
---|
293 | |
---|
294 | //iteration |
---|
295 | for(i = 0; i < lq.horizon - 1; i++){ |
---|
296 | u = mL * xold; |
---|
297 | x = mA * xold + mB * u; |
---|
298 | /*tmploss*/loss_rct = x.transpose() * mQ * x + u.transpose() * mR * u; |
---|
299 | //loss_rct += tmploss.get(0); |
---|
300 | xold = x; |
---|
301 | } |
---|
302 | /*tmploss*/loss_rct = x.transpose() * mS * x; |
---|
303 | //loss_rct += tmploss.get(0); |
---|
304 | |
---|
305 | //cout << "Loss LQG_universal: " << loss_uni << " vs Loss LQG Riccati: " << loss_rct << endl; |
---|
306 | CHECK_CLOSE_EX(loss_uni, loss_rct, 0.0001); |
---|
307 | } |
---|