Objetivos

 

  • Presentar los sensores de distancia infrarrojos.
  • Mostar los diferenetes tipos disponibles.
  • Mostrar su uso con Arduino GP2Y0A21.
  • Usar un Ring Buffer para limpiar la señal. [/three-fourth] [clear/]
  •  

    Material requerido.

     

    Imagen de Arduino UNO Arduino UNO

    Medicion de distancias
    Sensor infrarrojo Sharp GP2Y0A21
    Condensador 100uF  Condensador de 100 µF

    Sensores de distancia infrarrojos

     

    Ya hemos visto en estas páginas los sensores de distancia ultrasónicos tipo SR-04 que son de uso muy frecuente en nuestros robots o practicas escolares, debido a su buen funcionamiento y su bajo coste.

    Podéis encontrar aquí un tutorial que en su día montamos en Prometec con estos sensores y valdría la pena preguntarnos que si estos funcionan bien porque queremos enredar con otros sensores infrarrojos que en principio hacen lo mismo.

    La respuesta es que los módulos de infrarrojos al ser de mayor frecuencia que los ultrasónicos deberían ser más precisos y aunque valen un poco más tampoco es para tanto y como aquí estamos para jugar y probar cosas no podíamos por menos de hacer un tuto sobre esto sensores de los que Sharp es el proveedor típico:

    Medicion de distancias

    Básicamente son un emisor y un receptor de infrarrojos que calculan el tiempo de vuelo de la luz y sabiendo la velocidad a la que esta se desplaza por el aire es fácil calcular la distancia recorrida.

    Como además, la velocidad de la luz IR en el aire casi no se ve afectada por las variaciones de la temperatura ambiente, estos sensores no tiene la deriva térmica típica de los sensores de ultrasonidos y son capaces de entregar medidas constantes a lo largo de, digamos las diferentes estaciones del año. Algo que se puede decir de los módulos de ultrasonidos.

    Además al usar luz como sensor, esta puede ser enfocada mediante lentes con precisión lo que hace que su rango de aplicación pueda ser afinado a las distancias a las que necesitamos trabajar y naturalmente el mercado nos ofrece versiones en varios rangos de distancias útiles.

    A cambio (No todo iba a ser bueno) Estos sensores pueden ser falseados por la luz solar, ya que emite en el espectro infrarrojo, lo que limita estos sensores a interiores.

    Un ejemplo de esto es el catálogo de Pololu que nos ofrece estas variantes de los sensores infrarrojos:

    sensores infraroojos

    Que varían en función de la distancia útil a la que pueden trabajar y naturalmente esto se consigue con lentes de diferentes tamaños y a diferentes distancias del sensor y emisor, haciendo que el tamaño de los sensores y su encapsulado varíen en función del modelo.

    diferentes modelos

    Probando el sensor Sharp GP2Y0A21

     

    En este tuto vamos a usar un sensor Sharp de infrarrojos modelo GP2Y0A21 YK0F, que como podéis ver en la tabla de arriba permite unas distancias útiles de medición de entre 10 y 80 cm.

    Su conexión a nuestro Arduino es trivial:

    diagrama de cableado

    El sensor produce una salida analógica que depende de la distancia a la que detecta el objeto que debe ser corregido porque no es lineal pero en cualquier caso el fabricante nos proporciona una forma para determinar la distancia a partir de esta medida analógica.

    En mi caso, he encontrado que la longitud de los cables afecta sensiblemente a la estabilidad de la medida por lo que decidí soldar los pines del sensor directamente a mi placa Arduino:

    muestra del resultado

    Podemos leer directamente la salida analógica del sensor y presentarla en el display de Arduino con este programita:

    #define sensor A5 // Sharp IR GP2Y0A41SK0F (4-30cm, analog)
    
    void setup()
    {  Serial.begin(9600);
    }
    
    void loop()
     {
       float volts = analogRead(sensor)*0.0048828125;  // value from sensor * (5/1024)
       delay(100); // slow down serial port
       Serial.println(volts );     
    }

    Veras como al mover algo en el rango del sensor la salida analógica, varia de forma inversa s la distancia:

    variaciones de medida

    Pero naturalmente nos interesa calibrar la salida en digamos cm y según el fabricante podemos usar esta fórmula:

    float volts = analogRead(sensor)*0.0048828125;  // valuor * (5/1024)
    float distance = 13*pow(volts, -1);
    
    
    • Atentos aquí, porque la formula depende del modelo y debe aplicarse la que corresponde o de lo contrario no medirá bien
    • Comprobad la data sheet de vuestro sensor para estar tranquilos.[/fancy-ul] [/three-fourth]

    El resultado es algo así:

    Variaciones de las lecturas del sensor IR

    Con esta operación el sensor mide ya distancia, pero se puede ver a pesar de todo la variabilidad de la medida es relativamente alta llegando a ±1,5 cm, por lo que el fabricante recomienda que incluyamos un condensador de 100 µF entre positivo y negativo para mejorar:

    Detalle de condensador

    Si ponemos el condensador de 100 µF y limitamos la máxima medida para evitar saltos bruscos en el fondo de escala, el resultado es poco más o menos así, cuando voy moviendo la mano frente al sensor, que ya parece algo mas útil:

    Sensor sharp suavizado por el condensador

    Aquí os dejo el programa completo por si acaso:

    #define sensor A5 // Sharp IR GP2Y0A41SK0F (4-30cm, analog)
    
    void setup()
    {  Serial.begin(9600);
    }
    
    void loop()
    {
      float volts = analogRead(sensor)*0.0048828125;  // value from sensor * (5/1024)
      float distance = 13*pow(volts, -1);
      delay(100); // slow down serial port
    
      if (distance > 30) distance = 30;  // Para limitar la escala del serial plotter
          Serial.println(distance ); 
    }

    Procesando la señal

     

    No me puedo resistir a la tentación de tratar un poco la señal que leemos usando un ring buffer que ya hemos usado en alguna otra ocasión.

    He visto que algunos tutos en internet cogen una serie de valores por ejemplo 16 y los promedian para presentar solo este valor. Es una técnica que funciona pero que no elimina el aspecto aleatorio de muchas medidas y personalmente prefiero usar un ring buffer que hace mucho más suaves las transiciones:

    Buffer circular

    La idea es coger un número de valores que decidamos y crear un array de este tamaño

    int index = 0 ;        // Posicion a escribir
    const int N = 16 ;     // Numero de muestras a considerar
    float Buffer[N] ;      // Array donde guardamos las lecturas

    Arriba he elegido 16 pero podéis variar ese numero según necesidades, y ahora la idea es que vamos guardando cada valor que leemos en el buffer y cando llegamos al valor de N muestras volvemos a apuntar al principio a la posición 0 (Por eso le llaman buffer circular)

     

    Buffer[ index] = analogRead(A5) ;
    index = ++index  % N  ;

    Como ves guardamos la lectura analógica en la posición del buffer que indica index , y después hacemos la segunda operación que es donde está el truco. Al incrementar index y tomar el resto con respecto al valor máximo, tenemos una sucesión de valores de index que va desde 0 hasta N sin comernos mucho el tarro, y que nos garantiza apuntar siempre a un punto de nuestro array sin posibilidad de salirnos. ¿Qué te parece el truco?

    Calculamos la media de las últimas N lecturas pero sin cambiar nunca todos los valores lo que hace que las transiciones sean suaves.

    • Cuidado aquí porque si N es grande, las transiciones serán tan suaves que la medida tardara en reaccionar y eso puede ser un problema.
    • Puedes jugar con el valor de N para ver que la curva se va suavizando cuanto más grande es N, pero también tardas más tiempo en alcanzar el valor real de la media.[/fancy-ul] [/three-fourth]

    El programa final puede ser asi:

    int index = 0 ;        // Posicion a escribir
     const int N = 16 ;     // Numero de muestras a considerar
     float Buffer[N] ;      // Array donde guardamos las lecturas
    
    void setup()
     {    Serial.begin(9600);
     }
    
    void loop()
     {
     Buffer[ index] = analogRead(A5) ;
     index = ++index  % N  ;
    
     float Tmedia = 0 ;
     for (int i=0 ; i < N ; i++)
     Tmedia = Tmedia + Buffer[i] ;
    
     float  T = 147737 / (Tmedia/ N)/1000 ;
     float  Dist = 10 * pow( T, 1.2134);
    
     if (Dist > 25)
         Dist = 25 ;
    
     Serial.println( Dist) ;  delay (100);
     delay (100);
     }

     

    Y la curva que nos genera es algo asi para N=16:

    Salida a consola de las lecturas corregidas
    Valores de entre 8 y 32 suelen dar buenos resultados para señales como esta, pero haced vuestras pruebas y ya me diréis

    Deja una respuesta