Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
poppy-kine:poppy-kine-2016-s5 [2017/03/06 15:45]
dbinoux created
poppy-kine:poppy-kine-2016-s5 [2019/04/25 14:08] (current)
Line 1: Line 1:
-====== Analyse de données Kinect et Xsens ======+======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. 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.
-__ 
-====== I- Enregistrement ======__ 
  
 +===== 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:
 +<​hidden>​
 +<code python>​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;</​code></​hidden>​
 +    ​
 +
 +
 +==== 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 ​ <​frames>​.
 +
 +Puis on  applique le code suivant pour lire les données:
 +
 +<​hidden><​code>​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</​code></​hidden>​
 +
 +===== 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.
 +
 +<​hidden><​code>​
 +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() </​code></​hidden>​
 +
 +
 +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}}
 +
 +
 +<​hidden><​code>​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()</​code></​hidden>​
 +
 +==== Xsens ====
 + 
 +
 +Le code pour le Xsens est similaire à celui de la kinect. Il faut juste appeler LirePositionXsens () au lieu de LirePositionKinect()
  • poppy-kine/poppy-kine-2016-s5.1488815140.txt.gz
  • Last modified: 2019/04/25 14:08
  • (external edit)