Skip to content

Commit 28744f8

Browse files
committed
Implement ERA, change api to TimeResponseData
1 parent 4acc78b commit 28744f8

File tree

1 file changed

+87
-21
lines changed

1 file changed

+87
-21
lines changed

control/modelsimp.py

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -368,38 +368,104 @@ def minreal(sys, tol=None, verbose=True):
368368
return sysr
369369

370370

371-
def era(YY, m, n, nin, nout, r):
372-
"""Calculate an ERA model of order `r` based on the impulse-response data
373-
`YY`.
371+
def era(data, r, m=None, n=None, dt=True):
372+
"""Calculate an ERA model of order `r` based on the impulse-response data.
374373
375-
.. note:: This function is not implemented yet.
374+
This function computes a discrete time system
375+
376+
.. math::
377+
378+
x[k+1] &= A x[k] + B u[k] \\\\
379+
y[k] &= C x[k] + D u[k]
380+
381+
for a given impulse-response data (see [1]_).
376382
377383
Parameters
378384
----------
379-
YY: array
380-
`nout` x `nin` dimensional impulse-response data
381-
m: integer
382-
Number of rows in Hankel matrix
383-
n: integer
384-
Number of columns in Hankel matrix
385-
nin: integer
386-
Number of input variables
387-
nout: integer
388-
Number of output variables
389-
r: integer
390-
Order of model
385+
data : TimeResponseData
386+
impulse-response data from which the StateSpace model is estimated.
387+
r : integer
388+
Order of model.
389+
m : integer, optional
390+
Number of rows in Hankel matrix.
391+
Default is 2*r.
392+
n : integer, optional
393+
Number of columns in Hankel matrix.
394+
Default is 2*r.
395+
dt : True or float, optional
396+
True indicates discrete time with unspecified sampling time,
397+
positive number is discrete time with specified sampling time.
398+
It can be used to scale the StateSpace model in order to match
399+
the impulse response of this library.
400+
Default values is True.
391401
392402
Returns
393403
-------
394-
sys: StateSpace
395-
A reduced order model sys=ss(Ar,Br,Cr,Dr)
404+
sys : StateSpace
405+
A reduced order model sys=StateSpace(Ar,Br,Cr,Dr,dt)
406+
S : array
407+
Singular values of Hankel matrix.
408+
Can be used to choose a good r value.
409+
410+
References
411+
----------
412+
.. [1] Samet Oymak and Necmiye Ozay
413+
Non-asymptotic Identification of LTI Systems
414+
from a Single Trajectory.
415+
https://arxiv.org/abs/1806.05722
396416
397417
Examples
398418
--------
399-
>>> rsys = era(YY, m, n, nin, nout, r) # doctest: +SKIP
400-
419+
>>> T = np.linspace(0, 10, 100)
420+
>>> response = ct.impulse_response(ct.tf([1], [1, 0.5], True), T)
421+
>>> sysd, _ = ct.era(response, r=1)
401422
"""
402-
raise NotImplementedError('This function is not implemented yet.')
423+
def block_hankel_matrix(Y, m, n):
424+
425+
q, p, _ = Y.shape
426+
YY = Y.transpose(0,2,1) # transpose for reshape
427+
428+
H = np.zeros((q*m,p*n))
429+
430+
for r in range(m):
431+
# shift and add row to hankel matrix
432+
new_row = YY[:,r:r+n,:]
433+
H[q*r:q*(r+1),:] = new_row.reshape((q,p*n))
434+
435+
return H
436+
437+
Y = np.array(data.outputs, ndmin=3)
438+
if data.transpose:
439+
Y = np.transpose(Y)
440+
q, p, l = Y.shape
441+
442+
if m is None:
443+
m = 2*r
444+
if n is None:
445+
n = 2*r
446+
447+
if m*q < r or n*p < r:
448+
raise ValueError("Hankel parameters are to small")
449+
450+
if (l-1) < m+n:
451+
raise ValueError("Not enough data for requested number of parameters")
452+
453+
H = block_hankel_matrix(Y[:,:,1:], m, n+1) # Hankel matrix (q*m, p*(n+1))
454+
Hf = H[:,:-p] # first p*n columns of H
455+
Hl = H[:,p:] # last p*n columns of H
456+
457+
U,S,Vh = np.linalg.svd(Hf, True)
458+
Ur =U[:,0:r]
459+
Vhr =Vh[0:r,:]
460+
461+
# balanced realizations
462+
Sigma_inv = np.diag(1./np.sqrt(S[0:r]))
463+
Ar = Sigma_inv @ Ur.T @ Hl @ Vhr.T @ Sigma_inv
464+
Br = Sigma_inv @ Ur.T @ Hf[:,0:p]*dt
465+
Cr = Hf[0:q,:] @ Vhr.T @ Sigma_inv
466+
Dr = Y[:,:,0]
467+
468+
return StateSpace(Ar,Br,Cr,Dr,dt), S
403469

404470

405471
def markov(Y, U, m=None, transpose=False):

0 commit comments

Comments
 (0)