Table des matières:

Infinity Bike - Jeu vidéo d'entraînement de vélo d'intérieur : 5 étapes
Infinity Bike - Jeu vidéo d'entraînement de vélo d'intérieur : 5 étapes

Vidéo: Infinity Bike - Jeu vidéo d'entraînement de vélo d'intérieur : 5 étapes

Vidéo: Infinity Bike - Jeu vidéo d'entraînement de vélo d'intérieur : 5 étapes
Vidéo: Comment débuter sur Zwift ? 2024, Juin
Anonim
Image
Image
Matériaux
Matériaux

Pendant les saisons hivernales, les journées froides et le mauvais temps, les amateurs de cyclisme n'ont que peu d'options pour faire de l'exercice en pratiquant leur sport préféré. Nous cherchions un moyen de rendre l'entraînement en salle avec une configuration vélo/entraîneur un peu plus divertissant, mais la plupart des produits disponibles sont soit coûteux, soit tout simplement ennuyeux à utiliser. C'est pourquoi nous avons commencé à développer Infinity Bike en tant que jeu vidéo d'entraînement Open Source. Le vélo Infinity lit la vitesse et la direction de votre vélo et offre un niveau d'interactivité difficile à trouver avec les home trainers.

Nous profitons de la simplicité offerte par le microcontrôleur Arduino et de quelques pièces imprimées en 3D pour fixer des capteurs peu coûteux à un vélo monté sur un home trainer. Les informations sont transmises à un jeu vidéo réalisé avec le célèbre moteur de création de jeux, Unity. À la fin de cette instructable, vous devriez pouvoir configurer vos propres capteurs sur votre vélo et transférer les informations de vos capteurs vers Unity. Nous avons même inclus une piste sur laquelle vous pouvez rouler et tester votre nouvelle configuration. Si vous souhaitez contribuer, vous pouvez consulter notre GitHub.

Étape 1: Matériaux

Matériaux
Matériaux

La liste de matériel dont vous aurez besoin peut varier un peu; pour

Par exemple, la taille de votre vélo dictera la longueur des câbles de démarrage dont vous avez besoin, mais voici les principales pièces dont vous aurez besoin. Vous pourriez probablement trouver des prix moins chers pour chaque pièce sur un site Web comme AliExpress, mais attendre 6 mois pour l'expédition n'est pas toujours une option, donc nous utilisions les pièces légèrement plus chères afin que l'estimation ne soit pas faussée.

1 x Arduino nano (22,00 $)

1 x mini planche à pain (1,33 $/unité)

1 résistance de 220 Ohm (1,00$/kit)

1 potentiomètre 10K (1,80$/unité)

1 x capteur à effet Hall (0,96 $)

Courroie de distribution d'imprimante 3D 20 cm x 6 mm (3,33 $)

1 kit x vis et boulons M3 de diverses longueurs (6,82 $)

1 x aimant de compteur de vitesse de vélo (0,98 $)

Nous avons monté le matériau ci-dessus avec des pièces imprimées en 3D. Les fichiers que nous avons utilisés sont listés ci-dessous et ils sont numérotés avec la même convention que l'image au début de cette section. Tous les fichiers peuvent être trouvés sur Thingiverse. Vous pouvez les utiliser tels quels, mais assurez-vous que les dimensions que nous avons utilisées correspondent à votre vélo.

1. FrameConnection_PotentiometerHolder_U_Holder.stl

2. FrameConnection_Spacer.stl

3. BreadboardFrameHolder.stl

4. Poulie_PotentiomètreSide.stl

5. Pot_PulleyConnection.stl

6. FrameConnection.stl

7. Poulie_HandleBarSide_Print2.stl

8. FrameToHallSensorConnector.stl

9. PotHolder.stl

10. HallSensorAttach.stl

Étape 2: Lecture et transfert de données vers Unity

Lecture et transfert de données vers Unity
Lecture et transfert de données vers Unity

Le code Arduino et Unity travailleront ensemble pour collecter, transférer et traiter les données des capteurs du vélo. Unity demandera la valeur à l'Arduino en envoyant une chaîne via la série et attendra que l'Arduino réponde avec les valeurs demandées.

Tout d'abord, nous préparons l'Arduino avec la bibliothèque Serial Command qui permet de gérer les requêtes de Unity en associant une chaîne de requête à une fonction. Une configuration de base pour cette bibliothèque peut être effectuée comme suit;

#include "SerialCommand.h"

Commande série sCmd; void setup() { sCmd.addCommand("TRIGG", TriggHanlder); Serial.begin(9600); } boucle vide() { while (Serial.available() > 0) { sCmd.readSerial(); } } void TriggHandler () { /*Lire et transmettre les capteurs ici*/ }

La fonction TriggHandler est attachée à l'objet SCmd. Si la série reçoit une chaîne qui correspond à la commande jointe (dans ce cas TRIGG), la fonction TriggHandler est exécutée.

Nous utilisons un potentiomètre pour mesurer la direction de la direction et un capteur Halls pour mesurer la rotation par minute du vélo. Les lectures du potentiomètre peuvent être facilement effectuées à l'aide des fonctions intégrées de l'Arduino. La fonction TriggHandler peut alors imprimer la valeur sur la série avec la modification suivante.

void TriggHandler (){

/*Lecture de la valeur du potentiomètre*/ Serial.println(analogRead(ANALOGPIN)); }

Le capteur Hall a un peu plus de configuration avant que nous puissions avoir des mesures utiles. Contrairement au potentiomètre, la valeur instantanée du capteur halls n'est pas très utile. Puisque nous essayions de mesurer la vitesse de la roue, le temps entre les déclenchements est ce qui nous intéressait.

Chaque fonction utilisée dans le code Arduino prend du temps et si l'aimant s'aligne avec le capteur Hall au mauvais moment, la mesure pourrait être au mieux retardée ou complètement ignorée au pire. C'est évidemment mauvais car l'Arduino pourrait signaler une vitesse BEAUCOUP différente de la vitesse réelle de la roue.

Pour éviter cela, nous utilisons une fonctionnalité d'Arduinos appelée interruption d'attache qui nous permet de déclencher une fonction chaque fois qu'une broche numérique désignée est déclenchée avec un signal montant. La fonction rpm_fun est attachée à une interruption avec une seule ligne de code ajoutée au code de configuration.

void setup(){

sCmd.addCommand("TRIGG", TriggHanlder); attachInterrupt(0, rpm_fun, RISING); Serial.begin(9600); } //La fonction rpm_fun est utilisée pour calculer la vitesse et est définie comme; non signé long lastRevolTime = 0; vitesse de rotation longue non signée = 0; void rpm_fun() { long revolTime non signé = millis(); deltaTime long non signé = revolTime - lastRevolTime; /*revolSpeed est la valeur transmise au code Arduino*/ revolSpeed = 20000 / deltaTime; lastRevolTime = revolTime; } TriggHandler peut alors transmettre le reste des informations sur demande. void TriggHanlder () { /*Lecture de la valeur du potentiomètre*/ Serial.println(analogRead(ANALOGPIN)); Serial.println(revolSpeed); }

Nous avons maintenant tous les blocs de construction qui peuvent être utilisés pour créer le code Arduino qui transférera les données via la série jusqu'à ce qu'une demande soit faite par Unity. Si vous souhaitez avoir une copie du code complet, vous pouvez le télécharger sur notre GitHub. Pour tester si le code a été configuré correctement, vous pouvez utiliser le moniteur série pour envoyer TRIGG; assurez-vous de définir la fin de la ligne sur Retour chariot. La section suivante se concentrera sur la façon dont nos scripts Unity peuvent demander et recevoir les informations de l'Arduino.

Étape 3: Réception et traitement des données

Réception et traitement des données
Réception et traitement des données

Unity est un excellent logiciel disponible gratuitement pour les amateurs

intéressé par la création de jeux; il est livré avec un grand nombre de fonctionnalités qui peuvent vraiment réduire le temps de configuration de certaines choses telles que le threading ou la programmation GPU (AKA shading) sans restreindre ce qui peut être fait avec les scripts C#. Les microcontrôleurs Unity et Arduino peuvent être utilisés ensemble pour créer des expériences interactives uniques avec un budget relativement petit.

L'objectif de cette instructable est d'aider à configurer la communication entre Unity et l'Arduino afin que nous ne plongeons pas trop profondément dans la plupart des fonctionnalités disponibles avec Unity. Il existe de nombreux excellents didacticiels pour Unity et une communauté incroyable qui pourrait faire un bien meilleur travail pour expliquer le fonctionnement de Unity. Cependant, il existe un prix spécial pour ceux qui parviennent à se frayer un chemin à travers cette instructable qui sert de petite vitrine de ce qui pourrait être fait. Vous pouvez télécharger sur notre Github notre première tentative de création d'une piste avec une physique de vélo réaliste.

Tout d'abord, passons en revue le strict minimum qui doit être fait pour communiquer avec un Arduino via la série. Il apparaîtra rapidement que ce code n'est pas adapté au gameplay, mais il est bon de parcourir chaque étape et d'apprendre quelles sont les limites.

Dans Unity, créez une nouvelle scène avec un seul GameObject vide nommé ArduinoReceive et attachez un script C# également nommé ArduinoReceive. Ce script est l'endroit où nous ajouterons tout le code qui gère la communication avec l'Arduino.

Il y a une bibliothèque qui doit être accessible avant que nous puissions communiquer avec les ports série de votre ordinateur; Unity doit être configuré pour permettre l'utilisation de certaines bibliothèques. Accédez à Édition->ProjectSerring->Lecteur et à côté du niveau de compatibilité Api sous Configuration, basculez le sous-ensemble. NET 2.0 vers. NET 2.0. Ajoutez maintenant le code suivant en haut du script;

en utilisant System. IO. Ports;

Cela vous permettra d'accéder à la classe SerialPort que vous pouvez définir en tant qu'objet de la classe ArduinoReceive. Rendez-le privé pour éviter toute interférence d'un autre script.

arduinoPort SerialPort privé;

L'objet arduinoPort peut être ouvert en sélectionnant le bon port (par exemple dans quel USB l'Arduino est connecté) et un débit en bauds (c'est-à-dire la vitesse à laquelle les informations sont envoyées). Si vous ne savez pas sur quel port l'Arduino est branché, vous pouvez le savoir soit dans le gestionnaire de périphériques, soit en ouvrant l'IDE Arduino. Pour le débit en bauds, la valeur par défaut sur la plupart des appareils est 9600, assurez-vous simplement d'avoir cette valeur dans votre code Arduino et cela devrait fonctionner.

Le code devrait maintenant ressembler à ceci;

en utilisant System. Collections;

en utilisant System. Collections. Generic; en utilisant UnityEngine; en utilisant System. IO. Ports; public class ArduinoReceive: MonoBehaviour { private SerialPort arduinoPort; // Utilisez ceci pour l'initialisation void Start() { arduinoPort = new SerialPort("COM5", 9600); arduinoPort. Open(); WriteToArduino("TRIGG"); } }

Votre numéro COM sera probablement différent. Si vous êtes sur un MAC, votre nom COM peut avoir un nom qui ressemble à ceci /dev/cu.wchusbserial1420. Assurez-vous que le code de la section 4 est téléchargé sur l'Arduino et que le moniteur série est fermé pour le reste de cette section et que ce code se compile sans problème.

Envoyons maintenant une requête à l'Arduino à chaque image et écrivons les résultats dans la fenêtre de la console. Ajoutez la fonction WriteToArduino à la classe ArduinoReceive. Le retour chariot et la nouvelle ligne sont nécessaires pour que le code Arduino analyse correctement l'instruction entrante.

void privé WriteToArduino (message de chaîne)

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

Cette fonction peut ensuite être appelée dans la boucle Update.

annuler la mise à jour ()

{ WriteToArduino ("TRIGG"); Debug. Log("Première valeur: " + arduinoPort. ReadLine()); Debug. Log("Deuxième valeur: " + arduinoPort. ReadLine()); }

Le code ci-dessus est le strict minimum dont vous avez besoin pour lire les données d'un Arduino. Si vous portez une attention particulière au FPS donné par unity, vous devriez constater une baisse significative des performances. Dans mon cas, cela passe d'environ 90 FPS sans lecture/écriture à 20 FPS. Si votre projet ne nécessite pas de mises à jour fréquentes, cela peut être suffisant mais pour un jeu vidéo, 20 FPS est beaucoup trop bas. La section suivante couvrira comment vous pouvez améliorer les performances en utilisant le multi-threading.

Étape 4: Optimiser le transfert de données

La section précédente portait sur la configuration de base

communication entre le programme Arduino et Unity. Le problème majeur avec ce code est la performance. Dans son implémentation actuelle, Unity doit attendre que l'Arduino reçoive, traite et réponde à la demande. Pendant ce temps, le code Unity doit attendre que la requête soit effectuée et ne fait rien d'autre. Nous avons résolu ce problème en créant un thread qui gérera les demandes et stockera la variable sur le thread principal.

Pour commencer, nous devons inclure la bibliothèque de threads en ajoutant;

en utilisant System. Threading;

Ensuite, nous configurons la fonction que nous commençons dans les threads. AsynchronousReadFromArduino commence par écrire la demande à l'Arduino avec la fonction WrtieToArduino. La lecture est enfermée dans un bloc try-catch, si le délai de lecture expire, les variables restent nulles et la fonction OnArduinoInfoFail est appelée à la place de OnArduinoInfoReceive.

Ensuite, nous définissons les fonctions OnArduinoInfoFail et OnArduinoInfoReceive. Pour cette instructable, nous imprimons les résultats sur la console, mais vous pouvez stocker les résultats dans les variables dont vous avez besoin pour votre projet.

vide privé OnArduinoInfoFail()

{ Debug. Log("La lecture a échoué"); } private void OnArduinoInfoReceived (rotation de la chaîne, vitesse de la chaîne) { Debug. Log ("Readin Successfull"); Debug. Log("Première valeur: " + rotation); Debug. Log("Deuxième valeur: " + vitesse); }

La dernière étape consiste à démarrer et à arrêter les threads qui demanderont les valeurs à l'Arduino. Nous devons nous assurer que le dernier thread est terminé avec sa dernière tâche avant d'en commencer un nouveau. Sinon, plusieurs requêtes pourraient être adressées à l'Arduino en même temps, ce qui pourrait dérouter l'Arduino/Unity et donner des résultats imprévisibles.

thread privé activeThread = null;

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

Si vous comparez les performances du code avec celui que nous avons écrit à la section 5, les performances devraient être considérablement améliorées.

vide privé OnArduinoInfoFail()

{ Debug. Log("La lecture a échoué"); }

Étape 5: Où ensuite ?

Où ensuite ?
Où ensuite ?

Nous avons préparé une démo que vous pouvez télécharger sur notre Github (https://github.com/AlexandreDoucet/InfinityBike), téléchargez le code et le jeu et parcourez notre piste. Tout est prêt pour un entraînement rapide et nous espérons que cela pourra vous donner un avant-goût de ce que vous pourriez construire si vous utilisez ce que nous vous avons appris avec cette instructable.

Crédits

Contributeurs au projet

Alexandre Doucet (_Doucet_)

Maxime Boudreau (MxBoud)

Ressources externes [Le moteur de jeu Unity](https://unity3d.com)

Ce projet a commencé après avoir lu le tutoriel d'Allan Zucconi "comment intégrer Arduino avec Unity" (https://www.alanzuconi.com/2015/10/07/how-to-int…)

Les requêtes de l'Arduino sont gérées à l'aide de la bibliothèque SerialCommand (https://github.com/kroimon/Arduino-SerialCommand)

Conseillé: