Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Step info improve jpp#583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 121 additions & 1 deletioncontrol/tests/timeresp_test.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -222,6 +222,15 @@ def tf2_matlab_help(self):
D = [0.06]
sys = StateSpace(A, B, C, D, 0.2)
return sys

@pytest.fixture
def mimo_matlab_help(self):
A = [[0.68, -0.34], [0.34, 0.68]];
B = [[0.18, -0.05], [0.04, 0.11]];
C = [[0, -1.53], [-1.12, -1.10]];
D = [[0, 0], [0.06, -0.37]];
sys = StateSpace(A,B,C,D,0.2);
return sys

@pytest.fixture
def tsystem(self,
Expand All@@ -233,7 +242,7 @@ def tsystem(self,
mimo_dss1, mimo_dss2, mimo_dtf1,
pole_cancellation, no_pole_cancellation, siso_tf_type1,
siso_tf_kpos, siso_tf_kneg, tf1_matlab_help,
tf2_matlab_help):
tf2_matlab_help, mimo_matlab_help):
systems = {"siso_ss1": siso_ss1,
"siso_ss2": siso_ss2,
"siso_tf1": siso_tf1,
Expand All@@ -256,6 +265,7 @@ def tsystem(self,
"siso_tf_kneg": siso_tf_kneg,
"tf1_matlab_help": tf1_matlab_help,
"tf2_matlab_help": tf2_matlab_help,
"mimo_matlab_help":mimo_matlab_help,
}
return systems[request.param]

Expand DownExpand Up@@ -341,6 +351,116 @@ def test_step_info(self):

# tolerance for all parameters could be wrong for some systems
# discrete systems time parameters tolerance could be +/-dt
def test_step_info(self):
# From matlab docs:
sys = TransferFunction([1, 5, 5], [1, 1.65, 5, 6.5, 2])
Strue = {
'RiseTime': 3.8456,
'SettlingTime': 27.9762,
'SettlingMin': 2.0689,
'SettlingMax': 2.6873,
'Overshoot': 7.4915,
'Undershoot': 0,
'Peak': 2.6873,
'PeakTime': 8.0530,
'SteadyStateValue': 2.50
}

S = step_info(sys)

Sk = sorted(S.keys())
Sktrue = sorted(Strue.keys())
assert Sk == Sktrue
# Very arbitrary tolerance because I don't know if the
# response from the MATLAB is really that accurate.
# maybe it is a good idea to change the Strue to match
# but I didn't do it because I don't know if it is
# accurate either...
rtol = 2e-2
np.testing.assert_allclose([S[k] for k in Sk],
[Strue[k] for k in Sktrue],
rtol=rtol)

def test_step_info_mimo(self):
# From matlab docs:
A = [[0.68, -0.34], [0.34, 0.68]];
B = [[0.18, -0.05], [0.04, 0.11]];
C = [[0, -1.53], [-1.12, -1.10]];
D = [[0, 0], [0.06, -0.37]];
sys = StateSpace(A,B,C,D,0.2);

Strue = [[{'RiseTime': 0.6000,
'SettlingTime': 3.0,
'SettlingMin': -0.5999,
'SettlingMax': -0.4689,
'Overshoot': 15.5072,
'Undershoot': 0,
'Peak': 0.5999,
'PeakTime': 1.4000,
'SteadyStateValue': -0.51935},
{'RiseTime': 0.0,
'SettlingTime': 3.6000,
'SettlingMin': -0.2797,
'SettlingMax': -0.1043,
'Overshoot': 118.9918,
'Undershoot': 0,
'Peak': 0.2797,
'PeakTime': 0.60000,
'SteadyStateValue': -0.1277}],
[{'RiseTime': 0.40000,
'SettlingTime': 2.8,
'SettlingMin': -0.6724,
'SettlingMax': -0.5188,
'Overshoot': 24.6476,
'Undershoot': 11.1224,
'Peak': 0.6724,
'PeakTime': 1.0,
'SteadyStateValue': -0.5394},
{'RiseTime': 0.0,
'SettlingTime': 3.4,
'SettlingMin': -0.435, #this value is not a result of step_info
'SettlingMax': -0.1485,
'Overshoot': 132.0170,
'Undershoot': 0,
'Peak': 0.4350,
'PeakTime': 0.2,
'SteadyStateValue': -0.18748}]]

S = step_info(sys)

S1k = sorted(S[0][0].keys())
S1ktrue = sorted(Strue[0][0].keys())
assert S1k == S1ktrue

S2k = sorted(S[0][1].keys())
S2ktrue = sorted(Strue[0][1].keys())
assert S2k == S2ktrue

S3k = sorted(S[1][0].keys())
S3ktrue = sorted(Strue[1][0].keys())
assert S3k == S3ktrue

S4k = sorted(S[1][1].keys())
S4ktrue = sorted(Strue[1][1].keys())
assert S4k == S4ktrue

rtol = 0.2 # tolerance could be better was chosen to meet the discret times parameters
np.testing.assert_allclose([S[0][0][k] for k in S1k],
[Strue[0][0][k] for k in S1ktrue],
rtol=rtol)
np.testing.assert_allclose([S[0][1][k] for k in S2k],
[Strue[0][1][k] for k in S2ktrue],
rtol=rtol)
np.testing.assert_allclose([S[1][0][k] for k in S3k],
[Strue[1][0][k] for k in S3ktrue],
rtol=rtol)
np.testing.assert_allclose([S[1][1][k] for k in S4k],
[Strue[1][1][k] for k in S4ktrue],
rtol=rtol)

# Tolerance for all parameters could be wrong for some systems
# discrete systems time parameters tolerance
# could be +/-dt
@pytest.mark.parametrize(
"tsystem, info_true, tolerance",
[("tf1_matlab_help", {
Expand Down
208 changes: 127 additions & 81 deletionscontrol/timeresp.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -746,7 +746,7 @@ def step_info(sys, T=None, T_num=None, SettlingTimeThreshold=0.02,

Parameters
----------
sys : SISO dynamic system model. Dynamic systems that you can use include:
sys : SISOor MIMOdynamic system model. Dynamic systems that you can use include:
StateSpace or TransferFunction
LTI system to simulate

Expand All@@ -766,101 +766,147 @@ def step_info(sys, T=None, T_num=None, SettlingTimeThreshold=0.02,

Returns
-------
S: a dictionary containing:
RiseTime: Time from 10% to 90% of the steady-state value.
S: a dictionary for SISO systems or a list of Ny-by-Nu of dictionaries
for MIMO systems, each dictionary containing:

RiseTime: Rise time (by default time from 10% to 90% of the steady-state value).
SettlingTime: Time to enter inside a default error of 2%
SettlingMin: Minimum value after RiseTime
SettlingMax: Maximum value after RiseTime
SettlingMin: Minimum valueof `y(t)`after RiseTime
SettlingMax: Maximum valueof `y(t)`after RiseTime
Overshoot: Percentage of the Peak relative to steady value
Undershoot: Percentage of undershoot
Peak:Absolute peak value
PeakTime:time of thePeak
Undershoot: Percentage of undershoot
Peak:Peak absolute value of `y(t)`
PeakTime:Time of thepeak value
SteadyStateValue: Steady-state value


See Also
--------
step, lsim, initial, impulse
step_response, initial_response, impulse_response, forced_response


Examples
--------
>>> info = step_info(sys, T)
>>> from control import tf, step_info
>>> sys = tf([3.32, 0, -162.8],[1, 24.56, 186.5, 457.8, 116.2])
>>> step_info(sys)
>>>
>>> {'RiseTime': 7.70245002940221,
>>> 'SettlingTime': 14.13151114265325,
>>> 'SettlingMin': -1.3994264225116284,
>>> 'SettlingMax': -1.2612640212267976,
>>> 'Overshoot': 0,
>>> 'Undershoot': 0.6864433321178778,
>>> 'Peak': 1.3994264225116284,
>>> 'PeakTime': 24.13227287437709,
>>> 'SteadyStateValue': -1.4010327022375215}

'''

if not sys.issiso():
sys = _mimo2siso(sys,0,0)
warnings.warn(" Internal conversion from a MIMO system to a SISO system,"
" the first input and the first output were used (u1 -> y1);"
" it may not be the result you are looking for")

if T is None or np.asarray(T).size == 1:
if T is None:
T, y = step_response(sys)
elif np.asarray(T).size == 1:
T = _default_time_vector(sys, N=T_num, tfinal=T, is_step=True)

T, yout = step_response(sys, T)
T, y = step_response(sys, T)
else:
T, y = step_response(sys, T)

# Steady state value
InfValue = sys.dcgain().real

# TODO: this could be a function step_info4data(t,y,yfinal)
rise_time: float = np.NaN
settling_time: float = np.NaN
settling_min: float = np.NaN
settling_max: float = np.NaN
peak_value: float = np.Inf
peak_time: float = np.Inf
undershoot: float = np.NaN
overshoot: float = np.NaN
steady_state_value: float = np.NaN

if not np.isnan(InfValue) and not np.isinf(InfValue):
# SteadyStateValue
steady_state_value = InfValue
# Peak
peak_index = np.abs(yout).argmax()
peak_value = np.abs(yout[peak_index])
peak_time = T[peak_index]

sup_margin = (1. + SettlingTimeThreshold) * InfValue
inf_margin = (1. - SettlingTimeThreshold) * InfValue

# RiseTime
tr_lower_index = (np.where(np.sign(InfValue.real) * (yout- RiseTimeLimits[0] * InfValue) >= 0 )[0])[0]
tr_upper_index = (np.where(np.sign(InfValue.real) * yout >= np.sign(InfValue.real) * RiseTimeLimits[1] * InfValue)[0])[0]

# SettlingTime
settling_time = T[np.where(np.abs(yout-InfValue) >= np.abs(SettlingTimeThreshold*InfValue))[0][-1]+1]
# Overshoot and Undershoot
y_os = (np.sign(InfValue.real)*yout).max()
dy_os = np.abs(y_os) - np.abs(InfValue)
if dy_os > 0:
overshoot = np.abs(100. * dy_os / InfValue)
else:
overshoot = 0

y_us = (np.sign(InfValue.real)*yout).min()
dy_us = np.abs(y_us)
if dy_us > 0:
undershoot = np.abs(100. * dy_us / InfValue)
else:
undershoot = 0

# RiseTime
rise_time = T[tr_upper_index] - T[tr_lower_index]

settling_max = (yout[tr_upper_index:]).max()
settling_min = (yout[tr_upper_index:]).min()

return {
'RiseTime': rise_time,
'SettlingTime': settling_time,
'SettlingMin': settling_min,
'SettlingMax': settling_max,
'Overshoot': overshoot,
'Undershoot': undershoot,
'Peak': peak_value,
'PeakTime': peak_time,
'SteadyStateValue': steady_state_value
}
yfinal = sys.dcgain().real

# TODO: this could be a function step_info4data(t,y,yfinal)
# y: Step-response data, specified as:
# For SISO response data, a vector of length Ns, where Ns is the number of samples in the response data.
# For MIMO response data, an Ny-by-Nu-by-Ns array, where Ny is the number of system outputs, and Nu is the number of system inputs.
# T: Time vector corresponding to the response data in y, specified as a vector of length Ns.

# TODO: check dimentions for time vs step output and final output
if len(y.shape) == 3: # MIMO
Ny,Nu,Ns = y.shape
elif len(y.shape) == 1: # SISO
Ns = y.shape
Ny = 1
Nu = 1
else:
raise TypeError("Poner un mensaje")

S = [[[] for x in range(Ny)] for x in range(Nu)]

# Struct for each couple of inputs
for i in range(Ny): # Ny
for j in range(Nu): # Nu
rise_time: float = np.NaN
settling_time: float = np.NaN
settling_min: float = np.NaN
settling_max: float = np.NaN
peak_value: float = np.Inf
peak_time: float = np.Inf
undershoot: float = np.NaN
overshoot: float = np.NaN
steady_state_value: float = np.NaN

if len(y.shape) > 1:
yout = y[i,j,:]
InfValue = yfinal[i,j]
else:
yout = y
InfValue = yfinal

InfValue_sign = np.sign(InfValue)

if not np.isnan(InfValue) and not np.isinf(InfValue):
# SteadyStateValue
steady_state_value = InfValue
# Peak
peak_index = np.abs(yout).argmax()
peak_value = np.abs(yout[peak_index])
peak_time = T[peak_index]

sup_margin = (1. + SettlingTimeThreshold) * InfValue
inf_margin = (1. - SettlingTimeThreshold) * InfValue

# RiseTime
tr_lower_index = (np.where(InfValue_sign * (yout- RiseTimeLimits[0] * InfValue) >= 0 )[0])[0]
tr_upper_index = (np.where(InfValue_sign * yout >= InfValue_sign * RiseTimeLimits[1] * InfValue)[0])[0]

# SettlingTime
settling_time = T[np.where(np.abs(yout-InfValue) >= np.abs(SettlingTimeThreshold*InfValue))[0][-1]+1]
# Overshoot and Undershoot
y_os = (InfValue_sign*yout).max()
dy_os = np.abs(y_os) - np.abs(InfValue)
if dy_os > 0:
overshoot = np.abs(100. * dy_os / InfValue)
else:
overshoot = 0

y_us = (InfValue_sign*yout).min()
y_us_index = (InfValue_sign*yout).argmin()
if (InfValue_sign * yout[y_us_index]) < 0: # must have oposite sign
undershoot = np.abs(100. * np.abs(y_us) / InfValue)
else:
undershoot = 0

# RiseTime
rise_time = T[tr_upper_index] - T[tr_lower_index]

settling_max = (yout[tr_upper_index:]).max()
settling_min = (yout[tr_upper_index:]).min()

S[i][j] = {
'RiseTime': rise_time,
'SettlingTime': settling_time,
'SettlingMin': settling_min,
'SettlingMax': settling_max,
'Overshoot': overshoot,
'Undershoot': undershoot,
'Peak': peak_value,
'PeakTime': peak_time,
'SteadyStateValue': steady_state_value
}
if Ny > 1 or Nu > 1:
return S
else:
return S[0][0]

def initial_response(sys, T=None, X0=0., input=0, output=None, T_num=None,
transpose=False, return_x=False, squeeze=None):
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp