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.