Linealizar ecuaciones diferenciales no lineales para diseñar controles PID
El diseño de controladores se facilita cuando tenemos sistemas que son lineales. En el proceso de diseño de controladores. Se siguen la siguientes etapas.
La linealización se hace para las ecuaciones diferenciales no lineales del sistema.
Antes de linealizar ecuaciones diferenciales, empecemos analizando funciones no lineales.
x = numpy.linspace(-1, 1, 100)
xpos = numpy.linspace(0.001, 1, 100)
plt.figure()
ax = plt.subplot(111)
ax.plot(x, numpy.exp(x), label="$e^x$"); # Función exponencial
ax.plot(x,x*x, label="$x^2$"); # Función cuadratica
ax.plot(xpos, numpy.log10(xpos), label="$log(x)$") # Función logaritmica
ax.legend()
plt.xlabel('$x$')
plt.ylabel('$y$');
Para linealizar funciones utilizamos la serie de Taylor. La cual se define como:
$$f(x) = \sum_{n=o}^\infty \, \frac{f^{(n)}(a)}{n!}(x-a)^n$$escrita de otra forma:
$$f(x) \approx f(a) + \frac{f'(a)}{1!}(x-a) + \frac{f''(a)}{2!}(x-a)^2 + \frac{f^{(3)}(a)}{3!}(x-a)^3 + \cdots$$se toma únicamente el termino lineal.
$$f(x) \approx f(a) + \frac{f'(a)}{1!}(x-a) $$La ecuación del tanque es la siguiente, donde $A$ es el area del tanque, $a$ es el area del orificio del tanque, $Q(t)$ es el caudal de entrada:
$$A\,\frac{d\,h(t)}{dt}+a\sqrt{2g\,h(t)}=Q(t)$$Los pasos para la linealización de ecuaciones diferenciales son los siguientes:
A,a,g,t,h,Q = sympy.symbols('A a g t h Q')
hs = sympy.Function('h')(t)
qs = sympy.Function('Q')(t)
Eq_tanque = sympy.Eq(A*sympy.diff(hs,t)+a*sympy.sqrt(2*g*hs),qs); display(Eq_tanque)
F = sympy.Function('F')(h,Q)
Fhq = sympy.solve(Eq_tanque,sympy.diff(hs,t))[0]
Eq_tanque2 = sympy.Eq(F,Fhq); display(Eq_tanque2)
hss, qss = sympy.symbols('h_{ss} Q_{ss}')
Eq_estacionario = sympy.Eq(hss,sympy.solve(Eq_tanque2.subs(F,0),hs)[0].subs(qs,qss)); display(Eq_estacionario)
Eq_estacionario2 = sympy.Eq(qss,sympy.solve(Eq_tanque2.subs(F,0),qs)[0].subs(hs,hss)); display(Eq_estacionario2)
Este es entonces el punto de linealización $(h_{ss},Q_{ss})$
taylor = sympy.Eq(F,F.subs(h,hss).subs(Q,qss)+sympy.diff(F,h).subs(h,hss).subs(Q,qss)*(hs-hss)+sympy.diff(F,Q).subs(h,hss).subs(Q,qss)*(qs-qss))
display(taylor)
para el tanque tenemos
lineal = sympy.Eq(F,Fhq.subs(hs,hss).subs(qs,qss)+sympy.diff(Fhq,hs).subs(hs,hss).subs(qs,qss)*(hs-hss)+sympy.diff(Fhq,qs).subs(hs,hss).subs(qs,qss)*(qs-qss))
display(lineal)
lineal2 = sympy.Eq(sympy.diff(hs,t),lineal.rhs)
display(lineal2)
hp = sympy.Function("h'")(t)
qp = sympy.Function("Q'")(t)
Eq_diff = lineal2.subs(hs,hp+hss).subs(qs,qp+qss).subs(qss,Eq_estacionario2.rhs)
Eq_diff2 = sympy.Eq(sympy.simplify(Eq_diff.lhs),Eq_diff.rhs);
display(Eq_diff2)
Linealizar las siguientes funciones:
$$m\frac{d^2y(t)}{dt^2}+b\frac{dy(t)}{dt}+k\,y(t)^3 = - F(t)$$$$\frac{d^2y(t)}{dt^2} + \sin(\omega t)\frac{dy(t)}{dt} + e^{t/\tau}y(t) = 0$$$$t\frac{d^2y(t)}{dt^2}+\sin(\omega t)\frac{dy(t)}{dt} = 0$$El root locus presenta los caminos de los polos en el plano complejo para un sistema en lazo cerrado en donde la ganancia del controlador $K$ varia de $0$ hasta $\infty$.
Veremos como construir los caminos del root locus a partir de la información de los polos en lazo abierto de un sistema.
Consideremos un sistema de primer orden con una constante de tiempo $\tau=2\text{ seg}$:
ss = sympy.Symbol('s'); G1A = 1/(1+2*ss); display(G1A)
Este sistema tiene un polo que es $s=-0.5$. Si le agregamos al sistema un controlador con ganancia $K$ en lazo abierto.
sK = sympy.Symbol('K'); G1B = G1A*sK; display(G1B)
La posición de polo no cambia, el polo sigue siendo $s=-0.5$, ¿qué pasa en lazo cerrado?
En lazo cerrado, tendremos la siguiente función de transferencia.
G1C = sympy.simplify(G1B/(1+G1B)); display(G1C)
Aquí vemos, que la posición del polo depende del valor que tome $K$.
G1Cpole = sympy.Eq(ss,sympy.solve(sympy.denom(G1C),ss)[0]); display(G1Cpole)
Comienza en $s=-0.5$ cuando $K=0$, y este polo se vuelve más negativo cuando $K$ se incrementa.
En la siguiente figura interactiva se puede ver el comportamiento del polo:
root_locus_ejemplo_1(0)
Del mismo modo se puede usar la funcion root_locus de la libreria control, para obtener la gráfica. La función toma como entrada la funcion de transferencia en lazo abierto sin controlador.
$$\frac{1}{2\,s+1}$$G1D = control.tf(1,[2,1]);
control.root_locus(G1D);
plt.axvspan(0, 0.5, facecolor='r', alpha=0.5)
plt.xlim(-1.5,0.5);
La zona en rojo corresponde a los valores reales positivos, esta zona se conoce como lo región inestrable, si hay al menos un polo del sistema en esta zona el sistema será inestable.
Lejos de los polos y ceros del lazo abierto, los locis se vuelven asimptoticos a una lineas que tienen angulos $\alpha_n$ con respecto al eje real.
$$\alpha_n=\pm \frac{n\pi}{P-Z} \qquad\text{donde,}\quad n=1,3,5,..., P-Z$$
Las asimptotas intersectan al eje real en un punto $S$, conocido como centroide del mapa de polos y ceros, dado por:
$$S=\frac{\Sigma \text{polos} - \Sigma \text{ceros}}{P-Z}$$
Analicemos la siguiente función de transferencia.
s = control.tf([1,0],1)
G2s = (1+0.5*ss)/(ss*(1+0.3*ss)*(1+0.6*ss+0.4*ss**2)); display(G2s)
G2 = (1+0.5*s )/(s *(1+0.3*s )*(1+0.6*s +0.4*s **2));
polos_G2 = control.pole(G2)
print("Tiene P = %d polos y valen: \n\n%s" % (len(polos_G2),'\n'.join(map(str, polos_G2))))
Tiene P = 4 polos y valen: (-3.3333333333333357+0j) (-0.7499999999999991+1.3919410907075038j) (-0.7499999999999991-1.3919410907075038j) 0j
ceros_G2 = control.zero(G2)
print("Tiene Z = %d ceros y vale: \n\n%s" % (len(ceros_G2),'\n'.join(map(str, ceros_G2))))
Tiene Z = 1 ceros y vale: (-2+0j)
Eq_caracteristica = sympy.Eq(sympy.denom(G2s),0); display(Eq_caracteristica)
Se puede partir del mapa de polos y ceros y de la reglas para graficarlo manualmente.
control.pzmap(G2);
plt.axvspan(0, 4, facecolor='r', alpha=0.5)
plt.xlim(-5,3);
O directamente con la función root_locus
control.root_locus(G2);
plt.axvspan(0, 4, facecolor='r', alpha=0.5)
plt.xlim(-10,4);
Existe un valor limite para $K$ el cual hace que los caminos pasen de la zona real negativa a la zona real positiva. Este valor se conoce como $K_u$ la ganacia última del sistema. Para encontrarlo se puede usar criterio de Routh o usando la función margin.
Para la matriz de Routh debemos empezar por encontra la ecuación característica del sistema en lazo cerrado.
carac2 = sympy.expand(sympy.denom(sympy.simplify(G2s*sK/(1+G2s*sK)))); display(carac2)
Buscamos los coeficientes:
coeff2 = [];
for i in range(5):
coeff2.append(carac2.coeff(ss,i)); print("El valor que acompaña a s^%d es %s" % (i,coeff2[i]))
El valor que acompaña a s^0 es K El valor que acompaña a s^1 es 0.5*K + 1 El valor que acompaña a s^2 es 0.900000000000000 El valor que acompaña a s^3 es 0.580000000000000 El valor que acompaña a s^4 es 0.120000000000000
Completamos la matriz de Routh
print("s⁴ \t %3.2f \t\t\t\t\t\t %3.2f \t\t %s" %(coeff2[4],coeff2[2],coeff2[0]))
print("s³ \t %3.2f \t\t\t\t\t\t %s \t %s" %(coeff2[3],coeff2[1],0))
s21 = sympy.simplify((coeff2[3]*coeff2[2]-coeff2[4]*coeff2[1])/coeff2[3])
print("s² \t %s \t %s" %(s21,coeff2[0]))
s11 = sympy.simplify((s21*coeff2[1]-coeff2[3]*sK)/s21)
print("s¹ \t X")
print("s⁰ \t K")
print("\n donde X = %s"%(s11))
s⁴ 0.12 0.90 K s³ 0.58 0.5*K + 1 0 s² 0.693103448275862 - 0.103448275862069*K K s¹ X s⁰ K donde X = (0.0517241379310345*K**2 + 0.336896551724138*K - 0.693103448275862)/(0.103448275862069*K - 0.693103448275862)
La primer columna de la matriz debe toda tener el mismo signo. Ya que los dos primeros elementos son positivos el resto también.
display(sympy.solve(s11,sK))
s11.subs(sK,1.6); # Verificamos la desigualdad.
[-8.15624601369002, 1.64291268035669]
De aqui tenemos que... $K_u= 1.643$
Usando la función margin tenemos:
margin_G2,_,_,_ = control.margin(G2)
display(margin_G2)
1.6429126803566858
El primer valor reportado es la ganacia última $K_u \approx 1.643$
Podemos verificar que pasa al sistema en lazo cerrado con valores de $K$ alrededor de $K_u$
Para $K=1.5$
respuesta_ejemplo_2(1.5)
Para $K=1.65$
respuesta_ejemplo_2(1.65)
Para la siguiente función:
G3 = 1/(ss*(ss+1)*(ss+8)); display(G3)
Realizar:
Para considerar el retardo en los sistemas y diseñar el controlador PID, debemos aproximar el retardo:
¿Qué es PID?
PID es un control proporcional integral derivativo. Se representa por la siguiente ecuación:
$$u(t) = K_p\,e(t) + K_i \int_0^t e(\tau)d\tau + Kd_\, \frac{e(t)}{dt}$$Los coefficientes $K_p$, $K_i$ y $K_d$ se definen positivos. La anterior expresión puede ser escrita en el dominio de Laplace de la siguiente forma:
$$K(s)= K_p + \frac{K_i}{s}+K_d\,s$$Intensamente (inside-out)
Proporcional | Integral | Derivativo | |
reactivo | vengativa | miedoso | |
furia | asco | miedo |
Tomando la función de transferencia del PID
Separando el bloque en tres de manera práctica tenemos:
La forma clásica de la función de transferencia del PID es:
$$\frac{U(s)}{E(s)} = K_p + \frac{K_i}{s} + K_d\,s$$En la práctica esta función se representa de la siguiente forma:
$$\frac{U(s)}{E(s)} = K_p \left(1 + \frac{K_i}{K_p}\frac{1}{s} + \frac{K_d}{K_p} s \right) = K_p \left(1 + \frac{1}{T_i\, s} + T_d\, s \right)$$Aquí presentamos las combinaciones para el PID que funcionan bien en la mayoria de los casos.
Descripción | |||
---|---|---|---|
P | Control proporcional | ||
I | Control integral | ||
P | I | Control proporcional integral | |
P | D | Control proporcional derivativo | |
P | I | D | Control proporcional integral derivativo |
Esta es la representación de bloques de un sistema en lazo cerrado incluyendo un controlador.
La función de transferencia del error la calculamos sabiendo que:
$$E(s) = R(s)-B(s) \qquad\text{y}\qquad B(s)=H(s)C(s)$$Obtenemos lo siguiente:
$$\frac{E(s)}{R(s)} = \frac{1}{1+K(s)G(s)H(s)}$$Controlar un proceso puede reducirse a :
"Encontra el método que nos permita eliminar el error estacionario entre la medida y la referencia".
Suponiendo una función de transferencial polinomial:
$$G(s)=\frac{b_0+b_1\, s+ \cdots+ b_m\, s^m}{a_0+a_1\, s+ \cdots+ b_n\, s^n}$$aplicando el teorema del valor final al error $E(s)$, con un escalón como entrada $R(s) = A/s$ y asumiendo $K(s)=H(s)=1$, tenemos:
$$\lim_{t\to\infty} e(t) = \lim_{s\to 0} s\, E(s) = \frac{A}{1+g_{\infty}} \qquad\text{con,}\quad g_{\infty} = \lim_{s\to 0} G(s) = \frac{b_0}{a_0}$$La siguinte tabla muestra el efecto que tiene incrementar un parámetro de forma independiente:
Parámetro | $t_r$ | $M_p$ | $t_s$ | $E_{ss}$ | Estabilidad |
---|---|---|---|---|---|
$K_p$ | $\searrow$ | $\nearrow$ | $\searrow$ | degrada | |
$K_i$ | $\searrow$ | $\nearrow$ | $\nearrow$ | elimina | degrada |
$K_d$ | $\searrow$ | $\searrow$ | mejora si $K_D$ pequeño |
recordemos que $t_r$ es el tiempo de subida, $M_p$ es el máximo pico, $t_s$ es el tiempo de establecimiento, $E_{ss}$ es el error estacionario.
En general se sintoniza el PID empezando por P, luego I y finalmente D.
Encontrando $K_u$ de forma experimental.
Es un método heuristico para sintonizar el PID, creado en 1942. Para este método se inicia con un controlador proporcional puro P y se empieza a aumentar la ganancia $K_p$ hasta que el sistema se vuelva oscilatorio (oscilaciones que se mantienen en el tiempo), esta ganancia que genera este comportamiento denota la ganancia ultima $K_u$. Este movimiento de la salida tiene asociado un periodo $T_u$ de oscilación. Con estos dos valores se calculan los valores del PID, siguiendo la próxima tabla.
Tipo de controlador | $K_p$ | $T_i$ | $T_d$ |
---|---|---|---|
P | $0.50K_u$ | ||
PI | $0.45K_u$ | $T_u/1.2$ | |
PD | $0.80K_u$ | $T_u/8$ | |
PID clásico | $0.60K_u$ | $T_u/2$ | $T_u/8$ |
PID con poco sobrepico | $0.33K_u$ | $T_u/2$ | $T_u/3$ |
PID sin sobrepico | $0.20K_u$ | $T_u/2$ | $T_u/3$ |
$K_u$ también puede ser encontrado utilizando el margen de ganancia (funcion control.margin). El periodo $T_u$ esta relacionado con la frecuencia a la cual ocurre el margen de ganancia.
Analicemos la función de transferencia del error del primer ejemplo trabajado, cuya funcion de transferencia se presenta a continuación:
display(G1A)
veamos que pasa con la función del error para este sistema en el tiempo.
respuesta_error_1(2)
Para este sistema no existe un valor $K>0$ que haga el sistema inestable.
Analicemos el ejemplo número 2, cuya función de transferencia es presentada acontinuación.
display(G2)
respuesta_error_2(1)
K = 1.6
E6 = 1/(1+K*G2); display(E6)
t,y = control.step_response(E6);
plt.plot(t,y);
Ku = 1.64
Tu = 2*3.14159/1.77
Ti = Tu/2
Td = Tu/3
K = 0.2 * Ku *(1+1/(Ti*s)+Td*s)
E6 = 1/(1+K*G2); display(E6)
t,y = control.step_response(E6);
plt.plot(t,y);
K = (2.5 + 1.5*s + 1*s**2)*2;
E6 = 1/(1+K*G2); display(E6)
t,y = control.step_response(E6);
plt.plot(t,y);
sympy.expand((ss - (-0.75+1.39194109j))*(ss-((-0.75-1.39194109j))))
control.root_locus(K*G2);