Control PID para Arduino mejorado.
Un controlador PID es un mecanismo de control por realimentación ampliamente usado en sistemas de control industrial. Este calcula la desviación o error entre un valor medido y un valor deseado. (Wikipedia.)
Todo lo que se expone en esta página sólo es aplicable a encoders incrementales.
Antes de comenzar pongo un vídeo. Se trata de "arrastrar" un peso de 500gr; son tres imanes de magnetrones de microondas domésticos. En el vídeo no llega a los 500 porque para que se pudiera ver los dígitos de la báscula, tuve que inclinarla y eso hace perder un poco de peso (en apariencia). Ten en cuenta que el motor mueve directamente el carro, no hay engranajes ni reducciones para aumentar el par. El vídeo demuestra que a pesar de llevar una carga pesada y la inercia que provoca, el controlador PID sigue siendo igual de preciso. He utilizado las mismas constantes PID en los tres vídeos de esta página. Verás lucir unos leds; los extremos se encienden cuando se activa el PWM para mover el motor en un sentido u otro, y los dos del medio lucen cuando ha llegado a la posición designada. Al final de esta página pongo dos vídeos más.
En esta página presento un control PID para posicionar un motor y está basado en el algoritmo publicado por Brett Beauregard. Modifiqué el algoritmo original para "suavizar" la respuesta del motor, dándole unas características únicas, entre ellas un posicionamiento al punto designado mucho más suave y progresivo, y permite "sintonizar" las constantes PID de un modo más intuitivo y simple.
Es un Arduino Nano, pero es 100% compatible con Arduino UNO.
La diferencia entre el controlador PID con librería y el que presento en esta página es que he puesto el algoritmo dentro del mismo código para que puedas ver dónde hago las modificaciones para mejorar el comportamiento del motor.
El problema:
En un control PID de posición convencional la parte más difícil de sintonizar es el control integral. Sucede que el control integral se suma al control proporcional y como el control integral acumula las desviaciones anteriores [ITerm += (error * ki)], para cuando llega a la meta designada no le da tiempo a minimizar ese error (o desviación) y el motor tiene tendencia a ir un poco más allá del punto designado para luego corregirse. Normalmente lo que haríamos sería bajar la constante KI, o bien subir el valor de KD (o ambas cosas) para contrarrestar esa inercia, pero en muchos casos se hace muy difícil porque al aumentar/disminuir una de las constantes se suele necesitar reajustar las demás constantes.
La solución:
Se trata de que el control integral "ITerm" sólo sume el error proporcional por la constante integral en el caso de que no exista diferencia entre dos tiempos del valor de la entrada del encoder. Y en el caso contrario, si existe diferencia de contaje entre dos tiempos, lo que hace es sumar ese valor de diferencia (dInput) multiplicada por la constante Ki.
Lo lógico sería poner después del "else" ITerm -= (error * ki) y lo cierto es que da buen resultado, sin embargo ITerm -= (dInput * ki ) da mucho mejor resultado. Cuando el motor se acerca a la meta, la condición entre: ITerm += (error * ki) e ITerm -= (dInput * ki) se combinan muy rápidamente y provoca que el motor se acerque a la meta de una forma más suave y progresiva, eliminando la oscilación del control integral.
Existen otras modificaciones menores dentro del algortimo PID, como por ejemplo, multiplico la contante KD antes de pasar al filtro del control integral (así estará amplificada), y a su vez, el control integral le multiplico la constante KI antes de pasar al Output.
Comunicación a través del terminal serie.
Cuando estés en el terminal serie no importará si la letra que escribimos es en mayúscula o minúscula, dentro del programa se convertirá en mayúscula.
Posición relativa (sumar una cantidad a la posición en la que está el motor):
Desde el terminal serie, las letras que hacen mover el motor son Q-W, A-S, Z-X, 1-2. Cada par es derecha o izquierda, por ejemplo, Q es izquierda y W es derecha, así con el resto. Escribes una de esas letras y al pulsar enter (o dándole a "Enviar"), el motor se moverá cierto número de pasos. Q y W es una distancia corta, A y S una distancia media, Z y X una distancia más larga y finalmente la tecla 1 y 2 es una distancia aún más larga. Si necesitas una distancia relativa muy larga puedes poner por ejemplo "11111" y pulsar enter, aunque lo mejor para esto es usar posiciones absolutas.
Posición absoluta: Escribes 'G' junto a un valor numérico y le das a enter, por ejemplo: "G23000" e irá a esa posición. Para posicionar recomiendo hacerlo de esta manera.
Modificar las constantes PID y tiempo de muestreo:
Ponemos 'P' y el valor de la constante proporcional.
Ponemos 'I' y el valor de la constante integral.
Ponemos 'D' y el valor de la constante derivativa.
Ponemos 'T' y el valor en milisegundos del tiempo de muestreo.
Por ejemplo, en el terminal serie ponemos: "D20" (y pulsamos enter), cambiará la constante KD a 20. Se puede utilizar decimales, por ejemplo "D21.35". Nótese que el decimal es un punto, no una coma, esto es importante. También se puede poner varias a la vez y han de ir obligatoriamente separadas por un espacio.
Ejemplos:
P1 D22.1 (y pulsamos enter)
P1.5 D20.3 T14 (y pulsamos enter)
Arduino te responderá con los datos de las constantes PID y tiempo de muestreo que hay en ese momento.
Si pulsas la letra "K" y le damos a enter, también te saldrá los valores de las constantes PID y tiempo de muestreo. Esto nos sirve para consultarlas en cualquier momento, porque cuando se hace pruebas, el terminal se llena de números y es fácil olvidar cómo estaban esos parámetros.
Métete dentro del programa y modifica la cantidad de distancia que ha de recorrer a tu gusto, añadir teclas con otras distancias, otras funciones, etc.
Antes de ponerlo en marcha has de saber:
Si al poner en marcha el Arduino y al mover el eje el motor gira sin parar, significa que la polaridad del motor está invertida por tanto has de enrocar (intercambiar) las dos señales del PWM que va al puente en H.
Los puentes en H suelen tener dos patillas para el control del sentido del motor y una patilla "Enable" o también llamada "PWM". El programa que presento aquí mete la señal de PWM directamente por las dos salidas de control de sentido, por tanto el "Enable" o "PWM" que tienen muchos puentes en H hay que ponerlas a 1 lógico, es decir a 3,3V ó 5V, según la lógica de alimentación que use tu puente en H. Esa patilla (Enable o PWM) nunca la has de poner a 12V porque estropearía tu puente en H. Si nunca has usado un puente en H, primero trata de mover el motor directamente en ambos sentidos (poniendo a 1 ó 0 manualmente las entradas del puente en H) y cuando ya conozcas el funcionamiento entonces ya estarás listo para conectarlo a cualquier Hardware. Recuerda que el control de velocidad lo hago directamente con los pines de control de sentido de giro. ¿Por qué lo hago así? Porque de esta manera en vez de usar tres cables uso sólo dos.
Es necesario que los dos pines que van al puente en H (pines D5 y D6 del Arduino Uno o Nano) sean esos pines y no otros, porque están configurados como PWM y a máxima frecuencia (cerca de 60KHz). Recuerda que no todos los pines son PWM y que ciertos pines los maneja registros internos muy concretos que en el programa ya están preconfigurados. Es decir, que si decides cambiar la salida a otros pines no te va a funcionar. Por tanto, al menos la primera vez, cíñete al proyecto tal como lo presento y si luego quieres hacer modificaciones, al menos sabrás que antes de hacer las modificaciones todo funcionaba bien.
Lo mismo sucede con las entradas del encoder, han de ser los pines D2 y D3 y no otros, porque están configurados como interrupción externa. Si decides cambiar a otros pines tampoco te funcionará.
Podría suceder que el encoder que tu utilices tenga mucha resolución y/o que el motor sea especialmente veloz; el propio Arduino ha de contar los pulsos y si el encoder tiene mucha resolución podría perderlos porque no le daría tiempo a contarlos. Si este es tu caso y sólo quieres probar el control PID que propongo en esta página, has de bajar la velocidad del motor alimentándolo con una tensión menor. Yo he utilizado un encoder de 334 ppr con un motor de 12V y no he tenido problemas.
El puente en H puede ser cualquiera que se adapte al motor que vayas a utilizar. Para los que no tienen mucha experiencia en puentes en H decirles que es importante que tenga protección para cuando los dos niveles de entrada están en "alto", y ha de llevar los 4 diodos de protección para eliminar las corrientes inversas que producen las bobinas del motor. Yo utilizo puentes en H que compro en Internet y a día de hoy son muy económicos y están preparados para controlar motores y bobinas.
En este enlace puedes ver la configuración del patillaje de algunos modelos de encoders ópticos de media/alta resolución.
Desde el terminal serie podemos poner cifras enteras y decimales para modificar las constantes PID, pero si haces una modificación de esas constantes desde dentro del programa (no desde el terminal) has de utilizar siempre la notación en punto flotante porque dichas constantes son del tipo "double" y si no le pones el punto decimal, en algunos casos puede interpretarlo como entero y eso puede provocar errores en los cálculos.
Si vas a usar encoders de baja resolución (no lo recomiendo si es la primera vez que usas un control PID), antes de tocar las constantes PID lo que has de hacer es subir el tiempo de muestreo. Por ejemplo, por defecto está unos 25ms (2500), pues subirlo a mayor tiempo de muestreo, el doble o más. Cuanto menor es la resolución mayor ha de ser el tiempo de muestreo. Una vez que veas un comportamiento aceptable entonces puedes intentar mejorar el comportamiento variando las constantes PID.
Si has venido directamente a esta página, en Control PID con Librería tienes toda la información sobre la comunicación PC-Arduino, manejo óptimo del motor, una introducción para encontrar las constantes PID óptimas y otras cuestiones. En el ZIP de descarga también tienes unas instrucciones con todas estas cuestiones.