Está en la página 1de 16

Maqueta de control PID con Arduino

.
.
Con el motivo de la primera VirtualCamp (Julio 2011), el proyecto ha sido crear una maqueta de
control didctica con un presupuesto de menos de 10 euros (Arduino no incluido). La idea fue hacer un
proyecto en 48h durante el fin de semana del 16-17 de Julio.
El proceso elegido es el control de temperatura. Para ello dos resistencias de potencia generan el calor y
un ventilador actua como control para la refrigeracin. Un sensor resistivo de tipo NTC se encarga de
registrar la temperatura del habitculo.
Se ha instalado el sensor de temperatura en medio de las resistencias de potencia para que el proceso
disponga de la menor inercia posible, y as se comporte ms rpido ante cambios de la entrada.
Colaboradores del proyecto:
Igor R.
Alberto Yebra -> Interfaz grfica en python para la visualizacin del proceso. [se ampliar este
proyecto con su trabajo].
LeoBot (Manuel) -> Teora control PID. [se ampliar este proyecto con su trabajo].

Componentes necesarios:

Ventilador 12v usado para refrigeracin de componentes en ordenadores.


Resistencias de potencia (47ohm) --> 2 unidades
Un sensor temperatura resistivo (NTC) + resistencia 3k pull up
Transistor Mosfet.
Diodo 1N4148.
Un recipiente.
Transformador externo para alimentacin a 12v.

Los objetivos del proyecto usando Arduino han sido:


1.
2.
3.
4.

Creacin fsica de la maqueta.


Control de un proceso usando un PID.
Captura de seal analgica.
Convertir la seal elctrica a fsica mediante una tabla guardada en memoria de programa
(flash) para evitar uso de RAM del microcontrolador.
5. Control carga de potencia mediante PWM.
6. Monitorizacin en el PC de resultados.

Vemos una foto de como ha quedado la maqueta:

Otra foto de la instalacin del ventilador:

La instalacin elctrica necesaria es bastante sencilla. La podemos dividir en dos partes: control del
ventilador mediante transistor mosfet y adquisicin de la temperatura mediante NTC.
A continuacin, se puede observar un esquema de la conexin del ventilador. La salida utilizada en
Arduino es el pin 9 (PWM).

La salida de control PID es el tiempo encendido del ventilador. Es decir, el duty cycle de nuestro PWM.
Se ha modificado el registro del timer 1, que controla la frecuencia del pin 9, para que la frecuencia sea
aproximadamente 32 kHz.
Vemos una captura real de la misma:

Un ventilador es una carga inductiva, por lo que es necesario aadir un diodo cmo indica la figura
anterior.
Para el caso del sensor NTC:

Para evitar resolver operaciones matemticas en el microcontrolador, se ha creado una tabla de valores
obtenidos del conversor ADC del Arduino versus temperatura en grados centgrados.

Dicha tabla, ser guardada en memoria de programa, evitando consumir memoria RAM
innecesariamente de nuestro Arduino.
Se observa que la respuesta de un sensor NTC no es lineal.
La NTC se ha situado entre las dos resistencias calentadoras. De esta forma, se consigue que el sistema
tenga menos inercia y su respuesta sea rpida.

CONTROL PID
El control Proporcional Integral Derivativo (PID) es mecanismo de control mediante realimentacin
negativa, el cual aplica una accin correctora al sistema para obtener el valor de consigna (Setpoint).
Existe multitud de recursos en internet acerca del mismo, por lo que aqu se ver desde el punto de
vista prctico. Por ejemplo, wikipedia dispone de una buena introduccin.
El nuestro sistema:
Set Point -> Temperatura de consigna del sistema. El algoritmo oscila entre dos temperaturas,
con el motivo de ver como se comporta ante cambios de la entrada. Es decir, es a la temperatura
que queremos que se encuentre nuesto recipiente.
La realimentacin o informacin de cmo se encuentra el sistema, es adquirida por la NTC.
El actuador es el ventilador, el cual regulamos su velocidad mediante modulacin de ancho de
pulsos (PWM).
Para la visualizacin, se ha utilizado el software gratuito para Windows Stamp Plot Lite
(http://www.selmaware.com/stampplotlite/index.htm).

Empezaremos por realizar slo un control PROPORCIONAL. El programa de Arduino, cambia cada
120 segundos el Set Point entre 60 y 59 grados centgrados.
Se puede observar cmo el control proporcional no elimina el error permanente del sistema.

Existe un lmite en el valor de la constante proporcional, ya que nuestro sistema se vuelve oscilatorio,
ya que un pequeo cambio en el error, hace que la salida de controlador sea muy grande. A
continuacin un ejemplo:

Como se ha observado, la parte proporcional no tiene en cuenta el tiempo, por lo que para corregir el
error permanente, necesitamos aadirle la parte INTEGRAL.
Para poder observar cmo se comporta, se ha introducido un retraso a la actuacin de la misma.

La parte integral consigue que nuestro sistema alcance los valores objetivo (Set Points de 60 y 58
grados).
La parte DERIVATIVA,slo actua si hay un cambio en el valor absoluto del error. Es decir, si el error es
constante, no actuar.
El objetivo final de un sistema de control, es que el sistema siga lo ms fielmente a su entrada ante
perturbaciones externas y que responda ante cambios de consigna (SetPoint). Vemos un video, en el
cual se altera el sistema (tapando la salida de aire-cables) y como al subir la temperatura, el sistema
aumenta la velocidad del ventilador para tratar de contrarrestar la perturbacin.

Sketch:
//-------------------------------------------// MAQUETA DE CONTROL PID
// VIRTUALCAMP JUL 2011
// IGOR R.
//-------------------------------------------#include <avr/pgmspace.h>
//CONSTANTES
#define cte_kp 500.0
#define cte_ki 10.0
#define cte_kd 3500.0
#define Margin_Int 0.9
#define target1 60.0
#define target2 58.0

//300
//5.0
//60
//58

//Variables de control de flujo


long tInit;
long tSerial;
//Input
float Target;
//Sensor
float temp;
//Output
float dutycycle;
//PID
float error;
float prev_error;
float Proportional;
float Integral;
float Derivative;
float Kp;
float Ki;
float Kd;
long LastTime;
long SampleTime;
long timeOutInt;
//Tabla guardada en memoria de programa del Arduino (FLASH)
PROGMEM prog_uint16_t ntc[21][2] = {
{861,0},
{778,10},
{678,20},
{570,30},
{463,40},
{366,50},
{285,60},
{219,70},
{168,80},
{128,90},

{99,100},
{77,110},
{60,120},
{47,130},
{38,140},
{30,150},
{24,160},
{20,170},
{16,180},
{14,190},
{11,200},
};
void setup()
{
//Empiezo comunicacion serie a 19200(maxima velocidad de StampPlot)
Serial.begin(19200);
delay(10);
//Configuro el StampPlot (maximo, minimo, grabar en un archivo los datos, etc).
config_StampPlot();
//Configuro el pin 9 como salida (PWM)
pinMode(9,OUTPUT);
//Capturo tiempos para el control de mi programa.
tInit=millis();
LastTime=millis();
//Valor de mis constantes del control PI
Kp=cte_kp;
Ki=cte_ki;
Kd=cte_kd;
//Modificar frecuencia PWM
//Setting
Divisor
Frequency
//0x01
1
31250
//0x02
8
3906.25
//0x03
64
488.28125
//0x04
256
122.0703125
//0x05
1024
30.517578125
//TCCR1B = TCCR1B & 0b11111000 | <setting>;
//Configuro el PWM a 30 kHz.
TCCR1B=TCCR1B & 0b11111000 | 0x01;
}
void loop()
{
//Recojo 10 veces el dato del sensor y hago la media
for (int i=0; i<10;i++)
{
temp+=calcTemp(analogRead(A5)); //Funcin que transforma mi lectura del ADC
en grados usando una tabla guardad en memoria FLASH
}
temp/=10;

// Cambio el SetPoint a target1 y target2 cada 120 segundos


Target=target1;
long time=(millis()-tInit)/1000;
if ((time>60) && (time<120) )
{
Target=target2;
}else if ( time>120 )
{
tInit=millis();
}
//---------------------------------------------------//CONTROL PID
//Hago los calculos de manera periodica
SampleTime=(millis()-LastTime);
if (SampleTime>=100)
{
LastTime=millis();
//Calculo de error (diferencia entre SetPoint y temperatura actual
prev_error=(float)error;
error=(float)temp-(float)Target;
Proportional=(float)Kp*(float)error;
//El control integral solo entra cuando esta lo suficientemente cerca
//Esto es para evitar saturar el control
if ( (abs(error)<=Margin_Int))
{
if (timeOutInt++ >10)
//Si se cumple durante X veces el SampleTime
(delay de la parte integral)
{
Integral+=(float)Ki*(float)error;
timeOutInt=3000;
//Para evitar desbordamiento del numero si
esta durante mucho tiempo
}
}else
{
Integral=0;
timeOutInt=0;
}
//Parte derivativa
Derivative=(float)Kd*((float)error-(float)prev_error);
//---------------------------------------------------------------dutycycle=(int)Proportional+ (int)Integral + (int)Derivative;
//Limites de la salida. PWM de Arduino 0-255 (8 bits).
if (dutycycle <0)
{
dutycycle=0;
}else if (dutycycle>255)
{
dutycycle=255;
}

//Actualizo salida
analogWrite(9,(int)dutycycle);
//Mando datos a Stamplot
Serial.print(temp);
Serial.print(13,BYTE);
Serial.print("D=");
Serial.print(dutycycle);
Serial.print(";E=");
Serial.print(error);
Serial.print("; P=");
Serial.print(Proportional);
Serial.print("; I=");
Serial.print(Integral);
Serial.print("; D=");
Serial.print(Derivative);
Serial.print(13,BYTE);
}
//---------------------------------------------------}
// Funcion de conversion lectura ADC a grados mediante una tabla guardada en
memoria de programa del micro
// para no gastar memoria RAM.
float calcTemp(uint16_t myADC)
{
//Recorro la tabla de valores del sensor NTC
for(int i=0; i<21;i++)
{
//No extrapola, si es menor se queda con el primer valor de la tabla
if (myADC>=pgm_read_word(&(ntc[0][0])))
{
temp=pgm_read_word(&(ntc[0][1]));
break;
}
//No extrapola, si es mayor se queda con el ultimo valor de la tabla
if (myADC<=pgm_read_word(&(ntc[20][0])))
{
temp=pgm_read_word(&(ntc[20][1]));
break;
}
uint16_t actualADC=pgm_read_word(&(ntc[i][0]));
if (myADC>=actualADC)
{
if (i>0)
{
uint16_t previousADC=pgm_read_word(&(ntc[i-1][0]));
uint16_t previousTemp=pgm_read_word(&(ntc[i-1][1]));
uint16_t actualTemp=pgm_read_word(&(ntc[i][1]));

temp=( ((float)myADC - (float)previousADC) * ((float)actualTemp (float)previousTemp) / ((float)actualADC - (float)previousADC) )+previousTemp;


}
}

break;

return (temp);
}
void config_StampPlot()
{
//---------------------------------------------------------//CONFIGURACION STAMP PLOT LITE
//---------------------------------------------------------//Titulo de la ventana (FORM)
Serial.print("!TITL Arduino Power!");
Serial.print(13,BYTE);
//Titulo de usuario (STATUS)
Serial.print("!USRS Control PID");
Serial.print(13,BYTE);
//Valor maximo del eje Y
Serial.print("!AMAX 62");
Serial.print(13,BYTE);
//Valor minimo del eje Y
Serial.print("!AMIN 57");
Serial.print(13,BYTE);
//Valor maximo de tiempo
Serial.print("!TMAX 500");
Serial.print(13,BYTE);
//Configuro el numero de puntos
Serial.print("!PNTS 8000");
Serial.print(13,BYTE);
//Ayadir Tiempo en la lista de mensajes
Serial.print("!TSMP OFF");
Serial.print(13,BYTE);
//Plot ON
Serial.print("!PLOT ON");
Serial.print(13,BYTE);
//Borra el valor Max y Min almacenado despues del RESET
Serial.print("!CLMM");
Serial.print(13,BYTE);
//Limpio la lista de mensajes
Serial.print("!CLRM");
Serial.print(13,BYTE);
//Borro el fichero stampdat.txt
Serial.print("!DELD");
Serial.print(13,BYTE);
//Borro el fichero stampmsg.txt
Serial.print("!DELM");
Serial.print(13,BYTE);
//Salvar datos Analogicos y digitales en stampdat.txt
Serial.print("!SAVD ON");

Serial.print(13,BYTE);
//Salvar Mensajes en stampmsg.txt
Serial.print("stampmsg.txt");
Serial.print(13,BYTE);
//RESET DEL GRAFICO PARA COMENZAR A PLOTEAR
Serial.print("!RSET");
Serial.print(13,BYTE);
//---------------------------------------------------------}

También podría gustarte