Calculate the Symbolic Formulas for the PID Gains¶
Define Symbols¶
In [1]:
import numpy as np
from sympy import *
from IPython.display import Math
PI = np.pi
K, alpha = symbols('K alpha') # actuator symbols. K is a velocity gain and alpha is the corner frequency
Ki, Kp, Kd = symbols('Ki Kp Kd') # controller symbols. The
lam, s = symbols('lambda s') # transfer function symbols
Define the controller and motor transfer functions and display them¶
In [2]:
Gc = Ki/s + Kp + Kd*s # PID controller
Ga = (K*alpha)/(s*(s+alpha)) # actuator transfer function
display(Math('\nGc(s) = '+latex(Gc))) # the symbolic formula for the integrator gain
display(Math('\nGa(s) = '+latex(Ga))) # the symbolic formula for the integrator gain
$\displaystyle
Gc(s) = Kd s + \frac{Ki}{s} + Kp$
$\displaystyle
Ga(s) = \frac{K \alpha}{s \left(\alpha + s\right)}$
The closed loop transfer function¶
In [3]:
cltf = simplify((Gc*Ga)/(1+Gc*Ga)) # closed loop transfer function
num, den = fraction(simplify(cltf)) # the denominator is the characteristic equation
display(Math('\ncltf(s) = '+latex(cltf))) # the symbolic formula for the integrator gain
display(Math('\nnum(s) = '+latex(num))) # the symbolic formula for the integrator gain
display(Math('\nden(s) = '+latex(den))) # the symbolic formula for the integrator gain
$\displaystyle
cltf(s) = \frac{K \alpha \left(Ki + s \left(Kd s + Kp\right)\right)}{K \alpha \left(Ki + s \left(Kd s + Kp\right)\right) + s^{2} \left(\alpha + s\right)}$
$\displaystyle
num(s) = K \alpha \left(Ki + s \left(Kd s + Kp\right)\right)$
$\displaystyle
den(s) = K \alpha \left(Ki + s \left(Kd s + Kp\right)\right) + s^{2} \left(\alpha + s\right)$
The desired closed loop transfer function¶
In [20]:
dcltf = collect((s+lam)**3,s) # desired closed loop transfer function, three poles at -lamda
display(Math('desired\\ closed\\ transfer\\ function = '+latex(dcltf))) # notice how to create spaces between words in LaTeX
$\displaystyle desired\ closed\ transfer\ function = \left(\lambda + s\right)^{3}$
In [5]:
difce = simplify(expand(den-dcltf)) # difference between actual and desired CE
# difce = collect(difce,s)
# print(difce)
print(collect(difce,lam))
collect(difce,lam)
K*Kd*alpha*s**2 + K*Ki*alpha + K*Kp*alpha*s + alpha*s**2 - lambda**3 - 3*lambda**2*s - 3*lambda*s**2
Out[5]:
$\displaystyle K Kd \alpha s^{2} + K Ki \alpha + K Kp \alpha s + \alpha s^{2} - \lambda^{3} - 3 \lambda^{2} s - 3 \lambda s^{2}$
Symbolically solve for the controller PID gains¶
In [6]:
ki = solve(difce.coeff(s,0), Ki)[0]
kp = solve(difce.coeff(s,1), Kp)[0]
kd = solve(difce.coeff(s,2), Kd)[0]
display(Math('\nKi = '+latex(ki)))
display(Math('\nKp = '+latex(kp)))
display(Math('\nKd = '+latex(kd)))
$\displaystyle
Ki = \frac{\lambda^{3}}{K \alpha}$
$\displaystyle
Kp = \frac{3 \lambda^{2}}{K \alpha}$
$\displaystyle
Kd = \frac{- \alpha + 3 \lambda}{K \alpha}$
Assign values to the open loop symbols¶
In [7]:
_difce = difce.subs([(K,10),(alpha,2*PI*10),(lam,2*PI*10)])
_ki = solve(_difce.coeff(s,0), Ki)[0] # returns a list of one element
_kp = solve(_difce.coeff(s,1), Kp)[0]
_kd = solve(_difce.coeff(s,2), Kd)[0]
print(f'\nKi={_ki:8.6f}, Kp={_kp:8.6f}, Kd={_kd:8.6f}')
Ki=394.784176, Kp=18.849556, Kd=0.200000
Display the closed loop transfer function's numerator and denominator¶
In [8]:
_cltf = cltf.subs([(K,10),(alpha,2*PI*10),(lam,2*PI*10), (Ki,_ki), (Kp,_kp), (Kd, _kd)])
_num, _den = fraction(simplify(_cltf)) # the denominator is the characteristic equation
display(Math('numerator = '+latex(_num)))
display(Math('denominator = '+latex(_den)))
$\displaystyle numerator = 125.663706143592 s^{2} + 11843.5252813072 s + 248050.213442399$
$\displaystyle denominator = 1.0 s^{3} + 188.495559215388 s^{2} + 11843.5252813072 s + 248050.213442399$
Display the Bode and Pole Zero Plots¶
In [9]:
from bodeplot import bodeplot # in lib
from pzplot import pzplot # in lib
if _num.as_poly() == None:
num_coeffs = [float(_num)]
else:
num_coeffs = list(map(float,_num.as_poly().all_coeffs())) # convert list of symbols to list of floats
den_coeffs = list(map(float,_den.as_poly().all_coeffs())) # convert list of symbols to list of floats
zeros = np.roots(num_coeffs)
poles = np.roots(den_coeffs)
bodeplot(num_coeffs, den_coeffs)
pzplot(poles, zeros)