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: Δω=1111900=211 rad/s.
  • Pulsation centrale : ω0=900×1111=1000 rad/s.
  • Valeur du module en ω^=1111 rad/s: Tc=103/20=0.707,
  • Valeur du module en ω^=2000 rad/s: Ts=1040/20=0.01.

En utilisant la formule, nous obtenons :

ωc=11112100021111×2111 rad/sωs=20002100022000×2117.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)=kl=13(szl)l=13(spl)

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.8660254,1,0.50.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.943911.424j et p^1=47.943+911.424j,
    • p^2=57.5561094.155j et p^2=57.556+1094.155j
    • p^3=105.5994.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(sz^l)l=16(sp^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