Ритм жизни современного человека становится все более насыщенным и в его плотном графике становится все меньше времени на уборку собственного дома. В связи с этим в последнее время появляется все больше устройств, упрощающих наведение порядка в доме, одними из которых являются роботы-пылесосы, позволяющие в автоматическом режиме производить уборку пола в помещениях. У этих роботов-пылесосов достаточно много достоинств, но их существенным недостатком, сдерживающим их широкое распространение, является цена. Поэтому в данной статье мы рассмотрим создание робот-пылесоса на основе платы Arduino, который по функциональности будет мало отличаться от коммерческих моделей роботов-пылесосов, но стоить будет существенно дешевле них.
В составе робота мы будем использовать ультразвуковые датчики и инфракрасный датчик (IR proximity sensor). Ультразвуковые датчики будут помогать роботу избегать столкновения с препятствиями во время уборки помещения, а датчик приближения будет предотвращать падение робота с лестниц.
Ранее на нашем сайте мы уже рассматривали проект чистящего робота пылесоса на основе Arduino, но он был недостаточно совершенным и очень громоздким. Рассматриваемый в данном проекте робот-пылесос значительно более компактный и отличается более интеллектуальным алгоритмом работы. Также на нашем сайте вы поможете посмотреть похожие проекты роботов на основе платы Arduino:
- робот для автоматической дезинфекции помещений;
- робот, объезжающий препятствия;
- робот для очистки пола ( с помощью щетки).
Необходимые компоненты
- Плата Arduino Pro Mini (купить на AliExpress).
- Ультразвуковой датчик HC-SR04 – 3 шт. (купить на AliExpress).
- Драйвер двигателей L293d (купить на AliExpress).
- Электродвигатели постоянного тока формата N20, работающие от 5 В, с кронштейнами для их установки – 2 шт. (купить на AliExpress - смог найти только на 6 В, на 5 В почему то не удалось найти).
- Переключатель.
- Регулятор напряжения LM7805 (купить на AliExpress).
- Литий-ионная батарея 7.4V (купить на AliExpress).
- Инфракрасный датчик (купить на AliExpress).
- Перфорированная плата.
- Опорный ролик (колесо) для робота.
- MDF (из него будет делаться корпус робота).
- Портативный вакуумный пылесос (Vacuum Cleaner).
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Внешний вид компонентов, необходимых для сборки данного робота-пылесоса, показан на следующем рисунке:
Портативный пылесос (Portable Vacuum Cleaner)
Для того, чтобы наш робот мог выполнять свою функцию по предназначению (то есть пылесосить помещение), в его составе должен быть портативный пылесос. Внешний вид подобного пылесоса показан на рисунке ниже. Этот пылесос имеет очень простой механизм. Он имеет три части внизу – небольшую камеру для сбора пыли, двигатель постоянного тока и вентилятор. Сверху пылесоса имеется контакт для подключения питания. Двигатель непосредственно запитывается от напряжения 3V (2 батарейки по 1,5 В формата AA) через простой выключатель. Поскольку мы будем запитывать все наше устройство от литий-ионной батареи 7.4V, мы можем отрезать провода пылесоса от его внутреннего источника питания и запитать его от напряжения 5V с нашей схемы. Таким образом, мы удалили все ненужные нам внутренности пылесоса и он стал выглядеть внутри так, как показано на следующем рисунке.
Ультразвуковой датчик HC-SR04
Для обнаружения роботом препятствий в нашем проекте мы будем использовать популярные ультразвуковые датчики HC-SR04. Принцип их работы достаточно прост: передающий модуль датчика излучает ультразвуковую волну, которая распространяется в окружающем пространстве, отражается от препятствия и улавливается (принимается) приемным модулем датчика, в результате чего на выходе датчика формируется импульс, равный времени распространения ультразвуковой волны до препятствия и обратно. Зная скорость распространения звука в воздухе, достаточно просто на основе этого времени определить расстояние до препятствия. Более подробно об определении расстояний с помощью данного ультразвукового датчика и платы Arduino можно прочитать в этой статье. Также на нашем сайте вы можете посмотреть все проекты, в которых для определения расстояния использовался ультразвуковой датчик HC-SR04.
Инфракрасный датчик для обнаружения лестниц
Для того, чтобы наш робот-пылесос мог обнаруживать лестницы и не падать с них, мы будем использовать инфракрасный датчик (IR Sensor). Принцип его действия достаточно прост – он содержит в своем составе излучающий инфракрасный диод (IR LED) и фотодиод. Излучающий инфракрасный диод излучает инфракрасный свет и если на его пути встречается препятствие, то он отражается от него и улавливается (принимается) фотодиодом. Но напряжение на выходе фотодиода достаточно мало, поэтому для его усиления до необходимого уровня в составе датчика содержится компаратор на основе операционного усилителя.
Инфракрасный датчик содержит 3 контакта – Vcc (питающее напряжение), ground (общий провод, земля) и output (выход). Когда вблизи датчика есть препятствие, то на его выходе формируется напряжение низкого уровня (low). Поэтому данный датчик мы можем использовать для обнаружения пола комнаты. Если он передвигается по полу, то на выходе датчика будет low. Если же на выходе датчика мы неожиданно обнаружим напряжение высокого уровня, то мы должны либо остановить робота, либо двигать его в обратном направлении, либо сделать что-либо другое чтобы предотвратить его падение с лестницы.
Схема проекта
Схема робота-пылесоса на основе платы Arduino представлена на следующем рисунке.
Для обнаружения препятствий мы в схеме робота используем три ультразвуковых датчика. Их контакты питания подключены к общему питанию схемы, а земля – к общему проводу схемы. Управляющие (trigger) и выходные контакты (echo pins) датчиков подключены к ШИМ (широтно-импульсная модуляция) платы Arduino. Инфракрасный датчик также запитывается от общих VCC и земли (ground) схемы, а его выходной контакт подключен к цифровому контакту D2 платы Arduino. У драйвера двигателя мы два его контакта, разрешающих его работу (enable pins), подключили к 5 В, также контакт подачи питающего напряжения мы подключили к 5 В поскольку мы используем электродвигатели, работающие от напряжения 5 В. Поскольку наш робот-пылесос запитывается от литий-ионной батареи напряжением 7.4 В, а все компоненты схемы питаются от напряжения 5 В, то для преобразования напряжения 7.4 В в напряжение 5 В мы используем регулятор напряжения LM7805.
Сборка конструкции робота
Для спайки компонентов между собой мы использовали перфорированную плату. Эта часть работы очень проста, но к ней все равно необходимо отнестись с тщательностью. Для подключения платы Arduino pro mini мы использовали два контакта типа "мама" (female headers). После того как мы закончили пайку на перфорированной плате мы использовали соединительные провода для подключения ультразвуковых датчиков.
Изготовление корпуса для робота-пылесоса
Мы решили сделать наш робот-пылесос круглой формы как и большинство современных коммерческих моделей роботов-пылесосов. В качестве материала для изготовления корпуса робота мы решили использовать MDF поскольку он достаточно прочный и имеет неплохую влагозащищенность. Разумеется, вы можете выбрать другой материал, какой вам больше по душе.
Для изготовления корпуса робота мы вырезали из MDF круг радиусом 8 см, а в нем отверстие радиусом 4 см – в него будет вставляться наш портативный пылесос. Также мы вырезали соответствующие отверстия под колеса и три небольшие отверстия для установки опорного валика (колеса). Далее мы установили двигатели с помощью кронштейнов, колеса и опорное колесо. Затем мы установили ультразвуковые датчики слева, справа и спереди робота. Также мы закрепили инфракрасный датчик снизу робота. И не забудьте установить в корпус робота выключатель питания. На следующем рисунке вы можете визуально посмотреть описанную последовательность шагов по сборке корпуса робота.
Для изготовления верхней части робота мы вырезали круг радиусом 11 см. Для скрепления верхней и нижней частей робота и обеспечения необходимого промежутка между ними мы использовали три пластиковых трубы длиной 4 см. Всю конструкцию мы скрепили с помощью клея. При желании боковые стенки робота вы можете изготовить из пластика или какого-нибудь другого материала.
Объяснение программы для Arduino
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
В коде программы мы не будем использовать никаких внешних библиотек, поскольку взаимодействие с датчиком HC-SR04 осуществляется достаточно просто. Первым делом в программе мы объявим переменные для взаимодействия контактами Echo и Trigger ультразвуковых датчиков. Первый датчик у нас стоит слева робота, второй – спереди, а третий – справа робота.
1 2 3 4 5 6 7 |
const int trigPin1 = 3; const int echoPin1 = 5; const int trigPin2 = 6; const int echoPin2 =9; const int trigPin3 = 10; const int echoPin3 = 11; int irpin =2; |
Также мы объявим переменные для хранения значений расстояния от датчиков до препятствий (типа int) и переменные для хранения длительностей распространения ультразвуковой волны (типа long). Также мы объявим переменную для хранения состояния движения.
1 2 3 4 5 6 7 |
long duration1; long duration2; long duration3; int distanceleft; int distancefront; int distanceright; int a=0; |
Далее зададим режимы работы для всех используемых контактов (на ввод или вывод данных) с помощью функции pinMode(). Для считывания информации от инфракрасного датчика мы будем использовать контакт irpin (его мы конфигурируем на ввод данных).
1 2 3 4 5 6 7 |
pinMode(trigPin1, OUTPUT); pinMode(trigPin2, OUTPUT); pinMode(trigPin3, OUTPUT); pinMode(echoPin1, INPUT); pinMode(echoPin2, INPUT); pinMode(echoPin3, INPUT); pinMode(irpin, INPUT); |
Контакты, с которых осуществляется управление драйвером двигателей, конфигурируются на вывод данных (OUTPUT).
1 2 3 4 |
pinMode(4, OUTPUT); pinMode(7, OUTPUT); pinMode(8, OUTPUT); pinMode(12, OUTPUT); |
В основной функции loop программы мы будем определять расстояние до препятствия с помощью ультразвуковых датчиков. Фрагменты кода для всех трех датчиков будут аналогичны. Для определения раасстояния до препятствия мы сначала подаем на контакт trigger датчика уровень LOW на 2 мкс (чтобы "очистить" его). Далее для излучения ультразвуковой волны передатчиком датчика мы подаем на его контакт trigger уровень HIGH на 10 мкс. Далее время, в течение которого распространялась ультразвуковая волна до препятствия и обратно, мы определяем с помощью функции pulseIn() и сохраняем ее в переменной “duration”. У функции pulseIn() два параметра – контакт, на котором производится измерение (echo) и уровень (HIGH или LOW). Если установлен параметр HIGH, то функция ждет пока на контакте не появится состояние HIGH, после появления этого состояния она начинает счет и заканчивает она счет когда на контакте снова будет состояние LOW. Функция возвращает длительность импульса в микросекундах. Для расчета расстояния мы умножаем измеренную длительность на 0.034 (эта константа получена исходя из скорости распространения звука в воздухе – 340 м/с) и делим ее на 2 (потому мы измерили длительность распространения волны до препятствия и обратно). Определенное значение расстояние до препятствия мы сохраняем в соответствующей переменной.
1 2 3 4 5 6 7 |
digitalWrite(trigPin1, LOW); delayMicroseconds(2); digitalWrite(trigPin1, HIGH); delayMicroseconds(10); digitalWrite(trigPin1, LOW); duration1 = pulseIn(echoPin1, HIGH); distanceleft = duration1 * 0.034 / 2; |
После определения расстояния до препятствия для всех ультразвуковых датчиков мы можем управлять двигателями с помощью операторов if. Расстояние, когда препятствие будет считаться близко расположенным к роботу, мы определили равным 15 см – вы можете изменить это значение по своему желанию. К примеру, если левый датчик показываем расстояние меньше или равное 15 см, а расстояния с двух остальных датчиков большие, то мы должны давать команду на поворот робота вправо. Также мы непрерывно проверяем состояние инфракрасного датчика (IR sensor). Если робот находится на полу, то состояние выходного контакта датчика будет равным LOW, в противном случае оно будет равно HIGH. Мы сохраняем это состояние в переменной s – ее значение мы также будем использовать для управления движением робота.
Следующий фрагмент кода используется для движения робота вперед или назад:
1 2 3 4 5 6 7 8 9 |
if(s==HIGH) { digitalWrite(4, LOW); digitalWrite(7, HIGH); digitalWrite(8, LOW); digitalWrite(12, HIGH); delay(1000); a=1; } |
Но с этим методом есть некоторая проблема, что когда робот обнаружит отсутствие пола, он будет двигаться назад, потом снова будет двигаться вперед и, таким образом, зациклится. Для преодоления этой проблемы мы используем переменную "a", в которую мы записываем значение 1, которое будет свидетельствовать о том, что впереди робота нет пола (то есть он стоит на краю лестницы). В дальнейшем мы будем использовать значение этой переменной для формирования условий движения робота.
Таким образом, при обнаружении отсутствии пола робот не будет двигаться вперед – вместо этого он будет двигаться влево, этим и решится проблема его зацикливания.
В представленном далее блоке условий робот проверяет наличие пола и состояние переменной "a". Робот будет двигаться вперед только если все условия выполнены.
1 |
if ((a==0)&&(s==LOW)&&(distanceleft <= 15 && distancefront > 15 && distanceright <= 15) || (a==0)&&(s==LOW)&&(distanceleft > 15 && distancefront > 15 && distanceright > 15)) |
Фрагмент кода для движения робота вправо:
1 2 3 4 |
digitalWrite(4, HIGH); digitalWrite(7, LOW); digitalWrite(8, HIGH); digitalWrite(12, LOW); |
Если робот обнаруживает отсутствие пола, то значение переменной "a" изменяется на 1 и робот будет двигаться влево. После поворота налево значение переменной 'a' изменяется.
1 2 3 4 5 6 7 8 9 |
if ((a==1) &&(s==LOW) ||(s==LOW) && (distanceleft <= 15 && distancefront <= 15 && distanceright > 15) || (s== LOW) && (distanceleft <= 15 && distancefront <= 15 && distanceright > 15) || (s==LOW) && (distanceleft <= 15 && distancefront > 15 && distanceright > 15) || (distanceleft <= 15 && distancefront > 15 && distanceright > 15)) { digitalWrite(4, HIGH); digitalWrite(7, LOW); digitalWrite(8, LOW); digitalWrite(12, HIGH); delay(100); a=0; } |
Фрагмент кода для движения робота влево:
1 2 3 4 5 6 7 |
if ((s==LOW)&&(distanceleft > 15 && distancefront <= 15 && distanceright <= 15) ||(s==LOW)&& (distanceleft > 15 && distancefront > 15 && distanceright <= 15) ||(s==LOW)&& (distanceleft > 15 && distancefront <= 15 && distanceright > 15) ) { digitalWrite(4, LOW); digitalWrite(7, HIGH); digitalWrite(8, HIGH); digitalWrite(12, LOW); } |
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
// defining the pins const int trigPin1 = 3; const int echoPin1 = 5; const int trigPin2 = 6; const int echoPin2 = 9; const int trigPin3 = 10; const int echoPin3 = 11; int irpin =2; // defining variables long duration1; long duration2; long duration3; int distanceleft; int distancefront; int distanceright; int a=0; void setup() { pinMode(trigPin1, OUTPUT); pinMode(trigPin2, OUTPUT); pinMode(trigPin3, OUTPUT);// Sets the trigPin as an Output pinMode(echoPin1, INPUT); // Sets the echoPin as an Input pinMode(echoPin2, INPUT); pinMode(echoPin3, INPUT); pinMode(irpin, INPUT); pinMode(4, OUTPUT); pinMode(7, OUTPUT); pinMode(8, OUTPUT); pinMode(12, OUTPUT); } void loop() { digitalWrite(trigPin1, LOW); delayMicroseconds(2); digitalWrite(trigPin1, HIGH); delayMicroseconds(10); digitalWrite(trigPin1, LOW); duration1 = pulseIn(echoPin1, HIGH); distanceleft = duration1 * 0.034 / 2; Serial.print("Distance1: "); Serial.println(distanceleft); digitalWrite(trigPin2, LOW); delayMicroseconds(2); digitalWrite(trigPin2, HIGH); delayMicroseconds(10); digitalWrite(trigPin2, LOW); duration2 = pulseIn(echoPin2, HIGH); distancefront = duration2 * 0.034 / 2; Serial.print("Distance2: "); Serial.println(distancefront); digitalWrite(trigPin3, LOW); delayMicroseconds(2); digitalWrite(trigPin3, HIGH); delayMicroseconds(10); digitalWrite(trigPin3, LOW); duration3 = pulseIn(echoPin3, HIGH); distanceright = duration3 * 0.034 / 2; Serial.print("Distance3: "); Serial.println(distanceright); int s = digitalRead(irpin); if(s==HIGH) { digitalWrite(4, LOW); digitalWrite(7, HIGH); digitalWrite(8, LOW); digitalWrite(12, HIGH); delay(1000); a=1; } if ((a==0)&&(s==LOW)&&(distanceleft <= 15 && distancefront > 15 && distanceright <= 15) || (a==0)&&(s==LOW)&&(distanceleft > 15 && distancefront > 15 && distanceright > 15)) { digitalWrite(4, HIGH); digitalWrite(7, LOW); digitalWrite(8, HIGH); digitalWrite(12,LOW); } if ((a==1)&&(s==LOW)||(s==LOW)&&(distanceleft <= 15 && distancefront <= 15 && distanceright > 15)||(s==LOW)&&(distanceleft <= 15 && distancefront <= 15 && distanceright > 15)||(s==LOW)&& (distanceleft <= 15 && distancefront > 15 && distanceright > 15)||(distanceleft <= 15 && distancefront > 15 && distanceright > 15)) { digitalWrite(4, HIGH); digitalWrite(7, LOW); digitalWrite(8, LOW); digitalWrite(12, HIGH); delay(100); a=0; } if ((s==LOW)&&(distanceleft > 15 && distancefront <= 15 && distanceright <= 15) ||(s==LOW)&& (distanceleft > 15 && distancefront > 15 && distanceright <= 15) ||(s==LOW)&& (distanceleft > 15 && distancefront <= 15 && distanceright > 15) ) { digitalWrite(4, LOW); digitalWrite(7, HIGH); digitalWrite(8, HIGH); digitalWrite(12, LOW); } } |