Skip to content

Commit bd0e515

Browse files
authored
Merge pull request #284 from joaoantoniocardoso/master
Added an initial version for step_info
2 parents 00962cf + 335fb86 commit bd0e515

File tree

3 files changed

+199
-2
lines changed

3 files changed

+199
-2
lines changed

control/matlab/timeresp.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
Note that the return arguments are different than in the standard control package.
55
"""
66

7-
__all__ = ['step', 'impulse', 'initial', 'lsim']
7+
__all__ = ['step', 'stepinfo', 'impulse', 'initial', 'lsim']
88

99
def step(sys, T=None, X0=0., input=0, output=None, return_x=False):
1010
'''
@@ -66,6 +66,52 @@ def step(sys, T=None, X0=0., input=0, output=None, return_x=False):
6666

6767
return yout, T
6868

69+
def stepinfo(sys, T=None, SettlingTimeThreshold=0.02, RiseTimeLimits=(0.1,0.9)):
70+
'''
71+
Step response characteristics (Rise time, Settling Time, Peak and others).
72+
73+
Parameters
74+
----------
75+
sys: StateSpace, or TransferFunction
76+
LTI system to simulate
77+
78+
T: array-like object, optional
79+
Time vector (argument is autocomputed if not given)
80+
81+
SettlingTimeThreshold: float value, optional
82+
Defines the error to compute settling time (default = 0.02)
83+
84+
RiseTimeLimits: tuple (lower_threshold, upper_theshold)
85+
Defines the lower and upper threshold for RiseTime computation
86+
87+
Returns
88+
-------
89+
S: a dictionary containing:
90+
RiseTime: Time from 10% to 90% of the steady-state value.
91+
SettlingTime: Time to enter inside a default error of 2%
92+
SettlingMin: Minimum value after RiseTime
93+
SettlingMax: Maximum value after RiseTime
94+
Overshoot: Percentage of the Peak relative to steady value
95+
Undershoot: Percentage of undershoot
96+
Peak: Absolute peak value
97+
PeakTime: time of the Peak
98+
SteadyStateValue: Steady-state value
99+
100+
101+
See Also
102+
--------
103+
step, lsim, initial, impulse
104+
105+
Examples
106+
--------
107+
>>> S = stepinfo(sys, T)
108+
'''
109+
from ..timeresp import step_info
110+
111+
S = step_info(sys, T, SettlingTimeThreshold, RiseTimeLimits)
112+
113+
return S
114+
69115
def impulse(sys, T=None, X0=0., input=0, output=None, return_x=False):
70116
'''
71117
Impulse response of a linear system

control/tests/timeresp_test.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,65 @@ def test_step_response(self):
8787
np.testing.assert_array_equal(Tc.shape, Td.shape)
8888
np.testing.assert_array_equal(youtc.shape, youtd.shape)
8989

90+
def test_step_info(self):
91+
# From matlab docs:
92+
sys = TransferFunction([1,5,5],[1,1.65,5,6.5,2])
93+
Strue = {
94+
'RiseTime': 3.8456,
95+
'SettlingTime': 27.9762,
96+
'SettlingMin': 2.0689,
97+
'SettlingMax': 2.6873,
98+
'Overshoot': 7.4915,
99+
'Undershoot': 0,
100+
'Peak': 2.6873,
101+
'PeakTime': 8.0530
102+
}
103+
104+
S = step_info(sys)
105+
106+
# Very arbitrary tolerance because I don't know if the
107+
# response from the MATLAB is really that accurate.
108+
# maybe it is a good idea to change the Strue to match
109+
# but I didn't do it because I don't know if it is
110+
# accurate either...
111+
rtol = 2e-2
112+
np.testing.assert_allclose(
113+
S.get('RiseTime'),
114+
Strue.get('RiseTime'),
115+
rtol=rtol)
116+
np.testing.assert_allclose(
117+
S.get('SettlingTime'),
118+
Strue.get('SettlingTime'),
119+
rtol=rtol)
120+
np.testing.assert_allclose(
121+
S.get('SettlingMin'),
122+
Strue.get('SettlingMin'),
123+
rtol=rtol)
124+
np.testing.assert_allclose(
125+
S.get('SettlingMax'),
126+
Strue.get('SettlingMax'),
127+
rtol=rtol)
128+
np.testing.assert_allclose(
129+
S.get('Overshoot'),
130+
Strue.get('Overshoot'),
131+
rtol=rtol)
132+
np.testing.assert_allclose(
133+
S.get('Undershoot'),
134+
Strue.get('Undershoot'),
135+
rtol=rtol)
136+
np.testing.assert_allclose(
137+
S.get('Peak'),
138+
Strue.get('Peak'),
139+
rtol=rtol)
140+
np.testing.assert_allclose(
141+
S.get('PeakTime'),
142+
Strue.get('PeakTime'),
143+
rtol=rtol)
144+
np.testing.assert_allclose(
145+
S.get('SteadyStateValue'),
146+
2.50,
147+
rtol=rtol)
148+
90149
def test_impulse_response(self):
91150
# Test SISO system
92151
sys = self.siso_ss1

control/timeresp.py

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
from .statesp import _convertToStateSpace, _mimo2simo, _mimo2siso
5959
from .lti import isdtime, isctime
6060

61-
__all__ = ['forced_response', 'step_response', 'initial_response',
61+
__all__ = ['forced_response', 'step_response', 'step_info', 'initial_response',
6262
'impulse_response']
6363

6464
# Helper function for checking array-like parameters
@@ -433,6 +433,98 @@ def step_response(sys, T=None, X0=0., input=None, output=None,
433433

434434
return T, yout
435435

436+
def step_info(sys, T=None, SettlingTimeThreshold=0.02, RiseTimeLimits=(0.1,0.9)):
437+
'''
438+
Step response characteristics (Rise time, Settling Time, Peak and others).
439+
440+
Parameters
441+
----------
442+
sys: StateSpace, or TransferFunction
443+
LTI system to simulate
444+
445+
T: array-like object, optional
446+
Time vector (argument is autocomputed if not given)
447+
448+
SettlingTimeThreshold: float value, optional
449+
Defines the error to compute settling time (default = 0.02)
450+
451+
RiseTimeLimits: tuple (lower_threshold, upper_theshold)
452+
Defines the lower and upper threshold for RiseTime computation
453+
454+
Returns
455+
-------
456+
S: a dictionary containing:
457+
RiseTime: Time from 10% to 90% of the steady-state value.
458+
SettlingTime: Time to enter inside a default error of 2%
459+
SettlingMin: Minimum value after RiseTime
460+
SettlingMax: Maximum value after RiseTime
461+
Overshoot: Percentage of the Peak relative to steady value
462+
Undershoot: Percentage of undershoot
463+
Peak: Absolute peak value
464+
PeakTime: time of the Peak
465+
SteadyStateValue: Steady-state value
466+
467+
468+
See Also
469+
--------
470+
step, lsim, initial, impulse
471+
472+
Examples
473+
--------
474+
>>> info = step_info(sys, T)
475+
'''
476+
sys = _get_ss_simo(sys)
477+
if T is None:
478+
if isctime(sys):
479+
T = _default_response_times(sys.A, 1000)
480+
else:
481+
# For discrete time, use integers
482+
tvec = _default_response_times(sys.A, 1000)
483+
T = range(int(np.ceil(max(tvec))))
484+
485+
T, yout = step_response(sys, T)
486+
487+
# Steady state value
488+
InfValue = yout[-1]
489+
490+
# RiseTime
491+
tr_lower_index = (np.where(yout >= RiseTimeLimits[0] * InfValue)[0])[0]
492+
tr_upper_index = (np.where(yout >= RiseTimeLimits[1] * InfValue)[0])[0]
493+
RiseTime = T[tr_upper_index] - T[tr_lower_index]
494+
495+
# SettlingTime
496+
sup_margin = (1. + SettlingTimeThreshold) * InfValue
497+
inf_margin = (1. - SettlingTimeThreshold) * InfValue
498+
# find Steady State looking for the first point out of specified limits
499+
for i in reversed(range(T.size)):
500+
if((yout[i] <= inf_margin) | (yout[i] >= sup_margin)):
501+
SettlingTime = T[i + 1]
502+
break
503+
504+
# Peak
505+
PeakIndex = np.abs(yout).argmax()
506+
PeakValue = yout[PeakIndex]
507+
PeakTime = T[PeakIndex]
508+
SettlingMax = (yout).max()
509+
SettlingMin = (yout[tr_upper_index:]).min()
510+
# I'm really not very confident about UnderShoot:
511+
UnderShoot = yout.min()
512+
OverShoot = 100. * (yout.max() - InfValue) / (InfValue - yout[0])
513+
514+
# Return as a dictionary
515+
S = {
516+
'RiseTime': RiseTime,
517+
'SettlingTime': SettlingTime,
518+
'SettlingMin': SettlingMin,
519+
'SettlingMax': SettlingMax,
520+
'Overshoot': OverShoot,
521+
'Undershoot': UnderShoot,
522+
'Peak': PeakValue,
523+
'PeakTime': PeakTime,
524+
'SteadyStateValue': InfValue
525+
}
526+
527+
return S
436528

437529
def initial_response(sys, T=None, X0=0., input=0, output=None,
438530
transpose=False, return_x=False):

0 commit comments

Comments
 (0)