Créer un Système de Reconnaissance des Positions de Mains avec Raspberry Pi et Camera Tilt Hat
Les technologies modernes permettent aujourd’hui de transformer un simple Raspberry Pi en un système innovant de reconnaissance gestuelle et de position des mains. Ce type de projet peut être utilisé pour des applications variées telles que l’apprentissage du langage des signes, l’interaction homme-machine ou encore la création d’outils pédagogiques et ludiques.
Dans cet article, nous allons explorer comment construire un tel système en utilisant un Raspberry Pi, une caméra, un Camera Tilt Hat, ainsi que des bibliothèques Python telles qu’OpenCV et MediaPipe.
Matériel nécessaire
- Raspberry Pi 4 ou Pi Zero W (selon vos besoins en performances)
- Caméra compatible Raspberry Pi (comme la Camera Module v2)
- Camera Tilt Hat (pour suivre les mouvements)
- Carte microSD avec Raspberry Pi OS installé
- Bloc d’alimentation pour Raspberry Pi
- Câbles et accessoires GPIO
- Accès réseau (facultatif pour télécharger les dépendances)
Objectifs du projet
- Détecter et suivre les mains de l’utilisateur à l’aide de la caméra.
- Reconnaître les positions et articulations des doigts avec précision.
- Enregistrer les gestes capturés et les associer à des lettres, mots ou phrases.
- Sauvegarder les données pour un apprentissage ou une reconnaissance ultérieure.
- Offrir une interface intuitive et interactive.
Suivez notre formation gratuite à "github" (pour les auto-hébergeurs avancées, vous saurez l'adapter à votre propre dépot), ou peut-être pour commencer cette formation au "Shell et bash"
Contactez-nous pour vous joindre à notre formation "apprenti Dragon"
Étape 1 : Préparer l’environnement
Installer Raspberry Pi OS
- Téléchargez et installez Raspberry Pi Imager depuis le site officiel.
- Insérez la carte microSD et flashez Raspberry Pi OS.
- Connectez votre Raspberry Pi à un écran, un clavier et configurez le réseau.
Installer les dépendances
Connectez-vous au Raspberry Pi via SSH ou terminal, et installez les bibliothèques nécessaires :
sudo apt update
sudo apt install -y python3 python3-pip libatlas-base-dev
pip3 install opencv-python mediapipe RPi.GPIO
Étape 2 : Configurer le Camera Tilt Hat
Branchements GPIO
- Connectez le Camera Tilt Hat au Raspberry Pi via les broches GPIO. Par défaut :
- Pin Pan : GPIO 17
- Pin Tilt : GPIO 18
- Branchez également la caméra au port CSI du Raspberry Pi.
Tester le Hat
Vous pouvez utiliser un script simple pour tester les mouvements du Hat :
import RPi.GPIO as GPIO
from time import sleep
pan_pin = 17
tilt_pin = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(pan_pin, GPIO.OUT)
GPIO.setup(tilt_pin, GPIO.OUT)
pan = GPIO.PWM(pan_pin, 50)
tilt = GPIO.PWM(tilt_pin, 50)
pan.start(7.5) # Position neutre
tilt.start(7.5)
try:
while True:
pan.ChangeDutyCycle(5) # Déplace vers la gauche
tilt.ChangeDutyCycle(10) # Déplace vers le bas
sleep(1)
pan.ChangeDutyCycle(10) # Déplace vers la droite
tilt.ChangeDutyCycle(5) # Déplace vers le haut
sleep(1)
except KeyboardInterrupt:
pan.stop()
tilt.stop()
GPIO.cleanup()
Étape 3 : Détecter les mains avec MediaPipe
MedaPipe offre des solutions prêtes à l’emploi pour détecter les mains et leurs articulations.
Exemple de détection simple
Voici un script qui détecte les mains et affiche les points clés sur la vidéo en temps réel :
import cv2
import mediapipe as mp
# Initialisation de MediaPipe Hands
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=2, min_detection_confidence=0.5)
# Initialisation de la caméra
cap = cv2.VideoCapture(0)
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
frame = cv2.flip(frame, 1)
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = hands.process(rgb_frame)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
mp.solutions.drawing_utils.draw_landmarks(
frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
cv2.imshow('Hand Detection', frame)
if cv2.waitKey(1) & 0xFF == 27: # Échapper pour quitter
break
cap.release()
cv2.destroyAllWindows()
Étape 4 : Ajouter le suivi automatique
Pour que la caméra suive les mains, nous ajusterons le Camera Tilt Hat en fonction des coordonnées détectées.
Ajouter le suivi
Modifiez le script pour inclure le contrôle des moteurs du Tilt Hat :
def adjust_camera(hand_landmarks):
x = hand_landmarks.landmark[mp_hands.HandLandmark.WRIST].x
y = hand_landmarks.landmark[mp_hands.HandLandmark.WRIST].y
pan_angle = np.interp(x, [0, 1], [2.5, 12.5]) # Ajustez les limites selon votre configuration
tilt_angle = np.interp(y, [0, 1], [2.5, 12.5])
pan.ChangeDutyCycle(pan_angle)
tilt.ChangeDutyCycle(tilt_angle)
Ajoutez cet appel à la boucle de détection des mains.
Étape 5 : Enregistrer les gestes
Capturer les données
Pour chaque geste détecté, enregistrez les coordonnées dans un fichier JSON :
import json
gestures = {}
def save_gesture(label, hand_landmarks):
data = [{"x": lm.x, "y": lm.y, "z": lm.z} for lm in hand_landmarks.landmark]
gestures[label] = data
with open('gestures.json', 'w') as f:
json.dump(gestures, f)
Étape 6 : Reconnaissance et apprentissage
Une fois les gestes enregistrés, vous pouvez les comparer aux données en temps réel pour reconnaître des signes ou des positions.
Algorithme de reconnaissance
- Chargez les gestes depuis le fichier JSON.
- Comparez les positions actuelles avec les gestes enregistrés en utilisant un score de similarité.
- Affichez le geste correspondant à l’écran ou émettez un son.
Étape 7 : Améliorations potentielles
- Synthèse vocale : Traduire les gestes en parole avec des bibliothèques comme
pyttsx3
. - Interface graphique : Ajouter une interface utilisateur pour simplifier l’interaction.
- Modèles avancés : Utiliser des modèles d’apprentissage machine pour reconnaître des gestes complexes.
Voici le code complet combinant les étapes précédentes pour un système de reconnaissance de positions de mains avec suivi automatique de la caméra et enregistrement des gestes.
Système de reconnaissance des positions de mains (et des visages)
hand_recognition.py
import cv2
import mediapipe as mp
import numpy as np
import json
import RPi.GPIO as GPIO
from time import sleep
# Configuration du Camera Tilt Hat
pan_pin = 17
tilt_pin = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(pan_pin, GPIO.OUT)
GPIO.setup(tilt_pin, GPIO.OUT)
pan = GPIO.PWM(pan_pin, 50)
tilt = GPIO.PWM(tilt_pin, 50)
pan.start(7.5) # Position neutre
tilt.start(7.5)
# Initialisation de MediaPipe Hands
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=2, min_detection_confidence=0.5)
# Initialisation de la caméra
cap = cv2.VideoCapture(0)
# Dictionnaire pour stocker les gestes
gestures = {}
# Fonction pour ajuster la caméra en fonction des coordonnées de la main
def adjust_camera(hand_landmarks):
x = hand_landmarks.landmark[mp_hands.HandLandmark.WRIST].x
y = hand_landmarks.landmark[mp_hands.HandLandmark.WRIST].y
pan_angle = np.interp(x, [0, 1], [2.5, 12.5]) # Ajustez les valeurs selon votre configuration
tilt_angle = np.interp(y, [0, 1], [2.5, 12.5])
pan.ChangeDutyCycle(pan_angle)
tilt.ChangeDutyCycle(tilt_angle)
sleep(0.1)
# Fonction pour capturer un geste
def capture_gesture():
while cap.isOpened():
success, image = cap.read()
if not success:
print("Ignorer le cadre vide de la caméra.")
continue
image = cv2.flip(image, 1)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = hands.process(image_rgb)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
adjust_camera(hand_landmarks)
mp.solutions.drawing_utils.draw_landmarks(image, hand_landmarks, mp_hands.HAND_CONNECTIONS)
cv2.imshow('Détection des mains', image)
key = cv2.waitKey(5)
if key == 32: # Barre d'espace pour capturer
if results.multi_hand_landmarks:
return [[lm.x, lm.y, lm.z] for lm in results.multi_hand_landmarks[0].landmark]
else:
print("Aucune main détectée.")
return None
elif key == 27: # ESC pour quitter
return None
# Fonction principale
def main():
global gestures
# Charger les gestes existants si le fichier JSON est présent
try:
with open('gestures.json', 'r') as f:
gestures = json.load(f)
print("Gestes chargés depuis gestures.json.")
except FileNotFoundError:
print("Aucun fichier de gestes trouvé, démarrage avec une base vide.")
while True:
print("\n1. Enregistrer un nouveau geste")
print("2. Afficher les gestes enregistrés")
print("3. Reconnaître un geste en temps réel")
print("4. Sauvegarder et quitter")
choice = input("Choisissez une option : ")
if choice == '1':
label = input("Entrez une lettre, un mot ou une phrase pour ce geste : ")
print("Placez votre main devant la caméra et appuyez sur la barre d'espace pour capturer.")
gesture_data = capture_gesture()
if gesture_data:
gestures[label] = gesture_data
print(f"Geste pour '{label}' enregistré.")
else:
print("Capture annulée.")
elif choice == '2':
print("\nGestes enregistrés :")
for label in gestures:
print(f"- {label}")
elif choice == '3':
print("Placez votre main devant la caméra pour reconnaître un geste...")
while True:
success, image = cap.read()
if not success:
print("Erreur avec la caméra.")
break
image = cv2.flip(image, 1)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = hands.process(image_rgb)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
current_gesture = [[lm.x, lm.y, lm.z] for lm in hand_landmarks.landmark]
# Comparaison avec les gestes enregistrés
recognized_label = None
min_distance = float('inf')
for label, saved_gesture in gestures.items():
distance = np.linalg.norm(np.array(saved_gesture) - np.array(current_gesture))
if distance < min_distance:
min_distance = distance
recognized_label = label
if recognized_label:
print(f"Geste reconnu : {recognized_label}")
cv2.putText(image, f"Geste : {recognized_label}", (50, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)
cv2.imshow('Reconnaissance de geste', image)
if cv2.waitKey(5) & 0xFF == 27: # ESC pour quitter
break
elif choice == '4':
with open('gestures.json', 'w') as f:
json.dump(gestures, f)
print("Gestes sauvegardés. Au revoir !")
break
else:
print("Option invalide. Veuillez réessayer.")
cap.release()
cv2.destr
enhanced_gesture_recognition.py
import cv2
import mediapipe as mp
import numpy as np
import tensorflow as tf
import pyttsx3
import tkinter as tk
from tkinter import messagebox, filedialog
import os
import json
import RPi.GPIO as GPIO
from time import sleep
import threading
# Configuration du Camera Tilt Hat
pan_pin = 17
tilt_pin = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(pan_pin, GPIO.OUT)
GPIO.setup(tilt_pin, GPIO.OUT)
pan = GPIO.PWM(pan_pin, 50)
tilt = GPIO.PWM(tilt_pin, 50)
pan.start(7.5)
tilt.start(7.5)
# Initialisation des modules de détection
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=2, min_detection_confidence=0.5)
mp_face_detection = mp.solutions.face_detection
face_detection = mp_face_detection.FaceDetection(min_detection_confidence=0.5)
# Synthèse vocale
engine = pyttsx3.init()
# Modèles d'apprentissage profond
hand_model = tf.keras.models.load_model("hand_recognition_model.h5")
face_model = tf.keras.models.load_model("face_recognition_model.h5")
# Dictionnaire pour les utilisateurs
users = {
"hands": {},
"faces": {}
}
# Fonction pour ajuster la caméra
def adjust_camera(x, y):
pan_angle = np.interp(x, [0, 1], [2.5, 12.5])
tilt_angle = np.interp(y, [0, 1], [2.5, 12.5])
pan.ChangeDutyCycle(pan_angle)
tilt.ChangeDutyCycle(tilt_angle)
sleep(0.1)
# Chargement et sauvegarde des utilisateurs
def load_users():
if os.path.exists("users.json"):
with open("users.json", "r") as f:
return json.load(f)
return {"hands": {}, "faces": {}}
def save_users():
with open("users.json", "w") as f:
json.dump(users, f)
messagebox.showinfo("Succès", "Les utilisateurs ont été sauvegardés avec succès !")
# Détection et enregistrement des mains
def detect_and_register_hand():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
messagebox.showerror("Erreur", "Impossible d'accéder à la caméra.")
return
messagebox.showinfo("Instruction", "Placez votre main devant la caméra et appuyez sur ESPACE pour enregistrer.")
while cap.isOpened():
success, image = cap.read()
if not success:
print("Échec de lecture vidéo.")
continue
image = cv2.flip(image, 1)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = hands.process(image_rgb)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
mp.solutions.drawing_utils.draw_landmarks(image, hand_landmarks, mp_hands.HAND_CONNECTIONS)
# Ajuster la caméra
wrist = hand_landmarks.landmark[mp_hands.HandLandmark.WRIST]
adjust_camera(wrist.x, wrist.y)
# Convertir les points en tableau pour le modèle d'IA
hand_features = np.array([[lm.x, lm.y, lm.z] for lm in hand_landmarks.landmark]).flatten()
# Prédiction avec le modèle d'IA
predicted_hand = hand_model.predict(np.expand_dims(hand_features, axis=0))[0]
recognized_hand = np.argmax(predicted_hand)
# Enregistrer la main
if cv2.waitKey(1) & 0xFF == ord(" "):
name = hand_name.get()
users["hands"][name] = recognized_hand
messagebox.showinfo("Succès", f"Main enregistrée sous le nom '{name}'.")
save_users()
break
cv2.imshow("Détection des mains", image)
if cv2.waitKey(5) & 0xFF == 27: # ESC pour quitter
break
cap.release()
cv2.destroyAllWindows()
# Détection et enregistrement des visages
def detect_and_register_face():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
messagebox.showerror("Erreur", "Impossible d'accéder à la caméra.")
return
messagebox.showinfo("Instruction", "Placez votre visage devant la caméra et appuyez sur ESPACE pour enregistrer.")
while cap.isOpened():
success, image = cap.read()
if not success:
print("Échec de lecture vidéo.")
continue
image = cv2.flip(image, 1)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = face_detection.process(image_rgb)
if results.detections:
for detection in results.detections:
mp.solutions.drawing_utils.draw_detection(image, detection)
# Ajuster la caméra
bboxC = detection.location_data.relative_bounding_box
adjust_camera(bboxC.xmin + bboxC.width/2, bboxC.ymin + bboxC.height/2)
# Extraire les caractéristiques faciales pour le modèle d'IA
face_image = image[int(bboxC.ymin*image.shape[0]):int((bboxC.ymin+bboxC.height)*image.shape[0]),
int(bboxC.xmin*image.shape[1]):int((bboxC.xmin+bboxC.width)*image.shape[1])]
face_image = cv2.resize(face_image, (224, 224))
face_features = face_model.predict(np.expand_dims(face_image, axis=0))[0]
# Enregistrer le visage
if cv2.waitKey(1) & 0xFF == ord(" "):
name = face_name.get()
users["faces"][name] = face_features.tolist()
messagebox.showinfo("Succès", f"Visage enregistré sous le nom '{name}'.")
save_users()
break
cv2.imshow("Détection des visages", image)
if cv2.waitKey(5) & 0xFF == 27: # ESC pour quitter
break
cap.release()
cv2.destroyAllWindows()
# Mode de détection et prédiction
def detect_and_predict():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
messagebox.showerror("Erreur", "Impossible d'accéder à la caméra.")
return
while cap.isOpened():
success, image = cap.read()
if not success:
print("Échec de lecture vidéo.")
continue
image = cv2.flip(image, 1)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Détection des mains
if hand_detection_var.get():
hand_results = hands.process(image_rgb)
if hand_results.multi_hand_landmarks:
for hand_landmarks in hand_results.multi_hand_landmarks:
mp.solutions.drawing_utils.draw_landmarks(image, hand_landmarks, mp_hands.HAND_CONNECTIONS)
hand_features = np.array([[lm.x, lm.y, lm.z] for lm in hand_landmarks.landmark]).flatten()
predicted_hand = hand_model.predict(np.expand_dims(hand_features, axis=0))[0]
recognized_hand = np.argmax(predicted_hand)
for name, hand_id in users["hands"].items():
if hand_id == recognized_hand:
cv2.putText(image, f"Main: {name}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
engine.say(f"Main détectée : {name}")
engine.runAndWait()
break
# Détection des visages
if face_detection_var.get():
face_results = face_detection.process(image_rgb)
if face_results.detections:
for detection in face_results.detections:
mp.solutions.drawing_utils.draw_detection(image, detection)
bboxC = detection.location_data.relative_bounding_box
face_image = image[int(bboxC.ymin*image.shape[0]):int((bboxC.ymin+bboxC.height)*image.shape[0]),
int(bboxC.xmin*image.shape[1]):int((bboxC.xmin+bboxC.width)*image.shape[1])]
face_image = cv2.resize(face_image, (224, 224))
face_features = face_model.predict(np.expand_dims(face_image, axis=0))[0]
min_distance = float('inf')
recognized_name = None
for name, stored_features in users["faces"].items():
distance = np.linalg.norm(np.array(stored_features) - face_features)
if distance < min_distance:
min_distance = distance
recognized_name = name
if recognized_name and min_distance < 0.6: # Seuil de similarité
cv2.putText(image, f"Visage: {recognized_name}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
engine.say(f"Visage détecté : {recognized_name}")
engine.runAndWait()
if resolution_var.get():
image = cv2.resize(image, (320, 240))
cv2.imshow("Détection et Prédiction", image)
if cv2.waitKey(5) & 0xFF == 27: # ESC pour quitter
break
cap.release()
cv2.destroyAllWindows()
# Interface graphique
root = tk.Tk()
root.title("Reconnaissance des mains et visages")
hand_name = tk.StringVar()
face_name = tk.StringVar()
frame = tk.Frame(root, padx=10, pady=10)
frame.pack()
tk.Label(frame, text="Nom pour la main :").grid(row=0, column=0, padx=5, pady=5)
tk.Entry(frame, textvariable=hand_name).grid(row=0, column=1, padx=5, pady=5)
tk.Button(frame, text="Enregistrer une main", command=detect_and_register_hand).grid(row=1, column=0, columnspan=2, pady=10)
tk.Label(frame, text="Nom pour le visage :").grid(row=2, column=0, padx=5, pady=5)
tk.Entry(frame, textvariable=face_name).grid(row=2, column=1, padx=5, pady=5)
tk.Button(frame, text="Enregistrer un visage", command=detect_and_register_face).grid(row=3, column=0, columnspan=2, pady=10)
face_detection_var = tk.BooleanVar()
hand_detection_var = tk.BooleanVar()
tk.Checkbutton(frame, text="Détection de visage", variable=face_detection_var).grid(row=4, column=0)
tk.Checkbutton(frame, text="Détection de main", variable=hand_detection_var).grid(row=4, column=1)
resolution_var = tk.BooleanVar()
tk.Checkbutton(frame, text="Basse résolution", variable=resolution_var).grid(row=5, column=0, columnspan=2)
hand_count = tk.IntVar(value=2)
tk.Radiobutton(frame, text="Une main", variable=hand_count, value=1).grid(row=6, column=0)
tk.Radiobutton(frame, text="Deux mains", variable=hand_count, value=2).grid(row=6, column=1)
tk.Button(frame, text="Mode Détection et Prédiction", command=detect_and_predict).grid(row=7, column=0, columnspan=2, pady=10)
tk.Button(frame, text="Sauvegarder les utilisateurs", command=save_users).grid(row=8, column=0, columnspan=2, pady=10)
tk.Button(frame, text="Quitter", command=root.quit).grid(row=9, column=0, columnspan=2, pady=10)
# Chargement des utilisateurs existants
users = load_users()
# Traitement asynchrone
def process_frame(frame):
# Traitement de l'image ici
pass
def capture_and_process():
while running:
ret, frame = cap.read()
if ret:
if resolution_var.get():
frame = cv2.resize(frame, (320, 240))
threading.Thread(target=process_frame, args=(frame,)).start()
running = True
threading.Thread(target=capture_and_process).start()
root.mainloop()
# Nettoyage
GPIO.cleanup()
Fonctionnalités
- Ajustement automatique de la caméra : La caméra suit les mouvements de la main pour rester alignée grâce au Camera Tilt Hat.
- Détection des mains : Utilise MediaPipe pour capturer les points clés des mains en temps réel.
- Enregistrement des gestes : Permet d’associer une position de main à une lettre, un mot ou une phrase.
- Reconnaissance en temps réel : Compare les gestes actuels avec ceux enregistrés pour les reconnaître.
- Sauvegarde et chargement : Les gestes enregistrés sont sauvegardés dans un fichier JSON.
Installation des dépendances
Installez les bibliothèques nécessaires avant d’exécuter le script :
pip3 install opencv-python mediapipe RPi.GPIO
pyttsx3
tensorflow
Utilisation
Lancez le script :
python3 hand_recognition.py
. ouenhanced_gesture_recognition.py
Choisissez les options dans le menu :
- 1 : Enregistrer un nouveau geste ou visage.
- 2 : Voir les gestes enregistrés.
- 3 : Reconnaître un geste ou visage en temps réel.
- 4 : Sauvegarder et quitter.
Appuyez sur
ESC
pour quitter une option.
Avec ce système, vous disposez d’un outil puissant pour expérimenter avec la reconnaissance gestuelle et explorer des applications créatives comme l’apprentissage du langage des signes ou la commande de dispositifs interactifs.
Améliorations possibles
- Reconnaissance plus robuste : Utilisez des modèles d’apprentissage profond pour une précision accrue.
- Interface graphique : Ajouter une interface utilisateur pour une meilleure accessibilité.
- Traduction vocale : Intégrez une synthèse vocale pour traduire les gestes reconnus en paroles.
Partageons nos innovations sur "pad.p2p.legal/RpiTiltHat
Vous pigez tout, et avez envie de partager ce savoir faire avec d'autres ?
>>> Joignez vous à la formation "Dragon"
Système de Reconnaissance des Positions de Mains avec Raspberry
suivez les étapes de création du code