Describir los sistemas en el espacio de estados y encontrar un controlador para esta representación.
Es una representación matricial de un sistema lineal la cual permite representar sistemas MIMO (multiple-input multiple-output) entre otros. La representación cuenta con dos ecuaciones:
donde $\mathbf{x}$ es el vector de estados, $\mathbf{y}$ es el vector de salidas, $\mathbf{u}$ es el vector de entradas, $A$ es la matriz de estado, $B$ es la matriz de entrada, $C$ es la matriz de salida y $D$ es la matriz de transmision directa.
Cuando nosotros diseñamos un sistema estamos diseñando dichas matrices.
Para el control del sistema, la pregunta es:
$$\textbf{¿Cómo debe ser la entrada seleccionada?}$$para lograr que el sistema haga lo que yo quiero.
Para esto usaremos la ecuación diferencial de un sistema masa-resorte-amortiguador (MKC).
t,m,c,k = sympy.symbols('t m c k')
F = sympy.Function('F')(t)
x = sympy.Function('x')(t)
eqMKC = sympy.Eq(F,m*sympy.diff(x,t,2)+c*sympy.diff(x,t)+k*x)
display(eqMKC)
Inicialmente construyamos un diagrama de bloques que represente esta ecuación.
Se evidencia el uso de dos bloques integradores. El uso de estos bloques da indicios de los estados de este sistema, de cada bloque integrador tenemos, la posición $x(t)$ y la velocidad $\dot{x}(t)$ de la masa. Estos son los estados del sistema.
Sabemos del diagrama de bloques que la posición $x(t)$ y la velocidad $\dot{x}(t)$ son los estados del sistema.
Con estos podemos construir el vector de estados:
$$\mathbf{x} = \left[\begin{array}{}\dot{x}(t)\\{x}(t)\end{array}\right] =\left[\begin{array}{}x_1\\x_2\end{array}\right]$$Con $x_1$ y $x_2$, podemos reescribir la ecuación diferencial como:
x1, x2, dx1, dx2 = sympy.symbols('x_1 x_2 \dot{x}_1 \dot{x}_2')
eqMKC = eqMKC.subs(sympy.diff(x,t,2),dx1)
eqMKC = eqMKC.subs(sympy.diff(x,t),x1)
eqMKC = eqMKC.subs(x,x2)
sol_dx1 = sympy.Eq(dx1,sympy.expand(sympy.solve(eqMKC,dx1)[0]))
sol_dx2 = sympy.Eq(dx2,x1)
display(sol_dx1)
Falta la expresion para $\dot{x}_2$. La cual es simplemente
$\dot{x}_2 = x_1$
De aquí ya podemos construir la ecuación de estado.
De la ecuaciones anteriores podemos contruir la ecuación matricial de estados:
MA = sympy.Symbol('A'); display(MA)
mA = sympy.Matrix([[-c/m,-k/m],[1,0]])
display(mA)
MD = sympy.Symbol('D'); display(MD)
display(sympy.Matrix([1/m,0]))
Para verificar la estabilidad del sistema debemos evaluar los valores propios ($\lambda$) de la matriz $A$:
$$I-\lambda A$$para encontrarlo encontremos la ecuación caracteristica con $\textbf{det}(I-\lambda A)$:
l = sympy.Symbol('\lambda')
caract = (sympy.eye(2)-l*mA).det();
display(caract)
de aquí los valores propios son:
display(mA.eigenvals())
Vemos que son los mismos polos del sistema MKC si lo analizamos de forma clásica.
Diseñemos un control proporcional para el sistema propuesto, utilizando el espacio de estados.
$$m \ddot{x} = F$$La ecuación de estado quedaría asi (con $m=1$).
$$\dot{\mathbf{x}}=\left[\begin{array}{}0&1\\0&0\end{array}\right]\mathbf{x}+\left[\begin{array}{}0\\1\end{array}\right]\mathbf{u}$$La ecuación de salida sería.
$$\mathbf{y}=\left[\begin{array}{}1&0\end{array}\right]\mathbf{x}$$Si queremos controlar el sistema con un lazo cerrado debemos conectar de alguna forma la salida $\mathbf{y}$ con la entrada $\mathbf{u}$
plt.plot([-2,2],[0,0])
plt.plot([0,0],[-1,1],color='r')
plt.plot([-1],[0],color='k',marker='.',markersize=30)
plt.xlim(-2,2);
El objetivo de control es que se mueva al origen. ¿Cómo lo logramos?
En general tenemos:
$$\mathbf{u}=-K\mathbf{y} = -KC\mathbf{x} $$entonces:
$$\dot{\mathbf{x}}=A\mathbf{x}+B\mathbf{u}=A\mathbf{x}-BKC\mathbf{x} = \left(A-BKC\right)\mathbf{x}$$tenemos aquí un nuevo sistema, el sistema en lazo cerrado.
$$\dot{\mathbf{x}} = \left(A-BKC\right)\mathbf{x}=\hat{A}\mathbf{x}$$nuestro trabajo ahora es seleccionar $K$ de tal forma que los valores propios de la matriz $\hat{A}$ den por lo menos un sistema estable.
Remplazamos los valores de las matrices y de $K=1$:
$$\hat{A}=\left(A-BKC\right)=\left(\left[\begin{array}{}0&1\\0&0\end{array}\right]-\left[\begin{array}{}0\\1\end{array}\right]1\left[\begin{array}{}1&0\end{array}\right]\right)$$mA = sympy.Matrix([[0,1],[0,0]])-sympy.Matrix([0,1])*1*sympy.Matrix([[1,0]])
display(mA)
Analizando los valores propios del sistema tenemos que:
display(mA.eigenvals())
El sistema es criticamente estable.
Con el controlador propuesto, la particula se comporta como se muestra.
display(ani)
¿Por qué no se queda en el origen si este es el objetivo?
Para estabilizar la particula necesitamos conocer la información de todos los estados del sistema. Salvo que en nuestro sistema solo tenemos un sensor de posición:
$$\mathbf{y}=\left[\begin{array}{}1&0\end{array}\right]\mathbf{x}$$El estado desconocido es la velocidad, el cual puede ser estimado de la posición. Por ahora supongamos que podemos medir ambos estados.
$$\mathbf{y}_{supuesto}=\left[\begin{array}{}1&0\\0&1\end{array}\right]\mathbf{x}$$con esta nueva matriz $C$, proponemos un controlador $K$:
$$K = \left[\begin{array}{}k_1&k_2\end{array}\right]$$Encontremos la nueva matriz $\hat{A}$ para el sistema en lazo cerrado.
Recordemos que aquí estamos suponiendo que podemos medir ambos estados del sistema (posición y velocidad). Remplazamos los valores de las matrices y de $K$:
$$\hat{A}=\left(A-BKC\right)=\left(\left[\begin{array}{}0&1\\0&0\end{array}\right]-\left[\begin{array}{}0\\1\end{array}\right]\left[\begin{array}{}k_1&k_2\end{array}\right]\left[\begin{array}{}1&0\\0&1\end{array}\right]\right)$$Luego $\hat{A}=$
k1,k2 = sympy.symbols('k_1 k_2')
mA3 = sympy.Matrix([[0,1],[0,0]])-sympy.Matrix([0,1])*sympy.Matrix([[k1,k2]])*sympy.Matrix([[1,0],[0,1]])
display(mA3)
Los valores propios o polos del sistema son:
eigen = mA3.eigenvals()
display(eigen)
polos(3,2)
Tomemos los valores para el controlador $k_1=1$ y $k_2=2$:
display(ani)
El posicionamiento de polos nos permite definir el comportamiento deseado del sistema. Tomemos como ejemplo es caso anterior de la particula, cuya ecuación de estados se presenta a contuación:
$$\dot{\mathbf{x}}=\left[\begin{array}{}0&1\\0&0\end{array}\right]\mathbf{x}+\left[\begin{array}{}0\\1\end{array}\right]\mathbf{u}$$A traves de la libreria de control de python, podemos obtener los valores del controlador que nos permita tener de comportamiento deseado. Con contro.place(A,B,poles) obtenemos los siguientes valores para los polos [-1,-2]
A4 = [[0,1],[0,0]]
B4 = [[0],[1]]
display(control.place(A4,B4,[-1,-2]))
#display(control.acker(A4,B4,[-1,-2]))
matrix([[2., 3.]])
Con el siguiente sistema no se le puede ubicar los polos.
$$\dot{\mathbf{x}}=\left[\begin{array}{}2&0\\1&1\end{array}\right]\mathbf{x}+\left[\begin{array}{}1\\1\end{array}\right]\mathbf{u}$$El controlador para dicho sistema seria:
A4 = [[2,0],[1,1]]
B4 = [[1],[1]]
display(control.place(A4,B4,[-21,-20]))
matrix([[-6.36905167e+15, 6.36905167e+15]])