Table des matières Python

Série de Fourier et filtrage d'un signal en créneaux

1. Définition de la série de Fourier

Les coefficients de Fourier d'un signal créneau sont :

Cn=-jnπ(1-(-1)n)

La série de Fourier s'écrit :

u(t)=n=12Re[Cnexp(jn2πTt)]

Les coefficients de Fourier sont définis par une fonction :

import math,cmath
import numpy as np
from matplotlib.pyplot import *

def cnCreneau(n):
    if n==0:
        return 0
    else:
        return -1j/(n*math.pi)*(1-(-1)**n)
            

Tracé du spectre en décibel :

def plotSpectre(cn,nmax):
    indices = np.arange(start=1,stop=nmax,step=2)
    n = indices.size
    spectre = np.zeros(n)
    for k in range(n):
        spectre[k] = 20*math.log10(abs(cn(indices[k]))) 
    stem(indices,spectre)
    xlabel('n')
    ylabel('AdB')
    grid()
figure(figsize=(8,4))
plotSpectre(cnCreneau,20)
            
figAfigA.pdf

La fonction suivante calcule la somme partielle de la série de Fourier, avec une période 1 :

def serie(cn,nmax,t):
    s = 0.0
    for k in range(1,nmax):
        z = cn(k)*cmath.exp(1j*2*math.pi*k*t)
        s += 2*z.real
    return s
            

Tracé de la somme partielle à N=20 :

def plotSomme(cn,nmax,step):
    t = np.arange(start=0.0,stop=2.0,step=step)
    n = t.size
    u = np.zeros(n)
    for k in range(n):
        u[k] = serie(cn,nmax,t[k])
    plot(t,u)
    xlabel('t')
    ylabel('u')
    grid()
figure(figsize=(8,4))
plotSomme(cnCreneau,20,0.001)
            
figBfigB.pdf

Cette somme partielle est très éloignée du signal créneau. Essayons N=100 :

figure(figsize=(8,4))
plotSomme(cnCreneau,100,0.001)
            
figCfigC.pdf

Il y a des oscillations à l'approche des discontinuités (phénomène de Gibbs). On peut essayer d'augmenter encore N, mais il faut aussi augmenter la fréquence d'échantillonnage du tracé car les oscilations sont de plus en plus rapides.

figure(figsize=(8,4))
plotSomme(cnCreneau,1000,0.0001)
            
figDfigD.pdf

Cela est très couteux en calculs car il y 1000x1000 termes à calculer.

On peut aussi obtenir le signal en appliquant la transformée de Fourier discrète inverse au spectre. Dans ce cas, un spectre à 1000 termes donne 1000 échantillons, ce qui ne permet pas de restituer les oscillations :

import numpy.fft
def calculerSignal(cn,nmax):
    indices = np.arange(start=0,stop=nmax,step=1)
    n = indices.size
    spectre = np.zeros(n,dtype=numpy.complex)
    for k in range(n):
        spectre[k] = cn(indices[k])
    u = numpy.fft.ifft(spectre)*nmax
    return u
u = calculerSignal(cnCreneau,1000)
figure(figsize=(8,4))
plot(u)
grid()
            
figEfigE.pdf

Le calcul est beaucoup plus rapide (algorithme FFT) mais le résultat correspond à un signal dont les hautes fréquences ont été atténuées, ce qui fait disparaitre le phénomène de Gibbs.

2. Filtrage

2.a. Méthode

On effectue un filtrage dans le domaine fréquentiel, c'est-à-dire en agissant sur les coefficients de Fourier. En pratique, le filtrage numérique du signal dans les sytèmes temps-réel se fait dans le domaine temporel (par convolution). Voir à ce sujet la page Filtre à réponse impulsionnelle finie..

Le filtre est défini par une fonction de transfert de la forme H(f), f étant la fréquence. Dans le cas présent, les fréquences sont définies par rapport à la fréquence du signal, qui vaut 1 par convention.

La fonction suivante calcule les coefficients de Fourier résultant du filtrage, en les multipliant par la valeur de la fonction de transfert aux fréquences correspondantes. La fréquence de l'harmonique de rang n est simplement n puis la fréquence du fondamental est 1.

def filtrage(H,cn_in):
    def cn_out(n):
        return H(1.0*n)*cn_in(n)
    return cn_out
                

2.b. Filtre passe-bas du premier ordre

La fonction de transfert d'un filtre passe-bas du premier ordre est :

H(s)=11+s

où la variable s est définie par :

s=jffc

Pour une fréquence de coupure de 10 :

def H(f):
    x = f/10
    s = 1j*x
    return 1.0/(1+s)
                

Effet du filtre sur le créneau :

cnSortie = filtrage(H,cnCreneau)
figure(figsize=(8,4))
plotSomme(cnSortie,100,0.001)
                
figFfigF.pdf

On voit que la série de Fourier peut être arrêtée au rang 100 en raison de l'atténuation des hautes fréquences; le phénomène de Gibbs est à peine visible.

Dans le cas suivant, toutes les fréquences du créneau sont dans le domaine intégrateur du filtre :

def H(f):
    x = f/0.1
    s = 1j*x
    return 1.0/(1+s)
cnSortie = filtrage(H,cnCreneau)
figure(figsize=(8,4))
plotSomme(cnSortie,100,0.001)
                
figGfigG.pdf

2.c. Filtres passe-bas de Butterworth

Un filtre de Butterworth d'ordre n a le gain suivant (à une constante multiplicative près) :

|H|=11+x2n

Le gain présente donc une décroissance de -20n dB par décade pour x>1.

Voir la page Filtres passe-bas de Butterworth pour le calcul de la fonction de transfert et pour un exemple de réalisation électronique.

La fonction de transfert d'un filtre passe-bas de Butterworth d'ordre 2p est :

H(s)=1(s2+c0s+1)(s2+c1s+1)...(s2+cps+1)

avec :

ck = 2sin(πn(k+12))
def butterworth(f,fc,p):
    s = 1j*f/fc
    h = 1.0
    n = 2*p
    for k in range(p):
        c = 2*math.sin(math.pi/n*(k+0.5))
        h = h*(s**2+c*s+1)
    return 1.0/h
                

Diagramme de Bode d'un filtre de Butterworth d'ordre 2 :

def H(f):
    return butterworth(f,1,1)
def reponseFrequentielle(H,start,stop):
    freq = np.logspace(start=start,stop=stop,num=1000)
    n = freq.size
    gdb = np.zeros(n)
    phi = np.zeros(n)
    for k in range(n):
        h = H(freq[k])
        gdb[k] = 20*math.log10(abs(h))
        phi[k] = cmath.phase(h)
    return [np.log10(freq),gdb,phi]
[logf,gdb,phi] = reponseFrequentielle(H,-2,2)
figure(figsize=(8,4))
plot(logf,gdb)
xlabel('log(f)')
ylabel('GdB')
grid()
                
figHfigH.pdf
figure(figsize=(8,4))
plot(logf,phi)
xlabel('log(f)')
ylabel('phi')
grid()
                
figIfigI.pdf

Voici l'effet sur le spectre d'un signal en créneaux :

cn_out = filtrage(H,cnCreneau)
figure(figsize=(8,4))
plotSpectre(cn_out,20)
                
figJfigJ.pdf

et sur sa représentation temporelle :

figure(figsize=(8,4))
plotSomme(cn_out,100,0.001)
grid()
                
figKfigK.pdf

Le signal de sortie n'est pas très loin d'une sinusoïde. Comme on le voit sur le spectre, il y a environ 30 dB entre le fondamental et l'harmonique de rang 3.

La même chose avec un filtre de Butterworth d'ordre 4 :

def H(f):
    return butterworth(f,1,2)
cn_out = filtrage(H,cnCreneau)
figure(figsize=(8,4))
plotSpectre(cn_out,20)
                
figLfigL.pdf
figure(figsize=(8,4))
plotSomme(cn_out,100,0.001)
grid()
                
figMfigM.pdf

L'écart entre le fondamental et l'harmonique de rang 3 est supérieur à 40 dB et la sortie est pratiquement sinusoïdale. Ce type de filtrage peut être utile pour transformer un signal numérique TTL en sinusoïde.

Creative Commons LicenseTextes et figures sont mis à disposition sous contrat Creative Commons.