Skip to content

Commit 5c79fc8

Browse files
authored
Merge pull request #441 from bnavigator/zero-poles-time-vector
avoid log(0) in automatic timevector calculation
2 parents 76ec2de + 920649b commit 5c79fc8

File tree

1 file changed

+41
-42
lines changed

1 file changed

+41
-42
lines changed

control/timeresp.py

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
Initial Author: Eike Welk
6262
Date: 12 May 2011
6363
64-
Modified: Sawyer B. Fuller (minster@uw.edu) to add discrete-time
64+
Modified: Sawyer B. Fuller (minster@uw.edu) to add discrete-time
6565
capability and better automatic time vector creation
6666
Date: June 2020
6767
@@ -463,14 +463,14 @@ def step_response(sys, T=None, X0=0., input=None, output=None, T_num=None,
463463
LTI system to simulate
464464
465465
T: array-like or number, optional
466-
Time vector, or simulation time duration if a number. If T is not
467-
provided, an attempt is made to create it automatically from the
468-
dynamics of sys. If sys is continuous-time, the time increment dt
469-
is chosen small enough to show the fastest mode, and the simulation
470-
time period tfinal long enough to show the slowest mode, excluding
466+
Time vector, or simulation time duration if a number. If T is not
467+
provided, an attempt is made to create it automatically from the
468+
dynamics of sys. If sys is continuous-time, the time increment dt
469+
is chosen small enough to show the fastest mode, and the simulation
470+
time period tfinal long enough to show the slowest mode, excluding
471471
poles at the origin. If this results in too many time steps (>5000),
472-
dt is reduced. If sys is discrete-time, only tfinal is computed, and
473-
tfinal is reduced if it requires too many simulation steps.
472+
dt is reduced. If sys is discrete-time, only tfinal is computed, and
473+
tfinal is reduced if it requires too many simulation steps.
474474
475475
X0: array-like or number, optional
476476
Initial condition (default = 0)
@@ -483,10 +483,10 @@ def step_response(sys, T=None, X0=0., input=None, output=None, T_num=None,
483483
output: int
484484
Index of the output that will be used in this simulation. Set to None
485485
to not trim outputs
486-
486+
487487
T_num: number, optional
488-
Number of time steps to use in simulation if T is not provided as an
489-
array (autocomputed if not given); ignored if sys is discrete-time.
488+
Number of time steps to use in simulation if T is not provided as an
489+
array (autocomputed if not given); ignored if sys is discrete-time.
490490
491491
transpose: bool
492492
If True, transpose all input and output arrays (for backward
@@ -550,12 +550,12 @@ def step_info(sys, T=None, T_num=None, SettlingTimeThreshold=0.02,
550550
LTI system to simulate
551551
552552
T: array-like or number, optional
553-
Time vector, or simulation time duration if a number (time vector is
553+
Time vector, or simulation time duration if a number (time vector is
554554
autocomputed if not given, see :func:`step_response` for more detail)
555555
556556
T_num: number, optional
557-
Number of time steps to use in simulation if T is not provided as an
558-
array (autocomputed if not given); ignored if sys is discrete-time.
557+
Number of time steps to use in simulation if T is not provided as an
558+
array (autocomputed if not given); ignored if sys is discrete-time.
559559
560560
SettlingTimeThreshold: float value, optional
561561
Defines the error to compute settling time (default = 0.02)
@@ -588,7 +588,7 @@ def step_info(sys, T=None, T_num=None, SettlingTimeThreshold=0.02,
588588
sys = _get_ss_simo(sys)
589589
if T is None or np.asarray(T).size == 1:
590590
T = _default_time_vector(sys, N=T_num, tfinal=T)
591-
591+
592592
T, yout = step_response(sys, T)
593593

594594
# Steady state value
@@ -640,7 +640,7 @@ def initial_response(sys, T=None, X0=0., input=0, output=None, T_num=None,
640640
LTI system to simulate
641641
642642
T: array-like or number, optional
643-
Time vector, or simulation time duration if a number (time vector is
643+
Time vector, or simulation time duration if a number (time vector is
644644
autocomputed if not given; see :func:`step_response` for more detail)
645645
646646
X0: array-like or number, optional
@@ -655,10 +655,10 @@ def initial_response(sys, T=None, X0=0., input=0, output=None, T_num=None,
655655
output: int
656656
Index of the output that will be used in this simulation. Set to None
657657
to not trim outputs
658-
658+
659659
T_num: number, optional
660-
Number of time steps to use in simulation if T is not provided as an
661-
array (autocomputed if not given); ignored if sys is discrete-time.
660+
Number of time steps to use in simulation if T is not provided as an
661+
array (autocomputed if not given); ignored if sys is discrete-time.
662662
663663
transpose: bool
664664
If True, transpose all input and output arrays (for backward
@@ -730,7 +730,7 @@ def impulse_response(sys, T=None, X0=0., input=0, output=None, T_num=None,
730730
LTI system to simulate
731731
732732
T: array-like or number, optional
733-
Time vector, or simulation time duration if a number (time vector is
733+
Time vector, or simulation time duration if a number (time vector is
734734
autocomputed if not given; see :func:`step_response` for more detail)
735735
736736
X0: array-like or number, optional
@@ -746,8 +746,8 @@ def impulse_response(sys, T=None, X0=0., input=0, output=None, T_num=None,
746746
to not trim outputs
747747
748748
T_num: number, optional
749-
Number of time steps to use in simulation if T is not provided as an
750-
array (autocomputed if not given); ignored if sys is discrete-time.
749+
Number of time steps to use in simulation if T is not provided as an
750+
array (autocomputed if not given); ignored if sys is discrete-time.
751751
752752
transpose: bool
753753
If True, transpose all input and output arrays (for backward
@@ -830,56 +830,55 @@ def _ideal_tfinal_and_dt(sys):
830830
constant = 7.0
831831
tolerance = 1e-10
832832
A = ssdata(sys)[0]
833-
if A.shape == (0,0):
833+
if A.shape == (0,0):
834834
# no dynamics
835-
tfinal = constant * 1.0
835+
tfinal = constant * 1.0
836836
dt = sys.dt if isdtime(sys, strict=True) else 1.0
837837
else:
838838
poles = sp.linalg.eigvals(A)
839-
if isdtime(sys, strict=True):
840-
poles = np.log(poles)/sys.dt # z-poles to s-plane using s=(lnz)/dt
841-
842839
# calculate ideal dt
843840
if isdtime(sys, strict=True):
841+
# z-poles to s-plane using s=(lnz)/dt, no ln(0)
842+
poles = np.log(poles[abs(poles) > 0])/sys.dt
844843
dt = sys.dt
845844
else:
846845
fastest_natural_frequency = max(abs(poles))
847846
dt = 1/constant / fastest_natural_frequency
848-
847+
849848
# calculate ideal tfinal
850849
poles = poles[abs(poles.real) > tolerance] # ignore poles near im axis
851-
if poles.size == 0:
850+
if poles.size == 0:
852851
slowest_decay_rate = 1.0
853852
else:
854853
slowest_decay_rate = min(abs(poles.real))
855-
tfinal = constant / slowest_decay_rate
854+
tfinal = constant / slowest_decay_rate
856855

857856
return tfinal, dt
858857

859-
# test below: ct with pole at the origin is 7 seconds, ct with pole at .5 is 14 s long,
858+
# test below: ct with pole at the origin is 7 seconds, ct with pole at .5 is 14 s long,
860859
def _default_time_vector(sys, N=None, tfinal=None):
861-
"""Returns a time vector suitable for observing the response of the
862-
both the slowest poles and fastest resonant modes. if system is
860+
"""Returns a time vector suitable for observing the response of the
861+
both the slowest poles and fastest resonant modes. if system is
863862
discrete-time, N is ignored """
864-
863+
865864
N_max = 5000
866865
N_min_ct = 100
867866
N_min_dt = 7 # more common to see just a few samples in discrete-time
868-
867+
869868
ideal_tfinal, ideal_dt = _ideal_tfinal_and_dt(sys)
870-
869+
871870
if isdtime(sys, strict=True):
872-
if tfinal is None:
871+
if tfinal is None:
873872
# for discrete time, change from ideal_tfinal if N too large/small
874873
N = int(np.clip(ideal_tfinal/sys.dt, N_min_dt, N_max))# [N_min, N_max]
875874
tfinal = sys.dt * N
876-
else:
875+
else:
877876
N = int(tfinal/sys.dt)
878-
else:
879-
if tfinal is None:
877+
else:
878+
if tfinal is None:
880879
# for continuous time, simulate to ideal_tfinal but limit N
881880
tfinal = ideal_tfinal
882-
if N is None:
881+
if N is None:
883882
N = int(np.clip(tfinal/ideal_dt, N_min_ct, N_max)) # N<-[N_min, N_max]
884-
883+
885884
return np.linspace(0, tfinal, N, endpoint=False)

0 commit comments

Comments
 (0)