Innehållsförteckning:

Infinity Bike - Inomhus cykelträning Videospel: 5 steg
Infinity Bike - Inomhus cykelträning Videospel: 5 steg

Video: Infinity Bike - Inomhus cykelträning Videospel: 5 steg

Video: Infinity Bike - Inomhus cykelträning Videospel: 5 steg
Video: Dishonored 2 [Full Game Movie - All Cutscenes Longplay] Gameplay Walkthrough No Commentary [PC] 2024, Juni
Anonim
Image
Image
Material
Material

Under vintersäsongen, kalla dagar och dåligt väder har cyklistentusiaster bara några alternativ att träna med sin favoritsport. Vi letade efter ett sätt att göra inomhusträning med en cykel/tränarinställning lite mer underhållande men de flesta tillgängliga produkterna är antingen dyra eller helt enkelt tråkiga att använda. Det är därför vi började utveckla Infinity Bike som ett öppen källträningsspel. Infinity bike läser hastigheten och riktningen från din cykel och erbjuder en nivå av interaktivitet som inte är lätt att hitta med cykeltränare.

Vi drar nytta av enkelheten från Arduino mikrokontroller och några 3D -tryckta delar för att säkra billiga sensorer till en cykel monterad på en tränare. Informationen vidarebefordras till ett tv -spel tillverkat med den populära speltillverkningsmotorn, Unity. I slutet av denna instruerbara bör du kunna konfigurera dina egna sensorer på din cykel och överföra informationen om dina sensorer till Unity. Vi inkluderade till och med ett spår på vilket du kan åka med och testa din nya uppställning. Om du är intresserad av att bidra kan du kolla in vår GitHub.

Steg 1: Material

Material
Material

Materiallistan du behöver kan variera lite; för

Till exempel kommer din cykels storlek att bestämma längden på de bygelkablar du behöver, men här är de viktigaste delarna du behöver. Du kan förmodligen hitta billigare priser för varje bit på webbplatsen som AliExpress, men att vänta 6 månader på frakt är inte alltid ett alternativ, så att använda de lite dyrare delarna så att uppskattningen inte är sned.

1 x Arduino nano ($ 22,00)

1 x Mini Brödbräda ($ 1,33/enhet)

1 x 220 Ohm motstånd ($ 1,00/kit)

1 x 10K Potentiometer ($ 1,80/enhet)

1 x Hall -sensor ($ 0,96)

20 cm x 6 mm 3D -skrivare tandrem ($ 3,33)

1 kit x olika längder M3 skruvar och bultar ($ 6,82)

1 x Cykelhastighetsmätare ($ 0,98)

Vi monterade materialet ovan med 3D -tryckta delar. Filerna vi använde listas nedan och de är numrerade med samma konvention som bilden i början av detta avsnitt. Alla filer finns på Thingiverse. Du kan använda dem som de är men se till att måtten vi använde matchar din cykel.

1. FrameConnection_PotentiometerHolder_U_Holder.stl

2. FrameConnection_Spacer.stl

3. BreadboardFrameHolder.stl

4. Remskiva_PotentiometerSida.stl

5. Pot_PulleyConnection.stl

6. FrameConnection.stl

7. Remskiva_HandleBarSide_Print2.stl

8. FrameToHallSensorConnector.stl

9. PotHolder.stl

10. HallSensorAttach.stl

Steg 2: Läsa och överföra data till enhet

Läsa och överföra data till enhet
Läsa och överföra data till enhet

Arduino- och Unity -koden fungerar tillsammans för att samla in, överföra och bearbeta data från sensorerna på cykeln. Enhet kommer att begära värdet från Arduino genom att skicka en sträng genom serien och vänta på att Arduino ska svara med de begärda värdena.

Först förbereder vi Arduino med bibliotekets seriekommando som används för att hantera förfrågningar från Unity genom att para ihop en begärningssträng med en funktion. En grundläggande inställning för detta bibliotek kan göras enligt följande;

#inkludera "SerialCommand.h"

SerialCommand sCmd; void setup () {sCmd.addCommand ("TRIGG", TriggHanlder); Serial.begin (9600); } void loop () {while (Serial.available ()> 0) {sCmd.readSerial (); }} void TriggHandler () { /*Läs och överför sensorerna här* /}

Funktionen TriggHandler är kopplad till objektet SCmd. Om serien får en sträng som matchar det bifogade kommandot (i det här fallet TRIGG) körs funktionen TriggHandler.

Vi använder potentiometer för att mäta styrriktningen och en hallsensor för att mäta rotationen per minut på cykeln. Avläsningarna från potentiometern kan enkelt göras med hjälp av de inbyggda funktionerna från Arduino. Funktionen TriggHandler kan sedan skriva ut värdet till serien med följande ändring.

void TriggHandler () {

/*Läser värdet på potentiometern*/ Serial.println (analogRead (ANALOGPIN)); }

Hallsensorn har lite mer inställning innan vi kan ha användbara mätningar. I motsats till potentiometern är det direkta värdet för hallsensorn inte särskilt användbart. Eftersom vi försökte mäta hjulets hastighet, var tiden mellan triggers det som var intresserat.

Varje funktion som används i Arduino -koden tar tid och om magneten ligger i linje med Hall -sensorn vid fel tidpunkt kan mätningen i bästa fall försenas eller hoppas över i värsta fall. Detta är uppenbarligen dåligt eftersom Arduino kan rapportera en hastighet som är MYCKET annorlunda än hjulets faktiska hastighet.

För att undvika detta använder vi en funktion hos Arduinos som kallas attach interrupt som gör att vi kan utlösa en funktion när en angiven digital pin utlöses med en stigande signal. Funktionen rpm_fun är kopplad till ett avbrott med en enda kodrad som läggs till i installationskoden.

void setup () {

sCmd.addCommand ("TRIGG", TriggHanlder); attachInterrupt (0, rpm_fun, RISING); Serial.begin (9600); } // Funktionen rpm_fun används för att beräkna hastigheten och definieras som; osignerad lång lastRevolTime = 0; osignerad lång revolSpeed = 0; void rpm_fun () {unsigned long revolTime = millis (); osignerad lång deltaTime = revolTime - lastRevolTime; /*revolSpeed är värdet som överförs till Arduino -koden* / revolSpeed = 20000 / deltaTime; lastRevolTime = revolTime; } TriggHandler kan sedan överföra resten av informationen på begäran. void TriggHanlder () { /*Läser av värdet på potentiometern* / Serial.println (analogRead (ANALOGPIN)); Serial.println (revolSpeed); }

Vi har nu alla byggstenar som kan användas för att bygga Arduino -koden som kommer att överföra data via serien till när en begäran görs av Unity. Om du vill ha en kopia av hela koden kan du ladda ner den på vår GitHub. För att testa om koden var korrekt inställd kan du använda den seriella bildskärmen för att skicka TRIGG; se till att du ställer in raden som slutar på vagnretur. Nästa avsnitt kommer att fokusera på hur våra Unity -skript kan begära och ta emot informationen från Arduino.

Steg 3: Ta emot och bearbeta data

Ta emot och behandla data
Ta emot och behandla data

Unity är en bra programvara som är gratis tillgänglig för amatörer

intresserad av speltillverkning; det kommer med ett stort antal funktioner som verkligen kan minska tiden när du ställer in vissa saker som trådning eller GPU -programmering (AKA -skuggning) utan att begränsa vad som kan göras med C# -skripten. Unity och Arduino mikrokontroller kan användas tillsammans för att skapa unika interaktiva upplevelser med en relativt liten budget.

Fokus för denna instruerbara är att hjälpa till att konfigurera kommunikationen mellan Unity och Arduino så att vi inte dyker för djupt in i de flesta funktioner som finns med Unity. Det finns gott om bra handledning för enhet och en otrolig gemenskap som kan göra ett mycket bättre jobb för att förklara hur Unity fungerar. Det finns dock ett specialpris för dem som lyckas arbeta sig igenom denna instruerbara som fungerar som en liten uppvisning av vad som kan göras. Du kan ladda ner vårt första försök att göra ett spår med realistisk cykelfysik på vår Github.

Låt oss först gå igenom det minsta som måste göras för att kommunicera med en Arduino genom serien. Det kommer snabbt att framgå att denna kod inte är lämplig för spel, men det är bra att gå igenom varje steg och lära sig vilka begränsningar som är.

Skapa i Unity en ny scen med ett enda tomt GameObject som heter ArduinoReceive vid bifoga ett C# -skript som också heter ArduinoReceive. Detta skript är där vi kommer att lägga till all kod som hanterar kommunikationen med Arduino.

Det finns ett bibliotek som måste vara tillgängligt innan vi kan kommunicera med datorns seriella portar. Enhet måste konfigureras för att tillåta vissa bibliotek att användas. Gå till Edit-> ProjectSerring-> Player och bredvid Api-kompatibilitetsnivån under konfigurationsomkopplaren. NET 2.0-delmängd till. NET 2.0. Lägg nu till följande kod högst upp i skriptet;

använder System. IO. Ports;

Detta låter dig komma åt SerialPort -klassen som du kan definiera som ett objekt för ArduinoReceive -klassen. Gör det privat för att undvika störningar från ett annat skript.

privat SerialPort arduinoPort;

Objektet arduinoPort kan öppnas genom att välja rätt port (t.ex. i vilken USB Arduino är ansluten) och en överföringshastighet (dvs. hastigheten med vilken informationen skickas). Om du inte är säker på vilken port Arduino är ansluten kan du ta reda på det antingen i enhetshanteraren eller genom att öppna Arduino IDE. För överföringshastigheten är standardvärdet på de flesta enheter 9600, se bara till att du har det här värdet i din Arduino -kod och det borde fungera.

Koden ska nu se ut så här;

använder System. Collections;

använder System. Collections. Generic; använder UnityEngine; använder System. IO. Ports; public class ArduinoReceive: MonoBehaviour {private SerialPort arduinoPort; // Använd detta för initialisering void Start () {arduinoPort = ny SerialPort ("COM5", 9600); arduinoPort. Open (); WriteToArduino ("TRIGG"); }}

Ditt COM -nummer kommer sannolikt att vara annorlunda. Om du använder en MAC kan ditt COM -namn ha ett namn som ser ut så här /dev/cu.wchusbserial1420. Se till att koden från avsnitt 4 laddas upp till Arduino och att den seriella bildskärmen är stängd för resten av detta avsnitt och att koden kompileras utan problem.

Låt oss nu skicka en begäran till Arduino varje ram och skriva resultaten till konsolfönstret. Lägg till WriteToArduino -funktionen i klassen ArduinoReceive. Vagnretur och ny rad är nödvändiga för att Arduino -koden ska kunna analysera den inkommande instruktionen korrekt.

private void WriteToArduino (strängmeddelande)

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

Denna funktion kan sedan kallas i uppdateringsslingan.

ogiltig uppdatering ()

{WriteToArduino ("TRIGG"); Debug. Log ("First Value:" + arduinoPort. ReadLine ()); Debug. Log ("Second Value:" + arduinoPort. ReadLine ()); }

Koden ovan är det minsta du behöver för att läsa data från en Arduino. Om du är mycket uppmärksam på FPS som ges av enhet bör du se en betydande minskning av prestanda. I mitt fall går det från cirka 90 FPS utan att läsa/skriva till 20 FPS. Om ditt projekt inte kräver frekventa uppdateringar kan det vara tillräckligt, men för ett tv -spel är 20 FPS alldeles för lågt. Nästa avsnitt kommer att behandla hur du kan förbättra prestandan genom att använda flertrådning.

Steg 4: Optimera dataöverföring

Det föregående avsnittet täckte hur du konfigurerar grundläggande

kommunikation mellan Arduino och Unity -programmet. Det stora problemet med den här koden är prestanda. I den nuvarande implementeringen måste Unity vänta på att Arduino ska ta emot, bearbeta och svara på förfrågan. Under den tiden måste Unity -koden vänta på att begäran ska göras och gör inget annat. Vi löste detta problem genom att skapa en tråd som hanterar förfrågningarna och lagrar variabeln på huvudtråden.

För att börja måste vi inkludera trådbiblioteket genom att lägga till;

använder System. Threading;

Därefter konfigurerar vi den funktion som vi startar i trådarna. AsynchronousReadFromArduino börjar med att skriva förfrågan till Arduino med funktionen WrtieToArduino. Avläsningen är innesluten i ett try-catch-block. Om timeout för avläsning förblir variablerna noll och OnArduinoInfoFail-funktionen kallas istället för OnArduinoInfoReceive.

Därefter definierar vi funktionerna OnArduinoInfoFail och OnArduinoInfoReceive. För detta instruerbara, skriver vi ut resultaten till konsolen men du kan lagra resultaten i de variabler du behöver för ditt projekt.

privat tomrum OnArduinoInfoFail ()

{Debug. Log ("Läsning misslyckades"); } privat tomrum OnArduinoInfoReceived (strängrotation, stränghastighet) {Debug. Log ("Readin Sucessfull"); Debug. Log ("First Value:" + rotation); Debug. Log ("Andra värde:" + hastighet); }

Det sista steget är att starta och stoppa trådarna som kommer att begära värdena från Arduino. Vi måste se till att den sista tråden är klar med den sista uppgiften innan vi börjar en ny. Annars kan flera förfrågningar göras till Arduino på en gång som kan förvirra Arduino/Unity och ge oförutsägbara resultat.

private Thread activeThread = null;

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

Om du jämför kodens prestanda med den vi skrev i avsnitt 5, bör prestandan förbättras avsevärt.

privat tomrum OnArduinoInfoFail ()

{Debug. Log ("Läsning misslyckades"); }

Steg 5: Var nästa?

Var Nästa?
Var Nästa?

Vi förberedde en demo som du kan ladda ner på vår Github (https://github.com/AlexandreDoucet/InfinityBike), ladda ner koden och spelet och åka genom vårt spår. Det är klart för ett snabbt träningspass och vi hoppas att det kan ge dig en smak av vad du kan bygga om du använder det vi lärde dig med detta instruerbara.

Poäng

Projektbidragsgivare

Alexandre Doucet (_Doucet_)

Maxime Boudreau (MxBoud)

Externa resurser [Unity -spelmotorn] (https://unity3d.com)

Detta projekt startade efter att vi läst handledningen av Allan Zucconi "hur man integrerar Arduino med enhet" (https://www.alanzucconi.com/2015/10/07/how-to-int…)

Begäran från Arduino hanteras med SerialCommand-biblioteket (https://github.com/kroimon/Arduino-SerialCommand)

Rekommenderad: