Arduino: motores DC-CC con encoder. Ángulo y dirección de giro del eje. Encoder de cuadratura Hall.
En esta entrada vamos a explicar cómo calcular el ángulo que gira un motor de corriente continua que tenga acoplado un encoder de cuadratura con sensores del tipo Hall desfasados 90º. El motor utilizado en el vídeo es un motor DC con caja de engranes metálicos de 6 a 12v. Este tipo de encoders son muy típicos en la industria y lo podemos encontrar en otros dispositivos. También se explica como conectar el motor y sensor al Arduino.
El código es el siguiente:
//Motor de CD con motorreductor, caja de engranes metálicos y encoder cuadratura. Torque de 5.2 kg.cm a 110 rpm. //Autor: Ing. Josep Ramon Vidal. Joober Tecnologies. https://joober.page.link/joober const byte Encoder_C1 = 3; // Cable amarillo pin 3 digital -- Usa interrupciones const byte Encoder_C2 = 4; // Cable verde al pin 4 digital byte Encoder_C1Last; int paso; boolean direccion; void setup() { Serial.begin(57600); } void loop() { pinMode(Encoder_C2, INPUT); attachInterrupt(digitalPinToInterrupt(3), calculapulso, CHANGE); Serial.print("Paso: "); Serial.print(paso); Serial.print(", "); Serial.print("Ángulo: "); Serial.println(paso * 0.481283422459893); /*Para por cada vuelta del rotor antes de la caja de engranes, se producen 22 pulsos (o pasos) del encoder. El motor tiene una relación de reducción en su caja de engranes de 1:34. Por tanto, se tienen 748 ticks o pulsos del encoder por cada revolución del rotor después de la caja de engranes. Por lo que 748/360 = 0.48128...*/ } void calculapulso() { int Lstate = digitalRead(Encoder_C1); if ((Encoder_C1Last == LOW) && Lstate == HIGH) { int val = digitalRead(Encoder_C2); if (val == LOW && direccion) { direccion = false; //Atrás } else if (val == HIGH && !direccion) { direccion = true; //Adelante } } Encoder_C1Last = Lstate; if (!direccion) paso++; else paso--; }
Dicho lo anterior, hay una interesante librería de Arduino que calcula el número de pulsos de cuadratura en encoders de posición lineales y rotativos. El encoder cuenta las señales en forma de pulsos del tipo cuadratura, que comúnmente se encuentran en encoders ópticos giratorios, sensores del tipo hall para motores y otro tipo de sensores de posición. El autor de esta librería es Paul Stoffregen.
Lo interesante de esta librería es que se puede emplear en multitud de modelos de Arduino como el DUE, UNO, LEONARDO, MEGA, etc. Para que la librería funcione a la perfección es recomendable emplear pines aptos para las interrupciones. Luego, hay que tener cuidado con la longitud de los cables. Si estos son largos, unas resistencias en PULL-UP de 1K, puede dar mejores resultados.
Las funciones básicas se describen a continuación.
Encoder myEnc(pin1, pin2); //Create an Encoder object, using 2 pins. You may create mulitple Encoder objects, where each uses its own 2 pins. The first pin should be capable of interrupts. If both pins have interrupt capability, both will be used for best performance. Encoder will also work in low performance polling mode if neither pin has interrupts. myEnc.read(); //Returns the accumulated position. This number can be positive or negative. myEnc.write(newPosition); //Set the accumulated position to a new number.
Lo bueno, es que podemos medir los pasos de varios motores en el caso de un Arduino Mega. La librería además cuenta con el siguiente ejemplo.
/* Encoder Library - TwoKnobs Example * http://www.pjrc.com/teensy/td_libs_Encoder.html * * This example code is in the public domain. */ #include <Encoder.h> // Change these pin numbers to the pins connected to your encoder. // Best Performance: both pins have interrupt capability // Good Performance: only the first pin has interrupt capability // Low Performance: neither pin has interrupt capability Encoder knobLeft(5, 6); Encoder knobRight(7, 8); // avoid using pins with LEDs attached void setup() { Serial.begin(9600); Serial.println("TwoKnobs Encoder Test:"); } long positionLeft = -999; long positionRight = -999; void loop() { long newLeft, newRight; newLeft = knobLeft.read(); newRight = knobRight.read(); if (newLeft != positionLeft || newRight != positionRight) { Serial.print("Left = "); Serial.print(newLeft); Serial.print(", Right = "); Serial.print(newRight); Serial.println(); positionLeft = newLeft; positionRight = newRight; } // if a character is sent from the serial monitor, // reset both back to zero. if (Serial.available()) { Serial.read(); Serial.println("Reset both knobs to zero"); knobLeft.write(0); knobRight.write(0); } }
LA LATENCIA
Hay un concepto muy interesante que se llama latencia. La latencia hace referencia al tiempo de respuesta. Una baja latencia significa que por ejemplo, movemos el encoder y rápidamente se obtiene una respuesta en el Monitor Serie. Interesa tener una baja latencia sobretodo en procesos críticos.
Un encoder requiere una respuesta de baja latencia a los cambios en las señales. Esta librería funciona muy bien si se usan uno o ambos pines con interrupciones. Sin embargo, si las interrupciones están deshabilitadas durante mucho tiempo, ya sea por el código que estamos empleando u otra biblioteca, el encoder puede no detectar un cambio. Por lo tanto, el resultado es un recuento incorrecto. Es muy probable que las librerías como el SoftwareSerial y NewSoftSerial generen problemas.
INTERRUPCIONES OPTIMIZADAS
Cuando se emplea esta librería en Teensy o Arduino, un Encoder utiliza rutinas de interrupción muy optimizadas escritas en lenguaje ensamblador. Normalmente, los Encoder suelen usar los attachInterrupt(), esto permite adjuntar dinámicamente funciones a cada interrupción. Al llamar a una función dinámica se produce una ligera sobrecarga. Para eliminar tal sobrecarga adicional, la librería permite utilizar la siguiente opción:
// This optional setting causes Encoder to use more optimized code, // It must be defined before Encoder.h is included. #define ENCODER_OPTIMIZE_INTERRUPTS #include <Encoder.h>
El encoder definirá directamente las interrupciones con la mínima sobrecarga. La desventaja principal es que pudiera generar un conflicto con su código o cualquier otra librería que requiera de attachInterrupt().
RESUMIENDO
En este artículo se proporciona un código propio y una librería. La ventaja principal de esta librería es el mismo autor: Paul Stoffregen. Dicho esto es una librería muy útil y precisa. Sin embargo, la desventaja principal que tiene esta librería es que pudiera generar incompatibilidades con las librerías SoftwareSerial y NewSoftSerial. Esto puede ser perjudicial en proyectos donde se requiere el envío de datos por el puerto Serie.
En estos supuestos puede ser interesante el empleo de nuestro código. Además éste, indica la dirección de giro sin tener que pasar por 0.