Создание робота, способного проходить лабиринты, — это интересный способ погрузиться в мир робототехники и автономных систем. Представьте себе, что вы создаете небольшое устройство, которое может интеллектуально перемещаться по сложным лабиринтам, принимая решения на каждом шагу. Это пошаговое руководство покажет вам, как собрать собственного робота для прохождения лабиринтов используя плату Arduino UNO, три ИК-датчика и другие простые компоненты, которые можно легко найти в любом магазине электроники.

Также на нашем сайте вы можете посмотреть и другие похожие проекты роботов на основе Arduino:
- робот на Arduino следующий вдоль линии;
- объезжающий препятствия робот на Arduino;
- робот на Arduino следующий за человеком.
Необходимые компоненты
- Плата Arduino Uno (купить на AliExpress).
- Плата расширения для управления двигателями на Arduino UNO.
- Инфракрасные датчики - 3 шт. (купить на AliExpress).
- Шасси автомобиля с приводом на два колеса.
- Колесико на ролике.
- Моторы Bo с соответствующими колесами - 2 шт.
- Литий-ионный аккумуляторный блок 2S с BMS.
- Соединительные провода.
- Винты, гайки и прокладки.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Источник питания
Хотя источник питания достаточно гибкий, рекомендуется использовать напряжение от 5 до 12 В. Батарея на 9 В также подойдет, но любое напряжение выше 5 В и ниже 12 В идеально подходит для обеспечения стабильного питания, особенно для двигателей. Более высокое напряжение гарантирует, что даже если двигатели потребляют значительную мощность, падение напряжения не повлияет на функциональность Arduino (что может привести к перезапуску).
Шасси
Для шасси можно использовать готовое или даже выбрать картонное шасси в качестве быстрой и недорогой альтернативы. Остальные компоненты просты и обычно встречаются в наборах для робототехники.
Имея эти компоненты, вы готовы начать сборку своего робота!
Что представляет собой робот для прохождения лабиринтов
Это робот, предназначенный для автономного перемещения по лабиринту и поиска выхода. Он использует датчики, такие как ультразвуковые, инфракрасные или камеры, для обнаружения препятствий и составления карты окружающей местности. Затем робот применяет различные алгоритмы, такие как поиск в глубину (Depth First Search, DFS), поиск в ширину (Breadth First Search, BFS) или следование вдоль стен, чтобы составить карту лабиринта и выбрать оптимальный путь. Обычно он перемещается на основе обратной связи в реальном времени и принимает решения, чтобы избегать стен и достичь пункта назначения.

В этом проекте мы будем создавать простого робота для прохождения лабиринтов на основе Arduino. Робот использует ИК-датчик для обнаружения лабиринта и алгоритм, называемый правилом «рука о стену», для навигации по лабиринту и поиска выхода. Обратите внимание, что мы использовали линии для создания лабиринта вместо строительства стен. Как показано на GIF-анимации выше, мы можем легко создать несколько вариантов лабиринтов и протестировать робота. Такие роботы, осуществляющие прохождение лабиринтов по прямой линии, также называются роботами, следующими по линии.
Алгоритм робота для прохождения лабиринтов
Одним из важнейших аспектов создания робота для прохождения лабиринтов является выбор подходящего алгоритма для конкретного типа лабиринта. Разные типы лабиринтов требуют индивидуального подхода для обеспечения эффективной навигации. Для простого линейного лабиринта в этом проекте мы будем использовать алгоритм «Правило руки на стене», также известный как правило левой или правой руки.
Этот алгоритм хорошо работает в лабиринтах со соединенными стенами, где путь решения существует в виде непрерывной границы. Ниже приведен пример такого лабиринта:

Без цветового разделения (например, оранжевого и зеленого) лабиринт может показаться запутанным. Однако, если четко обозначены пути, навигация становится простой. Алгоритм «Правило руки о стену» особенно полезен для лабиринтов с четко определенными границами и сплошными стенами. Он также идеально подходит для роботов с ограниченной вычислительной мощностью или памятью, поскольку не требует от робота «запоминания» своего пути.
Этот подход имитирует поведение человека при перемещении в темных или незнакомых пространствах, где прикосновение к стене дает направление и ощущение границ. Просто, эффективно и гениально!
Надеюсь, вы поняли основную концепцию этого алгоритма.
Основная логика работы робота
В представленном ниже GIF-видео вы можете увидеть как наш робот проходит лабиринт, используя правило левой руки. Этот выбор был сделан потому, что в данном конкретном лабиринте применение правила правой руки привело бы к тому, что робот не достиг бы цели. Вместо этого он бы бесконечно ходил по кругу.

Это ограничение возникает из-за того, что робот использует всего три датчика. С большим количеством датчиков можно было бы успешно пройти лабиринт, используя и правило правой руки. Однако для простоты мы выбрали конструкцию всего с тремя датчиками.
Благодаря тому, что количество датчиков ограничено тремя, роботу требуется лишь небольшой набор возможных решений, что упрощает и делает более доступным проектирование и программирование. Ниже мы рассмотрим таблицу истинности, используемую в этом роботе, чтобы определить его поведение в различных сценариях.

Схема проекта
Поскольку мы используем плату расширения для управления двигателями Arduino, схема подключения упрощается, так как не требуется специального соединения между платой расширения и Arduino UNO. Схема робота для прохождения лабиринтов на основе платы Arduino представлена на следующем рисунке.
Поскольку мы используем плату расширения для двигателей версии 1, мы ограничены использованием аналоговых выводов. Поэтому я использую A0, A1 и A2 в качестве цифровых входов для левого, переднего и правого датчиков. Это единственные выводы с отдельными контактными площадками, что упрощает пайку. Левый и правый двигатели подключены к выходам M1 и M2 платы расширения. Во время программирования вы можете проверить направление вращения двигателей. Если потребуется внести какие-либо изменения, вы можете просто изменить полярность двигателей.
Наконец, у нас есть источник питания, который имеет решающее значение. Мы используем старый аккумуляторный блок 2S, обеспечивающий выходное напряжение от 5,6 В до 8,4 В. Этого более чем достаточно для нашего проекта. Если вы выберете другой двигатель, вам, возможно, потребуется заменить источник питания.

Кроме того, необходимо убедиться в правильном подключении к выводам PWR модуля управления двигателем, как показано на изображении выше. Эти выводы отвечают за подключение входа батареи к выводу Vin платы Arduino. Если это подключение отсутствует, плата Arduino UNO не будет получать питание.
Сборка конструкции робота
Если вы используете те же компоненты, что и я, то особых трудностей при сборке робота не возникнет. Однако, если вы заменяете компоненты на более совершенные, могут потребоваться небольшие доработки.
Ещё один важный момент, на который следует обратить внимание, — это расстояние между каждым датчиком и его положением. Неправильное размещение может привести к некорректным движениям. Кроме того, потребуется калибровка, чтобы обеспечить правильную работу робота.

На фотографии выше вы можете увидеть собранного робота. Процесс сборки прост и понятен. Датчики размещаются на правильном расстоянии и проверяются в ходе тестовых запусков. Датчики подключаются с помощью проводов, которые припаиваются к модулю управления двигателем.
Объяснение работы кода программы
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Робот для прохождения лабиринтов использует ИК-датчики для обнаружения препятствий и применяет алгоритм логического принятия решений для навигации по лабиринту. Движение робота управляется двигателями постоянного тока, подключенными к плате Arduino Motor Shield V1. Обратите внимание, что приведенный код предназначен только для поиска выхода из лабиринта и не предназначен для поиска кратчайшего пути. Однако, при желании, вы можете реализовать собственный алгоритм для поиска кратчайшего пути.
Используемые библиотеки
AFMotor: Эта библиотека управляет двигателями постоянного тока с помощью платы Arduino Motor Shield V1. Она упрощает управление двигателями благодаря таким функциям, как setSpeed() и run() .
|
1 2 3 |
#include <AFMotor.h> AF_DCMotor motorA(1); AF_DCMotor motorB(2); |
Константы и переменные
ИК-датчики подключены к аналоговым выводам A0, A1 и A2, которые соответствуют левому, переднему и правому датчикам соответственно. Скорость вращения двигателей и задержки поворота также задаются с помощью констант для точного управления движением.
|
1 2 3 4 5 6 7 |
const int leftSensor = A0; const int frontSensor = A1; const int rightSensor = A2; const int forwardSpeed = 120; const int TurningSpeed = 115; const int turnDelay = 25; const int uTurnDelay = 50; |
Функция настройки
Функция setup() инициализирует ИК-датчики как устройства ввода для обнаружения препятствий и устанавливает последовательную связь для целей отладки.
|
1 2 3 4 5 6 |
void setup() { pinMode(leftSensor, INPUT); pinMode(frontSensor, INPUT); pinMode(rightSensor, INPUT); Serial.begin(9600); } |
Главный цикл
Функция loop() непрерывно считывает данные с ИК-датчиков и определяет движения робота, используя логику switch-case. Значения датчиков (0 или 1) объединяются в двоичное представление для определения текущего состояния лабиринта (например, путь доступен впереди, налево, направо или пути нет). В зависимости от состояния вызываются специальные функции управления движением, такие как moveForward(), turnLeft(), turnRight() или uTurn().
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
void loop() { int leftValue = digitalRead(leftSensor); int frontValue = digitalRead(frontSensor); int rightValue = digitalRead(rightSensor); int sensorState = (leftValue << 2) | (frontValue << 1) | rightValue; switch (sensorState) { case 0b000:uTurn();break; case 0b010:moveForward();break; case 0b111:turnLeft();break; case 0b100:turnLeft(); break; case 0b110:turnLeft();break; case 0b001:turnRight();break; case 0b011:turnRight();break; case 0b101:stopMotors();break; default:stopMotors();break; } } |
Принцип работы робота для прохождения лабиринтов
После загрузки кода в собранного робота через среду разработки Arduino IDE настаёт время тестирования. Полное видео работы этого робота, вы найдете внизу этой страницы. Но чтобы понять что происходит, давайте рассмотрим всё пошагово.
Шаг 1: Исходное положение
- Робот начинает движение от входа в лабиринт.
- Оно следует правилу левой руки, то есть всегда будет прижимать левый датчик или боковую сторону к стене.

Шаг 2: Принятие решения на перекрестке
- Первый перекресток представляет собой трехстороннее движение.
- Согласно алгоритму, робот выбирает левый путь.
- Если на перекрестке есть только правый и левый въезды, приоритет всегда отдается левой стороне.

Шаг 3: Повороты на углах
- Далее робот преодолевает два правых поворота.
- Поскольку оба угла загнуты вправо, робот движется вправо, как и ожидалось.

Шаг 4 : Принятие решения на втором перекрестке
- Следующий перекресток также представляет собой трехсторонний перекресток, но на этот раз пути прямые и ведут налево.
- Поскольку правило левой руки соблюдается, робот поворачивает налево.

Шаг 5: Поворот на повороте
Как и в предыдущих поворотах, робот повернет за угол в правильном направлении.

Шаг 6: Заключительное движение
- Последний перекресток похож на первый, предлагая как правый, так и левый путь.
- Как и ожидалось, робот выбирает левый путь, ведущий к месту назначения.

В ходе тестирования вы заметите, что робот движется не по идеально прямой линии. Эта проблема возникает из-за неравномерности скоростей левого и правого двигателей. Для решения этой проблемы можно выбрать метод полного привода для лучшей балансировки и устойчивости.
Вы можете сопоставить движения робота с таблицей истинности, предоставленной в коде. Робот работает как ожидалось, успешно проходя лабиринт.
Репозиторий GitHub с кодом и схемой
Полный код этого проекта робота, решающего лабиринты, приведен внизу этой страницы. Кроме того, здесь же вы найдете ссылку на наш репозиторий GitHub, где вы найдете исходный код этого проекта.
Заключение и дальнейшие улучшения
Если вы дошли до этого момента, похвалите себя, ведь вы успешно собрали простого робота для решения лабиринтов. Но самое замечательное в этом проекте — это то, что его легко масштабировать и экспериментировать со сложными алгоритмами. Сделайте перерыв и поиграйте с этим роботом, создайте различные варианты лабиринтов и посмотрите, как он их решает. Вы скоро поймете, что этот робот не способен решать сложные лабиринты и уж точно не является самым быстрым роботом для решения лабиринтов. Теперь вы можете попробовать себя в роли программиста и запрограммировать этого робота на более быстрое решение лабиринтов и даже поиск кратчайшего пути. Это также позволит вам участвовать в соревнованиях роботов по решению лабиринтов, где вы сможете бросить вызов себе и своему роботу, решая сложные лабиринты и соревнуясь с другими инженерами.
Полный код проекта
|
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
//Arduino Code for Maze Solving Robot Project #include <AFMotor.h> // Include the Adafruit Motor Shield library for motor control // Motor Definitions AF_DCMotor motorA(1); // Motor A connected to terminal M1 on the motor shield AF_DCMotor motorB(2); // Motor B connected to terminal M2 on the motor shield // Sensor Pin Definitions const int leftSensor = A0; // Left IR sensor pin const int frontSensor = A1; // Front IR sensor pin const int rightSensor = A2; // Right IR sensor pin // Movement Parameters const int forwardSpeed = 120; // Speed for forward movement const int TurningSpeed = 115; // Speed for turning movements const int turnDelay = 25; // Delay for completing a turn const int uTurnDelay = 50; // Delay for completing a U-turn void setup() { // Configure sensor pins as input pinMode(leftSensor, INPUT); pinMode(frontSensor, INPUT); pinMode(rightSensor, INPUT); // Initialize serial communication for debugging Serial.begin(9600); } void loop() { // Read sensor values (0 or 1) int leftValue = digitalRead(leftSensor); int frontValue = digitalRead(frontSensor); int rightValue = digitalRead(rightSensor); // Combine sensor states into a single value for switch-case logic int sensorState = (leftValue << 2) | (frontValue << 1) | rightValue; // Decision-making based on sensor states switch (sensorState) { case 0b000: // No sensors detect a wall uTurn(); // Perform a U-turn Serial.println("Stop"); break; case 0b010: // Only the front sensor detects a wall moveForward(); // Move forward Serial.println("Move Forward"); break; case 0b111: // All sensors detect walls turnLeft(); // Turn left Serial.println("Turn Left"); break; case 0b100: // Only the left sensor detects a wall turnLeft(); // Turn left Serial.println("Turn Left"); break; case 0b110: // Front and left sensors detect walls turnLeft(); // Turn left Serial.println("Turn Left"); break; case 0b001: // Only the right sensor detects a wall turnRight(); // Turn right Serial.println("Turn Right"); break; case 0b011: // Front and right sensors detect walls turnRight(); // Turn right Serial.println("Turn Right"); break; case 0b101: // Left and right sensors detect walls stopMotors(); // Stop the motors Serial.println("Turn Left"); break; default: // Unknown sensor state stopMotors(); // Stop the motors as a safety measure Serial.println("Unknown State"); break; } } // Function to move forward void moveForward() { motorA.setSpeed(forwardSpeed); // Set speed for motor A motorB.setSpeed(forwardSpeed); // Set speed for motor B motorA.run(FORWARD); // Move motor A forward motorB.run(FORWARD); // Move motor B forward } // Function to turn left void turnLeft() { motorA.setSpeed(TurningSpeed - 20); // Reduce speed of motor A for smoother turn motorB.setSpeed(TurningSpeed); // Set speed for motor B motorA.run(BACKWARD); // Move motor A backward motorB.run(FORWARD); // Move motor B forward delay(turnDelay); // Delay to complete the turn } // Function to turn right void turnRight() { motorA.setSpeed(TurningSpeed); // Set speed for motor A motorB.setSpeed(TurningSpeed - 20); // Reduce speed of motor B for smoother turn motorA.run(FORWARD); // Move motor A forward motorB.run(BACKWARD); // Move motor B backward delay(turnDelay); // Delay to complete the turn } // Function to stop the motors void stopMotors() { motorA.run(RELEASE); // Release motor A motorB.run(RELEASE); // Release motor B } // Function to perform a U-turn void uTurn() { motorA.setSpeed(TurningSpeed); // Set speed for motor A motorB.setSpeed(TurningSpeed); // Set speed for motor B motorA.run(FORWARD); // Move motor A forward motorB.run(BACKWARD); // Move motor B backward delay(uTurnDelay); // Delay to complete the U-turn } |





