Skip to content

Commit 9b10121

Browse files
committed
sisotool's step response plot can be something other than feedback(K*sys) by providing a 2-input, 2-output system; some visual cleanup
1 parent 26a595f commit 9b10121

2 files changed

Lines changed: 43 additions & 16 deletions

File tree

control/rlocus.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,12 +241,12 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None,
241241
else:
242242
_sgrid_func()
243243
else:
244-
ax.axhline(0., linestyle=':', color='k', zorder=-20)
245-
ax.axvline(0., linestyle=':', color='k', zorder=-20)
244+
ax.axhline(0., linestyle=':', color='k', linewidth=.75, zorder=-20)
245+
ax.axvline(0., linestyle=':', color='k', linewidth=.75, zorder=-20)
246246
if isdtime(sys, strict=True):
247247
ax.add_patch(plt.Circle(
248248
(0, 0), radius=1.0, linestyle=':', edgecolor='k',
249-
linewidth=1.5, fill=False, zorder=-20))
249+
linewidth=0.75, fill=False, zorder=-20))
250250

251251
return mymat, kvect
252252

control/sisotool.py

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
__all__ = ['sisotool']
22

3+
from control.exception import ControlMIMONotImplemented
34
from .freqplot import bode_plot
45
from .timeresp import step_response
56
from .lti import issiso, isdtime
7+
from .xferfcn import TransferFunction
8+
from .bdalg import append, connect
69
import matplotlib
710
import matplotlib.pyplot as plt
811
import warnings
@@ -22,7 +25,11 @@ def sisotool(sys, kvect = None, xlim_rlocus = None, ylim_rlocus = None,
2225
Parameters
2326
----------
2427
sys : LTI object
25-
Linear input/output systems (SISO only)
28+
Linear input/output systems. If sys is SISO, use the same
29+
system for the root locus and step response. If sys is
30+
two-input, two-output, insert the selected gain between the
31+
first output and first input and use the second input and output
32+
for computing the step response.
2633
kvect : list or ndarray, optional
2734
List of gains to use for plotting root locus
2835
xlim_rlocus : tuple or list, optional
@@ -60,8 +67,14 @@ def sisotool(sys, kvect = None, xlim_rlocus = None, ylim_rlocus = None,
6067
"""
6168
from .rlocus import root_locus
6269

63-
# Check if it is a single SISO system
64-
issiso(sys,strict=True)
70+
# sys as loop transfer function if SISO
71+
if sys.issiso():
72+
sys_loop = sys
73+
else:
74+
if not sys.ninputs == 2 and sys.noutputs == 2:
75+
raise ControlMIMONotImplemented(
76+
'sys must be SISO or 2-input, 2-output')
77+
sys_loop = sys[0,0]
6578

6679
# Setup sisotool figure or superimpose if one is already present
6780
fig = plt.gcf()
@@ -84,12 +97,15 @@ def sisotool(sys, kvect = None, xlim_rlocus = None, ylim_rlocus = None,
8497
}
8598

8699
# First time call to setup the bode and step response plots
87-
_SisotoolUpdate(sys, fig,1 if kvect is None else kvect[0],bode_plot_params)
100+
_SisotoolUpdate(sys, fig,
101+
1 if kvect is None else kvect[0], bode_plot_params)
88102

89103
# Setup the root-locus plot window
90-
root_locus(sys,kvect=kvect,xlim=xlim_rlocus,ylim = ylim_rlocus,plotstr=plotstr_rlocus,grid = rlocus_grid,fig=fig,bode_plot_params=bode_plot_params,tvect=tvect,sisotool=True)
104+
root_locus(sys_loop, kvect=kvect, xlim=xlim_rlocus,
105+
ylim=ylim_rlocus, plotstr=plotstr_rlocus, grid=rlocus_grid,
106+
fig=fig, bode_plot_params=bode_plot_params, tvect=tvect, sisotool=True)
91107

92-
def _SisotoolUpdate(sys,fig,K,bode_plot_params,tvect=None):
108+
def _SisotoolUpdate(sys, fig, K, bode_plot_params, tvect=None):
93109

94110
if int(matplotlib.__version__[0]) == 1:
95111
title_font_size = 12
@@ -99,49 +115,60 @@ def _SisotoolUpdate(sys,fig,K,bode_plot_params,tvect=None):
99115
label_font_size = 8
100116

101117
# Get the subaxes and clear them
102-
ax_mag,ax_rlocus,ax_phase,ax_step = fig.axes[0],fig.axes[1],fig.axes[2],fig.axes[3]
118+
ax_mag, ax_rlocus, ax_phase, ax_step = \
119+
fig.axes[0], fig.axes[1], fig.axes[2], fig.axes[3]
103120

104121
# Catch matplotlib 2.1.x and higher userwarnings when clearing a log axis
105122
with warnings.catch_warnings():
106123
warnings.simplefilter("ignore")
107124
ax_step.clear(), ax_mag.clear(), ax_phase.clear()
108125

126+
sys_loop = sys if sys.issiso() else sys[0,0]
127+
109128
# Update the bodeplot
110-
bode_plot_params['syslist'] = sys*K.real
129+
bode_plot_params['syslist'] = sys_loop*K.real
111130
bode_plot(**bode_plot_params)
112131

113132
# Set the titles and labels
114133
ax_mag.set_title('Bode magnitude',fontsize = title_font_size)
115134
ax_mag.set_ylabel(ax_mag.get_ylabel(), fontsize=label_font_size)
135+
ax_mag.tick_params(axis='both', which='major', labelsize=label_font_size)
116136

117137
ax_phase.set_title('Bode phase',fontsize=title_font_size)
118138
ax_phase.set_xlabel(ax_phase.get_xlabel(),fontsize=label_font_size)
119139
ax_phase.set_ylabel(ax_phase.get_ylabel(),fontsize=label_font_size)
120140
ax_phase.get_xaxis().set_label_coords(0.5, -0.15)
121141
ax_phase.get_shared_x_axes().join(ax_phase, ax_mag)
142+
ax_phase.tick_params(axis='both', which='major', labelsize=label_font_size)
122143

123144
ax_step.set_title('Step response',fontsize = title_font_size)
124145
ax_step.set_xlabel('Time (seconds)',fontsize=label_font_size)
125146
ax_step.set_ylabel('Amplitude',fontsize=label_font_size)
126147
ax_step.get_xaxis().set_label_coords(0.5, -0.15)
127148
ax_step.get_yaxis().set_label_coords(-0.15, 0.5)
149+
ax_step.tick_params(axis='both', which='major', labelsize=label_font_size)
128150

129151
ax_rlocus.set_title('Root locus',fontsize = title_font_size)
130152
ax_rlocus.set_ylabel('Imag', fontsize=label_font_size)
131153
ax_rlocus.set_xlabel('Real', fontsize=label_font_size)
132154
ax_rlocus.get_xaxis().set_label_coords(0.5, -0.15)
133155
ax_rlocus.get_yaxis().set_label_coords(-0.15, 0.5)
134-
135-
156+
ax_rlocus.tick_params(axis='both', which='major',labelsize=label_font_size)
136157

137158
# Generate the step response and plot it
138-
sys_closed = (K*sys).feedback(1)
159+
if sys.issiso():
160+
sys_closed = (K*sys).feedback(1)
161+
else:
162+
sys_closed = append(sys, K)
163+
connects = [[1, 3],
164+
[3, 1]]
165+
sys_closed = connect(sys_closed, connects, (2,), (2,))
139166
if tvect is None:
140167
tvect, yout = step_response(sys_closed, T_num=100)
141168
else:
142-
tvect, yout = step_response(sys_closed,tvect)
169+
tvect, yout = step_response(sys_closed, tvect)
143170
if isdtime(sys_closed, strict=True):
144-
ax_step.plot(tvect, yout, 'o')
171+
ax_step.plot(tvect, yout, '.')
145172
else:
146173
ax_step.plot(tvect, yout)
147174
ax_step.axhline(1.,linestyle=':',color='k',zorder=-20)

0 commit comments

Comments
 (0)