Tutorial · Principiante · 25 min
Sensores de choque y antirrebotes para colisiones de robot
El cierre de un interruptor no es un flanco, son nueve. Cómo el rebote engaña a una interrupción, qué cuesta el antirrebotes y cómo hacer un parachoques fuerte.
Un interruptor no es un flanco limpio
Pulsa un microinterruptor de palanca y el contacto metálico llega, rebota, se separa y llega de nuevo —varias veces, en los primeros milisegundos, antes de asentarse. Eso es rebote de contacto, y es mecánico: ninguna cantidad de software antes ni ningún condensador después hace que el metal deje de moverse.
La consecuencia solo importa porque el pin lo lee algo mucho más rápido que el metal. Un microcontrolador muestreando a 16 MHz ve cada una de esas transiciones como un flanco real.
El panel inferior es la curva de diseño. El conteo de flancos cae en escalones al crecer la ventana, llega a uno en el punto donde termina el rebote y se queda ahí. Cualquier cosa más corta sobrecuenta; cualquier cosa más larga es latencia pagada por nada.
Duraciones típicas de rebote, que vale la pena medir en lugar de fiarse:
| Interruptor | Rebote |
|---|---|
| Microinterruptor de palanca de calidad | 0,5–2 ms |
| Pulsador táctil barato | 2–10 ms |
| Interruptor reed | 0,1–1 ms |
| Contacto de relé | 1–20 ms |
El habitual consejo de «5 ms bastan» es una suposición en mitad de esa tabla. Mide el tuyo: es una línea de código y convierte una superstición en un número.
Cableado, antes de que nada de esto importe
Un interruptor de choque quiere contactos normalmente abiertos a tierra, con el pin en pull-up:
pinMode(BUMP_L, INPUT_PULLUP); // idle HIGH, LOW when the bumper is pressed
Tres razones para esta disposición en lugar de la otra:
- Activo en bajo falla seguro. Un hilo roto lee HIGH, que es no pulsado, así que un cable de parachoques roto no hace creer al robot que choca permanentemente. Una disposición con pull-down falla al revés.
- El pull-up interno es gratis. Sin resistencia, sin pieza extra en un robot cuyo frontal va a recibir golpes repetidos.
- El ruido se deriva, no se recoge. Un cable largo de parachoques es una antena; el camino de baja impedancia a tierra cuando está cerrado es lo que evita que capte el manejador de motores de al lado.
Usa en su lugar el contacto normalmente cerrado del interruptor y obtienes el comportamiento de fallo opuesto —un hilo roto parece una colisión permanente, que es la elección correcta para una parada de emergencia y la equivocada para un parachoques.
Antirrebotes: elige uno de tres
Ignorar tras aceptar. El vago, y correcto para un parachoques. Acepta el primer flanco al instante, luego niégate a mirar de nuevo durante la ventana:
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;
}
Latencia cero en el flanco que importa, y el rebote se descarta detrás. Esto es lo que quieres cuando la reacción es detener los motores —esperar 6 ms para estar seguro son 6 ms conduciendo contra el mueble.
Esperar estabilidad. El enfoque del ejemplo de Arduino: recuerda cuándo cambió la lectura por última vez y acéptala solo cuando se ha mantenido quieta durante la ventana. Cuesta la ventana completa en latencia y es la forma adecuada para un botón de un menú, donde una pulsación falsa es peor que una lenta.
Hardware. Un condensador de 100 nF en los contactos más el pull-up da un RC de unos 10 ms con un pull-up interno de 100 kΩ —pero el flanco que produce es lento, y un flanco lento en una entrada digital puede oscilar por el umbral. Si vas por esta vía, síguelo con una entrada con disparador Schmitt. Normalmente no merece las piezas.
Fíjate en lo que los tres tienen en común: son no bloqueantes. Un delay(20) tras el flanco elimina el rebote perfectamente y deja al robot sin leer nada más durante 20 ms, que en una máquina a 0,3 m/s son 6 mm de recorrido ciego —ver temporización con millis().
Las interrupciones y el rebote son mala pareja
Conectar una interrupción a un parachoques parece obviamente correcto y normalmente no lo es.
Nueve transiciones significan que la ISR se dispara nueve veces. Si incrementa un contador ahora tienes nueve colisiones; si pone una bandera tienes una, sin daño. Usa una interrupción solo si el manejador es idempotente —poner una bandera, capturar una marca de tiempo— y nunca si cuenta.
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() no avanza dentro de una ISR en un AVR, lo que lo hace seguro durante un rebote pero erróneo para cualquier cosa más larga —usa micros() si la ventana importa con precisión.
Honestamente, para un parachoques rara vez necesitas la interrupción. Un bucle a 200 Hz nota una colisión en 5 ms, y el robot recorre 1,5 mm en ese tiempo. Sondéalo, y gasta los pines de interrupción en los encoders que genuinamente no pueden sondearse.
Construir un parachoques que funcione
El interruptor es la mitad fácil. El mecanismo delante de él decide si el robot detecta una pata de silla que golpea en ángulo.
- Una carcasa móvil, dos interruptores. Un solo panel frontal sobre dos puntos de flexión, con un interruptor detrás de cada lado. Solo el interruptor izquierdo significa que el obstáculo está a la izquierda, que es la información que el comportamiento de evitación necesita para elegir hacia dónde girar.
- Envuélvelo por las esquinas. Un parachoques frontal plano falla todo lo golpeado a más de unos 30°, y los golpes de refilón son la mayoría. Lleva la carcasa al menos a 45° por cada lado.
- Los muelles lo devuelven, los interruptores no. El propio muelle de un microinterruptor es de 50–100 g y no empujará una carcasa de vuelta de forma fiable. Usa gomas o muelles ligeros de compresión, y deja que el interruptor solo detecte.
- Dale recorrido y un tope duro. 3–5 mm de movimiento antes de que actúe el interruptor, y un tope que absorba el impacto en lugar del cuerpo del interruptor. Los cuerpos de interruptor son lo que se rompe.
- Emparejalo con algo sin contacto. Un parachoques es el último recurso —informa de una colisión que ya ocurrió. Un sensor ultrasónico o de tiempo de vuelo delante de él es lo que evita la mayoría; el parachoques atrapa la pata negra de la silla, el cristal y lo que está a la altura equivocada.
Medir tu propio rebote
// 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);
}
Fija la ventana en el tiempo de asentamiento que midas, más un 50 % aproximadamente. Remide cuando el interruptor sea viejo —los contactos empeoran, no mejoran.
Cuando algo sale mal
| Síntoma | Normalmente |
|---|---|
| Un choque contado como varios | Sin antirrebotes, o una ventana más corta que el rebote |
| Choques perdidos por completo | Llamada bloqueante en algún sitio; el bucle no estaba mirando |
| El robot retrocede al azar | Cable largo de parachoques sin blindar cerca del manejador de motores |
| Informa de una colisión permanente | Hilo roto en un contacto normalmente cerrado, o sin pull-up |
| Solo detecta golpes frontales | Carcasa de parachoques demasiado plana —envuélvela por las esquinas |
| El interruptor deja de funcionar al cabo de una semana | El cuerpo del interruptor recibió el impacto en lugar de un tope duro |
| Detecta la pared pero no la pata de la silla | Nada mal en el interruptor; la carcasa tiene un hueco |
Para ver dónde está un parachoques en un robot entero, el robot de cobertura de habitaciones está construido sobre dos de ellos y nada más —y el simulador de cobertura muestra exactamente hasta dónde llega un robot con un bit de sensado.
Explora el grafo
Forma parte de estos montajes
Proyectos y rutas de aprendizaje que incluyen este tutorial.
Lecturas adicionales