👋 Je souhaiterais vous partager mon projet d’ingénieur, que j’ai réalisé en binôme cette année (2023).
Mon binôme, Mathieu SCHWOERER : camarade de classe à Telecom Physique Strasbourg, formation ingénieur en électronique et informatique industrielle.
Le projet a été proposé par notre enseignant de traitement du signal, Monsieur Vincent MAZET : Enseignant-Chercheur et Directeur des études à Telecom Physique Stasbourg.
Introduction
L’objectif de ce projet est de réaliser 2 maquettes de travaux pratiques pour les cours de traitement du signal[1][2][3].
Cahier des charges
Cette partie définit les différents critères demandés par notre enseignant pour les deux maquettes. D’un point de vue général, les deux maquettes doivent être pédagogiques et adaptées à l’apprentissage du traitement du signal. Les travaux pratiques de traitement du signal sont réalisés sur ordinateur à l’aide du langage de programmation Python. Pendant les TP, les étudiants utilisent des notebooks Jupyter pour programmer en Python.
Maquette 1 : Mesure de la vitesse du son
La première maquette a pour but de mesurer la vitesse du son. La maquette sera composée de deux microphones et d’un haut-parleur. Le haut-parleur émettra un son, et les deux microphones acquerront ce son. Les étudiants devront utiliser les outils de traitement du signal[1][2] pour calculer le décalage temporel entre les deux signaux acquis, afin de déterminer la vitesse du son.
Maquette 2 : Communication numérique
La dernière maquette a pour but de mettre en œuvre une communication numérique (voir figure 1 ci-dessous). Deux étudiants munis d’un ordinateur (avec Python) pourront s’envoyer des messages numériques.
Entre les deux ordinateurs, un boîtier simulant un canal permettra d’appliquer du bruit, du filtrage et une inversion de polarité aux signaux.
L’objectif de ce boîtier CANAL est de montrer aux étudiants les différentes problématiques liées à la communication numérique et comment utiliser les outils vus en cours de traitement du signal[3] pour y répondre.
Réalisation de la première maquette
Synoptique
Avant de réaliser la première maquette, il a fallu rechercher le matériel. Nous avons sélectionné :
- 2 microphones
- 1 haut-parleur
- 1 adaptateur jack
Ci-dessous, sur la figure 2, le synoptique de la première maquette.
Acquisition avec les microphones Hardware
Comme le montre la figure 2, les deux microphones sont reliés à l’ordinateur via l’entrée micro.
La figure 3 montre les différentes sorties d’une prise Jack. On peut y voir que chaque microphone est stéréo et divise l’acquisition en 2 canaux.
Afin d’acquérir les signaux des deux microphones avec une seule entrée Jack, nous avons utilisé un doubleur Jack (voir figure 4). Cette astuce nous a permis de récupérer le signal du premier microphone sur le canal gauche et le signal du deuxième microphone sur le canal droit.
Acquisition avec les microphones via python
Dans cette partie, nous allons vous détailler comment nous avons réussi à acquérir les deux signaux des microphones avec Python en utilisant la librairie sounddevice. Cette dernière permet d’enregistrer et de jouer du son sur un ordinateur.
Exemple de code pour acquérir du son via sounddevice
import sounddevice as sd
sample_rate = 44100 #Fréquence d'échantillonnage mp3
duration = 1 #Durée de l'enregistrement
channels = 2 #Nombre de canaux
# Enregistrer le son du microphone dans un fichier WAV
recorded_audio = sd.rec(int(duration * sample_rate), samplerate=sample_rate, channels=channels)
sd.wait() # Attendre la fin de l'enregistrement
Traitement du signal
Pour obtenir le décalage temporel entre les deux signaux, nous avons utilisé l’intercorrélation[1].
Voici l’expression de cette dernière :
R_{xy}[m] = \sum_{n=-\infty}^{+\infty} x[n+m] y[n]\\ \text{Où $x$ est le premier signal et $y$ le signal envoyé par le haut-parleur.}
Exemple
Dans l’exemple ci-dessous les deux microphones ont enregistré un bruit blanc :
L’intercorrélation entre le bruit blanc et chacune des acquisitions est alors égale à :
Le pic de chaque corrélation croisée représente l’instant où le bruit blanc apparaît sur le signal. Nous pouvons observer que le bruit blanc est apparu à environ 2 secondes.
Le pic de CorrelLeft
est à 2.0084353 secondes, tandis que le pic de CorrelRight
est à 2.0098866 secondes. En faisant la différence entre ces deux pics, nous pouvons obtenir le décalage temporel entre l’acquisition des deux signaux.
\Delta t = 2.0098866 - 2.0084353 = 0.0014513s
Une distance de 50 cm sépare les micros, nous pouvons alors déduire la vitesse du son.
v = \frac{d}{\Delta t} = \frac{0.5}{ 0.0014513} = 344.5 m/s
Deuxième variante de la maquette
Cette deuxième approche permet de mesurer la vitesse du son sans utiliser de haut-parleur. En effet, les deux microphones peuvent acquérir un signal significatif généré par les étudiants.
Un signal significatif peut être généré en faisant un claquement de mains, ce signal ressemble alors à un pic de Dirac.
δ_n[k]= \begin{cases} 1 & \text{si } k=n \\ 0 & \text{sinon} \end{cases}\\ \text{où $n$ est l'indice du pic et $k$ est l'indice de la position dans la séquence discrète.}\\ \text{La fonction $\delta_n(k)$ est égale à 1 si l'indice $k$ correspond à l'indice $n$ du pic de Dirac,}\\ \text{et égale à 0 pour tous les autres indices.}
Vous pouvez voir l’expérience avec un claquement de main dans cet vidéo.
Conclusion
En conclusion, nous avons réussi à fournir à notre enseignant un sujet de TP et sa correction fonctionnelle. La maquette que nous avons conçue est capable de mesurer la vitesse du son. Cependant, il y a quelques points que nous pourrions approfondir. Par exemple, nous pourrions envisager la création d’une nouvelle maquette équipée de deux supports de micros réglables pour mieux ajuster la distance entre eux. Idéalement, ces micros pourraient être placés dans une boîte, ce qui nous permettrait de calculer ses dimensions.
Réalisation de la deuxième maquette
Synoptique
En premier lieu nous avons réfléchi à comment mettre en place une telle maquette.
Nous avons d’abord recherché plusieurs microcontrôleurs capable de traiter et restituer un signal audio.
Finalement, nous avons sélectionné le microcontrôleur Teensy 4.0 et son module audio pour réaliser le CANAL.
Caractéristique Teensy 4.0
- ARM Cortex-M7 (600 MHz)
- Flash de 1984 Ko, RAM de 1024 Ko (512 Ko fortement couplés), EEPROM de 1 Ko (émulée)
- USB périphérique à 480 Mbit/s et USB hôte à 480 Mbit/s
- 40 broches d’entrée/sortie numériques, 31 broches de sortie PWM
- 14 broches d’entrée analogiques
- 7 ports série, 3 ports SPI, 3 ports I2C
- 2 ports audio numériques I2S/TDM et 1 port audio numérique S/PDIF
- 3 bus CAN (1 avec CAN FD)
- RTC pour la date/heure
Caractéristique module audio
- Codec audio MP3 : 44100Hz 16 bits (qualité CD)
Ces deux éléments permettent d’acquérir un signal, de le traiter et de le restituer, comme le montre la figure ci-dessous.
Avec ces différentes informations nous avons déduit, pour la deuxième maquette, le synoptique suivant :
Mécanique
Comme nous pouvons le voir sur la figure 8, un boîtier est situé entre les deux ordinateurs. Comme expliqué dans l’introduction, l’objectif du boîtier est de simuler un véritable canal (en ajoutant des bruits au signal…).
Le boîtier CANAL contient la carte Teensy, le module audio, une prise jack femelle et 5 interrupteurs. Ces interrupteurs permettent d’activer/désactiver la présence de bruit, le filtrage et l’inversion de polarité.
Nous avons défini la configuration ci-dessous pour les interrupteurs en collaboration avec notre enseignant.
Après avoir définit l’emplacement des interrupteurs nous avons réalisé le boîtier ci-dessous avec l’outil de CAO SolidWorks.
Programmation de la carte Teensy
Une des premières étapes du projet a été de comprendre le fonctionnement de la carte et du module audio. La carte Teensy se programme comme un arduino en langage C.
Pour se faire, nous avons d’abord téléchargé la librairie arduino Teensy
de Paul Stoffregen.
Pour le module audio, il existe une interface web avec des blocs pour acquérir, traiter et restituer le flux audio. Ces blocks permettent d’appliquer au signal, des filtres, du bruit, des effets (pour les plus musiciens d’entre vous 🎵). L’interface se présente comme ci-dessous.
Les blocs i2s
représentent l’entrée et la sortie du module audio. Chaque bloc est bien documenté et permet une bonne compréhension du schéma bloc.
Par exemple le bloc i2s
output, est documenté comme sur la figure 11.
Le code arduino du schéma bloc de la figure 10 peut être exporté avec le bouton Export
.
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// GUItool: begin automatically generated code
AudioInputI2S i2s1; //xy=194.1999969482422,165.1999969482422
AudioSynthNoiseWhite noise1; //xy=194.20001602172852,232.1999855041504
AudioMixer4 mixer1; //xy=388.2000026702881,195.19999885559082
AudioOutputI2S i2s2; //xy=556.2000350952148,170.19999885559082
AudioConnection patchCord1(i2s1, 0, mixer1, 0);
AudioConnection patchCord2(noise1, 0, mixer1, 1);
AudioConnection patchCord3(mixer1, 0, i2s2, 0);
AudioConnection patchCord4(mixer1, 0, i2s2, 1);
// GUItool: end automatically generated code
Ce code va permettre d’initialiser les différents objets que nous allons utiliser dans notre code. Après avoir récupéré ce code généré par l’interface web Teensy nous pouvons passer à la programmation !
void setup() {
/*configuration du codec de la carte teensy */
AudioMemory(160);
ctl.enable();
ctl.volume(0.8);
amp.gain(1);
ctl.inputSelect(AUDIO_INPUT_LINEIN);
/*Configuration des objets*/
//Création du bruit blanc
noise1.amplitude(1);
/*Initialisation du programme en mode passe tout*/
signalPassthrough();
}
Ci-dessus le code de la fonction setup
, elle va permettre d’initialiser la carte Teensy.
Dans le code void loop()
pour activer le bruit blanc, il suffit de faire :
void loop(){
int bruit; //variable qui active le bruit quand elle vaut 1
if(bruit==0){
mixer1.gain(0,1);//Signal actif
mixer1.gain(1,0);//Bruit pas actif
}
if(bruit==1){
mixer1.gain(0,1);//Signal actif
mixer1.gain(1,1);//Bruit actif
}
}
Le bloc mixer présent dans la figure 10 permet d’additionner des signaux. En effet pour ajouter du bruit à un signal nous devons utiliser :
y(t)=x(t)+b(t)\\ \text{Où $y$ est le signal reçu, $x$ le signal envoyé et $b$ le bruit ajouté.}
La fonction mixerX.gain(channel,level)
permet d’activer ou désactiver un signal qui passe dans le bloc. Comme nous pouvons le voir sur la figure 12, le bruit est sur la deuxième ligne du bloc mixer. mixer1.gain(1,1)
va ajouter le bruit à l’addition donc le signal i2s2
sera bruité.
Pour conclure cette partie, nous avons réalisé un code pour la carte Teensy qui permet à notre CANAL de respecter le cahier des charges, le canal ajoute du bruit, du filtrage et une inversion de polarité comme défini sur la figure 9.
Programmation d’une librairie python
Après avoir programmé la carte Teensy, nous sommes passés à la programmation d’une bibliothèque Python afin de permettre aux étudiants d’utiliser le boîtier.
Cette bibliothèque python devra permettre aux étudiants de communiquer des messages en utilisant l’alphabet ASCII entre deux ordinateurs. Dans la bibliothèque, différentes fonctions seront mises à disposition des étudiants pour leur permettre d’envoyer des chaînes de caractères ASCII modulées avec diverses modulations en bande de base.
Il est important de rappeler que les deux ordinateurs seront reliés par un câble jack (voir figure 8), et que dans un premier temps le boîtier CANAL n’interfèrera pas avec le signal.
Envoi d’un message numérique via la prise jack
En premier lieu, nous avons commencé par un POC (Proof of concept) pour valider le fait qu’on peut envoyer un signal numérique en Python avec un câble jack .
Un signal numérique, contrairement à un signal analogique, est discrétisé c’est-à-dire qu’au lieu d’être continu, le signal va être échantillonné (voir figure 13). La plupart des ordinateurs possèdent des cartes son travaillant à une fréquence d’échantillonnage de Fe = 44100 Hz
: Sur un signal d’une seconde, la carte son va prendre 44100
points de ce signal.
Nous avons d’abord utilisé le site online tone generator pour émettre une sinusoïde à 440 Hz à partir d’un ordinateur émetteur, puis nous avons réceptionné ce signal avec un ordinateur récepteur. Lors de cette expérience, nous avons réussi à afficher le signal reçu en temps réel avec Python en utilisant l’exemple de code de la bibliothèque Sounddevice, qui permet d’afficher le signal reçu par l’entrée microphone de l’ordinateur. Vous pouvez retrouver le code pour tracer le signal en temps réel sur le site de la bibliothèque sous le nom « Plot Microphone Signal(s) in Real-Time ».
Après avoir réussi à émettre et recevoir une sinusoïde, nous avons commencé la rédaction de la librairie commnumfip
.
La première fonction que nous avons rédigé permet de convertir une chaîne de caractères ASCII en signal modulé en bande de base NRZ[3]. Ci-dessous le code de la fonction.
def NRZ_signal_generator(message):
"""
Convertit le message rentré en paramètre en code NRZ.
Entrées :
message (string) : Message à envoyer au canal en ASCII.
Sortie :
aucune
"""
# Paramètres du signal Manchester
framerate = 44100 # Fréquence d'échantillonnage en Hz
amplitude = 0.5 # Amplitude du signal
frequence = 44 # Fréquence du signal en Hz
duree_bit = 1/frequence # Durée d'un bit en secondes
# Convertir le message en une séquence de bits (0 et 1)
bits = np.unpackbits(np.array([ord(c) for c in message], dtype=np.uint8))
# Générer le signal NRZ
temps_bit = np.linspace(0, duree_bit, int(duree_bit * framerate), endpoint=False)
signal_nrz = np.zeros(0)
#preparation de la trame de début et ajout au message
print(bits)
for bit in bits:
if bit == 0:
signal_nrz = np.append(signal_nrz, amplitude * np.ones_like(temps_bit))
else:
signal_nrz = np.append(signal_nrz, -amplitude * np.ones_like(temps_bit))
# Ajouter des zéros au début et à la fin du signal
plt.figure()
plt.plot(signal_nrz)
plt.show()
signal_nrz = np.pad(signal_nrz, (int(framerate/2), int(framerate/2)), 'constant')
return signal_nrz
Cette fonction va convertir une chaîne de caractères en séquence de bits grâce à la fonction np.unpackbits
. Par exemple le caractère 'A'
en ASCII vaut 0x41
donc 01000001
en binaire. Ci-dessous le tableau des caractères ASCII :
Le reste de la fonction va permettre de générer un signal NRZ d’amplitude 0.5V à -0.5V, un signal NRZ est définit comme ci-dessous.
Après avoir créé le signal NRZ, nous devons l’envoyer au récepteur. Pour ce faire, nous utilisons la prise jack de l’ordinateur. Comme pour la première maquette, nous pouvons utiliser la bibliothèque sounddevice
.
Nous avons écrit une fonction send_signal_to_canal
qui utilise la bibliothèque sounddevice
pour envoyer le signal sur la prise jack. Ci-dessous le code de la fonction :
def send_signal_to_canal(signal):
"""
Envoie le signal au canal.
Entrées :
signal (array) : signal.
Sortie :
aucune
"""
sd.play(signal, 44100) #Code permettant de jouer le signal à 44,1 kHz de fréquence d'échantillonnage
sd.wait()
Tous ces éléments permettent à l’étudiant de générer et d’envoyer un message ASCII via le port jack d’un ordinateur. Maintenant, dans un dernier temps, nous allons nous pencher sur l’acquisition et le décodage du signal.
Réception d’un message numérique via la prise jack
Après avoir émis le signal, nous avons créé une fonction Python qui permet de recevoir le signal, de le convertir du format NRZ en ASCII et de retrouver le message initial.
Dans la librairie commnumfip
, nous avons ajouté la fonction signal acquire_signal
. Voir ci-dessous :
def acquire_signal(fe: int, duration: float):
"""
Acquisition du message de la communication numérique
Entrée :
- fe (int): Taux d'échantillonnage de l'acquisition.
- duration (float): Durée de l'acquisition en secondes.
Sortie :
- numpy.ndarray: Signal contenant uniquement le message
"""
# Acquisition du signal.
signal = sd_cust.rec(int(duration*fe), samplerate=fe, channels=1)
sd_cust.wait()
# Suppression des premières valeurs si le bit de synchronisation est encore présent.
signal = signal[2200::]
t = np.arange(0,len(signal))/44100
return t,signal
Cette fonction permet au récepteur d’attendre une trame de l’émetteur, en effet nous avons modifié la fonction rec
originale de sounddevice
, pour enregistrer en temps réel et acquérir le message. Nous avons mis cela en place en modifiant la librairie sounddevice
nous avons rajouté dans la fonction une fonction callback qui scrute l’amplitude du signal, si le seuil est détecté alors un message est transmis sur la ligne. Le bruit pouvant être activé avec le boîtier CANAL ne posera pas de problème étant donné que le bruit est seulement actif lors de l’émission d’un signal.
Gestion des problématique liées au canal
Dans la communication numérique, le canal réel est soumis à plusieurs problématiques, le signal réceptionné est potentiellement bruité, filtré… Lors de la réception, nous devons d’abord appliquer un filtre de réception, puis effectuer l’échantillonnage, puis seuiller le signal afin de décoder l’information.
Sans bruit, les étudiants pourront échantillonner et seuiller le signal avec les fonctions sample_and_threshold
et par la suite décoder les différentes modulations.
Cependant, en présence de bruit, les étudiants doivent utiliser un filtrage adapté pour retrouver les différents fronts dans le signal.
y[n]=x[n]+b[n]\\ \text{Où $y$ est le signal reçu, $x$ le signal envoyé et $b$ le bruit ajouté.}
Pour récupérer le signal x, nous devons faire une intercorrélation entre y et le motif recherché.
La figure de gauche montre le motif à rechercher.
Le filtrage adapté sera donc égal à :
R_{yh}[m] = \sum_{n=-\infty}^{+\infty} y[n+m] h[n]\\ \text{Où $y$ est le signal reçu et $h$ le motif à rechercher.}
Exemple
Dans cet exemple, nous allons voir l’intercorrélation (filtrage adapté) entre un signal NRZ bruité et le motif de la figure 16.
D’après la figure 18, le filtrage adapté donne le message binaire suivant :
y[n] = [0 1 0 0\space0 0 1 0\space0 1 1 0\space1 1 1 1\space0 1 1 0\space1 1 1 0\space0 1 1 0\space1 0 1 0\space 0 1 1 0\space 1 1 1 1\space 0 1 1 1 \space0 1 0 1 \space0 1 1 1 \space0 0 1 0]\\ \text{Valeur en ASCII : 0x42 0x6F 0x6E 0x6A 0x6F 0x75 0x72 = Bonjour}
Conclusion
En conclusion, nous avons réussi à fournir à notre professeur une deuxième maquette fonctionnelle qui comprend un boîtier CANAL. Ce dernier est capable de récupérer un flux audio, d’ajouter du bruit, du filtrage et une inversion de polarité, et de restituer le flux modifié. De plus, nous avons fourni un TP pédagogique pour mettre en œuvre une communication numérique. Les étudiants pourront ainsi comprendre les problématiques liées au canal de communication réel et utiliser les outils de traitement du signal pour y répondre.
Références
- [1] «Traitement du signal 1». [en ligne]. Site de Vincent MAZET (Université de Strasbourg), 2020-2023 [consulté le 22/04/2023]. https://vincmazet.github.io/signal1/
- [2] «Traitement du signal 2». [en ligne]. Site de Vincent MAZET (Université de Strasbourg), 2020-2023 [consulté le 22/04/2023]. https://vincmazet.github.io/signal2/
- [3] «Communication numérique». [en ligne]. Site de Vincent MAZET (Université de Strasbourg), 2020-2023 [consulté le 22/04/2023]. https://vincmazet.github.io/comnum/
- [4] «American Standard Code for Information Interchange». [en ligne]. Page Wikipédia ASCII,[consulté le 28/04/2023]. https://fr.wikipedia.org/wiki/American_Standard_Code_for_Information_Interchange
Ressources
- Github du projet : https://github.com/zuce68/ProjetFIP2A [consulté le 25/04/2023]
- Carte Teensy 4.0 : https://www.pjrc.com/store/teensy40.html [consulté le 25/04/2023]
- Module audio : https://www.pjrc.com/store/teensy3_audio.html [consulté le 25/04/2023]
- Site de jupyter : https://jupyter.org [consulté le 25/04/2023]
- Courte vidéo du projet : https://youtu.be/Gd5IspfefNo
Franchement trop bien j’ai adoré