encoder incremental

Encoder de cuadratura para robótica

En el mundo de la robótica, el encoder es dispositivo esencial que nos permite medir la posición y el movimiento de los ejes de un robot de manera precisa. Estos dispositivos proporcionan información en tiempo real sobre la ubicación de los componentes mecánicos, lo que nos permite controlar y programar los movimientos de nuestros robots de manera eficiente. En este artículo, exploraremos cómo utilizar encoders de cuadratura en la robótica utilizando la plataforma Arduino, y cómo pueden mejorar el rendimiento de nuestros proyectos robóticos.

¿Qué es un encoder?

encoder incremental

Un encoder es un dispositivo que convierte el movimiento rotativo o lineal en una señal eléctrica que se puede utilizar para medir y controlar la posición. En el contexto de la robótica, los encoders se utilizan para medir la posición de los ejes de los motores y otros componentes móviles. Estos encoders generan señales de salida en forma de pulsos eléctricos proporcionales al movimiento, que luego pueden ser interpretados por un microcontrolador como Arduino, ESP32 , etc.

Tipos de encoders utilizados en robótica

Existen diferentes tipos de encoders utilizados en robótica, pero los dos más comunes son los encoders absolutos y los encoders incrementales.

Encoders absolutos: Los encoders absolutos proporcionan una salida de señal única para cada posición del eje. Esto significa que cuando encendemos nuestro sistema, el encoder absoluto nos dará una lectura precisa de la posición inicial sin la necesidad de un punto de referencia adicional. Estos encoders son ideales para aplicaciones que requieren un control preciso de la posición, como en brazos robóticos utilizados en ensamblaje industrial.

Encoders incrementales: Los encoders incrementales generan una serie de pulsos eléctricos a medida que el eje o componente se mueve. La posición se determina contando estos pulsos. A diferencia de los encoders absolutos, los encoders incrementales no tienen información sobre la posición inicial, por lo que se requiere un punto de referencia conocido para determinar la posición absoluta. Sin embargo, son más comunes en aplicaciones donde la velocidad y la dirección del movimiento son más importantes que la posición absoluta, como en robots móviles.

Encoder de cuadratura

Un encoder de cuadratura es un tipo especial de encoder incremental que ofrece una mayor precisión y capacidad de detección de dirección en comparación con los encoders incrementales estándar.
encoder de cuadratura

Estos encoders utilizan dos canales de salida, A y B, para generar señales que indican tanto el movimiento como la dirección. Estos canales producen una secuencia de pulsos en cuadratura, lo que significa que las transiciones de estado de las señales están desfasadas en 90 grados entre sí. Esta característica permite detectar la dirección del movimiento y proporciona una mayor precisión en la medición de la posición.

encoder pulsos digitales desfasados

Dirección de movimiento

Para la determinar el sentido de giro (signo), se toman como referencia el desfasamiento que existe entre las señales AB. El hecho de trabajar con dos canales, nos lleva a tener 4 estados, estos mismos son la combinación de A y B.

Tabla de un encoder

Si asumimos la salida A como el bit más significativo y la salida B como el bit menos significativo, en la tabla de verdad podemos saber la dirección de giro, si comparamos el estado actual con el estado anterior. Además, en la figura también se muestra una matriz de incrementos donde podemos comparar el estado anterior con el actual y saber cómo fue el movimiento. Por ejemplo, si el estado anterior fue 01 y el estado actual del encoder es 11 podemos saber que el motor está girando en sentido anti-horario (+). Esta matriz es muy útil para saber en pocos pasos que ocurre con el encoder.

También nos puede servir para detectar errores de nuestra señal o del encoder. Ya que hay 4 combinaciones que no pueden aparecer en nuestra señal, como por ejemplo la combinación (11,00), marcada con una E en la matriz.

Aprende robótica ahora

En la robótica, cada error es una oportunidad para aprender y mejorar

30

%

Off

Resolución

La resolución de un encoder es una medida en pulsos por revolución (PPR), es decir el número de conteo (cuentas) que genera el encoder en cada vuelta. En el caso de los motores Chihai Motor CHR-GM25-370 de 140RPM a 12v, el encoder incremental de cuadratura emite 11 cuentas por revolución del eje del motor, que corresponde a 495 cuentas por revolución del eje de salida de la caja de reductora (Holzer resolution).

Datasheet motor 140 rpm con encoder

Precisión

Respecto a la precisión, tenemos más de una opción.

  • Precisión simple, registrando un único flanco (subida o bajada) en un único canal.
  • Precisión doble, registrando ambos flancos en un único canal.
  • Precisión cuádruple, registrando ambos flancos en ambos canales

Resolución para una precisión cuádruple

Para aplicaciones de robótica se utiliza la precisión cuádruple, esta precisión es la que permite detectar el sentido de giro del eje del motor. Por lo tanto, la resolución del encoder para una precisión cuádruple (R) se calcula de manera diferente al Holzer resolution.

Donde:

R: Es la resolución del encoder para una precisión cuádruple.
mH: Es el número de cuentas por revolución del eje del motor (Motor Holzer). En nuestro caso 11.
s: Es el número de estados generado por los canales AB. En nuestro caso 4.
r: Es la relación de reducción de la caja reductora (ratio). En nuestro caso 45 cuentas por revolución.

Por lo tanto,

Cómo utilizar un encoder de cuadratura con Arduino

En base a la tabla anterior se debe crear un código que permita comparar el estado anterior y el estado actual de los pines C1 (canal A) y C2 (canal B) del encoder. En primer lugar, se debe detectar los flancos de subida y bajada de ambos canales AB. Para esta tarea lo más recomendable es usar interrupciones. Entonces, definimos los pines y las variables que se van a utilizar, configuramos los pines como entradas y activamos las interrupciones para detectar ambos flancos.

const int    C1 = 3; // Entrada de la señal A del encoder.
const int    C2 = 2; // Entrada de la señal B del encoder.

volatile int  n    = 0;
volatile byte ant  = 0;
volatile byte act  = 0;

void setup()
{

  pinMode(C1, INPUT);
  pinMode(C2, INPUT);

  attachInterrupt(digitalPinToInterrupt(C1), encoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(C2), encoder, CHANGE);
  

}


En la función encoder vamos a comparar el estado anterior y actual e incrementar o decrementar un contador según la tabla.

// Encoder precisión cuádruple.
void encoder(void)
{

  ant=act;
  
  if(digitalRead(C1)) bitSet(act,1); else bitClear(act,1);            
  if(digitalRead(C2)) bitSet(act,0); else bitClear(act,0);
  
  
  
  if(ant == 2 && act ==0) n++;
  if(ant == 0 && act ==1) n++;
  if(ant == 3 && act ==2) n++;
  if(ant == 1 && act ==3) n++;
  
  if(ant == 1 && act ==0) n--;
  if(ant == 3 && act ==1) n--;
  if(ant == 0 && act ==2) n--;
  if(ant == 2 && act ==3) n--;    
    

}

Finalmente, si cargamos el siguiente sketch en Arduino al dar una vuelta completa al eje del motor se debe mostrar en el monitor serial el valor de la resolución del encoder para una precisión cuádruple R. Para este caso R=1980.

const int    C1 = 3; // Entrada de la señal A del encoder.
const int    C2 = 2; // Entrada de la señal B del encoder.

volatile int  n    = 0;
volatile byte ant  = 0;
volatile byte act  = 0;

unsigned long lastTime = 0;  // Tiempo anterior
unsigned long sampleTime = 100;  // Tiempo de muestreo

void setup()
{
  Serial.begin(9600);

  pinMode(C1, INPUT);
  pinMode(C2, INPUT);

  attachInterrupt(digitalPinToInterrupt(C1), encoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(C2), encoder, CHANGE);
  
  Serial.println("Numero de conteos");

}

void loop() {
  if (millis() - lastTime >= sampleTime || lastTime==0)
  {  // Se actualiza cada sampleTime (milisegundos)
      lastTime = millis();
      Serial.print("Numero de cuentas: ");Serial.println(n);
   }
}

// Encoder precisión cuádruple.
void encoder(void)
{

  ant=act;
  
  if(digitalRead(C1)) bitSet(act,1); else bitClear(act,1);            
  if(digitalRead(C2)) bitSet(act,0); else bitClear(act,0);
  
  
  
  if(ant == 2 && act ==0) n++;
  if(ant == 0 && act ==1) n++;
  if(ant == 3 && act ==2) n++;
  if(ant == 1 && act ==3) n++;
  
  if(ant == 1 && act ==0) n--;
  if(ant == 3 && act ==1) n--;
  if(ant == 0 && act ==2) n--;
  if(ant == 2 && act ==3) n--;    
    

}

Implementa este sensor en un robot físico

Arduino| Odometría | Visión por computador | Ingeniería de control

30

%

Off

Posición en grados

Para medir la posición relativa del eje del motor, vamos a utilizar la siguiente fórmula.

Donde:
P: Es la posición relativa en grados.
n: Es el número de cuentas generadas.
R: Es la resolución del encoder para una precisión cuádruple. En nuestro caso 1980 cuentas por revolución.

Posición en grados en Arduino

const int    C1 = 3; // Entrada de la señal A del encoder.
const int    C2 = 2; // Entrada de la señal B del encoder.

volatile int  n    = 0;
volatile byte ant  = 0;
volatile byte act  = 0;

unsigned long lastTime = 0;  // Tiempo anterior
unsigned long sampleTime = 100;  // Tiempo de muestreo

double P = 0;
double R = 1980;

void setup()
{
  Serial.begin(9600);

  pinMode(C1, INPUT);
  pinMode(C2, INPUT);

  attachInterrupt(digitalPinToInterrupt(C1), encoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(C2), encoder, CHANGE);
  
  Serial.println("Numero de conteos");

}

void loop() {
  if (millis() - lastTime >= sampleTime || lastTime==0)
  {  // Se actualiza cada sampleTime (milisegundos)
      lastTime = millis();
      P = (n*360.0)/R;
      Serial.print("Posicion en grados: ");Serial.println(P);
   }
}

// Encoder precisión cuádruple.
void encoder(void)
{

  ant=act;
  
  if(digitalRead(C1)) bitSet(act,1); else bitClear(act,1);            
  if(digitalRead(C2)) bitSet(act,0); else bitClear(act,0);
  
  
  
  if(ant == 2 && act ==0) n++;
  if(ant == 0 && act ==1) n++;
  if(ant == 3 && act ==2) n++;
  if(ant == 1 && act ==3) n++;
  
  if(ant == 1 && act ==0) n--;
  if(ant == 3 && act ==1) n--;
  if(ant == 0 && act ==2) n--;
  if(ant == 2 && act ==3) n--;    
    

}