Руководство · Начальный · 25 min

Датчики удара и устранение дребезга при столкновениях робота

Замыкание переключателя — это не один фронт, а девять. Как дребезг обманывает прерывание, чего стоит фильтрация и как сделать долговечный бампер.

Переключатель — не чистый фронт

Нажмите рычажный микропереключатель, и металлический контакт коснётся, отклонится, разомкнётся и снова коснётся — несколько раз за первые миллисекунды, прежде чем стабилизируется. Это дребезг контактов, и он механический: ни программное обеспечение перед ним, ни конденсатор после него не заставят металл перестать двигаться.

Последствие важно только потому, что вывод считывает контроллер, гораздо более быстрый, чем металл. Микроконтроллер с частотой дискретизации 16 MHz воспринимает каждый из этих переходов как настоящий фронт.

Два наложенных графика. На верхнем показан сигнал переключателя примерно за шесть миллисекунд: исходный вывод переходит в низкий уровень, затем девять раз дребезжит между высоким и низким за 4.7 миллисекунды, прежде чем установиться в низком уровне, а выход с устранением дребезга остаётся высоким до истечения окна и затем чисто переходит вниз. На нижнем графике число фронтов, которое посчитало бы прерывание, ступенчато падает с девяти до одного по мере увеличения окна устранения дребезга от нуля до восьми миллисекунд, достигая одного при 4.7 миллисекунды.
Одно нажатие смоделированного рычажного переключателя: девять переходов за 4.7 ms. Окно устранения дребезга короче самого дребезга подсчитывает его лишние фронты, а окно, достаточно длинное для стабилизации, создаёт задержку, равную своей длительности; это компромисс, о котором никто не упоминает. Скачать SVG

Нижняя панель — кривая проектирования. Число фронтов падает ступенями по мере увеличения окна, достигает одного в момент окончания дребезга и остаётся там. Более короткое окно считает слишком много, более длинное приносит лишнюю задержку.

Типичные длительности дребезга, которые стоит измерить, а не принимать на веру:

Переключатель Дребезг
Качественный рычажный микропереключатель 0.5–2 ms
Дешёвая тактильная кнопка 2–10 ms
Герконовый выключатель 0.1–1 ms
Контакт реле 1–20 ms

Обычный совет «5 ms достаточно» — это догадка о середине таблицы. Измерьте свой: это одна строка кода, и она превращает суеверие в число.

Подключение, прежде чем всё это станет важным

Датчик удара требует нормально разомкнутых контактов на землю и подтянутого вывода:

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

Почему именно такое расположение, а не противоположное:

  • Активный низкий уровень безопасен при отказе. Оборванный провод считывается как HIGH, то есть не нажато, поэтому оторванный провод бампера не заставит робот считать, что он постоянно сталкивается. Расположение с подтяжкой вниз отказывает наоборот.
  • Внутренняя подтяжка бесплатна. Не нужен резистор и лишний компонент на роботе, передняя часть которого будет постоянно получать удары.
  • Шум отводится, а не собирается. Длинный провод бампера — антенна; низкоомный путь к земле при замыкании не даёт ему подхватить помехи от соседнего драйвера двигателя.

Если использовать нормально замкнутый контакт, поведение при отказе станет противоположным: оборванный провод выглядит как постоянное столкновение. Для аварийного останова это правильный выбор, а для бампера — неправильный.

Устранение дребезга: выберите один из трёх способов

Принять и больше не смотреть. Простой способ, правильный для бампера. Примите первый фронт сразу, затем не смотрите на вывод снова в течение окна:

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

На важном фронте задержки нет, а дребезг за ним отбрасывается. Это то, что нужно, когда реакция — остановить двигатели: ожидание 6 ms ради уверенности означает 6 ms въезда в мебель.

Дождаться стабилизации. Подход из примера Arduino: запомнить, когда показание последний раз изменилось, и принять его только после того, как оно удерживалось неизменным в течение окна. Стоимость — полная задержка окна, но такой способ подходит для кнопки меню, где ложное нажатие хуже медленного.

Аппаратное решение. Конденсатор 100 nF между контактами вместе с подтяжкой даёт RC-время около 10 ms при внутренней подтяжке 100 kΩ, но создаваемый фронт медленный, а медленный фронт на цифровом входе может колебаться вокруг порога. При таком подходе используйте после него вход с триггером Шмитта. Обычно затраты на детали того не оправдывают.

Обратите внимание, что у всех трёх способов есть общее свойство: они неблокирующие. delay(20) после фронта прекрасно устраняет дребезг, но блокирует чтение любых других данных на 20 ms, из-за чего робот, движущийся со скоростью 0.3 m/s, вслепую проедет 6 mm — см. измерение времени через millis().

Прерывания и дребезг — плохая пара

Подключение прерывания к бамперу кажется очевидно правильным, но обычно не является.

Девять переходов означают девять срабатываний ISR. Если он увеличивает счётчик, теперь у вас девять столкновений; если устанавливает флаг — одно, и это безвредно. Используйте прерывание только тогда, когда обработчик идемпотентен — устанавливает флаг, сохраняет метку времени, — и никогда для подсчёта.

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() на AVR не увеличивается внутри ISR, поэтому это безопасно в течение одного дребезга, но неверно для более длительных событий — если окно должно быть точным, используйте micros().

Честно говоря, для бампера прерывание редко нужно вообще. Цикл с частотой 200 Hz заметит столкновение за 5 ms, а робот за это время проедет 1.5 mm. Опрашивайте вывод, а выводы прерываний отдайте энкодерам, которые действительно требуют такого режима.

Создание рабочего бампера

Переключатель — простая половина. Механизм перед ним определяет, обнаружит ли робот ножку стула при ударе под углом.

  • Одна подвижная оболочка, два переключателя. Одна передняя панель на двух точках изгиба, с переключателем за каждой стороной. Срабатывание только левого переключателя означает, что препятствие слева — именно эта информация нужна поведению обхода препятствий, чтобы выбрать направление поворота.
  • Заверните её вокруг углов. Плоский передний бампер пропускает всё, что задевает под углом больше примерно 30°, а косые удары — большинство ударов. Заведите оболочку хотя бы на 45° с каждой стороны.
  • Пружины возвращают, переключатели — нет. Собственная пружина микропереключателя рассчитана на 50–100 g и не сможет надёжно вернуть оболочку. Используйте резиновые ленты или лёгкие пружины сжатия, а переключателю оставьте только обнаружение.
  • Оставьте ход и жёсткий ограничитель. 3–5 mm хода до срабатывания переключателя и ограничитель, принимающий удар вместо корпуса переключателя. Корпуса переключателей — то, что ломается.
  • Дополните его бесконтактным датчиком. Бампер — последнее средство: он сообщает о столкновении, которое уже произошло. Ультразвуковой датчик или датчик времени пролёта перед ним предотвращает большинство столкновений; бампер ловит чёрную ножку стула, стекло и предмет на неподходящей высоте.

Измерение собственного дребезга

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

Установите окно равным измеренному времени стабилизации плюс примерно 50%. Повторяйте измерение, когда переключатель состарится: контакты становятся хуже, а не лучше.

Когда всё идёт не так

Симптом Обычно
Одно касание посчитано как несколько Нет устранения дребезга или окно короче дребезга
Касания полностью пропущены Где-то есть блокирующий вызов; цикл не смотрел на вывод
Робот случайно едет назад Длинный неэкранированный провод бампера рядом с драйвером двигателя
Сообщается постоянное столкновение Оборванный провод на нормально замкнутом контакте или отсутствует подтяжка
Обнаруживаются только лобовые удары Оболочка бампера слишком плоская — заверните её вокруг углов
Переключатель перестаёт работать через неделю Удар принял корпус переключателя, а не жёсткий ограничитель
Обнаруживается стена, но не ножка стула С переключателем всё в порядке; в оболочке есть зазор

Чтобы понять, где бампер располагается на роботе целиком, посмотрите робота для покрытия помещения: он построен на двух таких датчиках и ни на чём больше, а симулятор покрытия показывает, как далеко продвигается робот при одном бите сенсорной информации.

Исследуйте граф

Входит в эти сборки

Проекты и учебные пути, в которые входит это руководство.

Дополнительные материалы

Источники