Пример #1
0
def bode(G, w_start=-2, w_end=2, axlim=None, points=1000, margin=False):
    """
    Shows the bode plot for a plant model

    Parameters
    ----------
    G : tf
        Plant transfer function.
    margin : boolean
        Show the cross over frequencies on the plot (optional).

    Returns
    -------
    GM : array containing a real number
        Gain margin.
    PM : array containing a real number
        Phase margin.
    Plot : matplotlib figure
    """

    if axlim is None:
        axlim = [None, None, None, None]
    plt.clf()
    plt.gcf().set_facecolor('white')

    GM, PM, wc, w_180 = utils.margins(G)

    # plotting of Bode plot and with corresponding frequencies for PM and GM
#    if ((w2 < numpy.log(w_180)) and margin):
#        w2 = numpy.log(w_180)
    w = numpy.logspace(w_start, w_end, points)
    s = 1j*w

    # Magnitude of G(jw)
    plt.subplot(2, 1, 1)
    gains = numpy.abs(G(s))
    plt.loglog(w, gains)
    if margin:
        plt.axvline(w_180, color='black')
        plt.text(w_180, numpy.average([numpy.max(gains), numpy.min(gains)]), r'$\angle$G(jw) = -180$\degree$')
    plt.axhline(1., color='red', linestyle='--')
    plt.axis(axlim)
    plt.grid()
    plt.ylabel('Magnitude')

    # Phase of G(jw)
    plt.subplot(2, 1, 2)
    phaseangle = utils.phase(G(s), deg=True)
    plt.semilogx(w, phaseangle)
    if margin:
        plt.axvline(wc, color='black')
        plt.text(wc, numpy.average([numpy.max(phaseangle), numpy.min(phaseangle)]), '|G(jw)| = 1')
    plt.axhline(-180., color='red', linestyle='--')
    plt.axis(axlim)
    plt.grid()
    plt.ylabel('Phase')
    plt.xlabel('Frequency [rad/unit time]')

    return GM, PM
Пример #2
0
def bode(G, w_start=-2, w_end=2, axlim=None, points=1000, margin=False):
    """
    Shows the bode plot for a plant model

    Parameters
    ----------
    G : tf
        Plant transfer function.
    margin : boolean
        Show the cross over frequencies on the plot (optional).

    Returns
    -------
    GM : array containing a real number
        Gain margin.
    PM : array containing a real number
        Phase margin.
    Plot : matplotlib figure
    """

    s, w, axlim = df.frequency_plot_setup(axlim, w_start, w_end, points)
    plt.clf()

    GM, PM, wc, w_180 = utils.margins(G)

    # plotting of Bode plot and with corresponding frequencies for PM and GM
    #    if ((w2 < numpy.log(w_180)) and margin):
    #        w2 = numpy.log(w_180)

    # Magnitude of G(jw)
    plt.subplot(2, 1, 1)
    gains = numpy.abs(G(s))
    plt.loglog(w, gains)
    if margin:
        plt.axvline(w_180, color='black')
        plt.text(w_180, numpy.average([numpy.max(gains),
                                       numpy.min(gains)]),
                 r'$\angle$G(jw) = -180$\degree$')
    plt.axhline(1., color='red', linestyle='--')
    plt.axis(axlim)
    plt.grid()
    plt.ylabel('Magnitude')

    # Phase of G(jw)
    plt.subplot(2, 1, 2)
    phaseangle = utils.phase(G(s), deg=True)
    plt.semilogx(w, phaseangle)
    if margin:
        plt.axvline(wc, color='black')
        plt.text(wc,
                 numpy.average([numpy.max(phaseangle),
                                numpy.min(phaseangle)]), '|G(jw)| = 1')
    plt.axhline(-180., color='red', linestyle='--')
    plt.axis(axlim)
    plt.grid()
    plt.ylabel('Phase')
    plt.xlabel('Frequency [rad/unit time]')

    return GM, PM
Пример #3
0
def rule6(G, Gm, message=False):
    """
    This is rule six of chapter five

    Calculates if tight control at low frequencies with RHP-zeros is possible

    Parameters
    ----------
    G : tf
        plant model

    Gm : tf
        measurement model

    message : boolean
        show the rule message (optional)

    Returns
    -------
    valid6 : boolean value if rule conditions was met

    wc : crossover frequency where | G(jwc) | = 1

    wd : crossover frequency where | Gd(jwd) | = 1

    """

    GGm = G * Gm
    zeros = GGm.zeros()

    _, _, wc, _ = margins(GGm)

    wz = 0
    if len(zeros) > 0:
        if np.imag(np.min(zeros)) == 0:
            # If the roots aren't imaginary.
            # Looking for the minimum values of the zeros =>
            # results in the tightest control.
            wz = (np.min(np.abs(zeros))) / 2
            valid6 = wc < 0.86 * np.abs(wz)
        else:
            wz = 0.86 * np.abs(np.min(zeros))
            valid6 = wc < wz / 2
    else:
        valid6 = False

    if message:
        print('Rule 6:')
        if wz != 0:
            print('These are the roots of the transfer function '
                  'matrix GGm', zeros)
        if valid6:
            print(
                'The critical frequency of S for the system to be '
                'controllable is', wz)
        else:
            print('No zeros in the system to evaluate')
    return valid6, wz
Пример #4
0
def rule6(G, Gm, message=False):
    """
    This is rule six of chapter five

    Calculates if tight control at low frequencies with RHP-zeros is possible

    Parameters
    ----------
    G : tf
        plant model

    Gm : tf
        measurement model

    message : boolean
        show the rule message (optional)

    Returns
    -------
    valid6 : boolean value if rule conditions was met

    wc : crossover frequency where | G(jwc) | = 1

    wd : crossover frequency where | Gd(jwd) | = 1

    """

    GGm = G * Gm
    zeros = GGm.zeros()

    _, _, wc, _ = margins(GGm)

    wz = 0
    if len(zeros) > 0:
        if np.imag(np.min(zeros)) == 0:
            # If the roots aren't imaginary.
            # Looking for the minimum values of the zeros =>
            # results in the tightest control.
            wz = (np.min(np.abs(zeros)))/2
            valid6 = wc < 0.86*np.abs(wz)
        else:
            wz = 0.86*np.abs(np.min(zeros))
            valid6 = wc < wz/2
    else:
        valid6 = False

    if message:
        print('Rule 6:')
        if wz != 0:
            print('These are the roots of the transfer function '
                  'matrix GGm', zeros)
        if valid6:
            print('The critical frequency of S for the system to be '
                  'controllable is', wz)
        else:
            print('No zeros in the system to evaluate')
    return valid6, wz
Пример #5
0
def rule5(G, Gm=1, message=False):
    """
    This is rule five of chapter five

    Calculates constraints for time delay, wc < 1 / theta

    Parameters
    ----------
    G : tf
        plant model

    Gm : tf
        measurement model

    message : boolean
        show the rule message (optional)

    Returns
    -------
    valid5 : boolean
        value if rule conditions was met

    wtd: real
        time delay frequency
    """

    GGm = G * Gm
    TimeDelay = GGm.deadtime
    _, _, wc, _ = margins(GGm)

    valid5 = False
    if TimeDelay == 0:
        wtd = 0
    else:
        wtd = 1 / TimeDelay
        valid5 = wc < wtd

    if message:
        print('Rule 5:')
        if TimeDelay == 0:
            print("There isn't any deadtime in the system")
        if valid5:
            print('wc < 1 / theta :', np.round(wc, 2), '<', np.round(wtd, 2))
        else:
            print('wc > 1 / theta :', np.round(wc, 2), '>', np.round(wtd, 2))

    return valid5, wtd
Пример #6
0
def rule5(G, Gm=1, message=False):
    """
    This is rule five of chapter five

    Calculates constraints for time delay, wc < 1 / theta

    Parameters
    ----------
    G : tf
        plant model

    Gm : tf
        measurement model

    message : boolean
        show the rule message (optional)

    Returns
    -------
    valid5 : boolean
        value if rule conditions was met

    wtd: real
        time delay frequency
    """

    GGm = G * Gm
    TimeDelay = GGm.deadtime
    _, _, wc, _ = margins(GGm)

    valid5 = False
    if TimeDelay == 0:
        wtd = 0
    else:
        wtd = 1 / TimeDelay
        valid5 = wc < wtd

    if message:
        print('Rule 5:')
        if TimeDelay == 0:
            print("There isn't any deadtime in the system")
        if valid5:
            print('wc < 1 / theta :', np.round(wc, 2), '<', np.round(wtd, 2))
        else:
            print('wc > 1 / theta :', np.round(wc, 2), '>', np.round(wtd, 2))

    return valid5, wtd
Пример #7
0
def rule7(G, Gm, message=False):
    """
    This is rule seven of chapter five

    Calculates the phase lag constraints

    Parameters
    ----------
    G : tf
        plant model

    Gm : tf
        measurement model

    message : boolean
        show the rule message (optional)

    Returns
    -------
    valid7 : boolean
        value if rule conditions was met

    wc : real
        crossover frequency where | G(jwc) | = 1

    wd : real
        crossover frequency where | Gd(jwd) | = 1

    """
    # Rule 7 determining the phase of GGm at -180 deg.
    # This is solved visually from a plot.

    GGm = G*Gm
    _, _, wc, wu = margins(GGm)

    valid7 = wc < wu

    if message:
        print('Rule 7:')
        if valid7:
            print('wc < wu :', wc, '<', wu)
        else:
            print('wc > wu :', wc, '>', wu)

    return valid7, wu
Пример #8
0
def rule7(G, Gm, message=False):
    """
    This is rule seven of chapter five

    Calculates the phase lag constraints

    Parameters
    ----------
    G : tf
        plant model

    Gm : tf
        measurement model

    message : boolean
        show the rule message (optional)

    Returns
    -------
    valid1 : boolean
        value if rule conditions was met

    wc : real
        crossover frequency where | G(jwc) | = 1

    wd : real
        crossover frequency where | Gd(jwd) | = 1

    """
    # Rule 7 determining the phase of GGm at -180 deg.
    # This is solved visually from a plot.

    GGm = G * Gm
    _, _, wc, wu = margins(GGm)

    valid7 = wc < wu

    if message:
        print('Rule 7:')
        if valid7:
            print('wc < wu :', wc, '<', wu)
        else:
            print('wc > wu :', wc, '>', wu)

    return valid7, wu
Пример #9
0
def rule8(G, message=False):
    '''
    This is rule one of chapter five
    
    This function determines if the plant is open-loop stable at its poles
    
    Parameters
    ----------
    G : tf
        plant model   
    
    Gd : tf
        plant distrubance model
    
    message : boolean 
        show the rule message (optional)
    
    Returns
    -------
    valid1 : boolean 
        value if rule conditions was met
    
    wc : real
        crossover frequency where | G(jwc) | = 1            
    '''
    #Rule 8 for critical frequency min value due to poles

    poles = np.roots(G.denominator)
    _,_,wc,_ = margins(G)

    wp = 0
    if np.max(poles) < 0:
        wp = 2 * np.max(np.abs(poles))
        valid8 = wc > wp
    else: valid8 = False
        
    if message:
        print 'Rule 8:'
        if valid8:
            print 'wc > 2p :', wc , '>' , wp
        else:
            print 'wc < 2p :', wc , '<' , wp

    return valid8, wp
Пример #10
0
def rule8(G, message=False):
    """
    This is rule eight of chapter five

    This function determines if the plant is open-loop stable at its poles

    Parameters
    ----------
    G : tf
        plant model

    message : boolean
        show the rule message (optional)

    Returns
    -------
    valid1 : boolean
        value if rule conditions was met

    wc : real
        crossover frequency where | G(jwc) | = 1
    """
    # Rule 8 for critical frequency min value due to poles

    poles = G.poles()
    _, _, wc, _ = margins(G)

    wp = 0
    if np.max(poles) < 0:
        wp = 2 * np.max(np.abs(poles))
        valid8 = wc > wp
    else:
        valid8 = False

    if message:
        print('Rule 8:')
        if valid8:
            print('wc > 2p :', wc, '>', wp)
        else:
            print('wc < 2p :', wc, '<', wp)

    return valid8, wp
Пример #11
0
def rule8(G, message=False):
    """
    This is rule eight of chapter five

    This function determines if the plant is open-loop stable at its poles

    Parameters
    ----------
    G : tf
        plant model

    message : boolean
        show the rule message (optional)

    Returns
    -------
    valid8 : boolean
        value if rule conditions were met

    wc : real
        crossover frequency where | G(jwc) | = 1
    """
    # Rule 8 for critical frequency min value due to poles

    poles = G.poles()
    _, _, wc, _ = margins(G)

    wp = 0
    if np.max(poles) < 0:
        wp = 2*np.max(np.abs(poles))
        valid8 = wc > wp
    else:
        valid8 = False

    if message:
        print('Rule 8:')
        if valid8:
            print('wc > 2p :', wc, '>', wp)
        else:
            print('wc < 2p :', wc, '<', wp)

    return valid8, wp
Пример #12
0
import matplotlib.pyplot as plt
import utilsplot
import numpy as np

s = tf([1,0], 1)
z = 0.1
tau = 1

L = (-s + z)/(s*(tau*s + tau*z + 2))
T = (-s + z)/((s + z)*(tau*s + 1))
S = 1/(1 + L)

mT = maxpeak(T)
mS = maxpeak(S)
GM, PM, wc, wb, wbt, valid = marginsclosedloop(L)
GM, PM, wc, w_180 = margins(L)
print('GM = ', np.round(GM, 1))
print('PM = ', np.round(PM, 1), 'rad')
print('wB = ', np.round(wb, 3))
print('wC = ', np.round(wc, 3))
print('wBT = ', wbt)
print('w180 = ', np.round(w_180, 3))
print('Ms = ', np.round(mS, 2))
print('Mt = ', np.round(mT, 1))

[t, y] = tf_step(T, 50)
plt.figure('Figure 2.17')
plt.title('Step response for system T')
plt.plot(t, y)
plt.xlabel('time (s)')
plt.ylabel('y(t)')
Пример #13
0
from utils import tf, margins
import Chapter_05 as ch5

s = tf([1, 0], 1)

# Example plant based on Example 2.9 and Example 2.16
G = (s + 200) / ((10 * s + 1) * (0.05 * s + 1)**2)
G.deadtime = 0.002
Gd = 33 / (10 * s + 1)
K = 0.4 * ((s + 2) / s) * (0.075 * s + 1)

L = G * K

w = np.logspace(-3, 3)

GM, PM, wc, wu = margins(G)
GM, PM, wd, w_180 = margins(Gd)

[valid6, wz] = ch5.rule6(G, 1)
[valid5, wtd] = ch5.rule5(G)
[valid8, wp] = ch5.rule8(G)

print 'wc: ', np.round(wc, 3)
print 'wd: ', np.round(wd, 3)
print 'wu: ', np.round(wu, 3)

wuy = abs(G((1j * wu)))
wzy = abs(G((1j * wz)))
wpy = abs(G((1j * wp)))
wtdy = abs(G((1j * wtd)))
Пример #14
0
def rule1(G, Gd, K=1, message=False, plot=False, w1=-4, w2=2):
    """
    This is rule one of chapter five

    Calculates the speed of response to reject distrurbances. Condition require
    |S(jw)| <= |1/Gd(jw)|

    Parameters
    ----------
    G : tf
        plant model

    Gd : tf
        plant distrubance model

    K : tf
        control model

    message : boolean
        show the rule message (optional)

    plot : boolean
        show the bode plot with constraints (optional)

    w1 : integer
        start frequency, 10^w1 (optional)

    w2 : integer
        end frequency, 10^w2 (optional)

    Returns
    -------
    valid1 : boolean
        value if rule conditions was met

    wc : real
        crossover frequency where | G(jwc) | = 1

    wd : real
        crossover frequency where | Gd(jwd) | = 1
    """

    _, _, wc, _ = margins(G)
    _, _, wd, _ = margins(Gd)

    valid1 = wc > wd

    if message:
        print('Rule 1: Speed of response to reject distrubances')
        if valid1:
            print('First condition met, wc > wd')
        else:
            print('First condition no met, wc < wd')
        print('Seconds conditions requires |S(jw)| <= |1/Gd(jw)|')

    if plot:
        plt.figure('Rule 1')

        w, mag_s = df.setup_plot(['|S|', '1/R', '$w_r$'], w1, w2, G, K, wr)

        inv_gd = 1 / Gd
        mag_i = np.abs(inv_gd(s))

        plt.loglog(w, mag_s)
        plt.loglog(w, mag_i, ls='--')
        df.setup_plot(['|S|', '1/|Gd|'])

    return valid1, wc, wd
Пример #15
0
def rule1(G, Gd, K=1, message=False, plot=False, w1=-4, w2=2):
    """
    This is rule one of chapter five

    Calculates the speed of response to reject disturbances. Condition require
    |S(jw)| <= |1/Gd(jw)|

    Parameters
    ----------
    G : tf
        plant model

    Gd : tf
        plant distrubance model

    K : tf
        control model

    message : boolean
        show the rule message (optional)

    plot : boolean
        show the bode plot with constraints (optional)

    w1 : integer
        start frequency, 10^w1 (optional)

    w2 : integer
        end frequency, 10^w2 (optional)

    Returns
    -------
    valid1 : boolean
        value if rule conditions was met

    wc : real
        crossover frequency where | G(jwc) | = 1

    wd : real
        crossover frequency where | Gd(jwd) | = 1
    """

    _, _, wc, _ = margins(G)
    _, _, wd, _ = margins(Gd)

    valid1 = wc > wd

    if message:
        print('Rule 1: Speed of response to reject distrubances')
        if valid1:
            print('First condition met, wc > wd')
        else:
            print('First condition not met, wc < wd')
        print('Second condition requires |S(jw)| <= |1/Gd(jw)|')

    if plot:
        w = np.logspace(w1, w2, 1000)
        s = 1j * w
        S = 1 / (1 + G * K)
        gain = np.abs(S(s))
        inv_gd = 1 / Gd
        mag_i = np.abs(inv_gd(s))

        plt.figure('Rule 1')
        plt.loglog(w, gain, label='|S|')
        plt.loglog(w, mag_i, ls='--', label='|1/G_d |')
        plt.xlabel('Frequency [rad/s]')
        plt.ylabel('Magnitude')
        plt.legend(bbox_to_anchor=(0, 1.01, 1, 0), loc=3, ncol=3)
        plt.show()

    return valid1, wc, wd
import utilsplot
import numpy as np


s = tf([1, 0], 1)
z = 0.1
tau = 1

L = (-s+z)/(s*(tau*s + tau*z + 2))
T = (-s+z)/((s+z)*(tau*s+1))
S = 1/(1+L)

mT = maxpeak(T)
mS = maxpeak(S)
GM, PM, wc, wb, wbt, valid = marginsclosedloop(L)
GM, PM, wc, w_180 = margins(L)
print('GM = ', np.round(GM, 1))
print('PM = ', np.round(PM, 1), 'rad')
print('wB = ', np.round(wb, 3))
print('wC = ', np.round(wc, 3))
print('wBT = ', wbt)
print('w180 = ', np.round(w_180, 3))
print('Ms = ', np.round(mS, 2))
print('Mt = ', np.round(mT, 1))

[t, y] = tf_step(T, 50)
plt.figure('Figure 2.17')
plt.plot(t, y)
plt.xlabel('time (s)')
plt.ylabel('y(t)')
plt.show()
Пример #17
0
import utils
import numpy as np
import sympy as sp
from scipy.optimize import fsolve

s = utils.tf([1,0],[1])
L = (-s + 2)/(s*(s + 2))

_,_,_,w180 = utils.margins(L)
GM = 1/np.abs(L(1j*w180))
print('w_180',w180)
print('GM = 1/|L(w180)|',GM)
print('From 7.55, kmax = GM = ',GM)

omega = np.logspace(-2,2,1000)

def rk(kmax):
    return (kmax - 1)/(kmax + 1)

def kbar(kmax):
    return (kmax + 1)/2

def RScondition(kmax):
    abs_ineq = [abs(rk(kmax) * kbar(kmax) * L(1j*s)/(1 + kbar(kmax)*L(1j*s))) for s in omega]
    max_ineq = max(abs_ineq) - 1
    return max_ineq

kcal = fsolve(RScondition,1)
print('From 7.58, kmax = ',np.round(kcal,2))
Пример #18
0
def rule1(G, Gd, K=1, message=False, plot=False, w1=-4, w2=2):
    """
    This is rule one of chapter five

    Calculates the speed of response to reject disturbances. Condition require
    |S(jw)| <= |1/Gd(jw)|

    Parameters
    ----------
    G : tf
        plant model

    Gd : tf
        plant disturbance model

    K : tf
        control model

    message : boolean
        show the rule message (optional)

    plot : boolean
        show the bode plot with constraints (optional)

    w1 : integer
        start frequency, 10^w1 (optional)

    w2 : integer
        end frequency, 10^w2 (optional)

    Returns
    -------
    valid1 : boolean
        value if rule conditions were met

    wc : real
        crossover frequency where | G(jwc) | = 1

    wd : real
        crossover frequency where | Gd(jwd) | = 1
    """

    _, _, wc, _ = margins(G)
    _, _, wd, _ = margins(Gd)

    valid1 = wc > wd

    if message:
        print('Rule 1: Speed of response to reject disturbances')
        if valid1:
            print('First condition met, wc > wd')
        else:
            print('First condition not met, wc < wd')
        print('Second condition requires |S(jw)| <= |1/Gd(jw)|')

    if plot:
        w = np.logspace(w1, w2, 1000)
        s = 1j*w
        S = 1/(1+G*K)
        gain = np.abs(S(s))
        inv_gd = 1/Gd
        mag_i = np.abs(inv_gd(s))

        plt.figure('Rule 1')
        plt.loglog(w, gain, label='|S|')
        plt.loglog(w, mag_i, ls='--', label='|1/G_d |')
        plt.xlabel('Frequency [rad/s]')
        plt.ylabel('Magnitude')
        plt.legend(bbox_to_anchor=(0, 1.01, 1, 0), loc=3, ncol=3)
        plt.show()

    return valid1, wc, wd
import Chapter_05 as ch5


s = tf([1, 0], 1)

# Example plant based on Example 2.9 and Example 2.16
G = (s + 200) / ((10 * s + 1) * (0.05 * s + 1)**2)
G.deadtime = 0.002
Gd = 33 / (10 * s + 1)
K = 0.4 * ((s + 2) / s) * (0.075 * s + 1)

L = G * K

w = np.logspace(-3,3)

GM, PM, wc, wu = margins(G)
GM, PM, wd, w_180 = margins(Gd)

[valid6, wz] = ch5.rule6(G, 1)
[valid5, wtd] = ch5.rule5(G)
[valid8, wp] = ch5.rule8(G)

print 'wc: ' , np.round(wc, 3)
print 'wd: ' , np.round(wd, 3)
print 'wu: ' , np.round(wu, 3)

wuy = abs(G((1j*wu)))
wzy = abs(G((1j*wz)))
wpy = abs(G((1j*wp)))
wtdy = abs(G((1j*wtd)))
Пример #20
0
def rule1(G, Gd, K=1, message=False, plot=False, w1=-4, w2=2):
    """
    This is rule one of chapter five

    Calculates the speed of response to reject distrurbances. Condition require
    |S(jw)| <= |1/Gd(jw)|

    Parameters
    ----------
    G : tf
        plant model

    Gd : tf
        plant distrubance model

    K : tf
        control model

    message : boolean
        show the rule message (optional)

    plot : boolean
        show the bode plot with constraints (optional)

    w1 : integer
        start frequency, 10^w1 (optional)

    w2 : integer
        end frequency, 10^w2 (optional)

    Returns
    -------
    valid1 : boolean
        value if rule conditions was met

    wc : real
        crossover frequency where | G(jwc) | = 1

    wd : real
        crossover frequency where | Gd(jwd) | = 1
    """

    _,_,wc,_ = margins(G)
    _,_,wd,_  = margins(Gd)

    valid1 = wc > wd

    if message:
        print('Rule 1: Speed of response to reject distrubances')
        if valid1:
            print('First condition met, wc > wd')
        else:
            print('First condition no met, wc < wd')
        print('Seconds conditions requires |S(jw)| <= |1/Gd(jw)|')

    if plot:
        plt.figure('Rule 1')

        w, mag_s = df.setup_plot(['|S|', '1/R', '$w_r$'], w1, w2, G, K, wr)

        inv_gd = 1 / Gd
        mag_i = np.abs(inv_gd(s))

        plt.loglog(w, mag_s)
        plt.loglog(w, mag_i, ls = '--')
        df.setup_plot(['|S|', '1/|Gd|'])

    return valid1, wc, wd