Руководство · Начальный · 25 min
Датчики удара и устранение дребезга при столкновениях робота
Замыкание переключателя — это не один фронт, а девять. Как дребезг обманывает прерывание, чего стоит фильтрация и как сделать долговечный бампер.
Переключатель — не чистый фронт
Нажмите рычажный микропереключатель, и металлический контакт коснётся, отклонится, разомкнётся и снова коснётся — несколько раз за первые миллисекунды, прежде чем стабилизируется. Это дребезг контактов, и он механический: ни программное обеспечение перед ним, ни конденсатор после него не заставят металл перестать двигаться.
Последствие важно только потому, что вывод считывает контроллер, гораздо более быстрый, чем металл. Микроконтроллер с частотой дискретизации 16 MHz воспринимает каждый из этих переходов как настоящий фронт.
Нижняя панель — кривая проектирования. Число фронтов падает ступенями по мере увеличения окна, достигает одного в момент окончания дребезга и остаётся там. Более короткое окно считает слишком много, более длинное приносит лишнюю задержку.
Типичные длительности дребезга, которые стоит измерить, а не принимать на веру:
| Переключатель | Дребезг |
|---|---|
| Качественный рычажный микропереключатель | 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%. Повторяйте измерение, когда переключатель состарится: контакты становятся хуже, а не лучше.
Когда всё идёт не так
| Симптом | Обычно |
|---|---|
| Одно касание посчитано как несколько | Нет устранения дребезга или окно короче дребезга |
| Касания полностью пропущены | Где-то есть блокирующий вызов; цикл не смотрел на вывод |
| Робот случайно едет назад | Длинный неэкранированный провод бампера рядом с драйвером двигателя |
| Сообщается постоянное столкновение | Оборванный провод на нормально замкнутом контакте или отсутствует подтяжка |
| Обнаруживаются только лобовые удары | Оболочка бампера слишком плоская — заверните её вокруг углов |
| Переключатель перестаёт работать через неделю | Удар принял корпус переключателя, а не жёсткий ограничитель |
| Обнаруживается стена, но не ножка стула | С переключателем всё в порядке; в оболочке есть зазор |
Чтобы понять, где бампер располагается на роботе целиком, посмотрите робота для покрытия помещения: он построен на двух таких датчиках и ни на чём больше, а симулятор покрытия показывает, как далеко продвигается робот при одном бите сенсорной информации.
Исследуйте граф
Входит в эти сборки
Проекты и учебные пути, в которые входит это руководство.
Дополнительные материалы