Machine Learning et Boxe Connectée : Un projet du Lab ESENS
Objectifs
L'objectif de ce nouveau projet développé au sein du LAB ESENS était d'arriver à détecter et classifier les mouvements de gants de boxe en temps réel en utilisant simplement l'information d'un accéléromètre ainsi qu'un modèle de Machine Learning embarqué sur une carte électronique, la solution devant différencier un coup porté à une cible et d'un autre donné dans le vide.
Réflexion
Chez ESENS, les objets connectés ont toujours suscité notre plus grand intérêt. Et c'est justement par le projet des gants de boxe que le LAB a été initié.
Ce premier projet avait pour principaux objectifs la détection et la comptabilisation des coups portés au moyen de gants de boxe connectés dans une application web.
Le challenge de cette première version était donc la détection des coups. Nous avions réussi à l'implémenter en utilisant des capteurs de pression attachés aux gants tout en réfléchissant déjà à la façon de faire évoluer cette méthode vers une détection basée sur les données d'un accéléromètre ou d'un gyroscope, enlevant ainsi la problématique d'attacher un capteur de pression et de modifier les gants, ce qui complexifiait fortement l'industrialisation du produit.
Le problème de la détection d'un coup est en soi un problème de détection de gestes humains. Pour caractériser un geste, on peut se baser sur des propriétés comme la vélocité, l’accélération ou l'orientation du geste. On peut alors dire que pour détecter un geste, il suffit de déterminer un patron dans l’évolution des ces caractéristiques, qui nous permettra de juger si un mouvement correct est effectué. Pour cela, on peut utiliser l'analyse de signaux avec des calculs permettant la comparaison des amplitudes ou fréquences pour un segment déterminé. Mais la réalisation de ces calculs peut s'avérer coûteux en termes de temps et de puissance de calcul.
Une autre technique utilisée actuellement pour détecter des corrélations dans des sources d'information sont les modèles de Machine Learning. Nous avons donc réfléchi à la possibilité d'entraîner un modèle pour apprendre à reconnaître un geste dans un segment de données d’accélération et pouvoir le consulter pour détecter un coup porté.
Pour livrer cette solution, deux options s'offraient à nous en matière de hardware.
Le première était d'utiliser le même système que pour la première version de gants : un Arduino MKR1000 avec un accéléromètre. Avec cette disposition, il est possible d'envoyer les données au serveur via WiFi pour entraîner et consulter le modèle.
Mais l'idée de reprendre le sujet de la détection des coups via Machine Learning nous est ensuite venue en résultat de notre veille tech sur les cartes électroniques.
Nous avons alors identifié une nouvelle carte, la Sparkfun Edge, qui, avec l'aide de la librairie Tensorflow Lite, permet de stocker et d'utiliser un modèle basé sur les réseaux de neurones.
La carte et la libraire font partie d'une nouvelle tendance, appelée TinyML, qui cherche à donner plus d'intelligence aux capteurs en permettant de faire tourner et de faire de l’inférence de modèles de Machine Learning sur des microcontrôleurs, le tout avec de basses consommations.
Cette technologie est déjà utilisée dans le domaine professionnel de l'IoT, dans des applications qui nécessitent la détection de patrons dans des signaux (classification des sons, des images, mouvements, etc...). Elle permet de ne pas constamment envoyer des données au Cloud, ce qui diminue la bande passante, aide à la prise de décisions, augmente la sécurité et la vie de la batterie. C'est la raison pour laquelle ces dispositifs peuvent fonctionner des années avec une seule batterie.
Solution livrée
Au travers de nos recherches, nous avons pu déterminer que les type de réseaux de neurones les plus utilisés pour le type d'analyse que nous nécessitions étaient celles du Deep Learning.
Et pour construire un modèle de ce type, Tensorflow propose un flow (un modèle), qu'on va pouvoir adapter à notre projet.
1. Objectif
La solution doit différencier un coup porté à une cible d'un autre donné dans le vide.
2. Collecter le dataset
La solution livrée se base sur la carte Sparkfun Edge. Le modèle va être entraîné avec les données d'accélération des trois axes (hauteur, largeur et profondeur), pour 20 coups portés à une cible (pâte d'ours) et 20 coups dans le vide. On récupère les informations de l'accéléromètre depuis la carte dans un ordinateur via le port série.
2.1 Installation et programmation de la carte
La carte contient un accéléromètre de 3 axes qui peut aller jusqu'à 16G, un processeur ARM Cortex-M4F 48MHz (avec 96MHz burst mode), un ADC de 15 canaux avec 14 bit, 1MB de mémoire flash et 384KB de SRAM et Bluetooth Low Energy. Elle peut fonctionner avec une batterie de type a CR2032 jusqu'à 10 jours et pour programmer on a besoin d'un convertisseur USB-C à serial.
Pour installer le SDK de la carte on peut suivre les instructions du fabricant ou ce codelab de Google.
Pour débuter et prouver le concept, nous avons utilisé un exemple de détection du mot 'yes' dans un enregistrement audio fourni dans le repository de Tensorflow :
Comme le déploiement de notre modèle nécessitera pycripto et pyserial, commençons par les installer :
pip3 install pycrypto pyserial --user
Nous pouvons maintenant récupérer l'exemple, le builder et générer le binaire :
git clone --depth 1 https://github.com/tensorflow/tensorflow.git
cd tensorflow
make -f tensorflow/lite/experimental/micro/tools/make/Makefile TARGET=sparkfun_edge micro_speech_bin
python3 tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/create_cust_wireupdate_blob.py \
--load-address 0x20000 \
--bin main_nonsecure_ota.bin \
-i 6 \
-o main_nonsecure_wire \
--options 0x1
Nous avons désormais un fichier main_nonsecure_wire.bin qu'on va vouloir déposer sur notre carte.
Mais, pour qu'il puisse être déployé sur le microcontrôleur, nous devons signer le binaire avec les clés fournies par Tensorflow :
cp tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/keys_info0.py \
tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/keys_info.py
python3 tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/create_cust_image_blob.py \
--bin tensorflow/lite/experimental/micro/tools/make/gen/sparkfun_edge_cortex-m4/bin/micro_speech.bin \
--load-address 0xC000\
--magic-num 0xCB\
-o main_nonsecure_ota \
--version 0x0
Pour ne pas avoir d'erreur au moment de flasher notre microcontrôleur, il est important de télécharger et d'installer les drivers pour le connecteur USB-C vers serial depuis Sparkfun.
make clean
make
sudo make load
sudo rmmod ch341
Une fois chose faite, nous pouvons à présent flasher binaire sur notre carte, en appuyant sur le bouton 14 et sur reset, puis en lançant les commandes suivantes, sans relâcher le bouton 14 :
export DEVICENAME=/dev/ttyUSB0
export BAUD_RATE=921600
python3 tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/uart_wired_update.py ${DEVICENAME} \
-b ${BAUD_RATE} \
-r 1 \
-f main_nonsecure_wire.bin \
-i 6
Une fois déployé, notre micro-contrôleur est désormais capable de détecter quand quelqu'un dit 'yes' ou 'no' (avec l'accent approprié), ce qui déclenchera l'allumage de la LED jaune pour 'yes' et de la LED rouge pour 'no'. Si le mot détecté n'est pas connu, ce sera la LED verte qui s'allumera et si aucun mot n'est dit, la LED bleu continuera de clignoter.
On peut à présent passer à la production de notre propre code.
2.2 Récupération des données de l’accéléromètre
Maintenant que nous avons prouvé que nous pouvions déployer un modèle de Machine Learning sur notre carte, nous pouvons développer notre propre bout de code qui va lire l’accéléromètre et les envoyer via le port série. Cela nous permettra d'entraîner notre modèle afin qu'il détecte plus tard les coups à la volée.
Commençons par créer le dossier de notre application:
tensorflow/tensorflow/lite/experimental/micro/examples/box_gesture
Dans la méthode checkForGesture() du fichier main_functions.cc on va inclure le code qui va réagir à un changement considérable dans les courbes de valeurs absolues des données d'accélération des trois axes. Afin de limiter le volume de données à traiter et d'éliminer les mesures non pertinentes, nous avons configuré la fréquence de lecture de l’accéléromètre à 1kHz et fixé le nombre d’échantillons envoyés pour l'entrainement à 150 mesures autour de la détection de notre "changement considérable".
Figure 1. Graphique d'accélération pour un geste de coup sans impact
Une fois reçues et afin de pouvoir être traitées, les données doivent être normalisés et en format csv avec les headers aX, aY, aZ, cela avec l'aide d'un print formaté sur le terminal Série.
Dans notre cas, nous avons pris le choix d'effectuer 20 coups portés sur la cible, et 20 coups dans le vide. Cela fait, nous récupérons ces informations depuis le terminal Série de notre IDE Arduino, qu'on copie ensuite dans des fichiers punch.csv et flex.csv.
Pour chaque coup donné, nous avons donc un échantillon de 150 groupes de mesures des axes abscisse, ordonnée et cote :
3. Génération du modèle
Pour générer notre modèle, nous nous sommes inspirés d'un colab de Google, dans lequel ils ont expliquent la procédure complète de génération, du format des données a remonter pour l'entraînement, a la génération du fichier du modèle qui pourra être utilisé par notre microcontrôleur Sparkfun, en passant par la création du réseau neuronal avec Tensorflow et Keras.
Le réseau neuronal que nous utilisons pour notre modèle est d type 1D CNN (Convolutional Neural Network d'une dimension) , qui est très effectif pour la détection de caractéristiques au sein de courts segments de mesures et pour lesquels la localisation des caractéristiques au sein du segment n'ont que peu d'importance. Cela s'applique donc parfaitement à l'analyse de séquences temporelles des données d'un capteur (notre cas), ou encore à l'analyse de signaux sur une période de temps fixe (comme pour l'analyse de signals audio).
Cette version es une variation des 2D CNN qui sont largement utilisés pour la classification d'images.
3.1 Préparation des données
Les données d’accélération normalisées des fichiers punch.csv et flex.csv contiennent chacun 20 enregistrements d'un des gestes :
Figure 2. Graphique d'accélération pour 20 coups avec impact
Figure 3. Graphique d'accélération pour 20 coups sans impact
Maintenant que nous avons nos donnée, nous devons les adapter pour les utiliser avec les librairies qui vont générer le réseau de neurones. Dans notre cas, nous allons générer une matrice de 40 groupes de 450 mesures, c'est à dire autant de groupes que de coups mesurés, avec les 150 données de mesure des 3 axes associées.
Exemple de données en entrée :
Une fois cela fait, nous créons un nouveau tableau de 40 entrées, qui permettra à notre modèle ML de déterminer si les données envoyées correspondent à un coup porté ou non. Au premier index, 1 correspondra à un coup porté, et au deuxième, 1 correspondra à un coup dans le vide.
Parmi ces éléments, nous allons en sélectionner 60% pour entraîner notre modèle. 20% des données restantes seront utilisées pour valider l'approche la plus performante (données de validation), et les derniers 20% vont permettre d'estimer la précision de nos résultats (données de test).
3.1 Création et entrainement du modèle
Le CNN donné par Google pour l'exemple, contient 3 couches de réseaux de neurones : la première de 50 neurones, la deuxième de 15 , et la troisième qui est en charge d'identifier le geste contient deux neurones, un pour chaque type de geste.
model = tf.kera.Sequential()
model.add(tf.keras.layers.Dense(50, activation='relu')) # relu est utilisé pour optimiser les performances
model.add(tf.keras.layers.Dense(15, activation='relu'))
model.add(tf.keras.layers.Dense(2, activation='softmax')) # softmax est utilisé pour associer un poids à chaque type de geste
Une fois le modèle créé, on peut l'entraîner :
Pour chaque époque, ou itération d'entraînement, on peut alors observer la diminution de la perte, un indicateur de médiocrité de la prévision de notre modèle (aussi appelé la plongée), ainsi que le niveau d'incertitude du modèle et le MAE (Mean Absolute Error) pour l'entraînement et la validation.
Voici par exemple l'évolution du MAE lors de l'entraînement de notre modèle :
3.2. Conversion du model
Maintenant que le modèle est entraîné et validé, on peut, à l'aide de Tensorflow Lite, générer le fichier model_data.h (fichier header C++) à utiliser qui contiendra un tableau, qui pourra être consulté dans notre carte Sparkfun Edge avec l'aide de Tensorflow Lite afin d'identifier si un coup est porté ou dans le vide :
3.3. Faire l’inférence
Intégrons donc ce modèle dans le code qui sera déployé sur notre microcontrôleur
Et consultons le au moment de la détection d'un changement significatif dans nos mesures :
La Démo en Vidéo
------------------------------
Article rédigé par Andrés, Lead Dev IoT chez ESENS | Retrouvez tous nos articles sur le Blog ESENS
Vous êtes à la recherche d'un nouveau challenge ? Rejoignez l'équipe ESENS en postulant à nos offres d'emploi !