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

Bode margins plot#199

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

Merged
murrayrm merged 5 commits intopython-control:masterfromicam0:bode_margins_plot
Dec 28, 2018
Merged
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
53 changes: 50 additions & 3 deletionscontrol/freqplot.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,7 @@
import math
from .ctrlutil import unwrap
from .bdalg import feedback
from .margins import stability_margins

__all__ = ['bode_plot', 'nyquist_plot', 'gangof4_plot',
'bode', 'nyquist', 'gangof4']
Expand All@@ -60,7 +61,7 @@

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

Expand All@@ -85,6 +86,8 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
If Hz=True the limits are in Hz otherwise in rad/s.
omega_num: int
number of samples
margins : boolean
if True, plot gain and phase margin
\*args, \**kwargs:
Additional options to matplotlib (color, linestyle, etc)

Expand DownExpand Up@@ -209,7 +212,7 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
color=pltline[0].get_color())

# Add a grid to the plot + labeling
ax_mag.grid(True, which='both')
ax_mag.grid(False if margins elseTrue, which='both')
ax_mag.set_ylabel("Magnitude (dB)" if dB else "Magnitude")

# Phase plot
Expand All@@ -219,6 +222,50 @@ def bode_plot(syslist, omega=None, dB=None, Hz=None, deg=None,
phase_plot = phase
ax_phase.semilogx(omega_plot, phase_plot, *args, **kwargs)

# Show the phase and gain margins in the plot
if margins:
margin = stability_margins(sys)
gm, pm, Wcg, Wcp = margin[0], margin[1], margin[3], margin[4]
if pm >= 0.:
phase_limit = -180.
else:
phase_limit = 180.

ax_mag.axhline(y=0 if dB else 1, color='k', linestyle=':')
ax_phase.axhline(y=phase_limit if deg else math.radians(phase_limit), color='k', linestyle=':')
mag_ylim = ax_mag.get_ylim()
phase_ylim = ax_phase.get_ylim()

if pm != float('inf') and Wcp != float('nan'):
if dB:
ax_mag.semilogx([Wcp, Wcp], [0.,-1e5],color='k', linestyle=':')
else:
ax_mag.loglog([Wcp,Wcp], [1.,1e-8],color='k',linestyle=':')

if deg:
ax_phase.semilogx([Wcp, Wcp], [1e5, phase_limit+pm],color='k', linestyle=':')
ax_phase.semilogx([Wcp, Wcp], [phase_limit + pm, phase_limit],color='k')
else:
ax_phase.semilogx([Wcp, Wcp], [1e5, math.radians(phase_limit)+math.radians(pm)],color='k', linestyle=':')
ax_phase.semilogx([Wcp, Wcp], [math.radians(phase_limit) +math.radians(pm), math.radians(phase_limit)],color='k')

if gm != float('inf') and Wcg != float('nan'):
if dB:
ax_mag.semilogx([Wcg, Wcg], [-20.*np.log10(gm), -1e5],color='k', linestyle=':')
ax_mag.semilogx([Wcg, Wcg], [0,-20*np.log10(gm)],color='k')
else:
ax_mag.loglog([Wcg, Wcg], [1./gm,1e-8],color='k', linestyle=':')
ax_mag.loglog([Wcg, Wcg], [1.,1./gm],color='k')

if deg:
ax_phase.semilogx([Wcg, Wcg], [1e-8, phase_limit],color='k', linestyle=':')
else:
ax_phase.semilogx([Wcg, Wcg], [1e-8, math.radians(phase_limit)],color='k', linestyle=':')

ax_mag.set_ylim(mag_ylim)
ax_phase.set_ylim(phase_ylim)
plt.suptitle('Gm = %.2f %s(at %.2f rad/s), Pm = %.2f %s (at %.2f rad/s)'%(20*np.log10(gm) if dB else gm,'dB ' if dB else '\b',Wcg,pm if deg else math.radians(pm),'deg' if deg else 'rad',Wcp))

if nyquistfrq_plot:
ax_phase.axvline(nyquistfrq_plot, color=pltline[0].get_color())

Expand All@@ -237,7 +284,7 @@ def genZeroCenteredSeries(val_min, val_max, period):
ylim = ax_phase.get_ylim()
ax_phase.set_yticks(genZeroCenteredSeries(ylim[0], ylim[1], math.pi / 4.))
ax_phase.set_yticks(genZeroCenteredSeries(ylim[0], ylim[1], math.pi / 12.), minor=True)
ax_phase.grid(True, which='both')
ax_phase.grid(False if margins elseTrue, which='both')
# ax_mag.grid(which='minor', alpha=0.3)
# ax_mag.grid(which='major', alpha=0.9)
# ax_phase.grid(which='minor', alpha=0.3)
Expand Down
29 changes: 29 additions & 0 deletionscontrol/tests/freqresp_test.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@
from control.xferfcn import TransferFunction
from control.matlab import ss, tf, bode, rss
from control.exception import slycot_check
from control.tests.margin_test import assert_array_almost_equal
import matplotlib.pyplot as plt

class TestFreqresp(unittest.TestCase):
Expand DownExpand Up@@ -108,6 +109,34 @@ def test_mimo(self):
#plt.figure(4)
#bode(sysMIMO,self.omega)

def test_bode_margin(self):
num = [1000]
den = [1, 25, 100, 0]
sys = ctrl.tf(num, den)
ctrl.bode_plot(sys, margins=True,dB=False,deg = True)
fig = plt.gcf()
allaxes = fig.get_axes()

mag_to_infinity = (np.array([6.07828691, 6.07828691]),
np.array([1.00000000e+00, 1.00000000e-08]))
assert_array_almost_equal(mag_to_infinity, allaxes[0].lines[2].get_data())

gm_to_infinty = (np.array([10., 10.]), np.array([4.00000000e-01, 1.00000000e-08]))
assert_array_almost_equal(gm_to_infinty, allaxes[0].lines[3].get_data())

one_to_gm = (np.array([10., 10.]), np.array([1., 0.4]))
assert_array_almost_equal(one_to_gm, allaxes[0].lines[4].get_data())

pm_to_infinity = (np.array([6.07828691, 6.07828691]),
np.array([100000., -157.46405841]))
assert_array_almost_equal(pm_to_infinity, allaxes[1].lines[2].get_data())

pm_to_phase = (np.array([6.07828691, 6.07828691]), np.array([-157.46405841, -180.]))
assert_array_almost_equal(pm_to_phase, allaxes[1].lines[3].get_data())

phase_to_infinity = (np.array([10., 10.]), np.array([1.00000000e-08, -1.80000000e+02]))
assert_array_almost_equal(phase_to_infinity, allaxes[1].lines[4].get_data())

def test_discrete(self):
# Test discrete time frequency response

Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp