Estrategias de control con Arduino
Introducción
El proyecto elegido para llevar a cabo con Arduino y en el que desarrollaremos diferentes tipos de controles, consiste en dos depósitos de agua de 13 litros de capacidad interconectados mediante una bomba. El depósito 1 se considera el principal y por lo tanto se implementará un control de nivel mediante PID y un control de temperatura del agua mediante un sistema ON/OFF con histéresis.
Control ON/OFF con histéresis
Para el control ON/OFF con histéresis, tal como se muestra en la figura 1, controlaremos la temperatura del depósito 1 mediante la sonda de temperatura DS18B20 que actuará de sensor y el calentador que hará las veces de actuador.
En la figura 2 se puede apreciar el diagrama de flujo del control ON/OFF con histéresis. Se trata de un control tradicional en lazo cerrado por lo que el actuador entra y sale repetidas veces hasta situarse en la temperatura de referencia.
Para minimizar el tiempo de funcionamiento del actuador y así alargar su vida útil, estableceremos una banda muerta o de tolerancia de modo que se amplíe la diferencia entre el tiempo de encendido y apagado (histéresis). El valor de la banda muerta dependerá de cada sistema y de su análisis.
PID
Para el control PID, tal y como muestra la figura 1, utilizaremos el sensor ultrasónico para medir el nivel y la bomba como actuador. El objetivo es tratar de controlar el nivel del depósito 1.
El diagrama de flujo de la planta es el siguiente:
Control por eventos
El control por eventos implica que es un evento el que hace que muestreamos una señal y no se haga periódicamente como es más habitual. Obviamente el tiempo no desaparece nunca del cálculo, ya que necesitamos tener una referencia a la hora de detectar eventos y muestrear señales.
Para ello generaremos una serie de funciones que nos ayudaran en que situación está nuestra planta y si debemos lanzar un evento o podemos esperar al siguiente.
Componentes utilizados
Arduino Uno R3
La placa elegida para el desarrollo del proyecto es Arduino Uno R3. Esta placa dispone de 13 entradas/salidas digitales, de las cuales 6 se pueden utilizar como PWM, y 5 entradas analógicas. Dispone de conectividad suficiente para pequeños proyectos aunque con ciertos componentes como el display LCD se hace necesario la utilización del bus I2C para economizar entradas y salidas.
Sensor HC-SR04
Como sensor de nivel utilizaremos un sensor ultrasónico de bajo costo. Además de su reducido precio, ofrece otras ventajas ya que no le afectan ni la luz solar ni materiales oscuros. Dispone de dos transductores, básicamente un micrófono y un altavoz que son los encargados de enviar y recibir la señal. El cálculo de la distancia se basa en la velocidad del sonido en el aire, 340 m/s a 20ºC, teniendo en cuenta que por cada grado centígrado de aumento la velocidad aumenta 0,6 m/s. Las características principales se describen a continuación:
- Pines utilizados: 2 (TRIGGER, ECHO) + 2 (alimentación).
- Rango de medición entre 20 y 4000mm (±3mm).
- Ángulo efectivo de medición: <15º.
- Consumo en reposo: <2mA
- Consumo en funcionamiento: 15mA.
Sonda DS18B20
Se trata de una sonda impermeable con una resolución configurable de entre 9 y 12 bits y un rango de medición de -55 a 125ºC (±0,5ºC). Funciona bajo la interfaz 1-Wire que requiere un solo hilo para su conexión, llevando cada sensor un numero identificativo de 64 bits grabado internamente para su distinción.
GDM2004D – Display 20×4
Con el fin de visualizar el estado de los diferentes sensores, utilizaremos un display de 20 caracteres por 4 columnas. Nativamente el display necesita la conexión de 6 pines digitales para su funcionamiento.
Con el fin de simplificar y economizar la conexión con Arduino, usaremos un adaptador I2C que nos reduce los pines necesarios a 2.
Micro bomba de agua
Utilizaremos una micro bomba (55x35mm) de 12V sin escobillas, sumergible (clase IP68) y capaz de proporcionar 240 L/H. El consumo nominal es de 400mA (5W). Debido a que trabaja a 12V y con el fin de poder regularla en un rango aceptable para nuestro proyecto, utilizaremos un transistor NPN para su conexión con arduino.
Calentador
Se trata de un calentador parcialmente sumergible con un rango de trabajo de 20 a 34ºC. Funciona a 220V por lo que su accionamiento desde arduino se hará a través de un relé.
Modelo matemático
A continuación se desarrollará un modelo matemático pragmático que nos servirá sobre todo para desarrollar el apartado de control por eventos.
- Datos
- a = 0,18m, b = 0,4m, c = 0,18m
- Qe = entre 3,05*10-5 m³/s y 4,71*10-5 m³/s
- Qs = 3,33×10-5m³/s
Conocidas las medidas del prisma rectangular obtenemos el volumen.
El volumen real lo podemos obtener en todo momento sabiendo la altura actual del nivel del depósito.
Conocido el volumen en cada instante podemos obtener el tiempo de llenado o vaciado. Para el tiempo de vaciado (tv) usamos el volumen calculado previamente. Para el tiempo de llenado (tl) necesitamos el volumen que nos queda por llenar.
Desarrollo del proyecto
Arduino proporciona un máximo de 40 mA por salida, debido a eso utilizaremos una fuente de alimentación externa de 5 y 12V para garantizar el buen funcionamiento de todos los equipos. En total usaremos entre entradas y salidas 13 pines, 5 analógicos y 8 digitales.
El panel de mando es físico como se puede apreciar en la figura 19. En la parte izquierda están situados los controles pertenecientes a la parte automática y a la derecha los manuales. En el display tenemos toda la información necesaria para el control PID y ON/OFF con histéresis. También se dispone de un modo manual para pruebas y ajustes.
La purga se realiza mediante una bomba estrangulada en la impulsión, debido a que por sus características no admite regulación mediante PWM. La bomba de purga se puede apreciar en la figura 20.
La bomba principal (bomba B1) se muestra en la figura 21. Esta bomba admite regulación PWM pero tiene un rango muerto entre 0 y 5,5V (0 – 117 PWM), algo que se tendrá en cuenta al ajustar los parámetros del PID y la purga.
Control ON/OFF con histéresis
Para este sistema desarrollaremos un control clásico de temperatura. Al tratarse de un calentador de 220V, se actuará mediante un relé, que para evitar que no esté entrando y saliendo de forma continua, se establecerá una banda muerta. Tras analizar el comportamiento del sistema, se establece una tolerancia de 0,5ºC como se puede observar en la figura 23.
El rango útil del calentador está entre 20 y 30ºC y leeremos la temperatura mediante un sensor de temperatura situado en el depósito 1. Para simplificar, el setpoint de la temperatura será un número entero.
Código Arduino resumido
Leemos el setpoint introducido mediante el potenciómetro y comprobamos que la diferencia con la temperatura real no es mayor o igual a medio grado centígrado. Con el fin de obtener una respuesta más rápida en la temperatura, y puesto que el calentador y la sonda se encuentran en depósitos separados, el nivel está variando entre 65 y 100mm de forma continua, de este modo se garantiza el movimiento de flujo de agua entre depósitos.
A continuación se muestra el código principal del control. Para ver el código completo saltar al apartado 7.
//SetPoint regulable Temperatura float sett=analogRead(A2); SPtemp=map(sett,0,1023,30,15); //Calculo el error ErrorTemp = SPtemp-temp; //Banda muerta de 0.5ºC if(ErrorTemp<=0.5){ digitalWrite(13, LOW); }else{ digitalWrite(13, HIGH); } … // Nivel entre 65 y 100mm de forma continua if (bypassPID==true){//No hago caso al setpoint if (mediamm>=100){ analogWrite(3, 0); } if (mediamm<=65){ analogWrite(3, 255); } }else{ analogWrite(3, control); } …
Muestreo de datos con Matlab
Aun favoreciendo el movimiento del agua entre depósitos, la respuesta de la temperatura es bastante lenta como muestra la figura 24.
Control PID
Lectura recomendada: Introducción al algoritmo PID y su implementación en Arduino
La implementación del PID se hará sobre un control de nivel simple. El nivel lo obtendremos en milímetros mediante un sensor de ultrasonidos y en función del setpoint de referencia actuaremos sobre una micro bomba de corriente contínua aprovechando una salida PWM de arduino. La microbomba es de 12V de modo que utilizaremos un transistor NPN para hacer funcionar a la bomba, ya que arduino no es capaz de aportar ese voltaje. En resumen, transmitiremos la señal PWM (0-5V) a la base del transistor y este dejerá pasar entre colector y emisor los 12V de forma proporcional a la señal transmitida por la base. El rango útil de trabajo de la bomba está entre 5,5 y 12V (117 – 255 PWM).
Código Arduino resumido
Solamente se muestra el código parcial del PID, para ver el código completo de arduino saltar al apartado 7.
El PID es un sistema de control robusto y eficiente, pero requiere de ciertas precauciones a la hora de implementarlo. A continuación se detallan las características principales incluidas.
- Tiempo de cálculo. El PID funciona mejor cuanto menos tiempo pase entre cálculos, por ello mismo se ha establecido el tiempo de cálculo en 10ms.
- Filtro: La variable más importante en el control, es el nivel, por esto mismo, se utiliza la media de las últimas cinco medidas a modo de filtro.
- Paso de automático a manual. Con el fin de economizar potencia de cálculo, cuando estemos en modo manual simplemente dejaremos de calcular los valores del PID.
- Anti Windup. Cuando se llega a saturar el actuador, la salida del controlador deja de corresponderse con la acción aplicada al proceso. Para ello, limitamos la acción integral entre un mínimo y un máximo con el fin de no llegar a la situación mencionada anteriormente.
… float Kp = 25; float Kd = 10; float Ki = 1; float I; // Valor Integral float D; // Valor Derivativo //AUTOMÁTICO---------------------------------------------------- if (modo==false){ //SetPoint regulable NIVEL float set=analogRead(A1); SetPoint=map(set,0,1023,120,50); // PID // Error----------------------------------------------------- ErrorAnt=Error; Error= SetPoint- mediamm; // Derivada-------------------------------------------------- D=Error-ErrorAnt; // Limito Integral (anti-windup) int ok = 3; //damos por bueno -> banda muerta de 3mm int nook = 10; //10mm de diferencia, dejo actuar if(abs(I)>ok && abs(I)<nook){ I=I+mediamm*Ki; }else { I=0; } // control=Kp*Error+Kd*D+I; //Límites del controlador if(control<=0){ control=0; } if(control>=255){ control=255; } if(control<=165 && control>=50){ control=175; } // Actuo sobre la bomba analogWrite(3, control); } …
Muestreo de datos con Matlab
Mediante un script en Matlab (ver apartado 10.2),recogeremos los datos que envía Arduino por el puerto serie (USB). El objetivo es recoger los datos suficientes para poder analizar posteriormente nuestro sistema y optimizar los datos de control.
Muestreamos el nivel, el setpoint del nivel y la salida pwm de la bomba. Las muestras se toman cada 100ms.
En la figura 26 vemos diferentes cambios de nivel sin optimizar los parámetros del PID.
En la figura 27 podemos ver los diferentes cambios de nivel que hemos muestreado. Partimos de 69mm y terminamos en 100mm. Vemos que la bomba se encuentra en el rango de acción especificado (165 – 255) y que la PV sigue al SP de forma correcta.
Ajuste manual del PID
Según la tabla 1 podemos sintonizar nuestro PID mediante prueba error. Nuestro sistema es sencillo, por lo que la sintonización no debería suponer mucho problema. Las pruebas se realizan en lazo cerrado empezando por un control proporcional (P) para pasar a un control PI y finalmente un PID.
Empezamos anulando los parámetros Ki y Kd y poniendo un valor de 100 en Kp para ver la respuesta que tiene el sistema. La respuesta es muy brusca por lo que vamos bajando sucesivamente el valor hasta que el valor que nos satisface es 25. A continuación hacemos lo propio con el parámetro Ki. En este caso llegamos a la conclusión de que necesitamos un valor bajo para que la respuesta sea adecuada, ya que valores superiores a 1 hacen que la respuesta sea demasiado anticipativa. Finalmente buscamos el valor para Kd. Los valores obtenidos manualmente se indican en la tabla 2.
Con estos parámetros obtenemos una respuesta adecuada. Se puede ver un ejemplo del funcionamiento del proceso en el siguiente video:
Ajuste del PID con Matlab
Como hemos visto anteriormente, el ajuste de las constantes del PID se realizó mediante prueba y error. Para optimizar estos valores, haremos uso de Matlab, de la herramienta de identificación de sistemas (System Identification Tool) y de la herramienta PID Tunning.
Lanzamos la herramienta de identificación de sistemas mediante el comando IDENT e importamos nuestros datos.
Los datos que vamos a importar están en el dominio del tiempo y las variables que nos interesan son la bomba (PWM) y el nivel (mm), el tiempo de muestreo (Sampling interval) es de 0.1 segundos.
A continuación procesamos el modelo y lo exportamos al espacio de trabajo como “P1”.
Una vez que tenemos nuestro modelo en el espacio de trabajo abrimos PID tunning e importamos el modelo. PID tunning nos brinda la oportunidad de ajustar los parámetros del control PID mediante visualización de la respuesta escalón del proceso analizado. Podemos ver la respuesta escalón y los parámetros obtenidos en las figuras 31 y 32.
Finalmente obtenemos los valores de las constantes para la respuesta elegida. El objetivo es minimizar el tiempo de respuesta y el sobreimpulso. Podemos ver una comparativa de los parámetros de control en la tabla 3.
Control por eventos
Para desarrollar el control por eventos, usaremos el modelo matemático desarrollado anteriormente. La idea es analizar tanto el control ON/OFF con histéresis como el PID y optimizar los consumos.
En el caso del control ON/OFF con histéresis, con los datos muestreados podemos afirmar que para subir un grado centígrado necesitamos aproximadamente 250 segundos. El sistema disipa temperatura aproximadamente a un ritmo de 1,5ºC/h por lo que el calentador actuará 3 veces por hora durante 750 segundos, para mantenerse en la temperatura de referencia. En este caso en concreto, la mejor opción para este sistema de control sería economizar el consumo durmiendo a Arduino y despertarle mediante interrupción. De este modo solamente despertaría cuando fuera necesario para activar el calentador. Si nuestro sistema de control contara con más relés, y estos se activaran de forma asíncrona, desarrollar un control por eventos si sería beneficioso, ya que la naturaleza del sistema nos impediría dormir el microcontrolador. En nuestro control de temperatura, no hay lugar a dudas, implementar el control por eventos es malgastar tiempo y recursos, ya que, por su simplicidad podemos economizar consumos al máximo durmiendo el microcontrolador. Tras el análisis del sistema y los consumos obtenidos, podemos afirmar que utilizando la estrategia de dormir el microcontrolador, estamos ahorrando 30mA con respecto al estado de reposo, esto es un 70% de consumo menos.
El control PID, al contrario que el caso anterior, es más efectivo cuanto menor sea el muestreo, pero una alta tasa de muestreo implica mayor comunicación con el actuador, mayor consumo y menor vida útil de los elementos que componen el sistema. Una de las maneras en que economiza consumos el control por eventos es minimizando la comunicación con el actuador. Para minimizar la acción del actuador necesitamos muestrear muy rápido y tener nuestro sistema modelado. Supongamos que inicialmente nuestro tiempo de muestreo era de 1 segundo, para implementar en control por eventos, establecemos el tiempo de muestreo en 100ms, de modo que ahora muestreamos 10 veces más que antes pero eso no significa que el actuador trabaje 10 veces más, sino que disponemos de más datos para decidir si debe actuar o no. Para que el control sea eficiente debemos además, de establecer ciertos límites como un tiempo máximo en el que el actuador llevará a cabo una acción obligada, y el error máximo que nos podemos permitir para que el sistema siga siendo estable y nos dé tiempo a actuar. Para establecer un tiempo máximo de actuación, deberemos analizar el sistema en detalle. En función del tiempo máximo, estableceremos un error máximo adecuado.
El caudal de entrada (Qe) lo hemos obtenido de forma experimental midiendo el tiempo que tarda en llenar un vaso de 250ml a diferentes voltajes.
A continuación, hallamos el tiempo de llenado o vaciado que usaremos como tiempo máximo que no debemos sobrepasar. Para ello usaremos las ecuaciones planteadas en el modelo matemático.
Teniendo en cuenta que:
- a = 0,18m, b = 0,4m, c = 0,18m
- Qe = entre 3,05×10-5 m³/s y 4,71×10-5 m³/s
- Qs = 3,33×10-5m³/s
Obtenida la tabla de tiempos, podemos establecer el tiempo máximo en 30 segundos sin perder la estabilidad del sistema.
Finalmente estableceremos un error máximo entre el nivel actual y el setpoint de 10mm, de este modo economizaremos actuaciones pero controlando el sistema en todo momento.
A continuación veamos el código PID original y el PID basado en eventos.
Tras el cálculo del error, establecemos una condición de actuación solamente si el error es mayor o igual a 10mm y si por el contrario llegamos a los 300 muestreos. Recordemos que el muestreo se realiza cada 100ms, por lo que 30000ms / 100ms = 300. Si cualquiera de las opciones se da, calculamos el control y reiniciamos el contador de muestreo.
Cada vez que no se ejecuta la acción de control, estamos ahorrando entre 290mA con la bomba principal parada, a 13 mA en régimen de control, dependiendo del consumo puntual de los elementos del sistema. También hay que tener en cuenta que cada vez que no ejecutamos una acción estamos preservando la vida útil de ciertos elementos del sistema, lo que supone un ahorro a largo plazo.
Código Arduino
//=============================================================== // Librerías //=============================================================== #include <OneWire.h> #include <DallasTemperature.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> //=============================================================== // Variables globales & Constantes //=============================================================== #define ONE_WIRE_BUS 2 // OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensores(&oneWire); LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);//Direccion de LCD // DeviceAddress S1 = {0x28, 0xFF, 0xAA, 0x58, 0x2D, 0x04, 0x00, 0xBD}; //Tª Tanque unsigned long time = 0; // tiempo de ejecucion del ultimo ciclo int period = 50; // Periodo de muestreo en ms int periodo = 1000; // Periodo de muestreo en ms boolean debug = false;//false=matlab boolean modo = false;//false=auto true=manual boolean bypassPID = false;//FALSE=PID normal TRUE=nivel 65mm-100mm String modos = ""; float temp; int Trig_pin, Echo_pin; //Trigger = pin 9 | ECHO = pin 8 int n=5; // valores para media int v[5]; unsigned long mm; int mediamm; unsigned long lastmm; int senal; // float Kp = 25;//19.45; float Kd = 0.1;//0.0058; float Ki = 0.3;//0.1; float I; // Valor Integral float D; // Valor Derivativo int Error; int ErrorAnt; float control; unsigned long SetPoint; int SPtemp; float ErrorTemp; //=============================================================== // SETUP //=============================================================== void setup(void) { Serial.begin(9600); //Abrimos la comunicación por serial sensores.begin(); //Iniciamos sensor Tª lcd.begin(20,4); //Indicamos tipo de LCD //lcd.backlight(); Ultrasonido(9,8); //Trigger = pin 9 | ECHO = pin mm = microsecondsToMeasure(duracion()); v[0] =mm; v[1] =mm; v[2] =mm; v[3] =mm; v[4] =mm; } //=============================================================== // BUCLE PRINCIPAL //=============================================================== void loop(void) { //Nivel mm = microsecondsToMeasure(duracion()); // Calculo la media del NIVEL for (int i=0; i<n-1; i++){ // Utilizamos array v[i] =v[i+1]; } v[n-1]= mm;//(mm - lastmm); mediamm=0; for (int i=0; i<n; i++){ // Calculamos la media mediamm = mediamm+ v[i]; } mediamm = mediamm/n; mediamm = 170-mediamm;//Nivel en parte superior //Temperatura sensores.setResolution(S1, 9);//0,5ºC resolución sensores.requestTemperatures(); Mostrar_Datos(S1); if (digitalRead(12) == HIGH){ modo=true; modos="MANUAL"; }else{ modo=false; modos="AUTOMATICO"; } //Purga //if (digitalRead(7) == HIGH && mediamm <=120){ // analogWrite(5, 255); //}else{ // analogWrite(5, 0); //} //MODO MANUAL------------------------------------------------- //------------------------------------------------------------- if (modo==true){ //Bomba en MANUAL int bomba=0; float niv=analogRead(A0); bomba=map(niv,0,1023,0,255); analogWrite(3, bomba); //Temperatura en MANUAL //Relé calentador if (digitalRead(7) == HIGH){ digitalWrite(13,HIGH); }else{ digitalWrite(13,LOW); } //LCD en MANUAL lcd.clear(); lcd.setCursor(0,0); lcd.print("====== "); lcd.print(modos); lcd.print(" ======"); // lcd.setCursor(0,1); lcd.print("Temperatura "); lcd.print(temp); lcd.print(" \337C"); // lcd.setCursor(0,2); lcd.print("Bomba "); lcd.print(bomba*0.0470588); lcd.print(" V"); // lcd.setCursor(0,3); lcd.print("Nivel "); lcd.print(mediamm); lcd.print(" mm"); // } //AUTOMÁTICO---------------------------------------------------- if (modo==false){ // MODO AUTOMÁTICO--------------------------------------------- //------------------------------------------------------------- //SetPoint regulable NIVEL float set=analogRead(A1); SetPoint=map(set,0,1023,50,120); //----------------------------- // PID // Error----------------------------------------------------- ErrorAnt=Error; Error=SetPoint-mediamm; //if(abs(Error)<=3){ //Banda muerta de 5mm // Error=0; //} // Derivada-------------------------------------------------- D=Error-ErrorAnt; // Limito Integral int ok = 3; //damos por bueno -> banda muerta de 5mm int nook = 10; //8mm de diferencia, dejo actuar if(abs(Error)>ok && abs(Error)<nook){ I=I+mediamm*Ki; } else { I=0; } // control=Kp*Error+Kd*D+I; if(control<=0){ control=0; senal=0; } if(control>=255){ control=255; senal=255; } if(control<=165 && control>=50){ senal=175; control=175; } else{ senal=control; } // Actuo sobre la bomba if (bypassPID==true){//No hago caso al setpoint if (mediamm>=100){ analogWrite(3, 0); } if (mediamm<=65){ analogWrite(3, 255); } }else{ analogWrite(3, control); } // //ON-OFF con histéresis------------------------ //SetPoint regulable Temperatura float sett=analogRead(A2); SPtemp=map(sett,0,1023,30,15); //Calculo el error ErrorTemp = SPtemp-temp; //Banda muerta de 0.5ºC if(ErrorTemp<=0.5){ digitalWrite(13, LOW); }else{ digitalWrite(13, HIGH); } //LCD en AUTO lcd.clear(); lcd.setCursor(0,0); lcd.print("==== "); lcd.print(modos); lcd.print(" ===="); // lcd.setCursor(0,1); lcd.print("Nivel "); lcd.print(mediamm); lcd.print(" mm"); lcd.print(" SP "); lcd.print(SetPoint); // lcd.setCursor(0,2); lcd.print("Bomba "); //control*=0,3921; lcd.print(control*0.3921); lcd.print(" % "); lcd.print(senal); // lcd.setCursor(0,3); lcd.print("Temp "); lcd.print(temp); lcd.print(" \337C"); lcd.print(" SP "); lcd.print(SPtemp); } //MATLAB------------------------------------------------- if(debug==false){ //(Nivel,SetPoint,control,temp,sptemp) //Serial.print(mediamm); //Serial.print(","); //Serial.print(SetPoint); //Serial.print(","); //Serial.print(control); //Serial.print(","); Serial.print(temp); Serial.print(","); Serial.print(SPtemp); Serial.print(","); //Serial.print(ErrorTemp); }else{ //TEMPERATURA //Serial.print("T "); //Serial.print(temp); //Serial.print(" SP "); //Serial.print(SPtemp); //Serial.print(" Error"); //Serial.print(ErrorTemp); //Serial.println(""); //NIVEL //Serial.print("Nivel: "); //Serial.print(mediamm); //Serial.print(" SP: "); //Serial.print(SetPoint); //Serial.print(" Error: "); //Serial.print(Error); //Serial.print(" ErrorAnt: "); //Serial.print(ErrorAnt); //Serial.print(" Control: "); //Serial.print(control); //Serial.print(" Kp*Error: "); //Serial.print(Kp*Error); //Serial.print(" D: "); //Serial.print(D); //Serial.print(" I: "); //Serial.print(I); //Serial.println(""); } delay (100);//Dura 0.1 segundos } //=============================================================== // Subroutinas //=============================================================== void Ultrasonido(int TP, int EP) { pinMode(TP,OUTPUT); pinMode(EP,INPUT); Trig_pin=TP; Echo_pin=EP; } unsigned long duracion(void) { digitalWrite(Trig_pin, LOW); delayMicroseconds(2); digitalWrite(Trig_pin, HIGH); delayMicroseconds(10); digitalWrite(Trig_pin, LOW); return pulseIn(Echo_pin,HIGH); } unsigned long microsecondsToMeasure(unsigned long microseconds) { //return microseconds * 0.017; //(cm) Distancia = 340m/s * 10^2cm/m * 1s/10^6us * tiempo_us return microseconds * 0.17; //(mm)Distancia = 340m/s * 10^3mm/m * * 1s/10^6us * tiempo_us } //Creamos una funcion para mostrar la direccion de los sensores void Mostrar_Direccion(DeviceAddress direccion) { for (uint8_t i = 0; i < 8; i++) { if (direccion[i] < 16) //Serial.print("0"); Serial.print(direccion[i], HEX); } } //Funcion que muestra la temperatura en grados centigrados del sensor void Mostrar_Temperatura(DeviceAddress direccion) { float tempC = sensores.getTempC(direccion); temp = tempC; } //Funcion que muestra la resolucion del sensor de dicha direccion. Las resoluciones posibles pueden ser: //Resolucion a 9 bits 0.50 ºC //Resolucion a 10 bits 0.25 ºC //Resolucion a 11 bits 0.125 ºC //Resolucion a 12 bits 0.0625 ºC void Mostrar_Resolucion(DeviceAddress direccion) { //Serial.print("Resolucion: "); //Serial.print(sensores.getResolution(direccion)); //Serial.println(); } //Funcion que muestra los datos del sensor de dicha direccion void Mostrar_Datos(DeviceAddress direccion) { //Serial.print("Direccion del dispositivo: "); //Mostrar_Direccion(direccion); //Serial.print(" "); Mostrar_Temperatura(direccion); }
Código Matlab lectura puerto serie del nivel
function Matlab_Arduino(muestras) close all; clc; %Variables global nivel; global nivelsp; global bomba; global tiempo; nivel=[]; nivelsp=[]; bomba=[]; delay=0.01; % delay de void loop en segundos tiempo=[]; %Inicializo el puerto serial delete(instrfind({'Port'},{'COM3'})); puerto_serial=serial('COM3'); puerto_serial.BaudRate=9600; warning('off','MATLAB:serial:fscanf:unsuccessfulRead'); %Abro el puerto serial fopen(puerto_serial); %Declaro un contador del número de muestras ya tomadas contador=1; %Creo una ventana para la gráfica %figure('Name','Comunicación serie') figure ax1 = subplot(2,1,1); % subplot superior ax2 = subplot(2,1,2); % subplot inferior grid off; hold on; %Bucle de toma de muestras while (contador<=muestras) %axis tight %d decimal numbers %e, %f, %g floating-point numbers puertoserie=fscanf(puerto_serial,'%f,%f,%f')'; nivel(contador)=puertoserie(1); nivelsp(contador)=puertoserie(2); bomba(contador)=puertoserie(3); plot(ax1,contador,nivel(contador),'X',contador,nivelsp(contador),'*'); ylim(ax1,[0 150]); xlim(ax1,[contador-5 contador+5]); title(ax1,'Nivel real (mm) y SetPoint (mm)') ylabel(ax1,'Niveles (mm)') xlabel(ax1,'Tiempo (s)') % plot(ax2,contador,bomba(contador),'X'); ylim(ax2,[0 260]); xlim(ax2,[contador-5 contador+5]); title(ax2,'PWM Bomba B1') ylabel(ax2,'PWM (0-255)') xlabel(ax2,'Tiempo (s)') drawnow contador=contador+1; pause(1); end %Auto-generamos la variable tiempo for i=1:length(nivel) tiempo(i)=i*delay; end %Guardo las variables en el espacio de trabajo assignin('base', 'Nivel', nivel); assignin('base', 'NivelSP', nivelsp); assignin('base', 'Bomba', bomba); assignin('base', 'tiempo', tiempo); putvar(nivel); putvar(nivelsp); putvar(bomba); putvar(tiempo); %Cierro y limpio fclose(puerto_serial); delete(puerto_serial); end
Código Matlab lectura puerto serie de la temperatura
function Datos_temperatura(muestras) close all; clc; %Variables global temp; global tempsp; global tiempo; temp=[]; tempsp=[]; delay=1; % delay de void loop en segundos tiempo=[]; %Inicializo el puerto serial delete(instrfind({'Port'},{'COM3'})); puerto_serial=serial('COM3'); puerto_serial.BaudRate=9600; warning('off','MATLAB:serial:fscanf:unsuccessfulRead'); %Abro el puerto serial fopen(puerto_serial); %Declaro un contador del número de muestras ya tomadas contador=1; %Creo una ventana para la gráfica %figure('Name','Comunicación serie') figure grid off; hold on; %Bucle de toma de muestras while (contador<=muestras) %for contador=1:length(muestras) %axis tight %d decimal numbers %e, %f, %g floating-point numbers puertoserie=fscanf(puerto_serial,'%f,%d'); temp(contador)=puertoserie(1); tempsp(contador)=puertoserie(2); plot(contador,temp(contador),'X',contador,tempsp(contador),'*'); ylim([10 30]); xlim([0 contador+2]); title('Temperatura (ºC) y SetPoint (ºC)') ylabel('Temperaturas (ºC)') xlabel('Tiempo (s)') drawnow contador=contador+1; pause(0.01); end %Auto-generamos la variable tiempo for i=1:length(Temperatura) tiempo(i)=i*delay; end %Guardo las variables en el espacio de trabajo assignin('base', 'Temperatura', temp); assignin('base', 'TemperaturaSP', tempsp); assignin('base', 'tiempo', tiempo); putvar(temp); putvar(tempsp); putvar(tiempo); %Cierro y limpio fclose(puerto_serial); delete(puerto_serial); end
Galería de imágenes
Bibliografía
[1] Arduino. Arduino board UNO. Especificaciones técnicas. [en línea]. Italia, 2016. Disponible en: https://goo.gl/31jbDl
[2] Farnell Components SL. BC549C. Transistor NPN. Especificaciones técnicas. [en línea]. España, 2016. Disponible en: http://goo.gl/hB8KzL
[3] Friends-of-Fritzing foundation. Fritzing. Software de diseño de esquemas electrónicos [en línea]. Alemania, 2016. Disponible en: http://goo.gl/3hta7i
[4] HC-SR04. Sensor ultrasonidos. Especificaciones técnicas. [en línea]. 2016. Disponible en: http://goo.gl/5jUiYM
[5] Karl-Erik Arzén. A simple event-based PID controller. [en línea]. 14th IFAC World Congress Beijing, China, 1999. Disponible en: http://goo.gl/y0zzGx
[6] Katsuhiko Ogata. Ingeniería de control moderna. 5ª edición. Editorial Pearson, 2010. ISBN: 978-84-8322-660-5
[7] Massimo Banzi, David Cuartielles, Tom Igoe, Gianluca Martino, and David Mellis. Arduino IDE. Software de programación de Arduino [en línea]. Italia, 2016. Disponible en: https://goo.gl/OibmBq
[8] Massimo Banzi, David Cuartielles, Tom Igoe, Gianluca Martino, and David Mellis. Arduino. Referencia de programación de Arduino [en línea]. Italia, 2016. Disponible en: https://goo.gl/PhfOHk
[9] MathWorks INC. Documentación técnica de Matlab [en línea]. España, 2016. Disponible en: http://goo.gl/CGNZyc
[10] Maxim integrated INC. DS18B20. Sonda de temperatura. Especificaciones técnicas. [en línea]. California, 2016. Disponible en: https://goo.gl/Mc9pjV
[11] S.Dormido. Educación en automática. Nuevos retos [en línea]. Madrid, 2016. Disponible en: http://goo.gl/1vj64j
[12] Xiamen Ocular Optics CO Ltd. GDM2004D. Display LCD. Especificaciones técnicas. [en línea]. China, 2016. Disponible en: https://goo.gl/4E87Ch
6 comentarios en «Estrategias de control con Arduino»
¿ que valor de resistencia usas con el transistor y modelo de transistor ?
Gracias de antemano
Creo que utilicé un transistor NPN 2N2222 con una resistencia de 220 ó 270 ohmios y un diodo 1N4001. Te recomiendo que eches un vistazo a este manual de Simon Monk: https://learn.adafruit.com/adafruit-arduino-lesson-13-dc-motors?view=all
Excelente trabajo. Felicidades!
¿La temperatura es constante? ¿Es modificada la temperatura del agua que entra por la bomba por medio de la resistencia?
En el sistema planteado, el calentador está regulado al máximo (34°C) y se activa y desactiva mediante un relé. De este modo se puede jugar con la temperatura encendiendo unos segundos o minutos para conseguir diferentes temperaturas. Eso sí, una vez caliente a cierta temperatura el agua, el enfriamiento es lento.
Excelente trabajo, muy bien explicado, estoy realizando un proyecto con control PID y la verdad este ejemplo me está orientando mucho.