Comment supprimer une composante fréquentielle parasite ?

NIVEAU 2

Objectifs

  • définir ce qu’est un traitement linéaire du signal
  • différentier un filtre à moyenne mobile (FIR) d’un filtre récursif (IIR)
  • régler les paramètres d’un filtre à moyenne mobile simple
  • mettre en œuvre un dispositif de filtrage temps réel d’un signal parasité

Pré-requis

Filtres numériques

Les filtres permettent de sélectionner des intervalles de fréquences à supprimer ou à conserver dans un signal. On supprime les fréquences aigues d’un signal sonore par exemple.

Les premiers filtres que vous avez étudié sont des filtres analogiques, réalisés :

  • soit avec des composants uniquement passifs (résistances, inductances, capacités), on parle de filtres analogiques passifs
  • soit avec des composants actifs également (amplificateurs linéaires intégrés), on parle de filtres analogiques actifs. On peut citer par exemple les structures de Rauch ou de Sallen-Key (de deuxième ordre) ou bien encore des filtres universels (type UAF42)
  • soit avec des composants actifs à capacité commutée, on parle de filtres à capacités commutées

Les premiers filtres sont difficilement reconfigurables. En effet, le choix des composants passifs imposent les diverses fréquences caractéristiques et le type de filtre. Les derniers cités, les filtres à capacités commutées, ont la particularité d’avoir une fréquence de coupure ajustable en fonction d’un signal d’horloge externe. Cependant, la plupart du temps, ils ne permettent que de réaliser des filtres passe-bas.

Si l’on souhaite rendre totalement reconfigurable un système de filtrage, il faut alors passer par le monde du numérique.

Principe de fonctionnement d’un filtre numérique

Ce type de système numérique, ici un filtre numérique, nécessite de travailler sur des échantillons numériques du signal d’entrée et restitue en sortie un échantillon numérique.

Afin de pouvoir réaliser cette opération de filtrage sur des signaux réels (et donc analogiques), il est indispensable que ce traitement numérique de l’information soit accompagné de modules de conversions analogique-numérique (ADC – Analog-Digital Converter) et numérique-analogique (DAC – Digital-Analog Converter).
Il faudra donc mettre en oeuvre la chaîne d’acquisition représentée dans la figure suivante pour pouvoir réaliser ce système numérique.

Le programme de traitement numérique devra alors réaliser les tâches suivantes (voir également figure suivante) :

  • Echantilloner le signal d’entrée, à intervalle régulier
  • Effectuer le calcul de la sortie à partir du nouvel échantillon, des précédents (stockés en mémoire) et des coefficients du filtre
  • Convertir la valeur de sortie de ce module de traitement en valeur analogique

Selon le type de filtre à réaliser, en particulier de son ordre, le temps de calcul de la phase de traitement peut différer d’un filtre à l’autre. Il faudra alors garantir que la période d’échantillonnage est suffisamment longue pour que le système ait le temps de réaliser toutes les étapes précédentes.

Structure d’un filtre numérique

Il existe 2 types de filtres permettant de travailler sur des échantillons réels :

  • les filtres non-récursifs ou à réponse impulsionnelle finie (FIR – Finite Impulse Response)
  • les filtres récursifs ou à réponse impulsionnelle infinie (IIR – Infinite Impulse Response)

Filtre à réponse impulsionnelle finie

Ces filtres non-récursifs ont une structure de ce type :

Chacun des nouveaux échantillons de sortie est calculée en fonction de \(M\) échantillons d’entrée, auxquels on associe des coefficients permettant de donner la réponse impulsionnelle souhaitée au filtre (i.e. sa réponse en fréquence… tout est qu’une question de référentiel de travail – temporel ou fréquentiel).

Ainsi, les échantillons de sortie sont régis par l’équation suivante : \(s[k] = s_k = \sum_{i=0}^{M-1} \alpha_i \cdot e[k-i]\)

L’ordre d’un tel filtre est \(M\).

La transformée de Fourier d’un tel filtre est alors donnée par la relation suivante : \(H(\nu) = \sum_{p=0}^{M-1} \alpha_i \cdot e^{-j 2 \pi p \nu}\) o$ \(\nu\) est la fréquence réduite égale à \(\nu = f/F_e\) où \(F_e\) est la fréquence d’échantillonnage.

Filtre à réponse impulsionnelle infinie

Ces filtres récursifs ont une structure de ce type :

https://fr.wikipedia.org/wiki/Filtre_%C3%A0_r%C3%A9ponse_impulsionnelle_infinie

Contrairement à la précédente structure, ces filtres dits récursifs calculent leurs échantillons de sortie à la fois en se basant sur une série de \(M\) échantillons d’entrée, mais en se basant également sur une série des \(N\) précédents échantillons de sortie.

La sortie est alors calculée de cette manière : \(s[k] = s_k = \sum_{i=0}^{M-1} \alpha_i \cdot e[k-i] + \sum_{p=1}^{N} \beta_p \cdot s[k-p]\)

L’ordre d’un tel filtre est donné par le maximum entre \(N\) et \(M\).

Il est a noter que ces filtres permettent généralement d’obtenir des ordres bien inférieurs aux filtres non-récursifs, pour un gabarit quasi-identique (i.e. des fréquences caractéristiques similaires pour une fréquence d’échantillonnage égale et des gains identiques dans les différentes zones utiles du gabarit). Ceci permet donc de gagner en temps de calcul. Cependant, ce type de filtre est plus difficile à mettre en oeuvre par leur rebouclage qui peut entrainer une instabilité.

Coefficients et calculs associés

Les coefficients utilisés pour les différents filtres sont des nombres réels (codés sur des variables de type double en C, par exemple, avec une certaine dynamique – on ne parlera pas ici des possibles erreurs de calcul liées à la précision du codage – voir module de calcul scientifique).

Les microcontroleurs industriels “basiques” (type PIC, par exemple, que vous avez utilisé en 1A du cycle ingénieur) ne possèdent pas de calculateurs à virgule flottante, implémentés dans leur structure. Ainsi, les opérations sur les nombres autres qu’entiers doivent passer par une étape logicielle, qui prend énormément de place en mémoire et qui est très gourmande en temps de calcul.

Pour pouvoir traiter ces calculs, les microcontroleurs plus “haut de gamme” possèdent des unités de calcul à virgule flottante. Certains sont également équipés de structure de type DSPDigital Signal Processing – rendant l’opération de multiplication et accumulation (telle que celle nécessaire pour les filtres numériques détaillés précédemment) réalisable en un seul cycle d’instruction.

C’est le cas du microcontroleur STM32L476RG présent sur la carte d’étude Nucléo que vous utilisez. Associé, par la suite, à la bibliothèque “mbed-dsp.h”, il est possible de réaliser des filtres assez conséquents à des fréquences d’échantillonnage acceptables pour le traitement de la plupart des signaux (par exemple, sonores) : un filtre à moyenne mobile sur 128 points à une fréquence d’échantillonnage de 50~kHz, par exemple.

Rappel : Transformée de Fourier et échantillonnage

Le passage au numérique impose quelques règles.

La première vient de la nécessité d’échantillonner le signal analogique en une série de données numériques. Il est donc indispensable de respecter le critère de Nyquist-Shannon, à savoir que la fréquence d’échantillonnage choisie doit être deux fois supérieure à la fréquence maximale du signal à analyser. Il est également important d’utiliser un filtre d’anti-repliement de spectre (passe-bas) avant d’utiliser un système numérique.

Signal et transformée de Fourier

Si \(x(t)\) est un signal analogique décrivant l’évolution d’une grandeur physique dans le temps, sa transformée de Fourier est alors donnée par la formule : \(\tilde{x}(\nu) = \int_{-\infty}^{+\infty} x(t) e^{-j 2 \pi \nu t} dt \), où \(\nu\) est la fréquence réduite (\(\nu = f / F_e\)).

On peut remarquer que le spectre d’un signal de ce type – réel à temps continu – est à symétrie hermitienne (module paire, phase impaire).

Signal échantillonné et transformée de Fourier

L’échantillonnage à une fréquence \(F_e\) (ou une période \(T_e\)) du signal précédent, donne un nouveau signal contenant des informations numériques discrétisées notées \(x_D\), telles que \(x_D[n] = x(n \cdot T_e) = x_n\).

Son spectre est celui du signal initial \(x(t)\) périodisé, de période 1 avec \(\nu\) la fréquence réduite (\(\nu = f / F_e\)). Il est donné par la formule suivante : \(\tilde{x}_D[\nu] = \sum_{k = -\infty}^{+\infty} x[k] \cdot e^{-j 2 \pi \nu k}\).

On peut remarquer que le spectre d’un signal discret est continu et périodique.

Transformée de Fourier échantillonnée

La transformée de Fourrier échantillonnée (ou TFD) d’un signal discret est un échantillonnage de la transformée de Fourier du signal initial.

On peut remarquer que la TFD d’un signal échantillonné sur N points est également échantillonnée sur N points, allant d’une fréquence de 0 (incluse) à \(F_e\) (exclue).

Mise en oeuvre d’un filtre numérique

Fonctions indispensables

Comme on l’a vu précédemment, pour pouvoir mettre en oeuvre un filtre numérique, il est indispensable de suivre les étapes suivantes :

  • Echantilloner le signal d’entrée, à intervalle régulier
  • Effectuer le calcul de la sortie à partir du nouvel échantillon, des précédents (stockés en mémoire) et des coefficients du filtre
  • Convertir la valeur de sortie de ce module de traitement en valeur analogique

Il est donc nécessaire de mettre en oeuvre les éléments suivants :

Enfin pour pouvoir vérifier le bon fonctionnement du filtre, il peut être intéressant de caractériser son temps de réponse ainsi que sa réponse en fréquence (voir tutoriel Caractériser un traitement numérique).

Code de base – Mode Suiveur

Une première étape à valider avant d’intégrer la partie filtrage numérique est la mise en oeuvre des deux modules de conversion : analogique-numérique (ADC) et numérique-analogique (DAC).

#include "mbed.h"
#define TE          0.00003
#define SAMPLE_RATE  1/TE

void convert(void);

/* E/S */
AnalogIn    mesure(A0);         // A0 on Arduino Header
AnalogOut   analog_out(PA_5);

/* Timer a repetition */
Ticker tik;

float in, out;

int main() {
    /* Association de la fonction convert à un timer */
    tik.attach(&convert, TE);
    
    while(1) {
    }
}

void convert(void){
    in = mesure.read();    // Lecture de l'entree analogique
    out = in;              // Calcul de la sortie
    analog_out.write(out); // Ecriture de la sortie analogique
}

Dans cet exemple, le signal de sortie recopie le signal d’entrée. Cela permet de vérifier le bon fonctionnement de ces deux étages de conversion, avant même l’ajout d’un calcul intermédiaire.

La relation entre l’entrée et la sortie est alors du type : \(s[n] = e[n]\).

Dans le code précédent, on peut également noter que l’acquisition du signal d’entrée (nommé mesure) et de la restitution de ce même signal vers la sortie (signal nommé analog_out) se fait à intervalle régulier (ici 30 us – soit une fréquence d’échantillonnage de 33kHz) grâce à l’utilisation d’un module de gestion du temps par interruption, le Ticker tik.

Exemple 1 : dérivateur

A présent que la structure de base est opérationnelle, on peut ajouter l’élément central : le filtre !

Pour cela, il faut définir les différents blocs nécessaires au calcul des différents éléments de la sortie.
Dans le cadre d’un dérivateur, l’opération à réaliser est la suivante : \(s[k] = \frac{1}{T_e} \cdot (e[k] – e[k-1])\). Il est donc nécessaire d’introduire des variables supplémentaires pour stocker la valeur des échantillons précédents.

float e[2] = {0};

Ensuite, on remplace la ligne de calcul par celle qui correspond au dérivateur.

void convert(void){
    in = mesure.read();    // Lecture de l'entree analogique
    e[0] = e[1];
    e[1] = in;
    out = (e[1] - e[0]);      // Calcul de la sortie
    analog_out.write(out); // Ecriture de la sortie analogique
}

Il est possible de raccourcir un peu le temps de calculs en supprimant le passage par la variable in.

On pourra alors s’intéresser au temps de calcul de ce filtre, à sa réponse à un signal triangulaire, puis à sa réponse en fréquence et enfin à sa réponse impulsionnelle…

Exemple 2 : moyennage mobile

Un filtre à moyenne mobile permet de faire la moyenne au cours du temps sur un nombre fini \(N\) d’échantillons. Ainsi, la sortie \(s[k]\) d’un tel filtre peut être décrite, en fonction des échantillons d’entrée \(e[k]\) par l’équation suivante : \(s[k] = 1/N \cdot \sum_{p = 0}^{N-1} e[k-p]\).

Comme pour l’application précédente, on voit ici qu’il faudra stocker les valeurs de \(N\) échantillons en mémoire. Pour cela, on définit un tableau de \(N\) cases de nombres réels (float en C).

Ce tableau devra être mis à jour à chaque nouvel échantillon entrant (i.e. à chaque nouvel échantillonnage).

On propose alors deux codes possibles.

Code 1

...
#define NB_POINT    256
...
DigitalOut clk_test(D10);
float measT[NB_POINT] = {0};
float somme;
...
void convert(void){
    clk_test = 1;
    /* FILTRE MOYENNEUR */
    somme = 0;
    for(i = 0; i < NB_POINT-1; i++){
        measT[i] = measT[i+1];
        somme += measT[i];
    }
    measT[NB_POINT-1] = analog_in.read(); // Nouvel echantillon
    somme += measT[NB_POINT-1]; 
    somme = somme / NB_POINT;             // Calcul de la sortie
    analog_out.write(somme);              // Envoi en sortie
    clk_test = 0; 
}

On pourra également remarquer que le signal de sortie est en retard par rapport au signal d’entrée…

Code 2

...
#define NB_POINT    256
...
DigitalOut clk_test(D10);
float measT[NB_POINT] = {0};
float somme = 0;
int indice;
...
void convert(void){
    clk_test = 1;
    /* FILTRE MOYENNEUR */
    if(indice == NB_POINT-1)  indice = 0;
    else indice++;
    somme = somme * NB_POINT;
    somme -= measT[indice];
    measT[indice] = analog_in.read();     // Nouvel echantillon
    somme += measT[indice];
    somme = somme / NB_POINT;             // Calcul de la sortie
    analog_out.write(somme);              // Envoi en sortie
    clk_test = 0; 
}

Démonstration TF – moyenne mobile

Un peu de maths… sur le filtre à moyenne mobile !

On vient de voir que la sortie \(s[k]\) d’un tel filtre, sur \(N\) points, peut être décrite, en fonction des échantillons d’entrée \(e[k]\) par l’équation suivante : \(s[k] = 1/N \cdot \sum_{p = 0}^{N-1} e[k-p]\).

La réponse en fréquence d’un tel filtre s’écrit alors : \(H(\nu) = \sum_{k = 0}^{N-1} \frac{1}{N} \cdot e^{-j 2 \pi k \nu}\).

Démo (pour rappel \(\nu = \frac{f}{F_e}\), fréquence réduire avec \(F_e\) la fréquence d’échantillonnage) :

\(H(\nu) – H(\nu) \cdot e^{-j 2 \pi \nu} = \sum_{k = 0}^{N-1} \frac{1}{N} \cdot e^{-j 2 \pi k \nu} – \sum_{k = 0}^{N-1} \frac{1}{N} \cdot e^{-j 2 \pi (k + 1) \nu}\) \(H(\nu) – H(\nu) \cdot e^{-j 2 \pi \nu} = \sum_{k = 0}^{N-1} \frac{1}{N} \cdot e^{-j 2 \pi k \nu} – \sum_{k = 1}^{N} \frac{1}{N} \cdot e^{-j 2 \pi k \nu}\) \(H(\nu) – H(\nu) \cdot e^{-j 2 \pi \nu} = \frac{1}{N} \cdot (1 – e^{-j 2 \pi N \nu})\) \(H(\nu) = \frac{1}{N} \cdot \frac{1 – e^{-j 2 \pi N \nu}}{1 – e^{-j 2 \pi \nu}}\)

Ainsi, \(H(\nu) = \frac{1}{N} \cdot \frac{sin(\pi \nu N)}{sin(\pi \nu)} \cdot e^{-j 2 \pi \nu (N-1)/2}\)

On s’intéresse pour la suite au module de ce nombre (pour pouvoir tracer le diagramme de Bode en gain de ce filtre).

Ainsi, \(A(\nu) = |H(\nu)|\)

On obtient : \(A(\nu) = \frac{1}{N} \cdot |\frac{sin(\pi \nu N)}{sin(\pi \nu)}|\)

En traçant \(A(\nu)\) en fonction de \(\nu\) pour différentes valeurs de N, on obtient une courbe de ce type :

On peut remarquer que selon la valeur de \(N\) utilisée, on obtient des fréquences particulières où l’amplification est nulle.

Code MATLAB utilisé :

clear variables

N1 = 8;
N2 = 16;
N3 = 32;

f = linspace(0,1,1001);
A1 = 1/N1 * abs(sin(pi * N1 .* f)./sin(pi .* f));
A2 = 1/N2 * abs(sin(pi * N2 .* f)./sin(pi .* f));
A3 = 1/N3 * abs(sin(pi * N3 .* f)./sin(pi .* f));

figure(1);
plot(f,A1, f, A2, f, A3);
xlabel('Frequence reduite - v');
ylabel('Amplification A(v)');
legend('N = 8','N = 16','N = 32');

Tutoriel lié

MInE Prototyper Prototyper avec Nucleo et MBED

Nucleo – Supprimer une fréquence parasite