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
- 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.
- Gere o disparo limpo: Trig em nível baixo por 2 µs, alto por 10 µs, baixo de novo.
- 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. - Trate 0 como "sem leitura", nunca como 0 cm.
- Espere 60 ms entre medições.
- 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
pulseInsem 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
pulseIntem 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?
Referência: http://www.nubiasouza.com.br/sensor-de-distancia-ultra-sonico-e-arduino/