« RodenburgThomas » : différence entre les versions

De wiki-se.plil.fr
Aller à la navigation Aller à la recherche
m (Drodenbu a déplacé la page Gabidann vers RodenburgThomas)
 
(56 versions intermédiaires par 2 utilisateurs non affichées)
Ligne 4 : Ligne 4 :
* Dann RODENBURG
* Dann RODENBURG
* Gabriel THOMAS
* Gabriel THOMAS
=Objectifs=


=Compte rendu des séances=
Le but de ce TP est de créer un réseau de systèmes communicants en partant d'un capteur XNucléo 53L5A1 pour finir sur une machine virtuelle Xen.
La particularité est d'intégrer une composante de Machine Learning.


==Séance du 04/12/2023==
[[Fichier:Capture d'écran 2023-12-19 151237.png|néant|vignette|800x800px]]
===Création de la machine virtuelle===
 
 
A travers ces heures de projet, nous avons développé un modèle permettant de reconnaître de visage des deux membres du groupes, grâce à l'utilisation d'une IA spécialement entraînée pour détecter nos visages.
 
=Edge Computing and Embedded AI=
 
===Machine virtuelle===
====Création====
Création d'une machine virtuelle '''gabidann''' sur le serveur chassiron :
Création d'une machine virtuelle '''gabidann''' sur le serveur chassiron :


Ligne 17 : Ligne 26 :
Après cela, nous avons configuré la machine virtuelle selon l'énoncé du TP en faisant un xen console gabidann. Nous avons ensuite participé à la mise en place du point d'accès WiFi.
Après cela, nous avons configuré la machine virtuelle selon l'énoncé du TP en faisant un xen console gabidann. Nous avons ensuite participé à la mise en place du point d'accès WiFi.


===Premières observations du capteur===
====Serveur TCP====
De plus nous avons observé le retour du capteur de distance Nucleo-53L5A1 avec l'outil minicom. Le contenu envoyé par le capteur était compréhensible avec une fréquence de 460 800 bauds ; sur minicom, on a pu constater que le capteur nous envoie des valeurs qui dépendent de la distance entre celui-ci et un obstacle.
 
Le rôle du serveur TCP est de recevoir la connexion de la Raspberry. En effet, c'est le client sur la Raspberry qui envoie les données non traitées vers le serveur TCP. Lorsque le serveur reçoit des données, il appelle l'exécutable NanoEdgeAI_Class_Emulator pour traiter les données reçues. Dans notre cas, une fois les données traitées, le serveur les affiche dans la console et les écrit dans un fichier csv.
 
<syntaxhighlight lang="python">
import socket
import subprocess
 
def write_csv(filename, data):
if data == ",":
return # avoid to put single commas in csv file
f = open(filename, "a")
f.write(data + "\n")
f.close()
 
def process_data(data):
if data == ",":
return
formatted_data = data.replace(',', ' ')
 
cmd = f'emulators/NanoEdgeAI_Class_Emulator neai_classification --knowledge_path emulators/knowledge.dat --array "{formatted_data}"'
signal = str(subprocess.check_output(cmd.split(" ")))
write_csv("signal.csv", signal)
 
if "dann" in signal:
print("dann")
elif "gaby" in signal:
print("gaby")
else:
print("Unknown face")
 
def handle_connection(conn, filename):
while True:
try:
msg = conn.recv(1024)
msg = msg.decode('utf-8')
except KeyboardInterrupt:
exit()
if not msg:
print("Cannot receive message from Raspberry client !")
break
else:
process_data(msg)
print("Closing Raspberry client connection...")
conn.close()
 
filename = 'data.csv'
server_ipv6 = '2001:660:4401:6050:216:3eff:fe98:11ff'
port = 9999
 
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.bind((server_ipv6, port))
s.listen(1)
print("Listening on port " + str(port))
 
while True:
try:
conn,addr = s.accept()
except KeyboardInterrupt:
exit()
with conn:
print(f"New connection from {addr[0]}")
handle_connection(conn, filename)
 
</syntaxhighlight>
 
====Serveur HTTP====
Configuration d'un serveur HTTP sur la machine virtuelle gabidann :<syntaxhighlight lang="python3">
import socket
from http.server import HTTPServer, SimpleHTTPRequestHandler
 
class MyHandler(SimpleHTTPRequestHandler):
  def do_GET(self):
    if self.path == '/ip':
      self.send_response(200)
      self.send_header('Content-type', 'text/html')
      self.end_headers()
      self.wfile.write(f'Your IP address is {self.client_address[0]}'.encode())
      return
    else:
      return SimpleHTTPRequestHandler.do_GET(self)
 
class HTTPServerV6(HTTPServer):
  address_family = socket.AF_INET6
 
def main():
  server = HTTPServerV6(('::', 8080), MyHandler)
  server.serve_forever()
 
if __name__ == '__main__':
  main()
 
</syntaxhighlight>
 
Pour se connecter au serveur depuis un navigateur, on peut ouvrir un navigateur depuis la zabeth et y accéder avec l'adresse : ''[<ipv6_VM>]:<port>''. Dans notre cas, le serveur écoute le port '''8080'''.
 
La page index.html s'affiche par défaut lorsque l'on se connecte au serveur HTTP :
<syntaxhighlight lang="html">
<!DOCTYPE html>
 
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Gabidann</title>
</head>
 
<body>
<h1>Bienvenue sur Gabidann !<h1>
<div id="data"></div>
</body>
 
</html>
</syntaxhighlight>Nous avons également testé la communication en TCP avec l'utilitaire socat


===Raspberry===
===Raspberry===
D'un autre côté, nous avons branché la Raspberry Pi à l'ordinateur selon le pinout fourni dans la documentation.
D'un autre côté, nous avons branché la Raspberry Pi à l'ordinateur selon le pinout fourni dans la documentation.
[[Fichier:20231218 085218.jpg|vignette|néant]]


Enfin, nous avons activé la communication UART de la Raspberry Pi afin de pouvoir communiquer avec. En fin de séance, nous étions en train de vérifier que la Raspberry Pi répondait bien, même en ayant ajouté la ligne enable_uart=1 dans le fichier config.txt de la Raspberry.
Enfin, nous avons activé la communication UART de la Raspberry Pi afin de pouvoir communiquer avec. En fin de séance, nous étions en train de vérifier que la Raspberry Pi répondait bien, même en ayant ajouté la ligne enable_uart=1 dans le fichier config.txt de la Raspberry.


==Séance du 18/12/2023==
====Configuration de la Raspberry====
===Configuration de la Raspberry===
Nous avons résolu le problème de la Raspberry qui ne répondait pas. Cela était dû à l'alimentation : brancher la carte en USBC résoud le problème. Cependant, nous recevons une erreur l'erreur EXT4-fs error (device mmcblk0p2).
Nous avons résolu le problème de la Raspberry qui ne répondait pas. Cela était dû à l'alimentation : brancher la carte en USBC résoud le problème. Cependant, nous recevons une erreur l'erreur EXT4-fs error (device mmcblk0p2).
Le problème venait des arguments saisis lors du lancement de Minicom. Avec la commande suivante, il est à présent possible de se connecter sur la Raspberry : <code>minicom -D /dev/ttyUSB0 -b 115200</code>
Le problème venait des arguments saisis lors du lancement de Minicom. Avec la commande suivante, il est à présent possible de se connecter sur la Raspberry : <code>minicom -D /dev/ttyUSB0 -b 115200</code>
Ligne 34 : Ligne 156 :
     wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
     wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
A présent à partir de la Raspberry, on peut ping la machine virtuelle sur chassiron avec son IPv6.
A présent à partir de la Raspberry, on peut ping la machine virtuelle sur chassiron avec son IPv6.
====Client TCP====
<syntaxhighlight lang="python">
import serial
import socket
server_ipv6 = '2001:660:4401:6050:216:3eff:fe98:11ff'
server_port = 9999
nucleo_baudrate = 460800
nucleo_port = '/dev/ttyACM0'
def read_serial(ser):
        try:
                serial_data = ser.readline().decode('utf-8').strip()
                return serial_data
        except KeyboardInterrupt:
                ser.close()
                return None
client = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
ser = serial.Serial(port=nucleo_port, baudrate=nucleo_baudrate)
</syntaxhighlight>


===Configuration du capteur de distance===
===Configuration du capteur de distance===
Un programme était déjà présent sur le capteur. On a téléchargé le logiciel STM32CubeIDE pour pouvoir éditer du code et le téléverser sur le capteur. Nous avons testé de téléverser un programme d'exemple dont voici le résultat :
Un programme était déjà présent sur le capteur. On a téléchargé le logiciel STM32CubeIDE pour pouvoir éditer du code et le téléverser sur le capteur. Nous avons testé de téléverser un programme d'exemple dont voici le résultat :
[[Fichier:Screenshot 2023-12-18 13-12-19.jpg]]
[[Fichier:Screenshot 2023-12-18 13-12-19.jpg|vignette|néant|700x700px]]
 
Nous avons modifié ce programme afin d'effectuer un formatage des données transmises par le capteur sous le format ''<distance>,'' pour chaque valeur. Grâce à ce formatage, il est à présent possible d'utiliser les données reçues par le logiciel NanoEdgeStudio AI pour la partie Machine Learning.
 
 
Pour recevoir les valeurs de manière exploitable, il faut modifier le code Exemple dans le logiciel STM Cube IDE


Nous avons modifié ce programme afin d'effectuer un formatage des données transmises par le capteur sous le format ''<distance>,'' pour chaque valeur. Grâce à ce formatage, il est à présent possible d'utiliser les données reçues par le logiciel NanoEdgeStudio IA pour la partie Machine Learning.
[[Fichier:Capture d'écran 2023-12-20 084719.png|néant|vignette|800x800px]]


Pour recevoir les valeurs de manière exploitable, il faut modifier le code Exemple pour obtenir une liste séparée par des virgules, il a donc fallu supprimer les espaces et les barres verticales :<syntaxhighlight lang="c">
L'objectif est d'obtenir une liste séparée par des virgules, il a donc fallu supprimer les espaces et les barres verticales de la fonction d'affichage ainsi que les statuts de chacun des capteurs :<syntaxhighlight lang="c">
static void print_result(RANGING_SENSOR_Result_t *Result)
static void print_result(RANGING_SENSOR_Result_t *Result)
{
{
Ligne 66 : Ligne 218 :
</syntaxhighlight>
</syntaxhighlight>


===Configuration du serveur===
===Emulation avec NanoEdgeStudio AI===
Configuration d'un serveur HTTP sur la machine virtuelle gabidann :<syntaxhighlight lang="python3">
 
import socket
On passe ensuite sur Nano Edge Studio pour définir les signaux dans l'onglet "Signals" et entraîner le modèle dans l'onglet "Benchmark".
from http.server import HTTPServer, SimpleHTTPRequestHandler
 
[[Fichier:Capture d'écran 2023-12-18 172047.png|néant|vignette|800x800px]]
 
Une fois le modèle entraîné, on peut le tester avec l'onglet "Emulator"  
 
[[Fichier:Capture d'écran 2023-12-18 173255.png|néant|vignette|800x800px]]
 


class MyHandler(SimpleHTTPRequestHandler):
  def do_GET(self):
    if self.path == '/ip':
      self.send_response(200)
      self.send_header('Content-type', 'text/html')
      self.end_headers()
      self.wfile.write(f'Your IP address is {self.client_address[0]}'.encode())
      return
    else:
      return SimpleHTTPRequestHandler.do_GET(self)


class HTTPServerV6(HTTPServer):
Dans un premier temps, nous avons essayé d'entraîner le programme à reconnaître les chiffres 1, 2, 3, 4 et 5 sur les doigts d'une main. Les résultats n'étaient pas concluant, il semblerait que le capteur détecte avant tout le volume de la main à la place de détecter le nombre de doigts.
  address_family = socket.AF_INET6


def main():
Nous avons ensuite choisi de faire un détecteur de visages capable de faire la différence entre Gabriel et Dann. L'émulation donne des résultats très satisfaisant proche de 100% de réussite
  server = HTTPServerV6(('::', 8080), MyHandler)
  server.serve_forever()


if __name__ == '__main__':
  main()


On peut lui passer un fichier CSV contenant les données brutes du capteur et grâce au fichier knowledge.dat, l'émulateur est capable de faire la différence entre les situations<syntaxhighlight lang="bat">
PS C:\Users\gabyt\Documents\Gaby\emulators> .\NanoEdgeAI_Class_Emulator.exe neai_classification --knowledge_path .\knowledge.dat --file .\data.csv
{
        "status": "classification",
        "lib_id": "6582b12625c3e0368b2c2a09",
        "input": ".\data.csv",
        "results": [
                {"signal": 1, "line": 1, "class_status": 2, "class_name": "dann", "class_proba": [0.01, 0.99]},
                {"signal": 2, "line": 2, "class_status": 2, "class_name": "dann", "class_proba": [0.01, 0.99]},
                {"signal": 3, "line": 3, "class_status": 2, "class_name": "dann", "class_proba": [0.37, 0.63]},
                {"signal": 4, "line": 4, "class_status": 1, "class_name": "dann", "class_proba": [0.83, 0.17]},
                {"signal": 5, "line": 5, "class_status": 1, "class_name": "dann", "class_proba": [0.63, 0.37]},
                {"signal": 6, "line": 6, "class_status": 1, "class_name": "dann", "class_proba": [0.63, 0.37]},
                {"signal": 7, "line": 7, "class_status": 1, "class_name": "dann", "class_proba": [0.50, 0.50]},
                {"signal": 8, "line": 8, "class_status": 1, "class_name": "dann", "class_proba": [0.63, 0.37]},
                {"signal": 9, "line": 9, "class_status": 2, "class_name": "dann", "class_proba": [0.37, 0.63]},
                {"signal": 10, "line": 10, "class_status": 2, "class_name": "dann", "class_proba": [0.17, 0.83]},
                {"signal": 11, "line": 11, "class_status": 1, "class_name": "dann", "class_proba": [0.63, 0.37]},
                {"signal": 12, "line": 12, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 13, "line": 13, "class_status": 2, "class_name": "gaby", "class_proba": [0.37, 0.63]},
                {"signal": 14, "line": 14, "class_status": 1, "class_name": "gaby", "class_proba": [0.74, 0.26]},
                {"signal": 15, "line": 15, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 16, "line": 16, "class_status": 1, "class_name": "gaby", "class_proba": [0.74, 0.26]},
                {"signal": 17, "line": 17, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 18, "line": 18, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 19, "line": 19, "class_status": 2, "class_name": "dann", "class_proba": [0.37, 0.63]},
                {"signal": 20, "line": 20, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 21, "line": 21, "class_status": 2, "class_name": "dann", "class_proba": [0.11, 0.89]},
                {"signal": 22, "line": 22, "class_status": 1, "class_name": "gaby", "class_proba": [0.74, 0.26]},
                {"signal": 23, "line": 23, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 24, "line": 24, "class_status": 1, "class_name": "gaby", "class_proba": [0.74, 0.26]},
                {"signal": 25, "line": 25, "class_status": 1, "class_name": "gaby", "class_proba": [0.50, 0.50]},
                {"signal": 26, "line": 26, "class_status": 2, "class_name": "gaby", "class_proba": [0.06, 0.94]},
                {"signal": 27, "line": 27, "class_status": 2, "class_name": "gaby", "class_proba": [0.37, 0.63]},
                {"signal": 28, "line": 28, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 29, "line": 29, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]}
        ],
        "classification_summary": {"signals": 29, "classified": [13, 16], "unclassified": 0}
}
</syntaxhighlight>
</syntaxhighlight>


Pour se connecter au serveur depuis un navigateur, on peut ouvrir un navigateur depuis la zabeth et y accéder avec l'adresse : '''[<ipv6_VM>]:<port>'''. Dans notre cas, le serveur écoute le port 8080.
 
Une fois le modèle entrainé et les tests concluants, on peut déployer notre modèle sur n'importe quel appareils linux ou windows. Pour cela, on va dans l'onglet deployment et on télécharge notre librairie personnalisée.
==Séance du 19/12/2023==
 
=Utilisation=
 
Vérifier que la Raspberry est branchée à l'ordinateur et vérifier le Nucleo est bien connecté à la Raspberry et vérifier que la machine virtuelle gabidann est bien lancée.
 
Lancer dans un premier temps le serveur TCP sur la machine virtuelle gabidann. Lancer ensuite le client TCP sur la Raspberry.
 
Les résultats seront affichés dans le terminal de la machine virtuelle et enregistrés dans un fichier csv.
 
 
=Résultats=
Voici une courte vidéo de présentation de notre modèle, montrant les résultats obtenus avec nos visages.
 
[[Fichier:Vidéo de présentation du modèle.mp4|sans_cadre]]
 
On remarque que notre système reconnaît plus facilement Gabriel que Dann, il n'est pas fiable à 100%. Pour fiabiliser les résultats, la reconnaissance faciale nécessite d'être améliorée avec davantage d'entraînement.
 
Voici d'autres résultats sous la forme de capture d'écran :
 
[[Fichier:Resultatdann.png|néant|vignette|600x600px]]
 
Sur cette capture, on voit que le visage de Dann est détecté à 94% par notre modèle.
 
 
[[Fichier:Resultatgaby.png|néant|vignette|600x600px]]
 
Sur cette capture, on voit que le visage de Gabriel est détecté à 99% par notre modèle.
 
 
[[Fichier:Resultatpersonne.png|néant|vignette|600x600px]]
 
Sur cette capture, aucun visage se trouve au dessus du capteur, de ce fait la signature "Personne" est détectée à 100%.
 
 
En utilisant le serveur TCP, nous avons la possibilité d'envoyer les résultats depuis la Raspberry vers notre machine virtuelle gabidann :
 
[[Fichier:Gabidann results.jpg|néant|vignette|800px]]
 
Données reçues dans le cas où le visage de Dann se trouve au dessus du capteur.
 
 
[[Fichier:Gabidann results2.jpg|néant|vignette|800px]]
 
Données reçues dans le cas où le visage de Gabriel se trouve au dessus du capteur.

Version actuelle datée du 28 janvier 2024 à 19:02

Le binôme

Le binôme est constitué de 2 élèves de SE5-SC :

  • Dann RODENBURG
  • Gabriel THOMAS

Objectifs

Le but de ce TP est de créer un réseau de systèmes communicants en partant d'un capteur XNucléo 53L5A1 pour finir sur une machine virtuelle Xen. La particularité est d'intégrer une composante de Machine Learning.

Capture d'écran 2023-12-19 151237.png


A travers ces heures de projet, nous avons développé un modèle permettant de reconnaître de visage des deux membres du groupes, grâce à l'utilisation d'une IA spécialement entraînée pour détecter nos visages.

Edge Computing and Embedded AI

Machine virtuelle

Création

Création d'une machine virtuelle gabidann sur le serveur chassiron :

xen-create-image --hostname=gabidann --force --dist=bookworm --size=10G --memory=10G --dir=/usr/local/xen --password=glopglop --dhcp --bridge=bridgeStudents

La machine virtuelle est à présent connectée au WiFi.

Après cela, nous avons configuré la machine virtuelle selon l'énoncé du TP en faisant un xen console gabidann. Nous avons ensuite participé à la mise en place du point d'accès WiFi.

Serveur TCP

Le rôle du serveur TCP est de recevoir la connexion de la Raspberry. En effet, c'est le client sur la Raspberry qui envoie les données non traitées vers le serveur TCP. Lorsque le serveur reçoit des données, il appelle l'exécutable NanoEdgeAI_Class_Emulator pour traiter les données reçues. Dans notre cas, une fois les données traitées, le serveur les affiche dans la console et les écrit dans un fichier csv.

import socket
import subprocess

def write_csv(filename, data):
	if data == ",":
		return		# avoid to put single commas in csv file
	f = open(filename, "a")
	f.write(data + "\n")
	f.close()

def process_data(data):
	if data == ",":
		return
	
	formatted_data = data.replace(',', ' ')

	cmd = f'emulators/NanoEdgeAI_Class_Emulator neai_classification --knowledge_path emulators/knowledge.dat --array "{formatted_data}"'
	signal = str(subprocess.check_output(cmd.split(" ")))	
	write_csv("signal.csv", signal)

	if "dann" in signal:
		print("dann")
	elif "gaby" in signal:
		print("gaby")
	else:
		print("Unknown face")

def handle_connection(conn, filename):
	while True:
		try:
			msg = conn.recv(1024)
			msg = msg.decode('utf-8')
		except KeyboardInterrupt:
			exit()
		if not msg:
			print("Cannot receive message from Raspberry client !")
			break
		else:
			process_data(msg)
	print("Closing Raspberry client connection...")
	conn.close()

filename = 'data.csv'
server_ipv6 = '2001:660:4401:6050:216:3eff:fe98:11ff'
port = 9999

s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.bind((server_ipv6, port))
s.listen(1)
print("Listening on port " + str(port))

while True:
	try:
		conn,addr = s.accept()
	except KeyboardInterrupt:
		exit()
	with conn:
		print(f"New connection from {addr[0]}")
		handle_connection(conn, filename)

Serveur HTTP

Configuration d'un serveur HTTP sur la machine virtuelle gabidann :

import socket
from http.server import HTTPServer, SimpleHTTPRequestHandler

class MyHandler(SimpleHTTPRequestHandler):
  def do_GET(self):
    if self.path == '/ip':
      self.send_response(200)
      self.send_header('Content-type', 'text/html')
      self.end_headers()
      self.wfile.write(f'Your IP address is {self.client_address[0]}'.encode())
      return
    else:
      return SimpleHTTPRequestHandler.do_GET(self)

class HTTPServerV6(HTTPServer):
  address_family = socket.AF_INET6

def main():
  server = HTTPServerV6(('::', 8080), MyHandler)
  server.serve_forever()

if __name__ == '__main__':
  main()

Pour se connecter au serveur depuis un navigateur, on peut ouvrir un navigateur depuis la zabeth et y accéder avec l'adresse : [<ipv6_VM>]:<port>. Dans notre cas, le serveur écoute le port 8080.

La page index.html s'affiche par défaut lorsque l'on se connecte au serveur HTTP :

<!DOCTYPE html>

<html lang="fr">
<head>
	<meta charset="UTF-8">
	<title>Gabidann</title>
</head>

<body>
	<h1>Bienvenue sur Gabidann !<h1>
	<div id="data"></div>
</body>

</html>

Nous avons également testé la communication en TCP avec l'utilitaire socat

Raspberry

D'un autre côté, nous avons branché la Raspberry Pi à l'ordinateur selon le pinout fourni dans la documentation.

20231218 085218.jpg

Enfin, nous avons activé la communication UART de la Raspberry Pi afin de pouvoir communiquer avec. En fin de séance, nous étions en train de vérifier que la Raspberry Pi répondait bien, même en ayant ajouté la ligne enable_uart=1 dans le fichier config.txt de la Raspberry.

Configuration de la Raspberry

Nous avons résolu le problème de la Raspberry qui ne répondait pas. Cela était dû à l'alimentation : brancher la carte en USBC résoud le problème. Cependant, nous recevons une erreur l'erreur EXT4-fs error (device mmcblk0p2). Le problème venait des arguments saisis lors du lancement de Minicom. Avec la commande suivante, il est à présent possible de se connecter sur la Raspberry : minicom -D /dev/ttyUSB0 -b 115200 Dans la Raspberry, nous avons configuré une interface réseau wlan0 pour se connecter à WiFi_IE_1 :

   auto wlan0
   iface wlan0 inet dhcp
   wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

A présent à partir de la Raspberry, on peut ping la machine virtuelle sur chassiron avec son IPv6.

Client TCP

import serial
import socket

server_ipv6 = '2001:660:4401:6050:216:3eff:fe98:11ff'
server_port = 9999

nucleo_baudrate = 460800
nucleo_port = '/dev/ttyACM0'

def read_serial(ser):
        try:
                serial_data = ser.readline().decode('utf-8').strip()
                return serial_data
        except KeyboardInterrupt:
                ser.close()
                return None

client = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)

ser = serial.Serial(port=nucleo_port, baudrate=nucleo_baudrate)

Configuration du capteur de distance

Un programme était déjà présent sur le capteur. On a téléchargé le logiciel STM32CubeIDE pour pouvoir éditer du code et le téléverser sur le capteur. Nous avons testé de téléverser un programme d'exemple dont voici le résultat :

Screenshot 2023-12-18 13-12-19.jpg

Nous avons modifié ce programme afin d'effectuer un formatage des données transmises par le capteur sous le format <distance>, pour chaque valeur. Grâce à ce formatage, il est à présent possible d'utiliser les données reçues par le logiciel NanoEdgeStudio AI pour la partie Machine Learning.


Pour recevoir les valeurs de manière exploitable, il faut modifier le code Exemple dans le logiciel STM Cube IDE

Capture d'écran 2023-12-20 084719.png

L'objectif est d'obtenir une liste séparée par des virgules, il a donc fallu supprimer les espaces et les barres verticales de la fonction d'affichage ainsi que les statuts de chacun des capteurs :

static void print_result(RANGING_SENSOR_Result_t *Result)
{
  int8_t j;
  int8_t k;
  int8_t l;
  uint8_t zones_per_line;
  zones_per_line = ((Profile.RangingProfile == RS_PROFILE_8x8_AUTONOMOUS) ||
         (Profile.RangingProfile == RS_PROFILE_8x8_CONTINUOUS)) ? 8 : 4;
  for (j = 0; j < Result->NumberOfZones; j += zones_per_line)
  {
    for (l = 0; l < RANGING_SENSOR_NB_TARGET_PER_ZONE; l++)
    {
      /* Print distance and status */
      for (k = (zones_per_line - 1); k >= 0; k--)
      {
        if (Result->ZoneResult[j+k].NumberOfTargets > 0)
          printf("%ld,",(long)Result->ZoneResult[j+k].Distance[l]);
      }
    }
  }
  printf("\n");
}

Emulation avec NanoEdgeStudio AI

On passe ensuite sur Nano Edge Studio pour définir les signaux dans l'onglet "Signals" et entraîner le modèle dans l'onglet "Benchmark".

Capture d'écran 2023-12-18 172047.png

Une fois le modèle entraîné, on peut le tester avec l'onglet "Emulator"

Capture d'écran 2023-12-18 173255.png


Dans un premier temps, nous avons essayé d'entraîner le programme à reconnaître les chiffres 1, 2, 3, 4 et 5 sur les doigts d'une main. Les résultats n'étaient pas concluant, il semblerait que le capteur détecte avant tout le volume de la main à la place de détecter le nombre de doigts.

Nous avons ensuite choisi de faire un détecteur de visages capable de faire la différence entre Gabriel et Dann. L'émulation donne des résultats très satisfaisant proche de 100% de réussite


On peut lui passer un fichier CSV contenant les données brutes du capteur et grâce au fichier knowledge.dat, l'émulateur est capable de faire la différence entre les situations

PS C:\Users\gabyt\Documents\Gaby\emulators> .\NanoEdgeAI_Class_Emulator.exe neai_classification --knowledge_path .\knowledge.dat --file .\data.csv
{
        "status": "classification",
        "lib_id": "6582b12625c3e0368b2c2a09",
        "input": ".\data.csv",
        "results": [
                {"signal": 1, "line": 1, "class_status": 2, "class_name": "dann", "class_proba": [0.01, 0.99]},
                {"signal": 2, "line": 2, "class_status": 2, "class_name": "dann", "class_proba": [0.01, 0.99]},
                {"signal": 3, "line": 3, "class_status": 2, "class_name": "dann", "class_proba": [0.37, 0.63]},
                {"signal": 4, "line": 4, "class_status": 1, "class_name": "dann", "class_proba": [0.83, 0.17]},
                {"signal": 5, "line": 5, "class_status": 1, "class_name": "dann", "class_proba": [0.63, 0.37]},
                {"signal": 6, "line": 6, "class_status": 1, "class_name": "dann", "class_proba": [0.63, 0.37]},
                {"signal": 7, "line": 7, "class_status": 1, "class_name": "dann", "class_proba": [0.50, 0.50]},
                {"signal": 8, "line": 8, "class_status": 1, "class_name": "dann", "class_proba": [0.63, 0.37]},
                {"signal": 9, "line": 9, "class_status": 2, "class_name": "dann", "class_proba": [0.37, 0.63]},
                {"signal": 10, "line": 10, "class_status": 2, "class_name": "dann", "class_proba": [0.17, 0.83]},
                {"signal": 11, "line": 11, "class_status": 1, "class_name": "dann", "class_proba": [0.63, 0.37]},
                {"signal": 12, "line": 12, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 13, "line": 13, "class_status": 2, "class_name": "gaby", "class_proba": [0.37, 0.63]},
                {"signal": 14, "line": 14, "class_status": 1, "class_name": "gaby", "class_proba": [0.74, 0.26]},
                {"signal": 15, "line": 15, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 16, "line": 16, "class_status": 1, "class_name": "gaby", "class_proba": [0.74, 0.26]},
                {"signal": 17, "line": 17, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 18, "line": 18, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 19, "line": 19, "class_status": 2, "class_name": "dann", "class_proba": [0.37, 0.63]},
                {"signal": 20, "line": 20, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 21, "line": 21, "class_status": 2, "class_name": "dann", "class_proba": [0.11, 0.89]},
                {"signal": 22, "line": 22, "class_status": 1, "class_name": "gaby", "class_proba": [0.74, 0.26]},
                {"signal": 23, "line": 23, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 24, "line": 24, "class_status": 1, "class_name": "gaby", "class_proba": [0.74, 0.26]},
                {"signal": 25, "line": 25, "class_status": 1, "class_name": "gaby", "class_proba": [0.50, 0.50]},
                {"signal": 26, "line": 26, "class_status": 2, "class_name": "gaby", "class_proba": [0.06, 0.94]},
                {"signal": 27, "line": 27, "class_status": 2, "class_name": "gaby", "class_proba": [0.37, 0.63]},
                {"signal": 28, "line": 28, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]},
                {"signal": 29, "line": 29, "class_status": 1, "class_name": "gaby", "class_proba": [0.63, 0.37]}
        ],
        "classification_summary": {"signals": 29, "classified": [13, 16], "unclassified": 0}
}


Une fois le modèle entrainé et les tests concluants, on peut déployer notre modèle sur n'importe quel appareils linux ou windows. Pour cela, on va dans l'onglet deployment et on télécharge notre librairie personnalisée.

Utilisation

Vérifier que la Raspberry est branchée à l'ordinateur et vérifier le Nucleo est bien connecté à la Raspberry et vérifier que la machine virtuelle gabidann est bien lancée.

Lancer dans un premier temps le serveur TCP sur la machine virtuelle gabidann. Lancer ensuite le client TCP sur la Raspberry.

Les résultats seront affichés dans le terminal de la machine virtuelle et enregistrés dans un fichier csv.


Résultats

Voici une courte vidéo de présentation de notre modèle, montrant les résultats obtenus avec nos visages.

On remarque que notre système reconnaît plus facilement Gabriel que Dann, il n'est pas fiable à 100%. Pour fiabiliser les résultats, la reconnaissance faciale nécessite d'être améliorée avec davantage d'entraînement.

Voici d'autres résultats sous la forme de capture d'écran :

Resultatdann.png

Sur cette capture, on voit que le visage de Dann est détecté à 94% par notre modèle.


Resultatgaby.png

Sur cette capture, on voit que le visage de Gabriel est détecté à 99% par notre modèle.


Resultatpersonne.png

Sur cette capture, aucun visage se trouve au dessus du capteur, de ce fait la signature "Personne" est détectée à 100%.


En utilisant le serveur TCP, nous avons la possibilité d'envoyer les résultats depuis la Raspberry vers notre machine virtuelle gabidann :

Gabidann results.jpg

Données reçues dans le cas où le visage de Dann se trouve au dessus du capteur.


Gabidann results2.jpg

Données reçues dans le cas où le visage de Gabriel se trouve au dessus du capteur.