Filtresï
Dans ce chapitre, nous nous familiarisons avec les filtres numériques en utilisant Python. Nous couvrons les types de filtres (FIR/IIR et passe-bas/passe-haut/passe-bande/coupe-bande), comment les filtres sont représentés numériquement et comment ils sont conçus. Nous terminons par une introduction aux filtres de mise en forme, que nous approfondissons dans le chapitre Mise en Forme.
Principes de Base des Filtresï
Les filtres sont utilisĂ©s dans de nombreuses disciplines. Par exemple, le traitement des images fait un usage intensif des filtres 2D, oĂč lâentrĂ©e et la sortie sont des images. Vous utilisez peut-ĂȘtre un filtre chaque matin pour faire votre cafĂ©, qui filtre les solides du liquide. En DSP, les filtres sont principalement utilisĂ©s pour :
Séparation des signaux qui ont été combinés (par exemple, extraction du signal souhaité).
Suppression de lâexcĂšs de bruit aprĂšs la rĂ©ception dâun signal
Restauration des signaux qui ont Ă©tĂ© dĂ©formĂ©s dâune maniĂšre ou dâune autre (par exemple, un Ă©galiseur audio est un filtre).
Il existe certainement dâautres utilisations des filtres, mais ce chapitre a pour but dâintroduire le concept plutĂŽt que dâexpliquer toutes les façons dont le filtrage peut se produire.
Vous pouvez penser que nous ne nous intĂ©ressons quâaux filtres numĂ©riques; ce manuel explore le DSP, aprĂšs tout. Cependant, il est important de savoir que de nombreux filtres seront analogiques, comme ceux de nos SDR placĂ©s avant le convertisseur analogique-numĂ©rique (CAN) du cĂŽtĂ© rĂ©ception. Lâimage suivante juxtapose le schĂ©ma dâun circuit de filtrage analogique et la reprĂ©sentation sous forme dâorganigramme dâun algorithme de filtrage numĂ©rique.
En DSP, oĂč lâentrĂ©e et la sortie sont des signaux, un filtre a un signal en entrĂ©e et un signal en sortie :
Vous ne pouvez pas introduire deux signaux diffĂ©rents dans un seul filtre sans les additionner au prĂ©alable ou effectuer une autre opĂ©ration. De mĂȘme, la sortie sera toujours un seul signal, câest-Ă -dire un tableau 1D de nombres.
Il existe quatre types de filtres de base :passe-bas, passe-haut, passe-bande et coupe-bande. Chaque type modifie les signaux pour se concentrer sur les diffĂ©rentes plages de frĂ©quences quâils contiennent. Les graphiques ci-dessous montrent comment les frĂ©quences des signaux sont filtrĂ©es pour chaque type.
(AJOUTER LE DIAGRAMME MONTRANT LES FRĂQUENCES NĂGATIVES AUSSI)
Chaque filtre permet Ă certaines frĂ©quences de rester dans un signal tout en bloquant dâautres frĂ©quences. La gamme de frĂ©quences quâun filtre laisse passer est appelĂ©e « bande passante », et le « bande rejetĂ©e » fait rĂ©fĂ©rence Ă ce qui est bloquĂ©. Dans le cas du filtre passe-bas, il laisse passer les basses frĂ©quences et arrĂȘte les hautes frĂ©quences, de sorte que 0 Hz sera toujours dans la bande passante. Pour un filtre passe-haut et un filtre passe-bande, 0 Hz sera toujours dans la bande coupĂ©e.
Ne confondez pas ces types de filtrage avec la mise en Ćuvre algorithmique du filtre (par exemple, IIR vs FIR). Le type le plus courant est de loin le filtre passe-bas (LPF pour Low Pass Filter en anglais) car nous reprĂ©sentons souvent des signaux en bande de base. Le LPF nous permet de filtrer tout ce qui se trouve « autour » de notre signal, en Ă©liminant le bruit excessif et les autres signaux.
ReprĂ©sentation des Filtresï
Pour la plupart des filtres que nous verrons (connus sous le nom de filtres FIR, pour Finite Impulse Response en anglais, ou filtres Ă rĂ©ponse impulsionnelle finie en français), nous pouvons reprĂ©senter le filtre lui-mĂȘme avec un seul tableau de flottants. Pour les filtres symĂ©triques dans le domaine frĂ©quentiel, ces flottants seront rĂ©els (par opposition Ă complexes), et leur nombre tend Ă ĂȘtre impair. Nous appelons ce tableau de flottants les coĂ©fficients du filtre ou usuellement taps en anglais. Nous utilisons souvent \(h\) comme symbole pour les taps du filtre. Voici un exemple dâun ensemble de taps qui dĂ©finissent un filtre :
h = [ 9.92977939e-04 1.08410297e-03 8.51595307e-04 1.64604862e-04
-1.01714338e-03 -2.46268845e-03 -3.58236429e-03 -3.55412543e-03
-1.68583512e-03 2.10562324e-03 6.93100252e-03 1.09302641e-02
1.17766532e-02 7.60955496e-03 -1.90555639e-03 -1.48306750e-02
-2.69313236e-02 -3.25659606e-02 -2.63400086e-02 -5.04184562e-03
3.08099470e-02 7.64264738e-02 1.23536693e-01 1.62377258e-01
1.84320776e-01 1.84320776e-01 1.62377258e-01 1.23536693e-01
7.64264738e-02 3.08099470e-02 -5.04184562e-03 -2.63400086e-02
-3.25659606e-02 -2.69313236e-02 -1.48306750e-02 -1.90555639e-03
7.60955496e-03 1.17766532e-02 1.09302641e-02 6.93100252e-03
2.10562324e-03 -1.68583512e-03 -3.55412543e-03 -3.58236429e-03
-2.46268845e-03 -1.01714338e-03 1.64604862e-04 8.51595307e-04
1.08410297e-03 9.92977939e-04]
Exemple de cas dâutilisationï
Pour comprendre comment les filtres sont utilisĂ©s, prenons un exemple oĂč nous accordons notre radio logicielle sur la frĂ©quence dâun signal existant, et nous voulons lâisoler des autres signaux. Rappelez-vous que nous indiquons Ă notre SDR la frĂ©quence Ă laquelle il doit sâaccorder, mais que les Ă©chantillons capturĂ©s par le SDR sont en bande de base, ce qui signifie que le signal sâaffichera comme centrĂ© autour de 0 Hz. Nous devrons garder la trace de la frĂ©quence sur laquelle nous avons demandĂ© au SDR de sâaccorder. Voici ce que nous pourrions recevoir :
Comme notre signal est dĂ©jĂ centrĂ© sur le courant continu DC (0 Hz), nous savons que nous voulons un filtre passe-bas. Nous devons choisir une « frĂ©quence de coupure » (aussi appelĂ©e frĂ©quence dâangle), qui dĂ©terminera le moment oĂč la bande passante passe en bande rejetĂ©e. La frĂ©quence de coupure sera toujours exprimĂ©e en Hz. Dans cet exemple, 3 kHz semble ĂȘtre une bonne valeur :
Cependant, dans la plupart des filtres passe-bas, la limite de frĂ©quence nĂ©gative sera Ă©galement de -3 kHz. Câest-Ă -dire quâelle est symĂ©trique autour du DC (vous verrez plus tard pourquoi). Nos frĂ©quences de coupure ressembleront Ă ceci (la bande passante est la zone intermĂ©diaire) :
AprÚs avoir créé et appliqué le filtre avec une fréquence de coupure de 3 kHz, nous avons maintenant :
Ce signal filtrĂ© peut sembler dĂ©routant jusquâĂ ce que vous vous rappeliez que notre plancher de bruit Ă©tait Ă la ligne verte autour de -65 dB. MĂȘme si nous pouvons toujours voir le signal parasite centrĂ© Ă 10 kHz, nous avons sĂ©vĂšrement diminuĂ© la puissance de ce signal. Elle est maintenant infĂ©rieure Ă celle du plancher de bruit! Nous avons Ă©galement Ă©liminĂ© la plupart du bruit qui existait dans la bande rejetĂ©e.
En plus de la frĂ©quence de coupure, lâautre paramĂštre principal de notre filtre passe-bas est appelĂ© « largeur de transition ». La largeur de transition, Ă©galement mesurĂ©e en Hz, indique au filtre Ă quelle vitesse il doit passer de la bande passante Ă la bande rejetĂ©e, car une transition instantanĂ©e est impossible en pratique.
Visualisons la largeur de transition. Dans le diagramme ci-dessous, la ligne :green:`verte` reprĂ©sente la rĂ©ponse idĂ©ale pour la transition entre une bande passante et une bande dâarrĂȘt, qui a essentiellement une largeur de transition de zĂ©ro. La ligne :red:`rouge` montre le rĂ©sultat dâun filtre rĂ©aliste, qui prĂ©sente une certaine ondulation et une certaine largeur de transition.
Vous vous demandez peut-ĂȘtre pourquoi nous nâavons pas simplement dĂ©fini la largeur de transition la plus petite possible. La raison principale est quâune largeur de transition plus petite entraĂźne un plus grand nombre de taps, et plus de taps signifie plus de calculs - nous verrons pourquoi sous peu. Un filtre de 50 taps peut fonctionner toute la journĂ©e en utilisant 1% du CPU dâun Raspberry Pi. En revanche, un filtre Ă 50 000 prises fera exploser votre CPU ! En gĂ©nĂ©ral, nous utilisons un outil de conception de filtre, puis nous voyons combien de taps il produit, et si câest beaucoup trop (par exemple, plus de 100), nous augmentons la largeur de transition. Tout dĂ©pend de lâapplication et du matĂ©riel qui exĂ©cute le filtre, bien sĂ»r.
Dans lâexemple de filtrage ci-dessus, nous avions utilisĂ© une coupure de 3 kHz et une largeur de transition de 1 kHz (il est difficile de voir la largeur de transition en regardant ces captures dâĂ©cran). Le filtre rĂ©sultant a 77 taps.
Revenons Ă la reprĂ©sentation des filtres. MĂȘme si nous pouvons montrer la liste des taps dâun filtre, nous reprĂ©sentons gĂ©nĂ©ralement les filtres visuellement dans le domaine frĂ©quentiel. Nous appelons cela la « rĂ©ponse frĂ©quencielle » du filtre, et elle nous montre le comportement du filtre en frĂ©quence. Voici la rĂ©ponse en frĂ©quence du filtre que nous venons dâutiliser :
Notez que ce que je montre ici nâest pas un signal - câest juste la reprĂ©sentation du filtre dans le domaine frĂ©quenciel. Cela peut ĂȘtre un peu difficile Ă comprendre au dĂ©but, mais au fur et Ă mesure des exemples et du code, cela deviendra plus clair.
Un filtre donnĂ© a Ă©galement une reprĂ©sentation dans le domaine temporel; on lâappelle la « rĂ©ponse impulsionnelle » du filtre car câest ce que vous voyez dans le domaine temporel si vous prenez une impulsion et la faites passer par le filtre. (Cherche sur Google « fonction delta de Dirac » pour plus dâinformations sur ce quâest une impulsion). Pour un filtre de type FIR, la rĂ©ponse impulsionnelle est simplement les taps eux-mĂȘmes. Pour le filtre Ă 77 taps que nous avons utilisĂ© prĂ©cĂ©demment, les prises sont les suivantes:
h = [-0.00025604525581002235, 0.00013669139298144728, 0.0005385575350373983,
0.0008378280326724052, 0.000906112720258534, 0.0006353431381285191,
-9.884083502996931e-19, -0.0008822851814329624, -0.0017323142383247614,
-0.0021665366366505623, -0.0018335371278226376, -0.0005912294145673513,
0.001349081052467227, 0.0033936649560928345, 0.004703888203948736,
0.004488115198910236, 0.0023609865456819534, -0.0013707970501855016,
-0.00564080523326993, -0.008859002031385899, -0.009428252466022968,
-0.006394983734935522, 4.76480351940553e-18, 0.008114570751786232,
0.015200719237327576, 0.018197273835539818, 0.01482443418353796,
0.004636279307305813, -0.010356673039495945, -0.025791890919208527,
-0.03587324544787407, -0.034922562539577484, -0.019146423786878586,
0.011919975280761719, 0.05478153005242348, 0.10243935883045197,
0.1458890736103058, 0.1762896478176117, 0.18720689415931702,
0.1762896478176117, 0.1458890736103058, 0.10243935883045197,
0.05478153005242348, 0.011919975280761719, -0.019146423786878586,
-0.034922562539577484, -0.03587324544787407, -0.025791890919208527,
-0.010356673039495945, 0.004636279307305813, 0.01482443418353796,
0.018197273835539818, 0.015200719237327576, 0.008114570751786232,
4.76480351940553e-18, -0.006394983734935522, -0.009428252466022968,
-0.008859002031385899, -0.00564080523326993, -0.0013707970501855016,
0.0023609865456819534, 0.004488115198910236, 0.004703888203948736,
0.0033936649560928345, 0.001349081052467227, -0.0005912294145673513,
-0.0018335371278226376, -0.0021665366366505623, -0.0017323142383247614,
-0.0008822851814329624, -9.884083502996931e-19, 0.0006353431381285191,
0.000906112720258534, 0.0008378280326724052, 0.0005385575350373983,
0.00013669139298144728, -0.00025604525581002235]
Et mĂȘme si nous nâavons pas encore abordĂ© la conception des filtres, voici le code Python qui a gĂ©nĂ©rĂ© ce filtre:
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
num_taps = 51 # Il est utile d'utiliser un nombre impair de robinets.
cut_off = 3000 # Hz
sample_rate = 32000 # Hz
# créer notre filtre passe-bas
h = signal.firwin(num_taps, cut_off, fs=sample_rate)
# tracer la réponse impulsionnelle
plt.plot(h, '.-')
plt.show()
Le simple fait de tracer ce tableau de flottants nous donne la réponse impulsionnelle du filtre:
Et voici le code qui a Ă©tĂ© utilisĂ© pour produire la rĂ©ponse frĂ©quentielle, prĂ©sentĂ©e plus tĂŽt. Câest un peu plus compliquĂ© car nous devons crĂ©er le tableau des frĂ©quences sur lâaxe des x.
# tracer la réponse en fréquence
H = np.abs(np.fft.fft(h, 1024)) # prendre la FFT 1024 points et la magnitude
H = np.fft.fftshift(H) # centrer Ă 0 Hz
w = np.linspace(-sample_rate/2, sample_rate/2, len(H)) # axe des x
plt.plot(w, H, '.-')
plt.show()
Filtres RĂ©els et Complexesï
Le filtre que je vous ai montrĂ© avait des taps rĂ©elles, mais les taps peuvent aussi ĂȘtre complexes. Le fait que les taps soient rĂ©elles ou complexes ne doit pas nĂ©cessairement correspondre au signal que vous faites passer par le filtre, câest-Ă -dire que vous pouvez faire passer un signal complexe par un filtre avec des taps rĂ©elles et vice versa. Lorsque les taps sont rĂ©elles, la rĂ©ponse en frĂ©quence du filtre sera symĂ©trique autour du DC (0 Hz). En gĂ©nĂ©ral, nous utilisons des prises complexes lorsque nous avons besoin dâasymĂ©trie, ce qui arrive trĂšs rarement.
Pour illustrer les prises complexes, revenons au cas dâutilisation du filtrage, sauf que cette fois, nous voulons recevoir lâautre signal parasite (sans avoir Ă rĂ©accorder la radio). Cela signifie que nous voulons un filtre passe-bande, mais pas un filtre symĂ©trique. Nous voulons seulement garder (câest-Ă -dire « passer ») les frĂ©quences entre environ 7 kHz et 13 kHz (nous ne voulons pas passer Ă©galement de -13 kHz Ă -7 kHz) :
Une façon de concevoir ce type de filtre est de rĂ©aliser un filtre passe-bas avec une coupure de 3 kHz, puis de le dĂ©caler en frĂ©quence. Rappelez-vous que nous pouvons dĂ©caler la frĂ©quence de x(t) (domaine temporel) en la multipliant par \(e^{j2\pi f_0t}\). Dans ce cas, \(f_0\) devrait ĂȘtre 10 kHz, ce qui dĂ©cale notre filtre de 10 kHz. Rappelez-vous que dans notre code Python ci-dessus, \(h\) Ă©tait les taps du filtre passe-bas. Afin de crĂ©er notre filtre passe-bande, il suffit de multiplier ces prises par \(e^{j2\pi f_0t}\), bien que cela implique la crĂ©ation dâun vecteur pour reprĂ©senter le temps basĂ© sur notre pĂ©riode dâĂ©chantillonnage (inverse de la frĂ©quence dâĂ©chantillonnage) :
# (h a été trouvé en utilisant le premier extrait de code)
# Décaler le filtre en fréquence en multipliant par exp(j*2*pi*f0*t)
f0 = 10e3 # le montant que nous allons transférer
Ts = 1.0/sample_rate # période de l'échantillon
t = np.arange(0.0, Ts*len(h), Ts) # vecteur temps. les arguments sont (début, fin, pas)
exponential = np.exp(2j*np.pi*f0*t) # il s'agit essentiellement d'une onde sinusoĂŻdale complexe
h_band_pass = h * exponential # faire le décallage
# tracer la réponse impulsionnelle
plt.figure('impulsion')
plt.plot(np.real(h_band_pass), '.-')
plt.plot(np.imag(h_band_pass), '.-')
plt.legend(['reél', 'imag'], loc=1)
# tracer la réponse en fréquence
H = np.abs(np.fft.fft(h_band_pass, 1024)) # prendre la FFT 1024 points et l'amplitude
H = np.fft.fftshift(H) # faire 0 Hz au centre
w = np.linspace(-sample_rate/2, sample_rate/2, len(H)) # axes des x
plt.figure('freq')
plt.plot(w, H, '.-')
plt.xlabel('Fréquence [Hz]')
plt.show()
The plots of the impulse response and frequency response are shown below:
Comme notre filtre nâest pas symĂ©trique autour de 0 Hz, il doit utiliser des taps complexes. Nous avons donc besoin de deux lignes pour tracer ces taps complexes. Ce que nous voyons dans le graphique de gauche ci-dessus est toujours la rĂ©ponse impulsionnelle. Notre courbe de rĂ©ponse frĂ©quencielle est ce qui valide rĂ©ellement le fait que nous avons créé le type de filtre que nous espĂ©rions, oĂč il filtrera tout sauf le signal centrĂ© autour de 10 kHz. Une fois encore, nâoubliez pas que le tracĂ© ci-dessus nâest pas un signal rĂ©el: il sâagit simplement dâune reprĂ©sentation du filtre. Cela peut ĂȘtre trĂšs dĂ©routant Ă comprendre, car lorsque vous appliquez le filtre au signal et que vous tracez la sortie dans le domaine frĂ©quentiel, dans de nombreux cas, elle aura Ă peu prĂšs la mĂȘme apparence que la rĂ©ponse en frĂ©quence du filtre lui-mĂȘme.
Si cette sous-section a ajouté à la confusion, ne vous inquiétez pas, dans 99% des cas, vous aurez affaire à de simples filtres passe-bas avec des taps réelles de toute façon.
ImplĂ©mentation des Filtresï
Nous nâallons pas nous plonger trop profondĂ©ment dans lâimplĂ©mentation des filtres. Je me concentre plutĂŽt sur la conception des filtres (de toute façon, vous pouvez trouver des implĂ©mentations prĂȘtes Ă lâemploi dans nâimporte quel langage de programmation). Pour lâinstant, voici ce quâil faut retenir: pour filtrer un signal avec un filtre FIR, il suffit de convoluer la rĂ©ponse impulsionnelle (le vecteur de taps) avec le signal dâentrĂ©e. (Ne vous inquiĂ©tez pas, une section ultĂ©rieure explique la convolution.) Dans le monde discret, nous utilisons une convolution discrĂšte (exemple ci-dessous). Les triangles labelisĂ©s par des b sont les taps. Dans le schĂ©ma, les carrĂ©s labelisĂ©s \(z^{-1}\) au-dessus des triangles signifient quâil faut retarder dâun pas de temps.
Vous pouvez peut-ĂȘtre comprendre pourquoi nous les appelons maintenant des « taps » (robinet en anglais) de filtre, compte tenu de la façon dont le filtre lui-mĂȘme est mis en Ćuvre.
FIR vs IIRï
Il existe deux grandes classes de filtres numériques: FIR et IIR 1. Réponse impulsionnelle finie (FIR pour Finite Impulse Response en anglais) 2. Réponse impulsionnelle infinie (IIR pour InFinite Impulse Response en anglais)
Nous nâentrerons pas trop dans la thĂ©orie, pour lâinstant, souvenez-vous que Les filtres FIR sont plus faciles Ă concevoir et peuvent faire tout ce que vous voulez si vous utilisez suffisamment de taps. Les filtres IIR en revanche sont plus compliquĂ©s et peuvent ĂȘtre instables, mais ils sont plus efficaces (ils utilisent moins de CPU et de mĂ©moire pour un filtre donnĂ©). Si quelquâun vous donne une liste de taps, on suppose quâil sâagit de taps pour un filtre FIR. Sâil commence Ă mentionner des « pĂŽles », il sâagit de filtres IIR. Nous nous en tiendrons aux filtres FIR dans ce manuel.
Vous trouverez ci-dessous un exemple de rĂ©ponse frĂ©quencielle, comparant un filtre FIR et un filtre IIR qui effectuent presque exactement le mĂȘme filtrage; ils ont une largeur de transition similaire qui, comme nous lâavons appris, dĂ©termine le nombre de taps nĂ©cessaires. Le filtre FIR a 50 prises et le filtre IIR a 12 pĂŽles, ce qui revient Ă avoir 12 taps en termes de calculs nĂ©cessaires.
La leçon Ă retenir est que le filtre FIR nĂ©cessite beaucoup plus de ressources informatiques que le filtre IIR pour effectuer Ă peu prĂšs la mĂȘme opĂ©ration de filtrage.
Voici quelques exemples concrets de filtres FIR et IIR que vous avez peut-ĂȘtre dĂ©jĂ utilisĂ©s.
Si vous effectuez une « moyenne glissante » sur une liste de nombres, il sâagit simplement dâun filtre FIR avec des taps de 1: - h = [1 1 1 1 1 1 1 1 1 1 1 1] pour un filtre de moyenne glissante avec une taille de fenĂȘtre de 10. Il sâagit Ă©galement dâun filtre passe-bas, pourquoi? Quelle est la diffĂ©rence entre lâutilisation de 1 et lâutilisation de taps qui diminuent jusquâĂ zĂ©ro ?
Réponse
Un filtre Ă moyenne glissante est un filtre passe-bas car il attĂ©nue les changements de « haute frĂ©quence », ce qui est gĂ©nĂ©ralement la raison pour laquelle les gens en utilisent un. La raison pour laquelle il faut utiliser des taps qui diminuent jusquâĂ zĂ©ro aux deux extrĂ©mitĂ©s est dâĂ©viter un changement soudain dans la sortie, comme si le signal filtrĂ© Ă©tait nul pendant un certain temps, puis augmentait soudainement.
Maintenant, un exemple de filtre IIR. Lâun dâentre vous a-t-il dĂ©jĂ fait ceci :
x = x*0.99 + nouvelle_valeur*0.01
oĂč les 0,99 et 0,01 reprĂ©sentent la vitesse de mise Ă jour de la valeur (ou le taux de dĂ©croissance, mĂȘme chose). Câest un moyen pratique de mettre Ă jour lentement une variable sans avoir Ă se souvenir des derniĂšres valeurs. Il sâagit en fait dâune forme de filtre IIR passe-bas. Avec un peu de chance, vous avez compris pourquoi les filtres IIR sont moins stables que les filtres FIR: les valeurs ne disparaissent jamais complĂštement !
Outils de conception de filtresï
En pratique, la plupart des gens utiliseront un outil de conception de filtre ou une fonction dans le code qui conçoit le filtre. Il existe de nombreux outils diffĂ©rents, mais pour les Ă©tudiants, je recommande cette application Web facile Ă utiliser de Peter Isza qui vous montrera la rĂ©ponse impulsionnelle et frĂ©quencielle : http://t-filter.engineerjs.com. En utilisant les valeurs par dĂ©faut, du moins au moment de lâĂ©criture de ce document, lâapplication est configurĂ©e pour concevoir un filtre passe-bas avec une bande passante de 0 Ă 400 Hz et une bande rejetĂ©e Ă partir de 500 Hz. La frĂ©quence dâĂ©chantillonnage est de 2 kHz, donc la frĂ©quence maximale que nous pouvons « voir » est de 1 kHz.
Cliquez sur le bouton « Design Filter » pour créer les prises et tracer la réponse en fréquence.
Cliquez sur le texte « Impulse Response » au-dessus du graphique pour voir la rĂ©ponse impulsionnelle, qui est une courbe des taps puisquâil sâagit dâun filtre FIR.
Cette application inclut mĂȘme le code source C++ pour implĂ©menter et utiliser ce filtre. Lâapplication web nâinclut aucun moyen de concevoir des filtres IIR, qui sont en gĂ©nĂ©ral beaucoup plus difficiles Ă concevoir.
Convolutionï
Nous allons faire un bref dĂ©tour pour prĂ©senter lâopĂ©rateur de convolution. NâhĂ©sitez pas Ă sauter cette section si elle vous est dĂ©jĂ familiĂšre.
Lâaddition de deux signaux est une façon de combiner deux signaux en un seul. Dans le chapitre Domaine frĂ©quentiel, nous avons Ă©tudiĂ© comment la propriĂ©tĂ© de linĂ©aritĂ© sâapplique Ă lâaddition de deux signaux. La convolution est une autre façon de combiner deux signaux en un seul, mais elle est trĂšs diffĂ©rente de leur simple addition. La convolution de deux signaux revient Ă en glisser un sur lâautre et Ă lâintĂ©grer. Elle est trĂšs similaire Ă une corrĂ©lation croisĂ©e, si vous ĂȘtes familier avec cette opĂ©ration. En fait, elle est Ă©quivalente Ă une corrĂ©lation croisĂ©e dans de nombreux cas.
Je pense que lâopĂ©ration de convolution sâapprend mieux par des exemples. Dans ce premier exemple, nous convoluons deux impulsions carrĂ©es ensemble :
Comme il sâagit simplement dâune intĂ©gration glissante, le rĂ©sultat est un triangle avec un maximum au point oĂč les deux impulsions carrĂ©es sâalignent parfaitement. Voyons ce qui se passe si nous convolvons une impulsion carrĂ©e avec une impulsion triangulaire :
Dans les deux exemples, nous avons deux signaux dâentrĂ©e (un rouge, un bleu), puis la sortie de la convolution est affichĂ©e. Vous pouvez voir que la sortie est lâintĂ©gration des deux signaux, lâun glissant sur lâautre. En raison de cette nature « glissante », la longueur de la sortie est en fait plus longue que celle de lâentrĂ©e. Si un signal contient M Ă©chantillons et lâautre N Ă©chantillons, la convolution des deux signaux peut produire N+M-1 Ă©chantillons. Cependant, des fonctions telles que numpy.convolve() permettent de spĂ©cifier si vous voulez la totalitĂ© du rĂ©sultat (max(M, N) Ă©chantillons) ou seulement les Ă©chantillons oĂč les signaux se chevauchent complĂštement (max(M, N) - min(M, N) + 1 si vous ĂȘtes curieux). Il nâest pas nĂ©cessaire de sâattarder sur ces dĂ©tails. Sachez simplement que la longueur de la sortie dâune convolution nâest pas seulement la longueur des entrĂ©es.
Alors pourquoi la convolution est-elle importante en DSP? Pour commencer, pour filtrer un signal, nous pouvons simplement prendre la réponse impulsionnelle de ce filtre et la convoluer avec le signal. Le filtrage FIR est simplement une opération de convolution.
Cela peut prĂȘter Ă confusion car nous avons mentionnĂ© prĂ©cĂ©demment que la convolution prend deux signaux et en sort un. Nous pouvons traiter la rĂ©ponse impulsionnelle comme un signal, et la convolution est un opĂ©rateur mathĂ©matique aprĂšs tout, qui opĂšre sur deux tableaux 1D. Si lâun de ces tableaux 1D est la rĂ©ponse impulsionnelle du filtre, lâautre tableau 1D peut ĂȘtre un morceau du signal dâentrĂ©e, et la sortie sera une version filtrĂ©e de lâentrĂ©e.
Voyons un autre exemple. Dans lâexemple ci-dessous, le triangle reprĂ©sente la rĂ©ponse impulsionnelle de notre filtre, et le signal :green:`vert` est notre signal filtrĂ©.
La sortie :red:`rouge` est le signal filtré.
Question : Quel type de filtre était le triangle ?
Réponse
Il attĂ©nue les composantes haute frĂ©quence du signal vert (câest-Ă -dire les transitions nettes du carrĂ©) et agit donc comme un filtre passe-bas.
Maintenant que nous commençons Ă comprendre la convolution, je vais vous prĂ©senter son Ă©quation mathĂ©matique. LâastĂ©risque (*) est gĂ©nĂ©ralement utilisĂ© comme symbole de la convolution :
Dans lâexpression ci-dessus, \(g(t)\) est le signal ou lâentrĂ©e qui est inversĂ©e et glisse sur \(f(t)\), mais \(g(t)\) et \(f(t)\) peuvent ĂȘtre intervertis et il sâagit toujours de la mĂȘme expression. En gĂ©nĂ©ral, le vecteur le plus court sera utilisĂ© comme \(g(t)\). La convolution est Ă©gale Ă une corrĂ©lation croisĂ©e, dĂ©finie comme \(\int f(\tau) g(t+\tau)\), lorsque \(g(t)\) est symĂ©trique, câest-Ă -dire quâil ne change pas lorsquâil est retournĂ© autour de lâorigine.
Conception de Filtres en Pythonï
Nous allons maintenant Ă©tudier une façon de concevoir nous-mĂȘmes un filtre FIR en Python. Bien quâil existe de nombreuses approches de la conception de filtres, nous utiliserons la mĂ©thode consistant Ă commencer dans le domaine frĂ©quentiel et Ă revenir en arriĂšre pour trouver la rĂ©ponse impulsionnelle. Car en fin de compte, câest ainsi que notre filtre est reprĂ©sentĂ© (par ses taps).
Vous commencez par créer un vecteur de votre réponse en fréquence souhaitée. Concevons un filtre passe-bas de forme arbitraire illustré ci-dessous:
Le code utilisé pour créer ce filtre est assez simple :
import numpy as np
import matplotlib.pyplot as plt
H = np.hstack((np.zeros(20), np.arange(10)/10, np.zeros(20)))
w = np.linspace(-0.5, 0.5, 50)
plt.plot(w, H, '.-')
plt.show()
hstack() est une façon de concaténer des vecteur en numpy. Nous savons que cela mÚnera à un filtre avec des taps complexes. Pourquoi ?
Réponse
Il nâest pas symĂ©trique autour de 0 Hz.
Notre objectif final est de trouver les prises de ce filtre afin de pouvoir lâutiliser. Comment obtenir les taps, Ă©tant donnĂ© la rĂ©ponse frĂ©quentielle? Eh bien, comment convertir le domaine frĂ©quentiel en domaine temporel? La FFT inverse (IFFT)! Rappelez-vous que la fonction IFFT est presque exactement la mĂȘme que la fonction FFT. Nous devons Ă©galement dĂ©caler la rĂ©ponse en frĂ©quence souhaitĂ©e avant la IFFT, puis dĂ©caler Ă nouveau la rĂ©ponse en frĂ©quence aprĂšs la IFFT (non, elles ne sâannulent pas toutes seules, vous pouvez essayer). Ce processus peut sembler dĂ©routant. Rappelez-vous simplement que vous devez toujours effectuer un FFTshift aprĂšs un FFT et un IFFshift aprĂšs un IFFT.
h = np.fft.ifftshift(np.fft.ifft(np.fft.ifftshift(H)))
plt.plot(np.real(h))
plt.plot(np.imag(h))
plt.legend(['réél','imag'], loc=1)
plt.show()
Nous allons utiliser les taps indiqués ci-dessus comme filtre. Nous savons que la réponse impulsionnelle consiste à tracer les taps, donc ce que nous voyons ci-dessus est notre réponse impulsionnelle. Prenons la FFT de nos taps pour voir à quoi ressemble réellement la réponse fréquentielle. Nous allons faire une FFT de 1 024 points pour obtenir une haute résolution :
H_fft = np.fft.fftshift(np.abs(np.fft.fft(h, 1024)))
plt.plot(H_fft)
plt.show()
Voyez comment la rĂ©ponse en frĂ©quence nâest pas trĂšs droite⊠elle ne correspond pas trĂšs bien Ă notre forme originale, si vous vous souvenez de la forme pour laquelle nous voulions initialement faire un filtre. Une des raisons principales est que notre rĂ©ponse impulsionnelle nâa pas fini de dĂ©croĂźtre, câest-Ă -dire que les cĂŽtĂ©s gauche et droit nâatteignent pas zĂ©ro. Nous avons deux options qui lui permettront de dĂ©croĂźtre jusquâĂ zĂ©ro :
Option 1: Nous « fenĂȘtrons » notre rĂ©ponse impulsionnelle actuelle de maniĂšre Ă ce quâelle dĂ©croisse vers 0 des deux cĂŽtĂ©s. Il sâagit de multiplier notre rĂ©ponse impulsionnelle par une « fonction de fenĂȘtrage » qui commence et se termine Ă zĂ©ro.
# AprĂšs avoir créé h en utilisant le code prĂ©cĂ©dent, crĂ©ez et appliquez la fenĂȘtre
window = np.hamming(len(h))
h = h * window
Option 2: Nous gĂ©nĂ©rons Ă nouveau notre rĂ©ponse impulsionnelle en utilisant davantage de points afin quâelle ait le temps de sâannuler. Nous devons ajouter de la rĂ©solution Ă notre vecteur original dans le domaine des frĂ©quences (appelĂ© interpolation).
H = np.hstack((np.zeros(200), np.arange(100)/100, np.zeros(200)))
w = np.linspace(-0.5, 0.5, 500)
plt.plot(w, H, '.-')
plt.show()
# (le reste du code est le mĂȘme)
Les deux options ont fonctionnĂ©. Laquelle choisiriez-vous? La deuxiĂšme mĂ©thode a permis dâobtenir plus de prises, mais la premiĂšre mĂ©thode a permis dâobtenir une rĂ©ponse en frĂ©quence qui nâĂ©tait pas trĂšs nette et dont le front descendant nâĂ©tait pas trĂšs raide. Il existe de nombreuses façons de concevoir un filtre, chacune ayant ses propres compromis. Beaucoup considĂšrent la conception de filtres comme un art.
Introduction Ă la Mise en Formeï
Nous allons prĂ©senter briĂšvement un sujet trĂšs intĂ©ressant au sein de la DSP: la mise en forme. Nous lâĂ©tudierons plus tard en profondeur dans son propre chapitre, voir Mise en Forme. Il est intĂ©ressant de le mentionner en mĂȘme temps que le filtrage, car la mise en forme est finalement un type de filtre, utilisĂ© dans un but spĂ©cifique, avec des propriĂ©tĂ©s spĂ©ciales.
Comme nous lâavons appris, les signaux numĂ©riques utilisent des symboles pour reprĂ©senter un ou plusieurs bits dâinformation. Nous utilisons un schĂ©ma de modulation numĂ©rique tel que ASK, PSK, QAM, FSK, etc., pour moduler une porteuse afin que les informations puissent ĂȘtre envoyĂ©es sans fil. Lorsque nous avons simulĂ© la QPSK dans le chapitre Modulation numĂ©rique, nous nâavons simulĂ© quâun seul Ă©chantillon par symbole, câest-Ă -dire que chaque nombre complexe que nous avons créé Ă©tait lâun des points de la constellation - câĂ©tait un symbole. En pratique, nous gĂ©nĂ©rons normalement plusieurs Ă©chantillons par symbole, et la raison est liĂ©e au filtrage.
Nous utilisons des filtres pour façonner la « forme » de nos symboles car la forme dans le domaine temporel modifie la forme dans le domaine frĂ©quentiel. Le domaine des frĂ©quences nous informe de la quantitĂ© de spectre/largeur de bande que notre signal utilisera, et nous voulons gĂ©nĂ©ralement la minimiser. Ce quâil est important de comprendre, câest que les caractĂ©ristiques spectrales (dans le domaine des frĂ©quences) des symboles de la bande de base ne changent pas lorsque nous modulons sur une porteuse; la bande de base est simplement dĂ©placĂ©e vers le haut en frĂ©quence alors que sa forme reste la mĂȘme, ce qui signifie que la quantitĂ© de bande passante quâelle utilise reste la mĂȘme. Lorsque nous utilisons 1 Ă©chantillon par symbole, cela revient Ă transmettre des impulsions carrĂ©es. En fait, la BPSK utilisant 1 Ă©chantillon par symbole est juste une onde carrĂ©e de 1 et -1 alĂ©atoires :
Et comme nous lâavons appris, les impulsions carrĂ©es ne sont pas efficaces car elles utilisent une quantitĂ© excessive de spectre:
Nous procĂ©dons donc Ă une « mise en forme » de ces symboles en forme de blocs afin quâils occupent moins de bande passante dans le domaine des frĂ©quences. Pour ce faire, nous utilisons un filtre passe-bas qui Ă©limine les composantes haute frĂ©quence de nos symboles. Vous trouverez ci-dessous un exemple de symboles dans les domaines temporel (en haut) et frĂ©quentiel (en bas), avant et aprĂšs lâapplication dâun filtre de mise en forme:
Notez la rapiditĂ© avec laquelle le signal chute en frĂ©quence. Les lobes secondaires sont infĂ©rieurs de 30 dB aprĂšs la mise en forme, soit 1 000 fois moins! Et surtout, le lobe principal est plus Ă©troit, donc moins de spectre est utilisĂ© pour le mĂȘme nombre de bits par seconde.
Pour lâinstant, sachez que les filtres de mise en forme les plus courants sont les suivants :
Filtre à cosinus surélevé
Filtre à racines cosinus surélevé
Filtre Sinc
Filtre gaussien
Ces filtres ont gĂ©nĂ©ralement un paramĂštre que vous pouvez ajuster pour diminuer la bande passante utilisĂ©e. La figure ci-dessous montre le domaine temporel et frĂ©quentiel dâun filtre cosinus surĂ©levĂ© avec diffĂ©rentes valeurs de \(\beta\), le paramĂštre qui dĂ©finit la pente de lâamortissement, souvent appelĂ© roll-off.
Vous pouvez voir quâune valeur plus faible de \(\beta\) rĂ©duit le spectre utilisĂ© (pour la mĂȘme quantitĂ© de donnĂ©es). Cependant, si la valeur est trop faible, les symboles du domaine temporel mettent plus de temps Ă revenir Ă zĂ©ro. En fait, lorsque \(\beta=0\), les symboles ne tombent jamais complĂštement Ă zĂ©ro, ce qui signifie que nous ne pouvons pas transmettre ces symboles dans la pratique. Une valeur de \(\beta\) autour de 0,35 est courante.
Vous en apprendrez beaucoup plus sur la mise en forme, y compris certaines propriétés spéciales que les filtres de mise en forme doivent satisfaire, dans le chapitre Mise en Forme.