======Analyse de données Kinect et Xsens ====== {{tag>project poppy-kine}} Cette page explique comment extraire la courbe de position d'un segment du corps à partir d'un enregistrement fait par la Kinect et par le Xsens et comment comparer le bruit des courbes de mouvement. ===== Obtenir les fichiers de données ===== {{tag>tutorial}} Il faut réaliser l'enregistrement avec la kinect et le Xsens de manière simultanée pour que les courbes de mouvements correspondent. ==== Kinect ==== Pour enregistrer et obtenir le fichier de données des nouvements avec la kinect suivre le point 2.2.1 de la page: [[poppy-kine:poppy-kine-2015-s5|http://wiki-robot.enstb.org/doku.php?id=poppy-kine-2015-s5]] ==== Xsens ==== Pour enregistrer avec le Xsens en utilisant MVN Studio suivre les explications de la page suivante [[sensors:x-sens|http://wiki-robot.enstb.org/doku.php?id=sensors:x-sens]] Pour extraire le fichier de données de mouvement suivre les explications des vidéos suivantes: [[https://tutorial.xsens.com/video/exporting-mvnx]] (Pour paramétrer les données à exporter) [[https://tutorial.xsens.com/video/exporting-data]] (Pour exporter le fichier) ===== Structure des fichiers ===== {{tag>tutorial}} ==== Kinect ==== Les fichiers textes contenant les mesures prises par la kinect sont des fichier JSON structuré de la manière suivante {{ :jsonkinect.png?300 |}} ==== Xsens ==== Les fichiers mvnx contenant les données extraites du Xsens sont des fichiers XML structurés de la manière suivante : -La première partie du fichier définit la position initiale des segments du corps. -La seconde partie du fichier contient les données de position en tant que telles, organisées par type de données et par instant. {{ :xmlxsens.png?300 |}} ===== Extraction des données ===== Le but est d'obtenir une matrice par axe de la forme:{{ :matriceres.png?300 |}} Cette forme de matrice permettra de représenter la courbe de mouvement d'un segment du corps en fonction du temps et de réaliser des calculs pour évaluer le bruit. Les fichiers de données issus de la Kinect et du Xsens étant différent il faut réaliser deux scripts python d'extraction différent. ==== Kinect ==== {{tag>software}} Pour lire un fichier de données kinect on applique le code suivant: import json from pprint import pprint from collections import OrderedDict import matplotlib.pyplot as plt import numpy as np import sys, os str = "%s/%s.%s"%("C:/Users/binou/Desktop/Kinect", sys.argv[1], "txt") print str with open(str) as data_file: data = json.load(data_file, object_pairs_hook=OrderedDict) #declaration des ordonnees joints = ["Head", "Neck", "bSpine", "lAnkle", "lElbow", "lHand", "lHip", "lKnee", "lShoulder", "lThumb", "lWrist", "mShoulder", "mSpine", "rAnkle", "rElbow", "rHand", "rHip", "rKnee", "rShoulder", "rThumb", "rWrist"] #declaration de l'abscisse x = [] #Declaration d'une fonction permettant de lire les donnees de position def LirePositionKinect(): #declaration des matrices de sortie mat1 = [] mat2 = [] mat3 = [] #p sert a parcourir les lignes de la matrice resultat p = 0 #On repere les valeurs des abscisses dans le jeu de donnees for element in data["positions"]: x.append(json.dumps(element).replace("\"", "")) #On cherche ensuite les donnees par jointure #Pour chaque instant capture for i in x: #On initialise les lignes a ajouter dans les matrices de resultat y1 = [] y2 = [] y3 = [] #On ajoute un emplacement pour ajouter la ligne mat1.append([]) mat2.append([]) mat3.append([]) #Pour chaque joint de l'instant capture for j in joints: #On ajoute les valeurs dans la ligne a inserer y1.append(data["positions"][i][j][0]) y2.append(data["positions"][i][j][1]) y3.append(data["positions"][i][j][2]) #On ajoute la ligne dans la matrice resultat mat1[p] += y1 mat2[p] += y2 mat3[p] += y3 #On incremente p pour passer a la ligne suivante p += 1 #Convertion des listes en tableau et transposition res1 = np.asarray(mat1).T res2 = np.asarray(mat2).T res3 = np.asarray(mat3).T resx = np.asarray(x) #On retourne les resultats return res1, res2, res3, resx; ==== Xsens ==== Pour le Xsens il faut d'abord modifier le fichier. Il faut tout d'abord l'enregistrer en tant que fichier XML, par exemple en l'ouvrant sous NotePad++, puis enregistrer sous. Ensuite, il faut retirer la première partie qui définie les position initiales des segments, pour ne garder que ce qui est entre les balises . Puis on applique le code suivant pour lire les données: str = "%s.%s"%(sys.argv[1], "xml") print str tree = etree.parse(str) #Declaration de la matrice en abscisse (le temps) t = [] for frame in tree.xpath("/frames/frame"): t.append(frame.get("index")) root = tree.getroot() #récupération des données d'orientation def LirePositionXsens(): #declarations des matrice de sortie mat1 = [] mat2 = [] mat3 = [] #p sert a parcourir les lignes de la matrice resultat p = 0 #On initialise les lignes a ajouter dans les matrices de resultat y1 = [] y2 = [] y3 = [] for i in range(2,len(t),1): mat1.append([]) mat2.append([]) mat3.append([]) #Pour chaque segment de l'instant de capture str = root[i][1].text.split() #on remplis la matrice y1 for j in range(0,69,3): #92 = nombe se seg * 4 y1.append(str[j]) #on remplis la matrice y2 for j in range(1,69,3): #92 = nombe se seg * 4 y2.append(str[j]) #on remplis la matrice y3 for j in range(2,69,3): #92 = nombe se seg * 4 y3.append(str[j]) mat1[p] += y1 del y1[:] mat2[p] += y2 del y2[:] mat3[p] += y3 del y3[:] p +=1 res1 = np.array(mat1) res2 = np.array(mat2) res3 = np.array(mat3) return res1, res2, res3 ===== Affichage des courbes de mouvements ===== On cherche à afficher en simultané les courbes de mouvements de la kinect et du Xsens pour pouvoir les comparer. Le code suivant permet d'afficher les courbes de mouvement pour un fichier dont le nom est passé en parmaètres dans la ligne de commande ainsi que le segment et l'axe voulu. from LireXsens import LirePositionXsens, LireOrientationXsens, LireSegment from LireKinect import LireOrientationKinect, LirePositionKinect import matplotlib.pyplot as plt import numpy as np import sys, os #declaration des joints de la kinect joints = ["Head", "Neck", "bSpine", "lAnkle", "lElbow", "lHand", "lHip", "lKnee", "lShoulder", "lThumb", "lWrist", "mShoulder", "mSpine", "rAnkle", "rElbow", "rHand", "rHip", "rKnee", "rShoulder", "rThumb", "rWrist"] #creation de dictionnaire pour associer au membre leur index dicK={"Head":1, "Neck":2, "bSpine":3, "lAnkle":4, "lElbow":5, "lHand":6, "lHip":7, "lKnee":8, "lShoulder":9, "lThumb":10, "lWrist":11, "mShoulder":12, "mSpine":13, "rAnkle":14, "rElbow":15, "rHand":16, "rHip":17, "rKnee":18, "rShoulder":19, "rThumb":20, "rWrist":21} dicX={'Pelvis':1, 'L5':2, 'L3':3, 'T12':4, 'T8':5, 'Neck':6, 'Head':7, 'rShoulder':8, 'rUpperArm':9, 'rForeArm':10, 'rHand':11, 'lShoulder':12, 'lUpperArm':13, 'lElbow':14, 'lHand':15, 'rUpperLeg':16, 'rLowerLeg':17, 'rtFoot':18, 'rToe':18, 'lUpperLeg':20, 'lLowerLeg':21, 'lFoot':22, 'lToe':23} #On recupere les valeurs de orientation du Xsens Xores1, Xores2, Xores3, Xores4, t = LireOrientationXsens(); #on transpose les matrice Xores tXores1 = Xores1.T tXores2 = Xores2.T tXores3 = Xores3.T tXores4 = Xores4.T #On recupere les valeurs des positions Xpres1, Xpres2, Xpres3= LirePositionXsens(); #on transpose les matrice Xpres tXpres1 = Xpres1.T tXpres2 = Xpres2.T tXpres3 = Xpres3.T #on enleve les index de pose de calibration t.remove("-2") t.remove("-3") #On recupere les valeurs de position de la kinect #Kores1, Kores2, Kores3, Kores4, Koresx = LireOrientationKinect(); #On recupere les valeurs de position de la kinect Kpres1, Kpres2, Kpres3, Kpresx = LirePositionKinect(); #on plot la position du segments passe en parametre if sys.argv[3] == "x": plt.figure(1) plt.subplot(211) plt.plot(Kpresx, Kpres1[dicK.get(sys.argv[2])]) plt.title ("Kinect") plt.subplot(212) plt.plot(t, tXpres1[dicX.get(sys.argv[2])]) plt.title ("Xsens") plt.show() if sys.argv[3] == "y": plt.figure(1) plt.subplot(211) plt.plot(Kpresx, Kpres2[dicK.get(sys.argv[2])]) plt.title ("Kinect") plt.subplot(212) plt.plot(t, tXpres2[dicX.get(sys.argv[2])]) plt.title ("Xsens") plt.show() if sys.argv[3] == "z": plt.figure(1) plt.subplot(211) plt.plot(Kpresx, Kpres3[dicK.get(sys.argv[2])]) plt.title ("Kinect") plt.subplot(212) plt.plot(t, tXpres3[dicX.get(sys.argv[2])]) plt.title ("Xsens") plt.show() On exécute ce script dans la console: {{ :script.png?300 |}} Remarque: Il faut que les fichiers Kinect et Xsens aient le même nom (suivis de leur extension respective) et se trouve dans le même dossier que le script Afficher.py Le fichier chris1_0 est un enregistrement d'un exercice où la personne lève le bras droit, puis le bras gauche, puis les deux. On obtient le graphique suivant: {{ :courbesmouvements.png?300 |}} On remarque sur ce graphique que la courbe de mouvement issue des données de la kinect est plus bruitée que celle issue du Xsens. La prochaine étape sera de quantifier ce bruit. ===== Calcul du bruit ===== Pour évaluer le bruit nous allons réaliser deux calculs. Le premier consiste à calculer le nombres de pics de bruit. Pour cela il faut tout d'abord fixer une valeur seuil. Si l'écart de valeur entre deux instant dépasse ce seuil alors on compte un pic. Plus le nombre de pic est élevé, plus le signal est bruité. Le deuxième calcul consiste à faire le moyenne des valeurs de 5 points consécutifs et de comparer cette valeur à celle du point central (c'est à dire le troisième). Dans le cas idéal, les valeurs sont identique. En pratique, plus les valeurs sont éloignées, plus le signal est bruité. On calcul aussi l'écart-type de l'écart entre le point central et la moyenne des 5 points. Pour réaliser ces calculs sur un ensemble de fichier on applique les scripts python suivants. ==== Kinect ==== {{tag>software}} from LireKinect import LirePositionKinect, LireOrientationKinect import numpy as np import os, sys #creation de dictionnaire pour associer au membre leur index dicK={"Head":1, "Neck":2, "bSpine":3, "lAnkle":4, "lElbow":5, "lHand":6, "lHip":7, "lKnee":8, "lShoulder":9, "lThumb":10, "lWrist":11, "mShoulder":12, "mSpine":13, "rAnkle":14, "rElbow":15, "rHand":16, "rHip":17, "rKnee":18, "rShoulder":19, "rThumb":20, "rWrist":21} def moyenne(tableau): return sum(tableau, 0.0) / len(tableau) def variance(tableau): m=moyenne(tableau) return moyenne([(x-m)**2 for x in tableau]) def ecartype(tableau): return variance(tableau)**0.5 #On creer une liste qui contient toutes les valeurs d'ecart type pour tous les fichier et tous les segments #on fera le calcul de l'ecart type sur cette liste plus tard listeEcartx = [] listeEcarty = [] listeEcartz = [] EcartMoyenSegmentx=[] EcartMoyenSegmenty=[] EcartMoyenSegmentz=[] PicMoyenSegmentx=[] PicMoyenSegmenty=[] PicMoyenSegmentz=[] for seg in range (1,21,1): print seg #On remplis une liste qui contient la valeur totale de pic pour chaque fichier totpicx = [] totpicy = [] totpicz = [] totEcartMoyenx=[] totEcartMoyeny=[] totEcartMoyenz=[] for element in os.listdir('C:/Users/binou/Desktop/Kinect'): if element.endswith('.txt'): #On recupere les valeurs de orientation de la kinect #Kores1, Kores2, Kores3, Kores4, Koresx = LireOrientationKinect(); #On recupere les valeurs des positions Kpres1, Kpres2, Kpres3, Kpresx = LirePositionKinect(element); #compteur de pics picx = 0 picy = 0 picz = 0 #suivant l'axe des x for i in range(len(Kpresx) - 1): if abs(Kpres1[seg][i+1] - Kpres1[seg][i]) >0.05: picx += 1 #suivant l'axe des z for i in range(len(Kpresx) - 1): if abs(Kpres2[seg][i+1] - Kpres2[seg][i]) >0.05: picy += 1 #suivant l'axe des z for i in range(len(Kpresx) - 1): if abs(Kpres3[seg][i+1] - Kpres3[seg][i]) >0.05: picz += 1 totpicx.append(picx) totpicy.append(picy) totpicz.append(picz) #faire la moyenne de 5 points Mx = [] My = [] Mz = [] for i in range(2,len(Kpresx)-2,5): Mx.append((Kpres1[seg][i-2]+Kpres1[seg][i-1]+Kpres1[seg][i]+Kpres1[seg][i+1]+Kpres1[seg][i+2])/5) for i in range(2,len(Kpresx)-2,5): My.append((Kpres2[seg][i-2]+Kpres2[seg][i-1]+Kpres2[seg][i]+Kpres2[seg][i+1]+Kpres2[seg][i+2])/5) for i in range(2,len(Kpresx)-2,5): Mz.append((Kpres3[seg][i-2]+Kpres3[seg][i-1]+Kpres3[seg][i]+Kpres3[seg][i+1]+Kpres3[seg][i+2])/5) #On evalue l'ecart du point du milieu a la moyenne ecartx = [] ecarty = [] ecartz = [] for i,j in zip(range (len(Mx)),range (3, len(Kpresx), 5)) : ecartx.append(abs(Mx[i] - Kpres3[seg][j])) listeEcartx.append(abs(Mx[i] - Kpres3[seg][j])) for i,j in zip(range (len(My)),range (3, len(Kpresx), 5)) : ecarty.append(abs(My[i] - Kpres3[seg][j])) listeEcarty.append(abs(My[i] - Kpres3[seg][j])) for i,j in zip(range (len(Mz)),range (3, len(Kpresx), 5)) : ecartz.append(abs(Mz[i] - Kpres3[seg][j])) listeEcartz.append(abs(Mz[i] - Kpres3[seg][j])) #on calcul l'ecart moyen sur une courbe EcartMoyenx = moyenne(ecartx) EcartMoyeny = moyenne(ecarty) EcartMoyenz = moyenne(ecartz) totEcartMoyenx.append(EcartMoyenx) totEcartMoyeny.append(EcartMoyeny) totEcartMoyenz.append(EcartMoyenz) #On remplis une liste qui contient les ecarts moyens de chaque segment EcartMoyenSegmentx.append(moyenne(totEcartMoyenx)) EcartMoyenSegmenty.append(moyenne(totEcartMoyeny)) EcartMoyenSegmentz.append(moyenne(totEcartMoyenz)) #On remplis une liste qui contient le nombre moyen de pic de chaque segment PicMoyenSegmentx.append(moyenne(totpicx)) PicMoyenSegmenty.append(moyenne(totpicy)) PicMoyenSegmentz.append(moyenne(totpicz)) #Moyenne globale des pics pour tout les fichiers et tout les segments MoyenneGlobalePicx = moyenne (PicMoyenSegmentx) MoyenneGlobalePicy = moyenne (PicMoyenSegmenty) MoyenneGlobalePicz = moyenne (PicMoyenSegmentz) #Moyenne globale des ecarts pour tout les fichiers et tout les segments MoyenneGlobaleEcartx = moyenne (EcartMoyenSegmentx) MoyenneGlobaleEcarty = moyenne (EcartMoyenSegmenty) MoyenneGlobaleEcartz = moyenne (EcartMoyenSegmentz) #On calcul l'ecart type de la serie d'ecart du point central ETx = ecartype(listeEcartx) ETy = ecartype(listeEcarty) ETz = ecartype(listeEcartz) fichier = open("Bruit_Moyen_Kinect.txt","w") fichier.write("""------------------Ecart moyen entre le point central et la moyenne ------------------\n""") fichier.write ("""%s = %s \n""" % ("Ecart Moyen suivant x", MoyenneGlobaleEcartx)) fichier.write ("""%s = %s \n""" % ("Ecart Moyen suivant y", MoyenneGlobaleEcarty)) fichier.write ("""%s = %s \n""" % ("Ecart Moyen suivant z", MoyenneGlobaleEcartz)) fichier.write ("""------------------Ecart type de l'ecart du point central a la moyenne------------------\n""") fichier.write ("""%s = %s \n""" % ("Ecart Type suivant x", ETx)) fichier.write ("""%s = %s \n""" % ("Ecart Type suivant y", ETy)) fichier.write ("""%s = %s \n""" % ("EcartType suivant z", ETz)) fichier.write ("""------------------Nombre moyen de pic------------------\n""") fichier.write ("""%s = %s \n""" % ("Nombre moyen de pic suivant x", MoyenneGlobalePicx)) fichier.write ("""%s = %s \n""" % ("Nombre moyen de pic suivant y", MoyenneGlobalePicy)) fichier.write ("""%s = %s \n""" % ("Nombre moyen de pic suivant z", MoyenneGlobalePicz)) fichier.close() ==== Xsens ==== Le code pour le Xsens est similaire à celui de la kinect. Il faut juste appeler LirePositionXsens () au lieu de LirePositionKinect()