En automatización y control industrial, el controlador PID se ha convertido en uno de los algoritmos de control más confiables que se pueden implementar para estabilizar la respuesta de salida de cualquier sistema. PID significa Proporcional-Integral-Derivado. Estos tres tipos de mecanismos de control están tan combinados que producen una señal de error, y esta señal de error se utiliza como retroalimentación para controlar la aplicación final. Los controladores PID se pueden encontrar en una amplia gama de aplicaciones industriales y comerciales, como se usa para regular la presión, el movimiento lineal y muchas otras variables. Un controlador de temperatura PIDes la aplicación más común que puede encontrar en Internet. Sin un controlador PID, realizar el trabajo manualmente puede ser un proceso tedioso. Y en esta era de microcontroladores y electrónica digital avanzada, se hizo más fácil diseñar e implementar un controlador PID en cualquier sistema.

¿Qué es un controlador PID y cómo funciona?

Como le dijimos en la sección de introducción,  PID es un acrónimo de proporcional , integral y derivativo . Pero, ¿qué significa eso y hay alguna manera más fácil de entenderlo? Sí hay. Para eso, tomemos un ejemplo de Robot de limpieza al vacío inteligente de  bricolaje usando Arduino que hicimos en uno de nuestros proyectos anteriores. Para mí, es un proyecto muy bueno y es muy simple en términos de circuitos y mecanismo de control. Pero su mayor inconveniente es que no cuenta con ningún mecanismo de control basado en PID. Ahora, supongamos que el robot se está limpiando solo y se acerca a una escalera, tiene un sensor de proximidad. debajo del robot que detecta tal situación y corta la energía al motor, pero debido a la inercia, el robot no se detiene inmediatamente. Si eso sucede, es muy probable que el robot se tropiece por las escaleras. Ahora, imagina que tienes un robocar y quieres detenerlo en una posición determinada, esto puede ser muy difícil sin PID porque si simplemente cortas la energía, el auto fallará absolutamente su objetivo debido a su impulso. La siguiente imagen le dará una mejor idea del proceso.

Ahora que conocemos el concepto, podemos avanzar y comprender algunas de las partes avanzadas. Si busca en línea un controlador PID, el primer resultado que obtendrá es del controlador PID, y en esta publicación, encontrará un diagrama de bloques junto con una ecuación. Pero, ¿Qué significa esta ecuación y cómo la implementamos en nuestro microcontrolador? Buena pregunta, ahora síguela y entenderá cómo,

Este controlador recibe el nombre de cómo se trata un error, antes de sumarlo y luego enviarlo a la planta / proceso. ¡Dejame explicar! En el diagrama de bloques, puede ver que en la ruta proporcional, el error se multiplica por una constante Kp . En la ruta integral, el error se multiplica por la constante Ki, luego se integra , y en la ruta derivada, se multiplica por Kd, y luego diferenciado. Después de eso, los tres valores se suman para producir la salida. Ahora, en el controlador, los parámetros Kp, Kd y Ki se denominan ganancia. Y se ajustan o sintonizan para cumplir con un cierto conjunto de requisitos y, al cambiar estos valores, puede ajustar la sensibilidad de su sistema a cada uno de estos diferentes parámetros, ya sea los parámetros P, I o D. Permítanme explicarlo examinando individualmente cada parámetro.

El P-Controller:

Digamos que el error en el sistema está cambiando con el tiempo como puede observar en la línea roja. En un controlador proporcional, la salida es el error definido por la ganancia Kp. Como puede ver, cuando el error es grande, la salida producirá una salida grande, y cuando el error es cero, el error de salida es cero y cuando el error es negativo, la salida es negativa.

El I-Controller:

En un controlador integral, a medida que el valor del error cambia con el tiempo, la integral comenzará a sumar los inicios del error y lo multiplicará con Ki constante. En este tipo de controlador, es fácil ver que el resultado integral es el área debajo de la curva donde el área que se muestra en azul es el área positiva y el área que se muestra en amarillo es el área negativa. En un sistema complicado, el controlador integral se utiliza para eliminar errores constantes en un sistema de control. No importa cuán pequeño sea el error constante, eventualmente, la suma de los errores será suficiente para ajustar la salida del controlador. En la imagen de arriba, el error se representa con la línea verde.

El controlador D: 

En un controlador derivado , es la tasa de cambio del error lo que contribuye a la señal de salida. Cuando el cambio en el error se mueve relativamente lento, como ejemplo podemos usar la posición inicial de la onda sinusoidal. La salida derivada será pequeña como puede ver en la imagen de arriba (representada por la línea verde). Y más rápido cambia el error, mayor se vuelve la salida.

Ahora, en este punto, puede resumir las tres salidas y tiene el controlador PID. Pero a menudo no es necesario que los tres controladores trabajen juntos, en su lugar, podríamos eliminar a cualquiera estableciendo el punto de ajuste en cero. Por ejemplo, podemos tener un controlador PI configurando el valor D en cero, de lo contrario, podemos tener un controlador PD configurando el parámetro I en cero. Ahora que tenemos una idea clara, podemos pasar al ejemplo de hardware real.

¿Qué es un motor codificador y cómo funciona?

El concepto de un motor codificador es muy simple: es un motor de CC con escobillas que tiene un codificador adjunto. En un artículo anterior, hemos hablado todo sobre los codificadores rotativos en detalle, puede consultarlo si desea saber más sobre el tema.

En un motor codificador, se monta un codificador rotatorio en un motor de CC que proporciona retroalimentación al sistema mediante el seguimiento de la velocidad o la posición del eje del motor. Hay muchos tipos diferentes de motores disponibles y todos pueden tener diferentes tipos de configuración de codificador, como incremental o absoluto, óptico, de eje hueco, magnético y la lista sigue y sigue. Se fabrican diferentes tipos de motores para diferentes tipos de aplicaciones. No solo los motores de CC, sino también muchos servomotores, motores paso a paso y motores de CA vienen con un codificador incorporado. En la imagen de arriba, puede ver un motor de codificación de tipo imán permanente N20 , que reduce las RPM de salida a 15 con la ayuda de una caja de cambios adjunta. También puedes ver dos sensores de pasillo. adjunta a la PCB. Estos sensores hall captan la dirección en la que gira el motor y, con la ayuda de un microcontrolador, podemos leerlo con mucha facilidad.

Componentes necesarios para construir un controlador de motor de codificador habilitado para PID

En este punto, tenemos una buena idea sobre el funcionamiento de un controlador PID y también conocemos nuestro objetivo final. En base a eso, hemos decidido usar un Arduino y algunos otros componentes complementarios para construir un circuito. A continuación se muestra una lista de esos componentes complementarios.

  • Arduino Nano – 1
  • Motor de codificación N20 – 1
  • BD139 – 2
  • BD140 – 2
  • BC548 – 2
  • Resistencia 100R – 2
  • Resistencia 4.7K – 2
  • Tablero de circuitos
  • Cables de puente
  • Fuente de alimentación

Diagrama esquemático para probar el controlador de motor de codificador habilitado para PID

A continuación se muestra el diagrama esquemático completo para el controlador de motor de codificador habilitado para PID . El principio de funcionamiento de este circuito es muy simple y se describe a continuación.

El circuito es muy sencillo. Primero, en el esquema, tenemos el motor del codificador N20 que tiene seis pines, los pines están etiquetados como M1, M2 que se utilizan para alimentar el motor, ya que este es un motor muy pequeño con una potencia nominal de 3,3 V. A continuación, tenemos los pines VCC y GND que se utilizan para alimentar los circuitos del codificador. Para alimentar el circuito del codificador, debe darle + 5V; de lo contrario, el circuito del codificador no funcionará correctamente. A continuación, tenemos el PIN_A y PIN_Bdel motor. Estos dos pines están conectados directamente al codificador. Al leer el estado de estos pines, podemos medir fácilmente las RPM, este motor N20 de 15RPM tiene una relación de transmisión de 1: 2098, lo que significa que el eje del motor principal debe girar 2098 veces para que el eje auxiliar gire una vez. El PIN_A y PIN_B están conectados al Pin 9 y el pin 10 del Pin 9 y 10 de Arduino son ambos pines con capacidad PWM; los pines seleccionados deben tener funcionalidad PWM; de lo contrario, el código no funcionará. El controlador PID controla el motor controlando el PWM.

A continuación, tenemos nuestro controlador de motor de puente H, el controlador del motor está hecho de tal manera que podemos controlar el motor usando solo dos pines del Arduino e incluso evita que el motor se active en falso.

Código Arduino para controlador de motor codificador habilitado para PID

El código completo utilizado en este proyecto se puede encontrar al final de esta página. Después de agregar los archivos de encabezado y los archivos de origen necesarios, debería poder compilar directamente el código Arduino sin ningún error. Puede descargar la biblioteca del controlador PID desde el enlace que se proporciona a continuación o, de lo contrario, puede usar el método del administrador de la placa para instalar la biblioteca.

La explicación del código en ino. El archivo  es el siguiente. Primero, comenzamos por incluir todas las bibliotecas requeridas. En este programa, solo estamos usando la biblioteca del controlador PID,  por lo que debemos incluirla primero. Después de eso, definimos todos los pines necesarios que se requieren para leer el codificador y accionar el motor. Una vez hecho esto, definimos todos los valores para Kp, Ki y Kd.

#include <PIDController.h>
/ * Los pines ENCODER_A y ENCODER_B se utilizan para leer el codificador 
 * Datos del microcontrolador, los datos del codificador
 * viene muy rápido, por lo que estos dos pines deben estar habilitados para interrupciones 
 * alfileres
* /
#define ENCODER_A 2 
#define ENCODER_B 3
/ * los pines MOTOR_CW y MOTOR_CCW se utilizan para impulsar el puente H
 * el puente en H impulsa los motores, estos dos pines deben tener 
 * estar habilitado para PWM; de lo contrario, el código no funcionará.
* /
#define MOTOR_CW 9
#define MOTOR_CCW 10

A continuación, hemos definido los valores __Kp , __Ki y __Kd para nuestro código. Estas tres constantes son responsables de configurar la respuesta de salida para nuestro código. En este punto, tenga en cuenta que para este proyecto, he utilizado el método de prueba y error para establecer las constantes, pero existen otros métodos que hacen el trabajo muy bien.

/ * En esta sección hemos definido los valores de ganancia para el 
 * controlador proporcional, integral y derivado que he configurado
 * los valores de ganancia con la ayuda de métodos de prueba y error.
* / 
#define __Kp 260 // Constante proporcional
#define __Ki 2.7 // Constante integral
#define __Kd 2000 // Constante derivada

A continuación, hemos definido todas las variables necesarias que se requieren en este código. Primero, tenemos la variable encoder_count que se usa para contar el número de interrupciones que se generan; por tanto, cuenta el número de vueltas. A continuación, hemos definido un valor entero de variable de tipo int sin signo que almacena el valor que hemos puesto en el monitor serial. A continuación, hemos definido una variable de tipo char entranteByte que almacena temporalmente los datos seriales entrantes. A continuación, hemos definido la variable más importante en este código, y es la variable motor_pwm_value después de que los datos se calculan a través del algoritmo PWM y se almacenan en esta variable. Cuando se definen estas variables, hacemos una instancia para elControlador PID. Una vez que hagamos esto, podemos pasar a nuestra función setup () .

volatile long int encoder_count = 0; // almacena el recuento actual del codificador
unsigned int integerValue = 0; // almacena el valor de serie entrante. El valor máximo es 65535
char entranteByte; // analiza y almacena cada carácter uno por uno
int motor_pwm_value = 255; // después de que los datos de cálculo de PID se almacenan en esta variable.
PIDController pid_controller;

En la función de configuración , hemos asignado los pines ENCODER_A y ENCODER_B como entrada y hemos definido los pines MOTOR_CW y MOTOR_CCW como salida. A continuación, hemos asignado el ENCODER_A como una interrupción, y en el borde RISING, este llamará a la función encoder (); Las siguientes tres líneas son una vez más las más importantes, ya que hemos habilitado el controlador PID con el método begin () y también hemos ajustado el controlador con los valores Kp, Ki y Kd. Y finalmente, hemos establecido el límite para la salida de nuestro controlador PID.

configuración vacía () {
  Serial.begin (115200); // Serie para depuración
  pinMode (ENCODER_A, ENTRADA); // ENCODER_A como entrada
  pinMode (ENCODER_B, ENTRADA); // ENCODER_B como entrada
  pinMode (MOTOR_CW, SALIDA); // MOTOR_CW como salida
  pinMode (MOTOR_CCW, SALIDA); // MOTOR_CW como salida
/ * adjunta una interrupción al pin ENCODER_A del Arduino, y cuando el pulso está en el borde RISING llama a la función encoder ().
* /
  attachInterrupt (digitalPinToInterrupt (ENCODER_A), codificador, RISING);
  pidcontroller.begin (); // inicializar la instancia PID
  pidcontroller.tune (__ Kp, __Ki, __Kd); // Ajusta el PID, argumentos: kP, kI, kD
  pidcontroller.limit (-255, 255); // ¡Limite la salida PID entre -255 y 255, esto es importante para deshacerse de la cuerda integral!
}

A continuación, tenemos nuestra sección loop () . En la sección de bucle, primero verificamos si el serial está disponible o no. Si la serie está disponible, analizamos el valor entero y lo guardamos en la variable de valor entero . Luego, tenemos un carácter ‘ / n’ que está entrando. Lo colocamos en la variable entranteByte y verificamos esta variable con una declaración if, si es verdadera, continuamos con el ciclo, luego establecemos el punto de destino con el controlador pid .setpoint (integerValue); y pasar el valor entero que acabamos de recibir de serial. A continuación, imprimimos el valor recibido para la depuración.

Tenemos la variable motor_pwm_value y calculamos los valores PID y los colocamos en esta variable. Si este valor es mayor que cero, llamamos a la función  motor_ccw (motor_pwm_value) y pasamos el valor; de lo contrario, llamamos a la función motor_cw (abs (motor_pwm_value)) . Esto marca el final de nuestra sección de bucle.

bucle vacío () {
  while (Serial.available ()> 0) {
    integerValue = Serial.parseInt (); // almacena el valor integerValue
    entranteByte = Serial.read (); // almacena el carácter / n
    pidcontroller.setpoint (integerValue); // El "objetivo" que el controlador PID intenta "alcanzar",
    Serial.println (integerValue); // imprime el valor entrante para depurar
    if (entranteByte == '\ n') // si recibimos un carácter de nueva línea continuaremos en el bucle
      Seguir;
  }
  motor_pwm_value = pidcontroller.compute (encoder_count); // Deje que el PID calcule el valor, devuelve la salida óptima calculada
  Serial.print (motor_pwm_value); // imprime el valor calculado para depurar
  Serial.print ("");
  if (motor_pwm_value> 0) // si el motor_pwm_value es mayor que cero giramos el motor en el sentido de las agujas del reloj
    MotorCounterClockwise (motor_pwm_value);
  else // else, lo movemos en sentido antihorario
    MotorClockwise (abs (motor_pwm_value));
  Serial.println (encoder_count); // imprime el recuento final del codificador.
}

A continuación, tenemos la función de codificador. Esta función se llama cuando se producen interrupciones de flanco ascendente tan en el ENCODER_B. Cuando es verdadero, verificamos la declaración nuevamente con if (digitalRead (ENCODER_B) == HIGH). Una vez verdadera, la variable del contador se incrementa. De lo contrario, se reduce.

codificador vacío () {
  if (digitalRead (ENCODER_B) == HIGH) // si ENCODER_B es alto aumentar el recuento
    Encoder_count ++; // incrementa el recuento
  else // si no disminuye el recuento
    Encoder_count--; // decrementa el recuento
}

A continuación, tenemos la función que hace girar el motor en el sentido de las agujas del reloj. Cuando se llama a esta función, comprueba si el valor es mayor que 100 o no. Si es así, giramos el motor en el sentido de las agujas del reloj; de lo contrario, paramos el motor.

void motor_cw (int power) {
  if (potencia> 100) {
    analogWrite (MOTOR_CW, potencia);
    digitalWrite (MOTOR_CCW, BAJO);
  }
// ambos pines están configurados en bajo
  demás {
    digitalWrite (MOTOR_CW, BAJO);
    digitalWrite (MOTOR_CCW, BAJO);
  }
}

Lo mismo ocurre con la función que hace girar el motor en sentido antihorario. Cuando se llama a esta función, verificamos el valor y giramos el motor en sentido antihorario.

void motor_ccw (potencia int) {
  if (potencia> 100) {
    analogWrite (MOTOR_CCW, potencia);
    digitalWrite (MOTOR_CW, BAJO);
  }
  demás {
    digitalWrite (MOTOR_CW, BAJO);
    digitalWrite (MOTOR_CCW, BAJO);
  }
}

Prueba del controlador de motor habilitado para PID

La siguiente configuración se utiliza para probar el circuito. Como puede ver, he usado una caja eléctrica con un poco de cinta de doble cara para mantener el motor en su lugar, y he usado un módulo convertidor reductor pequeño para alimentar el motor cuando el motor funciona con 3.3V.

Una es que también se puede ver, hemos conectado un cable USB con el Arduino que se utiliza para establecer el punto de ajuste del controlador PID. También obtenemos la información de depuración del Arduino con el USB. En este caso, da el recuento actual del codificador. La siguiente imagen le dará una mejor idea del proceso.

https://circuitdigest.com/