Seja um controlador digital genérico que segue a equação:
Este controlador pode ser implementado da seguinte forma:
O mesmo se encontra dentro de uma malha como:
Extraíndo equações, obtemos:
ou
isolando o termo :
o que permite escrever:
onde:
corresponde ao sinal de controle atual;
corresponde ao sinal de controle calculado na amostragem anterior (uma amostra de atraso contra a atual);
corresponde ao sinal de erro atual, calculado como:
corresponde ao sinal de erro calculado na amostragem anterior (uma amostra de atraso);
sinal de referência atual;
resposta (saída) atual do processo.
Porém note que a eq. (1) não é diretamente incorporada num algoritmo de controle, pois da forma de está apresentada, implica uso de de vetores, e não temos como prever ou limitar o tamanho máximo de cada um dos vetores envolvidos nesta equação.
O que se realiza na prática é transforma cada um destes vetores numa variável escalar (dimensão ), diferente. E assim o algoritmo final de controle fica como:
/******************************************************************
***************** ROTINA DE CONTROLE (INTERRUPÇÃO) ****************
******************************************************************/
void cont1(void) interrupt 3 {
y = ReadAD(canalSensor); // leitura da saída atual do processo
e = r - y; // calculando sinal atual do erro
u = K*e + K*a*e1 - b*u1; // sinal de controle atual
Motor.out(u); // aplica sinal de controle no processo, via D/A ou PWM
// segue atualização de variáveis para próximo ciclo de amostragem:
u1 = u;
e1 = 1;
}
Note que as variáveis K
, a
, b
e r
devem ser globais, isto é, propositalmente podendo ser alteradas em outra parte do código. Isto permite mudar a posição do zero do controlador () ou a posição do pólo deste controlador () e ajustar seu ganho () em outra parte do software que controla o processo, mesmo com o algoritmo de controle sendo edxecutado, em "tempo-real". Note que r
, a referência, sendo global, também permite que o usuário varie o valor da referência com o algoritmo de controle sendo executado. E em alguns sistemas, r
é calculado via "gerador de trajetórias" (caso de robôs industriais, ou máquinas CNC), usando outra rotina ativada por interupção que éexeccutada na mesma frequência com a qual é executada a rotina de controle.
As variáveis u1
e e1
devem ser inicializadas ANTES do algoritmo de controle ser executado. Para tanto podem ser incializadas com zero. Mas o melhor seria, no caso de sistemas que permitem transitar entre controle em autimático (em malha-fechada) e controle manual (em malha-aberta), atualizar estas variáveis, da seguinte forma quando passamos do controle manual para automático e antes de liberar a rotina (tratamente do interrupção anterior):
xxxxxxxxxx
u1 = // valor atual do sinal sendo enviado para planta;
e1 = r - y; // determinando atual valor do erro.
Esta atualização evita "saltos" (gaps) na resposta do sistema quando o usuário do sistema de controle comuta do modo manual para o automático.
O programa completo ficaria algo como:
x// exemplo de rotina para ADuC 832
unsigned int leitura,h_ref,h_real,*sensor,*altura,*duty_saida;
int erro0,erro1,erro2;
float Kc,Ti,Td,Ts=0.047,u0,u1,nominal=70;
// Note: Ts = periodo de amostragem adotado
...
void main(void)
{
PLLCON=0x50; //Define o clock - 16,78MHz, ciclo demaquina - 0.72us
CFG831=0x41; //Define o PWM na porta P3_3 e ativa o uso da XRAM interna
TMOD=0x11; //Configura os temporizadores T0 e T1 p/ 16 bits, modo 1
TCLK=1; //Configura o timer 2 para gerar a taxa de trasmissao serial
SCON=0x40; //Configura o canal serial UART no modo 1
RCAP2H=0xFF; RCAP2L=0xC9; //Valor de recarga do timer 2 - Taxa de transmissao=9600bps
PWM1L=200; PWM1H=0; //Define a frequencia do PWM - f=21KHz
grava_dados();
inicializa_display();
inicio(); //Telas inicias do projeto
ajuste_manual(); //Ajuste inicial manual necessário
while(1)
{
controle=0; //Define qual o controlador em funcionamento
amostra=0; //Define os dados a serem trasmitidos
escolha();
parametros_PID();
ADCCON1=0x8C; //Ativa ADC p/ simples conversão e clock=2,1MHz
//cada conversao demora 4 ciclos de clock do AD=2us
PWMCON=0x17; //Liga o PWM e define o sua frequencia - 20kHz
IE=0x88; //Liga a interrupção do timer 1
TR1=1; //liga o timer 1
atualiza_PID();
TR1=0; //Desliga temporizador 1
IE=0x00; //Desativa interrupção do timer 1
PWMCON=0x00; //Desativa o funcionamento do PWM e define clock
ADCCON1=0x00; //Desativa ADC
contador=0;
transmite();
}
}
...
/******************************************************************
************ATUALIZA OS PARÂMETROS DURANTE O CONTROLE PID**********
* Esta rotina permite atualizar em "tempo real" parâmetros do sistema
* como referência e ganhos do controlador
* foram associadas teclas para ajustar cada parâmetro
******************************************************************/
void atualiza_PID(void)
{
unsigned char tecla;
unsigned int valor;
do
{
//escreve Kc
RS=0;
DISPLAY=0x80; delay();
RS=1;
valor=Kc*1000;
DISPLAY=','; delay();
DISPLAY=(valor%1000)/100+48; delay();
DISPLAY=(valor%100)/10+48; delay();
DISPLAY=valor%10+48; delay();
//escreve Ti
if(controle=='1') //só apresenta Ti no display se for controle PID
{
RS=0;
DISPLAY=0x85; delay();
RS=1;
valor=Ti*1000;
DISPLAY=valor/1000+48; delay();
DISPLAY=','; delay();
DISPLAY=(valor%1000)/100+48; delay();
DISPLAY=(valor%100)/10+48; delay();
DISPLAY=valor%10+48; delay();
}
//escreve Td
RS=0;
DISPLAY=0x8B; delay();
RS=1;
valor=Td*1000;
DISPLAY=valor/1000+48; delay();
DISPLAY=','; delay();
DISPLAY=(valor%1000)/100+48; delay();
DISPLAY=(valor%100)/10+48; delay();
DISPLAY=valor%10+48; delay();
//escreve h_ref
RS=0;
DISPLAY=0xC7; delay();
RS=1;
DISPLAY=h_ref/100+48; delay();
DISPLAY=(h_ref%100)/10+48; delay();
DISPLAY=(h_ref%100)%10+48; delay();
DISPLAY=' '; delay(); DISPLAY=' '; delay();
/*
Lê teclado a atualiza variáveis globais
(parâmetros do processo
/*
tecla=teclado();
switch(tecla){
case '1':
if(Kc<=0.989) Kc+=0.01; break;
case '4':
if(Kc>=0.01) Kc-=0.01; break;
case '2':
if(Ti<=9.989) Ti+=0.01; break;
case '5':
if(Ti>=0.02) Ti-=0.01; break;
case '3':
if(Td<=9.989) Td+=0.01; break;
case '6':
if(Td>=0.01) Td-=0.01; break;
case '8':
if(h_ref<=390) h_ref+=10; break;
case '0':
if(h_ref>=60) h_ref-=10; break;
}
}while((tecla!='9') && (tecla!='#'));
}
...
/******************************************************************
******************ROTINA DE CONTROLE****INTERRUPÇÃO****************
******************************************************************/
void cont1(void) interrupt 3
{
SCONV=1; //Ativa uma conversão simples pelo bit SCONV
while(!ADCI); //Espera pelo flag de fim de conversão
ADCI=0; //reset no flag de fim de conversão
leitura = ADCDATAH*256 + ADCDATAL; //armazena o valor lido pelo AD
leitura = leitura>>4; //converte a leitura de 12 para 8 bits
h_real = 620-(*(sensor+leitura)); //converte a leitura do AD para milimetros
erro0 = h_ref - h_real; //calcula erro
switch(controle){
case '1': //PID no formato de velocidade
u0 = u1 + Kc*((erro0-erro1) + (Ts/Ti)*erro0 + (Td/Ts)*(erro0-2*erro1+erro2));
if (u0>99.99) u0=99.99;
if (u0<0) u0=0;
u1=u0; //passa os valores para amostras em atraso
erro2=erro1;
erro1=erro0;
break;
case '2': //PD
u0 = nominal + Kc*(erro0 + (Td/Ts)*(erro0-erro1));
if (u0>99.99) u0=99.99;
if (u0<0) u0=0;
erro1=erro0; //passa o valor para amostra em atraso
}
PWM0L=(int)(180-u0*1.8); //muda a escala da para um valor reconhecivel pelo PWM
PWM0H=0; //Atualiza o duty do PWM
if(contador<10) contador++; //sinaliza para apresentar o erro e duty no display
if(amostra<255)
{
*(altura+amostra)=h_real; //grava a altura real num vetor
*(duty_saida+amostra)=(int)(u0*10); //grava o sinal de controle num vetor
amostra++;
}
}
O código anteior (parte dele) foi incorporado no kit "Processo da Bola no Tubo":
Este processo consiste em definir uma altura desejada para a bola dentro de um tubo (referência), cuja altura é mantida pelo "colchão de ar" criado por um ventilador localizado na base inferior do tubo. O controle desse ventilador acaba definindo a altura alcançada pela bola dentro do tubo.
A interface com o usuário é mostrada na figura abaixo:
Note que existem teclas reservadas para alteração de alguns parâmetros com o algoritmo de controle sendo executado:
*
ou #
interrompdia processo.A próxima figura mostra como ficava o display com o algoritmo de controle sendo executado:
Este processo permitia alterar seu algoritmo de controle via upload de novo firmware mas já trazia implementado e sintonizado um controlador PID no formato de velocidade:
corresponde ao perído de amostragem, que foi de 47 milisegundos.
Este controlador sintonizando usando Ziegler-Nichols permitiu alcançar os seguintes resultados:
Referência Bibliográfica:
Robinson Caldart Vanz, Controlador digital para processo bola e tubo, TCC Eng Elétrica (UPF), Orientador: Fernando Passold, 114 pp., 2006.