Как видит ультразвуковой датчик — и когда не видит
Как HC-SR04 измеряет расстояние звуком, почему мягкие, наклонные и тонкие предметы могут пропадать и как правильно опрашивать, фильтровать и крепить датчик на роботе.

Фото: © Nevit Dilmen · CC BY-SA 3.0
Ультразвуковой датчик вроде HC-SR04 не видит света. Он посылает звук и слушает эхо. Когда вы знаете, как это устроено, можно предсказать и то, когда он ошибётся, и заранее написать код робота для таких моментов.
Звук туда и обратно
Измерение начинается с запускающего импульса на TRIG длительностью не меньше 10 мкс. Датчик посылает короткий звуковой сигнал частотой 40 кГц — намного выше того, что слышит человек (примерно до 20 кГц). Затем он выставляет на пине ECHO уровень HIGH и держит его, пока не вернётся эхо. Длительность этого импульса — это время, за которое звук дошёл до предмета и вернулся назад.
Скорость звука при 20 °C — около 343 м/с, поэтому на один сантиметр звуку нужно около 29 мкс. Туда и обратно — это 58 мкс на каждый сантиметр расстояния:
| Импульс ECHO | Расстояние |
|---|---|
| 116 мкс | 2 см, минимальное расстояние |
| 1160 мкс | 20 см |
| 5800 мкс | 1 м |
| 23 200 мкс | 4 м, примерно максимальное |
Вот функция, которую используют скетчи сайта:
int readDistanceCm() {
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH); // at least 10 microseconds starts a measurement
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
unsigned long us = pulseIn(ECHO, HIGH, 25000UL); // echo pulse length, 0 after 25 ms
if (us == 0) return 400;
return us / 58; // there and back at 343 m/s: 58 us per cm
}Луч — это конус
Звук идёт не тонкой линией, а расходится конусом шириной около 15 градусов. В 1 м от датчика ширина конуса около 26 см, в 2 м — около полуметра.
Для робота из этого следуют три вещи:
- Датчик сообщает о ближайшем предмете в любом месте конуса, а не только прямо впереди. Ножка стола сбоку может остановить робота посреди свободного коридора.
- Он даёт только расстояние. Где именно в конусе находится предмет, он не говорит.
- Вблизи робота конус уже самого робота. Препятствие, в которое врежется колесо или угол корпуса, может оказаться вне конуса. То же касается низкой ступеньки под конусом или столешницы над ним. Это слепые зоны.
Поверхности, которые не отвечают
Датчик может измерить только то эхо, которое вернулось. Некоторые предметы его не возвращают:
- Мягкие вещи, например шторы, подушки, одежда и поролон, поглощают большую часть звука. Эхо слабое или его нет вовсе.
- Наклонные поверхности отражают звук в сторону, как зеркало отражает свет. Стена, к которой робот подъезжает под малым углом, почти вдоль неё, может не вернуть вообще ничего.
- Тонкие или круглые предметы, например ножки стульев и стойки, возвращают совсем немного звука. Их можно заметить поздно или не заметить вовсе.
- Углы могут гонять звук от стены к стене. Эхо возвращается с опозданием, и показание получается слишком большим.
Опаснее всего, когда эха нет. pulseIn() ждёт 25 мс, ничего не получает и возвращает 0, а readDistanceCm() превращает это в 400. Для робота 400 означает «в пределах примерно 4 м ничего нет», даже если прямо перед ним наклонная стена.
Минимальное расстояние
Ближе примерно 2 см показаниям верить нельзя. Эхо с такого малого расстояния возвращается, пока датчик ещё посылает свой собственный звуковой сигнал. Для робота это редко важно, потому что он должен реагировать гораздо раньше: Робот, объезжающий препятствия отъезжает назад, когда что-то ближе 25 см.
Важнее, чтобы в конус не попадала никакая часть самого робота. Провод, шаровая опора или передний край пластины, попавшие в луч, дают устойчивое, но неверное показание.
Старое эхо и два датчика
Звук ещё недолго остаётся в комнате и отражается от стены к стене. Если измерить снова слишком рано, может прийти запоздавшее эхо от прошлого замера, и оно будет выглядеть как новый близкий предмет. Поэтому между замерами ждите около 60 мс. Скетч ObstacleAvoider измеряет раз в 60 мс как раз по этой причине.
Два датчика HC-SR04 работают на одной и той же частоте 40 кГц, поэтому каждый может услышать звук другого. Опрашивайте их по очереди, с такой же паузой между ними, и никогда — одновременно. Два робота, чьи датчики смотрят друг на друга, могут точно так же мешать друг другу.
Температура
Скорость звука меняется примерно на 0,6 м/с на каждый градус Цельсия, а число 58 в коде рассчитано примерно на 20 °C. При 0 °C звук медленнее, около 331 м/с, поэтому стена в 1 м даст показание около 104 см. В жаркой комнате показания получаются немного меньше настоящих. Если вы знаете температуру и вам нужны точные расстояния, считайте так: скорость = 331 + 0,6 × температура (в м/с), а расстояние в см = длительность импульса в мкс × скорость / 20000. Для робота, который объезжает стены, остальные эффекты, описанные на этой странице, гораздо важнее.
Время замера: pulseIn() ждёт
pulseIn() блокирует программу: пока функция ждёт эхо, Arduino больше ничего не делает. Без третьего аргумента она ждёт до одной секунды, и всё это время робот едет дальше без новых показаний. Скетчи сайта дают ей 25 000 мкс — чуть больше, чем 23 200 мкс эха от 4 м.
Подумайте и о том, сколько робот проезжает между замерами. При 0,5 м/с он проезжает 3 см за 60 мс. Медиана трёх замеров с интервалом 60 мс занимает около 180 мс, и за это время робот проезжает около 9 см. Поэтому скетч ObstacleAvoider делает замеры по таймерам на millis() и между ними продолжает ехать. Как это сделать, показывает урок Программы, которые никогда не ждут.
Фильтрация: отбрасываем одиночные скачки
Реальные показания время от времени скачут. Пропущенное эхо даёт 400. Случайное лишнее эхо даёт слишком маленькое значение. Среднее арифметическое этого не исправит: среднее из 20, 20 и 400 — около 147. А медиана, то есть среднее по величине из трёх значений, равна 20.
int median3(int a, int b, int c) {
if ((a <= b && b <= c) || (c <= b && b <= a)) return b;
if ((b <= a && a <= c) || (c <= a && a <= b)) return a;
return c;
}Скетч DistanceMeter выводит медиану трёх замеров. Одно неверное значение из трёх исчезает. Два неверных подряд проходят.
Когда робот измеряет часто, помогает и второе правило: верьте большому скачку, только если следующий замер его подтверждает. При 0,5 м/с робот между двумя замерами приближается всего на 3 см. Показание, которое отличается от предыдущего, например, на 50 см, — это либо ошибка, либо новый предмет. Ещё один замер через 60 мс покажет, что именно.
Где закрепить датчик на роботе
- Спереди, прямо вперёд и строго горизонтально. Если наклонить его вниз, он может видеть пол. Если вверх — пропустит низкие препятствия.
- Так, чтобы два его круглых ультразвуковых преобразователя выступали за передний край. Провода и шаровую опору держите вне луча.
- Надёжно. Плохо закреплённый датчик после каждого удара смотрит в новую сторону.
- В робот-машине CoreEngine: VCC — к +5V на L298N, GND — к GND платы Uno, TRIG — к D9, ECHO — к D10.
Урок Измеряем расстояние с HC-SR04 разбирает подключение и скетч DistanceMeter, который можно запустить в симуляторе, наблюдая за расстоянием на арене в реальном времени. Затем Робот, который держит дистанцию использует те же показания, чтобы следовать за вашей рукой. Сам датчик есть в Маркете.