Робот-жук на ESP32, который ползает по стенам и потолку

Перевод статьи We Built an ESP32 Wall Climbing Beetle Robot — Circuit Digest.
Настенные роботы бывают самые разные — от любительских до серьёзных, применяемых для инспекции, наблюдения и очистки. Обычно они опираются на крупные моторы от дронов, пятидюймовые пропеллеры и увесистые липо-аккумуляторы, чтобы создать достаточную удерживающую силу для вертикальных поверхностей.
В этом проекте мы бросаем себе вызов и делаем куда более компактную версию. Задача — построить лёгкого настенного робота, который помещается в ладонь, но при этом развивает достаточную присасывающую силу, чтобы карабкаться по стенам и ползать по потолку. И вместо того чтобы он выглядел как очередной робот, мы придадим ему вид настоящего жука с помощью лёгкого корпуса, напечатанного на 3D-принтере.
Что такое настенный робот#
Настенный робот — это робот, способный удерживаться и перемещаться по вертикальным или перевёрнутым поверхностям вроде стен и потолков. Держаться на поверхности он может за счёт вакуумных насосов, магнитного притяжения (на железных поверхностях) или вращающихся пропеллеров, создающих зону низкого давления под днищем. Промышленные версии применяются для инспекций, очистки резервуаров и разведки в местах, недоступных человеку. Здесь мы пробуем построить такого робота размером с небольшого жука под управлением ESP32.
Чем хорош этот вариант#
- Лёгкий и компактный. Весит около 102 граммов и помещается в руке.
- Никакого клея и липких следов. Робот не портит стены и потолки, поскольку работает на воздухе, а не на клейких веществах.
- Нужен всего один мотор. Он выдаёт более 360 г удерживающей силы одним бесколлекторным двигателем — экономия и энергии, и сложности.
- Беспроводное управление. Через собственный веб-сервер ESP32 из браузера телефона.
- Низкая цена. Собирается из широко доступных деталей.
Компоненты#

Компоненты для сборки настенного робота
| № | Компонент | Назначение | Кол-во |
|---|---|---|---|
| 1 | Бесколлекторный двигатель 4300KV | Создаёт присасывающую силу | 1 |
| 2 | Пропеллер 3 дюйма | Основной пропеллер | 1 |
| 3 | Плата ESP32 | Главный контроллер | 1 |
| 4 | Моторы N20, 100 об/мин | Обеспечивают движение | 2 |
| 5 | Лёгкие колёса | С резиновыми ободами для лучшего сцепления | 4 |
| 6 | Регулятор хода 2S, 6–8 А | Управляет бесколлекторным мотором | 1 |
| 7 | Li-Po аккумулятор 7,4 В, 350 мА·ч | Питание всего робота | 1 |
| 8 | Драйвер моторов MX1508 | Управляет моторами N20 | 1 |
| 9 | Понижающий преобразователь MP1584 | Даёт стабильные 5 В для ESP32 | 1 |
Как работает присасывание#
Вместо клейких накладок или вакуумных насосов робот использует высокооборотный бесколлекторный мотор с пропеллером, создающий область низкого давления под шасси. Вращаясь, пропеллер непрерывно вытягивает воздух из-под робота и выбрасывает его вверх. Возникает разница давлений между внутренним объёмом корпуса и окружающей атмосферой: более высокое наружное давление плотно прижимает робота к стене или потолку, создавая нужную удерживающую силу. Чем больше разница давлений, тем сильнее прижим.
Измеряем удерживающую силу#
Всю сборку положили на весы. Весы автора показывают положительные значения, когда груз лежит сверху, и отрицательные, когда платформу тянут вверх. После взвешивания всей электроники — аккумулятора, платы ESP32, бесколлекторного мотора, регулятора хода и ходовых моторов — суммарный вес составил примерно 137 граммов.
Для надёжного лазания присасывающая сила должна быть заметно больше собственного веса робота. Безопасное и оптимальное значение — примерно двукратный вес, то есть наша цель минимум 274 грамма. После испытаний и оптимизации конструкции получили силу присасывания 360 граммов.
Схема подключения#

Схема настенного робота-жука
Мозг робота — плата ESP32. Моторы N20 подключены к выходам драйвера MX1508, четыре входа которого заведены на выводы GPIO 16, 17, 18 и 19. Регулятор хода 2S подключён к GPIO 4.
Понижающий преобразователь MP1584 подключён напрямую к аккумулятору 7,4 В / 350 мА·ч. Выход 5 В питает и ESP32, и драйвер моторов. Регулятор хода 2S тоже подключён напрямую к аккумулятору, чтобы получить всю доступную мощность.
3D-модель жука#

Виды 3D-модели сверху и снизу
Нижняя часть, то есть шасси, на котором смонтированы все компоненты, имеет толщину 0,5 мм. Изначально колёса взяли от старой игрушечной машинки, но затем ради снижения веса напечатали свои толщиной 0,5 мм, переставив резиновые ободья для лучшего сцепления со стенами и потолком. В качестве оси передних колёс использован отрезок обычного прутка 2 мм, задние колёса насажены прямо на валы моторов. Рога и лапы жука приклеены к основному корпусу; рога выглядят как две отдельные детали, но для простоты спроектированы единой частью — так прочнее.
Верхняя часть имеет толщину 0,4 мм. Чтобы аккуратно состыковать верх и низ, в нижней детали сделаны пазы под отдельные полосы верхней части: вставляем полосы в соответствующие пазы, наносим немного клея — и детали соединены.
Проектирование верхнего панциря#

Превращение узора панциря в паттерн Вороного
Самая интересная часть проекта — дизайн верхней части жука; здесь понадобились и навыки проектирования, и механические знания, и немного творчества. В инженерии многие решения подсмотрены у природы, вот и мы решили повторить узоры настоящего жука. Изучив разные варианты, сделав наброски от руки и посоветовавшись с ChatGPT, выбрали сотовый узор и превратили его в паттерн Вороного.
Процесс был такой: сначала 3D-модель в Autodesk Fusion, затем перевод в поверхностную модель, импорт в Autodesk Meshmixer, где дизайн разворачивался в плоскость. Развёртку снова импортировали в Fusion, исправили наложения, возникшие при разворачивании, подогнали размер под реальный и отправили на печать. После печати панцирь смонтировали на нижнюю часть и проклеили стыки.
Печать корпуса#

Панцирь напечатан двумя половинами
Печатать изогнутый узор непросто: толщина всего 0,5 мм, требуется много поддержек, чтобы держать изгибы, и велик шанс сломать деталь при их удалении. После неудачной первой печати автор решил развернуть дизайн в плоскость, напечатать, а затем вернуть нужную форму нагревом. Развёртка оказалась слишком большой для стола принтера, поэтому её разрезали пополам — на фото красный панцирь состоит из двух половин, которые потом склеили. Остальные детали печатались чёрным.
Сборка#

Полная сборка робота-жука
В сердце конструкции — бесколлекторный мотор с пропеллером. ESP32 и аккумулятор сдвинуты максимально к центру, чтобы уменьшить опрокидывающий момент. Понижающий преобразователь и драйвер моторов закреплены поверх хомутов крепления моторов N20. Провода-перемычки из предыдущей версии заменили обычными проводами и аккуратно уложили.
После сборки робот стал похож на жука, оставшись достаточно лёгким для лазания. Готовая машина успешно взбиралась по вертикальным стенам и ползала по потолку, показав, как аккуратная оптимизация развесовки, конструкции и потока воздуха даёт работоспособного миниатюрного настенного робота.
Веб-приложение для управления#

Мобильный веб-интерфейс управления
После заливки кода в ESP32 через Arduino IDE монитор порта покажет IP-адрес платы. Введите его в браузере — откроется простой интерфейс. Сверху идёт управление бесколлекторным мотором, ниже — управление моторами N20. Для движения вперёд оба мотора крутятся против часовой стрелки, назад — по часовой; для поворота налево левый мотор идёт против часовой, правый по часовой, направо — наоборот. Кнопка Stop останавливает оба мотора.
Разбор кода#
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "wifi_name";
const char* password = "wifi_password";
WebServer server(80);
// Регулятор хода
const int escPin = 4;
const int pwmFreq = 50;
const int pwmResolution = 16;
// MX1508
const int L1 = 16;
const int L2 = 17;
const int R1 = 18;
const int R2 = 19;
bool bldcEnabled = false;
int throttle = 0;
uint32_t usToDuty(uint16_t us)
{
uint32_t maxDuty = (1UL << pwmResolution) - 1;
return (uint32_t)((us / 20000.0) * maxDuty);
}
Подключены библиотеки, настроены Wi-Fi и выводы GPIO под бесколлекторный мотор и моторы N20. Функция usToDuty переводит длительность импульса в микросекундах в соответствующую скважность ШИМ, которую ждёт регулятор хода.
void setThrottle(int percent)
{
if (!bldcEnabled)
{
ledcWrite(escPin, usToDuty(1000));
return;
}
percent = constrain(percent, 0, 100);
uint16_t pulse = map(percent, 0, 100, 1000, 2000);
ledcWrite(escPin, usToDuty(pulse));
}
Эта функция управляет скоростью бесколлекторного мотора в диапазоне от 0 до 100%.
void stopRobot()
{
digitalWrite(L1, LOW);
digitalWrite(L2, LOW);
digitalWrite(R1, LOW);
digitalWrite(R2, LOW);
}
void forward()
{
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
}
void backward()
{
digitalWrite(L1, LOW);
digitalWrite(L2, HIGH);
digitalWrite(R1, LOW);
digitalWrite(R2, HIGH);
}
void left()
{
digitalWrite(L1, LOW);
digitalWrite(L2, HIGH);
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
}
void right()
{
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
digitalWrite(R1, LOW);
digitalWrite(R2, HIGH);
}
Эти функции отвечают за остановку и движение вперёд, назад, влево и вправо, переключая соответствующие выводы GPIO в высокий и низкий уровень.
Функция handleRoot() формирует HTML-страницу с кнопками управления роботом.
void handleSpeed()
{
throttle = server.arg("value").toInt();
setThrottle(throttle);
server.send(200, "text/plain", "OK");
}
void handleBLDCOn()
{
bldcEnabled = true;
setThrottle(throttle);
server.send(200, "text/plain", "ON");
}
Первая функция выполняется, когда браузер отправляет запрос к /speed: значение скорости извлекается, приводится к целому и передаётся в setThrottle(), обновляя газ регулятора хода. При нажатии кнопки ON переменная bldcEnabled разрешает регулятору принимать значения газа.
Функция setup() инициализирует последовательный порт, настраивает выводы моторов на выход, печатает выданный IP-адрес в монитор порта и запускает веб-сервер. А loop() просто непрерывно проверяет, не пришёл ли новый HTTP-запрос:
void loop()
{
server.handleClient();
}
Результат#

Готовый робот карабкается по вертикальной стене
Несмотря на компактность, робот развивает более 360 граммов удерживающей силы, весит около 104 граммов и способен ползать по стенам и потолку с помощью всего одного бесколлекторного мотора. Проект показывает и итеративный инженерный подход: несколько неудачных прототипов в итоге привели к работающей конструкции. По пути пригодились испытания присасывания, оптимизация веса, визуализация концепта с помощью ИИ, моделирование по Вороному и разворачивание сетки в плоскость.
Если что-то не работает#
Робот сильно вибрирует на потолке. Не хватает присасывания — увеличьте его ползунком в веб-приложении, это плотнее прижмёт жука к потолку.
Бесколлекторный мотор постоянно перезапускается, пищит стартовую последовательность и перезагружается. Скорее всего, разряжен аккумулятор и он не может обеспечить мотору нужную мощность. Зарядите батарею.
Робот собран, но не держится на стенах и потолке. Проверьте зазоры колёс: диаметр колёс должен быть минимальным, чтобы расстояние от корпуса до поверхности было как можно меньше.
Частые вопросы#
Можно ли поставить аккумулятор большей ёмкости? Да, но нужно соблюдать баланс между весом батареи и максимальным присасыванием, которое даёт мотор.
Сколько робот работает от заряда? Сейчас около 8 минут. Время можно увеличить более продуманной конструкцией, лучшим аккумулятором или другим бесколлекторным мотором.
Заберётся ли он по неровной поверхности? Да, и по неровным, и по обычным поверхностям он поднимается без труда.
Заключение#
Проект показывает, что компактный настенный робот — это не только мощный мотор, но и аккуратный баланс воздушного потока, веса конструкции и механического дизайна. Перепробовав несколько вариантов корпуса, оптимизировав толщину стенок и разработав лёгкий панцирь по мотивам диаграммы Вороного, удалось собрать робота размером с ладонь, способного карабкаться по стенам и ползать по потолку.
Место для улучшений ещё есть — заказная плата, более лёгкая электроника, продвинутое управление, — но этот прототип служит хорошей основой для будущих версий.