Hola controleras y controleros, hoy vamos a aprender como implementar un Predictor de Smith en un microcontrolador PIC, utilizando como controlador primário un PID. Todo será implementado en el compilador CCS C.
Antes que nada te hago la invitación para 👉 Ver el Curso de PIC completo.
Predictor de Smith
El predictor de Smith es una estructura de control ampliamente utilizada en la industria para poder realizar control de procesos que presentan un retardo dominante. La estructura se presenta a continuación:
Donde P(s) es el proceso (Horno), C(S) será el controlador primário (En este caso usaremos un PID), Gn(s) es el modelo del proceso (función de transferencia del horno), Ln es el retardo del horno.
Mira la siguiente entrada para entender el funcionamiento del 👉 Predictor de Smith.
Predictor de Smith con PIC
Para la implementación de la estructura del predictor de Smith dentro del microcontrolador, tendremos que recurrir a la representación discreta de la estructura.
Y para eso vamos a tomar la función de transferencia que obtuvimos de la Identificación del Horno de Temperatura.
Vamos a discretizar esa función de transferencia con un tiempo de muestreo de T=10s usando el retenedor de orden cero ZOH. Donde obtendremos una función de transferencia discreta de la siguiente forma:
Implementando un controlador PID discreto, podemos proceder a montar nuestra estructura del predictor de Smith en un microcontrolador PIC.
Ecuaciones en diferencias
Para poder escribir la función de transferencia discreta dentro del microcontrolador PIC, se debe transformar dicha función en una ecuación en diferencias.
Se lleva a potencias negativas en z
Se resuelve para obtener la salida del modelo
Con eso ya podemos calcular el resultado de la salida del modelo (función de transferencia) del horno en lenguaje C.
//Salida del modelo nominal Pn(z) yE= b0*u[kT-d]+b1*u[kT-1-d]-a1*yE_1;
Notemos que
Y que u es un vector. En este caso de 15 posiciones, donde la ultima posición corresponde al tiempo actual (presente) y las demás posiciones corresponden a muestras en el pasado.
Implementación del Predictor de Smith en un PIC 18F4550
El controlador primário del PIC, sera el controlador PID explicado en la entrada Control de Temperatura de un Horno.
El circuito a ser implementado, será el mismo empleado en la entrada anterior de la identificación del horno.
Donde se ve que se usa 2 XBEE para realizar una comunicación inálambrica.
La respuesta del controlador sobre el horno
El código del PIC en CCS C y el código en MATLAB para la interfaz gráfica pueden ser descargados a continuación, solo basta con que compartas el contenido de este post con cualquiera de los siguientes botonos, de esa forma ayudas a que más personas aprendan sobre esto y contribuyes a mejorar la educación.
Otros Controladores implementados en PIC
Si deseas saber como implementar otro tipo de controladores como diversas configuraciones del PID, logica Fuzzy, Espacio de estados, RST, etc, te invito a que te matricules a mi curso de Control en Sistemas Embebidos, y puedes seleccionar el micro que más te guste (PIC o Arduino):
- Curso de Sistemas de Control en Dispositivos Microcontrolados en UDEMY (PIC y ARDUINO)
- Certificado de Aprobación una vez finalices el Curso
- DESCUENTO si accedes directamente con los siguientes botones de acceso.
- NOTA: Si buscas el curso directamente en UDEMY o si lo adquieres en otra plataforma distintas a las mostradas anteriormente NO OBTENDRÁS NINGUN DESCUENTO sobre el valor final del Curso.
Código en CCS C (PIC C) Predictor de Smith
#include <18F4550.h> #device ADC=10 #FUSES NOWDT //No Watch Dog Timer #FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale #FUSES NOBROWNOUT //No brownout reset #FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O #FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode) #FUSES HS #use delay(crystal=20000000) #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8) #define TC_CLK PIN_B1 #define TC_CS PIN_C0 #define TC_DATA PIN_B0 #include "max6675.c" #include <stdlib.h> #include <string.h> long deg=0; int muest=0; int16 control=0; float R=0;//Referencia de 150 °C por defecto float yM=0,e=0.0,e_1=0.0,e_2=0.0,T=10; float kp,ti,td,q0,q1,q2; float k=2.3553 ,tao=370 ,theta=30; float TsMA,Wn,P1,P2; float b0=0,b1=0.0628,a1=-0.9733; //Parametros de la Funcion de transferencia discreta int d=3-1; //Retardo discreto (d-1) float u[15]; //Ley de control (debe ser mayor que el retardo + el numero de ceros de la FT) int kT=14; //Tiempo discreto actual (Ultima posición del vector u) float yE=0,yE_1=0; //Salida del modelo interno float ep=0; //Error de prediccion float yR=0,yR_1=0; //Salida del modelo Rapido (sin retardo) float yp=0; //Salida predicha //Define la interrupción por recepción Serial #int_RDA void RDA_isr() { int i=0,ini=0,fin=0; char dat[5]; char degC[5]; // Almacena 10 datos leidos del serial for(i=0;i<5;i++){ dat[i]=getc(); putc(dat[i]); } //Busco el valor del escalon en los datos recibidos for(i=0;i<5;i++){ if(dat[i]=='S'){ ini=i+1; i=10; } } for(i=ini;i<5;i++){ if(dat[i]=='$'){ fin=i-1; i=10; } } if(ini!=0 && fin!=0){ // salvo en degC el caracter con el escalon for(i=ini;i<=fin;i++){ degC[i-ini]=dat[i]; } deg = atol(degC); //Convierte el String en un valor numerico // valida que el escalón esté ente 0 y 300 grados celcius if(deg>1000) deg=1000; if(deg<0) deg=0; R=deg; } } //! /*===========================================================================*/ /*======================= FUNCION DEL CONTROL PID =======================*/ /*===========================================================================*/ void PID(void) { //char msg[32]; e=1*(R-yp); // Controle PID u[kT] = u[kT-1] + q0*e + q1*e_1 + q2*e_2; //Ley del controlador PID discreto //! sprintf(msg,"e=%3.2f u=%3.2f",e,u); //! printf("%s",msg); if (u[kT] >= 1000.0) //Saturo la accion de control 'uT' en un tope maximo y minimo u[kT] = 1000.0; if (u[kT] <= 0.0) u[kT] = 0.0; control=u[kT]; //Actualizo los valores pasados e_2=e_1; e_1=e; //La accion calculada la transformo en PWM set_pwm1_duty(control); output_toggle(PIN_B2); //LED verifica el tiempo de muestreo correcto } void main() { int i; char msg[32]; delay_ms(50); //allow oscillator to stabilise setup_timer_2(t2_div_by_4,249,1); //Configuracion de Timer 2 para establecer frec. PWM a 1kHz setup_ccp1(ccp_pwm); //Configurar modulo CCP1 en modo PWM set_pwm1_duty(0); //Dejo en cero la salida PWM enable_interrupts(INT_RDA); enable_interrupts(GLOBAL); set_tris_b(0); //*************************************************************************// //************* DISEÑO POR ASIGNACIÓN DE 2 POLOS REALES ****************// //*************************************************************************// TsMA=600; //Tiempo deseado en Lazo Cerrado Wn=4/(TsMA); //Frecuencia natural del sistema //Ubicación de 2 Polos reales P1=2*Wn; P2=Wn*Wn; kp=(P1*tao-1)/k; //Calculo de Kc ti=(k*kp)/(P2*tao); //Calculo de ti td=0; //*************************************************************************// //***************** DISEÑO POR CANCELACIÓN DE POLOS *******************// //*************************************************************************// /* TsMA=7.5; //Tiempo deseado en Lazo Cerrado kp=(tao)/(TsMA*k); //Calculo de Kc ti=tao; //Calculo de Ti (Igual a la constante de tiempo) td=0; */ //*************************************************************************// //***************** SINTONIA POR ZIEGLER y NICHOLS *******************// //*************************************************************************// //! kp=(0.9*tao)/(k*theta); //Z-N Rapido //! kp=kp/5; //Z-N Lento //! ti=3.33*theta; //! td=0; //*************************************************************************// // Calculo do controle PID digital q0=kp*(1+T/(2*ti)+td/T); q1=-kp*(1-T/(2*ti)+(2*td)/T); q2=(kp*td)/T; //Inicializa el vector de control for(i=0;i<=kT;i++){ u[i]=0; } //Inicializa las variables que contienen la lectura de salida yM=do_everything(); yE=yM;yE_1=yM;yR=yM;yR_1=yM; while(1){ yM=do_everything(); //sprintf(msg,"%3.2f%cC\r\n",do_everything(),0xB0); sprintf(msg,"I%3.2fFI%3.2fFC%ldRC%ldR",yM,yM,control/10,control/10); printf("%s",msg); // printf(" Hola Mundo "); if(muest>6){ muest=0; //Salida del modelo nominal Pn(z) yE= b0*u[kT-d]+b1*u[kT-1-d]-a1*yE_1; //Diferencia entre proceso real y proceso nominal (error de prediccion) ep=yM-yE; //Salida del modelo nominal Rapido (sin retardo) yR= b0*u[kT]+b1*u[kT-1]-a1*yR_1; //Suma entre error de prediccion y salida predicha yp=yR+ep; //Llama la funcion del controlador PID PID(); //desplaza el vector de la ley de control for(i=1;i<=kT;i++){ u[i-1]=u[i]; } //Actualizo los valores pasados yE_1=yE; yR_1=yR; } //tiempo de muestreo delay_ms(1000); muest++; } }
Código en Matlab
%% Ejemplo Monitoreo de señales en tiempo Real function varargout=monitoreo(varargin) parar=false; fclose('all') global tiempo salida escalon control fig(1)=figure('name','Monitor','menubar','none','position',[200 200 800 700],'color',[0.9 0.6 0.3]) movegui(fig(1),'center'); axe(1)=axes('parent',fig(1),'units','pixels','position',[60 380 600 280],'xlim',[0 40],'ylim',[0 200],'xgrid','on','ygrid','on') axe(2)=axes('parent',fig(1),'units','pixels','position',[60 50 600 280],'xlim',[0 40],'ylim',[0 100],'xgrid','on','ygrid','on') set(get(axe(1),'XLabel'),'String','Tiempo (Seg)') set(get(axe(1),'YLabel'),'String','Temperatura (°C)') set(get(axe(2),'XLabel'),'String','Tiempo (Seg)') set(get(axe(2),'YLabel'),'String','Control (%)') lin(1)=line('parent',axe(1),'xdata',[],'ydata',[],'Color','r','LineWidth',2.5); lin(2)=line('parent',axe(1),'xdata',[],'ydata',[],'Color','k','LineWidth',2); lin(3)=line('parent',axe(2),'xdata',[],'ydata',[],'Color','r','LineWidth',2.5); bot(1)=uicontrol('parent',fig(1),'style','pushbutton','string','Detener','position',[680 50 100 50],'callback',@stop,'fontsize',11) bot(2)=uicontrol('parent',fig(1),'style','pushbutton','string','Enviar','position',[680 200 100 50],'callback',@enviar,'fontsize',11) txbx(1)=uicontrol('parent',fig(1),'style','tex','string','Temp','position',[680 100 100 50],'fontsize',11) txbx(2)=uicontrol('parent',fig(1),'style','edit','string','000','position',[680 250 100 50],'fontsize',11) %% Funcion Pare function varargout=stop(hObject,evendata) parar=true; fclose(SerialP); delete(SerialP); clear SerialP; end %% Funcion enviar function varargout=enviar(hObject,evendata) deg1=get(txbx(2),'string'); deg=["S"+deg1+"$"]; fwrite(SerialP,deg,'uchar'); end %% funcion Graficar % function varargout=grafique(hObject,evendata) tiempo=[0]; salida=[0]; escalon=[0]; control=[0]; deg1="0"; dt=1; limx=[0 40]; limy=[0 200]; set(axe(1),'xlim',limx,'ylim',limy); %% Configura el Puerto Serial SerialP=serial('COM8'); set(SerialP,'Baudrate',9600); % se configura la velocidad a 9600 Baudios set(SerialP,'StopBits',1); % se configura bit de parada a uno set(SerialP,'DataBits',8); % se configura que el dato es de 8 bits, debe estar entre 5 y 8 set(SerialP,'Parity','none'); % se configura sin paridad fopen(SerialP); %% Grafico k=5;nit = 10000; while(~parar) % if get(bot(3),'value') % drawnow(); % necesario para que actualice el grafico % continue % forza a salir del while para la proxima interacion y de esta forma nop actualizo datos % % end % Lectura del Dato por Puerto Serial variable= (fread(SerialP,30,'uchar')); ini=find(variable==73); %Busca el I (Primer dato) ini=ini(1)+1; fin=find(variable==70); %Busca F (ultimo dato) fin= fin(find(fin>ini))-1; fin=fin(1); tempC=char(variable(ini:fin))'; temp=str2num(tempC); %Lectura de la senal de control ini=find(variable==67); %Busca el C (Primer dato) ini=ini(1)+1; fin=find(variable==82); %Busca R (ultimo dato) fin= fin(find(fin>ini))-1; fin=fin(1); Con1=char(variable(ini:fin))'; cont=str2num(Con1); set(txbx(1),'string',tempC); %Actualiza las variables del grafico tiempo=[tiempo tiempo(end)+dt]; salida=[salida temp]; control=[control cont]; escalon=[escalon str2num(deg1)]; set(lin(1),'xdata',tiempo,'ydata',salida); set(lin(2),'xdata',tiempo,'ydata',escalon); set(lin(3),'xdata',tiempo,'ydata',control); pause(dt); %% espera 1 seg para cada interación if tiempo(end)>=limx % actualizo grafica cuando llega a su limite en tiempo real limx=[0 limx(2)+40]; set(axe(1),'xlim',limx) ; set(axe(2),'xlim',limx); end if salida(end)>=limy % actualizo grafica cuando llega a su limite en tiempo real limy=[0 limy(2)+30]; set(axe(1),'ylim',limy); end k=k+1; if(k==nit) parar=true; end end parar=false; % end end
Perfecto controleros y controleras con esto llegamos al final de otro post del sitio web.
Recuerda que si deseas apoyar mi trabajo, puedes invitarme a un café y seguirme ayudando a mantener los servidores de este sitio web, es muy barato el café y contribuyes con el tiempo y esfuerzo invertidos en las clases elaboradas en el canal y pagina web: 👉Invitar a un Café a Sergio ☕️
Espero que esten muy bien y nos vemos en la próxima.
Mi nombre es Sergio Andres Castaño Giraldo, y en este sitio web voy a compartir una de las cosas que mas me gusta en la vida y es sobre la Ingeniería de Control y Automatización. El sitio web estará en constante crecimiento, voy a ir publicando material sobre el asunto desde temas básicos hasta temas un poco más complejos. Suscríbete al sitio web, dale me gusta a la página en Facebook y únete al canal de youtube. Espero de corazón que la información que comparto en este sitio, te pueda ser de utilidad. Y nuevamente te doy las gracias y la bienvenida a control automático educación.