Acquisition • Traitement • Visualisation temps réel
Cette formation a pour objectif d’introduire les bases du développement d’interfaces logiciel–matériel avec Python. Les étudiants apprendront à communiquer avec des équipements externes, acquérir des données temps réel, traiter les informations et construire des interfaces graphiques modernes avec PyQt.
Mettre en place un environnement Python isolé et reproductible.
Anaconda, environnements virtuels, installation et exécution de scripts Python.
conda --version
conda create -n interface_tp python=3.10
conda activate interface_tp
python --version
Comprendre la communication entre logiciel et matériel via port série.
UART, pyserial, flux de données continu.
conda activate interface_tp
pip install pyserial
import serial
# Remplacer COM3 par votre port (ex: /dev/ttyUSB0 sous Linux)
ser = serial.Serial('COM3', 9600, timeout=1)
print("Connexion établie")
while True:
data = ser.readline().decode().strip()
if data:
print("Donnée reçue :", data)
Avant de se connecter à un équipement (Arduino, capteur, etc.), il est utile de vérifierles ports série disponibles sur l’ordinateur. Cela permet d’identifier le bon port(COM3, /dev/ttyUSB0, etc.).
from serial.tools import list_ports
# Liste tous les ports série disponibles
ports = list_ports.comports()
print("Ports série disponibles :")
for port in ports:
print("Port :", port.device)
print("Description :", port.description)
print("Hardware ID :", port.hwid)
print("------")💡 Cette étape est importante pour éviter les erreurs de connexion et choisir le bon portavant d’ouvrir la communication série.
Transformer des données brutes en informations exploitables.
Parsing, validation, conversion numérique.
import serial
# ===== PARTIE 1 + 2 + 3 =====
ser = serial.Serial('COM3', 9600, timeout=1)
print("Système initialisé")
while True:
data = ser.readline().decode().strip()
if data:
print("Brut :", data)
# Traitement des données
try:
value = float(data)
print("Valeur numérique :", value)
except:
print("Erreur de conversion")
Gérer un flux de données en temps réel sans blocage.
Threads, boucle continue, architecture modulaire.
import serial
import threading
# ===== PARTIE 1 + 2 + 3 + 4 =====
ser = serial.Serial('COM9', 115200, timeout=1)
def read_serial():
while True:
data = ser.readline().decode().strip()
if data:
print("\nBrut :", data)
try:
# Cas où les données contiennent 4 valeurs séparées par des espaces
values = data.split()
if len(values) == 4:
v1 = float(values[0])
v2 = float(values[1])
v3 = float(values[2])
v4 = float(values[3])
print("Valeurs :", v1, v2, v3, v4)
else:
print("Erreur: pas 4 valeurs")
except Exception as e:
print("Erreur de parsing :", e)
thread = threading.Thread(target=read_serial)
thread.start()
print("Lecture temps réel activée") conda activate interface_tp
pip install PyQt5
Créer une interface utilisateur pour contrôler et afficher les données.
Widgets, signaux/slots, communication UI-backend.
Exemple minimal utilisant une classe PyQt.
# Importer le module système
import sys
# Importer QWidget et QApplication
from PyQt5.QtWidgets import QApplication, QWidget
# Créer une classe de fenêtre
class App(QWidget):
# Constructeur de la classe
def __init__(self):
# Initialiser QWidget
super().__init__()
# Définir le titre de la fenêtre
self.setWindowTitle("Ma première fenêtre PyQt")
# Définir la taille de la fenêtre
self.resize(400, 300)
# Créer l'application Qt
app = QApplication(sys.argv)
# Créer la fenêtre
window = App()
# Afficher la fenêtre
window.show()
# Lancer la boucle principale
sys.exit(app.exec_())
import sys
import serial
import threading
from PyQt5.QtWidgets import (
QApplication, QWidget, QPushButton,
QVBoxLayout, QLabel, QLineEdit, QComboBox
)
# ===== PARTIE 1 + 2 + 3 + 4 + 5 =====
class App(QWidget):
def __init__(self):
super().__init__()
self.ser = None
# UI
self.label = QLabel("Aucune donnée")
self.port_input = QLineEdit()
self.port_input.setText("COM3")
self.port_input.setPlaceholderText("Ex: /dev/ttyUSB0")
self.baudrate_box = QComboBox()
self.baudrate_box.addItems(["9600", "19200", "38400", "57600", "115200"])
self.btn = QPushButton("Connecter")
self.btn.clicked.connect(self.start_connection)
layout = QVBoxLayout()
layout.addWidget(QLabel("Port série"))
layout.addWidget(self.port_input)
layout.addWidget(QLabel("Baudrate"))
layout.addWidget(self.baudrate_box)
layout.addWidget(self.btn)
layout.addWidget(self.label)
self.setLayout(layout)
def start_connection(self):
port = self.port_input.text()
baudrate = int(self.baudrate_box.currentText())
try:
self.ser = serial.Serial(port, baudrate, timeout=1)
self.label.setText("Connecté ✔")
threading.Thread(target=self.read_serial, daemon=True).start()
except Exception as e:
self.label.setText(f"Erreur: {e}")
def read_serial(self):
while True:
if self.ser:
data = self.ser.readline().decode().strip()
if data:
self.label.setText(data)
# Run app
app = QApplication(sys.argv)
window = App()
window.show()
sys.exit(app.exec_())
Pour apprendre PyQt en détail : Documentation officielle PyQt5
Tutoriel pratique (YouTube) : PyQt5 tutorial vidéos
conda activate interface_tp
pip install pyqtgraph pyqt5 pyserial
Visualisation des données multi-capteurs en temps réel sous forme de 4 graphiques dynamiques.
import sys
# ===== IMPORTATION DES LIBRAIRIES =====
import sys # Fonctions système (fermer proprement l'application)
import time # Gestion du temps (horodatage PC)
import serial # Communication série (Arduino, capteurs, etc.)
import threading # Exécution parallèle (évite blocage de l'interface)
import pyqtgraph as pg # Bibliothèque de graphes temps réel rapide
from PyQt5 import QtWidgets, QtCore # Interface graphique Qt (fenêtres, boutons, timer)
# ===== CLASSE PRINCIPALE DE L'APPLICATION =====
class App(QtWidgets.QWidget):
def __init__(self):
super().__init__() # Initialisation de la classe Qt
# ===== PARAMÈTRE DE FENÊTRE TEMPORELLE =====
self.window_size = 40 # Afficher uniquement les 40 dernières secondes
# ===== COMMUNICATION SÉRIE =====
self.ser = None # Objet port série (non initialisé)
self.connected = False # État de connexion
# ===== BASE DE TEMPS (PC) =====
self.t0 = time.time() # Temps de référence (début)
self.time_data = [] # Liste des temps des mesures
# ===== DONNÉES DES 4 CAPTEURS =====
self.data1 = [] # Capteur 1
self.data2 = [] # Capteur 2
self.data3 = [] # Capteur 3
self.data4 = [] # Capteur 4
# ===== INTERFACE UTILISATEUR =====
self.port_input = QtWidgets.QLineEdit() # Champ pour le port série
self.port_input.setText("COM9") # Port par défaut
self.baudrate_box = QtWidgets.QComboBox() # Liste des débits
self.baudrate_box.addItems(["9600", "19200", "38400", "57600", "115200"])
self.btn = QtWidgets.QPushButton("Connecter") # Bouton connexion
self.btn.clicked.connect(self.toggle_connection) # Action bouton
# ===== FENÊTRE DE GRAPHIQUES =====
self.win = pg.GraphicsLayoutWidget() # Conteneur de graphes
# ----- GRAPHE 1 -----
self.plot1 = self.win.addPlot(title="Capteur 1")
self.plot1.setMinimumHeight(100) # Hauteur minimale
self.curve1 = self.plot1.plot(pen='r') # Courbe rouge
self.win.nextRow()
# ----- GRAPHE 2 -----
self.plot2 = self.win.addPlot(title="Capteur 2")
self.plot2.setMinimumHeight(100)
self.curve2 = self.plot2.plot(pen='g') # Courbe verte
self.win.nextRow()
# ----- GRAPHE 3 -----
self.plot3 = self.win.addPlot(title="Capteur 3")
self.plot3.setMinimumHeight(100)
self.curve3 = self.plot3.plot(pen='b') # Courbe bleue
self.win.nextRow()
# ----- GRAPHE 4 -----
self.plot4 = self.win.addPlot(title="Capteur 4")
self.plot4.setMinimumHeight(100)
self.curve4 = self.plot4.plot(pen='y') # Courbe jaune
# ===== MISE EN PAGE =====
layout = QtWidgets.QVBoxLayout() # Disposition verticale
layout.addWidget(self.port_input) # Champ port série
layout.addWidget(self.baudrate_box) # Sélection baudrate
layout.addWidget(self.btn) # Bouton connexion
layout.addWidget(self.win) # Zone des graphiques
self.setLayout(layout) # Appliquer la mise en page
# ===== TIMER (MISE À JOUR GRAPHIQUE) =====
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.update_plot) # Appel périodique
self.timer.start(100) # 100 ms → 10 FPS
# =========================
# GESTION CONNEXION
# =========================
def toggle_connection(self):
# Alterne entre connexion et déconnexion
if not self.connected:
self.connect_device()
else:
self.disconnect_device()
def connect_device(self):
port = self.port_input.text() # Récupérer port COM
baudrate = int(self.baudrate_box.currentText()) # Vitesse communication
try:
# Ouverture du port série
self.ser = serial.Serial(port, baudrate, timeout=1)
self.connected = True
self.btn.setText("Déconnecter") # Changer texte bouton
# Lancer lecture série dans un thread (non bloquant)
threading.Thread(target=self.read_serial, daemon=True).start()
except Exception as e:
print("Erreur connexion :", e)
def disconnect_device(self):
# Arrêter lecture
self.connected = False
# Fermer port série
if self.ser:
self.ser.close()
self.btn.setText("Connecter") # Réinitialiser bouton
# =========================
# LECTURE SÉRIE (THREAD)
# =========================
def read_serial(self):
# Boucle de lecture continue
while self.connected and self.ser:
try:
line = self.ser.readline().decode().strip() # Lire ligne
if line:
values = line.split() # Séparer par espaces
# Vérifier 4 valeurs
if len(values) == 4:
# Temps écoulé depuis le début
t = time.time() - self.t0
# Stockage du temps
self.time_data.append(t)
# Stockage des 4 capteurs
self.data1.append(float(values[0]))
self.data2.append(float(values[1]))
self.data3.append(float(values[2]))
self.data4.append(float(values[3]))
except:
break # Arrêt en cas d'erreur
# =========================
# MISE À JOUR GRAPHIQUE
# =========================
def update_plot(self):
# Vérifier s'il y a des données
if len(self.time_data) == 0:
return
# Dernier temps enregistré
t_max = self.time_data[-1]
# Début de fenêtre (40 secondes)
start_time = t_max - self.window_size
# Trouver index début fenêtre
i = 0
for idx, t in enumerate(self.time_data):
if t >= start_time:
i = idx
break
# Découper uniquement les 40 dernières secondes
t = self.time_data[i:]
d1 = self.data1[i:]
d2 = self.data2[i:]
d3 = self.data3[i:]
d4 = self.data4[i:]
# Mise à jour des graphes
self.curve1.setData(t, d1)
self.curve2.setData(t, d2)
self.curve3.setData(t, d3)
self.curve4.setData(t, d4)
# Fixer fenêtre X (temps)
self.plot1.setXRange(start_time, t_max)
self.plot2.setXRange(start_time, t_max)
self.plot3.setXRange(start_time, t_max)
self.plot4.setXRange(start_time, t_max)
# =========================
# LANCEMENT DE L'APPLICATION
# =========================
app = QtWidgets.QApplication([]) # Application Qt
window = App() # Création fenêtre principale
window.setWindowTitle("Moniteur Multi-Capteurs Temps Réel") # Titre
window.resize(900, 600) # Taille fenêtre
window.showMaximized() # Plein écran
sys.exit(app.exec_()) # Boucle événementielle Qt
Exemple d’interface moderne : panneau de contrôle à gauche, visualisation multi-capteurs en temps réel, design professionnel adapté aux applications industrielles.
Pour reproduire cette interface professionnelle, les étudiants doivent maîtriser l’utilisation combinée de plusieurs layouts et widgets afin de construire une application modulaire, claire et évolutive.