IMA5 2019/2020 P17 : Différence entre versions

De Wiki de Projets IMA
(Récupération des mouvements)
(Bug)
Ligne 193 : Ligne 193 :
 
Je me suis rendu compte que lorsqu'on quitte le jeu, le programme plante. Je vais résoudre cette erreur plus tard.
 
Je me suis rendu compte que lorsqu'on quitte le jeu, le programme plante. Je vais résoudre cette erreur plus tard.
  
===Bug===
+
===Bug du jeu 2D===
 
En voulant tester la récupération des mouvements, je me suis rendu compte que le jeu ne marchait plus, les commandes des boutons appuyés n'étaient plus transmises. Le jeu affichait uniquement l'écran d'accueil. Pourtant, quand on appuie sur un bouton avec la manette, celui-ci change bien de couleur, montrant que la collision entre la manette et le bouton est bien détectée. Après de longues investigations, affichage de logs sur le casque... Il s'est avéré que le serveur gérant la réception des commandes et le gamepad sur la RaspberryPi avait planté. Il a "simplement" fallu le redémarrer.
 
En voulant tester la récupération des mouvements, je me suis rendu compte que le jeu ne marchait plus, les commandes des boutons appuyés n'étaient plus transmises. Le jeu affichait uniquement l'écran d'accueil. Pourtant, quand on appuie sur un bouton avec la manette, celui-ci change bien de couleur, montrant que la collision entre la manette et le bouton est bien détectée. Après de longues investigations, affichage de logs sur le casque... Il s'est avéré que le serveur gérant la réception des commandes et le gamepad sur la RaspberryPi avait planté. Il a "simplement" fallu le redémarrer.
Nous avons compris que ce problème était lié à la mise en veille automatique du casque.
+
Nous avons compris que ce problème était lié à la mise en veille automatique du casque, il sera réglé par un membre de l'équipe.
  
 
===Stockage des données===
 
===Stockage des données===

Version du 1 février 2020 à 20:55


Présentation générale

Sujet : Outil d'analyse de mouvements d'interaction
Etudiant : François Brassart
Encadrant : Laurent Grisoni

Description

L'équipe de recherche MINT souhaite réaliser une expérience engageante de l'utilisation de la réalité virtuelle dans la rééducation afin de répondre aux besoins des patients et de leurs thérapeutes. L'objectif est d'optimiser les protocoles de réadaptation, d'accélérer le rétablissement des patients et faciliter leur réintégration dans la vie quotidienne. Cette expérience s'adresse en particulier à la réadaptation des enfants touchés par des maladies chroniques ainsi qu'aux adultes frappés de fatigue et de douleur. Le patient portera un casque de réalité virtuelle et pourra jouer à un jeu, choisi par le médecin via son téléphone. Durant le jeu et à la fin, le thérapeute aura accès à un enregistrements et une analyse des mouvements du joueur sur son téléphone.

Objectifs

Mon projet se concentre sur la partie récupération et transmission des mouvements du joueur. L'objectif est de concevoir un système qui comporte un module d'enregistrement des gestes d'interaction. Une réflexion sera menée sur la manière optimale d'envoyer et de stocker les données utiles à la description des capacités et de la fatigue des participants. En fonction de mon avancement, il sera possible d'élaborer un modèle de fatigue gestuelle en traitant et en analysant les données récoltées. Des réunions avec des médecins peuvent être organisées pour retranscrire une analyse optimale.

Préparation du projet

Cahier des charges

Le médecin doit pouvoir avoir accès, via une interface web, à un enregistrement des mouvements d'interaction du patient qui a joué avec le casque de réalité virtuelle. Les mouvements d'interactions comprennent la position et l'orientation du joueur dans l'espace à minima, et si possible les mouvements des manettes. Il est important de choisir un mode de stockage des enregistrements optimal : il faut que chaque enregistrement soit associé au jeu, niveau de difficulté, date et durée de la session...

Choix techniques : matériel et logiciel

Le matériel a déja été choisi par l'équipe. Il s'agit notamment d'un casque de réalité virtuelle Occulus Quest et d'une RaspberryPi. Le jeu VR a été créé avec le logiciel Unity.

Liste des tâches à effectuer

  • Déterminer comment récuperer les mouvements à partir du jeu et du casque de réalité virtuelle (RV).
  • Envoyer ces données sur la raspberry avec un mode de "stockage" adapté (pistes : XML? JSON?).
  • Rendre disponibles ces informations sur un serveur Web pour que le medecin puisse y avoir accès.

En fonction de mon avancement et de la communication avec les médecins :

  • Déterminer avec les médecins des analyses de mouvements pertinentes.
  • automatiser ces analyses.

Calendrier prévisionnel

Etant parti en semestre à l'étranger, je dispose de 7 semaines pour réaliser ce projet (du 6 au 23 février 2020).

Feuille d'heures

Après la première rencontre avec M. Grisoni, j'ai intégré les bureaux de l'équipe MINT et m'y rend tous les jours de la semaine de 10h à 18h environ.

Réalisation du Projet

Prélude : 1-5 Janvier

Wiki

Création du Wiki et de son squelette, rédaction de la partie "description" du projet.

Unity

Installation et prise en main du logiciel Unity. Unity est un moteur de jeu très répandu. Il permet de développer des jeux 2D, 3D et RV. Il permet de créer des jeux multi-plateformes en C# (ou en Javascript mais le C# est utilisé lors de ce projet).

Recherche de tutos qui seraient utiles pour l'utilisation de ce logiciel dans le cadre de mon projet.

Liens utiles : Liste tutos Unity Tuto Unity Cours en ligne Unity VR HTC Vive HTC Vive Tuto

Semaine 1 : 6-12 Janvier

En attendant de rencontrer M. Grisoni pour préciser le cahier des charges, je me forme sur le logiciel Unity.

Tuto1 : Roll a ball

Notes sur ce tuto : Le C# est utilisé pour réaliser des scripts permettant le mouvement et l'interaction des objets du jeu avec l'utilisateur.

Tuto2 : VR Unity

Tuto3 : VR Unity : Dans scripts pour détécter mouvement : Input.GetAxis("Horizontal"); ou Input.GetAxis("Vertical");


Rencontre avec M. Grisoni et son équipe

J'ai rencontré M. Grisoni à l'IRCICA (Institut de Recherche sur les Composants logiciels et matériels pour l'Information et la Communication Avancée) pour fixer le cahier des charges de mon projet. Il s'avère que depuis la proposition du sujet et mon retour de l'étranger, l'équipe de recherche a avancé sur le projet. Ainsi, des petites modifications du sujet initial [1] ont été réalisées. L'équipe dispose maintenant d'un prototype de jeu fonctionnel. Il s'agit d'un jeu en 2D (Tetris par exemple) qui se commande via des boutons en 3D. L'objectif est que le patient appuie correctement sur les bons boutons qui sont situés dans l'espace à des endroits difficiles d'accès en fonction de sa pathologie (à droite s'il a des difficultés de motricité du bras droit par exemple).

Le jeu a été créé sur Unity et il est exécuté sur une RaspberryPi. La RaspberryPi est configurée avec plusieurs serveurs. Le premier envoie les images du jeu au casque de réalité virtuelle. Le second reçoit les boutons qui ont été appuyés par l'utilisateur provenant du casque. Il fait également le lien avec une interface web qui permet d'avoir un mode collaboratif (multijoueur) : le 1er joueur joue avec le casque et le second interagit avec lui depuis son smartphone. Enfin, le 3e serveur fait le lien avec une page web qui permet de configurer la difficulté du jeu et envoyer cette configuration au casque.

donner infos sur config

Dans la version actuelle, le médecin n'a aucun retour sur les performances du patient. On souhaite donc lui donner accès à un enregistrement des mouvements du joueur dans un premier temps. Ainsi, le médecin aura accès à ces enregistrements depuis une interface web. Cela permettra d'avoir une version "bêta" à présenter à des medecins. Si le temps me le permet, je pourrais être en contact avec eux pour comprendre quelles informations et analyses des mouvements ils ont besoin. On enverra donc sur l'interface web non plus un enregistrement des mouvements mais une analyse des performances du patient (nombre de tentatives pour appuyer sur un bouton par exemple). La partie "enregistrement des mouvements" est la partie principale et nécessaire de mon projet. La partie "analyse des mouvements" est facultative et dépendra donc du temps et de la disponibilité des médecins pour avoir un retour sur le projet.

Je peux me rendre à l'IRCICA pour avoir accès à un casque de réalité virtuelle et travailler avec l'équipe quand je le souhaite. On m'a accordé l'accès au Git du projet, hébergé sur le GitLab de Cristal.

Documentation sur l'Oculus Quest

L'Oculus Quest est un casque de réalité virtuelle entièrement autonome avec 2 manettes à six degrés de liberté et fonctionne sur un système Qualcomm Snapdragon 835 sur puce. L'OS utilisé est android 7.1.1.

OculusQuest.jpg


Les applications qui peuvent être installées sur ce casque VR sont au format apk, le même format que les applications Android.

Etude du code

Une fois que j'ai eu accès au Git, j'ai commencé par essayer de comprendre le code déja réalisé et ainsi avoir un aperçu global de ce qui a été réalisé et comment cela a été fait. J'ai également préparé mon "environnement de travail" en installant une machine virtuelle linux sur mon ordinateur, installant le logiciel Unity et en clonant les dépôts git. Le projet comprends 2 dépots git : Unity-asset et serverpi.

Rappel Git

Pour éviter les erreurs de git, deux branches "monitoring" ont été créées sur les projets.

Récupération des dossiers git :

git clone ...
git checkout monitoring         #switcher vers la branche monitoring

Attention à pusher mon travail au bon endroit :

git branch                      #vérifier la branche actuelle
git push origin monitoring      #pusher sur la branche monitoring

Formation sur C# et framework .NET

Sur Unity, les scripts sont rédigés en C#. J'ai donc décidé en ce début de projet d'apprendre les bases du C# et du framework .NET. Cela me servira probablement dans mes futures expériences professionnelles. Cours OpenClassrooms

Projet Unity

La scène créée dans Unity, visible dans le casque de RV consiste en une pièce 3D, un écran sur lequel est affiché le jeu 2D (Tetris par exemple) et une grande manette avec des boutons sur lesquels il faut appuyer pour interagir avec le jeu. Le jeu en 2D est exécuté sur la RaspberryPi. Le flux vidéo est envoyé sur le casque RV. Les boutons appuyés sont envoyés sur la RapsberryPi. J'explore les différents scripts afin de comprendre comment l'appui des boutons est détecté.

ScreenUnityfbrassar.png

Recuperation des mouvements des manettes

En cette fin de semaine, je me concentre à chercher un moyen de récupérer les mouvements des manettes et du casque. Je commence Documentation Oculus Developper

Position de la manette : retourne un vecteur (classe Vector3).

OVRInput.GetLocalControllerPosition(OVRInput.Controller.RTouch)


Angle de la manette : retourne un quaternion.

OVRInput.GetLocalControllerRotation (OVRInput.Controller.RTouch)


OVRInput.Controller.RTouch designe la manette droite et OVRInput.Controller.LTouch désigne la manette gauche. Attention, ces positions seraient relatives à la position d'origine. Question sur un forum demandant script qui crée un objet a position de la manette

Ces fonctions seront à tester.

Semaine 2 : 13-19 Janvier

Prermier Script

Je commence cette 2e semaine de projet en élaborant une première version d'un script qui me permettrait de récuperer les mouvements de l'utilisateur. Ainsi, mon script est une classe qui dérive de la clase de base MonoBehaviour. En effet, d'après la documentation Unity [2], "MonoBehaviour is the base class from which every Unity script derives."

Dans la version du jeu actuelle, la manière de détecter qu'un bouton est appuyé par l'utilisateur grâce à une manette fonctionne grâce à un système de collision. En effet, Unity permet de détecter quand 2 objets entrent en collision, dans le cas présent une manette et un bouton. Etant donné que je souhaite mapper tous les mouvemements des manettes, cet aspect de collision ne m'est pas utile, à moins de créer des objets invisibles dans tout l'espace 3D, de détecter la collision avec une manette et envoyer la position de l'objet. Ce n'est pas une solution la plus optimale.

Grace aux fonctions GetLocalControllerPosition et GetLocalControllerRotation, je peux récuperer les positions et rotation des manettes. Je vais d'abord vérifier que ces dernières soient connectées : IsControllerConnected() or GetActiveController().


Récupération de la position du casque

OVRCameraRig.centerEyeAnchor ( ) Always coincides with average of the left and right eye poses.

position = overCameraRig.centerEyeAnchor.position;

a tester, pour voir ce que cela renvoie. Je dois également voir tous l'intervalle de temps entre lequel je récupère la position.

Réunion avec l'équipe

Mardi 14 janvier à 14h, l'équipe organise un "Whiteboard and Cookies" autour du projet. L'idée est d'avoir une discutions libre autour d'un sujet. Le sujet de la semaine sera porté sur les différents scénarios que l'on pourrait mettre en place pour le projet ainsi que des techniques de monitoring qu'on pourrait mettre en place. Cette réunion m'a permis de rencontrer toute l'équipe du projet et de mieux comprendre l'objectif global de ce projet de recherche. Pour l'instant l'objectif est de préparer une discussion avec des médecins, thérapeutes en leur proposant un prototype fonctionnel. Plus précisément dans ma partie du projet, je vais commencer par uniquement afficher les logs des mouvements réalisé par les patients sur une interface téléphone. Suite aux discussion avec des médecins, une analyse pertinente de ces mouvements pourra être réalisée (en fonction du calendrier car la rencontre avec les médecins n'est pas encore planifiée). J'ai également eu l'occasion de tester un premier prototype fonctionnel du jeu.

Script Unity

documentation .NET : TcpClient : Avec .Net en C#, Il existe une classe TcpClient qui permet de se connecter à un serveur TCP. Ainsi, je peux créer mon script qui se connecte à un serveur TCP du Raspberry sur un port donné et envoyer les données utiles. Je dois me renseigner sur le type des données que renvoient les fonctions de récupération des mouvements afin de les convertir en un format qui fonctionne avec les fonctions de TcpClient. Par exemple pour envoyer des données je peux utiliser :

byte[] sendBytes = Encoding.UTF8.GetBytes("#chaine de caractères");
stream.Write(sendBytes, 0, sendBytes.Length);

Je vais donc convertir les positions en chaine de caractères.

Serveur RPi

Les serveurs actuellements existants sur la Raspberry Pi sont codés en Javascript avec Node.js. Je commence par me former succintement grâce à ce cours

J'ai ainsi pu créer un serveur HTTP en utilisant express. Ainsi, lorsqu'un client se connecte sur le bon port, une simple page HTML s'affiche. J'ai également créé un serveur TCP sur un autre port. Je l'ai testé en le configurant de manière à ce que lorsqu'un client se connecte, un message s'affiche sur la console. Je l'ai testé avec la commande :

nc localhost <port>

J'ai également testé sur la Raspberry Pi et cela fonctionne. Pour le moment, je choisi le port 8090 pour le serveur Web et le port 8091 pour le serveur TCP. Je fais en sorte que ces valeurs soient modifiables facilement.

Je souhaite maintenant réaliser un Script C# Unity qui connecte le casque au serveur TCP de la RPi.

Test Connexion Oculus

J'ai créé un script qui se connecte au serveur TCP et qui envoie la date une fois connecté. La date devrait ainsi s'afficher sur la page web. Premier tests avec le casque. Celui-ci n'est pas détecté par l'ordinateur. Pour que le casque autorise l'ordinateur à envoyer des applications : télécharger platform tools puis éxecuter dans un shell :

./adb.exe devices 

Ensuite autoriser l'accès avec le casque et les manettes.

Pour envoyer la date, j'ai utilisé les fonctions suivantes :

byte[] sendBytes = Encoding.UTF8.GetBytes(DateTime.Now.ToString("HH:mm"));
stream.Write(sendBytes, 0, sendBytes.Length);

Cela fonctionne, la date s'affiche sur la page web au lancement du casque.

Semaine 3 : 20-26 Janvier

Test Connexion Oculus

Le code que j'avais réalisé un fin de semaine dernière fonctionne. L'oculus se connecte au serveur TCP de la Raspberry, et envoie la date une fois la connexion établie. Cette date est ensuite affichée sur la page web grace au code javascript lié à la page HTML. Ainsi, la connexion entre l'oculus et la Raspberry est fonctionnelle, et l'affichage sur la page Web également. J'ai choisi le port 8090 pour la page Web et le port 8091 pour le serveur TCP. Ces ports peuvent être modifiés facilement dans Unity et dans le code du serveur de la Raspberry Pi.

Maintenant, je souhaite créer un bouton sur ma page web qui envoie une demande d'information au casque. Ainsi, je pourrais tester facilement la récupération des positions des manettes et du casque. J'ai donc créé un bouton sur ma page web qui envoie une demande d'update qui est transmise au casque. Pour l'instant, le casque répond l'heure actuelle. Cela fonctionne. Je peux donc passer aux tests des fonctions de récupération des position.

Récupération des mouvements

Test des fonctions de récupération des positions des manettes : Au lieu d'envoyer la date, j'ai créé un bouton update sur la page HTML qui envoie un message au serveur Web, ensuite transmis au casque grâce au serveur TCP. Le casque répond en envoyant les positions du casque et des manettes.

ScreenWebfbrassar.png

J'obtiens bien sur la page web les valeurs désirée. Je vais tester en bougeant, regarder si les positions sont relatives ou absolues, voir si la taille du guardian (espace de jeu) modifie les valeurs.

ScreenWeb2fbrassar.png

Il apparait que les positions des manettes sont relatives à la position initial de l'oeil central (milieu entre les 2 yeux du casques).

Je me suis rendu compte que lorsqu'on quitte le jeu, le programme plante. Je vais résoudre cette erreur plus tard.

Bug du jeu 2D

En voulant tester la récupération des mouvements, je me suis rendu compte que le jeu ne marchait plus, les commandes des boutons appuyés n'étaient plus transmises. Le jeu affichait uniquement l'écran d'accueil. Pourtant, quand on appuie sur un bouton avec la manette, celui-ci change bien de couleur, montrant que la collision entre la manette et le bouton est bien détectée. Après de longues investigations, affichage de logs sur le casque... Il s'est avéré que le serveur gérant la réception des commandes et le gamepad sur la RaspberryPi avait planté. Il a "simplement" fallu le redémarrer. Nous avons compris que ce problème était lié à la mise en veille automatique du casque, il sera réglé par un membre de l'équipe.

Stockage des données

MongoDB Node.js Dans un premier temps, je vais récuperer les positions toutes les secondes (ce temps peut être facilement modifié grâce à une variable publique). Les logs seront envoyés du casque à la Raspberry toutes les 10 secondes (de même, temps modifiable facilement). Sur la raspberry, je me renseigne sur MongoDB, base de données noSQL qui stocke sous format json. installation MongoDB

sudo apt-get install mongo-db

Suite à cette commande, le service est automatiquement lancé. Il peut etre lancé ou arrêté avec la commande :

sudo service mongodb start/stop

Installation du paquet mongodb pour node.js

npm install mongodb

J'ai réalisé quelques tests d'utilisation de mongoDB pour me familiariser. Je vais maintenant programmer le stockage des éléments envoyés par le casque dans la base de données. Ensuite, je réaliserais une interface web permettant de choisir et d'afficher les données de la base.

Format de BDD

Avec MongoDB, chaque collection de la base de données est un ensemble de documents sous le format json.

{ 
  "Date":"...",
  "Start Time":"...",
  "End Time":"...",
  "Configuration":"...",
  "Logs":[ 
     { 
        "Time":"...",
        "Left":"...",
        "Right":"...",
        "Head":"..."
     },
     { 
        "Time":"...",
        "Left":"...",
        "Right":"...",
        "Head":"..."
     }
  ]
}

La date et l'heure de début sont envoyées lors de la connexion du casque au serveur TCP.

Pour insérer dans la base de données :

var newLog= new Object();
newLog.Date="24/02/1997";
newLog.StartTime="14:30:22";
mydb.collection("logs").insertOne(objNew, null, function (error, results) {
   	if (error) throw error;
   	console.log("Le document a bien été inséré");    
		});

Problème de versions

J'ai commencé à programmer le stockage dans la bdd sur ma MV Ubuntu. Après avoir obtenu un résultat satisfaisant en testant avec un nc, je teste sur la raspberry en recevant les données du casque. Le code renvoie des erreurs rapidement. Après investigation, je me suis rendu compte que la version de mongo disponible sur Ubuntu n'est pas la même que sur retropie. Ainsi, je devrais la semaine prochaine modifier les commandes qui ont sont différentes selon les versions. OU VOIR pour forcer la version avec package.json

TODO

Voir avec Laurent Grisoni si l'orientation du casque et des manettes sont des données utiles ou non.

public OVRCameraRig overCameraRig;
overCameraRig.centerEyeAnchor.rotation                                        //orientation du casque
OVRInput.GetLocalControllerOrientation(OVRInput.Controller.LTouch).ToString()    //orientation des manettes

Semaine 4: 27 Janvier - 2 Février

Etant malade en début de semaine, je n'ai pas pu aller travailler à l'IRCICA.

Erreur lors d'execution sur Raspberry Pi

Après investigation, j'ai trouvé plus d'informations sur l'erreur que j'avais auparavant :

MongoServerSelectionError: Server at localhost:27017 reports maximum wire version 0, but this version of the Node.js Driver requires at least 2 (MongoDB 2.6)

LOWDB

dossier doit exister !!

Fin de Session

OnApplicationPaused() https://stackoverflow.com/questions/55584865/onquit-event-in-oculus-go-app-made-in-unity

Page Web

Gérer quand aucun fichier dans dossier

+ si dossier existe pas --> pas possible car créé lors du lancement de serverMonitoring.js

"Réunion" avec Valentin

ok bien. blc de la vue sur portable car sera consulté sur ordinateur probablement. Pas très visuel mais on connait pas encore les analyses que voudront faire les medecins donc on laisse comme ça. manettes et head rotation peuvent etre utiles donc on ajoute. récupération de la config : au début de session, stockage de la config reçue dans un json sous nom "YYYYMMDD_HHMMSS_config1.json". Dans mon json de log, pour chaque log j'associe la config1. Dès qu'un changement de config est détecté (voir comment détecter), création d'un nouveau json de config.

Problème de fin de session --> afficher sur page web un bouton "Run" et un bouton "Stop" pour lancer l'enregistrement. A voir réception de messages sur le casque.


Format BDD Final

{ 
  "Date":"...",
  "Start Time":"...",
  "Configuration":"...",
  "Logs":[ 
     { 
        "Time":"...",
        "Left":"...",
        "Left Rotation":"...",
        "Right":"...",
        "Right Rotation":"...",
        "Head":"...",
        "Head Rotation":"..."
        "Config":"...",
     },
     { 
        "Time":"...",
        "Left":"...",
        "Left Rotation":"...",
        "Right":"...",
        "Right Rotation":"...",
        "Head":"...",
        "Head Rotation":"..."
        "Config":"...",
     },
  ]
}

Semaine 5 : 3-9 Février

Semaine 6 : 10-16 Février

Semaine 7 : 17-23 Février (Soutenance)

Documents Rendus