Управление жестами — один из самых крутых способов взаимодействия с технологиями: никаких кнопок, никаких сенсорных экранов, только простые движения рук. Это почти волшебство, когда можно управлять устройствами взмахом руки! Эта технология имеет широкий спектр практического применения: от управления дронами и роботами до управления устройствами умного дома и даже помощи людям с ограниченными возможностями. Вместо нажатия кнопок или голосовых команд вы можете просто двигать рукой определенным образом, чтобы отдавать команды.
В этом проекте мы создадим простую аэромышь на базе ESP32, используя сенсор MPU6050 и микроконтроллер ESP32. MPU6050 будет отслеживать движения руки в реальном времени, а ESP32 будет обрабатывать данные и передавать их по беспроводной связи через Bluetooth или Wi-Fi, что упростит управление различными устройствами. Мы продемонстрируем этот проект, используя Processing для перемещения шарика по экрану, но вы можете использовать его для самых разных задач.
Также на нашем сайте вы можете посмотреть другие проекты, связанные с распознванием жестов:
- управление компьютером с помощью жестов рук и Arduino;
- управляемая жестами роботизированная рука на Arduino Nano;
- распознавание жестов с помощью Raspberry Pi и OpenCV;
- управляемый жестами видеоплеер на Raspberry Pi и MediaPipe;
- умный дом на ESP12 с управлением жестами и компьютерном зрении;
- модуль распознавания жестов PAJ7620 и как его подключить к Arduino.
Необходимые компоненты
- Плата разработки ESP32 (купить на AliExpress).
- Модуль акселерометра и гироскопа MPU6050 (купить на AliExpress).
- Фоторезистор.
- Резистор 56 кОм.
- Литий-полимерный аккумулятор.
- Перфорированная плата (при необходимости).
Реклама: ООО «АЛИБАБА.КОМ (РУ)» ИНН: 7703380158
Обзор проекта
Этот проект посвящен созданию устройства управления жестами рук с использованием ESP32 и датчика MPU6050. MPU6050 выступает в качестве основного датчика, обнаруживающего движения по осям X и Y, аналогично тому, как мышь отслеживает движение курсора по осям X и Y. Кроме того, в устройство включен фоторезистор (LDR), имитирующий щелчок мыши. Если вы совершенно не знакомы с MPU6050, ознакомьтесь с нашим руководством по использованию MPU6050 с ESP32 .
Основная идея заключается в использовании этого устройства для управления движением объектов, расположенных в плоскости XY. Например, с помощью жестов рук можно управлять радиоуправляемой машинкой, дроном или без усилий перемещать роботизированные системы.
В отличие от традиционных методов управления, основанных на кнопках, джойстиках или голосовых командах, управление жестами предлагает более интуитивный и захватывающий опыт. Акселерометр и гироскоп внутри MPU6050 точно отслеживают движения рук. Затем ESP32 обрабатывает эти данные, применяет фильтр Калмана для более плавного отслеживания движений и передает информацию по беспроводной связи через Bluetooth или Wi-Fi на управляемое устройство.
Плата разработки ESP32 имеет несколько вариантов. Вы можете использовать любой из них, но расположение контактов GPIO может отличаться, поэтому убедитесь, что используете правильные. Аналогично, для модуля MPU6050 существуют разные варианты, поэтому используйте правильную распиновку.
Что касается батареи, то выбор зависит от ваших потребностей. Такие факторы, как время автономной работы, наличие свободного места и бюджет, определяют ваше решение. В моем случае я использую небольшую литий-полимерную батарею, которая помещается в нижнюю часть платы ESP32, жертвуя при этом временем автономной работы ради компактности.
Дополнительные компоненты
- Выключатель питания (ползунок SPDT).
-
Застежки-липучки – по мере необходимости.
Ползунковый переключатель SPDT используется для включения и выключения цепи, а застежки-липучки помогают надежно закрепить устройство в руке для комфортного использования. Теперь перейдем к самой интересной части — соединениям цепи.
Схема проекта
Схема беспроводной мыши на ESP32 и MPU6050 представлена на следующем рисунке.
Как видите, схема достаточно проста. Начиная с источника питания, я подключаю LiPo-аккумулятор напрямую к 5-вольтовой шине платы разработки ESP32. Это связано с тем, что там используется стабилизатор напряжения LDO, например, AMS1117-3.3, основная задача которого — стабилизировать входное напряжение до 3,3 В, которое затем подается на ESP32. Таким образом, для этой работы нормально использовать LiPo-аккумулятор с максимальным напряжением 4,2 В, который можно напрямую подключить к 5-вольтовой шине ESP32. Поскольку я хотел сделать схему переключаемой, я добавил переключатель — двухпозиционный ползунковый переключатель (SPDT).
Далее следует схема с фоторезистором (LDR), состоящая из резистора 56 кОм. Здесь резистор и фоторезистор в совокупности образуют традиционную схему делителя напряжения. Проще говоря, сопротивление фоторезистора изменяется в зависимости от интенсивности света. Когда сопротивление одной стороны делителя напряжения изменяется, выходное напряжение также изменяется. Это изменяющееся напряжение затем подается на АЦП ESP32 и служит входным сигналом. В нашем случае, поскольку фоторезистор находится у нас в руке, он выдает значение 1 в темноте (когда он закрыт) и 0 при воздействии большего количества света (когда рука открыта). Это можно использовать для включения и выключения управляемого устройства.
Далее, выводы SCL и SDA микроконтроллера MPU6050 подключаются к выводам I2C микроконтроллера ESP32, а именно к выводам GPIO21 (SDA) и GPIO22 (SCL). Питание осуществляется от источника питания 3,3 В микроконтроллера ESP32.
Итак, теперь у нас есть четкое понимание схемы. Теперь давайте соберем схему.
Сборка аппаратной части проекта
В этом проекте самая сложная часть — сборка схемы, а точнее, соединение компонентов. Цель — добиться максимально компактных размеров, чтобы устройство было удобно держать в руке. Я покажу свой метод, но существует множество способов ещё больше улучшить конструкцию.
Шаг 1: Подготовка перфорированной платы
Учитывая необходимость минимального форм-фактора и удобного размещения, я вырезал перфорированную плату нужного размера из более крупной заготовки. После вырезания я обработал углы напильником, чтобы сделать плату более удобной и комфортной для удержания в руке.
Шаг 2: Размещение компонентов
После завершения подготовки платы пришло время для размещения компонентов. Сначала я разместил MPU6050 в нижней части платы разработки ESP32, чтобы оптимизировать пространство. Я также позаботился о том, чтобы под MPU6050 оставалось немного места, так как оно будет использоваться позже для крепления липучки.
Как видно на изображении выше, я расположил MPU6050 непосредственно под ESP32. Кроме того, я разместил фоторезистор сбоку от ESP32. Вы также можете заметить, что модуль ESP32 значительно приподнят, создавая пространство снизу. Это пространство впоследствии было использовано для установки литий-полимерного аккумулятора.
Шаг 3: Пайка компонентов
После завершения сборки я приступил к пайке. Этот этап оказался несколько сложным из-за компактного форм-фактора и необходимости обеспечить удобство удержания платы. Использование паяных соединений стандартного размера могло бы повлиять на компактность, поэтому я выбрал нестандартный подход.
Я отделил отдельные жилы от проволоки и вручную изготовил тонкие соединительные провода, чтобы сохранить аккуратную и компактную структуру. На изображении ниже показано, как это было сделано.
Поскольку я использовал оголённые жилы без изоляции, перекрещивание двух проводов оказалось довольно сложной задачей. Для решения этой проблемы я использовал каптоновую ленту, которая отлично справилась со своей задачей.
Шаг 4: Включение питания схемы
После тщательного выполнения всех соединений цепи настало время подключить источник питания.
Как показано выше, я вставил литий-полимерный аккумулятор и включил питание. Всё работало отлично, без каких-либо проблем. После окончательной проверки я надёжно закрепил аккумулятор под модулем ESP32. Ниже вы можете увидеть полностью собранное устройство.
Код Arduino
Этот код считывает данные акселерометра и гироскопа с датчика MPU6050 и применяет фильтр Калмана для сглаживания и коррекции показаний. Он также считывает уровень окружающего освещения с помощью фоторезистора и передает обработанные данные по беспроводной связи через Bluetooth. Кроме того, код непрерывно отправляет отфильтрованные данные с датчика по последовательному порту для целей отладки.
Давайте разберем код!
Используемые библиотеки
-
Adafruit_MPU6050.h — обеспечивает связь с датчиком MPU6050.
-
Adafruit_Sensor.h — Предоставляет общий интерфейс для передачи данных с датчиков.
-
Wire.h — обеспечивает связь по протоколу I2C с датчиком.
-
BluetoothSerial.h — позволяет передавать данные по Bluetooth.
Переменные и константы
-
Библиотечные объекты:
-
Adafruit_MPU6050 mpu — объект для взаимодействия с датчиком MPU6050.
-
BluetoothSerial SerialBT — объект для связи по Bluetooth.
-
-
Обозначения контактов:
-
const int LDRSensor = 34 — вывод GPIO, подключенный к датчику LDR
-
-
Параметры фильтра Калмана:
-
float x_angle, y_angle — сохраняет отфильтрованные оценки углов.
-
float x_bias, y_bias — сохраняет коррекцию смещения гироскопа.
-
float P[2][2] = { { 1, 0 }, { 0, 1 } } — матрица ковариации ошибок
-
Параметры настройки:
-
float q_angle = 0.01 — корекция оценки угла.
-
float q_bias = 0.01 — коррекция смещения гироскопа.
-
float r_measure = 0.01 — коррекция показаний датчика.
-
-
Эти значения можно регулировать для изменения чувствительности и плавности работы фильтра Калмана.
Функция setup()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void setup() { Serial.begin(115200); // Initialize Serial Monitor SerialBT.begin("ESP32_BT"); // Initialize Bluetooth with device name "ESP32_BT" // Initialize MPU6050 sensor if (!mpu.begin()) { Serial.println("MPU6050 not found!"); while (1) delay(10); // Halt execution if MPU6050 is not detected } // Set MPU6050 configuration mpu.setAccelerometerRange(MPU6050_RANGE_8_G); // Set accelerometer range mpu.setGyroRange(MPU6050_RANGE_500_DEG); // Set gyroscope range mpu.setFilterBandwidth(MPU6050_BAND_10_HZ); // Apply a low-pass filter delay(100); // Allow settings to take effect } |
-
Инициализирует последовательную связь со скоростью 115200 бод для отладки.
-
Начинает обмен данными по Bluetooth с именем устройства «ESP32_BT».
-
Инициализирует датчик MPU6050 и проверяет его наличие.
-
Настраивает датчик MPU6050:
-
Устанавливает диапазон измерения акселерометра на ±8g.
-
Устанавливает диапазон работы гироскопа в пределах ±500°/с.
-
Применяет фильтр нижних частот с полосой пропускания 10 Гц.
-
-
Предусмотрена небольшая задержка, чтобы дать возможность настройкам вступить в силу.
Функция loop()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void loop() { sensors_event_t a, g, temp; // Variables to store sensor readings mpu.getEvent(&a, &g, &temp); // Get sensor data int ldrValue = analogRead(LDRSensor); // Read LDR sensor value int outputValue = (ldrValue < 2000) ? 0 : 1; // Determine light or dark condition static unsigned long prevTime = millis(); float dt = (millis() - prevTime) / 1000.0; // Convert to seconds prevTime = millis(); float filteredX = kalmanFilter(a.acceleration.x, g.gyro.x, dt, x_angle, x_bias); float filteredY = kalmanFilter(a.acceleration.y, g.gyro.y, dt, y_angle, y_bias); String btData = String(filteredX, 2) + "," + String(filteredY, 2) + "," + String(outputValue); SerialBT.println(btData); Serial.println(btData); } |
-
Считывает данные об ускорении и показания гироскопа с микроконтроллера MPU6050.
-
Считывает уровень окружающего освещения с датчика LDR.
-
Вычисляет разницу во времени (dt) с момента последней итерации цикла.
-
Применяет фильтр Калмана для сглаживания показаний акселерометра и гироскопа.
-
Преобразует отфильтрованные данные в строку для передачи по Bluetooth.
-
Передает данные по беспроводной связи через Bluetooth, а также выводит их в последовательный монитор для отладки.
-
При необходимости может быть предусмотрена небольшая задержка для стабилизации выполнения цикла.
Вспомогательные функции
-
float kalmanFilter(float newAngle, float newRate, float dt, float &angle, float &bias);
-
Реализует фильтр Калмана для обработки необработанных данных с датчиков.
-
Корректирует дрейф гироскопа и шумы в показаниях.
-
Возвращает уточненную оценку угла.
-
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
float kalmanFilter(float newAngle, float newRate, float dt, float &angle, float &bias) { float rate = newRate - bias; // Remove bias from gyroscope rate angle += dt * rate; // Estimate new angle // Update estimation error covariance P[0][0] += dt * (dt * P[1][1] - P[0][1] - P[1][0] + q_angle); P[0][1] -= dt * P[1][1]; P[1][0] -= dt * P[1][1]; P[1][1] += q_bias * dt; // Compute Kalman gain float S = P[0][0] + r_measure; float K[2] = { P[0][0] / S, P[1][0] / S }; // Update estimates with measurement float y = newAngle - angle; angle += K[0] * y; bias += K[1] * y; // Update error covariance matrix P[0][0] -= K[0] * P[0][0]; P[0][1] -= K[0] * P[0][1]; P[1][0] -= K[1] * P[0][0]; P[1][1] -= K[1] * P[0][1]; return angle; // Return the filtered angle } With this, the coding part is completed. For the full code, refer below. |
Тестирование работы проекта
После успешной загрузки кода давайте проведём быстрый тест. Но вы, возможно, задаетесь вопросом, как сделать это тестирование более эффективным? Мы улучшили его с помощью программы Processing.
Эта программа на Processing визуализирует данные в реальном времени, получаемые через последовательное соединение, вероятно, от Arduino. Она сопоставляет входящие значения X и Y с экранными координатами и отображает шарик, меняющий цвет в зависимости от показаний датчика.
Ключевые особенности программы Processing включают визуализацию данных с датчиков в реальном времени, динамический градиентный фон, перекрестные линии, обозначающие центр для ориентира, эффект свечения для улучшения визуализации и отображение данных в реальном времени для более эффективной отладки.
Это делает демонстрацию более увлекательной и интерактивной. Полный код Processing вы можете найти в нашем репозитории на GitHub.
Для начала давайте настроим устройство.
Шаг 1: Включение и сопряжение устройства
Чтобы начать тест,
-
Включите устройство с помощью выключателя.
-
Теперь выполните сопряжение устройства с компьютером через Bluetooth. Выше вы можете увидеть этапы сопряжения.
-
После завершения сопряжения мы будем готовы принимать данные.
Важное примечание: Windows обеспечивает лучшую совместимость Bluetooth с этим проектом, чем Mac. В Windows, даже если отображается сообщение «Не подключено», устройство фактически подключено и может успешно передавать данные.
Шаг 2: Запуск кода обработки
-
Откройте среду разработки Processing и запустите этот код. Если вы впервые используете Processing, не волнуйтесь, это очень простая в использовании платформа программирования, похожая на Arduino. Ранее мы уже создавали множество проектов с использованием Processing, вы также можете ознакомиться с ними.
-
Сначала может появиться ошибка подключения, поскольку необходимо выбрать правильный порт связи.
-
При запуске кода Processing отобразится список доступных COM-портов.
-
Возможно, вам придётся методом проб и ошибок найти правильный вариант.
-
В моем случае правильный COM-порт — COM8.
Шаг 3: Просмотр выходных данных
-
При нажатии кнопки «Запуск» в небольшом окне, созданном программой Processing, будет отображаться движение устройства в реальном времени.
На этом этапе вы можете проверить работоспособность устройства.
-
Возможно, вам потребуется скорректировать значения X и Y, чтобы убедиться, что всё работает правильно.
-
Мяч на экране обработки должен реагировать на жесты рук в реальном времени.
Таким образом, устройство управления жестами теперь полностью функционально и готово к дальнейшему тестированию или применению в реальных условиях.
Полный код проекта
Все исходные коды для данного проекта вы также можете скачать с 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 |
/* Features: - Reads acceleration and gyroscope data from an MPU6050 sensor. - Uses a Kalman filter to smooth and correct sensor readings. - Reads ambient light levels using an LDR sensor. - Communicates sensor data wirelessly via Bluetooth. - Sends processed values over serial for debugging. Applications: - Motion tracking and gesture recognition. - Balancing robots and stabilization systems. - Smart lighting systems based on ambient light levels. - Wearable motion analysis and health tracking. - Wireless remote control based on tilt movements. */ #include <Adafruit_MPU6050.h> #include <Adafruit_Sensor.h> #include <Wire.h> #include "BluetoothSerial.h" Adafruit_MPU6050 mpu; // Create MPU6050 sensor object BluetoothSerial SerialBT; // Create Bluetooth serial object const int LDRSensor = 34; // Define the LDR sensor pin // Kalman filter variables float x_angle = 0, y_angle = 0; // Filtered angle estimates float x_bias = 0, y_bias = 0; // Gyroscope bias correction float P[2][2] = { { 1, 0 }, { 0, 1 } }; // Error covariance matrix // Kalman filter tuning parameters // Adjust these for different levels of noise filtering and responsiveness // Quick and Less Smooth Response float q_angle = 0.01; // Trust new angle estimates more float q_bias = 0.01; // Faster correction of gyroscope drift float r_measure = 0.01; // Trust sensor readings more (less smoothing) // // Alternative: Optimum Response // const float q_angle = 0.001; // Process noise // const float q_bias = 0.003; // const float r_measure = 0.03; // Measurement noise // // Alternative: Slow and smooth response // float q_angle = 0.0001; // Trust new angle estimates more // float q_bias = 0.0005; // Faster correction of gyroscope drift // float r_measure = 0.05; // Trust sensor readings more (less smoothing) void setup() { Serial.begin(115200); // Initialize Serial Monitor SerialBT.begin("ESP32_BT"); // Initialize Bluetooth with device name "ESP32_BT" // Initialize MPU6050 sensor if (!mpu.begin()) { Serial.println("MPU6050 not found!"); while (1) delay(10); // Halt execution if MPU6050 is not detected } // Set MPU6050 configuration mpu.setAccelerometerRange(MPU6050_RANGE_8_G); // Set accelerometer range mpu.setGyroRange(MPU6050_RANGE_500_DEG); // Set gyroscope range mpu.setFilterBandwidth(MPU6050_BAND_10_HZ); // Apply a low-pass filter delay(100); // Allow settings to take effect } // Kalman filter function to smooth sensor data float kalmanFilter(float newAngle, float newRate, float dt, float &angle, float &bias) { float rate = newRate - bias; // Remove bias from gyroscope rate angle += dt * rate; // Estimate new angle // Update estimation error covariance P[0][0] += dt * (dt * P[1][1] - P[0][1] - P[1][0] + q_angle); P[0][1] -= dt * P[1][1]; P[1][0] -= dt * P[1][1]; P[1][1] += q_bias * dt; // Compute Kalman gain float S = P[0][0] + r_measure; float K[2] = { P[0][0] / S, P[1][0] / S }; // Update estimates with measurement float y = newAngle - angle; angle += K[0] * y; bias += K[1] * y; // Update error covariance matrix P[0][0] -= K[0] * P[0][0]; P[0][1] -= K[0] * P[0][1]; P[1][0] -= K[1] * P[0][0]; P[1][1] -= K[1] * P[0][1]; return angle; // Return the filtered angle } void loop() { sensors_event_t a, g, temp; // Variables to store sensor readings mpu.getEvent(&a, &g, &temp); // Get sensor data int ldrValue = analogRead(LDRSensor); // Read LDR sensor value int outputValue = (ldrValue < 2000) ? 0 : 1; // Determine light or dark condition // Calculate time difference for Kalman filter static unsigned long prevTime = millis(); float dt = (millis() - prevTime) / 1000.0; // Convert to seconds prevTime = millis(); // Apply Kalman filter to smooth sensor data float filteredX = kalmanFilter(a.acceleration.x, g.gyro.x, dt, x_angle, x_bias); float filteredY = kalmanFilter(a.acceleration.y, g.gyro.y, dt, y_angle, y_bias); // Format data for Bluetooth transmission String btData = String(filteredX, 2) + "," + String(filteredY, 2) + "," + String(outputValue); // Send data over Bluetooth and Serial for debugging SerialBT.println(btData); Serial.println(btData); // Small delay to stabilize loop execution // delay(1); // Uncomment if needed } |
