Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix plot issues #382

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions control/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ def use_matlab_defaults():

The following conventions are used:
* Bode plots plot gain in dB, phase in degrees, frequency in
Hertz, with grids
rad/sec, with grids
* State space class and functions use Numpy matrix objects

"""
set_defaults('bode', dB=True, deg=True, Hz=True, grid=True)
set_defaults('bode', dB=True, deg=True, Hz=False, grid=True)
set_defaults('statesp', use_numpy_matrix=True)


Expand All @@ -128,7 +128,7 @@ def use_fbs_defaults():

The following conventions are used:
* Bode plots plot gain in powers of ten, phase in degrees,
frequency in Hertz, no grid
frequency in rad/sec, no grid

"""
set_defaults('bode', dB=False, deg=True, Hz=False, grid=False)
Expand Down
109 changes: 76 additions & 33 deletions control/freqplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@


def bode_plot(syslist, omega=None,
Plot=True, omega_limits=None, omega_num=None,
plot=True, omega_limits=None, omega_num=None,
margins=None, *args, **kwargs):
"""Bode plot for a system

Expand All @@ -100,7 +100,7 @@ def bode_plot(syslist, omega=None,
deg : bool
If True, plot phase in degrees (else radians). Default value (True)
config.defaults['bode.deg']
Plot : bool
plot : bool
If True (default), plot magnitude and phase
omega_limits: tuple, list, ... of two values
Limits of the to generate frequency vector.
Expand All @@ -110,9 +110,9 @@ def bode_plot(syslist, omega=None,
config.defaults['freqplot.number_of_samples'].
margins : bool
If True, plot gain and phase margin.
*args
Additional arguments for :func:`matplotlib.plot` (color, linestyle, etc)
**kwargs:
*args : `matplotlib` plot positional properties, optional
Additional arguments for `matplotlib` plots (color, linestyle, etc)
**kwargs : `matplotlib` plot keyword properties, optional
Additional keywords (passed to `matplotlib`)

Returns
Expand Down Expand Up @@ -153,12 +153,20 @@ def bode_plot(syslist, omega=None,
# Make a copy of the kwargs dictonary since we will modify it
kwargs = dict(kwargs)

# Check to see if legacy 'Plot' keyword was used
if 'Plot' in kwargs:
import warnings
warnings.warn("'Plot' keyword is deprecated in bode_plot; use 'plot'",
FutureWarning)
# Map 'Plot' keyword to 'plot' keyword
plot = kwargs.pop('Plot')

# Get values for params (and pop from list to allow keyword use in plot)
dB = config._get_param('bode', 'dB', kwargs, _bode_defaults, pop=True)
deg = config._get_param('bode', 'deg', kwargs, _bode_defaults, pop=True)
Hz = config._get_param('bode', 'Hz', kwargs, _bode_defaults, pop=True)
grid = config._get_param('bode', 'grid', kwargs, _bode_defaults, pop=True)
Plot = config._get_param('bode', 'grid', Plot, True)
plot = config._get_param('bode', 'grid', plot, True)
margins = config._get_param('bode', 'margins', margins, False)

# If argument was a singleton, turn it into a list
Expand Down Expand Up @@ -211,7 +219,7 @@ def bode_plot(syslist, omega=None,
# Get the dimensions of the current axis, which we will divide up
# TODO: Not current implemented; just use subplot for now

if Plot:
if plot:
nyquistfrq_plot = None
if Hz:
omega_plot = omega_sys / (2. * math.pi)
Expand Down Expand Up @@ -429,12 +437,13 @@ def gen_zero_centered_series(val_min, val_max, period):
else:
return mags, phases, omegas


#
# Nyquist plot
#

def nyquist_plot(syslist, omega=None, Plot=True,
labelFreq=0, arrowhead_length=0.1, arrowhead_width=0.1,
def nyquist_plot(syslist, omega=None, plot=True, label_freq=0,
arrowhead_length=0.1, arrowhead_width=0.1,
color=None, *args, **kwargs):
"""
Nyquist plot for a system
Expand All @@ -451,13 +460,13 @@ def nyquist_plot(syslist, omega=None, Plot=True,
If True, plot magnitude
color : string
Used to specify the color of the plot
labelFreq : int
label_freq : int
Label every nth frequency on the plot
arrowhead_width : arrow head width
arrowhead_length : arrow head length
*args
Additional arguments for :func:`matplotlib.plot` (color, linestyle, etc)
**kwargs:
*args : `matplotlib` plot positional properties, optional
Additional arguments for `matplotlib` plots (color, linestyle, etc)
**kwargs : `matplotlib` plot keyword properties, optional
Additional keywords (passed to `matplotlib`)

Returns
Expand All @@ -475,6 +484,22 @@ def nyquist_plot(syslist, omega=None, Plot=True,
>>> real, imag, freq = nyquist_plot(sys)

"""
# Check to see if legacy 'Plot' keyword was used
if 'Plot' in kwargs:
import warnings
warnings.warn("'Plot' keyword is deprecated in nyquist_plot; "
"use 'plot'", FutureWarning)
# Map 'Plot' keyword to 'plot' keyword
plot = kwargs.pop('Plot')

# Check to see if legacy 'labelFreq' keyword was used
if 'labelFreq' in kwargs:
import warnings
warnings.warn("'labelFreq' keyword is deprecated in nyquist_plot; "
"use 'label_freq'", FutureWarning)
# Map 'labelFreq' keyword to 'label_freq' keyword
label_freq = kwargs.pop('labelFreq')

# If argument was a singleton, turn it into a list
if not getattr(syslist, '__iter__', False):
syslist = (syslist,)
Expand Down Expand Up @@ -507,7 +532,7 @@ def nyquist_plot(syslist, omega=None, Plot=True,
x = sp.multiply(mag, sp.cos(phase))
y = sp.multiply(mag, sp.sin(phase))

if Plot:
if plot:
# Plot the primary curve and mirror image
p = plt.plot(x, y, '-', color=color, *args, **kwargs)
c = p[0].get_color()
Expand All @@ -527,8 +552,8 @@ def nyquist_plot(syslist, omega=None, Plot=True,
plt.plot([-1], [0], 'r+')

# Label the frequencies of the points
if labelFreq:
ind = slice(None, None, labelFreq)
if label_freq:
ind = slice(None, None, label_freq)
for xpt, ypt, omegapt in zip(x[ind], y[ind], omega[ind]):
# Convert to Hz
f = omegapt / (2 * sp.pi)
Expand All @@ -550,14 +575,15 @@ def nyquist_plot(syslist, omega=None, Plot=True,
str(int(np.round(f / 1000 ** pow1000, 0))) + ' ' +
prefix + 'Hz')

if Plot:
if plot:
ax = plt.gca()
ax.set_xlabel("Real axis")
ax.set_ylabel("Imaginary axis")
ax.grid(color="lightgray")

return x, y, omega


#
# Gang of Four plot
#
Expand All @@ -575,6 +601,8 @@ def gangof4_plot(P, C, omega=None, **kwargs):
Linear input/output systems (process and control)
omega : array
Range of frequencies (list or bounds) in rad/sec
**kwargs : `matplotlib` plot keyword properties, optional
Additional keywords (passed to `matplotlib`)

Returns
-------
Expand All @@ -590,16 +618,16 @@ def gangof4_plot(P, C, omega=None, **kwargs):
Hz = config._get_param('bode', 'Hz', kwargs, _bode_defaults, pop=True)
grid = config._get_param('bode', 'grid', kwargs, _bode_defaults, pop=True)

# Select a default range if none is provided
# TODO: This needs to be made more intelligent
if omega is None:
omega = default_frequency_range((P, C))

# Compute the senstivity functions
L = P * C
S = feedback(1, L)
T = L * S

# Select a default range if none is provided
# TODO: This needs to be made more intelligent
if omega is None:
omega = default_frequency_range((P, C, S))

# Set up the axes with labels so that multiple calls to
# gangof4_plot will superimpose the data. See details in bode_plot.
plot_axes = {'t': None, 's': None, 'ps': None, 'cs': None}
Expand Down Expand Up @@ -628,36 +656,49 @@ def gangof4_plot(P, C, omega=None, **kwargs):
# TODO: Need to add in the mag = 1 lines
mag_tmp, phase_tmp, omega = S.freqresp(omega)
mag = np.squeeze(mag_tmp)
plot_axes['s'].loglog(omega_plot, 20 * np.log10(mag) if dB else mag)
plot_axes['s'].set_ylabel("$|S|$")
if dB:
plot_axes['s'].semilogx(omega_plot, 20 * np.log10(mag), **kwargs)
else:
plot_axes['s'].loglog(omega_plot, mag, **kwargs)
plot_axes['s'].set_ylabel("$|S|$" + " (dB)" if dB else "")
plot_axes['s'].tick_params(labelbottom=False)
plot_axes['s'].grid(grid, which='both')

mag_tmp, phase_tmp, omega = (P * S).freqresp(omega)
mag = np.squeeze(mag_tmp)
plot_axes['ps'].loglog(omega_plot, 20 * np.log10(mag) if dB else mag)
if dB:
plot_axes['ps'].semilogx(omega_plot, 20 * np.log10(mag), **kwargs)
else:
plot_axes['ps'].loglog(omega_plot, mag, **kwargs)
plot_axes['ps'].tick_params(labelbottom=False)
plot_axes['ps'].set_ylabel("$|PS|$")
plot_axes['ps'].set_ylabel("$|PS|$" + " (dB)" if dB else "")
plot_axes['ps'].grid(grid, which='both')

mag_tmp, phase_tmp, omega = (C * S).freqresp(omega)
mag = np.squeeze(mag_tmp)
plot_axes['cs'].loglog(omega_plot, 20 * np.log10(mag) if dB else mag)
if dB:
plot_axes['cs'].semilogx(omega_plot, 20 * np.log10(mag), **kwargs)
else:
plot_axes['cs'].loglog(omega_plot, mag, **kwargs)
plot_axes['cs'].set_xlabel(
"Frequency (Hz)" if Hz else "Frequency (rad/sec)")
plot_axes['cs'].set_ylabel("$|CS|$")
plot_axes['cs'].set_ylabel("$|CS|$" + " (dB)" if dB else "")
plot_axes['cs'].grid(grid, which='both')

mag_tmp, phase_tmp, omega = T.freqresp(omega)
mag = np.squeeze(mag_tmp)
plot_axes['t'].loglog(omega_plot, 20 * np.log10(mag) if dB else mag)
if dB:
plot_axes['t'].semilogx(omega_plot, 20 * np.log10(mag), **kwargs)
else:
plot_axes['t'].loglog(omega_plot, mag, **kwargs)
plot_axes['t'].set_xlabel(
"Frequency (Hz)" if Hz else "Frequency (rad/sec)")
plot_axes['t'].set_ylabel("$|T|$")
plot_axes['t'].set_ylabel("$|T|$" + " (dB)" if dB else "")
plot_axes['t'].grid(grid, which='both')

plt.tight_layout()


#
# Utility functions
#
Expand Down Expand Up @@ -754,7 +795,7 @@ def default_frequency_range(syslist, Hz=None, number_of_samples=None,
# TODO
raise NotImplementedError(
"type of system in not implemented now")
except:
except NotImplementedError:
pass

# Make sure there is at least one point in the range
Expand Down Expand Up @@ -787,15 +828,17 @@ def default_frequency_range(syslist, Hz=None, number_of_samples=None,
omega = sp.logspace(lsp_min, lsp_max, endpoint=True)
return omega


#
# KLD 5/23/11: Two functions to create nice looking labels
# Utility functions to create nice looking labels (KLD 5/23/11)
#

def get_pow1000(num):
"""Determine exponent for which significand of a number is within the
range [1, 1000).
"""
# Based on algorithm from http://www.mail-archive.com/[email protected]/msg14433.html, accessed 2010/11/7
# Based on algorithm from http://www.mail-archive.com/
# [email protected]/msg14433.html, accessed 2010/11/7
# by Jason Heeris 2009/11/18
from decimal import Decimal
from math import floor
Expand Down
Loading