Tutorial · Principiante · 25 min

Sensori di urto: eliminazione del rimbalzo nelle collisioni

Chiusura, rimbalzo e eliminazione del rimbalzo: scopri perché un interruttore genera nove fronti, come ingannano un interrupt e come proteggere un paraurti.

Un interruttore non è un fronte pulito

Premi un microswitch a leva e il contatto metallico arriva, si flette, si separa e arriva di nuovo—più volte, nei primi millisecondi, prima di stabilizzarsi. Questo è il rimbalzo del contatto ed è meccanico: nessuna quantità di software prima e nessun condensatore dopo può far smettere al metallo di muoversi.

La conseguenza conta solo perché il pin viene letto da qualcosa molto più veloce del metallo. Un microcontrollore che campiona a 16 MHz vede ognuna di quelle transizioni come un fronte reale.

Due grafici sovrapposti. Quello superiore mostra il segnale di un interruttore per circa sei millisecondi: il pin grezzo scende a livello basso, poi oscilla tra alto e basso nove volte in 4.7 millisecondi prima di stabilizzarsi a basso, mentre l'uscita filtrata resta alta finché la finestra non scade e poi scende pulitamente. Il grafico inferiore mostra il numero di fronti che un interrupt conterebbe, che scende a gradini da nove a uno mentre la finestra di eliminazione del rimbalzo aumenta da zero a otto millisecondi, arrivando a uno a 4.7 millisecondi.
Una pressizione di un interruttore a leva modellato: nove transizioni in 4.7 ms. Una finestra di eliminazione del rimbalzo più breve del rimbalzo conta il rimbalzo—e una finestra abbastanza lunga da stabilizzarlo costa esattamente la propria durata in latenza, il compromesso che nessuno menziona. Scarica SVG

Il pannello inferiore è la curva di progetto. Il numero di fronti scende a gradini all’aumentare della finestra, arriva a uno quando il rimbalzo termina e ci resta. Una finestra più breve conta troppi fronti; una più lunga è latenza pagata senza vantaggio.

Durate tipiche del rimbalzo, da misurare invece che da dare per scontate:

Interruttore Rimbalzo
Microswitch a leva di qualità 0.5–2 ms
Pulsante tattile economico 2–10 ms
Interruttore a reed 0.1–1 ms
Contatto di relè 1–20 ms

Il consueto consiglio «5 ms bastano» è una stima fatta al centro di quella tabella. Misura il tuo: è una riga di codice e trasforma una superstizione in un numero.

Cablaggio, prima che tutto questo conti

Un interruttore di urto vuole contatti normalmente aperti verso terra, con il pin mantenuto alto:

pinMode(BUMP_L, INPUT_PULLUP);   // idle HIGH, LOW when the bumper is pressed

Tre motivi per preferire questa disposizione all’altra:

  • Il livello basso attivo è sicuro in caso di guasto. Un filo rotto legge HIGH, che significa non premuto, quindi un filo del paraurti spezzato non fa credere al robot di essere sempre in collisione. Una disposizione con pull-down fallisce nel modo opposto.
  • Il pull-up interno è gratis. Nessuna resistenza, nessun componente in più su un robot il cui fronte sarà colpito ripetutamente.
  • Il rumore viene deviato, non raccolto. Un filo lungo del paraurti è un’antenna; il percorso a bassa impedenza verso terra quando è chiuso è ciò che gli impedisce di captare il driver del motore accanto.

Se usi invece il contatto normalmente chiuso, ottieni il comportamento di guasto opposto—un filo rotto sembra una collisione permanente, che è la scelta corretta per un arresto di emergenza e quella sbagliata per un paraurti.

Eliminazione del rimbalzo: scegline uno tra tre

Ignora-dopo-accetta. Quello pigro, corretto per un paraurti. Accetta subito il primo fronte, poi rifiuta di controllare per la durata della finestra:

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;
}

Latenza zero sul fronte che conta e il rimbalzo viene scartato dietro di esso. È ciò che vuoi quando la reazione è fermare i motori—aspettare 6 ms per essere sicuri significa percorrere 6 ms prima di sbattere contro l’arredo.

Aspetta-che-stia-fermo. L’approccio dell’esempio Arduino: ricorda quando l’ultima lettura è cambiata e accettala solo dopo che è rimasta ferma per la finestra. Costa l’intera finestra in latenza ed è la forma giusta per un pulsante di un menu, in cui una pressione falsa è peggio di una lenta.

Hardware. Un condensatore da 100 nF tra i contatti più il pull-up dà una costante di tempo RC di circa 10 ms con un pull-up interno da 100 kΩ—ma il fronte che produce è lento e un fronte lento in ingresso digitale può oscillare attraverso la soglia. Se scegli questa strada, seguilo con un ingresso con trigger di Schmitt. Di solito non vale la pena aggiungere i componenti.

Nota ciò che tutti e tre hanno in comune: sono non bloccanti. Un delay(20) dopo il fronte effettua un’eliminazione del rimbalzo perfetta e impedisce al robot di leggere qualsiasi altro segnale per 20 ms, che su una macchina che viaggia a 0.3 m/s sono 6 mm di percorso alla cieca—vedi la temporizzazione con millis().

Interrupt e rimbalzo sono una coppia sbagliata

Collegare un interrupt a un paraurti sembra ovviamente corretto e solitamente non lo è.

Nove transizioni significano che la ISR viene eseguita nove volte. Se incrementa un contatore, ora hai nove collisioni; se imposta un flag, ne hai una, innocua. Usa un interrupt solo se il gestore è idempotente—impostare un flag, catturare una marca temporale—e mai se conta.

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() non avanza dentro una ISR su un AVR, il che rende questo sicuro per la durata di un rimbalzo ma sbagliato per qualsiasi cosa più lunga—usa micros() se la finestra deve essere precisa.

Sinceramente, per un paraurti raramente hai bisogno dell’interrupt. Un ciclo a 200 Hz nota una collisione entro 5 ms e il robot percorre 1.5 mm in quel tempo. Fai polling e usa i pin di interrupt per gli encoder che non possono davvero essere interrogati tramite polling.

Costruire un paraurti che funziona

L’interruttore è la metà facile. Il meccanismo davanti a lui decide se il robot rileva una gamba di sedia che colpisce di traverso.

  • Una scocca mobile, due interruttori. Un unico pannello frontale su due punti flessibili, con un interruttore dietro ciascun lato. Il solo interruttore sinistro significa che l’ostacolo è a sinistra, l’informazione che serve al comportamento di evitamento per scegliere da quale parte girare.
  • Avvolgilo attorno agli angoli. Un paraurti frontale piatto manca tutto ciò che viene colpito a più di circa 30°, e sono la maggior parte degli impatti laterali. Porta la scocca almeno a 45° su ciascun lato.
  • Le molle lo riportano indietro, gli interruttori no. La molla di un microswitch è di 50–100 g e non riporterà una scocca indietro in modo affidabile. Usa elastici o molle di compressione leggere e lascia che l’interruttore faccia solo da sensore.
  • Dagli una corsa e un fermo rigido. 3–5 mm di movimento prima dell’attuazione dell’interruttore e un fermo che riceva l’impatto invece del corpo dell’interruttore. I corpi degli interruttori sono ciò che si rompe.
  • Abbinalo a qualcosa senza contatto. Un paraurti è l’ultima risorsa—segnala una collisione già avvenuta. Un sensore a ultrasuoni o a tempo di volo davanti evita la maggior parte delle collisioni; il paraurti cattura la gamba nera della sedia, il vetro e ciò che si trova all’altezza sbagliata.

Misurare il proprio rimbalzo

// 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);
}

Imposta la finestra sul tempo di stabilizzazione misurato, più circa il 50%. Rimisura quando l’interruttore è vecchio—i contatti peggiorano, non migliorano.

Quando qualcosa va storto

Sintomo Di solito
Un urto viene contato come diversi Nessuna eliminazione del rimbalzo oppure una finestra più breve del rimbalzo
Gli urti vengono persi completamente Una chiamata bloccante da qualche parte; il ciclo non stava controllando
Il robot indietreggia a caso Filo lungo del paraurti non schermato vicino al driver del motore
Segnala una collisione permanente Filo rotto su un contatto normalmente chiuso oppure nessun pull-up
Rileva solo gli impatti frontali Scocca del paraurti troppo piatta—avvolgila attorno agli angoli
L’interruttore smette di funzionare dopo una settimana Il corpo dell’interruttore ha ricevuto l’impatto invece che un fermo rigido
Rileva il muro ma non la gamba della sedia L’interruttore è a posto; la scocca ha una fessura

Per capire dove si colloca un paraurti in un robot completo, il robot per la copertura della stanza è costruito su due di questi e nient’altro—e il simulatore di copertura mostra esattamente quanto lontano arriva un robot con un solo bit di informazione.

Esplora il grafo

Fa parte di queste costruzioni

Progetti e percorsi di apprendimento che includono questo tutorial.

Per approfondire

Fonti