Tabla de contenido:

Infinity Bike - Videojuego de entrenamiento de bicicletas en interiores: 5 pasos
Infinity Bike - Videojuego de entrenamiento de bicicletas en interiores: 5 pasos

Video: Infinity Bike - Videojuego de entrenamiento de bicicletas en interiores: 5 pasos

Video: Infinity Bike - Videojuego de entrenamiento de bicicletas en interiores: 5 pasos
Video: ZWIFT: Primeros Pasos, Actualizaciones del Juego y Recomendaciones 2024, Junio
Anonim
Image
Image
Materiales
Materiales

Durante las temporadas de invierno, los días fríos y el mal tiempo, los ciclistas aficionados solo tienen unas pocas opciones para ejercitarse haciendo su deporte favorito. Estábamos buscando una manera de hacer que el entrenamiento en interiores con una configuración de bicicleta / entrenador sea un poco más entretenido, pero la mayoría de los productos disponibles son costosos o simplemente aburridos de usar. Es por eso que comenzamos a desarrollar Infinity Bike como un videojuego de entrenamiento de código abierto. Infinity bike lee la velocidad y la dirección de su bicicleta y ofrece un nivel de interactividad que no se puede encontrar fácilmente con los entrenadores de bicicletas.

Aprovechamos la simplicidad disponible del microcontrolador Arduino y algunas piezas impresas en 3D para asegurar sensores económicos a una bicicleta montada en un entrenador. La información se transmite a un videojuego creado con el popular motor de creación de juegos, Unity. Al final de este instructivo, debería poder configurar sus propios sensores en su bicicleta y transferir la información de sus sensores a Unity. Incluso incluimos una pista en la que puedes viajar y probar tu nueva configuración. Si está interesado en contribuir, puede consultar nuestro GitHub.

Paso 1: Materiales

Materiales
Materiales

La lista de materiales que necesitará puede variar un poco; por

Por ejemplo, el tamaño de su bicicleta determinará la longitud de los cables de puente que necesita, pero aquí están las piezas principales que necesitará. Probablemente pueda encontrar precios más baratos para cada pieza en un sitio web como AliExpress, pero esperar 6 meses para el envío no siempre es una opción, por lo que usamos las piezas un poco más caras para que la estimación no sea sesgada.

1 x Arduino nano ($ 22,00)

1 x Mini tablero ($ 1.33 / unidad)

1 x resistencia de 220 ohmios ($ 1.00 / kit)

1 x potenciómetro de 10K ($ 1.80 / unidad)

1 x sensor Hall ($ 0,96)

Correa de distribución de impresora 3D de 20 cm x 6 mm ($ 3.33)

1 kit x tornillos y pernos M3 de varias longitudes ($ 6.82)

1 x imán de velocímetro de bicicleta ($ 0,98)

Montamos el material de arriba con piezas impresas en 3D. Los archivos que usamos se enumeran a continuación y están numerados con la misma convención que la imagen al comienzo de esta sección. Todos los archivos se pueden encontrar en Thingiverse. Puede usarlos como están, pero asegúrese de que las dimensiones que usamos coincidan con su bicicleta.

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

Paso 2: leer y transferir datos a Unity

Leer y transferir datos a Unity
Leer y transferir datos a Unity

El código de Arduino y Unity trabajarán juntos para recopilar, transferir y procesar los datos de los sensores de la bicicleta. Unity solicitará el valor del Arduino enviando una cadena a través del serial y esperará a que el Arduino responda con los valores solicitados.

Primero, preparamos el Arduino con la biblioteca Serial Command que se usa para administrar las solicitudes de Unity al emparejar una cadena de solicitud con una función. Se puede realizar una configuración básica para esta biblioteca de la siguiente manera;

#include "SerialCommand.h"

SerialCommand sCmd; configuración vacía () {sCmd.addCommand ("TRIGG", TriggHanlder); Serial.begin (9600); } bucle vacío () {while (Serial.available ()> 0) {sCmd.readSerial (); }} void TriggHandler () {/ * Lea y transmita los sensores aquí * /}

La función TriggHandler se adjunta al objeto SCmd. Si el serial recibe una cadena que coincide con el comando adjunto (en este caso TRIGG), se ejecuta la función TriggHandler.

Usamos potenciómetro para medir la dirección de la dirección y un sensor de pasillos para medir la rotación por minuto de la bicicleta. Las lecturas del potenciómetro se pueden realizar fácilmente utilizando las funciones integradas del Arduino. La función TriggHandler puede imprimir el valor en la serie con el siguiente cambio.

void TriggHandler () {

/ * Lectura del valor del potenciómetro * / Serial.println (analogRead (ANALOGPIN)); }

El sensor Hall tiene un poco más de configuración antes de que podamos tener mediciones útiles. A diferencia del potenciómetro, el valor instantáneo del sensor de pasillo no es muy útil. Dado que estábamos tratando de medir la velocidad de la rueda, el tiempo entre disparos es lo que nos interesaba.

Cada función utilizada en el código Arduino lleva tiempo y si el imán se alinea con el sensor Hall en el momento equivocado, la medición podría retrasarse en el mejor de los casos o omitirse por completo en el peor. Obviamente, esto es malo porque el Arduino podría informar una velocidad que es MUCHO diferente a la velocidad real de la rueda.

Para evitar esto, usamos una función de Arduinos llamada interrupción de conexión que nos permite activar una función cada vez que un pin digital designado se activa con una señal ascendente. La función rpm_fun se adjunta a una interrupción con una sola línea de código agregada al código de configuración.

configuración vacía () {

sCmd.addCommand ("TRIGG", TriggHanlder); attachInterrupt (0, rpm_fun, RISING); Serial.begin (9600); } // La función rpm_fun se usa para calcular la velocidad y se define como; unsigned long lastRevolTime = 0; unsigned long revolSpeed = 0; void rpm_fun () {tiempo revolTime largo sin firmar = millis (); unsigned long deltaTime = revolTime - lastRevolTime; / * revolSpeed es el valor transmitido al código Arduino * / revolSpeed = 20000 / deltaTime; lastRevolTime = revolTime; } TriggHandler puede transmitir el resto de la información cuando se le solicite. void TriggHanlder () {/ * Lectura del valor del potenciómetro * / Serial.println (analogRead (ANALOGPIN)); Serial.println (revolSpeed); }

Ahora tenemos todos los bloques de construcción que se pueden usar para construir el código Arduino que transferirá datos a través de la serie cuando Unity realice una solicitud. Si desea tener una copia del código completo, puede descargarlo en nuestro GitHub. Para probar si el código se configuró correctamente, puede usar el monitor en serie para enviar TRIGG; asegúrese de establecer el final de la línea en Retorno de carro. La siguiente sección se centrará en cómo nuestros scripts de Unity pueden solicitar y recibir la información del Arduino.

Paso 3: recepción y procesamiento de datos

Recepción y procesamiento de datos
Recepción y procesamiento de datos

Unity es un gran software disponible de forma gratuita para los aficionados.

interesado en la creación de juegos; viene con una gran cantidad de funcionalidades que realmente pueden reducir el tiempo de configuración de ciertas cosas, como la programación de subprocesos o GPU (también conocido como sombreado) sin restringir lo que se puede hacer con los scripts de C #. Los microcontroladores Unity y Arduino se pueden usar juntos para crear experiencias interactivas únicas con un presupuesto relativamente pequeño.

El objetivo de este instructivo es ayudar a configurar la comunicación entre Unity y Arduino para que no profundicemos demasiado en la mayoría de las funciones disponibles con Unity. Hay muchos GRANDES tutoriales para la unidad y una comunidad increíble que podría hacer un trabajo mucho mejor explicando cómo funciona Unity. Sin embargo, hay un premio especial para aquellos que logran abrirse camino a través de este instructivo que sirve como un pequeño escaparate de lo que se podría hacer. Puedes descargar en nuestro Github nuestro primer intento de hacer una pista con física de bicicleta realista.

Primero, veamos lo mínimo que se debe hacer para comunicarse con un Arduino a través de la serie. Pronto se verá que este código no es adecuado para el juego, pero es bueno repasar cada paso y aprender cuáles son las limitaciones.

En Unity, cree una nueva escena con un único GameObject vacío llamado ArduinoReceive y adjunte un script de C # también llamado ArduinoReceive. Este script es donde agregaremos todo el código que maneja la comunicación con el Arduino.

Hay una biblioteca a la que se debe acceder antes de que podamos comunicarnos con los puertos serie de su computadora; Unity debe configurarse para permitir el uso de determinadas bibliotecas. Vaya a Editar-> ProjectSerring-> Reproductor y al lado del Nivel de compatibilidad de Api en Configuración, cambie. NET 2.0 Subconjunto a. NET 2.0. Ahora agregue el siguiente código en la parte superior del script;

usando System. IO. Ports;

Esto le permitirá acceder a la clase SerialPort que podría definir como un objeto para la clase ArduinoReceive. Hágalo privado para evitar cualquier interferencia de otro guión.

arduinoPort privado SerialPort;

El objeto arduinoPort se puede abrir seleccionando el puerto correcto (por ejemplo, en qué USB está conectado el Arduino) y una velocidad en baudios (es decir, la velocidad a la que se envía la información). Si no está seguro en qué puerto está conectado el Arduino, puede averiguarlo en el administrador de dispositivos o abriendo el IDE de Arduino. Para la velocidad en baudios, el valor predeterminado en la mayoría de los dispositivos es 9600, solo asegúrese de tener este valor en su código Arduino y debería funcionar.

El código ahora debería verse así;

usando System. Collections;

usando System. Collections. Generic; usando UnityEngine; usando System. IO. Ports; clase pública ArduinoReceive: MonoBehaviour {ArduinoPort SerialPort privado; // Use esto para la inicialización void Start () {arduinoPort = new SerialPort ("COM5", 9600); arduinoPort. Open (); WriteToArduino ("TRIGG"); }}

Lo más probable es que su número COM sea diferente. Si está en una MAC, su nombre COM puede tener un nombre similar a este /dev/cu.wchusbserial1420. Asegúrese de que el código de la sección 4 esté cargado en el Arduino y el monitor serial esté cerrado durante el resto de esta sección y que este código se compile sin problemas.

Enviemos ahora una solicitud al Arduino en cada fotograma y escribamos los resultados en la ventana de la consola. Agregue la función WriteToArduino a la clase ArduinoReceive. El retorno de carro y la nueva línea son necesarios para que el código Arduino analice correctamente la instrucción entrante.

Private void WriteToArduino (mensaje de cadena)

{mensaje = mensaje + "\ r / n"; arduinoPort. Write (mensaje); arduinoPort. BaseStream. Flush (); }

A continuación, esta función se puede llamar en el bucle de actualización.

Void Update ()

{WriteToArduino ("TRIGG"); Debug. Log ("Primer valor:" + arduinoPort. ReadLine ()); Debug. Log ("Segundo valor:" + arduinoPort. ReadLine ()); }

El código anterior es lo mínimo que necesita para leer los datos de un Arduino. Si presta mucha atención al FPS proporcionado por la unidad, debería ver una caída significativa en el rendimiento. En mi caso, pasa de unos 90 FPS sin leer / escribir a 20 FPS. Si su proyecto no requiere actualizaciones frecuentes, podría ser suficiente, pero para un videojuego, 20 FPS es demasiado bajo. La siguiente sección cubrirá cómo puede mejorar el rendimiento mediante el uso de subprocesos múltiples.

Paso 4: Optimización de la transferencia de datos

La sección anterior cubría cómo configurar los

comunicación entre el programa Arduino y Unity. El principal problema con este código es el rendimiento. En su implementación actual, Unity tiene que esperar a que Arduino reciba, procese y responda la solicitud. Durante ese tiempo, el código de Unity tiene que esperar a que se realice la solicitud y no hace nada más. Resolvimos este problema creando un hilo que manejará las solicitudes y almacenará la variable en el hilo principal.

Para comenzar, debemos incluir la biblioteca de subprocesos agregando;

usando System. Threading;

A continuación, configuramos la función que estamos comenzando en los hilos. AsynchronousReadFromArduino comienza escribiendo la solicitud al Arduino con la función WrtieToArduino. La lectura se incluye en un bloque try-catch, si el tiempo de espera de lectura, las variables permanecen nulas y se llama a la función OnArduinoInfoFail en lugar de OnArduinoInfoReceive.

A continuación definimos las funciones OnArduinoInfoFail y OnArduinoInfoReceive. Para este instructable, imprimimos los resultados en la consola, pero puede almacenar los resultados en las variables que necesita para su proyecto.

vacío privado OnArduinoInfoFail ()

{Debug. Log ("Error de lectura"); } private void OnArduinoInfoReceived (rotación de la cadena, velocidad de la cadena) {Debug. Log ("Readin Successfull"); Debug. Log ("Primer valor:" + rotación); Debug. Log ("Segundo valor:" + velocidad); }

El último paso es iniciar y detener los hilos que solicitarán los valores del Arduino. Debemos asegurarnos de que el último hilo esté terminado con su última tarea antes de comenzar uno nuevo. De lo contrario, se podrían realizar múltiples solicitudes al Arduino al mismo tiempo, lo que podría confundir al Arduino / Unity y producir resultados impredecibles.

hilo privado activeThread = null;

Void Update () {if (activeThread == null ||! activeThread. IsAlive) {activeThread = new Thread (AsynchronousReadFromArduino); activeThread. Start (); }}

Si compara el rendimiento del código con el que escribimos en la sección 5, el rendimiento debería mejorar significativamente.

vacío privado OnArduinoInfoFail ()

{Debug. Log ("Error de lectura"); }

Paso 5: ¿Dónde sigue?

¿Dónde sigue?
¿Dónde sigue?

Preparamos una demo que puedes descargar en nuestro Github (https://github.com/AlexandreDoucet/InfinityBike), descarga el código y el juego y recorre nuestra pista. Todo está configurado para un entrenamiento rápido y esperamos que pueda darle una idea de lo que podría construir si usa lo que le enseñamos con este instructivo.

Créditos

Colaboradores del proyecto

Alexandre Doucet (_Doucet_)

Maxime Boudreau (MxBoud)

Recursos externos [El motor de juegos de Unity] (https://unity3d.com)

Este proyecto comenzó después de leer el tutorial de Allan Zucconi "cómo integrar Arduino con Unity" (https://www.alanzucconi.com/2015/10/07/how-to-int…)

La solicitud del Arduino se maneja usando la biblioteca SerialCommand (https://github.com/kroimon/Arduino-SerialCommand)

Recomendado: