Молодая, но интересная концепция управления чем бы то ни было при помощи жестов, пока не сравнится по быстроте, точности и набору функций с привычными нам кнопками. Согласитесь, что для того, чтобы включить чайник, проще нажать на кнопку, чем скажем махнуть рукой или выполнить какой-то специальный жест. Однако уже сейчас есть области применения, в которых традиционные интерфейсы проигрывают.
Если вы едете в машине, то нажатие на кнопку вовремя движения (например, чтобы включить обдув лобового стекла) требует от вас оторвать глаза от дороги и найти эту кнопку… ну или нащупать по памяти. Гораздо проще произнести команду или махнуть рукой. И хотя распознавание речи тоже интересная тема, но тут я решил сконцентрироваться исключительно на устройствах распознавания жестов, с которыми мне удалось поработать – это Myo armband, Leap Motion и SparkFun RGB and Gesture Sensor.
Три разные “игрушки” , которые обладают разным функционалом, ценой, размерами и, видимо, областью применения.
Но прежде чем перейти к непосредственно устройствам я бы хотел остановиться на более простых вариантах распознавания движения с использованием дальномеров.
одним из типичных представителей является ультразвуковой датчик определения расстояния, например hc-sr04.
Использовать его просто: согласно datasheet-у подается напряжение на триггер в течении 10 микросекунд, а затем считывается сигнал с ножки echo. Конечно уже есть готовые библиотеки для работы с этим датчиком, например NewPing, правда качество измерения крайне низкое если расстояние больше 10см.
Соответственно можно сделать простой скетч, который бы в случае обнаружения препятствия на расстоянии меньше, скажем, 6 сантиметров срабатывал как нажатие на кнопку. Вот пример реализации с ипользованием простенького i2c дисплея для отображения информации:
#include <Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); #define Trig_pin 8 #define Echo_pin 7 #define press_range 6 #define measures 20 bool pressed = false; void setup() { // put your setup code here, to run once: pinMode(Trig_pin, OUTPUT); digitalWrite(Trig_pin, LOW); pinMode(Echo_pin, INPUT); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); pressed = false; display.display(); } void loop() { unsigned int range = Ranging(); if (range < press_range) pressed = !pressed; display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(0,0); display.print("cm: "); display.println(range); if (pressed) display.println("pressed"); else display.println("released"); display.display(); delay(300); } unsigned int Timing() { digitalWrite(Trig_pin, HIGH); delayMicroseconds(10); digitalWrite(Trig_pin, LOW); return pulseIn(Echo_pin, HIGH); } unsigned int Ranging() { unsigned int avgduration = 0; unsigned int maxdur = 0; unsigned int mindur = 0; for (int i = 0; i < measures; i++) { unsigned int etime = Timing(); if (etime > maxdur) maxdur = etime; if (etime < mindur) mindur = etime; avgduration += etime; delay(1); } avgduration = (avgduration - (maxdur + mindur)) / (measures-2); return (avgduration /29 / 2); }
Функция Timing() замеряет время которое потребовалось отражённому сигналу, чтобы вернуться. Функция Ranging() вызывает её 20 раз с задержкой 1 милисекунда, максимальное и минимальное значение отбрасываются, а оставшиеся усредняются. Затем просто проверяется, что если расстояние меньше заданного значения – 6 см, то произошло нажатие на кнопку. Работает это таким вот образом:
Другой пример использования датчика расстояния для распознавания движений – это взять например два таких датчика и поставить их рядом. Соответственно , когда оба они сработают, то в зависимости от того, который первый сработал определить направление. Для разнообразия решил использовать инфракрасные дальномеры.
У них меньше дальность обнаружения, но есть цифровой выход, который говорит – близко объект или нет. Вот мой пример реализации:
#include <Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); #define din1 4 #define ain1 A0 #define din2 2 #define ain2 A1 bool t1 = false; bool t2 = false; short drctn = 0; void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.display(); pinMode(din1,INPUT); pinMode(ain1,INPUT); pinMode(din2,INPUT); pinMode(ain2,INPUT); } void loop() { display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); unsigned int ad1=analogRead(ain1); unsigned int ad2=analogRead(ain2); int din1_val = digitalRead(din1); int din2_val = digitalRead(din2); if (t1 && !t2 && (din2_val == LOW)) drctn = 1; else if (t2 && !t1 && (din1_val == LOW)) drctn = -1; else if (!t1 && !t2) drctn = 0; if(din1_val==LOW) { display.print("1: Near | "); t1 = true; } else { t1 = false; display.print("1: Far | "); } if(din2_val==LOW) { display.println("2: Near "); t2 = true; } else { t2 = false; display.println("2: Far "); } display.print(ad1); display.print(" | "); display.println(ad2); if (drctn == -1) display.println("left"); else if (drctn == 1) display.println("right"); display.display(); delay(100); }
И вот как это работает:
Соответственно если добавить ещё один датчик расстояния, то можно расширить количество распознаваемых движений.
Например теперь можно распознать движения вверх и вниз. Вот как это работает:
Скетч отличается не сильно – идея та же самая. Если сначала сработал верхний дальномер а потом оба нижних, то движение вниз, если сначала оба нижних, а потом верхний, то движение вверх.
Ещё один момент, которй можно сюда добавить – это “нажатие” как в примере с ультразвуковым датчиком. Вот полный скетч, который включает движения влево, вправо, вверх, вниз и нажатие.
#include <Adafruit_SSD1306.h> #define OLED_RESET 4 Adafruit_SSD1306 display(OLED_RESET); #define din1 4 #define ain1 A0 #define din2 2 #define ain2 A1 #define din3 7 #define ain3 A2 bool t1 = false; bool t2 = false; bool t3 = false; short drctn = 0; void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.display(); pinMode(din1,INPUT); pinMode(ain1,INPUT); pinMode(din2,INPUT); pinMode(ain2,INPUT); pinMode(din3,INPUT); pinMode(ain3,INPUT); } void loop() { display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); unsigned int ad1=analogRead(ain1); unsigned int ad2=analogRead(ain2); unsigned int ad3=analogRead(ain3); int din1_val = digitalRead(din1); int din2_val = digitalRead(din2); int din3_val = digitalRead(din3); if (drctn == 0) { if (t1 && !t2 && (din2_val==LOW)) drctn = 1; else if (t2 && !t1 && (din1_val==LOW)) drctn = -1; else if (t1 && t2 && (din3_val==LOW)) drctn = 2; else if (t3 && !t2 && !t2 && (din1_val==LOW) && (din2_val==LOW)) drctn = -2; else if (!t1 && !t2 && !t3 &&(din1_val==LOW) && (din2_val==LOW)&& (din3_val==LOW)) drctn = -3; } if(din1_val==LOW) { display.print("1 Near "); t1 = true; } else { t1 = false; display.print("1 Far "); } if(din2_val==LOW) { display.print("2 Near "); t2 = true; } else { t2 = false; display.print("2 Far "); } if (din3_val==LOW) { display.println("3 Near"); t3=true; } else { t3 = false; display.println("3 Far"); } display.print(ad1); display.print(" "); display.print(ad2); display.print(" "); display.println(ad3); if (drctn == -1) display.println("left"); else if (drctn == 1) display.println("right"); else if (drctn == 2) display.println("up"); else if (drctn == -2) display.println("down"); else if (drctn == -3) display.println("push"); display.display(); if (!t1 && !t2 && !t3) drctn = 0; delay(100); }
и пример того, как это работает
в следующем посте уже будут готовые решения вместо кустарных. Как вам идея управления жестами в целом?