Системы дополненной и виртуальной реальности с каждым годом все больше входят в жизнь современного общества. Времена использования клавиатуры и джойстика в компьютерных играх безвозвратно уходят в прошлое. В настоящее время игровые консоли все чаще оснащаются контроллерами виртуальной реальности, которые позволяют управлять процессами в игре с помощью движений рук и тела, что, конечно же, приводит к значительно большей вовлеченности геймера в процесс игры.
В этом проекте мы постараемся создать на основе платы Arduino упрощенную модель подобных игровых систем, а именно мы реализуем игру в Ping Pong (настольный теннис), в которую можно будет играть с помощью акселерометра, а управление в этой игре будет осуществляться с помощью движений рук.
В настоящее время для создания подобного проекта можно использовать кучу самых различных программ - мы будем использовать одну из них под названием Processing. Это основанное на JAVA приложение, с помощью которого мы можем сформировать необходимую нам программу в формате .exe и в формате .apk (для android приложения). Ранее программу Processing мы использовали в проекте создания комнаты чата с помощью Arduino и радиочастотных модулей.
Аппаратная часть нашего проекта будет состоять из платы Arduino, которая будет получать входные данные от акселерометра. Плата Arduino затем будет передавать данные на компьютер (лэптоп) с помощью последовательной связи. Внешний вид необходимых компонентов для проекта показан на следующем рисунке. В этом проекте можно использовать любой тип платы Arduino.
Работа схемы
Схема соединений рассматриваемого проекта показана на следующих рисунках.
Как видим, схема устройства очень проста и содержит плату Arduino Nano и акселерометр. Но при этом необходимо учитывать ряд моментов:
- На акселерометр нельзя подавать питающее напряжение 5V, поэтому его контакт Vcc следует подсоединить к контакту 3.3V платы Arduino.
- Каждый акселерометр страдает от силы тяжести, негативные эффекты которой следует обрабатывать программным путем или устранять с помощью фильтра.
Принципы работы акселерометра
Акселерометр представляет собой устройство, которое преобразует ускорение в любом направлении в соответствующее изменение напряжения. Это осуществляется с помощью конденсаторов (см. рисунок) – как только ускоритель (в виде шарика) двигается, одна пластина конденсатора также сдвигается, поэтому его емкость изменяется, что в итоге приводит к изменению напряжения.
Как уже было указано, каждый акселерометр страдает от эффекта гравитации. При этом независимо от того с какой точностью откалиброван акселерометр, он все равно будет страдать от этого эффекта. Далее дано более подробное объяснение этого эффекта.
Концептуально, датчик ускорения определяет ускорение устройства, которое зависит от приложенной силы и которое можно рассчитать по следующей формуле:
Ad = - ∑Fs / mass.
Вместе с этим сила гравитации всегда будет влиять на измеряемое ускорение в соответствии со следующим выражением:
Ad = -g - ∑F / mass.
По этой причине когда устройство лежит на столе (то есть не движется с ускорением), акселерометр считывает величину гравитационного ускорения g = 9.81 m/s2. А когда устройство находится в состоянии свободного падения с ускорением по отношению к земле 9.81 m/s2, то акселерометр считывает величину ускорения g = 0 m/s2. Следовательно, для получения точного значения ускорения вклад силы тяжести необходимо вычесть из получаемых акселерометром данных. Это можно сделать с помощью фильтра верхних частот. Наоборот, фильтр нижних частот может быть использован для изоляции эффекта силы тяжести.
То есть в нашей схеме мы эффект силы гравитации можем достаточно просто уменьшить с помощью фильтра. Этот фильтр будет состоять из двух массивов, один из которых будет хранить выборочные значения от датчика, а другой будет использоваться для сортировки этих значений и нахождения наиболее часто повторяющихся значений. Далее в программе мы рассмотрим реализацию этого подхода.
Программирование Arduino
Полный текст программы для Arduino представлен в конце статьи. Здесь рассмотрим только основные моменты.
Инициализируем объем выборки (Samplesize).
#define Samplesize 13 // filterSample number
Увеличьте объем выборки (Samplesize) если ваш акселерометр по прежнему показывает случайные значения.
Для последовательного порта будем использовать скорость 9600 бод/с. Для увеличения скорости взаимодействия можно увеличить эту скорость, но помните о том, что эти изменения необходимо внести и в программу для Arduino, и для Processing.
void setup(){
Serial.begin(9600);
}
Используемый нами акселерометр показывает значение максимальное значение 193 при ускорении влево и максимальное значение 280 при сдвиге, на вашем акселерометре измерьте эти значения и внесите соответствующие изменения в программу.
toSend = map (smoothData1, 193, 280, 0, 255);
Эти значения упаковываются в отдельный байт данных для их последовательной передачи.
Программирование Processing
Processing представляет собой программное обеспечение с открытым исходным кодом, удобное для реализации разнообразных проектов. В нем удобно реализовывать проекты похожие на те, которые можно создать с помощью Android Development IDE.
Код Processing для нашего проекта можно скачать по следующей ссылке:
• Processing Code for Arduino Ping Ball Game.
Щелкните на этой ссылке правой кнопкой мыши и выберите пункт "Сохранить как". Затем откройте файл в программе Processing и нажмите там Run чтобы запустить процесс игры. Скачанная вами по ссылке программа будет иметь расширение *.pde. Чтобы открывать такие файлы вам необходимо установить программу Processing.
В программе для Arduino в функции void setup() не забудьте не забудьте включить следующий код чтобы указать по какому порту вы будете передавать данные:
port = new Serial(this,Serial.list()[4],9600); //Reads the 4th PORT at 9600 baudrate
Здесь мы указали 4-й порт Arduino, но вы можете указать и другой порт если Arduino у вас подсоединено к другому COM-порту.
Исходный код программы
Теперь, когда наши программы в Processing и Arduino готовы, загрузите программу в плату Arduino и подсоедините ее к компьютеру по USB-кабелю. Запустите на исполнение .pde файл с помощью Processing. Далее берите в руки в акселерометр и начинайте играть в Ping Pong. Более подробно все эти процессы показаны в видео в конце статьи.
Если вы разберетесь с этим проектом то вы на его основе сможете создать много других интересных игр. Здесь мы использовали только X-ось, но вы можете в других проектах задействовать оси Y и Z.
Далее представлен полный текст программы.
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 |
#define AccelPin A0 // A0 подсоединен к X-оси акселерометра #define Samplesize 13 // объем выборки для фильтрации эффекта силы тяжести int Array1 [Samplesize]; // массив чтобы хранить начальные значения ("сырые", необработанные) с выхода акселерометра int rawData1, smoothData1; // переменные для данных акселерометра int toSend; void setup(){ Serial.begin(9600); } void loop() { rawData1 = analogRead(AccelPin); // считать X-ось акселерометра smoothData1 = digitalSmooth(rawData1, Array1); toSend = map (smoothData1, 193, 280, 0, 255); // данные от акселерометра упаковываются в байт Serial.write (toSend); delay(100); } int digitalSmooth(int rawIn, int *sensSmoothArray){ // "int *sensSmoothArray" passes an array to the function - the asterisk indicates the array name is a pointer int j, k, temp, top, bottom; long total; static int i; static int sorted[Samplesize]; boolean done; i = (i + 1) % Samplesize; // increment counter and roll over if necc. - % (modulo operator) rolls over variable sensSmoothArray[i] = rawIn; // input new data into the oldest slot for (j=0; j<Samplesize; j++){ // передача данных массива в другой массив для их сортировки и упорядочивания sorted[j] = sensSmoothArray[j]; } done = 0; // флаг, свидетельствующий о проведении сортировки while(done != 1){ // простая сортировка методом простых обменов, сортируем номера от нижних к верхних done = 1; for (j = 0; j < (Samplesize - 1); j++){ if (sorted[j] > sorted[j + 1]){ // numbers are out of order - swap temp = sorted[j + 1]; sorted [j+1] = sorted[j] ; sorted [j] = temp; done = 0; } } } bottom = max(((Samplesize * 15) / 100), 1); top = min((((Samplesize * 85) / 100) + 1 ), (Samplesize - 1)); // the + 1 is to make up for asymmetry caused by integer rounding k = 0; total = 0; for ( j = bottom; j< top; j++){ total += sorted[j]; // остальные оставшиеся индексы k++; } return total / k; // делим на число отсчетов } |