Sommario:

Infinity Bike - Videogioco di allenamento in bici al chiuso: 5 passaggi
Infinity Bike - Videogioco di allenamento in bici al chiuso: 5 passaggi

Video: Infinity Bike - Videogioco di allenamento in bici al chiuso: 5 passaggi

Video: Infinity Bike - Videogioco di allenamento in bici al chiuso: 5 passaggi
Video: Maranza cerca di prendermi la moto 2024, Settembre
Anonim
Image
Image
Materiali
Materiali

Durante le stagioni invernali, le giornate fredde e il maltempo, gli appassionati di ciclismo hanno solo poche opzioni per allenarsi praticando il loro sport preferito. Stavamo cercando un modo per rendere un po' più divertente l'allenamento indoor con una configurazione bici/trainer, ma la maggior parte dei prodotti disponibili sono costosi o semplicemente noiosi da usare. Questo è il motivo per cui abbiamo iniziato a sviluppare Infinity Bike come videogioco di formazione Open Source. Infinity bike legge la velocità e la direzione dalla tua bicicletta e offre un livello di interattività che non può essere facilmente trovato con i rulli da bicicletta.

Sfruttiamo la semplicità disponibile dal microcontrollore Arduino e alcune parti stampate in 3D per fissare sensori economici a una bicicletta montata su un trainer. Le informazioni vengono trasmesse a un videogioco realizzato con il popolare motore di creazione di giochi, Unity. Entro la fine di questa istruzione, dovresti essere in grado di configurare i tuoi sensori sulla tua bici e trasferire le informazioni dei tuoi sensori a Unity. Abbiamo anche incluso una pista su cui puoi guidare e testare il tuo nuovo assetto. Se sei interessato a contribuire puoi dare un'occhiata al nostro GitHub.

Passaggio 1: materiali

Materiali
Materiali

L'elenco dei materiali di cui avrai bisogno potrebbe variare leggermente; per

ad esempio, le dimensioni della tua bici determineranno le lunghezze dei cavi di avviamento necessari, ma qui ci sono le parti principali di cui avrai bisogno. Probabilmente potresti trovare prezzi più economici per ogni pezzo su siti Web come AliExpress, ma aspettare 6 mesi per la spedizione non è sempre un'opzione, quindi utilizzavi le parti leggermente più costose in modo che la stima non sia distorta.

1 x Arduino nano ($ 22,00)

1 x Mini tagliere ($ 1,33/unità)

1 x 220 Ohm resistore ($ 1,00/kit)

1 x 10K Potenziometro ($ 1,80/unità)

1 x sensore Hall ($ 0,96)

Cinghia dentata per stampante 3D da 20 cm x 6 mm (3,33 $)

1 kit x viti e bulloni M3 di varie lunghezze ($ 6,82)

1 x magnete del tachimetro della bicicletta ($ 0,98)

Abbiamo montato il materiale sopra con parti stampate in 3D. I file che abbiamo usato sono elencati di seguito e sono numerati con la stessa convenzione dell'immagine all'inizio di questa sezione. Tutti i file possono essere trovati su Thingiverse. Puoi usarli così come sono, ma assicurati che le dimensioni che abbiamo usato corrispondano alla tua bici.

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

Passaggio 2: lettura e trasferimento dei dati in Unity

Lettura e trasferimento di dati a Unity
Lettura e trasferimento di dati a Unity

Il codice Arduino e Unity lavoreranno insieme per raccogliere, trasferire ed elaborare i dati dai sensori sulla bici. Unity richiederà il valore ad Arduino inviando una stringa tramite la seriale e attenderà che Arduino risponda con i valori richiesti.

Per prima cosa prepariamo Arduino con la libreria Serial Command che serve per gestire le richieste da Unity accoppiando una stringa di richiesta con una funzione. Una configurazione di base per questa libreria può essere eseguita come segue;

#include "SerialCommand.h"

SerialCommand sCmd; void setup() { sCmd.addCommand("TRIGG", TriggHanlder); Serial.begin(9600); } void loop() { while (Serial.available() > 0) { sCmd.readSerial(); } } void TriggHandler () { /*Leggi e trasmetti i sensori qui*/ }

La funzione TriggHandler è collegata all'oggetto SCmd. Se il seriale riceve una stringa che corrisponde al comando allegato (in questo caso TRIGG), viene eseguita la funzione TriggHandler.

Utilizziamo un potenziometro per misurare la direzione di sterzata e un sensore di hall per misurare la rotazione al minuto della bicicletta. Le letture dal potenziometro possono essere facilmente effettuate utilizzando le funzioni integrate di Arduino. La funzione TriggHandler può quindi stampare il valore sul seriale con la seguente modifica.

void TriggHandler (){

/*Lettura del valore del potenziometro*/ Serial.println(analogRead(ANALOGPIN)); }

Il sensore di Hall ha un po' più di configurazione prima di poter avere misurazioni utili. Contrariamente al potenziometro, il valore istantaneo del sensore di hall non è molto utile. Poiché stavamo cercando di misurare la velocità della ruota, il tempo tra i trigger è ciò che interessava.

Ogni funzione utilizzata nel codice Arduino richiede tempo e se il magnete si allinea con il sensore Hall nel momento sbagliato, la misurazione potrebbe essere ritardata nel migliore dei casi o saltata del tutto nel peggiore dei casi. Questo è ovviamente un male perché Arduino potrebbe riportare una velocità MOLTO diversa dalla velocità effettiva della ruota.

Per evitare ciò, utilizziamo una funzionalità di Arduino chiamata interrupt di collegamento che ci consente di attivare una funzione ogni volta che un pin digitale designato viene attivato con un segnale in aumento. La funzione rpm_fun è collegata a un interrupt con una singola riga di codice aggiunta al codice di installazione.

void setup(){

sCmd.addCommand("TRIGG", TriggHanlder); attachInterrupt(0, rpm_fun, RISING); Serial.begin(9600); } //La funzione rpm_fun è usata per calcolare la velocità ed è definita come; unsigned long lastRevolTime = 0; velocità di rotazione lunga senza segno = 0; void rpm_fun() { unsigned long revolTime = millis(); deltaTime lungo unsigned = revolTime - lastRevolTime; /*revolSpeed è il valore trasmesso al codice Arduino*/ revolSpeed = 20000 / deltaTime; lastRevolTime = revolTime; } TriggHandler può quindi trasmettere il resto delle informazioni quando richiesto. void TriggHanlder() { /*Lettura del valore del potenziometro*/ Serial.println(analogRead(ANALOGPIN)); Serial.println(revolSpeed); }

Ora abbiamo tutti i blocchi di costruzione che possono essere usati per costruire il codice Arduino che trasferirà i dati attraverso il seriale a quando viene effettuata una richiesta da Unity. Se vuoi avere una copia del codice completo, puoi scaricarlo sul nostro GitHub. Per verificare se il codice è stato impostato correttamente, è possibile utilizzare il monitor seriale per inviare TRIGG; assicurati di impostare la fine della riga su Ritorno a capo. La prossima sezione si concentrerà su come i nostri script Unity possono richiedere e ricevere le informazioni da Arduino.

Passaggio 3: ricezione ed elaborazione dei dati

Ricezione ed elaborazione dei dati
Ricezione ed elaborazione dei dati

Unity è un ottimo software disponibile gratuitamente per gli hobbisti

interessato alla creazione di giochi; viene fornito con un gran numero di funzionalità che possono davvero ridurre i tempi di impostazione di determinate cose come il threading o la programmazione della GPU (ombreggiatura AKA) senza limitare ciò che può essere fatto con gli script C#. I microcontrollori Unity e Arduino possono essere utilizzati insieme per creare esperienze interattive uniche con un budget relativamente ridotto.

L'obiettivo di questa istruzione è aiutare a configurare la comunicazione tra Unity e Arduino in modo da non approfondire la maggior parte delle funzionalità disponibili con Unity. Ci sono un sacco di GRANDI tutorial per Unity e un'incredibile community che potrebbe fare un lavoro molto migliore spiegando come funziona Unity. Tuttavia, c'è un premio speciale per coloro che riescono a farsi strada attraverso questo istruttore che funge da piccola vetrina di ciò che potrebbe essere fatto. Puoi scaricare sul nostro Github il nostro primo tentativo di creare una pista con una fisica della bici realistica.

Per prima cosa, esaminiamo il minimo indispensabile che deve essere fatto per comunicare con un Arduino attraverso il seriale. Sarà subito evidente che questo codice non è adatto al gameplay, ma è bene seguire ogni passaggio e scoprire quali sono i limiti.

In Unity, crea una nuova scena con un singolo GameObject vuoto chiamato ArduinoReceive in allegare uno script C# chiamato anche ArduinoReceive. Questo script è dove aggiungeremo tutto il codice che gestisce la comunicazione con Arduino.

C'è una libreria che deve essere accessibile prima di poter comunicare con le porte seriali del tuo computer; Unity deve essere configurato per consentire l'utilizzo di determinate librerie. Vai a Modifica->ProjectSerring->Player e accanto al Livello di compatibilità API in Configurazione passa. NET 2.0 Sottoinsieme a. NET 2.0. Ora aggiungi il seguente codice nella parte superiore dello script;

utilizzando System. IO. Ports;

Questo ti permetterà di accedere alla classe SerialPort che potresti definire come un oggetto per la classe ArduinoReceive. Rendilo privato per evitare qualsiasi interferenza da parte di un altro script.

Porta seriale privata arduinoPort;

L'oggetto arduinoPort può essere aperto selezionando la porta corretta (es. USB in cui è collegato Arduino) e un baud rate (cioè la velocità con cui vengono inviate le informazioni). Se non sei sicuro in quale porta è collegato Arduino, puoi scoprirlo in Gestione dispositivi o aprendo l'IDE di Arduino. Per la velocità di trasmissione, il valore predefinito sulla maggior parte dei dispositivi è 9600, assicurati di avere questo valore nel codice Arduino e dovrebbe funzionare.

Il codice dovrebbe ora assomigliare a questo;

utilizzando System. Collections;

utilizzando System. Collections. Generic; utilizzando UnityEngine; utilizzando System. IO. Ports; public class ArduinoReceive: MonoBehaviour { private SerialPort arduinoPort; // Usa questo per l'inizializzazione void Start() { arduinoPort = new SerialPort("COM5", 9600); arduinoPort. Open(); WriteToArduino("TRIGG"); } }

Molto probabilmente il tuo numero COM sarà diverso. Se sei su un MAC, il tuo nome COM potrebbe avere un nome simile a questo /dev/cu.wchusbserial1420. Assicurati che il codice della sezione 4 sia caricato su Arduino e che il monitor seriale sia chiuso per il resto di questa sezione e che questo codice venga compilato senza problemi.

Inviamo ora una richiesta ad Arduino ogni frame e scriviamo i risultati nella finestra della console. Aggiungi la funzione WriteToArduino alla classe ArduinoReceive. Il ritorno a capo e la nuova riga sono necessari affinché il codice Arduino analizzi correttamente l'istruzione in entrata.

private void WriteToArduino (messaggio stringa)

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

Questa funzione può quindi essere chiamata nel ciclo di aggiornamento.

vuoto Aggiorna ()

{ WriteToArduino ("TRIGG"); Debug. Log("Primo valore: " + arduinoPort. ReadLine()); Debug. Log("Secondo valore: " + arduinoPort. ReadLine()); }

Il codice sopra è il minimo indispensabile per leggere i dati da un Arduino. Se presti molta attenzione all'FPS dato da unity, dovresti vedere un calo significativo delle prestazioni. Nel mio caso, passa da circa 90 FPS senza lettura/scrittura a 20 FPS. Se il tuo progetto non richiede aggiornamenti frequenti potrebbe essere sufficiente, ma per un videogioco 20 FPS sono troppo bassi. La prossima sezione illustrerà come migliorare le prestazioni utilizzando il multi threading.

Passaggio 4: ottimizzazione del trasferimento dei dati

La sezione precedente ha spiegato come impostare la base

comunicazione tra Arduino e il programma Unity. Il problema principale con questo codice sono le prestazioni. Nella sua attuale implementazione, Unity deve attendere che Arduino riceva, elabori e risponda alla richiesta. Durante quel tempo, il codice Unity deve attendere che la richiesta venga eseguita e non fa altro. Abbiamo risolto questo problema creando un thread che gestirà le richieste e memorizzerà la variabile nel thread principale.

Per iniziare, dobbiamo includere la libreria di threading aggiungendo;

usando System. Threading;

Successivamente, impostiamo la funzione che stiamo avviando nei thread. AsynchronousReadFromArduino inizia scrivendo la richiesta su Arduino con la funzione WrtieToArduino. La lettura è racchiusa in un blocco try-catch, se il read timeout, le variabili rimangono nulle e viene chiamata la funzione OnArduinoInfoFail al posto della OnArduinoInfoReceive.

Successivamente definiamo le funzioni OnArduinoInfoFail e OnArduinoInfoReceive. Per questo istruibile, stampiamo i risultati sulla console ma puoi memorizzare i risultati nelle variabili necessarie per il tuo progetto.

vuoto privato OnArduinoInfoFail()

{ Debug. Log("Lettura non riuscita"); } private void OnArduinoInfoReceived(rotazione della stringa, velocità della stringa) { Debug. Log("Readin Successfull"); Debug. Log("Primo valore: " + rotazione); Debug. Log("Secondo valore: " + velocità); }

L'ultimo passaggio consiste nell'avviare e interrompere i thread che richiederanno i valori da Arduino. Dobbiamo assicurarci che l'ultimo thread sia terminato con la sua ultima attività prima di iniziarne uno nuovo. Altrimenti, potrebbero essere fatte più richieste ad Arduino contemporaneamente, il che potrebbe confondere Arduino/Unity e produrre risultati imprevedibili.

thread privato activeThread = null;

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

Se si confrontano le prestazioni del codice con quelle che abbiamo scritto nella sezione 5, le prestazioni dovrebbero essere notevolmente migliorate.

vuoto privato OnArduinoInfoFail()

{ Debug. Log("Lettura non riuscita"); }

Passaggio 5: dove il prossimo?

Dove Avanti?
Dove Avanti?

Abbiamo preparato una demo che puoi scaricare sul nostro Github (https://github.com/AlexandreDoucet/InfinityBike), scaricare il codice e il gioco e percorrere la nostra pista. È tutto pronto per un allenamento veloce e speriamo che possa darti un assaggio di ciò che potresti costruire se usi ciò che ti abbiamo insegnato con questo tutorial.

Titoli di coda

Collaboratori al progetto

Alexandre Doucet (_Doucet_)

Maxime Boudreau (MxBoud)

Risorse esterne [Il motore di gioco Unity](https://unity3d.com)

Questo progetto è iniziato dopo aver letto il tutorial di Allan Zucconi "come integrare Arduino con Unity" (https://www.alanzucconi.com/2015/10/07/how-to-int…)

Le richieste da Arduino vengono gestite utilizzando la libreria SerialCommand (https://github.com/kroimon/Arduino-SerialCommand)

Consigliato: