Sensor Ultrassônico no Arduino e Raspberry Pi

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.

sonar-diagrama-funcionamento
Uma figura para ilustrar o comportamento de reflexão das ondas transmitidas pelo sensor ultrassônico

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:

distance

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.

HC-SR04 front
O módulo HC-SR04
HC-SR04 back
Visão traseira do módulo HC-SR04

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.

sonar_arduino_bb
À prova de erros!

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.

sonar_raspberry_bb8.png
TCHA-RAM

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?

David Borges

Um dia... Boom! Dragão sem Chama.

3 thoughts to “Sensor Ultrassônico no Arduino e Raspberry Pi”

  1. 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.

  2. 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.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *