|
6 | 6 | import numpy as np |
7 | 7 | import pytest |
8 | 8 |
|
| 9 | +<<<<<<< HEAD |
9 | 10 | from control import StateSpace, TimeResponseData, c2d, forced_response, \ |
10 | 11 | impulse_response, step_response, rss, tf |
11 | 12 | from control.exception import ControlArgument, ControlDimension |
12 | 13 | from control.modelsimp import balred, eigensys_realization, hsvd, markov, \ |
13 | 14 | modred |
14 | 15 | from control.tests.conftest import slycotonly |
| 16 | +======= |
| 17 | + |
| 18 | +from control import StateSpace, impulse_response, forced_response, tf, rss, c2d |
| 19 | +from control.exception import ControlMIMONotImplemented |
| 20 | +from control.tests.conftest import slycotonly |
| 21 | +from control.modelsimp import balred, hsvd, markov, okid, modred |
| 22 | +>>>>>>> 67d6ef0 (Add unit tests, change okid output for siso) |
15 | 23 |
|
16 | 24 |
|
17 | 25 | class TestModelsimp: |
@@ -320,6 +328,142 @@ def testERASignature(self): |
320 | 328 | rtol=1e-6, atol=1e-8) |
321 | 329 |
|
322 | 330 |
|
| 331 | + def testOKIDSignature(self): |
| 332 | + |
| 333 | + # Example 6.1, Applied System Identification |
| 334 | + m1, k1, c1 = 1., 1., 0.01 |
| 335 | + A = np.array([ |
| 336 | + [0., 1.], |
| 337 | + [-k1/m1, -c1/m1], |
| 338 | + ]) |
| 339 | + B = np.array([[0.],[1./m1]]) |
| 340 | + C = np.array([[-k1/m1, -c1/m1]]) |
| 341 | + D = np.array([[1.]]) |
| 342 | + sys = StateSpace(A, B, C, D) |
| 343 | + dt = 0.1 |
| 344 | + sysd = sys.sample(dt, method='zoh') |
| 345 | + |
| 346 | + T = np.arange(0,200,dt) |
| 347 | + U = np.random.randn(sysd.B.shape[-1], len(T)) |
| 348 | + response = forced_response(sysd, U=U) |
| 349 | + Y = response.outputs |
| 350 | + |
| 351 | + m = 5 |
| 352 | + ir_true = impulse_response(sysd, T=T) |
| 353 | + Htrue = ir_true.outputs[:m+1]*dt |
| 354 | + H = okid(Y, U, m, dt=True) |
| 355 | + |
| 356 | + np.testing.assert_allclose(Htrue, H, atol=1e-1) |
| 357 | + |
| 358 | + # Test mimo example |
| 359 | + # Mechanical Vibrations: Theory and Application, SI Edition, 1st ed. |
| 360 | + # Figure 6.5 / Example 6.7 |
| 361 | + m1, k1, c1 = 1., 4., 1. |
| 362 | + m2, k2, c2 = 2., 2., 1. |
| 363 | + k3, c3 = 6., 2. |
| 364 | + |
| 365 | + A = np.array([ |
| 366 | + [0., 0., 1., 0.], |
| 367 | + [0., 0., 0., 1.], |
| 368 | + [-(k1+k2)/m1, (k2)/m1, -(c1+c2)/m1, c2/m1], |
| 369 | + [(k2)/m2, -(k2+k3)/m2, c2/m2, -(c2+c3)/m2] |
| 370 | + ]) |
| 371 | + B = np.array([[0.,0.],[0.,0.],[1/m1,0.],[0.,1/m2]]) |
| 372 | + C = np.array([[1.0, 0.0, 0.0, 0.0],[0.0, 1.0, 0.0, 0.0]]) |
| 373 | + D = np.zeros((2,2)) |
| 374 | + |
| 375 | + sys = StateSpace(A, B, C, D) |
| 376 | + dt = 0.25 |
| 377 | + sysd = sys.sample(dt, method='zoh') |
| 378 | + |
| 379 | + T = np.arange(0,100,dt) |
| 380 | + U = np.random.randn(sysd.B.shape[-1], len(T)) |
| 381 | + response = forced_response(sysd, U=U) |
| 382 | + Y = response.outputs |
| 383 | + |
| 384 | + m = 100 |
| 385 | + _, Htrue = impulse_response(sysd, T=dt*(m)) |
| 386 | + |
| 387 | + |
| 388 | + # test array_like |
| 389 | + atol = 1e-1 |
| 390 | + H = okid(Y, U, m, dt=dt) |
| 391 | + np.testing.assert_allclose(H, Htrue, atol=atol) |
| 392 | + |
| 393 | + # test array_like, truncate |
| 394 | + H = okid(Y, U, m, dt=dt, truncate=True) |
| 395 | + np.testing.assert_allclose(H, Htrue, atol=atol) |
| 396 | + |
| 397 | + # test array_like, transpose |
| 398 | + HT = okid(Y.T, U.T, m, dt=dt, transpose=True) |
| 399 | + np.testing.assert_allclose(HT, np.transpose(Htrue), atol=atol) |
| 400 | + |
| 401 | + # test response data |
| 402 | + H = okid(response, m, dt=dt) |
| 403 | + np.testing.assert_allclose(H, Htrue, atol=atol) |
| 404 | + |
| 405 | + # test response data |
| 406 | + H = okid(response, m, dt=dt, truncate=True) |
| 407 | + np.testing.assert_allclose(H, Htrue, atol=atol) |
| 408 | + |
| 409 | + # test response data, transpose |
| 410 | + response.transpose = True |
| 411 | + HT = okid(response, m, dt=dt) |
| 412 | + np.testing.assert_allclose(HT, np.transpose(Htrue), atol=atol) |
| 413 | + |
| 414 | + # Make sure markov() returns the right answer |
| 415 | + @pytest.mark.parametrize("k, m, n", |
| 416 | + [(2, 2, 2), |
| 417 | + (2, 5, 5), |
| 418 | + (5, 2, 2), |
| 419 | + (5, 5, 5), |
| 420 | + (5, 10, 10)]) |
| 421 | + def testOkidResults(self, k, m, n): |
| 422 | + # |
| 423 | + # Test over a range of parameters |
| 424 | + # |
| 425 | + # k = order of the system |
| 426 | + # m = number of Markov parameters |
| 427 | + # n = size of the data vector |
| 428 | + # |
| 429 | + # Values *should* match exactly for n = m, otherewise you get a |
| 430 | + # close match but errors due to the assumption that C A^k B = |
| 431 | + # 0 for k > m-2 (see modelsimp.py). |
| 432 | + # |
| 433 | + |
| 434 | + # Generate stable continuous time system |
| 435 | + Hc = rss(k, 1, 1) |
| 436 | + |
| 437 | + # Choose sampling time based on fastest time constant / 10 |
| 438 | + w, _ = np.linalg.eig(Hc.A) |
| 439 | + Ts = np.min(-np.real(w)) / 10. |
| 440 | + |
| 441 | + # Convert to a discrete time system via sampling |
| 442 | + Hd = c2d(Hc, Ts, 'zoh') |
| 443 | + |
| 444 | + # Compute the Markov parameters from state space |
| 445 | + Mtrue = np.hstack([Hd.D] + [ |
| 446 | + Hd.C @ np.linalg.matrix_power(Hd.A, i) @ Hd.B |
| 447 | + for i in range(m-1)]) |
| 448 | + |
| 449 | + Mtrue = np.squeeze(Mtrue) |
| 450 | + |
| 451 | + # Generate input/output data |
| 452 | + T = np.array(range(n)) * Ts |
| 453 | + U = np.cos(T) + np.sin(T/np.pi) |
| 454 | + _, Y = forced_response(Hd, T, U, squeeze=True) |
| 455 | + Mcomp = okid(Y, U, m-1) |
| 456 | + |
| 457 | + |
| 458 | + # TODO: Check tol |
| 459 | + # Compare to results from markov() |
| 460 | + # experimentally determined probability to get non matching results |
| 461 | + # with rtot=1e-6 and atol=1e-8 due to numerical errors |
| 462 | + # for k=5, m=n=10: 0.015 % |
| 463 | + np.testing.assert_allclose(Mtrue, Mcomp, atol=1e-1) |
| 464 | + |
| 465 | + |
| 466 | + |
323 | 467 | def testModredMatchDC(self): |
324 | 468 | #balanced realization computed in matlab for the transfer function: |
325 | 469 | # num = [1 11 45 32], den = [1 15 60 200 60] |
|
0 commit comments