Bienvenidos controleros y controleras a una nueva entrada en nuestro sitio web, donde hoy abordaremos cómo implementar un controlador PID en Arduino para gestionar el nivel de agua en un tanque esférico.
Si te interesa profundizar en microcontroladores, sensores, programación y teoría de instrumentación y control, no olvides al mayor canal de youtube en español en estos temas.
También te puedes suscribir a este sitio web si quieres estar al tanto de las nuevas publicaciones sobre estos temas de automatización y control:
Suscríbete a este sitio WEB para estar enterado de las nuevas entradas!
Video Tutorial de Control PID con Arduino en Tanque de Nivel
Fundamentos Previos
Antes de adentrarnos en el control PID para nuestro tanque esférico, te recomiendo revisar nuestros posts anteriores. El primero sobre el uso del sensor de presión diferencial MPX10DP, que es clave para medir el nivel del líquido en el tanque. El segundo trata sobre el sensor de caudal YF-S201, que, si bien no está directamente relacionado con el PID, sienta las bases del código que desarrollaremos. Además, no olvides repasar nuestro post sobre los fundamentos y la teoría del control PID con Arduino, donde hicimos un control de temperatura, pues esa teoría será aplicada en nuestro proyecto actual.
Sensor de Flujo YF-S201: Medición de Caudal con Arduino
Como Hacer un Control PID de Temperatura con Arduino
Medidor de Nivel de Agua por Presión con Arduino
Componentes Necesarios
Para este proyecto, necesitaremos los siguientes componentes:
- Arduino Mega (u otro modelo de Arduino)
- Sensor de presión MPX10DP
- Bomba de agua de corriente continua (CC)
- Driver de potencia BTS7960 (y se mencionará el L298)
- Tanque con geometría esférica
- Tubo para transportar el agua
Configuración del Hardware
En el proyecto anterior, conectamos el sensor MPX5010DP al pin A0 de Arduino y utilizamos dos bombas controladas por drivers de potencia, moduladas con la señal PWM de Arduino. Para nuestro proyecto actual, nos centraremos en la bomba conectada al driver BTS7960 en los pines 4 y 6 de Arduino.
Implementación del Control PID en Tanque con Arduino
En la implementación del control PID para gestionar el nivel de agua en un tanque esférico, nos enfrentamos a un desafío de control de procesos donde se busca la estabilidad y precisión en la respuesta del sistema. El objetivo primordial es mantener el nivel de agua dentro del tanque en un punto establecido, lo cual requiere un ajuste dinámico y continuo de la velocidad de la bomba de agua.
Principios del Control PID
El controlador PID (Proporcional-Integral-Derivativo) implementado en Arduino se basa en tres términos que ajustan la salida (en este caso, la velocidad de la bomba) basándose en el error, que es la diferencia entre el nivel deseado de agua (setpoint) y el nivel actual (medido por el sensor de presión diferencial):
- Proporcional (P): Este término calcula un ajuste proporcional al error actual. Si el error es grande, el ajuste será grande, y viceversa. Esto permite una respuesta rápida a los cambios en el nivel del agua, pero solo con P, el sistema puede sobrepasar el setpoint o mantener un error constante (offset).
- Integral (I): Acumula el error pasado y lo integra en el tiempo, permitiendo al controlador corregir desviaciones sostenidas. Ayuda a eliminar el offset producido por el término proporcional, asegurando que el nivel del agua alcance y se mantenga en el setpoint a largo plazo.
- Derivativo (D): Este término predice los cambios futuros en el error basándose en su tasa de cambio actual. Ayuda a amortiguar la respuesta del sistema, reduciendo el sobrepaso (overshoot) y la oscilación producida por los términos P e I.
Aplicación en el Tanque Esférico
Para implementar el control PID en nuestro tanque esférico, se realiza lo siguiente:
- Medición: Utilizamos el sensor de presión diferencial (MPX10DP) para medir continuamente el nivel de agua en el tanque. Esta señal se convierte en una lectura que el Arduino puede interpretar como el nivel actual de agua.
- Cálculo de Error: El Arduino calcula la diferencia entre el nivel deseado de agua (setpoint) y el nivel actual medido.
- Aplicación de PID: El algoritmo PID procesa el error y determina la salida adecuada para la bomba. Esto implica ajustar la señal PWM enviada al driver de potencia (BTS7960), controlando así la velocidad de la bomba para agregar o reducir agua en el tanque.
- Ajuste Continuo: El controlador realiza ajustes continuos en la velocidad de la bomba para mantener el nivel de agua dentro del rango deseado, respondiendo tanto a perturbaciones (como un cambio en la demanda de agua) como a variaciones constantes en el nivel.
Consideraciones de Diseño
- Dinámica del Tanque: Dado que el tanque es esférico, la relación entre el nivel del agua y el volumen no es lineal, lo que debe considerarse en la calibración del controlador PID.
- Parámetros PID: La sintonización de los parámetros PID (Kp, Ki, Kd) es crucial para lograr una respuesta óptima del sistema. Requiere un proceso iterativo de prueba y ajuste para encontrar la configuración ideal que minimice el error y estabilice el nivel de agua rápidamente sin causar oscilaciones excesivas.
En este caso fue empleado la técnica de Ziegler Nichols para encontrar estos parámetros:
Código Fuente
El código de Arduino para implementar el PID será detallado, explicando cómo se utiliza la lectura del sensor de presión para calcular la variable de proceso y cómo se ajusta la velocidad de la bomba según los parámetros PID.
//***************************************************// //***************************************************// //***** PID Tanque Esferico *****// //***** *****// //***** by: Sergio Andres Castaño Giraldo *****// //***** https://controlautomaticoeducacion.com/ *****// //***** *****// //***************************************************// //***************************************************// #include <TimerOne.h> // BTS7960 - 1 (Bomba 1) byte RPWM1 = 4; byte LPWM1 = 6; // l298 - 1 (Bomba 3) byte IN3_L1 = 24; byte IN4_L1 = 25; byte BEN_L1 = 5; // Sensor YF-S201 byte flowSensor1Pin = 2; volatile unsigned long pulseCount1 = 0; float flowRate1 = 0.0; unsigned long oldTime = 0; // Sensor MPX5010DP (Presión Diferencial) byte MPX1 = A0; //Estructura da Bomba L298 typedef struct{ byte enPin1; byte enPin2; byte pwmPin; }BombaL; //Estructura da Bomba BTS7960 typedef struct{ byte pwmA; byte pwmB; }BombaBTS; //Crear las bombas con las estructuras const BombaBTS bomba1 = {RPWM1, LPWM1}; const BombaL bomba3 = {IN3_L1, IN4_L1, BEN_L1}; float inputValue = 0; int velocity = 0; float tankLevel = 0; String inputString = ""; // Una cadena para mantener los datos recibidos boolean stringComplete = false; // Ya se recibió toda la cadena? float r1=0.0; //Referencia del Heater 1 volatile float u=0.0,u_1=0.0; //Acción de Control byte Ts = 1; //Periodo de muestreo //Parámetros del PID float kp,ti,td; float q0,q1,q2; volatile float e=0.0,e_1=0.0,e_2=0.0; float k=0.14,tau=35,theta=4+Ts/2; //Parámetros del Modelo del sistema void setup() { Serial.begin(9600); //Comunicación Serial inputString.reserve(200); // Reserva espacio para la cadena pinMode(flowSensor1Pin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(flowSensor1Pin), flowSensor1Interrupt, RISING); //Valor máximo del Timer es 8.3 Segundos Timer1.initialize(1000000); //Configura el TIMER en 8 Segundos Timer1.attachInterrupt(SampleTime) ; //Configura la interrupción del Timer 1 //*************************************************************************// //***************** SINTONIA POR ZIEGLER y NICHOLS *******************// //*************************************************************************// kp=(0.9*tau)/(k*theta); ti=3.33*theta; td=0.0*theta; //*************************************************************************// //***************** PID DIGITAL *******************// //*************************************************************************// // Calculo do controle PID digital q0=kp*(1+Ts/(2.0*ti)+td/Ts); q1=-kp*(1-Ts/(2.0*ti)+(2.0*td)/Ts); q2=(kp*td)/Ts; } void loop() { /* // Ativar bomba3 (BombaL) digitalWrite(bomba3.enPin1, HIGH); digitalWrite(bomba3.enPin2, LOW); analogWrite(bomba3.pwmPin, velocity); */ //Leitura da altura via pressão tankLevel = readPressureLevel(MPX1, 5.0, 0.03); if(tankLevel < 0){tankLevel = 0;} //Serial.println("Setpoint (cm),Nivel (cm),Bomba (%)"); Serial.print(r1); Serial.print(","); Serial.print(tankLevel); Serial.print(","); Serial.print(0); Serial.println(); //Calcular flujo cada 1segundo if ((millis() - oldTime) > 1000) { detachInterrupt(digitalPinToInterrupt(flowSensor1Pin)); flowRate1 = (pulseCount1 * 60.0) / 7.5; // L/min (YF-S201: 1 pulso = 2.25 mL) // Reiniciar conteo de pulsos y tiempo pulseCount1 = 0; oldTime = millis(); attachInterrupt(digitalPinToInterrupt(flowSensor1Pin), flowSensor1Interrupt, RISING); } delay(1000); } // Esta función se llama cada vez que el puerto serie recibe un byte: void serialEvent() { while (Serial.available()) { char inChar = (char)Serial.read(); // Obtiene el nuevo byte inputString += inChar; // Añade el byte a la cadena if (inChar == '\n') { // Si es un byte de nueva línea, la entrada está completa inputValue = inputString.toInt(); // Convierte la cadena a int // Si inputValue está en el rango de 0 a 100 if(inputValue > 100){inputValue = 100;} if(inputValue < 0){inputValue = 0;} inputString = ""; // Limpia la cadena r1 = inputValue; } } } //Función de la interrupción void flowSensor1Interrupt() { pulseCount1++; } //Función del Periodo de Muestreo (Timer 1) void SampleTime(void) { PID(); } //Lectura sensor de presion double readPressureLevel(byte mpxPin, float Vs, float tolP) { // mpcPin = Pino do sensor // Vs = 5.0 Voltagem de alimentação (pode botar o valor exato para maior presição) // tolP = Tolerancia do sensor, usado para ajustar a medida double Vout, P, aux = 0; const int numSamples = 10; double rho = 997; double g=9.8; // Ler voltagem do sensor (filtro de meia móvel) for (int i = 0; i < numSamples; i++) { aux += (double(analogRead(mpxPin)) * 5.0 / 1023.0); delay(5); } Vout = aux / numSamples; // Calcular a pressão em kPa segundo equação do DataSheet (Gráfico) P = (Vout - 0.04 * Vs) / (0.09 * Vs) + tolP; // Calcular o nível do tanque em cm double Level = ((P * 1000) / (rho * g)) * 100; return Level; } /*===========================================================================*/ /*======================= FUNCION DEL CONTROL PID =======================*/ /*===========================================================================*/ void PID(void) { e=(r1-tankLevel); // Controle PID u = u_1 + q0*e + q1*e_1 + q2*e_2; //Ley del controlador PID discreto if (u >= 100.0) //Saturo la accion de control 'uT' en un tope maximo y minimo u = 100.0; if (u <= 0.0 || r1==0 || tankLevel >=17) u = 0.0; //u = r1; //Retorno a los valores reales e_2=e_1; e_1=e; u_1=u; //La accion calculada la transformo en PWM //Transforma el valor de porcentaje a velocidad if(r1 == 0){ velocity = 0; } else { velocity = map(u, 0,100,150,255); } // Ativar bomba1 (BombaBTS) digitalWrite(bomba1.pwmA, LOW); analogWrite(bomba1.pwmB, velocity); }
Curso Premium de Control con Arduino
Para quienes deseen profundizar en sistemas de control implementados en Arduino, invito a explorar mis cursos premium. En ellos, enseño desde la construcción económica de plantas didácticas hasta la implementación y sintonía de diversas estrategias de controladores PID, incluyendo controladores en cascada, control RST, control por variables de estado y controlador Fuzzy Mandani.
- 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.
Eso es todo por la entrada del dia de hoy, espero les haya gustado y hayan aprendido algo nuevo. Si te ha servido el contenido de esta entrada, de los videos y los códigos de implementación y deseas apoyar mi trabajo invitandome a un café super barato, puedes hacerlo en el siguiente link:
👉 Invitar a Sergio a un Café ☕️
Que esten muy bien, nos vemos en la siguiente entrada.
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.