Оглавление:

Infinity Bike - тренировочная видеоигра для велотренажера в помещении: 5 шагов
Infinity Bike - тренировочная видеоигра для велотренажера в помещении: 5 шагов

Видео: Infinity Bike - тренировочная видеоигра для велотренажера в помещении: 5 шагов

Видео: Infinity Bike - тренировочная видеоигра для велотренажера в помещении: 5 шагов
Видео: ЭКСТРЕМАЛЬНЫЕ 24 ЧАСА НА BMX ЧЕЛЛЕНДЖ ! СУПЕР ВЕЛОСИПЕДИСТ **ОЧЕНЬ БЫСТРО** 2024, Сентябрь
Anonim
Image
Image
Материалы
Материалы

Зимой, в холодные дни и в плохую погоду у велосипедистов-энтузиастов есть только несколько вариантов, чтобы заниматься своим любимым видом спорта. Мы искали способ сделать тренировки в помещении с велосипедом / тренажером немного более интересными, но большинство доступных продуктов либо дороги, либо просто скучны в использовании. Вот почему мы начали разрабатывать Infinity Bike как обучающую видеоигру с открытым исходным кодом. Бесконечный велосипед считывает скорость и направление с вашего велосипеда и предлагает уровень интерактивности, который невозможно легко найти с помощью велотренажеров.

Мы используем простоту микроконтроллера Arduino и несколько деталей, напечатанных на 3D-принтере, чтобы закрепить недорогие датчики на велосипеде, установленном на тренажере. Информация передается в видеоигру, созданную с помощью популярного движка для создания игр Unity. К концу этой инструкции вы сможете настроить свои собственные датчики на своем велосипеде и передать информацию своих датчиков в Unity. Мы даже включили трек, по которому вы можете прокатиться и протестировать свою новую настройку. Если вы заинтересованы в участии, вы можете проверить наш GitHub.

Шаг 1: материалы

Материалы
Материалы

Список материалов, которые вам понадобятся, может немного отличаться; для

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

1 x Arduino nano (22 доллара США)

1 x миниатюрная макетная плата (1,33 доллара США за единицу)

1 резистор 220 Ом (1,00 $ / комплект)

1 x 10K потенциометр (1,80 доллара США за единицу)

1 х датчик Холла (0,96 доллара США)

Ремень ГРМ для 3D-принтера размером 20 см x 6 мм (3,33 доллара США)

1 комплект x винтов и болтов M3 разной длины (6,82 доллара США)

1 х велосипедный магнит спидометра (0,98 доллара США)

Мы смонтировали материал выше с помощью деталей, напечатанных на 3D-принтере. Используемые нами файлы перечислены ниже, и они пронумерованы в соответствии с тем же соглашением, что и изображение в начале этого раздела. Все файлы можно найти на Thingiverse. Вы можете использовать их как есть, но убедитесь, что размеры, которые мы использовали, соответствуют вашему велосипеду.

1. FrameConnection_PotentiometerHolder_U_Holder.stl

2. FrameConnection_Spacer.stl

3. BreadboardFrameHolder.stl

4. Pulley_PotentiometerSide.stl

5. Pot_PulleyConnection.stl

6. FrameConnection.stl

7. Pulley_HandleBarSide_Print2.stl

8. FrameToHallSensorConnector.stl

9. PotHolder.stl

10. HallSensorAttach.stl

Шаг 2: чтение и передача данных в Unity

Чтение и передача данных в Unity
Чтение и передача данных в Unity

Код Arduino и Unity будет работать вместе, чтобы собирать, передавать и обрабатывать данные с датчиков на велосипеде. Unity запросит значение у Arduino, отправив строку через последовательный порт, и будет ждать, пока Arduino ответит запрошенными значениями.

Сначала мы подготавливаем Arduino с библиотечной последовательной командой, которая используется для управления запросами от Unity путем объединения строки запроса с функцией. Базовая настройка этой библиотеки может быть сделана следующим образом;

#include "SerialCommand.h"

SerialCommand sCmd; void setup () {sCmd.addCommand ("TRIGG", TriggHanlder); Serial.begin (9600); } void loop () {в то время как (Serial.available ()> 0) {sCmd.readSerial (); }} void TriggHandler () {/ * Здесь считываются и передаются датчики * /}

К объекту SCmd привязана функция TriggHandler. Если серийный номер получает строку, которая соответствует присоединенной команде (в данном случае TRIGG), выполняется функция TriggHandler.

Мы используем потенциометр для измерения направления поворота и датчик Холла для измерения скорости вращения велосипеда в минуту. Показания потенциометра можно легко сделать с помощью встроенных функций Arduino. Затем функция TriggHandler может распечатать значение в серийный номер со следующим изменением.

void TriggHandler () {

/ * Чтение значения потенциометра * / Serial.println (analogRead (ANALOGPIN)); }

Датчик Холла требует дополнительных настроек, прежде чем мы сможем проводить полезные измерения. В отличие от потенциометра, мгновенное значение датчика Холла не очень полезно. Поскольку мы пытались измерить скорость колеса, время между триггерами - вот что нас интересовало.

Каждая функция, используемая в коде Arduino, требует времени, и если магнит выровняется с датчиком Холла не в то время, измерение может быть в лучшем случае задержано или полностью пропущено в худшем случае. Это явно плохо, потому что Arduino может сообщать скорость, которая НАМНОГО отличается от фактической скорости колеса.

Чтобы избежать этого, мы используем функцию Arduinos, называемую прерыванием присоединения, которая позволяет нам запускать функцию всякий раз, когда назначенный цифровой вывод запускается с нарастающим сигналом. Функция rpm_fun присоединяется к прерыванию с одной строкой кода, добавляемой к коду установки.

void setup () {

sCmd.addCommand («TRIGG», TriggHanlder); attachInterrupt (0, rpm_fun, RISING); Serial.begin (9600); } // Функция rpm_fun используется для вычисления скорости и определяется как; беззнаковый длинный lastRevolTime = 0; беззнаковый длинный revolSpeed = 0; void rpm_fun () {unsigned long revolTime = millis (); unsigned long deltaTime = revolTime - lastRevolTime; / * revolSpeed - значение, передаваемое в код Arduino * / revolSpeed = 20000 / deltaTime; lastRevolTime = revolTime; } Затем TriggHandler может передать остальную информацию по запросу. void TriggHanlder () {/ * Считывание значения потенциометра * / Serial.println (analogRead (ANALOGPIN)); Serial.println (revolSpeed); }

Теперь у нас есть все строительные блоки, которые можно использовать для создания кода Arduino, который будет передавать данные через последовательный порт, когда Unity делает запрос. Если вы хотите получить копию полного кода, вы можете скачать ее на нашем GitHub. Чтобы проверить правильность настройки кода, вы можете использовать последовательный монитор для отправки TRIGG; убедитесь, что вы установили конец строки на возврат каретки. В следующем разделе мы рассмотрим, как наши скрипты Unity могут запрашивать и получать информацию от Arduino.

Шаг 3. Получение и обработка данных

Получение и обработка данных
Получение и обработка данных

Unity - отличная программа, доступная бесплатно любителям

интересуется созданием игр; он поставляется с большим количеством функций, которые могут действительно сократить время на настройку определенных вещей, таких как многопоточность или программирование на графическом процессоре (шейдинг AKA), без ограничения того, что можно делать с помощью сценариев C #. Микроконтроллеры Unity и Arduino можно использовать вместе для создания уникальных интерактивных приложений с относительно небольшим бюджетом.

Цель этого руководства - помочь настроить связь между Unity и Arduino, чтобы мы не слишком углублялись в большинство функций, доступных в Unity. Существует множество ОТЛИЧНЫХ руководств по единству и невероятное сообщество, которое могло бы гораздо лучше объяснить, как работает Unity. Тем не менее, есть специальный приз для тех, кто сумел пройти через это руководство, которое служит небольшой демонстрацией того, что можно было бы сделать. Вы можете скачать на нашем Github нашу первую попытку создать трек с реалистичной физикой велосипеда.

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

В Unity создайте новую сцену с одним пустым GameObject с именем ArduinoReceive при прикреплении сценария C #, также называемого ArduinoReceive. В этот скрипт мы добавим весь код, который обрабатывает связь с Arduino.

Существует библиотека, к которой необходимо получить доступ, прежде чем мы сможем связаться с последовательными портами вашего компьютера; Необходимо настроить Unity, чтобы разрешить использование определенных библиотек. Перейдите в Edit-> ProjectSerring-> Player и рядом с уровнем совместимости Api в разделе Configuration переключите Подмножество. NET 2.0 на. NET 2.0. Теперь добавьте следующий код вверху скрипта;

используя System. IO. Ports;

Это позволит вам получить доступ к классу SerialPort, который вы можете определить как объект для класса ArduinoReceive. Сделайте его закрытым, чтобы избежать вмешательства со стороны другого скрипта.

частный SerialPort arduinoPort;

Объект arduinoPort можно открыть, выбрав правильный порт (например, к которому USB подключен Arduino) и скорость передачи данных (то есть скорость, с которой отправляется информация). Если вы не уверены, к какому порту подключена Arduino, вы можете узнать это в диспетчере устройств или открыв Arduino IDE. Для скорости передачи значение по умолчанию на большинстве устройств составляет 9600, просто убедитесь, что у вас есть это значение в вашем коде Arduino, и оно должно работать.

Теперь код должен выглядеть так:

using System. Collections;

using System. Collections. Generic; using UnityEngine; используя System. IO. Ports; открытый класс ArduinoReceive: MonoBehaviour {частный SerialPort arduinoPort; // Используйте это для инициализации void Start () {arduinoPort = new SerialPort ("COM5", 9600); arduinoPort. Open (); WriteToArduino («TRIGG»); }}

Ваш номер COM, скорее всего, будет другим. Если вы используете MAC, ваше имя COM может иметь такое имя /dev/cu.wchusbserial1420. Убедитесь, что код из раздела 4 загружен в Arduino, а монитор последовательного порта закрыт до конца этого раздела и что этот код компилируется без проблем.

Теперь давайте отправим запрос в Arduino каждый кадр и запишем результаты в окно консоли. Добавьте функцию WriteToArduino в класс ArduinoReceive. Возврат каретки и новая строка необходимы для кода Arduino для правильного анализа входящей инструкции.

private void WriteToArduino (строковое сообщение)

{сообщение = сообщение + "\ г / п"; arduinoPort. Write (сообщение); arduinoPort. BaseStream. Flush (); }

Затем эту функцию можно вызвать в цикле обновления.

void Update ()

{WriteToArduino ("TRIGG"); Debug. Log ("Первое значение:" + arduinoPort. ReadLine ()); Debug. Log ("Второе значение:" + arduinoPort. ReadLine ()); }

Приведенный выше код - это минимум, который вам нужен для чтения данных из Arduino. Если вы обратите пристальное внимание на FPS, заданный единицей, вы должны увидеть значительное падение производительности. В моем случае он идет примерно с 90 FPS без чтения / записи до 20 FPS. Если ваш проект не требует частых обновлений, этого может быть достаточно, но для видеоигры 20 кадров в секунду - это слишком мало. В следующем разделе будет рассказано, как можно повысить производительность с помощью многопоточности.

Шаг 4: Оптимизация передачи данных

В предыдущем разделе рассказывалось, как настроить базовую

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

Для начала мы должны включить библиотеку потоков, добавив;

using System. Threading;

Затем мы настраиваем функцию, которую запускаем в потоках. AsynchronousReadFromArduino начинается с записи запроса в Arduino с помощью функции WrtieToArduino. Чтение заключено в блок try-catch, если таймаут чтения, переменные остаются нулевыми и вместо OnArduinoInfoReceive вызывается функция OnArduinoInfoFail.

Затем мы определяем функции OnArduinoInfoFail и OnArduinoInfoReceive. Для этого мы выводим результаты на консоль, но вы можете сохранить результаты в переменных, которые вам нужны для вашего проекта.

частная пустота OnArduinoInfoFail ()

{Debug. Log («Ошибка чтения»); } private void OnArduinoInfoReceived (вращение строки, скорость строки) {Debug. Log ("Readin Sucessfull"); Debug. Log ("Первое значение:" + вращение); Debug. Log ("Второе значение:" + скорость); }

Последний шаг - запустить и остановить потоки, которые будут запрашивать значения у Arduino. Мы должны убедиться, что последний поток завершил свою последнюю задачу, прежде чем запускать новый. В противном случае к Arduino можно было бы сделать несколько запросов одновременно, что могло бы сбить с толку Arduino / Unity и привести к непредсказуемым результатам.

частный поток activeThread = null;

void Update () {if (activeThread == null ||! activeThread. IsAlive) {activeThread = новый поток (AsynchronousReadFromArduino); activeThread. Start (); }}

Если вы сравните производительность кода с кодом, который мы написали в разделе 5, производительность должна быть значительно улучшена.

частная пустота OnArduinoInfoFail ()

{Debug. Log («Ошибка чтения»); }

Шаг 5: Что дальше?

Куда дальше?
Куда дальше?

Мы подготовили демоверсию, которую вы можете скачать на нашем Github (https://github.com/AlexandreDoucet/InfinityBike), загрузить код и игру и прокатиться по нашему треку. Все готово для быстрой тренировки, и мы надеемся, что она даст вам представление о том, что вы можете построить, если воспользуетесь тем, чему мы вас научили с помощью этого руководства.

Кредиты

Участники проекта

Александр Дусе (_Doucet_)

Максим Будро (MxBoud)

Внешние ресурсы [игровой движок Unity] (https://unity3d.com)

Этот проект начался после того, как мы прочитали руководство Аллана Зуккони «Как интегрировать Arduino с Unity» (https://www.alanzucconi.com/2015/10/07/how-to-int…)

Запрос от Arduino обрабатывается с помощью библиотеки SerialCommand (https://github.com/kroimon/Arduino-SerialCommand)

Рекомендуемые: