46
46
# External packages and modules
47
47
import numpy as np
48
48
import warnings
49
- from .exception import ControlSlycot , ControlMIMONotImplemented
49
+ from .exception import ControlSlycot , ControlMIMONotImplemented , \
50
+ ControlDimension
50
51
from .lti import isdtime , isctime
51
52
from .statesp import StateSpace
52
53
from .statefbk import gram
@@ -394,10 +395,22 @@ def era(YY, m, n, nin, nout, r):
394
395
raise NotImplementedError ('This function is not implemented yet.' )
395
396
396
397
397
- def markov (Y , U , m , transpose = None ):
398
- """Calculate the first `M ` Markov parameters [D CB CAB ...]
398
+ def markov (Y , U , m = None , transpose = None ):
399
+ """Calculate the first `m ` Markov parameters [D CB CAB ...]
399
400
from input `U`, output `Y`.
400
401
402
+ This function computes the Markov parameters for a discrete time system
403
+
404
+ .. math::
405
+
406
+ x[k+1] &= A x[k] + B u[k] \\ \\
407
+ y[k] &= C x[k] + D u[k]
408
+
409
+ given data for u and y. The algorithm assumes that that C A^k B = 0 for
410
+ k > m-2 (see [1]). Note that the problem is ill-posed if the length of
411
+ the input data is less than the desired number of Markov parameters (a
412
+ warning message is generated in this case).
413
+
401
414
Parameters
402
415
----------
403
416
Y : array_like
@@ -407,8 +420,8 @@ def markov(Y, U, m, transpose=None):
407
420
time points.
408
421
U : array_like
409
422
Input data, arranged in the same way as `Y`.
410
- m : int
411
- Number of Markov parameters to output.
423
+ m : int, optional
424
+ Number of Markov parameters to output. Defaults to len(U).
412
425
transpose : bool, optional
413
426
Assume that input data is transposed relative to the standard
414
427
:ref:`time-series-convention`. The default value is true for
@@ -417,7 +430,14 @@ def markov(Y, U, m, transpose=None):
417
430
Returns
418
431
-------
419
432
H : ndarray
420
- First m Markov parameters
433
+ First m Markov parameters, [D CB CAB ...]
434
+
435
+ References
436
+ ----------
437
+ .. [1] J.-N. Juang, M. Phan, L. G. Horta, and R. W. Longman,
438
+ Identification of observer/Kalman filter Markov parameters - Theory
439
+ and experiments. Journal of Guidance Control and Dynamics, 16(2),
440
+ 320–329, 2012. http://doi.org/10.2514/3.21006
421
441
422
442
Notes
423
443
-----
@@ -432,8 +452,8 @@ def markov(Y, U, m, transpose=None):
432
452
--------
433
453
>>> T = numpy.linspace(0, 10, 100)
434
454
>>> U = numpy.ones((1, 100))
435
- >>> Y = forced_response(tf([1], [1, 1] ), T, U)
436
- >>> H = markov(Y, U, m , transpose=False)
455
+ >>> T, Y, _ = forced_response(tf([1], [1, 0.5], True ), T, U)
456
+ >>> H = markov(Y, U, 3 , transpose=False)
437
457
438
458
"""
439
459
# Check on the specified format of the input
@@ -449,29 +469,93 @@ def markov(Y, U, m, transpose=None):
449
469
Umat = np .array (U , ndmin = 2 )
450
470
Ymat = np .array (Y , ndmin = 2 )
451
471
452
- # Transpose the data from our normal convention to match algorithm
453
- # TODO: rewrite the algorithm to use standard conventions
454
- if not transpose :
455
- Umat = np .transpose (Umat )
456
- Ymat = np .transpose (Ymat )
472
+ # If data is in transposed format, switch it around
473
+ if transpose :
474
+ Umat , Ymat = np .transpose (Umat ), np .transpose (Ymat )
457
475
458
476
# Make sure the system is a SISO system
459
- if Umat .shape [1 ] != 1 or Ymat .shape [1 ] != 1 :
477
+ if Umat .shape [0 ] != 1 or Ymat .shape [0 ] != 1 :
460
478
raise ControlMIMONotImplemented
461
- n = Umat .shape [0 ]
462
479
463
- # Construct a matrix of control inputs to invert
480
+ # Make sure the number of time points match
481
+ if Umat .shape [1 ] != Ymat .shape [1 ]:
482
+ raise ControlDimension (
483
+ "Input and output data are of differnent lengths" )
484
+ n = Umat .shape [1 ]
485
+
486
+ # If number of desired parameters was not given, set to size of input data
487
+ if m is None :
488
+ m = Umat .shape [1 ]
489
+
490
+ # Make sure there is enough data to compute parameters
491
+ if m > n :
492
+ warn .warning ("Not enough data for requested number of parameters" )
493
+
494
+ #
495
+ # Original algorithm (with mapping to standard order)
496
+ #
497
+ # RMM note, 24 Dec 2020: This algorithm sets the problem up correctly
498
+ # until the final column of the UU matrix is created, at which point it
499
+ # makes some modifications that I don't understand. This version of the
500
+ # algorithm does not seem to return the actual Markov parameters for a
501
+ # system.
502
+ #
503
+ # # Create the matrix of (shifted) inputs
504
+ # UU = np.transpose(Umat)
505
+ # for i in range(1, m-1):
506
+ # # Shift previous column down and add a zero at the top
507
+ # newCol = np.vstack((0, np.reshape(UU[0:n-1, i-1], (-1, 1))))
508
+ # UU = np.hstack((UU, newCol))
509
+ #
510
+ # # Shift previous column down and add a zero at the top
511
+ # Ulast = np.vstack((0, np.reshape(UU[0:n-1, m-2], (-1, 1))))
512
+ #
513
+ # # Replace the elements of the last column new values (?)
514
+ # # Each row gets the sum of the rows above it (?)
515
+ # for i in range(n-1, 0, -1):
516
+ # Ulast[i] = np.sum(Ulast[0:i-1])
517
+ # UU = np.hstack((UU, Ulast))
518
+ #
519
+ # # Solve for the Markov parameters from Y = H @ UU
520
+ # # H = [[D], [CB], [CAB], ..., [C A^{m-3} B], [???]]
521
+ # H = np.linalg.lstsq(UU, np.transpose(Ymat))[0]
522
+ #
523
+ # # Markov parameters are in rows => transpose if needed
524
+ # return H if transpose else np.transpose(H)
525
+
526
+ #
527
+ # New algorithm - Construct a matrix of control inputs to invert
528
+ #
529
+ # This algorithm sets up the following problem and solves it for
530
+ # the Markov parameters
531
+ #
532
+ # [ y(0) ] [ u(0) 0 0 ] [ D ]
533
+ # [ y(1) ] [ u(1) u(0) 0 ] [ C B ]
534
+ # [ y(2) ] = [ u(2) u(1) u(0) ] [ C A B ]
535
+ # [ : ] [ : : : : ] [ : ]
536
+ # [ y(n-1) ] [ u(n-1) u(n-2) u(n-3) ... u(n-m) ] [ C A^{m-2} B ]
537
+ #
538
+ # Note: if the number of Markov parameters (m) is less than the size of
539
+ # the input/output data (n), then this algorithm assumes C A^{j} B = 0
540
+ # for j > m-2. See equation (3) in
541
+ #
542
+ # J.-N. Juang, M. Phan, L. G. Horta, and R. W. Longman, Identification
543
+ # of observer/Kalman filter Markov parameters - Theory and
544
+ # experiments. Journal of Guidance Control and Dynamics, 16(2),
545
+ # 320–329, 2012. http://doi.org/10.2514/3.21006
546
+ #
547
+
548
+ # Create matrix of (shifted) inputs
464
549
UU = Umat
465
- for i in range (1 , m - 1 ):
466
- # TODO: second index on UU doesn't seem right; could be neg or pos??
467
- newCol = np .vstack ((0 , np .reshape (UU [0 :n - 1 , i - 2 ], (- 1 , 1 ))))
468
- UU = np .hstack ((UU , newCol ))
469
- Ulast = np .vstack ((0 , np .reshape (UU [0 :n - 1 , m - 2 ], (- 1 , 1 ))))
470
- for i in range (n - 1 , 0 , - 1 ):
471
- Ulast [i ] = np .sum (Ulast [0 :i - 1 ])
472
- UU = np .hstack ((UU , Ulast ))
550
+ for i in range (1 , m ):
551
+ # Shift previous column down and add a zero at the top
552
+ new_row = np .hstack ((0 , UU [i - 1 , 0 :- 1 ]))
553
+ UU = np .vstack ((UU , new_row ))
554
+ UU = np .transpose (UU )
473
555
474
556
# Invert and solve for Markov parameters
475
- H = np .linalg .lstsq (UU , Ymat )[0 ]
557
+ YY = np .transpose (Ymat )
558
+ H , _ , _ , _ = np .linalg .lstsq (UU , YY , rcond = None )
476
559
477
- return H
560
+ # Return the first m Markov parameters
561
+ return H if transpose else np .transpose (H )
0 commit comments