Eletrônica Por Equipe DevBras

Capa do artigo: Sensor ultrassônico HC-SR04: distância confiável com timeout e mediana

Sensor ultrassônico HC-SR04: distância confiável com timeout e mediana

A conta do eco, um código com timeout e mediana que não trava nem mente, a correção por temperatura e os limites que nenhum código resolve.

O sensor de vaga da garagem funciona na bancada. Instalado, ele às vezes mostra 0 cm com o carro longe, às vezes 300 cm com o carro parado na frente, e de vez em quando o Arduino parece travar por um instante. A resposta curta: distância é o tempo do eco vezes a velocidade do som, dividido por dois. Para a leitura ser confiável, use pulseIn com timeout, descarte leituras sem eco, tire a mediana de algumas medições espaçadas por 60 ms e respeite o alcance e o ângulo do módulo.

Os exemplos mais copiados chamam pulseIn sem limite de tempo e imprimem qualquer número que voltar. É assim que nascem os 0 cm e as pausas misteriosas.

Como o eco vira centímetro

O HC-SR04 tem um emissor e um receptor de ultrassom em cerca de 40 kHz, acima do que a gente ouve. Você manda um pulso de 10 microssegundos no pino Trig. O módulo dispara uma rajada de som e coloca o pino Echo em nível alto até o eco voltar. A largura desse pulso é o tempo de ida e volta.

A velocidade do som no ar, perto de 20 °C, é cerca de 343 m/s, ou 0,0343 cm por microssegundo. Como o som foi e voltou, a conta é:

distância (cm) = duração (µs) x 0,0343 / 2

Exemplo: eco de 5.830 µs  ->  5.830 x 0,0343 / 2 = 100,0 cm

Esquecer a divisão por dois faz o obstáculo parecer duas vezes mais longe, e o robô bate na parede achando que tem espaço.

O que o datasheet promete e o que limita

O datasheet que circula para o módulo cita alcance de 2 cm a 4 m, ângulo de medição em torno de 15° e intervalo recomendado de 60 ms entre medições. Na prática, superfícies macias (tecido, espuma), alvos finos e superfícies inclinadas devolvem pouco ou nenhum eco. Abaixo de uns 2 cm, o eco chega antes de o módulo começar a escutar. E disparar a medição seguinte cedo demais faz você captar o eco atrasado da anterior.

Como medir do jeito robusto

  1. Ligue VCC em 5 V, GND em GND, Trig num pino digital e Echo em outro. Em placas de 3,3 V, o pino Echo precisa de um divisor de tensão, porque o módulo responde em 5 V.
  2. Gere o disparo limpo: Trig em nível baixo por 2 µs, alto por 10 µs, baixo de novo.
  3. Leia com timeout. pulseIn(pino, HIGH, timeout) devolve 0 se o pulso não vier dentro do prazo. Para 4 m, o eco leva cerca de 23 ms; 25.000 µs dá folga.
  4. Trate 0 como "sem leitura", nunca como 0 cm.
  5. Espere 60 ms entre medições.
  6. Tire a mediana de cinco leituras válidas. A mediana ignora um eco espúrio isolado, coisa que a média não faz.

Exemplo completo: medição com timeout e mediana

const int PINO_TRIG = 9;
const int PINO_ECHO = 10;
const unsigned long TIMEOUT_US = 25000UL;  // cerca de 4 m de ida e volta, com folga
const float CM_POR_US = 0.0343;            // velocidade do som perto de 20 °C
const int LEITURAS = 5;

float medirUmaVez() {
  digitalWrite(PINO_TRIG, LOW);
  delayMicroseconds(2);
  digitalWrite(PINO_TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(PINO_TRIG, LOW);

  unsigned long duracao = pulseIn(PINO_ECHO, HIGH, TIMEOUT_US);
  if (duracao == 0) {
    return -1.0;                           // sem eco dentro do prazo
  }
  return duracao * CM_POR_US / 2.0;
}

float medirMediana() {
  float v[LEITURAS];
  int n = 0;
  for (int i = 0; i < LEITURAS; i++) {
    float d = medirUmaVez();
    if (d > 0) {
      v[n++] = d;
    }
    delay(60);                             // deixa o eco anterior morrer
  }
  if (n == 0) {
    return -1.0;
  }
  for (int i = 1; i < n; i++) {            // ordenação por inserção
    float x = v[i];
    int j = i - 1;
    while (j >= 0 && v[j] > x) {
      v[j + 1] = v[j];
      j--;
    }
    v[j + 1] = x;
  }
  return v[n / 2];
}

void setup() {
  pinMode(PINO_TRIG, OUTPUT);
  pinMode(PINO_ECHO, INPUT);
  digitalWrite(PINO_TRIG, LOW);
  Serial.begin(9600);
}

void loop() {
  float cm = medirMediana();
  if (cm < 0) {
    Serial.println("sem leitura: fora de alcance ou superficie que absorve o som");
  } else {
    Serial.print(cm, 1);
    Serial.println(" cm");
  }
}

Cada ciclo leva cerca de 300 ms (cinco medições de 60 ms), suficiente para vaga de garagem, nível de caixa d'água ou robô lento. Se o projeto precisa reagir mais rápido, reduza o número de leituras, não o intervalo.

Temperatura muda a conta

A velocidade do som cresce com a temperatura: aproximadamente 331,3 + 0,606 x T m/s, com T em graus Celsius. Na garagem do exemplo, num dia de 35 °C, ela é cerca de 352,5 m/s. Com o carro a 150 cm, o código calibrado para 20 °C mostra uns 146 cm. Quatro centímetros não importam para avisar "pare", mas importam se você quer medir nível com precisão.

Se precisar, leia um sensor de temperatura e calcule o fator em tempo de execução:

float cmPorUs = (331.3 + 0.606 * tempC) / 10000.0;   // m/s para cm/µs

O cuidado com a unidade e o atraso desse sensor está em sensor de temperatura no Arduino.

Validando contra a trena

Aponte o sensor para uma parede lisa e perpendicular. Meça a 10, 30, 50, 100, 200 e 300 cm com uma trena e anote a mediana de cada ponto. Onde o erro crescer ou a leitura sumir, você encontrou o limite real do seu módulo, na sua instalação. Repita com o alvo verdadeiro (o para-choque, a água, a caixa) porque ele reflete diferente da parede.

A montagem também conta. Fixe o módulo firme e alinhado com o alvo, longe de bordas e cantos que caiam dentro do cone de cerca de 15°, porque o eco de uma prateleira lateral chega antes e vira a leitura. Em ambiente externo, proteja o módulo de chuva sem cobrir os transdutores: condensação na tela metálica derruba o alcance.

Para varrer um ângulo maior que o cone do sensor, monte-o sobre um servo e meça em várias posições, como em servo motor no Arduino.

Erros comuns

  • Chamar pulseIn sem timeout. Sem eco, a função espera o prazo padrão de um segundo e o resto do programa congela.
  • Tratar 0 como distância. O sistema conclui que há algo encostado no sensor quando, na verdade, não houve eco.
  • Esquecer de dividir por dois. Toda distância sai dobrada.
  • Medir sem intervalo. Ecos atrasados da medição anterior viram leituras fantasmas.
  • Usar média em vez de mediana. Um único eco espúrio de 300 cm puxa a média para longe.
  • Ligar Echo direto numa placa de 3,3 V. O sinal de 5 V pode danificar o pino.

Checklist

  • pulseIn tem timeout compatível com o alcance que preciso.
  • Leitura sem eco é tratada como ausência de dado.
  • Há pelo menos 60 ms entre disparos.
  • Uso a mediana de várias leituras.
  • Comparei as leituras com uma trena em vários pontos.
  • Testei com o alvo real, não só com a parede.
  • Em placa de 3,3 V, o Echo passa por divisor de tensão.

Tire o alvo da frente do seu sensor agora: o monitor mostra "sem leitura" ou um número inventado que o resto do programa vai tratar como verdade?

Avalie este artigo

Referência: http://www.nubiasouza.com.br/sensor-de-distancia-ultra-sonico-e-arduino/