Uma dúvida que atormenta boa parte da humanidade é “como construir robôs autônomos?”. Quanto mais pensamos e realizamos cálculos complexos, mais chegamos à conclusão inevitável de que uma máquina autônoma precisa de uma maneira de compreeender o ambiente em que está, ou seja, necessita de sensores. O objeto de estudo de hoje são os sonares, ou o sensor de distância ultrassônico. Vamos aprender como funciona e como utilizá-lo, com exemplos em duas plataformas DIY (Arduino e Raspberry Pi) e quais são seus limites.
Introdução
Sonares são dispositivos que utilizam o som para medir distância até um obstáculo. Estes são também chamados sensores de distância ultrassônicos, pois utilizam ondas sonoras de frequêcia acima da faixa audível do ser humano de forma a evitar interferência com o batidão do funk ali da esquina. A ideia é a seguinte: um sonar possui dois elementos principais, um transmissor e um receptor de som, posicionados lado a lado. Quando se deseja fazer uma medida, o transmissor é ativado e produz uma série de pulsos sonoros. Esses pulsos viajam pelo espaço entre o sonar e um obstáculo na velocidade do som. Lembrando que a velocidade do som é diferente dependendo do meio percorrido, mas nessa discussão vamos imaginar que o meio em questão é o ar. Quando as ondas sonoras atingem um obstáculo, parte delas sofre reflexão. Essas ondas refletidas são então captadas pelo receptor. A diferença de tempo entre a transmissão dos pulsos sonoros e a captação pelo receptor é usada para calcular a distância até o objeto.
A equação abaixo pode ser usada para obter a distância (d), em metros, a partir da velocidade do som (vs), em metros por segundo, e do seu tempo de ida e volta (Δt), em segundos:
O sensor ultrassônico mais famoso no mercado atualmente é o modelo HC-SR04. Esse foi o modelo que utilizamos para os testes. Ele possui 4 pinos: VCC, GND, TRIG, ECHO e exige uma alimentação de 5V. TRIG é o pino de ativação do transmissor: quando esse pino recebe um pulso de 10 microssegundos, o transmissor do módulo produz uma série de pulsos ultrassônicos. O pino ECHO vai para o nível lógico alto logo após o transmissor finalizar o envio de seus pulsos ultrassônicos e permanece em alto até o momento em que o receptor capta as ondas refletidas por um obstáculo. O intervalo em que ECHO está em nível lógico alto é o tempo de ida e volta dos pulsos ultrassônicos.
Arduino
Esquemático
A plataforma perfeita para testar o HC-SR04. Basta conectar TRIG e ECHO a dois pinos digitais, VCC a 5V, GND a GND. Não há nenhum detalhe que, se esquecido, possa derreter seu Arduino.
Código
O código que elaboramos tem alguns truques, então vou mencioná-los aqui.
No Arduino, as medidas do sonar serão feitas continuamente dentro da função loop(). Isso significa que podemos escolher a frequência média com que fazemos a leitura do sonar ao configurar um pequeno delay entre as medições. Essa frequência de leitura é chamada taxa de amostragem, ou seja, a taxa com que obtemos amostras de um sensor. Devemos fazer 20 leituras por segundo (20 Hz)? Ou que tal forçar a barra e tentar 1 milhão (1MHz)? Hum, a velocidade do som e o próprio equipamento não permitem aumentar a taxa de amostragem tanto assim. No código, fixamos a taxa de amostragem (sampling_rate) a 20Hz.
O módulo HC-SR04 não é adequado para medições a longa distância. O alcance máximo do módulo é 4 metros, mas a confiabilidade das leituras já cai bastante após 2.5 metros. Logo não faz sentido tentar medir distâncias muito maiores que essas. No código abaixo definimos uma distância máxima (max_distance) a ser medida e isso implica em um tempo máximo de espera pela borda de descida de ECHO (max_delta_t). Você verá esse valor sendo usado na linha 75, para impedir que fiquemos esperando demais por resultados incertos.
Pronto! Execute o código a seguir e veja o resultado no Monitor Serial. Por favor funciona.
/* TRIG será conectado ao pino 7 * ECHO ao pino 8. */ int TRIG = 7; int ECHO = 8; /* Variáveis utilizadas para medir o intervalo de tempo * em que ECHO permanece em alto. Isso é o tempo de ida * e volta dos pulsos ultrassônicos. * start_t: momento da borda de subida de ECHO * end_t: momento da borda de descida de ECHO * delta_t: intervalo entre a borda de subida e descida de ECHO */ unsigned long start_t; unsigned long end_t; unsigned long delta_t; /* Variáveis para auxiliar no controle do loop principal * sampling_rate: taxa de amostragem em Hz, isto é, em média, * quantas leituras do sonar serão feitas por segundo * speed_of_sound: velocidade do som no ar a 30ºC em m/s * max_distance: máxima distância permitida para medição * max_delta_t: um valor máximo para a variável delta_t, * baseado na distância máxima max_distance */ double sampling_rate = 20; double speed_of_sound = 349.10; double max_distance = 4; unsigned long max_delta_t = max_distance*pow(10,6)/speed_of_sound; /* A variável de saída. Tudo que se faz nesse código é para * obtê-la. Bem valorizada. */ double distance; void setup() { /* Código de inicialização * Inicializa Serial * Define TRIG como saída digital * Define ECHO como entrada digital * Inicializa TRIG em nível lógico baixo */ Serial.begin(9600); pinMode(TRIG, OUTPUT); pinMode(ECHO, INPUT); digitalWrite(TRIG, LOW); } void loop() { /* Gera um pulso de 10ms em TRIG. * Essa ação vai resultar na transmissão de ondas * ultrassônicas pelo transmissor do módulo sonar. */ digitalWrite(TRIG, HIGH); delayMicroseconds(10); digitalWrite(TRIG, LOW); /* Atualiza a variável start_t enquanto ECHO está em nível * lógico baixo. Quando ECHO trocar de estado, start_t * manterá seu valor, marcando o momento da borda de subida * de ECHO. Este é o momento em que as ondas sonoras * acabaram de ser enviadas pelo transmissor. */ while(digitalRead(ECHO) == LOW) start_t = micros(); /* Atualiza a variável end_t enquando ECHO está em alto. * Quando ECHO voltar ao nível baixo, end_t vai manter * seu valor, marcando o tempo da borda de descida de ECHO, * ou o momento em que as ondas refletidas por um objeto * foram captadas pelo receptor. * Caso o intervalo de tempo seja maior que max_delta_t, * o loop de espera também será interrompido. */ while(digitalRead(ECHO) == HIGH && micros() - start_t < max_delta_t) end_t = micros(); /* Se a diferença entre end_t e start_t estiver dentro * dos limites que foram impostos, atualizamos a variável * delta_t e calculamos a distância até um obstáculo. * * O valor de delta_t que obtemos é em microssegundos, * portanto dividimos o valor obtido por 10^4 para obter * a distância em centímetros. * * Caso o valor de delta_t não esteja nos limites determinados * definimos a distância como -1, sinalizando uma medida * mal-sucedida. */ if(end_t - start_t < max_delta_t) { delta_t = end_t - start_t; distance = (0.5*delta_t*speed_of_sound)/pow(10,4); } else { distance = -1; } /* Enviamos o valor calculado pela Serial * Um pequeno delay para manter a média da taxa de amostragem * definida anteriormente. */ Serial.println(distance); delay(int(1000/sampling_rate)); }
Raspberry Pi
Esquemático
Os pinos digitais do Raspberry trabalham com níveis lógicos de 0 a 3.3V. Portanto, não podemos conectar diretamente o pino ECHO do HC-SR04 a um pino de entrada do Raspberry, já que ECHO estará a 5V quando for ativado pelo módulo. Você corre o risco de danificar o GPIO do Raspberry se fizer isso. Uma solução simples é usar um divisor de tensão para trazer o nível lógico alto de 5V para 3.3V. Usamos 3 resistores de 1kΩ para construir o divisor de tensão. Veja o esquema abaixo e faça as conexões com carinho.
Código
Usamos o mesmo procedimento que elaboramos anteriormente no código para Arduino. Se precisar, confira os pontos em que menciono o porquê da utilização das variáveis sampling_rate, max_distance e max_delta_t. Fora isso, adicionamos uma função no código a seguir para interromper de forma segura a execução do programa ao pressionarmos CTRL-C. O código em Python foi testado com a versões 2.7 e 3.5, na placa Raspberry Pi 3. Caso esteja usando um outro modelo do Raspberry, é melhor verificar a numeração dos pinos e trocá-los para um número adequado, se necessário. Tomei o cuidado de escolher pinos que não mudaram nas diversas revisões dos Raspberries que eu conheço. Usamos a numeração da placa, em que os pinos 16 e 18 são equivalentes aos GPIO23 e GPIO24 na numeração BCM.
# coding:utf-8 import sys import time import signal import RPi.GPIO as GPIO # Define a numeração dos pinos de acordo com a placa GPIO.setmode(GPIO.BOARD) # Função para finalizar o acesso à GPIO do Raspberry de forma segura def clean(): GPIO.cleanup() # Função para finalizar o programa de forma segura com CTRL-C def sigint_handler(signum, instant): clean() sys.exit() # Ativar a captura do sinal SIGINT (Ctrl-C) signal.signal(signal.SIGINT, sigint_handler) # TRIG será conectado ao pino 18. ECHO ao pino 16. TRIG = 18 ECHO = 16 # Variáveis para auxiliar no controle do loop principal # sampling_rate: taxa de amostragem em Hz, isto é, em média, # quantas leituras do sonar serão feitas por segundo # speed_of_sound: velocidade do som no ar a 30ºC em m/s # max_distance: máxima distância permitida para medição # max_delta_t: um valor máximo para a variável delta_t, # baseado na distância máxima max_distance sampling_rate = 20.0 speed_of_sound = 349.10 max_distance = 4.0 max_delta_t = max_distance / speed_of_sound # Define TRIG como saída digital # Define ECHO como entrada digital GPIO.setup(TRIG, GPIO.OUT) GPIO.setup(ECHO, GPIO.IN) # Inicializa TRIG em nível lógico baixo GPIO.output(TRIG, False) time.sleep(1) print ("Sampling Rate:", sampling_rate, "Hz") print ("Distances (cm)") # Loop principal. Será executado até que que seja pressionado CTRL-C while True: # Gera um pulso de 10ms em TRIG. # Essa ação vai resultar na transmissão de ondas ultrassônicas pelo # transmissor do módulo sonar. GPIO.output(TRIG, True) time.sleep(0.00001) GPIO.output(TRIG, False) # Atualiza a variável start_t enquanto ECHO está em nível lógico baixo. # Quando ECHO trocar de estado, start_t manterá seu valor, marcando # o momento da borda de subida de ECHO. Este é o momento em que as ondas # sonoras acabaram de ser enviadas pelo transmissor. while GPIO.input(ECHO) == 0: start_t = time.time() # Atualiza a variável end_t enquando ECHO está em alto. Quando ECHO # voltar ao nível baixo, end_t vai manter seu valor, marcando o tempo # da borda de descida de ECHO, ou o momento em que as ondas refletidas # por um objeto foram captadas pelo receptor. Caso o intervalo de tempo # seja maior que max_delta_t, o loop de espera também será interrompido. while GPIO.input(ECHO) == 1 and time.time() - start_t < max_delta_t: end_t = time.time() # Se a diferença entre end_t e start_t estiver dentro dos limites impostos, # atualizamos a variável delta_t e calculamos a distância até um obstáculo. # Caso o valor de delta_t não esteja nos limites determinados definimos a # distância como -1, sinalizando uma medida mal-sucedida. if end_t - start_t < max_delta_t: delta_t = end_t - start_t distance = 100*(0.5 * delta_t * speed_of_sound) else: distance = -1 # Imprime o valor da distância arredondado para duas casas decimais print (round(distance, 2)) # Um pequeno delay para manter a média da taxa de amostragem time.sleep(1/sampling_rate)
Considerações
Limites para a taxa de amostragem
Depois de alguns testes, verifiquei que as medições com o Arduino funcionam bem até uma taxa aproximada de 40 medições por segundo (40 Hz). Mais do que isso e começa a aparecer ruído, valores sem sentido. Já no Raspberry, obtive taxas maiores, com medidas seguras dentro de uma margem de 5% até 100 Hz.
Agora que você já sabe como usar o sensor ultrassônico, que tal ver nossos outros posts sobre Arduino e Raspberry Pi?
Que massa, obrigado ajudou muito aqui.
Olá. Show o artigo. Copiei o código, formatei o rasp, salvei. Montei o projeto e testei. Tudo certo agora vou pras próximas etapas que quero produzir. Obrigado pela ajuda.
Parabéns cara, usei alguns exemplos de site gringos mas as medições estavam saindo erradas (eles estavam
fazendo a conta errada), ae lendo seu código consegui entender como funciona o sensor e como faz para calcular a distância.