☁️
☁️
🌴
🌴
🏎️
🚗
Arduino Days 2026 · Cotonou, Bénin · Hackathon IA

Gesture Hero

Rise of the Motion Masters

Documentation technique complète du projet : contrôler Beach Buggy Racing par les gestes de la main grâce à l'intelligence artificielle embarquée, sans jamais toucher un clavier. 🖐️🎮

Arduino Nano 33 BLE Edge Impulse ESP32 Python / pynput TinyML Beach Buggy Racing
Explorer la doc 🚀
Scroll pour commencer

Présentation du projet

Gesture Hero est un système de contrôle gestuel intelligent développé lors du hackathon Arduino Days 2026 à Cotonou. L'idée centrale est simple et ambitieuse à la fois : remplacer complètement le clavier par les mouvements de la main pour jouer à un jeu vidéo en temps réel.

Le joueur tient dans sa main une carte Arduino Nano 33 BLE Sense. Selon la direction dans laquelle il incline ou déplace sa main, un modèle d'intelligence artificielle — entraîné sur la plateforme Edge Impulse — reconnaît le geste et envoie la commande correspondante au jeu via Bluetooth, un pont ESP32, et un script Python.

🎯 Objectif Opérationnel

Prendre le contrôle d'un véhicule dans Beach Buggy Racing (tourner à gauche, tourner à droite, accélérer, freiner) uniquement avec des gestes de la main, sans aucune modification du jeu.

🎮 Le jeu : Beach Buggy Racing

Beach Buggy Racing est un jeu de course arcade multi-plateforme aux commandes directes et réactives. Il se joue normalement avec les quatre touches directionnelles du clavier. Ce choix est stratégique pour le hackathon : les commandes sont simples (4 touches), la réactivité se teste immédiatement, et aucune modification du jeu n'est nécessaire.

🖐️ Les gestes à reconnaître

⬆️
HAUT
Main inclinée vers l'avant
KEY.UP
Accélérer
⬇️
BAS
Main inclinée vers l'arrière
KEY.DOWN
Freiner / Reculer
⬅️
GAUCHE
Main balayée vers la gauche
KEY.LEFT
Tourner à gauche
➡️
DROITE
Main balayée vers la droite
KEY.RIGHT
Tourner à droite
IDLE
Main au repos
NONE
Aucune action
⚠️ Classe supplémentaire importante

La classe idle (main au repos) est indispensable pour éviter les faux positifs. Sans elle, le modèle interprétera tout bruit comme un geste.


Les composants du système

Le système repose sur trois composants matériels et un logiciel, chacun ayant un rôle précis dans la chaîne de traitement.

🔵
Arduino Nano 33 BLE Sense
Le cerveau et les capteurs du système. Embarque un IMU 9 axes + modèle TinyML + module BLE 5.0.
MCU ARM Cortex-M4 @ 64 MHz
IMU LSM9DS1 (9 axes)
Module BLE nRF52840
1 MB Flash, 256 KB RAM
Alimenté via powerbank
🟢
ESP32 DevKit
Le pont de communication. Reçoit les données BLE de l'Arduino et les retransmet au PC via USB Serial.
MCU Xtensa LX6 dual-core
Bluetooth 4.2 BLE intégré
Connexion USB → PC
Pont transparent BLE → Serial
Alimenté via PC (USB)
🐍
PC + Script Python
Lit le port série, convertit les gestes en appuis clavier via pynput. Le jeu ne sait pas que ce n'est pas un vrai clavier.
Python 3.x
pyserial — lecture port série
pynput — simulation clavier
Latence < 5 ms
Beach Buggy Racing installé

🧠 Edge Impulse (plateforme cloud)

Edge Impulse est la plateforme utilisée pour collecter les données d'entraînement, concevoir et entraîner le modèle de classification, et l'exporter en bibliothèque Arduino prête à l'emploi.


Comment tout fonctionne ensemble

Du geste physique jusqu'à l'action dans le jeu — en temps réel.

🖐️
Main
Geste physique
🔵
Arduino
IMU + TinyML + BLE
📡
🟢
ESP32
BLE → Serial USB
🔌
🐍
Python
pynput → clavier
⌨️
🏎️
Beach Buggy
Jeu vidéo

⏱️ Latence attendue sur la chaîne

SegmentTechnologieLatence estimée
Capture IMU → inférence modèleArduino + TinyML~80–120 ms
Transmission BLEBluetooth Low Energy 5.0~10–30 ms
ESP32 → PC (Serial USB)UART 115200 baud< 5 ms
Script Python → touche clavierpynput< 5 ms
TOTAL~100–160 ms

✅ Critères de qualité du système

  • LATENCE — Réaction < 200 ms entre geste et action
  • PRÉCISION — Modèle > 85 % sur les données de test
  • STABILITÉ — Connexion BLE maintenue pendant toute la partie
  • ROBUSTESSE — Pas de faux positifs sur la classe idle
  • FLUIDITÉ — Contrôle naturel et intuitif du véhicule
  • PORTÉE — BLE stable jusqu'à ~2–3 mètres du PC
🌊 🏖️ 🌴 🏎️ 🌊
01
Préparation de l'environnement
SAMEDI MATIN · ~30 MIN

Installer Edge Impulse CLI

Edge Impulse CLI permet de connecter l'Arduino directement à la plateforme cloud.

terminal
npm install -g edge-impulse-cli

# Vérifier l'installation :
edge-impulse-daemon --version

Configurer l'IDE Arduino

  • Télécharger Arduino IDE 2.x depuis arduino.cc
  • Ajouter le support : Tools → Board Manager → Arduino Mbed OS Nano Boards
  • Installer la bibliothèque ArduinoBLE via le Library Manager
  • Installer la bibliothèque Arduino_LSM9DS1

Installer Python et les dépendances

terminal
pip install pyserial pynput
💡 Conseil

Faire une rapide vérification de toute la chaîne avant de commencer : Arduino reconnu par l'IDE, Edge Impulse CLI connecté, Python fonctionnel. Identifier les problèmes dès le début évite de perdre du temps plus tard.


02
Collecte des données de gestes
SAMEDI MATIN · ~45 MIN

C'est l'étape la plus importante du projet. La qualité du modèle dépend entièrement de la qualité des données collectées.

Connecter l'Arduino à Edge Impulse

terminal
edge-impulse-data-forwarder

Paramètres de collecte

  • Fréquence d'échantillonnage : 62.5 Hz ou 100 Hz
  • Durée par sample : 1000 ms (1 seconde)
  • Colonnes : accX, accY, accZ, gyrX, gyrY, gyrZ
ClasseDescription du gesteSamples minimum
hautIncliner/pousser la main vers le haut ou l'avant60 samples
basIncliner/pousser la main vers le bas ou l'arrière60 samples
gaucheBalayer ou incliner la main vers la gauche60 samples
droiteBalayer ou incliner la main vers la droite60 samples
idleMain au repos, sans mouvement intentionnel60 samples
🏆 Règles d'or pour la collecte
  • Faire chaque geste de façon nette et cohérente
  • Varier la vitesse (rapide et lente) et la position
  • La classe idle est aussi importante que les autres
  • Répartition recommandée : 80 % train / 20 % test

03
Entraînement du modèle sur Edge Impulse
SAMEDI APRÈS-MIDI · ~30 MIN

1. Configurer l'Impulse

  • Window size : 1000 ms
  • Window increase : 200 ms
  • Bloc de traitement : Spectral Analysis
  • Bloc d'apprentissage : Classification (Keras)

2. Configurer Spectral Analysis

  • Type : FFT — FFT length : 16 ou 32
  • Cocher : Log of spectrum, Noise floor
  • Cliquer Save Parameters puis Generate Features

3. Architecture du réseau de neurones

Input Layer
78 features
Spectral features
Dense Layer
20 neurons
ReLU activation
Dense Layer
10 neurons
ReLU activation
Output Layer
5 classes
Softmax
  • Epochs : 30 — Learning rate : 0.0005
  • Validation set size : 20 %
  • Model version : Quantized (int8)

4. Exporter la bibliothèque Arduino

  • Onglet Deployment → Sélectionner Arduino Library
  • Optimisation : Enable EON Compiler
  • Cliquer Build et télécharger le fichier .zip
  • Arduino IDE : Sketch → Include Library → Add .ZIP Library

🏆 Performances du modèle — Résultats finaux

Résultats obtenus après entraînement sur Edge Impulse Studio — validation set (Quantized int8)

90.3%
Accuracy
📉
0.34
Loss
0.91
Precision
0.90
F1 Score
📈 Area under ROC Curve 0.99
🎯 Weighted Avg Precision 0.91
🔄 Weighted Avg Recall 0.90
⚖️ Weighted Avg F1 Score 0.90
📊 Matrice de Confusion (validation set)
DOWN IDLE LEFT RIGHT UP
DOWN 92.1% 0% 0% 0% 7.9%
IDLE 0% 100% 0% 0% 0%
LEFT 0% 0% 70% 26.7% 3.3%
RIGHT 0% 0% 20% 66.7% 13.3%
UP 8.9% 0% 0% 0% 91.1%
F1 SCORE 0.88 1.00 0.78 0.61 0.92
📖 Lecture de la matrice
  • IDLE → 100% F1 = 1.00 — Parfaitement reconnu, zéro faux positif 🎯
  • UP → 91.1% F1 = 0.92 — Excellent, léger bruit avec DOWN
  • DOWN → 92.1% F1 = 0.88 — Très bon, quelques confusions avec UP
  • LEFT → 70% F1 = 0.78 — Correct mais confondu avec RIGHT (26.7%)
  • RIGHT → 66.7% F1 = 0.61 — Point faible — confusion avec LEFT et UP
🔧 Piste d'amélioration

Les gestes LEFT et RIGHT se confondent mutuellement (20–27%). Pour améliorer : collecter +30 samples supplémentaires pour chaque, en exagérant la distinction latérale. Augmenter les epochs à 50 pourrait aussi aider le modèle à mieux séparer ces classes.

📸 Capture Edge Impulse Studio

Cliquez sur l'image pour l'agrandir — vue complète du dashboard d'entraînement

🔍 Cliquer pour agrandir
🔒 studio.edgeimpulse.com/studio/943378/impulse/1/learning/keras/4
EDGE IMPULSE
Edge Impulse Studio — Résultats d'entraînement du modèle Gesture Hero : 90.3% accuracy, matrice de confusion, métriques

Edge Impulse Studio — Architecture du réseau de neurones, matrice de confusion et métriques du modèle gesture-hero

📊 Interpréter la matrice de confusion

Chaque ligne = classe réelle, chaque colonne = classe prédite. Les cases diagonales (vert) sont les bonnes prédictions. Si idle est confondu avec un geste → collecter plus de samples idle. Dans notre cas, idle est parfait à 100% ! 🎯

04
Code Arduino — Émetteur BLE
SAMEDI APRÈS-MIDI · ~45 MIN

Ce sketch lit l'IMU, fait tourner le modèle d'inférence, et envoie le geste détecté via BLE.

Arduino C++
#include <ArduinoBLE.h>
#include <NOM_DE_TON_PROJET_inferencing.h>
#include <Arduino_LSM9DS1.h>

// ── UUIDs du service BLE ──
#define SERVICE_UUID "19B10000-E8F2-537E-4F6C-D104768A1214"
#define CHAR_UUID    "19B10001-E8F2-537E-4F6C-D104768A1214"

BLEService gestureService(SERVICE_UUID);
BLEStringCharacteristic gestureChar(CHAR_UUID, BLERead | BLENotify, 20);

#define CONFIDENCE_THRESHOLD 0.70f
#define IDLE_LABEL "idle"

float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE];

void setup() {
  Serial.begin(115200);
  if (!IMU.begin()) { Serial.println("IMU ERREUR"); while(1); }
  if (!BLE.begin()) { Serial.println("BLE ERREUR"); while(1); }

  BLE.setLocalName("GestureHero");
  BLE.setAdvertisedService(gestureService);
  gestureService.addCharacteristic(gestureChar);
  BLE.addService(gestureService);
  BLE.advertise();
  Serial.println("GestureHero prêt — en attente BLE...");
}

void loop() {
  BLEDevice central = BLE.central();
  if (!central) return;

  while (central.connected()) {
    // 1. Remplir le buffer IMU
    int samples = EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE / 6;
    for (int i = 0; i < samples; i++) {
      float ax, ay, az, gx, gy, gz;
      while (!IMU.accelerationAvailable());
      IMU.readAcceleration(ax, ay, az);
      IMU.readGyroscope(gx, gy, gz);
      int idx = i * 6;
      features[idx] = ax; features[idx+1] = ay; features[idx+2] = az;
      features[idx+3] = gx; features[idx+4] = gy; features[idx+5] = gz;
      delay(1000 / EI_CLASSIFIER_FREQUENCY);
    }

    // 2. Inférence
    signal_t signal;
    numpy::signal_from_buffer(features, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
    ei_impulse_result_t result = { 0 };
    run_classifier(&signal, &result, false);

    // 3. Meilleure classe
    String best = ""; float score = 0;
    for (int j = 0; j < EI_CLASSIFIER_LABEL_COUNT; j++) {
      if (result.classification[j].value > score) {
        score = result.classification[j].value;
        best = result.classification[j].label;
      }
    }

    // 4. Envoyer via BLE
    if (score >= CONFIDENCE_THRESHOLD && best != IDLE_LABEL) {
      gestureChar.writeValue(best);
    }
  }
  BLE.advertise();
}
⚠️ Important

Remplacer NOM_DE_TON_PROJET_inferencing.h par le nom exact du fichier d'en-tête généré par Edge Impulse lors de l'export.


05
Code ESP32 — Pont BLE vers Serial
SAMEDI SOIR · ~30 MIN

L'ESP32 scanne, se connecte au « GestureHero », et retransmet chaque geste reçu sur le port série USB.

Configuration IDE pour ESP32

  • Ajouter l'URL : https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  • Board Manager → chercher esp32 → installer
  • Sélectionner la carte ESP32 Dev Module
Arduino C++ (ESP32)
#include "BLEDevice.h"

static BLEUUID serviceUUID("19B10000-E8F2-537E-4F6C-D104768A1214");
static BLEUUID charUUID   ("19B10001-E8F2-537E-4F6C-D104768A1214");

BLEClient* pClient = nullptr;
BLERemoteCharacteristic* pChar = nullptr;
BLEAdvertisedDevice* myDevice = nullptr;
bool doConnect = false, connected = false;

// Callback : notification BLE reçue → envoyer au PC
void notifyCallback(BLERemoteCharacteristic* pCh,
                    uint8_t* data, size_t length, bool isNotify) {
  String gesture = "";
  for (int i = 0; i < (int)length; i++) gesture += (char)data[i];
  Serial.println(gesture); // ← Le PC Python lit cette ligne
}

// Callback : scan BLE — trouver "GestureHero"
class MyCallbacks : public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice adv) {
    if (adv.getName() == "GestureHero") {
      BLEDevice::getScan()->stop();
      myDevice = new BLEAdvertisedDevice(adv);
      doConnect = true;
    }
  }
};

void connectToServer() {
  pClient = BLEDevice::createClient();
  pClient->connect(myDevice);
  auto pSvc = pClient->getService(serviceUUID);
  if (!pSvc) { pClient->disconnect(); return; }
  pChar = pSvc->getCharacteristic(charUUID);
  if (pChar && pChar->canNotify())
    pChar->registerForNotify(notifyCallback);
  connected = true;
}

void setup() {
  Serial.begin(115200);
  BLEDevice::init("");
  auto pScan = BLEDevice::getScan();
  pScan->setAdvertisedDeviceCallbacks(new MyCallbacks());
  pScan->setActiveScan(true);
  pScan->start(30, false);
}

void loop() {
  if (doConnect && !connected) connectToServer();
  if (connected && !pClient->isConnected()) {
    connected = false; doConnect = false;
    BLEDevice::getScan()->start(30, false);
  }
  delay(100);
}

06
Script Python — Simulation du clavier
SAMEDI SOIR · ~20 MIN

Le dernier maillon : lit le port série, identifie le geste, et simule l'appui clavier via pynput.

Python 3
import serial
import time
from pynput.keyboard import Key, Controller

# ── Configuration ──
SERIAL_PORT = '/dev/ttyUSB0'  # Windows: 'COM3'
BAUD_RATE   = 115200
KEY_PRESS   = 0.15  # secondes

# ── Mapping geste → touche ──
KEY_MAP = {
    'haut':    Key.up,
    'bas':     Key.down,
    'gauche':  Key.left,
    'droite':  Key.right,
}

keyboard = Controller()
print(f"[GestureHero] Connexion sur {SERIAL_PORT}...")

try:
    port = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
    print("[GestureHero] Connecté. En attente de gestes...")

    while True:
        try:
            raw = port.readline()
            if not raw: continue
            gesture = raw.decode('utf-8').strip().lower()
            if not gesture: continue

            if gesture in KEY_MAP:
                key = KEY_MAP[gesture]
                print(f"[GestureHero] {gesture:8s} → touche simulée")
                keyboard.press(key)
                time.sleep(KEY_PRESS)
                keyboard.release(key)
        except UnicodeDecodeError:
            pass

except serial.SerialException as e:
    print(f"[ERREUR] Port série : {e}")
🔍 Trouver le port série sur ton OS
  • Linux : ls /dev/ttyUSB* ou ls /dev/ttyACM*
  • macOS : ls /dev/cu.usb*
  • Windows : Gestionnaire de périphériques → Ports (COM & LPT)

07
Intégration et tests complets
DIMANCHE MATIN · ~45 MIN

🚀 Procédure de démarrage

  1. Alimenter l'Arduino Nano 33 BLE Sense avec la powerbank
  2. Attendre l'advertising BLE (LED bleue)
  3. Brancher l'ESP32 au PC via câble USB
  4. L'ESP32 se connecte automatiquement (~5–10 sec)
  5. Lancer : python3 gesture_hero.py
  6. Ouvrir Beach Buggy Racing et lancer une course
  7. Tester chaque geste un par un 🖐️

📋 Checklist de tests

  • Geste HAUT → Voiture accélère immédiatement
  • Geste BAS → Voiture freine / recule
  • Geste GAUCHE → Voiture tourne à gauche
  • Geste DROITE → Voiture tourne à droite
  • Main au repos (idle) → Aucune action
  • Stabilité BLE 5 min → Connexion maintenue
  • Latence perçue → Réaction < 200 ms

🔧 Problèmes courants et solutions

ProblèmeCauseSolution
ESP32 ne trouve pas l'ArduinoPas encore en advertisingAttendre 10–15 sec ou redémarrer
Faux positifs fréquentsSeuil trop basMonter à 0.80 ou plus de données idle
Gestes non reconnusDonnées insuffisantesAjouter 20 samples par classe
Latence trop hauteWindow size trop grandeRéduire à 750 ms
Port série introuvableDriver manquantdmesg | tail sur Linux
🏁 🏎️ 💨 🏆 🎮

🔥 Défis & Difficultés rencontrées

Comme toute équipe de débutants en électronique embarquée assistée par l'IA, nous avons rencontré de nombreux obstacles. Voici notre parcours de combattants — et comment nous avons (parfois) survécu.

🔌 Connexion de l'Arduino Nano 33 BLE
La carte n'était pas immédiatement reconnue par nos ordinateurs. Problèmes de drivers, de câbles USB défectueux, et de versions de l'IDE Arduino incompatibles. Nous avons passé un temps considérable à simplement faire reconnaître la carte.
Bloquant
⚙️ Configuration du BLE
Configurer le Bluetooth Low Energy entre l'Arduino et l'ESP32 a été un vrai casse-tête. Les UUIDs devaient correspondre exactement, le pairing ne fonctionnait pas toujours, et la portée BLE était parfois instable dans l'environnement du hackathon.
Bloquant
🎮 Installation du jeu Beach Buggy Racing
Des soucis liés à nos ordinateurs (espace disque, compatibilité GPU, permissions) nous ont empêchés de rapidement installer et tester le jeu. Cela a retardé l'intégration finale du système.
Bloquant
🧠 Prise en main d'Edge Impulse
Première expérience avec le TinyML et Edge Impulse. Comprendre le pipeline (collecte → traitement → entraînement → déploiement) a nécessité beaucoup de tâtonnements et d'aide de l'IA.
Apprentissage
💪 La persévérance paie
Malgré toutes ces difficultés, nous n'avons jamais abandonné. Chaque problème résolu nous a rapprochés de la compréhension globale du système. C'est la vraie victoire de ce hackathon : avoir appris en faisant.
Leçon apprise
💡 Ce que nous ferions différemment
  • Vérifier tout le matériel et les installations avant le hackathon
  • Avoir des câbles USB de rechange
  • Tester la chaîne BLE en isolation avant l'intégration complète
  • Documenter chaque erreur rencontrée en temps réel

📅 Résumé du week-end

PériodeÉtapeDuréeLivrable
Samedi matin01 — Installation environnement~30 minOutils installés, compte Edge Impulse créé
Samedi matin02 — Collecte des données~45 min300+ samples collectés (5 classes)
Samedi après-midi03 — Entraînement modèle~30 minModèle précision > 85 %
Samedi après-midi04 — Code Arduino~45 minArduino envoie les gestes en BLE
Samedi soir05 — Code ESP32~30 minESP32 relaie BLE → PC
Samedi soir06 — Script Python~20 minScript simule les touches
Dimanche matin07 — Intégration & tests~45 minSystème complet fonctionnel
Dimanche après-midi08 — Doc & présentation~30 minGitHub publié, démo prête

👥 Les Motion Masters

Notre équipe de Gesture Heroes — des débutants passionnés qui ont relevé le défi de l'IA embarquée lors de l'Arduino Days 2026 à Cotonou 🇧🇯

👨‍💻
Gilchrist V.
Dev Fullstack · Arduino Lead
👩‍💻
Sabalimatou K.
Edge Impulse · Data Collection
🧑‍💻
David K.
Edge Impulse · Arduino Lead
👨‍🔬
Exaucé A.
Test · Documentation
🧑‍🔧
Mondoukpè A.
Hardware · Intégration
👨‍🎓
Adebayo E.
Gesture Design · Testing
🌴 🌊 🏖️ 🌅 🌴