Télécommande corporelle
par Arnaud BULCKE · Publié · Mis à jour
Groupe 1
Coralie BABIN et Ana SIMOES
Objectifs
La télécommande corporelle doit permettre à une personne de piloter un objet (robot, domotique, …) avec les mouvements de son corps.
Moyens
Le capteur Kinect
Le capteur Kinect permet, en scannant l’espace, de détecter la présence d’une personne et d’acquérir, en 3 dimensions, les positions des points de son squelette :

Le capteur sera programmé en Python.
Le robot Braduino

Réalisation :
1- Création d’un programme permettant d’afficher le squelette capté par la kinect.
2- Construction du braduino.
3- Faire en sorte que le braduino bouge en fonction des mouvements captés par la kinect.
Assemblage du robot
Braduino est un projet de bras de robot à très bas cout.
Programmes
Bibliothèques Python utilisées :
Pilotes du capteurs Kinect : Kinect for Windows SDK v1.8
Coté PC
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys, os
from math import *
# Import du module kinect
from kinect import *
# Import du module serie (pour communiquer avec l'Arduino)
import serie
# Import du module de calcul
from calcul import *
if __name__ == '__main__':
# Boucle principale ###########################################################################
ser = serie.connexion()
done = False
while not done:
# Attend un événement ....
for e in pygame.event.get(): #get_event():#
if e.type == QUITEVENT: # l'utilisateur ferme la fenêtre graphique
#raise SystemExit, "QUIT"
done = True
break
#######################################################################################
elif e.type == KINECTEVENT: # le capteur kinect dispose de nouvelles données
p0 = get_joint_pos_xyz("ShoulderLeft")
if p0 is not None:
p0 = point(p0.x, p0.y, p0.z)
p3 = get_joint_pos_xyz("WristRight")
if p3 is not None:
p3 = point(p3.x, p3.y, p3.z)
if p0 is not None and p3 is not None:
# plan xy (haut/bas)
p1 = point(p0.x, p0.y+1, p0.z)
p2 = point(p3.x, p3.y, p0.z)
angle_1 = 180 - int(angle(p1, p0, p2, deg = True))
# plan xz (avant/arrière)
p1 = point(p0.x, p0.y, p0.z+1)
p2 = point(p3.x, p0.y, p3.z)
angle_2 = 180 - int(angle(p1, p0, p2, deg = True))
# Envoi des angles à l'Arduino
if ser is not None:
ser.write(str(angle_2).encode()+","+str(angle_1).encode()+"\n")
# Affichage du squelette
if draw_skeleton and not done:
draw_skeletons(e.skeletons)
# Affichage des angles
if p0 is not None:
update_score(str(angle_1).encode()+" "+str(angle_2).encode())
else:
update_score()
update_display()
elif e.type == KEYDOWN: # L'utilisateur a tapé une touche du clavier
if e.key == K_ESCAPE:
done = True
break
# Position de la caméra
elif e.key == K_UP:
Kinect.camera.elevation_angle += 2
elif e.key == K_DOWN:
Kinect.camera.elevation_angle -= 2
elif e.key == K_x:
Kinect.camera.elevation_angle = 2
# Articulation "courante" (affichée à l'écran)
elif e.key == K_RIGHT:
modif_joint(1) # On ajoute 1
elif e.key == K_LEFT:
modif_joint(-1) # On enlève 1
elif e.key == K_RETURN:
print get_joint_pos("")[0] # Exemple d'utilisation de la fonction get_joint_pos()
quit() # Fonction de fermeture de la fenêtre graphique
Source d’inspiration : https://github.com/Microsoft/PTVS/blob/master/Python/Product/PyKinect/PyKinect/PyGameDemo.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
##########################################################################################
#
# Module pour l'acquisition des données du capteur Kinect
# l'affichage de la vidéo
# et des "squelettes"
#
# source : https://github.com/sridharavinash/Itches/blob/master/PyGameDemo.py
##########################################################################################
# Modules
##########################################################################################
# Pour le capteur kinect
import pykinect
from pykinect import nui
from pykinect.nui import JointId
# Pour l'interface graphique
import pygame
from pygame.color import THECOLORS
from pygame.locals import *
# Divers
import ctypes
import itertools
import thread
import sys
# Constantes
##########################################################################################
VIDEO_WINSIZE = 640,480
KINECTEVENT = pygame.USEREVENT
# DISPLAYSCOREEVENT = pygame.USEREVENT + 3
SKELETON_JOINTS = {"ShoulderCenter": JointId.ShoulderCenter,
"ShoulderLeft": JointId.ShoulderLeft,
"ElbowLeft": JointId.ElbowLeft,
"WristLeft": JointId.WristLeft,
"HandLeft": JointId.HandLeft,
"ShoulderRight": JointId.ShoulderRight,
"ElbowRight": JointId.ElbowRight,
"WristRight": JointId.WristRight,
"HandRight": JointId.HandRight,
"HipCenter": JointId.HipCenter,
"HipLeft": JointId.HipLeft,
"KneeLeft": JointId.KneeLeft,
"AnkleLeft": JointId.AnkleLeft,
"FootLeft": JointId.FootLeft,
"HipRight": JointId.HipRight,
"KneeRight": JointId.KneeRight,
"AnkleRight": JointId.AnkleRight,
"FootRight": JointId.FootRight,
"Spine": JointId.Spine,
"Head": JointId.Head}
SKELETON_JOINTS_NAMES = ["ShoulderLeft", "ElbowLeft", "WristLeft", "HandLeft",
"ShoulderCenter",
"ShoulderRight", "ElbowRight", "WristRight", "HandRight",
"HipCenter",
"HipLeft", "KneeLeft", "AnkleLeft", "FootLeft",
"HipRight", "KneeRight", "AnkleRight", "FootRight",
"Spine", "Head"]
SKELETON_COLORS = [THECOLORS["red"],
THECOLORS["blue"],
THECOLORS["green"],
THECOLORS["orange"],
THECOLORS["purple"],
THECOLORS["yellow"],
THECOLORS["violet"]]
LEFT_ARM = (JointId.ShoulderCenter,
JointId.ShoulderLeft,
JointId.ElbowLeft,
JointId.WristLeft,
JointId.HandLeft)
RIGHT_ARM = (JointId.ShoulderCenter,
JointId.ShoulderRight,
JointId.ElbowRight,
JointId.WristRight,
JointId.HandRight)
LEFT_LEG = (JointId.HipCenter,
JointId.HipLeft,
JointId.KneeLeft,
JointId.AnkleLeft,
JointId.FootLeft)
RIGHT_LEG = (JointId.HipCenter,
JointId.HipRight,
JointId.KneeRight,
JointId.AnkleRight,
JointId.FootRight)
SPINE = (JointId.HipCenter,
JointId.Spine,
JointId.ShoulderCenter,
JointId.Head)
QUITEVENT = pygame.QUIT
skeleton_to_depth_image = nui.SkeletonEngine.skeleton_to_depth_image
# Variables globales
##########################################################################################
full_screen = False
draw_skeleton = True
video_display = True
CurJoint = 0 # Articulation "courante"
CurScore = "" # Texte de score "courant"
# Fonctions
##########################################################################################
# recipe to get address of surface: http://archives.seul.org/pygame/users/Apr-2008/msg00218.html
if hasattr(ctypes.pythonapi, 'Py_InitModule4'):
Py_ssize_t = ctypes.c_int
elif hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
Py_ssize_t = ctypes.c_int64
else:
raise TypeError("Cannot determine type of Py_ssize_t")
_PyObject_AsWriteBuffer = ctypes.pythonapi.PyObject_AsWriteBuffer
_PyObject_AsWriteBuffer.restype = ctypes.c_int
_PyObject_AsWriteBuffer.argtypes = [ctypes.py_object,
ctypes.POINTER(ctypes.c_void_p),
ctypes.POINTER(Py_ssize_t)]
##########################################################################################
def surface_to_array(surface):
try:
buffer_interface = surface.get_buffer()
except:
pygame.quit()
sys.exit(0)
address = ctypes.c_void_p()
size = Py_ssize_t()
_PyObject_AsWriteBuffer(buffer_interface,
ctypes.byref(address), ctypes.byref(size))
bytes = (ctypes.c_byte * size.value).from_address(address.value)
bytes.object = buffer_interface
return bytes
##########################################################################################
def draw_skeletons(skeletons = None):
u""" Dessine le squelette (le 1er qui est détecté)
"""
global Skeleton, dispInfo
if skeletons is not None:
Skeleton = get_skeleton(skeletons)
def draw_skeleton_data(pSkelton, positions, width = 4):
start = pSkelton.SkeletonPositions[positions[0]]
for position in itertools.islice(positions, 1, None):
next = pSkelton.SkeletonPositions[position.value]
curstart = skeleton_to_depth_image(start, dispInfo.current_w, dispInfo.current_h)
curend = skeleton_to_depth_image(next, dispInfo.current_w, dispInfo.current_h)
pygame.draw.line(screen, SKELETON_COLORS[0], curstart, curend, width)
start = next
if Skeleton is not None:
HeadPos = skeleton_to_depth_image(Skeleton.SkeletonPositions[JointId.Head],
dispInfo.current_w, dispInfo.current_h)
draw_skeleton_data(Skeleton, SPINE, 10)
pygame.draw.circle(screen, SKELETON_COLORS[0], (int(HeadPos[0]), int(HeadPos[1])), 20, 0)
c = SKELETON_JOINTS[SKELETON_JOINTS_NAMES[CurJoint]]
CurPos = skeleton_to_depth_image(Skeleton.SkeletonPositions[c],
dispInfo.current_w, dispInfo.current_h)
pygame.draw.circle(screen, SKELETON_COLORS[1], (int(CurPos[0]), int(CurPos[1])), 10, 0)
# drawing the limbs
draw_skeleton_data(Skeleton, LEFT_ARM)
draw_skeleton_data(Skeleton, RIGHT_ARM)
draw_skeleton_data(Skeleton, LEFT_LEG)
draw_skeleton_data(Skeleton, RIGHT_LEG)
##########################################################################################
def get_skeleton(skeletons):
u""" Renvoie le premier squelette ()
détecté par le capteur Kinect
Renvoie None si aucun squelette n'est détecté
"""
global dispInfo
if skeletons is not None:
for index, data in enumerate(skeletons):
# position de la tête
HeadPos = skeleton_to_depth_image(data.SkeletonPositions[JointId.Head],
dispInfo.current_w, dispInfo.current_h)
if HeadPos[0] > 0 and HeadPos[1] > 0:
return data
##########################################################################################
def get_joint(n):
u""" Renvoie l'articulation (nui.structs.JointId) du Kinect
n peut être de type :
- int = indice de l'articulation dans la liste SKELETON_JOINTS_NAMES
- str = nom clef de l'articulation dans le dictionnaire SKELETON_JOINTS
- nui.structs.JointId (déja du type à renvoyer)
Renvoie None si aucune articulation ne correspond à n
"""
if type(n) == int:
if 0 <= n < len(SKELETON_JOINTS_NAMES):
return SKELETON_JOINTS[SKELETON_JOINTS_NAMES[n]]
elif type(n) == str:
if n in SKELETON_JOINTS_NAMES:
return SKELETON_JOINTS[n]
elif isinstance(n, nui.structs.JointId):
return n
##########################################################################################
def get_joint_name(n):
u""" Renvoie le nom de l'articulation (nui.structs.JointId) du Kinect
n peut être de type :
- int = indice de l'articulation dans la liste SKELETON_JOINTS_NAMES
- str = nom clef de l'articulation dans le dictionnaire SKELETON_JOINTS
- nui.structs.JointId (déja du type à renvoyer)
Renvoie None si aucune articulation ne correspond à n
"""
if type(n) == int:
if 0 <= n < len(SKELETON_JOINTS_NAMES):
return SKELETON_JOINTS_NAMES[n]
elif type(n) == str:
if n in SKELETON_JOINTS_NAMES:
return n
elif isinstance(n, nui.structs.JointId):
for k, v in SKELETON_JOINTS.items():
if v == n:
return k
##########################################################################################
def get_joint_pos(n):
u""" Renvoie les données de position
de l'articulation (nui.structs.JointId) du Kinect
dans le système de coordonnées de l'écran (2D)
n peut être de type :
- int = indice de l'articulation dans la liste SKELETON_JOINTS_NAMES
- str = nom clef de l'articulation dans le dictionnaire SKELETON_JOINTS
- nui.structs.JointId
Renvoie None si aucune articulation ne correspond à n
"""
j = get_joint(n)
if j is not None and Skeleton is not None:
return skeleton_to_depth_image(Skeleton.SkeletonPositions[j],
dispInfo.current_w, dispInfo.current_h)
##########################################################################################
def get_joint_pos_xyz(n):
u""" Renvoie les données de position
de l'articulation (nui.structs.JointId) du Kinect
dans le système de coordonnées capteur (3D)
n peut être de type :
- int = indice de l'articulation dans la liste SKELETON_JOINTS_NAMES
- str = nom clef de l'articulation dans le dictionnaire SKELETON_JOINTS
- nui.structs.JointId
Renvoie None si aucune articulation ne correspond à n
"""
j = get_joint(n)
if j is not None and Skeleton is not None:
return Skeleton.SkeletonPositions[j]
##########################################################################################
def video_frame_ready(frame):
if not video_display:
return
with screen_lock:
address = surface_to_array(screen)
frame.image.copy_bits(address)
del address
if draw_skeleton:
draw_skeletons()
update_score()
pygame.display.update()
##########################################################################################
def depth_frame_ready(frame):
return
##########################################################################################
def update_score(texte = None):
global CurScore
# Par défaut : les coordonnées du point
if Skeleton is not None and texte is None:
point = get_joint_pos(CurJoint)
texte = get_joint_name(CurJoint)+" : "+ str(int(round(point[0]))) + " , "+ str(int(round(point[1])))
elif texte is not None:
CurScore = texte
font = pygame.font.Font(None, 36)
text = font.render(CurScore, 1, (10,10,100))
try:
screen.blit(text,(50,40))
except:
pass
##########################################################################################
def update_display():
u""" Raffraichi la fenêtre graphique
"""
pygame.display.update()
##########################################################################################
def get_event():
u""" Attend puis renvoie un événement de la part de l'interface graphique
(touche de clavier, souris, evenement du capteur, ...)
"""
global dispInfo
dispInfo = pygame.display.Info()
return pygame.event.wait()
##########################################################################################
def quit():
u""" Quitte la fenêtre graphique
"""
pygame.quit()
##########################################################################################
def modif_joint(i):
global CurJoint
CurJoint = (CurJoint+i) % len(SKELETON_JOINTS_NAMES)
# Initialisations
##########################################################################################
pygame.init()
screen_lock = thread.allocate()
screen = pygame.display.set_mode(VIDEO_WINSIZE,0,32)
pygame.display.set_caption('Kinectino')
Skeleton = None # Le skelette "courant" (si détecté) ... à afficher
screen.fill(THECOLORS["black"])
update_display()
dispInfo = pygame.display.Info()
# get_event()
Kinect = nui.Runtime()
Kinect.skeleton_engine.enabled = True
##########################################################################################
def post_frame(frame):
try:
pygame.event.post(pygame.event.Event(KINECTEVENT, skeletons = frame.SkeletonData))
except:
# event queue full
pass
Kinect.skeleton_frame_ready += post_frame
Kinect.video_frame_ready += video_frame_ready
Kinect.video_stream.open(nui.ImageStreamType.Video, 2,
nui.ImageResolution.Resolution640x480, nui.ImageType.Color)
# Kinect.depth_frame_ready += depth_frame_ready
# Kinect.depth_stream.open(nui.ImageStreamType.Depth, 2,
# nui.ImageResolution.Resolution640x480, nui.ImageType.Depth)
print(u'Commandes : ')
print(u' \u2191 - Lever le capteur')
print(u' \u2193 - Baisser le capteur')
print(u" \u2194 - Changer d'articulation")
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from math import *
class point():
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __repr__(self):
def r(n):
return round(n,3)
return "point("+str(r(self.x))+","+str(r(self.y))+","+str(r(self.z))+")"
class vecteur():
def __init__(self,*args):
if len(args) == 2:
pt1 = args[0]
pt2 = args[1]
self.x = pt2.x - pt1.x
self.y = pt2.y - pt1.y
self.z = pt2.z - pt1.z
elif len(args) == 3:
self.x, self.y, self.z = args
elif len(args) == 1:
self.x, self.y, self.z = args[0].x, args[0].y, args[0].z
self.norm = self.calc_norm()
#self.dir = self.calc_dir()
def calc_norm(self):
return sqrt(self.x**2+self.y**2+self.z**2)
def calc_dir(self):
return vecteur(self) / self.norm
def __mul__(self, x):
if isinstance(x, vecteur):
return vecteur(self.y*x.z-self.z*x.y,
self.z*x.x-self.x*x.z,
self.x*x.z-self.z*x.x)
else:
return vecteur(self.x*x,
self.y*x,
self.z*x)
def __div__(self, x):
if x != 0:
return vecteur(self.x/x,
self.y/x,
self.z/x)
else:
return vecteur(self.x,
self.y,
self.z)
def __rmul__(self, x):
if isinstance(x, vecteur):
return vecteur(self.z*x.y-self.y*x.z,
self.x*x.z-self.z*x.x,
self.z*x.x-self.x*x.z)
else:
return vecteur(self.x*x,
self.y*x,
self.z*x)
def pscal(self, v):
return self.x*v.x + self.y*v.y + self.z*v.z
def angle(pt1, pt2, pt3, deg = False):
v1 = vecteur(pt2, pt1).calc_dir()
v2 = vecteur(pt2, pt3).calc_dir()
if deg:
return r2d(acos(v2.pscal(v1)))
else:
return acos(v2.pscal(v1))
def r2d(a):
return 180/pi*a
def d2r(a):
return pi/180*a
Coté Arduino
#include <Servo.h>;
Servo s1;
Servo s2;
void setup()
{
s1.attach(9); // avant/arrière
s2.attach(10); // haut/bas
Serial.begin(115200);
s1.write(90);
s2.write(90);
}
void loop() {
// si le buffer n'est pas vide ...
if (Serial.available()) {
int a1 = Serial.parseInt();
Serial.read(); // la virgule entre les deux nombres
int a2 = Serial.parseInt();
if (Serial.read() == '\n') {
s1.write(a1);
s2.write(a2);
Serial.flush();
}
}
delay(10);
}
Résultat
Carnet de bord
| Date |
Activité recherche documentaire, formation technique, analyse fonctionnelle, expérimentation simulation |
Réalisation conception fabrication assemblage |
Objectifs pour la prochaine séance |
Remarques |
| Formation Arduino | ||||
| 14/12 | Activité 1 : Piloter un logiciel 3D (Solidworks) par programmation : découverte des macro et des fonctions de visualisation de l’API de Solidworks |
|||
| 11/01 | Activité 2 : Piloter Solidworks à partir d’un programme Python. |
|||
| 18/01 | Préparation du robot Braduino | Assemblage d’une articulation du robot Braduino | ||
| 01/02 | Fixation du bras sur le socle / réglages | |||
| 08/03 | Acquisition des coordonnées des poignets/coudes/épaules droits pour pilotage d’une articulation du robot. | Apprendre à calculer un angle formé par 3 points dans l’espace ! | ||
| 22/3 | ||||
| 17/05 | Journée de valorisation à Ladoux |

