Skip to content

Synthèse d'un filtre passe-bande ​

Ce tutorial montre le process complet pour synthétiser un filtre d'ordre N à partir d'un cahier des charges. La méthodologie décrite ici est applicable à n'importe quel filtre.

Cahier des charges ​

  • Type de filtre: passe-bande,
  • Technique de synthèse: Butterworth,
  • Bande passante : ω∈[900,1111] rad/s,
  • Bande rejetée : ω∈[0,500[∪[2000,+∞[ rad/s,
  • Atténuation maximale dans la bande passante: 3 dB,
  • Atténuation minimale dans la bande rejetée: 40 dB.

Code ​

Dans ce tutorial, nous utiliserons un fichier tools.py avec le code suivant :

py
import matplotlib.pyplot as plt

def plot_pzmap(ax, z, p):
    ax.plot(p.real,p.imag,'x')
    ax.plot(z.real,z.imag,'o')
    ax.set_xlabel("Re (.)")
    ax.set_ylabel("Im (.)")
    ax.axis("equal")
    ax.grid()

def plot_prototype(ax, type, wc, Tc, ws, Ts):
    xmin, xmax = ax.get_xlim()
    ymin, ymax = ax.get_ylim()

    if type == "LP":
        polygon_data1 = [[xmin,Tc], [wc,Tc], [wc,ymin], [xmin,ymin]]
        polygon_data2 = [[xmin,ymax], [xmin,1], [ws,1], [ws,Ts], [xmax,Ts], [xmax,ymax], [xmin,ymax]]
    if type == "BP":
        polygon_data1 = [[wc[0], ymin], [wc[0],Tc], [wc[1],Tc], [wc[1],ymin], [wc[0], ymin]]
        polygon_data2 = [[xmin,Ts], [ws[0],Ts], [ws[0],1], [ws[1],1], [ws[1],Ts], [xmax,Ts], [xmax,ymax], [xmin,ymax], [xmin,Ts]]

    options = {"fill": False,"closed": True,"color": 'b',"hatch": "/"} 
    patch1 = plt.Polygon(polygon_data1,**options)
    patch2 = plt.Polygon(polygon_data2,**options)

    ax.add_patch(patch1)
    ax.add_patch(patch2)
    ax.set_xscale('log')
    ax.set_yscale('log')
    ax.set_xlabel("pulsation (rad/s)")
    ax.set_ylabel("module")

Détermination du Gabarit Normalisé ​

Dans un premier temps, nous allons revenir à la conception d'un filtre passe-bas normalisé (ωc=1 rad/s). Pour réaliser cette étape de normalisation, nous allons utiliser la formule liant la pulsation normalisée ω et la pulsation dénormalisée ω^.

ω=ω^2−ω02ω^Δω

Application Numérique ​

  • Bande passante: Δω=1111−900=211 rad/s.
  • Pulsation centrale : ω0=900×1111=1000 rad/s.
  • Valeur du module en ω^=1111 rad/s: Tc=10−3/20=0.707,
  • Valeur du module en ω^=2000 rad/s: Ts=10−40/20=0.01.

En utilisant la formule, nous obtenons :

ωc=11112−100021111×211≈1 rad/sωs=20002−100022000×211≈7.11 rad/s

Représentation du Gabarit ​

Code ​

py
import matplotlib.pyplot as plt
from tools import plot_prototype

fig, axs = plt.subplots(1, 2, figsize=(10, 4)) 

# Bandpass filter
ax0 = axs[0]
ax0.set_xlim([100, 10000])
ax0.set_ylim([0.001, 2])
ax0.set_title("filtre BP")

wc = [900, 1111]
ws = [500, 2000]
Tc = 0.707
Ts = 0.01
plot_prototype(ax0, "BP", wc, Tc, ws, Ts)

# Normalized Lowpass filter
ax1 = axs[1]
ax1.set_xlim([0.1, 100])
ax1.set_ylim([0.001, 2])
ax1.set_title("filtre LP normalisé")

wc_norm = 1
ws_norm = 7.11
plot_prototype(ax1, "LP", wc_norm, Tc, ws_norm, Ts)
fig.tight_layout()
plt.show()

Courbes ​

Détermination de l'ordre ​

Il est possible de déterminer l'ordre du filtre passe-bas normalisé en utilisant le comportement asymptotique :

N=⌈−ln⁡(0.7070.01)ln⁡(17.11)⌉=3

Synthèse du Filtre Normalisé ​

Pour synthétiser le filtre, nous allons utiliser la technique de synthèse de Butterworth. La fonction de transfert du filtre normalisé peut s'exprimer sous la forme factorisée suivante (zpk):

Hn(s)=k∏l=13(s−zl)∏l=13(s−pl)

Il est possible d'obtenir les pôles pl et les zéros zl ainsi que le gain k du filtre normalisé en utilisant `scipy`:

python
z,p,k  =  butter(3, 1, "low", analog=True, output="zpk")

Nous obtenons les paramètres suivants:

  • zéros: zl={},
  • pôles: pl={−0.5+0.8660254j,−1,−0.5−0.8660254j},
  • gain: k=1

Dénormalisation du Filtre ​

Pour dénormaliser le filtre, nous allons opérer un mapping des pôles et zéros. Dans le cas d'un passe-bande, le mapping des pôles est donné par :

p^l=αpl±α2pl2−ω02
  • α=Δω/2=211/2=105.5 rad/s désigne la moitié de la bande passante,
  • pl correspond aux pôles du filtre normalisé,
  • p^l correspond aux pôles du filtre dénormalisé.

Après dénormalisation, nous obtenons un filtre d'ordre 2N=6. Pour conserver un gain maximum unitaire dans la bande passante, il est également nécessaire de changer le coefficient k de la manière suivante:

k^=k(Δω)N

Application Numérique ​

Après dénormalisation, nous obtenons un filtre d'ordre 6 avec les paramètres suivants :

  1. zéros: 3 zéros en 0 (filtre passe-bande d'ordre 6)

    • z^l={0,0,0},
  2. pôles: 3 paires de pôles complexes-conjugués

    • p^1=−47.943−911.424j et p^1∗=−47.943+911.424j,
    • p^2=−57.556−1094.155j et p^2∗=−57.556+1094.155j
    • p^3=−105.5−994.419j et p^3∗=−105.5+994.419j,
  3. gain k:

    • k^=1×(211)3=9393931

Vérification ​

Pour vérifier que le filtre dénormalisé respecte bien les contraintes du cahier des charges, une solution naturelle consiste à afficher la réponse fréquentielle du filtre dénormalisé.

H(s)=k^∏l=13(s−z^l)∏l=16(s−p^l)

Code ​

py
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import lti, butter 
from tools import plot_prototype

k2 = 9393931
z2 = [0.+0.j, 0.+0.j, 0.+0.j]
p2 = [ -47.943-911.374j, -105.5-994.369j, -47.943 +911.374j, -57.556+1094.106j, -105.5 +994.369j, -57.556-1094.106j]
H = lti(z2, p2, k2)
w, Hjw = H.freqresp()

fig = plt.figure()
w, Hjw = H.freqresp()
plt.loglog(w, np.abs(Hjw))
plt.xlabel("pulsation (rad/s)")
plt.ylabel("module")
ax = plt.gca()

wc = [900, 1111]
ws = [500, 2000]
Tc = 0.707
Ts = 0.01
ax.set_xlim([100, 10000])
ax.set_ylim([0.001, 2])
plot_prototype(ax, "BP", wc, Tc, ws, Ts)
fig.tight_layout()
plt.show()

Résultat ​