LSR/env/lib/python3.6/site-packages/control/sisotool.py
2020-06-04 17:24:47 +02:00

149 lines
5.4 KiB
Python

__all__ = ['sisotool']
from .freqplot import bode_plot
from .timeresp import step_response
from .lti import issiso
import matplotlib
import matplotlib.pyplot as plt
import warnings
def sisotool(sys, kvect = None, xlim_rlocus = None, ylim_rlocus = None,
plotstr_rlocus = 'b' if int(matplotlib.__version__[0]) == 1 else 'C0',
rlocus_grid = False, omega = None, dB = None, Hz = None,
deg = None, omega_limits = None, omega_num = None,
margins_bode = True, tvect=None):
"""
Sisotool style collection of plots inspired by MATLAB's sisotool.
The left two plots contain the bode magnitude and phase diagrams.
The top right plot is a clickable root locus plot, clicking on the
root locus will change the gain of the system. The bottom left plot
shows a closed loop time response.
Parameters
----------
sys : LTI object
Linear input/output systems (SISO only)
kvect : list or ndarray, optional
List of gains to use for plotting root locus
xlim_rlocus : tuple or list, optional
control of x-axis range, normally with tuple (see matplotlib.axes)
ylim_rlocus : tuple or list, optional
control of y-axis range
plotstr_rlocus : Additional options to matplotlib
plotting style for the root locus plot(color, linestyle, etc)
rlocus_grid: boolean (default = False)
If True plot s-plane grid.
omega : freq_range
Range of frequencies in rad/sec for the bode plot
dB : boolean
If True, plot result in dB for the bode plot
Hz : boolean
If True, plot frequency in Hz for the bode plot (omega must be provided in rad/sec)
deg : boolean
If True, plot phase in degrees for the bode plot (else radians)
omega_limits: tuple, list, ... of two values
Limits of the to generate frequency vector.
If Hz=True the limits are in Hz otherwise in rad/s.
omega_num: int
number of samples
margins_bode : boolean
If True, plot gain and phase margin in the bode plot
tvect : list or ndarray, optional
List of timesteps to use for closed loop step response
Examples
--------
>>> sys = tf([1000], [1,25,100,0])
>>> sisotool(sys)
"""
from .rlocus import root_locus
# Check if it is a single SISO system
issiso(sys,strict=True)
# Setup sisotool figure or superimpose if one is already present
fig = plt.gcf()
if fig.canvas.get_window_title() != 'Sisotool':
plt.close(fig)
fig,axes = plt.subplots(2, 2)
fig.canvas.set_window_title('Sisotool')
# Extract bode plot parameters
bode_plot_params = {
'omega': omega,
'dB': dB,
'Hz': Hz,
'deg': deg,
'omega_limits': omega_limits,
'omega_num' : omega_num,
'sisotool': True,
'fig': fig,
'margins': margins_bode
}
# First time call to setup the bode and step response plots
_SisotoolUpdate(sys, fig,1 if kvect is None else kvect[0],bode_plot_params)
# Setup the root-locus plot window
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)
def _SisotoolUpdate(sys,fig,K,bode_plot_params,tvect=None):
if int(matplotlib.__version__[0]) == 1:
title_font_size = 12
label_font_size = 10
else:
title_font_size = 10
label_font_size = 8
# Get the subaxes and clear them
ax_mag,ax_rlocus,ax_phase,ax_step = fig.axes[0],fig.axes[1],fig.axes[2],fig.axes[3]
# Catch matplotlib 2.1.x and higher userwarnings when clearing a log axis
with warnings.catch_warnings():
warnings.simplefilter("ignore")
ax_step.clear(), ax_mag.clear(), ax_phase.clear()
# Update the bodeplot
bode_plot_params['syslist'] = sys*K.real
bode_plot(**bode_plot_params)
# Set the titles and labels
ax_mag.set_title('Bode magnitude',fontsize = title_font_size)
ax_mag.set_ylabel(ax_mag.get_ylabel(), fontsize=label_font_size)
ax_phase.set_title('Bode phase',fontsize=title_font_size)
ax_phase.set_xlabel(ax_phase.get_xlabel(),fontsize=label_font_size)
ax_phase.set_ylabel(ax_phase.get_ylabel(),fontsize=label_font_size)
ax_phase.get_xaxis().set_label_coords(0.5, -0.15)
ax_phase.get_shared_x_axes().join(ax_phase, ax_mag)
ax_step.set_title('Step response',fontsize = title_font_size)
ax_step.set_xlabel('Time (seconds)',fontsize=label_font_size)
ax_step.set_ylabel('Amplitude',fontsize=label_font_size)
ax_step.get_xaxis().set_label_coords(0.5, -0.15)
ax_step.get_yaxis().set_label_coords(-0.15, 0.5)
ax_rlocus.set_title('Root locus',fontsize = title_font_size)
ax_rlocus.set_ylabel('Imag', fontsize=label_font_size)
ax_rlocus.set_xlabel('Real', fontsize=label_font_size)
ax_rlocus.get_xaxis().set_label_coords(0.5, -0.15)
ax_rlocus.get_yaxis().set_label_coords(-0.15, 0.5)
# Generate the step response and plot it
sys_closed = (K*sys).feedback(1)
if tvect is None:
tvect, yout = step_response(sys_closed)
else:
tvect, yout = step_response(sys_closed,tvect)
ax_step.plot(tvect, yout)
ax_step.axhline(1.,linestyle=':',color='k',zorder=-20)
# Manually adjust the spacing and draw the canvas
fig.subplots_adjust(top=0.9,wspace = 0.3,hspace=0.35)
fig.canvas.draw()