Tutorial · Iniciante · 25 min
Sensores e microchaves de contato: debounce para robôs
O fechamento de uma microchave de contato não é um flanco, são nove. Entenda o bounce, o custo do debounce e como criar um para-choque durável.
Uma microchave de contato não é um flanco digital limpo
Pressione uma microchave de contato e o contato metálico chega, desvia, se separa e chega novamente — várias vezes, nos primeiros milissegundos, antes de se estabilizar. Isso é bounce de contato, e é mecânico: nem muito software antes nem um capacitor depois faz o metal parar de se mover.
A consequência só importa porque o pino é lido por algo muito mais rápido que o metal. Um microcontrolador que amostra a 16 MHz vê cada uma dessas transições como um flanco real.
O painel inferior é a curva de projeto. A contagem de flancos cai em degraus conforme a janela cresce, chega a um quando o bounce termina e permanece nesse valor. Qualquer janela mais curta superconta; qualquer janela mais longa representa latência adicional sem benefício.
Durações típicas de bounce, que valem medir em vez de confiar nelas:
| Microchave de contato | Bounce |
|---|---|
| Microchave de alavanca de qualidade | 0,5–2 ms |
| Botão tátil barato | 2–10 ms |
| Microchave reed | 0,1–1 ms |
| Contato de relé | 1–20 ms |
O conselho usual de que “5 ms bastam” é um palpite no meio dessa tabela. Meça o seu: é uma linha de código e transforma uma superstição em um número.
Ligação, antes de tudo isso importar
Uma microchave de contato quer contatos normalmente abertos à massa, com um resistor de pull-up no pino:
pinMode(BUMP_L, INPUT_PULLUP); // idle HIGH, LOW when the bumper is pressed
Há três razões para essa disposição, e não a outra:
- Ativo em baixo oferece falha segura. Um fio rompido produz nível ALTO, que é não pressionado, então um fio do para-choque rompido não faz o robô acreditar que está permanentemente em colisão. Uma disposição com resistor de pull-down falha de forma oposta.
- O resistor de pull-up interno é gratuito. Nenhum resistor, nenhum componente adicional em um robô cuja frente sofre impactos repetidamente.
- O ruído é desviado para a massa, não capturado. Um fio longo do para-choque é uma antena; o caminho de baixa impedância à massa quando a microchave está fechada é o que impede que ele capte o driver de motor ao lado.
Use o contato normalmente fechado da microchave e terá o comportamento de falha oposto — um fio rompido parece uma colisão permanente, que é a escolha correta para uma parada de emergência e a errada para um para-choque.
Debounce: escolha um dos três
Ignorar depois de aceitar. A opção preguiçosa e correta para um para-choque. Aceite o primeiro flanco instantaneamente e não considere novas transições durante a janela:
const unsigned long BOUNCE_MS = 6;
unsigned long lastBumpAt = 0;
bool bumped() {
if (digitalRead(BUMP_L) == HIGH) return false; // not pressed
if (millis() - lastBumpAt < BOUNCE_MS) return false; // still inside the window
lastBumpAt = millis();
return true;
}
Latência zero no flanco que importa, e o bounce posterior é descartado. É isso que você quer quando a reação é parar os motores — esperar 6 ms para ter certeza significa avançar por 6 ms até o móvel.
Esperar estabilizar. A abordagem do exemplo do Arduino: lembrar quando a leitura mudou pela última vez e aceitá-la somente depois que permanecer inalterada durante a janela. Custa a janela inteira em latência e é a forma certa para um botão de menu, onde um acionamento falso é pior do que um lento.
Solução com hardware. Um capacitor de 100 nF entre os contatos, junto com o resistor de pull-up, dá um RC de cerca de 10 ms com um pull-up interno de 100 kΩ — mas o flanco que ele produz é lento, e um flanco lento em uma entrada digital pode cruzar o limiar várias vezes. Se seguir esse caminho, use depois uma entrada com gatilho de Schmitt. Geralmente não vale a pena.
Observe o que os três têm em comum: são não bloqueantes. Um delay(20) depois do flanco elimina corretamente o bounce, mas impede o robô de verificar qualquer outra coisa por 20 ms, o que em uma máquina a 0,3 m/s representa 6 mm de deslocamento sem controle — veja temporização com millis().
Interrupções e bounce são uma dupla ruim
Anexar uma interrupção a um para-choque parece obviamente correto e normalmente não é.
Nove transições significam que a ISR dispara nove vezes. Se ela incrementar um contador, você tem nove colisões; se definir uma flag, há apenas uma, sem consequências. Use uma interrupção apenas se o tratador for idempotente — definir uma flag, capturar um registro de tempo — e nunca para incrementar um contador.
volatile bool bumpFlag = false;
volatile unsigned long bumpAt = 0;
void onBump() { // idempotent: nine calls do what one call does
if (millis() - bumpAt < BOUNCE_MS) return;
bumpAt = millis();
bumpFlag = true;
}
millis() não avança dentro de uma ISR em um AVR, o que torna isso seguro durante a duração de um bounce, mas errado para qualquer coisa mais longa — use micros() se a janela precisar ser medida com precisão.
Sinceramente, para um para-choque você quase nunca precisa da interrupção. Um laço rodando a 200 Hz percebe uma colisão em até 5 ms, e o robô percorre 1,5 mm nesse tempo. Faça a leitura por polling e use os pinos de interrupção nos encoders que realmente não podem ser consultados assim.
Construindo um para-choque que funciona
A microchave é a parte fácil. O mecanismo à frente dela decide se o robô detecta a perna de uma cadeira que atinge em um ângulo.
- Uma única estrutura móvel, duas microchaves. Um único painel frontal em dois pontos de flexão, com uma microchave atrás de cada lado. Apenas a microchave esquerda indica que o obstáculo está à esquerda, que é a informação de que o comportamento de desvio precisa para escolher para que lado virar.
- Dobre as laterais. Um para-choque frontal plano não detecta um objeto atingido a mais de cerca de 30°, e as colisões por raspão são as mais comuns. Leve a estrutura até pelo menos 45° de cada lado.
- As molas o retornam, as microchaves não. A mola própria da microchave tem uma força de 50–100 gf e não empurrará a estrutura de volta de forma confiável. Use elásticos ou molas de compressão leves e deixe a microchave apenas detectar.
- Dê curso e um batente rígido. De 3–5 mm de movimento antes de a microchave ser acionada, e um batente que receba o impacto em vez do corpo da microchave. O que mais quebra são os corpos das microchaves.
- Combine com outro sensor sem contato. Um para-choque é a última linha de defesa — ele relata uma colisão que já aconteceu. Um sensor ultrassônico ou de tempo de voo à frente dele é o que evita a maioria dessas colisões; o para-choque pega a perna preta da cadeira, o vidro e o objeto na altura errada.
Medindo seu próprio bounce
// Count transitions in the 20 ms after the first edge. Run it, press the switch,
// read the number. That number is your debounce window, in your robot, with your wiring.
void loop() {
if (digitalRead(BUMP_L) == HIGH) return;
unsigned long start = micros();
int last = LOW, edges = 0;
while (micros() - start < 20000) {
int now = digitalRead(BUMP_L);
if (now != last) { edges++; last = now; lastEdgeAt = micros(); }
}
Serial.print(edges); Serial.print(" edges, settled after ");
Serial.print(lastEdgeAt - start); Serial.println(" us");
delay(500);
}
Defina a janela como o tempo de estabilização medido, mais cerca de 50%. Meça novamente quando a microchave se desgastar — os contatos pioram, não melhoram.
Quando algo dá errado
| Sintoma | Causa provável |
|---|---|
| Um impacto contado como vários | Sem debounce, ou uma janela menor que o bounce |
| Impactos totalmente perdidos | Uma chamada bloqueante em algum lugar; o laço não estava verificando |
| O robô inverte aleatoriamente | Fio longo do para-choque sem proteção perto do driver de motor |
| Indica uma colisão permanente | Fio rompido em um contato normalmente fechado, ou ausência de pull-up |
| Detecta apenas impactos de frente | Estrutura do para-choque muito plana — dobre as laterais |
| A microchave para de funcionar após uma semana | O corpo da microchave absorveu o impacto em vez do batente rígido |
| Detecta a parede, mas não a perna da cadeira | Nada errado com a microchave; a estrutura tem uma folga |
Para saber onde um para-choque fica em um robô completo, o robô de cobertura de ambiente é construído com dois deles e nada mais — e o simulador de cobertura mostra exatamente até onde um robô chega com apenas um sinal binário de detecção.
Explore o grafo
Faz parte destas montagens
Projetos e caminhos de aprendizagem que incluem este tutorial.
Leitura complementar