Cuprins:

Infinity Bike - Joc video de antrenament pentru biciclete în interior: 5 pași
Infinity Bike - Joc video de antrenament pentru biciclete în interior: 5 pași

Video: Infinity Bike - Joc video de antrenament pentru biciclete în interior: 5 pași

Video: Infinity Bike - Joc video de antrenament pentru biciclete în interior: 5 pași
Video: lucruri despre MaxINFINITE 2024, Septembrie
Anonim
Image
Image
Materiale
Materiale

În timpul anotimpurilor de iarnă, zilelor reci și vremii nefavorabile, pasionații de bicicliști au doar câteva opțiuni pentru a-și exercita sportul preferat. Căutam o modalitate de a face antrenamentele în interior cu un set de biciclete / antrenori puțin mai distractiv, dar majoritatea produselor disponibile sunt costisitoare sau pur și simplu plictisitoare de utilizat. Acesta este motivul pentru care am început să dezvoltăm Infinity Bike ca un joc video de formare Open Source. Bicicleta Infinity citește viteza și direcția de pe bicicleta dvs. și oferă un nivel de interactivitate care nu poate fi găsit cu ușurință la antrenorii de biciclete.

Profităm de simplitatea disponibilă de la microcontrolerul Arduino și de câteva piese imprimate 3D pentru a asigura senzori ieftini la o bicicletă montată pe un trainer. Informațiile sunt transmise unui joc video realizat cu popularul motor de creare a jocurilor, Unity. Până la sfârșitul acestei instrucțiuni, ar trebui să vă puteți configura senzorii pe bicicletă și să transferați informațiile senzorilor către Unity. Am inclus chiar și o pistă pe care poți călări de-a lungul și să testezi noua ta configurare. Dacă sunteți interesat să contribuiți, puteți consulta GitHub.

Pasul 1: Materiale

Materiale
Materiale

Lista materialelor de care veți avea nevoie poate varia puțin; pentru

de exemplu, dimensiunea bicicletei dvs. va dicta lungimile cablurilor jumper de care aveți nevoie, dar aici sunt principalele părți de care veți avea nevoie. Probabil că ați putea găsi prețuri mai ieftine pentru fiecare piesă pe site-ul web, cum ar fi AliExpress, dar așteptarea a 6 luni pentru expediere nu este întotdeauna o opțiune, așa că foloseam piesele puțin mai scumpe, astfel încât estimarea nu este înclinată.

1 x Arduino nano (22,00 USD)

1 x Mini Breadboard (1,33 USD / unitate)

1 rezistor de 220 Ohm (1,00 USD / set)

1 x Potențiometru 10K (1,80 USD / unitate)

1 x senzor Hall (0,96 dolari)

Curea de distribuție a imprimantei 3D de 20 cm x 6 mm (3,33 USD)

1 kit x diverse șuruburi și șuruburi de lungime M3 (6,82 USD)

1 x magnet vitezometru pentru bicicletă (0,98 dolari)

Am montat materialul de mai sus cu piese imprimate 3D. Fișierele pe care le-am folosit sunt listate mai jos și sunt numerotate cu aceeași convenție ca și imaginea de la începutul acestei secțiuni. Toate fișierele pot fi găsite pe Thingiverse. Le puteți folosi așa cum este, dar asigurați-vă că dimensiunile pe care le-am folosit corespund bicicletei dvs.

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

Pasul 2: Citirea și transferul datelor către Unity

Citirea și transferul datelor către Unity
Citirea și transferul datelor către Unity

Codul Arduino și Unity vor lucra împreună pentru a colecta, transferați și procesați datele de la senzorii de pe bicicletă. Unity va solicita valoarea de la Arduino prin trimiterea unui șir prin serial și va aștepta ca Arduino să răspundă cu valorile solicitate.

Mai întâi, pregătim Arduino cu biblioteca Serial Command care este utilizată pentru a gestiona solicitările de la Unity prin asocierea unui șir de cerere cu o funcție. Un set de bază pentru această bibliotecă poate fi realizat după cum urmează;

#include "SerialCommand.h"

SerialCommand sCmd; void setup () {sCmd.addCommand ("TRIGG", TriggHanlder); Serial.begin (9600); } void loop () {while (Serial.available ()> 0) {sCmd.readSerial (); }} void TriggHandler () {/ * Citiți și transmiteți senzorii aici * /}

Funcția TriggHandler este atașată obiectului SCmd. Dacă serialul primește un șir care se potrivește cu comanda atașată (în acest caz TRIGG), funcția TriggHandler este executată.

Folosim potențiometru pentru a măsura direcția de direcție și un senzor de hale pentru a măsura rotația pe minut a bicicletei. Citirile din potențiometru pot fi făcute cu ușurință utilizând funcțiile de încorporare de la Arduino. Funcția TriggHandler poate imprima apoi valoarea pe serial cu următoarea modificare.

void TriggHandler () {

/ * Citirea valorii potențiometrului * / Serial.println (analogRead (ANALOGPIN)); }

Senzorul Hall are ceva mai multă setare înainte de a putea avea măsurători utile. Contrar potențiometrului, valoarea instantanee a senzorului de săli nu este foarte utilă. Din moment ce încercam să măsurăm viteza roții, timpul dintre declanșatoare este ceea ce ne interesa.

Fiecare funcție utilizată în codul Arduino necesită timp și dacă magnetul se aliniază cu senzorul Hall la momentul nepotrivit, măsurarea ar putea fi întârziată în cel mai bun caz sau omisă în totalitate în cel mai rău moment. Acest lucru este în mod evident rău, deoarece Arduino ar putea raporta o viteză care este MULTE diferită de viteza reală a roții.

Pentru a evita acest lucru, folosim o caracteristică a Arduinos numită atașare de întrerupere care ne permite să declanșăm o funcție ori de câte ori un pin digital desemnat este declanșat cu un semnal în creștere. Funcția rpm_fun este atașată la o întrerupere cu o singură linie de cod adăugată la codul de configurare.

configurare nulă () {

sCmd.addCommand ("TRIGG", TriggHanlder); attachInterrupt (0, rpm_fun, RISING); Serial.begin (9600); } // Funcția rpm_fun este utilizată pentru a calcula viteza și este definită ca; unsigned long lastRevolTime = 0; lung semnat revolSpeed = 0; void rpm_fun () {unsigned long revolTime = millis (); unsigned long deltaTime = revolTime - lastRevolTime; / * revolSpeed este valoarea transmisă codului Arduino * / revolSpeed = 20000 / deltaTime; lastRevolTime = revolTime; } TriggHandler poate transmite apoi restul informațiilor la cerere. void TriggHanlder () {/ * Citirea valorii potențiometrului * / Serial.println (analogRead (ANALOGPIN)); Serial.println (revolSpeed); }

Acum avem toate blocurile care pot fi utilizate pentru a construi codul Arduino, care va transfera date prin serial către momentul în care o cerere este făcută de Unity. Dacă doriți să aveți o copie a codului complet, îl puteți descărca de pe GitHub. Pentru a testa dacă codul a fost configurat corect, puteți utiliza monitorul serial pentru a trimite TRIGG; asigurați-vă că setați linia care se termină la Întoarcere transport. Următoarea secțiune se va concentra asupra modului în care scripturile noastre Unity pot solicita și primi informațiile de la Arduino.

Pasul 3: Primirea și prelucrarea datelor

Primirea și prelucrarea datelor
Primirea și prelucrarea datelor

Unity este un software excelent disponibil gratuit pentru amatori

interesat de realizarea jocurilor; vine cu un număr mare de funcționalități care pot reduce cu adevărat la timp setarea anumitor lucruri, cum ar fi threading sau programare GPU (umbrire AKA), fără a restricționa ceea ce se poate face cu scripturile C #. Microcontrolerele Unity și Arduino pot fi utilizate împreună pentru a crea experiențe interactive unice cu un buget relativ mic.

Accentul acestui instructable este de a ajuta la configurarea comunicării dintre Unity și Arduino, astfel încât să nu ne scufundăm prea adânc în majoritatea caracteristicilor disponibile cu Unity. Există o mulțime de tutoriale MARI pentru unitate și o comunitate incredibilă care ar putea face o treabă mult mai bună explicând modul în care funcționează Unity. Cu toate acestea, există un premiu special pentru cei care reușesc să-și croiască drum prin acest instructabil care servește ca o mică vitrină a ceea ce s-ar putea face. Puteți descărca pe Github prima noastră încercare de a face o pistă cu fizică realistă a bicicletelor.

În primul rând, să trecem prin minimul care trebuie făcut pentru a comunica cu un Arduino prin serial. Va fi rapid evident că acest cod nu este potrivit pentru joc, dar este bine să parcurgeți fiecare pas și să aflați care sunt limitele.

În Unity, creați o scenă nouă cu un singur GameObject gol, numit ArduinoReceive at atașat, un script C # numit și ArduinoReceive. În acest script vom adăuga tot codul care gestionează comunicarea cu Arduino.

Există o bibliotecă care trebuie accesată înainte de a putea comunica cu porturile seriale ale computerului dvs.; Unitatea trebuie configurată pentru a permite utilizarea anumitor biblioteci. Mergeți la Edit-> ProjectSerring-> Player și lângă Nivelul de compatibilitate Api sub comutator Configurare. NET 2.0 Subset la. NET 2.0. Acum adăugați următorul cod în partea de sus a scriptului;

folosind System. IO. Ports;

Acest lucru vă va permite să accesați clasa SerialPort pe care o puteți defini ca obiect al clasei ArduinoReceive. Faceți-l privat pentru a evita orice interferență de la un alt script.

private SerialPort arduinoPort;

Obiectul arduinoPort poate fi deschis prin selectarea portului corect (de ex. În care USB este conectat Arduino) și a unei viteze în baud (adică viteza cu care sunt trimise informațiile). Dacă nu sunteți sigur în ce port este conectat Arduino, puteți afla fie în managerul de dispozitive, fie deschizând ID-ul Arduino. Pentru rata de transmisie, valoarea implicită pe majoritatea dispozitivelor este 9600, asigurați-vă că aveți această valoare în codul Arduino și ar trebui să funcționeze.

Codul ar trebui să arate acum;

folosind System. Collections;

folosind System. Collections. Generic; folosind UnityEngine; folosind System. IO. Ports; public class ArduinoReceive: MonoBehaviour {private SerialPort arduinoPort; // Folosiți acest lucru pentru inițializare void Start () {arduinoPort = new SerialPort ("COM5", 9600); arduinoPort. Open (); WriteToArduino ("TRIGG"); }}

Numărul dvs. COM va fi foarte probabil diferit. Dacă sunteți pe un MAC, numele dvs. COM poate avea un nume care seamănă cu acest /dev/cu.wchusbserial1420. Asigurați-vă că codul din secțiunea 4 este încărcat pe Arduino și că monitorul serial este închis pentru restul acestei secțiuni și că acest cod se compilează fără probleme.

Să trimitem acum o cerere către Arduino fiecare cadru și să scriem rezultatele în fereastra consolei. Adăugați funcția WriteToArduino la clasa ArduinoReceive. Returul transportului și linia nouă sunt necesare pentru ca codul Arduino să analizeze corect instrucțiunile de intrare.

private void WriteToArduino (mesaj șir)

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

Această funcție poate fi apoi apelată în bucla de actualizare.

actualizare nulă ()

{WriteToArduino ("TRIGG"); Debug. Log ("Prima valoare:" + arduinoPort. ReadLine ()); Debug. Log ("A doua valoare:" + arduinoPort. ReadLine ()); }

Codul de mai sus este minimul necesar pentru a citi datele de pe un Arduino. Dacă acordați o atenție deosebită FPS-ului dat de unitate, ar trebui să vedeți o scădere semnificativă a performanței. În cazul meu, trece de la aproximativ 90 FPS fără citire / scriere la 20 FPS. Dacă proiectul dvs. nu necesită actualizări frecvente, ar putea fi suficient, dar pentru un joc video, 20 FPS este mult prea mic. Următoarea secțiune va acoperi modul în care puteți îmbunătăți performanța utilizând multi threading.

Pasul 4: Optimizarea transferului de date

Secțiunea anterioară a prezentat modul de configurare de bază

comunicare între programul Arduino și Unity. Problema majoră cu acest cod este performanța. În implementarea actuală, Unity trebuie să aștepte ca Arduino să primească, să proceseze și să răspundă la cerere. În acest timp, codul Unity trebuie să aștepte ca cererea să fie făcută și nu face altceva. Am rezolvat această problemă prin crearea unui thread care va gestiona solicitările și va stoca variabila pe thread-ul principal.

Pentru început, trebuie să includem biblioteca de fire prin adăugare;

folosind System. Threading;

Apoi, configurăm funcția pe care o pornim în fire. AsynchronousReadFromArduino începe prin scrierea cererii către Arduino cu funcția WrtieToArduino. Citirea este inclusă într-un bloc try-catch, dacă expirarea timpului de citire, variabilele rămân nule și funcția OnArduinoInfoFail este apelată în locul OnArduinoInfoReceive.

Apoi definim funcțiile OnArduinoInfoFail și OnArduinoInfoReceive. Pentru acest lucru instructabil, imprimăm rezultatele pe consolă, dar puteți stoca rezultatele în variabilele de care aveți nevoie pentru proiectul dvs.

private void OnArduinoInfoFail ()

{Debug. Log („Lectura eșuată”); } private void OnArduinoInfoReceived (rotație șir, viteză șir) {Debug. Log ("Readin Sucessfull"); Debug. Log ("Prima valoare:" + rotație); Debug. Log ("A doua valoare:" + viteză); }

Ultimul pas este de a porni și opri firele care vor solicita valorile de la Arduino. Trebuie să ne asigurăm că ultimul fir este finalizat cu ultima sa sarcină înainte de a începe unul nou. În caz contrar, ar putea fi făcute mai multe cereri către Arduino simultan, ceea ce ar putea confunda Arduino / Unity și ar putea produce rezultate imprevizibile.

fir privat activeThread = nul;

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

Dacă comparați performanța codului cu cea pe care am scris-o la secțiunea 5, performanța ar trebui îmbunătățită semnificativ.

private void OnArduinoInfoFail ()

{Debug. Log („Lectura eșuată”); }

Pasul 5: Unde urmează?

Unde urmează?
Unde urmează?

Am pregătit o demonstrație pe care o puteți descărca pe Github (https://github.com/AlexandreDoucet/InfinityBike), descărcați codul și jocul și călătoriți prin pista noastră. Totul este pregătit pentru un antrenament rapid și sperăm că vă poate oferi o idee despre ceea ce ați putea construi dacă utilizați ceea ce v-am învățat cu acest instructable.

credite

Contribuitori ai proiectului

Alexandre Doucet (_Doucet_)

Maxime Boudreau (MxBoud)

Resurse externe [Motorul de joc Unity] (https://unity3d.com)

Acest proiect a început după ce am citit tutorialul lui Allan Zucconi „cum să integrezi Arduino cu Unity” (https://www.alanzucconi.com/2015/10/07/how-to-int…)

Solicitarea de la Arduino este tratată utilizând biblioteca SerialCommand (https://github.com/kroimon/Arduino-SerialCommand)

Recomandat: