SE5 ECEAI/eceai 2024/2025/wijsman-lefranc
Git du projet:
https://gitlab.univ-lille.fr/louis.wijsman.etu/projet-ia-wijsman-lefranc
Séance 1 (09 Sept 2024):
Expérience :
L'objectif de l'expérience est de différencier des essences de bois à partir du spectre d'absorption et du temps de propagation d'un choc dans le bois.
Maquette :
La maquette est constituée de deux capteurs piézoélectriques fixés à une distance \( L \) l'un de l'autre. Une petite masse guidée permet de générer des chocs avec une amplitude variable.
Expérience :
L'expérimentation se fera en faisant varier plusieurs paramètres :
- Amplitude des chocs
- Forme de la planche de bois
- Épaisseur de la planche de bois
Cela permettra de discriminer l'essence du bois.
Séance 2
- Configuration du Raspberry Pi en Wi-Fi pour permettre la liaison SSH.
Séance 3 (23 Septembre 2024)
- Le Raspberry Pi peut maintenant communiquer en TCP avec notre serveur VM sur Capbreton.
- Création du code pour l'acquisition des données via le microcontrôleur.
Communication entre le Raspberry Pi et le serveur (SE5-WIJSMAN-LEFRANC-IA)
VM sur Capbreton : SE5-WIJSMAN-LEFRANC-IA ('172.26.145.111').
Contient un script Python ~/tcp_server/tcp_server.py permettant de récupérer des données depuis le Raspberry Pi.
import socket
# Set up the server IP and port
SERVER_IP = '0.0.0.0' # Listens on all interfaces
SERVER_PORT = 12345
# Create a TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the server IP and port
server_socket.bind((SERVER_IP, SERVER_PORT))
# Start listening for incoming connections
server_socket.listen(1)
print(f"Server listening on {SERVER_IP}:{SERVER_PORT}")
while True:
# Accept a connection
client_socket, client_address = server_socket.accept()
print(f"Connection from {client_address}")
# Receive the message from the client
message = client_socket.recv(1024).decode('utf-8')
print(f"Received message: {message}")
# Optionally, send a response back to the client
client_socket.send("Message received".encode('utf-8'))
# Close the connection
client_socket.close()
Raspbery pi:
Contient un script Python tcp_com/send_data.py qui permet d'envoyer des donees vers la VM
import socket
import json
def send_dict_to_server(server_ip, server_port, data_dict):
try:
# Serialize the dictionary to a JSON string
json_data = json.dumps(data_dict)
# Create a TCP socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect to the server
client_socket.connect((server_ip, server_port))
print(f"Connected to server {server_ip}:{server_port}")
# Send the JSON data to the server
client_socket.send(json_data.encode('utf-8'))
print(f"Sent: {json_data}")
# Receive and print the server's response
response = client_socket.recv(1024).decode('utf-8')
print(f"Server response: {response}")
except Exception as e:
print(f"Error: {e}")
finally:
# Close the connection
client_socket.close()
print("Connection closed.")
if __name__ == "__main__":
# Example dictionary to send
data = {
"name": "Raspberry Pi",
"type": "client",
"message": "Hello from the Pi!",
"value": 3
}
# Call the function to send the dictionary to the server
SERVER_IP = '172.26.145.111' # VM's IP (SE5-WIJSMAN-LEFRANC-IA)
SERVER_PORT = 12345 # The port the server is listening on
send_dict_to_server(SERVER_IP, SERVER_PORT, data)
Système de Mesure
Pour effectuer les mesures, nous avons réalisé le montage suivant :
- Un système de percussion par chute d'une masse le long d'un axe gradué.
Nous utilisons une graduation, car l'objectif est de réaliser des mesures variées en modifiant la force de l'impact (via la hauteur de chute).
En effet, si la hauteur de chute était constante, le modèle de machine learning risquerait d'apprendre uniquement à détecter les différences en fonction de l'intensité de l'impact. Cela le rendrait inutile, car notre objectif n'est pas de discriminer sur la base de la force d'impact, mais de différencier deux matériaux différents indépendamment de cette variable.
- Un système de mesure avec des capteurs piézoélectriques.
Le système de mesure est équipé de plusieurs capteurs piézoélectriques, ce qui nous offre une plus grande flexibilité lors de l'entraînement du modèle.
Système de percussion par chute d'une masse le long d'un axe gradué. | Système de mesure avec des capteurs piézoélectriques |
---|---|
Machine Learning
Tout d'abord, nous récupérons les données d'enregistrement et les convertissons en spectrogrammes, qui sont ensuite mis au format pickle selon la structure suivante :
data = {
"bin_centers": bin_centers, # numpy array de taille N
"bin_averages": bin_averages, # numpy array de taille N représentant l'intensité pour chaque gamme de fréquence.
"type": type_of_wood # booléen indiquant le type de bois (A ou B)
}
On peut observer la présence de l'attribut type
. Il s'agit d'un **TAG** utilisé pour entraîner et vérifier le modèle. Ce tag fait référence au type de bois utilisé.
A) Implémentation du modele de Machine Learning
Nous comptons entraîner le modèle sur des spectres fréquentiels obtenus à partir des enregistrements.
En revanche, dans un premier temps, nous n’avons pas encore de données.
Pour pouvoir déjà implémenter et tester le modèle, nous utilisons un script qui génère de fausses données.
Pour générer les données, nous créons deux sinusoïdes à des fréquences différentes, ajoutons du bruit et calculons la FFT.
En sortie, nous obtenons une FFT sur 50 échantillons.
Nous combinons ce tableau NumPy avec un label binaire "type" (True ou False) selon qu'il s'agit d'un type A ou B.
L’ensemble est ensuite sauvegardé sous forme de fichiers pickle, en veillant à ce que 80 % des données soient placées dans train/
et 20 % dans validation/
.
Au total, il y a 2 000 données.
Entraînement du modèle :
Pour entraîner le modèle, nous utilisons les données issues de train/.
Le dataloader du modèle est capable de lire les fichiers pickle et de repérer le label "type" (True ou False) afin de superviser l'entraînement du modèle.
Nous effectuons un entraînement de 500 époques et observons que la perte diminue et converge vers 0.
Voici un graphique représentant la perte au fil de l'entraînement :
Une fois le modèle entraîné, nous sauvegardons son état dans un fichier.
Validation du modèle :
Pour la validation, il suffit de charger le modèle à partir du fichier.
Ensuite, nous récupérons les données dans validation/
.
Le script itère sur chacune des données, effectue un passage avant (forward pass) du modèle et compare la sortie obtenue avec la sortie attendue contenue dans le fichier pickle.
Cela permet d'obtenir des statistiques de performance.
Résultats :
Dans le cadre des données artificielles, sans surprise, les résultats sont bons.
En effet, nous obtenons :
Résultats de l'évaluation :
Accuracy : 0.9818
Précision : 0.9649
Rappel (Recall) : 1.0000
F1 Score : 0.9821
Matrice de confusion :
[212 8]
[0 220]
On a une précision de 98%.
La matrice de confusion montre qu’il n’y a que 8 faux positifs et 0 faux négatifs sur 400 données de validation !