- Ультразвуковой дальномер на Arduino
- Суть проекта
- Работа с OLED-дисплеем
- Работа с ультразвуковым дальномером
- Финальный проект
- Что дальше?
- Ультразвуковой дальномер HC-SR04: подключение, схема и примеры работы
- Подключение ультразвукового дальномера к Arduino
- Пример работы
- Работа с библиотекой
- Работа с Iskra JS
- Arduino и датчик расстояния HC-SR04
- Подключение
- Библиотеки
- Примеры
- Домашнее задание
Ультразвуковой дальномер на Arduino
Так получилось, что в университете я изучаю C/C++. Для души пробую делать небольшие проекты на Python. Я много слышал про платформу Arduino, смотрел видео на YouTube, частенько посещал Arduino Project Hub и вот мне стало интересно самому поэкспериментировать, углубясь в разработку под микроконтроллеры. Купив стартовый набор с самой платой и горстью электронных компонентов и попробовав собрать проекты из обучающей брошюры, понял, что надо двигаться дальше. Продумав саму идею следующей самоделки, отправился на просторы Google и обнаружил, что не могу найти всего, что мне нужно на одном ресурсе. Безусловно, мне несложно было посетить несколько сайтитов и блогов с информацией, но я бы сильно сэкономил время, если бы нашел все в одном месте. Так и появилась эта статья-туториал.
Суть проекта
Мне хотелось сделать дальномер. Во-первых, из-за того, что у меня был ультразвуковой датчик и надо было научиться с ним взаимодействовать. Во-вторых, я хотел выводить всю информацию на OLED-дисплей. В статьях, которые я находил, либо рассказывалось про работу с дисплеем и датчиком по отдельности, либо они являлись частью совершенно другого проекта. Я собрал все необходимое тут и надеюсь, что это сможет как-то помочь другим.
- Любая плата Arduino (у меня Uno);
- Ультразвуковой дальномер HC-SR04;
- OLED-дисплей на 0,96 дюймов;
- Соединительные провода;
- Макетная плата.
Работа с OLED-дисплеем
OLED-дисплей идеально подходит для DIY-устройств. Во-первых, мы имеем достаточно высокое разрешение экрана — 128×64 пикселя. Во-вторых, дисплей работает без модуля подсветки, что обеспечивает низкое потребление энергии. В-третьих, для подключения используется всего четыре разъема — два для питания и два для обмена информацией. Но несмотря на это, у OLED-дисплеев есть и минусы. Со временем пиксели могут тускнеть и перегорать.
Вот таким образом можно подключить дисплей:
Есть несколько библиотек для работы с OLED-дисплеями, мне больше нравится OLED_I2C. Мне она кажется очень простой и максимально понятной. Следующим образом выведем классический «Hello, world!» на дисплей:
#include // Подключаем библиотеку OLED myOLED(A4, A5, A4); // Определяем пины extern uint8_t SmallFont[]; // Подключаем шрифт void setup() < myOLED.begin(); // Инициализируем дисплей >void loop() < myOLED.clrScr(); // Очищаем буфер дисплея myOLED.setFont(SmallFont); // Инициализируем шрифт myOLED.print("Hello, world!", CENTER, 0); // Выводим текст myOLED.printNumI(123, CENTER, 20); // Выводим целое число myOLED.update(); // Копируем буфер дисплея на дисплей >
Если функции без параметров понятны сразу и не вызывают вопросов, то с функциями вывода на дисплей могут быть вопросы. Давайте сразу с ними разберемся, их существует всего три вида:
- print(st, x, y) — печать строки на дисплей.
Параметры:
st: строка для печати;
x: координата верхнего левого угла первого символа по горизонтали;
y: координата верхнего левого угла первого символа по вертикали.
В качестве координат можно использовать как сами координаты, так и литералы LEFT, CENTER, RIGHT. - printNumI(num, x, y, [length, [filler]]) — печать целого числа на дисплей.
Параметры:
num: Число для вывода на экран (от -2147483648 до 2147483647);
x: координата верхнего левого угла первой цифры/знака по горизонтали;
y: координата верхнего левого угла первой цифры/знака по вертикали;
length: минимальное количество цифр для отображения на экране;
filler: Символ для заполнения, чтобы получить минимальную длину. По умолчанию “ “.
В качестве координат можно использовать как сами координаты, так и литералы LEFT, CENTER, RIGHT. - printNumF(num, dec, x, y, [divider, [length, [filler]]]) — печать числа с плавающей точкой на дисплей.
Параметры:
num: Число для вывода на экран (от -2147483648 до 2147483647);
dec: количество цифр после запятой (в дробной части) (допустимые значения 1-5);
x: координата верхнего левого угла первой цифры/знака по горизонтали;
y: координата верхнего левого угла первой цифры/знака по вертикали;
divider: Одиночный символ для использования в качестве десятичной точки. По умолчанию ‘.’;
length: минимальное количество цифр для отображения на экране;
filler: Символ для заполнения, чтобы получить минимальную длину.
По умолчанию “ “.В качестве координат можно использовать как сами координаты, так и литералы LEFT, CENTER, RIGHT.
Работа с ультразвуковым дальномером
Ультразвуковой датчик расстояния работает по принципу эхолокации — посылает пучок ультразвука и получает его отражение с некоторой задержкой, с помощью которой и можно высчитать расстояние до объекта. Работает датчик от напряжения в 5V на расстоянии от 2 до 400 сантиметров.
Для получения данных с датчика необходимо:
- Подать на выход Trig импульс длительностью 10 микросекунд;
- Трансмиттер отправит 8 импульсов с частотой 40 кГц;
- Когда импульсы отразятся от препятствия и будут приняты ресивером, то на выходе Echo образуется входной сигнал;
- С помощью формулы данные преобразуются в расстояние. Чтоб получить расстояние в сантиметрах, нам необходимо разделить ширину импульса на 58, для получения расстояния в дюймах — на 148.
Подключить датчик к плате можно следующим образом:
Финальный проект
Теперь, когда мы разобрались с работой с каждого элемента по отдельности, можно перейти к основному проекту дальномера.
Подключим все элементы к плате следующим образом:
#include OLED myOLED(A4, A5, A4); extern uint8_t SmallFont[]; // Инициализируем пины int echoPin = 2; int trigPin = 3; void setup() < myOLED.begin(); // Определяем ввод и вывод pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); >void loop() < int duration, cm, inch; // Генерация короткого импульса длительностью 2 микросекунды digitalWrite(trigPin, LOW); delayMicroseconds(2); // В течении 10 микросекунд датчик посылает сигналы в 40 кГц digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); // Получаем время задержки с выхода Echo duration = pulseIn(echoPin, HIGH); // Находим расстояние в сантиметрах и дюймах cm = duration / 58; inch = duration / 148; // Выводим информацию на дисплей myOLED.clrScr(); myOLED.setFont(SmallFont); myOLED.print("Centimeters:", CENTER, 10); myOLED.printNumI(cm, CENTER, 20); myOLED.print("Inches:", CENTER, 40); myOLED.printNumI(inch, CENTER, 50); myOLED.update(); >
Финальный проект в работе выглядит следующим образом:
Что дальше?
Проект дальномера готов и прекрасно работает, но несмотря на все, он не является идеальным законченным решением. Можно поработать над улучшениями, и сделать следующие:
- Перенести все на монтажную плату и избавиться от макетки. Тогда получится цельное устройства без торчащих проводов;
- Перейти на Arduino Nano ради более компактного размера;
- Добавить автономное питание для работы без кабеля;
- Изготовить корпус.
Ультразвуковой дальномер HC-SR04: подключение, схема и примеры работы
Ультразвуковой дальномер рассчитан на определение расстояния до объектов в радиусе четырёх метров.
Работа модуля основана на принципе эхолокации. Модуль посылает ультразвуковой сигнал и принимает его отражение от объекта. Измерив время между отправкой и получением импульса, не сложно вычислить расстояние до препятствия.
Подключение ультразвукового дальномера к Arduino
Модуль подключается четырьмя проводами. Контакты VCC и GND служат для подключения питания, а Trig и Echo — для отправки и приема сигналов дальномера. Подключим их к пинам 10 и 11 соответственно.
Напряжение питания дальномера 5 В. Модуль работает и с платами, напряжение которых 3,3 В — в этом случае подключайте его к пинам группы с P8 по P13. Установите джампер выбора питания V2 на Troyka Shield в положение V2+5V. Пин микроконтроллера, соединённый с пином Echo должен быть толерантен к 5 В. Приведённая схема подходит для подключения дальномера к Iskra JS.
Пример работы
Рассмотрим как работает дальномер.
Для того чтобы инициализировать отправку сигнала дальномером, необходимо подать высокий сигнал длительностью 10 μs на пин Trig .
После получения высокого сигнала длительностью 10 μs на пин Trig , модуль генерирует пучок из восьми сигналов частотой 40 кГц и устанавливает высокий уровень на пине Echo .
Зная продолжительность высокого сигнала на пине Echo можем вычислить расстояние, умножив время, которое потратил звуковой импульс, прежде чем вернулся к модулю, на скорость распространения звука в воздухе (340 м/с).
Функция pulseIn позволяет узнать длительность импульса в μs . Запишем результат работы этой функции в переменную duration.
Теперь вычислим расстояние переведя скорость из м/с в см/мкс:
distance = duration * 340 м/с = duration * 0.034 м/мкс
Преобразуем десятичную дробь в обыкновенную
distance = duration * 1/29 = duration / 29
Принимая во внимание то, что звук преодолел расстояние до объекта и обратно, поделим полученный результат на 2
Оформим в код всё вышесказанное и выведем результат в Serial Monitor
// Укажем, что к каким пинам подключено int trigPin = 10; int echoPin = 11; void setup() { Serial.begin (9600); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); } void loop() { int duration, distance; // для большей точности установим значение LOW на пине Trig digitalWrite(trigPin, LOW); delayMicroseconds(2); // Теперь установим высокий уровень на пине Trig digitalWrite(trigPin, HIGH); // Подождем 10 μs delayMicroseconds(10); digitalWrite(trigPin, LOW); // Узнаем длительность высокого сигнала на пине Echo duration = pulseIn(echoPin, HIGH); // Рассчитаем расстояние distance = duration / 58; // Выведем значение в Serial Monitor Serial.print(distance); Serial.println(" cm"); delay(100); }
Работа с библиотекой
Количество строк кода можно существенно уменьшить, используя библиотеку для работы с дальномером.
#include #define TRIGGER_PIN 10 #define ECHO_PIN 11 #define MAX_DISTANCE 400 NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); void setup() { Serial.begin(9600); } void loop() { delay(50); Serial.print("Ping: "); Serial.print(sonar.ping_cm()); Serial.println("cm"); }
Работа с Iskra JS
var sonic = require('@amperka/ultrasonic') .connect({trigPin: P10, echoPin: P11}); sonic.ping(function(err, value) { if (err) { console.log('An error occurred:', err); } else { console.log('The distance is:', value, 'millimeters'); } }, 'mm');
Arduino и датчик расстояния HC-SR04
HC-SR04 – ультразвуковой датчик расстояния. Характеристики:
- Питание: 5V
- Рабочий ток: 15 мА
- Звуковая частота: 40 кГц
- Угол измерения: 15 градусов
- Диапазон измерения: 2 см.. 4 м
- Точность: ~1 мм при грамотной фильтрации
Датчик работает довольно интересно: подаём импульс с продолжительностью 10 мкс на пин Trig, модуль посылает ультразвуковой импульс, он отражается от препятствия и детектируется. Затем с пина Echo возвращается импульс по продолжительности соответствующий времени путешествия звуковой волны. Ардуина этот импульс должна измерить.
Подключение
Подключаем к питанию и любым цифровым пинам:
Библиотеки
С датчиком можно работать без библиотек, стандартными средствами Arduino. Но есть и библиотеки:
- HC-SR04 – можно установить по названию HC-SR04 из менеджера библиотек (автор Dirk Sarodnick)
- NewPing – можно установить по названию NewPing из менеджера библиотек
Примеры
Небольшой момент: чтобы датчик не ловил “эхо” от самого себя – его не рекомендуется опрашивать чаще 30 мс!
Для опроса используем встроенные Arduino-функции
// пины #define HC_TRIG 3 #define HC_ECHO 2 void setup() < Serial.begin(9600); // для связи pinMode(HC_TRIG, OUTPUT); // trig выход pinMode(HC_ECHO, INPUT); // echo вход >void loop() < float dist = getDist(); // получаем расстояние Serial.println(dist); // выводим delay(50); >// сделаем функцию для удобства float getDist() < // импульс 10 мкс digitalWrite(HC_TRIG, HIGH); delayMicroseconds(10); digitalWrite(HC_TRIG, LOW); // измеряем время ответного импульса uint32_t us = pulseIn(HC_ECHO, HIGH); // считаем расстояние и возвращаем return (us / 58.2); >
Подвигаю рукой перед датчиком:
Отфильтруем данные при помощи простейшего экспоненциального фильтра
// пины #define HC_TRIG 3 #define HC_ECHO 2 void setup() < Serial.begin(9600); // для связи pinMode(HC_TRIG, OUTPUT); // trig выход pinMode(HC_ECHO, INPUT); // echo вход >float distFilt = 0; void loop() < float dist = getDist(); // получаем расстояние distFilt += (dist - distFilt) * 0.2; // фильтруем Serial.println(distFilt); // выводим delay(50); >// сделаем функцию для удобства float getDist() < // импульс 10 мкс digitalWrite(HC_TRIG, HIGH); delayMicroseconds(10); digitalWrite(HC_TRIG, LOW); // измеряем время ответного импульса uint32_t us = pulseIn(HC_ECHO, HIGH); // считаем расстояние и возвращаем return (us / 58.3); >
Данная библиотека умеет выдавать результат только в целых сантиметрах
// пины #define HC_TRIG 3 #define HC_ECHO 2 #include // указываем пины и макс. расстояние в сантиметрах NewPing sonar(HC_TRIG, HC_ECHO, 100); void setup() < Serial.begin(9600); // для связи >void loop() < // получаем и выводим Serial.println(sonar.ping_cm()); delay(50); >
Кстати, опрос датчика при помощи pulseIn() , не так плох, как про него пишут на форумах: погрешность измерения составляет всего 0.5 мкс:
что в пересчёте на расстояние даёт точность 0.17 мм! На деле точность получается в два раза выше, так как фактически мы измеряем сигнал два раза (путь до препятствия и обратно). Сам датчик шумит гораздо сильнее, поэтому миллиметровую точность получить абсолютно не проблема. Но есть проблема в другом: выполнение кода блокируется на время измерения, например на трёх метрах это будет 17 мс. Вроде и немного, но для некоторых задач это будет весьма критично.
Ещё один момент: скорость звука зависит от температуры: при +20°С это 343 м/с, а при -20°С – 318 м/с! А ведь это целых 318/343=7%, что на расстоянии в 1 метр даст погрешность 7 сантиметров. Много, гораздо больше возможных погрешностей в измерении. Давайте это исправим.
В диапазоне -50.. 50°С зависимость является линейной и аппроксимируется уравнением V = 0.609 * t + 330.75 :
Таким образом для нахождения более точного расстояния с поправкой на температуру достаточно делить время импульса не на 58, а на.. кстати, откуда берётся 58? Для прохождения 1 м звуку понадобится 1 / 343 = 0.0029 с, или 2.915 мс. Мы получаем время туда и обратно, поэтому умножаем ещё на 2. В и для сантиметров – ещё на 10, и получаем 58.3. Гораздо понятнее было бы умножать время импульса на скорость звука и делить пополам.
Таким образом для расчёта расстояния в миллиметрах с учётом температуры в °С получим формулу:
S = us * V / 2000 S = us * (0.609 * t + 330.75) / 2000 S = us * (t * 6 / 10 + 330) / 2000
Настолько высокая точность нам не нужна, поэтому можно избавиться от float , чтобы код весил меньше (третье уравнение). Погрешность составит не более 1 мм на 1 метр. И финальный пример тогда (подставим в первую программу):
// пины #define HC_TRIG 3 #define HC_ECHO 2 void setup() < Serial.begin(9600); // для связи pinMode(HC_TRIG, OUTPUT); // trig выход pinMode(HC_ECHO, INPUT); // echo вход >void loop() < int t = 24; // представим, что получили с датчика int dist = getMm(t); // получаем расстояние в мм Serial.println(dist); // выводим delay(50); >// сделаем функцию для удобства int getMm(int t) < // импульс 10 мкс digitalWrite(HC_TRIG, HIGH); delayMicroseconds(10); digitalWrite(HC_TRIG, LOW); // измеряем время ответного импульса uint32_t us = pulseIn(HC_ECHO, HIGH); // считаем расстояние и возвращаем return (us * (t * 6 / 10 + 330) / 2000ul); >
Отфильтровать – и будет супер!
Домашнее задание
- Попробовать другие библиотеки для HC-SR04
- Попробовать разные фильтры из урока на финальном примере