17 julio 2013

Sensor de distancia HC-SR04 con Arduino

Una de las funciones más útiles en las aplicaciones de robótica es la medición de distancias. Esto puede ser utilizado para evadir obstáculos o para generar una "imagen" del entorno en el que nuestro robot se está desplazando. En esta entrada explicaremos como utilizar el sensor HC-SR04 que nos permite estimar distancias a través de un eco ultrasonido.

 Luego de la muy bonita foto anterior... ¡Comencemos!

¿Cómo funciona el sensor?


El eco es ese sonido que parece repetir lo que decimos cuando hablamos o producimos sonidos en ciertos lugares. Este fenómemo se origina cuando las ondas de sonido rebotan en una superfície y regresan a nosotros generando ese particular sonido de repetición.

Debido a que el sonido viaja en el aire a una velocidad más o menos constante*, es posible estimar la distancia recorrida por un objeto gracias a una sencilla fórmula de física:



Donde delta s es la diferencia en la posición y delta t es el tiempo que tardó en realizarse este desplazamiento. Como en este caso no nos interesa la dirección que toman las ondas sonoras y estamos asumiendo una trayectoria casi recta frente al sensor, podemos utilizar la siguiente fórmula:


Siendo v la velocidad del sonido, d la distancia recorrida por la onda sonora  y t el tiempo transcurrido, haciendo uso de un poco de álgebra:


Para simplificar las cosas, vamos a asumir una velocidad del sonido constante en el aire de 340 m/s. La distancia la obtendremos entonces, multiplicando dicha velocidad por el tiempo en que tardemos en recibir el eco.

Sin embargo, la distancia hacia el objeto no es la calculada sino que se hace necesario dividir este total entre dos, ya que el tiempo medido es el tiempo de ida y vuelta de la señal ultrasónica hacia el sensor. Así que nuestra fórmula final es la siguiente:



El sensor


Una de las ventajas de este sensor es que incluye toda la circuitería necesaria para la generación del pulso ultrasónico y su correspondiente recepción, así que no tendremos que complicarnos en todos estos detalles técnicos.

En nuestro sensor vamos a encontrar 4 pines que conectaremos de la siguiente manera:

  1. VCC
  2. Trig
  3. Echo
  4. GND

El módulo inicia a trabajar al recibir un pulso positivo en la línea trg de al menos 10us. Luego de que el módulo recibe el pulso, este envía una señal ultrasónica y el módulo cambia el estado de la línea "echo" de 0 a 1 y la línea permanece en 1 hasta que se recibe el eco o en su defecto hasta que ha pasado el tiempo máximo de medición.

Esto último resulta muchísimo más fácil de visualizarse en el siguiente diagrama:



Como referencia, el rango máximo de este sensor va desde los 3cm hasta aproximadamente 3.5m. El rango depende esencialmente del material que esté reflejando la señal ultrasónica.

Midiendo la distancia con Arduino


Comencemos conectando el módulo a nuestro Arduino Mega 2560 de la siguiente manera:


Antes de continuar, debemos recordar que los tiempos de eco para una distancia de entre 3cm y 3.5m son muy cortos, en el rango de los micro-segundos. Si utilizamos el loop principal de Arduino para consultar el estado de la línea puede que perdamos muchos "ciclos" y no podamos medir tiempo.

La forma más adecuada de manejar este tipo de situaciones es mediante el uso de interrupciones, estas responden a "eventos". Para este caso en particular nos interesa responder al cambio de nivel en el voltaje de la línea.

El algoritmo que utilizaremos será el siguiente:

  1. Enviamos un pulso de 10 microsegundos a la línea "Trg" del módulo.
  2. Cuando la línea cambie de 0->1 guardaremos el contador de tiempo transcurrido de nuestro Arduino.
  3. Cuando la línea cambie de 1->0 compararemos el tiempo actual contra el que guardamos en el contador para determinar cuanto tiempo ha transcurrido.

¡A programar!


Primero definimos algunas constantes y variables a utilizar:

#define SOUND_SPD 320 // Velocidad del sonido
volatile long t_elapsed; // Almacena el tiempo de recorrido
volatile double lastDist; // Almacena la última distancia calculada


Luego inicializamos el puerto serie (para reportar las distancias medidas), configuramos el pin 21 (interrupción 2) como entrada y el pin 2 como salida.

Asociamos la interrupción 2 (línea 21) a la función onChange para que se llame cada vez que exista un cambio en el valor lógico de la línea.

void setup() {

  Serial.begin(9600);
  pinMode(21,INPUT);
  pinMode(2,OUTPUT);

  attachInterrupt(2,onChange,CHANGE);

  digitalWrite(2,LOW);
}


Para iniciar el proceso de medición, enviamos un pequeño pulso de 10us, para realizar esto simplemente ponemos la línea en alto, esperamos 10 microsegundos y volvemos la línea a 0, colocamos este código en una función aparte para poder llamarlo fácilmente desde el loop principal


void startMeasure() {

  t_start = 0;
  lastDist = 0;

  Serial.write("Starting measure...\r\n");
  digitalWrite(2,HIGH);
  delayMicroseconds(10);
  digitalWrite(2,LOW);
}


La función que realiza el manejo de interrupción debe de realizar dos tareas, la primera es guardar el punto en el tiempo actual, y luego a la siguiente llamada cuando el valor de la línea cae de 1 a 0 deberá calcular la diferencia de tiempo para estimar la distancia.

Es muy importante que el código de manejo de interrupción sea extremadamente rápido. El contador de tiempo de arduino será detenido mientras nos encontremos en el código de manejo de interrupción, así que perderemos algunos micro-segundos en lo que se ejecuta nuestro código que no serán registrados por el contador.

void onChange() {

  if(t_start==0) {
    t_start = micros();
  } else {
    lastDist = (((micros()-t_start)/1000000.0)*SOUND_SPD)/2;
    Serial.write("Distance: ");
    Serial.print(lastDist);
    Serial.write("m\r\n");
  }

}

A diferencia de la fórmula original dividimos entre 1,000,000 para obtener el tiempo en segundos y poder multiplicarlo por la velocidad del sonido.

Al revisar el código podemos notar como la instrucción que toma registro del tiempo es la primera luego del if esto es para disminuir el efecto del tiempo perdido por el manejo de la interrupción.

Si el contador de tiempo es cero, entonces simplemente guardamos el contador de tiempo actual en la variable t_start.

Al ser diferente de cero, primero almacenamos el contador del tiempo en una variable local. Y luego calculamos la diferencia entre los tiempos. En esta parte del código ya no resulta tan crítico que el código sea considerablemente rápido ya que ha terminado el período de medición, tanto así que nos podemos tomar la libertad de imprimir en el puerto serial.

Nota: Recuerden que mientras se ejecute el código de interrupción se pausará el loop principal, si su loop principal está realizando una tarea muy importante pueden reducir el código como se muestra a continuación:

void onChange() {

  if(t_start==0) {
    t_start = micros();
  } else {
    lastDist = (((micros()-t_start)/1000000.0)*SOUND_SPD)/2;
  }

}


Y luego desde el código del loop principal podemos leer el valor almacenado en lastDist con la última distancia calculada.

Por último el loop principal simplemente iniciará el proceso de lectura cada segundo y lo reportará al puerto serial al momento de haber sido calculado:

void loop() {

  startMeasure();
  delay(1000); // Repetir medición cada segundo

}

Si te da pereza copiar el código lo puedes descargar desde la siguiente dirección:


Si todo ha funcionado bien, deberán tener una salida en el puerto serie similar a la siguiente:

Starting measure...
Distance: 0.01800000m
Starting measure...
Distance: 0.0600000m

Conclusiones

El sensor ultrasónico de distancia es una sencilla solución para aplicaciones en que sea requerido estimar distancias o esquivar obstáculos. El módulo HC-SR04 facilita considerablemente la implementación de este tipo de funcionalidad ya que nos permite centrarnos en la parte de medición de tiempos en vez de los detalles técnicos de la generación de la señal y el procesamiento del eco recibido.

Y no habiendo nada más que decir por ahora... ¡Hasta la próxima!

+Fuentes y Recursos



*La velocidad del sonido en aire depende de la densidad del mismo. El sonido viaja más rápido a nivel del mar que en mayores altitudes. Sin embargo, para aplicaciones que no requieren de gran precisión, esta diferencia es despreciable.

5 comentarios:

Lucas dijo...

Hola, mi consulta es sobre la programación del sensor HC-SR04 usando un PIC 16F628a, mi objetivo es que mida a una distancia máxima de 1,5m y que cada vez que se ponga un objeto delante del sensor, prendan 2 led´s, no logro encontrar la solución con la programación , cualquier ayuda que me brinden sera de mucha ayuda, de ante mano gracias y saludos.-

Roberto Lopez dijo...

Mario:
Excelente tu blog, prometiste meterle a processing.

Tienes algun codigo para compartir con nosotros los principiantes
comunicacion Processing -Bluetooth
para controlar salidas arduino ??


Cperez

Arturo M. P. dijo...

Buenas tardes esta excelente tu Blog, una pregunta esto se puede hacer con arduino uno, salu2.

Gracias

Ji Yeon dijo...

Thanks for sharing! Nice post!
Máy ru võng em bé hay vong ru tu dong cho be hay máy đưa võng giúp bé ngủ ngon mà may dua vong ts không tốn sức ru võng. Võng tự động hay gia may dua vong chắc chắn, gọn gàng, dễ tháo xếp, dễ di chuyển và máy đưa võng tự động dễ dàng bảo quản.
Chia sẻ các có nên uống nước trước khi đi ngủ hay loài chó thông minh nhất thế giới hay lắc vòng có bị vô sinh không hay cách trị gàu hiệu quả nhất hay giải mã giấc mơ thấy người chết hay cách chống nắng bằng trà xanh hay Collagen trị mụn được không hay chữa mất ngủ bằng gừng đơn giản, bí quyết làm trắng da bằng cà phê và dầu dừa hay giảm cân nhanh bằng gạo lứt hq hay mẹo giúp tăng cường trí nhớ hiệu quả, những thực phẩm giúp cải thiện trí nhớ hiệu quả, hay bệnh viêm khớp không nên ăn gì hay mẹo giúp giảm độ cận thị cho bạn, bí quyết chống nắng với cà chua cực hiệu quả, cách giúp bé ngủ ngon giấcthực phẩm giúp bé ngủ ngon mẹ nên biết, chia sẻ cách làm trắng da toàn thân bằng thực phẩm, những món ăn chữa bệnh mất ngủ hiệu quả.

Những thực phẩm giúp đẹp da tại http://nhungthucphamgiupda.blogspot.com/
Thực phẩm giúp bạn trẻ đẹp tại http://thucphamgiuptre.blogspot.com/
Thực phẩm làm tăng tại http://thucphamlamtang.blogspot.com/
Những thực phẩm giúp làm giảm tại http://thucphamlamgiam.blogspot.com/
Những thực phẩm tốt cho tại http://thucphamtotcho.blogspot.com/

Roberto dijo...

Hola. Buen artículo. Sin embargo, al compilar me da el siguiente error "t_start' was not declared in this scope".
¿Cómo podría solucionarlo? Gracias