Objetivos
Material requerido.
Arduino UNO | |
Sensor infrarrojo Sharp GP2Y0A21 | |
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:
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:
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.
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:
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:
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:
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í:
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:
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:
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:
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:
Valores de entre 8 y 32 suelen dar buenos resultados para señales como esta, pero haced vuestras pruebas y ya me diréis