Filtre numerique RII, RIF….digital filter...FFT...atmega328, ESP32

khadim_mbengue:
Pour une fréquence d’entrée de 50hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes

Pour une fréquence d’entrée de 500Hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes

kadhim, impose un dephasage et mesure un truc extravagant ? ? ? ? ? ? ? ? ? ? ? ?
sans conclusions ? ? ? ? ?

pour saliou
Comparer l’atténuation des filtres qui n’ont pas le même gain statique, n’est pas très pertinent.
alors qaue pour une frenquence tres inferieure à 50Hz nomalement, un filtre calissique devrait avoir un gain unitaire.
D'ailleurs, ce serait bien, qu’il y a une courbe théorie (deuxième ordre par butterwotrh exemple) pour faire des comparaisons
De plus, si on avait les 3 courbes sur le même graph, une comparaison serait plus facile à observer.

Pour les filtres FIR correspondront à l’équation suivante :

Le gain statique à 0Hz est pour z=1, donc pour la somme des coefficients b. d’ailleurs cette somme, donne un gain statique unitaire théoriquement. Mais lorsque le nombre de coefficient 2M+1 est faible, cela s provoque une erreur statique.
Donc, il faut le corriger sinon la comparaison entre les FIR de differents coefficients ne sont pas viables.

il faut faire les mesures jusque fe/2 pour montrer le phenomene de rebond de l'attenuation appelé gibbs

Je suis ce post régulièrement, car la vulgarisation des filtres numériques permet de les appliquer facilement et de connaitre les limites du processeur en audio. Et j’ai du mal aussi de passer de la théorie à la pratique.

Mais, il a un problème de neurones ce kadhim pour faire des mesures qui ne correspond pas à ce qu’il doit mesurer donc que son programme n’est pas correcte.
En plus, à force de corriger son post precedent, on ne comprend plus les commentaires qui lui sont fait.
Pas sur qu’il maitrise les tenants et les aboutissants de ce qu’il fait.

Pareil pour ce saliou, cela ne le gêne pas d’avoir des gains statiques différents.
Tous les 2, ils font des choses sans réfléchir à leurs résultats, sans synthèse.
mais, le programme de saliuo sur le filtre passe haut avec les complexes sont top.

pas sur qu’il ait le niveau d’écrire sur ce post, ce sont plutot des exécutants.

mais, il est vrai que le filtrage numérique demande une base mathématique importante….

filtres RIF passe-bas
Nous allons d’abord faire quelques exemples avec différents coefficients (M=5, M=9, M=23) sans la troncature Hamming. Ensuite nous ferons d’autres exemples avec les mêmes coefficients mais avec la troncature de Hamming qui nous permettra de résoudre le problème de rebonds (Phénomène de Gibbs).

Voici le code complet:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>

#define PWM3   3      //   timer2   
#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;
         
float  out=0;
float entreeMax;
float outMax;
float attenuation;

float outaverage;
float outaverage1;

float  fe=1000;  //frequence d'echantilonnage 
float  fc=50;  //frequence de coupure desiréé 
float gain=0;
/*
byte M=5;               //nombre impaire de coefficients desirées 
float  entree[5];       //declaration de la table des entrées
float  b[5];   // table des coefficients
*/
/*
byte M=9;               //nombre impaire de coefficients desirées 
float  entree[9];       //declaration de la table des entrées
float  b[9];   // table des coefficients
*/

byte M=23;               //nombre impaire de coefficients desirées 
float  entree[23];       //declaration de la table des entrées
float  b[23];   // table des coefficients


           
void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
 
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

// calcul coefficient filtre FIR
for (byte i = 0; i < M; i++) {b[i]=(sin(2*fc*PI*(i-((M-1)/2))/fe)/(((i-((M-1)/2)))*PI));}   
b[((M-1)/2)]=2*fc/fe;            //sinus cardinal(0)
//Les coefficients et le gain sans la troncature hamming
//for (byte i = 0; i < M; i++) {b[i];gain=gain+b[i];}  

//ajustement des coefficients avec la troncature hamming
for (byte i = 0; i < M; i++) {b[i]=b[i]*(0.54+0.46*cos(PI*(i-((M-1)/2))/M));gain=gain+b[i];}
for (byte i = 0; i < M; i++) {Serial.print(b[i],3);Serial.print(";");} 
Serial.println("gain= ");
Serial.print(gain,3);Serial.print(";"); 
//Gain pour différents nombres de coefficients sans la troncature hamming
//M=5  gain=0.48
//M=9  gain=0.81
//M=23  gain=1.16

//Gain pour différents nombres de coefficients avec la troncature hamming
//M=5  gain=0.41
//M=9  gain=0.68
//M=23  gain=1.09

}


// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
entree[M-1]=analogRead(A0);         //convertisseur 10 bits sous 5V   https://www.gammon.com.au/adc
entree[M-1]=entree[M-1]/4;          //mise à l'echelle 10 bits en entrée et 8 bits en sortie+chiffre significatif   

out=0;     //remise à zero du calcul du filtre RIF
for (byte i = 0; i < M; i++) {out=(b[i]*entree[i]+out);/*Serial.print(entree[i]);Serial.print(";");*/}   
for (byte i = 0; i < M; i++) {entree[i]=entree[i+1];}       
//Serial.print(out,0);
//Serial.println(";");                                                             
if (out<0) {out=0;}
if (out>255) {out=255;}                 
analogWrite(PWM3,out);        //   

temps++;   
outaverage=outaverage+out;          //calcul de la valeur moyenne     
//rafraichissement toutes les 0.2s           
if (temps>=200)   {entreeMax=127;outMax=127;temps=0;outaverage1=outaverage/200;outaverage=0;}
if (entreeMax <entree[0]) {entreeMax = entree[0]; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }
digitalWrite(LED13,LOW); 
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {

 
lcd.setCursor(0,0);
lcd.print("Gain= ");
lcd.print(gain,2);   
lcd.setCursor(0,1);   
lcd.print("ordre");
lcd.print(M);
lcd.setCursor(0,2);
lcd.print("out_average");
lcd.print(outaverage1,0);   //valeur moyenne sur 0.2seconde

   lcd.setCursor(0,3);
  attenuation=((outMax-outaverage1)/(entreeMax-127));
    lcd.print("Attenuat= ");
  lcd.print(attenuation,2);   

} // fin loop

Les résultats des simulations effectuées sont représentés dans des tableaux excel pour mieux observer les rebonds et l’effet de la troncature de Hamming.

-sans la troncature Hamming

-avec la troncature Hamming

A 170 Hz pour M=23 nous avons ceci:

Sans la fenêtre de Hamming on voit qu’il y’a bien des rebonds entre 120 et 500 Hz: le phénomène Gibbs. Pour minimiser ces parasites, nous avons utilisé la fenêtre de Hamming qui améliore considérablement cette anomalie comme vous pouvez l’appercevoir sur la 2eme courbe.
J’ai oublié de rajouter cette ligne pour avoir un gain unitaire:

out=out/gain;

Bonjour,
Désolé je suis un débutant sur arduino et voici mon nouveau programme qui factionne.

#include <Wire.h>
///#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <math.h>

#define A1 20   // transformateur de tension
#define A2 21   //transformateur de courent
#define LED13    13      

LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

              float T1,T0;  
              float pi;
              float temps;           //
              float Time0, Time1;   // peridiode des signaux
              float dephasage, dephasagerad,fp;
              float fe;                         //frequence d'entrée
  

void setup()
{
  pinMode(LED13, OUTPUT);
  pinMode(A1, INPUT);
  pinMode(A2,INPUT);
  
  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  lcd.begin(20, 4);                   //modifier pour un afficheur 16x2
  
  Serial.begin(9600);

  attachInterrupt(0, func_k, RISING);   // broche2

  T0 = millis();
  attachInterrupt(1, powerfactor, RISING);   // broche3
  
 // TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet 
}

// faisons deux interruotion pour pouvoir calculer le temps

void func_k() // la fonction appelée par l'interruption externe n°0
{
Time0=(micros()-T0);  //pour mesurer la perriode du signal broche 2
T0= micros();
T1= T0;
}

void powerfactor() // la fonction appelée par l'interruption externe n°1
{
temps=Time0-micros()+T1;  //pour mesurer l'ecart du temps
}

void callback()      
{
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption                                     
digitalWrite(LED13,LOW);  
}//fin routine

 

void loop()
{  
    
 fe=1000000/Time0;                        
dephasage=((360*temps)/Time0);

dephasagerad=((dephasage*PI)/180);
fp = cos(dephasagerad);// factor power

lcd.setCursor(0,0);
lcd.print("Frequence=");     //
lcd.print(fe);
lcd.print("   ");

lcd.setCursor(0,1);
lcd.print("Delta T=");
lcd.print(temps,0);
lcd.print("      ");

lcd.setCursor(0,2);
lcd.print("Dephasage=");
lcd.print(dephasage);
lcd.print("   ");



lcd.setCursor(0,3);
lcd.print("Factor Power=");
lcd.print(fp,3);
lcd.print("   ");

} // fin loop

La sumilation:
90° avec 50Hz

45° avec 500Hz

On a fait quelques tests pour vérifier le bon fonctionnement du programme.

Pour une fréquence d’entrée de 50Hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes:

Pour une fréquence d’entrée de 500Hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes:

L’analyse de ces deux tableaux montre que la précision de mesure ne dépend pas de la fréquence .

khadim_mbengue:
L'analyse de ces deux tableaux montre que la précision de mesure ne dépend pas de la fréquence .

khadim
Comment fais tu l’analyse avec les tableaux et la précision ???????

Comment calculer l’erreur et la précision de la mesure de la fréquence ? puis l’erreur sur le dephasage
l'erreur depend du choix du compteur time qui est plus ou moins 1 microseconde avec les equations suivantes :
ErreurdeFe(Hz)=10^6/time0-10^6/(time0+1)= 10^6/ time0^2
Précision= ErreurdeFe/Fe=
Erreur Déphasage(°)=(360°/ time0).(temps- temps+1)= (360°/ time0)

Exemple pour 50Hz ou time0 en micro seconde donc 20 000 plus ou moins 1 microseconde.
Erreur de Fe= 10^6/ 20 000^2=0.0025Hz
Précision=0.0025Hz/50Hz*100=0.0005%
Erreur Déphasage(°)=360°/20000=0.018°
donc pas de probléme à ce niveau de frequence.

Exemple pour 500Hz ou time0 en micro seconde donc 2 000µs
Erreur de Fe= 10^6/ 2 000^2=0.25Hz
Précision=0.25Hz/500Hz*100=0.05%
Erreur Déphasage(°)=360°/2000=0.18°

Pour 5KHz, l’erreur de la fréquence passe à 25Hz, donc à 0.5% et l’erreur sur le déphasage à 1.8°.
cela commence à etre critique

Donc l’erreur sur le déphasage est proportionnelle à la fréquence et celle de l’erreur de la mesure de la frequence est inversement quadratique.
Mais si l’on désire, vérifier les erreurs, des calculs precedents , il faut mesurer les variations de Fe(Hz) mini et maxi, pareil sur le déphasage.
C’est très facile de le faire en programmation.

Mais une autre question plus cruciale :
Quelle est la fréquence maximale mesurable que peux faire ton programme ?
Mais tes méthodes , sur le forum…..cela devrait intéresser du monde….

A propos de mon dernier post, j’ai rajouté la dernière ligne que j’avais oublié pour ajuster le gain.

Voici le nouveau code complet:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>

#define PWM3   3      //   timer2   
#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;
         
float  out=0;
float entreeMax;
float outMax;
float attenuation;

float outaverage;
float outaverage1;

float  fe=1000;  //frequence d'echantilonnage 
float  fc=50;  //frequence de coupure desiréé 
float gain=0;
/*
byte M=5;               //nombre impaire de coefficients desirées 
float  entree[5];       //declaration de la table des entrées
float  b[5];   // table des coefficients
*/
/*
byte M=9;               //nombre impaire de coefficients desirées 
float  entree[9];       //declaration de la table des entrées
float  b[9];   // table des coefficients
*/

byte M=23;               //nombre impaire de coefficients desirées 
float  entree[23];       //declaration de la table des entrées
float  b[23];   // table des coefficients


           
void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
 
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

// calcul coefficient filtre FIR
for (byte i = 0; i < M; i++) {b[i]=(sin(2*fc*PI*(i-((M-1)/2))/fe)/(((i-((M-1)/2)))*PI));}   
b[((M-1)/2)]=2*fc/fe;            //sinus cardinal(0)
//Les coefficients et le gain sans la troncature hamming
//for (byte i = 0; i < M; i++) {b[i];gain=gain+b[i];}  

//ajustement des coefficients avec la troncature hamming
for (byte i = 0; i < M; i++) {b[i]=b[i]*(0.54+0.46*cos(PI*(i-((M-1)/2))/M));gain=gain+b[i];}
for (byte i = 0; i < M; i++) {Serial.print(b[i],3);Serial.print(";");} 
Serial.println("gain= ");
Serial.print(gain,3);Serial.print(";"); 
//Gain pour différents nombres de coefficients sans la troncature hamming
//M=5  gain=0.48
//M=9  gain=0.81
//M=23  gain=1.04

//Gain pour différents nombres de coefficients avec la troncature hamming
//M=5  gain=0.41
//M=9  gain=0.68
//M=23  gain=1.09

}


// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
entree[M-1]=analogRead(A0);         //convertisseur 10 bits sous 5V   https://www.gammon.com.au/adc
entree[M-1]=entree[M-1]/4;          //mise à l'echelle 10 bits en entrée et 8 bits en sortie+chiffre significatif   

out=0;     //remise à zero du calcul du filtre RIF
for (byte i = 0; i < M; i++) {out=(b[i]*entree[i]+out);/*Serial.print(entree[i]);Serial.print(";");*/}
out=out/gain;   
for (byte i = 0; i < M; i++) {entree[i]=entree[i+1];}       
//Serial.print(out,0);
//Serial.println(";");                                                             
if (out<0) {out=0;}
if (out>255) {out=255;}                 
analogWrite(PWM3,out);        //   

temps++;   
outaverage=outaverage+out;          //calcul de la valeur moyenne     
//rafraichissement toutes les 0.2s           
if (temps>=200)   {entreeMax=127;outMax=127;temps=0;outaverage1=outaverage/200;outaverage=0;}
if (entreeMax <entree[0]) {entreeMax = entree[0]; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }
digitalWrite(LED13,LOW); 
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {

 
lcd.setCursor(0,0);
lcd.print("Gain= ");
lcd.print(gain,2);   
lcd.setCursor(0,1);   
lcd.print("ordre");
lcd.print(M);
lcd.setCursor(0,2);
lcd.print("out_average");
lcd.print(outaverage1,0);   //valeur moyenne sur 0.2seconde

   lcd.setCursor(0,3);
  attenuation=((outMax-outaverage1)/(entreeMax-127));
    lcd.print("Attenuat= ");
  lcd.print(attenuation,2);   

} // fin loop

Voici les nouvelles valeurs des atténuations

Avec la fenêtre de Hamming, nous avons les résultats suivants

En ajustant les gains, nous parvenons à mieux observer les différences entre les courbes. On voit aussi l’importance de la fenêtre de Hamming qui nous permet d’obtenir de nouveaux coefficients et donc d’améliorer la qualité des filtres.
Après le filtre passe-bas, Je vais faire le filtre passe-bande FIR et présenter le code et les résultats des simulations.

On peut aussi rajouter la courbe d’un filtre analogique du seconde ordre dans Excel pour faire des comparaissions.
Il faut juste rentrer l’équation comme on peut l’observer sur la figure suivante.

sur la courbe, precedente, on peut observer qu'il faut 9 coefficients FIR pour attenuer comme un second ordre.

Une petite aide : :smiling_imp: :roll_eyes:
Voici les calculs qui permettent de trouver les coefficients d’un passe bande d’un filtre FIR.

On peut observer que pour un nombre de coefficient égale à 17, L’atténuation basse fréquence est faible et que la bande passante n’est pas respectée par rapport à un filtre du seconde ordre analogique.


La valeur max du coefficient bm correspond à 2 fois la bande passante divisée par la fréquence d’échantillonnage.
Mais quel est le nombre de coefficients pour avoir une atténuation aux fréquences de coupures désirées ?
Si on augmente le nombre à 21 coefficients, l’atténuation n’est pas encore atteinte.

Si on augmente à 41 coefficients, alors l’atténuation correspond à ce que l’on désire.

On peut remarquer que plus la bande passante est faible et plus il faudra de coefficient pur avoir l’atténuation désirée.
De même, plus la fréquence fo et éloignée de la fréquence fe, et plus il faudra de coefficient.
En effet, les termes b du sinus cardinal deviennent de plus en plus négligeable avec
Sur la figure suivante les fréquences de coupure est passé à 30 HZ et 80Hz, il suffira de 10 coefficients pour avoir des coefficients faibles

Saliou, il te reste à vérifier la théorie…….
En calculant les coefficients par l’arduino
Attention le temps du calcul du filtre ne doit pas dépasser le temps de la période d’echantillonage.

D’ailleurs, il serait intéressant de savoir en combien de temps le filtre est calculé en fonction du nombre de coefficient :sunglasses:

1 Like

A la place d’utiliser une PWM pour avoir une sortie analogique, un DAC 12 bits MCP4725 I2C pourrait etre utilisé si on n’a pas besoin de periode d’echantillonage rapide.

http://www.bristolwatch.com/arduino2/ard4.htm

En effet, le temps d’ecriture dans le DAC avec l’I2C est d’environ 0.14ms ce qui est lent.

alors que l’ecriture, d’un PCF8574 8bits I2C est de 0.22ms donc realisé un reseau R2R n’est pas interressant à faire.

L’écriture du DAC interne de l’ESP32 dure 0.05ms, 2 fois plus rapide que l’ATMEGA328.

Mais on ne peut pas utiliser la liaison série dans la routine d’interruption timer1, ni l’écriture dans le DAC.
Donc, il faut tout faire dans la boucle loop

On peut observer sur la figure suivante, la sortie du DAC I2C qui recopie l’entrée A0 avec période d’échantillonnage de 1ms.

Voici le code avec les adresses des circuits I2C
I2C device found at address 0x27 LCD PCF8574
I2C device found at address 0x60 DAC MCP4725 A0=0
I2C device found at address 0x68 DS1307

#include <Wire.h>     // // Join i2c bus
#include <LiquidCrystal_I2C.h>
//#include "RTClib.h"      // Bibliothèque pour le module RTC
#include <Adafruit_MCP4725.h>
#include <TimerOne.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter

#define LED13   13 
byte bufferDAC [3];
unsigned int counter;
float entree, out;
bool flag=0;


Adafruit_MCP4725 dac;

void setup()
{  // initialise l'afficheur LCD
pinMode(LED13, OUTPUT); 

lcd.init();  //et pas lcd begin comme cetraine biblio
  lcd.display();     // activer l'affichage
  lcd.backlight();   // allumer retroeclairage

  lcd.setCursor(0,0);
  lcd.print("IUT GENIE elect&info");
  lcd.setCursor(5,1);
  lcd.print("Soissons");
  //Serial.begin(57600);

 Wire.begin();

   
 // commande du DAC MCP4725 12 bits  4096
  bufferDAC [0] = 0b01000000; // control byte pointer
  bufferDAC[2]=0;
  bufferDAC[1]=64;     //1.25V
  Wire.beginTransmission(0x60);
  Wire.write(bufferDAC[0]);  
  Wire.write(bufferDAC[1]);  // 8 MSB   
  Wire.write(bufferDAC[2]);  // 4 LSB
  Wire.endTransmission();               //temps d'ecriture dans le DAC 0.4ms

  dac.begin(0x60);
 Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
 Timer1.attachInterrupt(callback); 


//  attachInterrupt(0, Rexterieur2, RISING);   // broche2
}


// Interruptions  tous les 1ms fait par le timer1***********************************
void callback()  {
flag=1;
//if (digitalRead(LED13)== 1) {digitalWrite(LED13,LOW);}  else {digitalWrite(LED13,HIGH);}
}//fin routine

void loop()
{
   
if (flag==1) {       //ne pas mettre dans la routine d'interruption timer1 ca rI2C ne foncitionne pas
  digitalWrite(LED13,HIGH);
  entree=analogRead(A0); //convertisseur 10 bits sous 5V
  out = entree*4;   //mise à l'echelle
  dac.setVoltage(out, false);            //12 bits

  flag=0;
  digitalWrite(LED13,LOW);                      //0.2ms
              }

}

Etant donné que les sorties de l’Arduino nano sont peu utilisées un DAC parallèle R2R serait intéressant à faire au niveau de la rapidité. Donc utiliser un DAC paralléle tel que DAC0800

mais, je n’ai pas trouvé de shield avec un DAC parallele ?

conclusions, la PWM avec ces 7micro secondes reste la meilleur solution

Pour kadhim
Plus de 60heures ouvrées pour trouver, la fréquence max mesurable par ton programme avec l’erreur le déphasage ??? .
C’est moi qui suis en train de faire ton sujet. :astonished:

Bref, avec un retard de 20µs imposé, qui peut être observé sur le signal à l’oscilloscope sur la figure suivante, mais le temps mesuré par l’Arduino est seulement de 12 µs, voir l’affichage LCD

En effet, le temps de lire micros() cela dure 6.5µs. mais, la lecture se fait sur les 2 routines d’interruptions.
Mais, il y a le temps d’empilement et dépilement due à la routine d’interruption.
De plus tout déclarer en float, ne permet pas de gagner du temps.
Voir le post suivant

Par contre, déclarer en entier non signé limite les fréquences mesurables supérieures à 15Hz.

Donc, avec un retard de 5µs± avec une erreur de 1 µs (20%), donne une fréquence maximale mesurable d’environ 25kHz comme on peut l’observer sur la figure suivante :

Voici le nouveau code

#include <Wire.h>
///#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
//#include <TimerOne.h>
//#include <math.h>

#define A1 20   // transformateur de tension
#define A2 21   //transformateur de courent
#define LED13    13      

LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

 unsigned  int T1;  //temps routine d'interruption D3
 unsigned  int T0;  //temps routine d'interruption D2
              float pi;
 unsigned  int temps;           //
 unsigned  int Time0;    // peridiode des signaux
              float dephasage, dephasagerad;
              float fp;  //facteur power
              float fe;                         //frequence d'entrée
  

void setup()
{
  pinMode(LED13, OUTPUT);
  pinMode(A1, INPUT);
  pinMode(A2,INPUT);
  
//  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
//  Timer1.attachInterrupt(callback);  
  lcd.begin(20, 4);                   //modifier pour un afficheur 16x2
  
  Serial.begin(9600);

  attachInterrupt(0, func_k, RISING);   // broche2

  T0 = millis();
  attachInterrupt(1, powerfactor, RISING);   // broche3
  
 // TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
}

// faisons deux interruotion pour pouvoir calculer le temps

void func_k() // la fonction appelée par l'interruption externe n°0
{
Time0=(micros()-T0);  //pour mesurer la perriode du signal broche 2
T0= micros();
}

void powerfactor() // la fonction appelée par l'interruption externe n°1
{
 temps=micros()-T0;  //pour mesurer l'ecart du temps et compenser le temps de la routine 
}

void callback()      
{
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption                                    
digitalWrite(LED13,LOW);  
}//fin routine

 

void loop()
{  
 
 fe=1000000/Time0;                        
dephasage=((360*temps)/Time0);

dephasagerad=((dephasage*PI)/180);
fp = cos(dephasagerad);// factor power

lcd.setCursor(0,0);
lcd.print("Frequence=");     //
lcd.print(fe);
lcd.print("   ");

lcd.setCursor(0,1);
lcd.print("Delta T=");
lcd.print(temps);
lcd.print("      ");

lcd.setCursor(0,2);
lcd.print("Dephasage=");
lcd.print(dephasage);
lcd.print("   ");

lcd.setCursor(0,3);
lcd.print("Factor Power=");
lcd.print(fp,3);
lcd.print("   ");

} // fin loop

Ce serait bien qu’un signal sinusoïdal de la tension avec une amplitude de 220V soit mesuré, ainsi qu’un, courant d’amplitude de moins de 10A avec un ACS712 soit effectué ainsi que la mesure de puissance.
Donc un programme pour faire une pince wattmetrique……
Quelle sera les modifications du programme si l’on prend un HSTS016L par rapport à ACS712 ?

** Filtre passe-bande RIF (fo=50Hz, bande passante 60Hz, donc frequence de coupure de fc1=20Hz et fc2=80HZ)avec atmega 328**
Comme pour le filtre passe-bas, nous allons voir le code et le résultat des simulations avec et sans la fenêtre de Hamming.
pour 25 coefficients voici les valeurs des coefficients du filtre FIR qui sont calculés par le programme arduino.
on peut observer qu’ils sont identiques à ceux determinés par iutgeiisoissons precedement transféré par USB via le virtual terminal de la simulation suivante

Mais avec la fenetre de Hamming nous avons trouvé de nouveaux coefficients:


Le code:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>

#define PWM3   3      //   timer2   
#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;
         
float  out=0;
float entreeMax;
float outMax;
float attenuation;

float outaverage;
float outaverage1;

float  fe=1000;  //frequence d'echantilonnage 
float  fc1=20;  //frequence de coupure basse 
float  fc2=80;  //frequence de coupure haute
float gain=0.35;
//float gain=0.1;

byte M=25;               //nombre impaire de coefficients desirées 
float  entree[25];       //declaration de la table des entrées
float  b[25];   // table des coefficients


           
void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
 
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

// calcul coefficient filtre FIR
for (byte i = 0; i < M; i++) {b[i]=(sin(2*fc2*PI*(i-((M-1)/2))/fe)/(((i-((M-1)/2)))*PI))-(sin(2*fc1*PI*(i-((M-1)/2))/fe)/(((i-((M-1)/2)))*PI));}   
b[((M-1)/2)]=2*(fc2-fc1)/fe; //sinus cardinal(0)

//ajustement des coefficients avec la troncature hamming
for (byte i = 0; i < M; i++) {b[i]=b[i]*(0.54+0.46*cos(PI*(i-((M-1)/2))/M));}
for (byte i = 0; i < M; i++) {Serial.print("b");Serial.print(i);Serial.print("=");Serial.print(b[i],3);Serial.println(";");}
//for (byte i = 0; i < M; i++) {Serial.println(b[i],3);/*Serial.print(";");*/} 
}

// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
entree[M-1]=analogRead(A0);         //convertisseur 10 bits sous 5V   https://www.gammon.com.au/adc
entree[M-1]=entree[M-1]/4;          //mise à l'echelle 10 bits en entrée et 8 bits en sortie+chiffre significatif   

out=0;     //remise à zero du calcul du filtre RIF
for (byte i = 0; i < M; i++) {out=(b[i]*entree[i]+out);/*Serial.print(entree[i]);Serial.print(";");*/}
out=out/gain;   
for (byte i = 0; i < M; i++) {entree[i]=entree[i+1];}       
//Serial.print(out,0);
//Serial.println(";");                                                             
if (out<0) {out=0;}
if (out>255) {out=255;}                 
analogWrite(PWM3,out);        //   

temps++;   
outaverage=outaverage+out;          //calcul de la valeur moyenne     
//rafraichissement toutes les 0.2s           
if (temps>=200)   {entreeMax=127;outMax=127;temps=0;outaverage1=outaverage/200;outaverage=0;}
if (entreeMax <entree[0]) {entreeMax = entree[0]; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }
digitalWrite(LED13,LOW); 
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {
 
lcd.setCursor(0,0);
lcd.print("Gain= ");
lcd.print(gain,2);   
lcd.setCursor(0,1);   
lcd.print("ordre");
lcd.print(M);
lcd.setCursor(0,2);
lcd.print("out_average");
lcd.print(outaverage1,0);   //valeur moyenne sur 0.2seconde

   lcd.setCursor(0,3);
  attenuation=((outMax-outaverage1)/(entreeMax-127));
    lcd.print("Attenuat= ");
  lcd.print(attenuation,2);   

} // fin loop

Le résultat des simulations sans la fenêtre de Hamming:

il a fallu mettre un gain 1/0.2 pour avoir un gain statique de 1 à 50Hz
On observe qu’il y’a des rebonds au niveau du filtre, ce qui est loin d’être idéal, raison pour laquelle nous allons essayer de résoudre ce problème.

Le résultat des simulations avec la fenêtre de Hamming avec un gain 1/0.35
la courbe bleu est la reponse numerique, la courbe orange est analogique


Avec la fenêtre, les rebonds n’apparaissent plus au niveau du filtre. Ce qui nous facilite aussi la comparaison des 2 filtres (numérique et analogique).
l’équation du filtre analogique est:

avec le coefficient Q=(fc2-fc1)/fo=1.2
pour le filtre analogique de second ordre pour les fréquence de coupure l’atténuation est de 0.707 alors que la bande passante pour le filtre numérique est bien plus grande.

avec une fréquence d’échantillonnage de 1KHz, il est possible d’augmenter le nombre de coefficients à 41 sans dépasser le temps de la routine d’interruption. cela devrait améliorer l’atténuation.
De plus, à la place de prendre une bande passante de 60Hz,
quelles seraient les valeurs des coefficients et l’atténuation si l’on prenait une bande passante de 20Hz?

Et le tuto exploitable pour le public de ce forum il arrive quand ?

Car pour le moment vous êtes toujours en phase de R&D.

Pour ce genre de discussion " entre vous de l'IUT ", car personne du forum ne participe, le bar serait plus approprié, ici excusez moi mais c'est de la pollution.

Le but de ce post est d’aider les programmateurs Arduino à comprendre et utiliser des filtres numériques.
Mais, lorsque je vois la difficulté de mes étudiants d’en réaliser,
Pas sûr que la vulgarisation soit possible car il faut de bonne base mathématique.
mais les programmes mis en ligne calculs les coefficients donc il suffit juste de mettre la periode et la frequence de coupure
L’objectif est aussi d’observer les limites possibles des processeurs

Voici un premier jet d’un article qui est prévue pour aider à la vulgarisation et faire un tuto




Il y a tellement de possibilité et de méthodes sur les filtres numériques qu’on les essaye …..pour l’instant
Mais, on veut bien un coup de main et on espere que des personnes sur le forum aide....
personnellement, c'est bizarre qu'il n y a pas ou peu de question

1 Like

Mais, on veut bien un coup de main et on espere que des personnes sur le forum aide…
personnellement, c’est bizarre qu’il n y a pas ou peu de question

Lisez les différentes questions posées et vous verrez que votre travail, sans nul doute intéressant et de qualité, ne correspond pas aux besoins des utilisateurs de ce forum.

Le public de ce forum n’est pas celui d’un IUT.
Pour faire une règle de trois avec décalage des bornes il faut utiliser la fonction map() !
Le public n’a souvent aucune formation en programmation, quant au traitement du signal même pas en rêve.
Le sujet le plus traité est celui de portes de poulailler automatiques suivant la luminosité et non pas suivant l’analyse du chant des poules.

Aidez nous à conserver ce forum propre. Ce sous forum est destiné à recevoir des tutos directement applicables et si possible des bibliothèques également directement applicables.
Votre étude toujours en perpétuel développement trouvera sa place dans le bar que, par ailleurs, vous avez déjà investi pour des exercices destinés aux étudiants de l’IUT.

on va essayer d'etre un peu plus explicite avec des filtres qui n'ont pas été etudiés encore dans ce post
avec des premieres année d'IUT avec des fonctions de bases

On va continuer avec des filtres qui n’ont pas encore été étudiés au préalable de ce poste à cause du lien suivant :
ECG AD8232 nano programme du monitoring, BPM heart rate

Il nous faut un dérivateur pour détecter le BPM du signal. Pour bien comprendre cette fonction numérique, un test dans Excel a été effectué. D’ailleurs, il existe de nombreuses possibilités mathématiques de la fonction dérivateur que l’on peut observer sur ce lien dont le principal et le plus simple est appelé derivée d’Euler…)
http://www.tangentex.com/MethodeEuler.htm

Formule Excel pour le calcul de la dérivée relative
derivéee relative =C4/(2*PI()*5)+512 avec C4 = (B4-B3)/(A4-A3)= (B4-B3)/0.01
Voici la dérivée d’un signal sinusoïdale d’amplitude 1024, de période 0.2 seconde (fréquence 5Hz) avec une période d’échantillonnage de 0.01 s (donc 20 échantillons dans Excel) :

[/img][/url[/iurl]]


voici le Programme Arduino qui permet de faire la meme chose

#include <LiquidCrystal.h>
#include <TimerOne.h>

int entree;                 // Déclaration des variables
float derivee, entree1;


      // Setup de l'affichage LCD
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);





void setup() {
  
pinMode(13,OUTPUT); // Définit la pin 13 comme sortie
pinMode(A0,INPUT);


// Configure le port série pour l'exemple
Serial.begin(57600);        // definition des baudes
  lcd.begin(16, 2);         // Mise affichage LCD 



  Timer1.initialize(10000);           //   pour 0.001s  1000   0.002s 2000     0.01s 10000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

}


void callback()  { // Boucle qui s'execute toute les 0.01 secondes
  entree1=entree;             // définition des valeurs au rang n et n-1
  entree=analogRead(A0);   //1024

derivee=(entree- entree1); // /0.01         Calcul de la dérivée (différence)

 /* 
  lcd.setCursor(0,0);   //colonne, ligne
lcd.print("input");
lcd.print(entree);
lcd.print("  ");
*/

  Serial.print(entree);Serial.print(";");Serial.print(derivee,0);Serial.println(";");       // Affichage sur le terminal des valeurs.

}



void loop() 
{


}

voici la capture d’ecran dans ISIS qui permet d’avoir le schema electrique, et recuperer sur le terminal serie le signal, la derivée tous les 0.01s avec la routine d’interruption du timer 1

les fonctions récurrentes permettent de faire du traitement du signal

2 Likes

Ce poste ne parle pas de filtre numérique réjecteur de signal ou coupe bande ou à encoche….
Car parfois, il faut atténuer une bande fréquence ou éliminer une fréquence qui perturbe le signal désirée
Tel que le 50Hz à éliminer pour la mesure de l’ECG sur le post suivant

Mais entre la théorie et la pratique quelles sont les différences ?
Quel doit être le rapport entre la fréquence d’échantillonnage et la fréquence à éliminer ?
Est-ce que le filtre sera impacté par la précision des 7 chiffres significatifs du compilateur ?

Dans un premier temps, la fréquence d’échantillonnage est de 500Hz pour éliminer le 50Hz
Voici l’étude mathématique théorique pour un coupe bande du second degré.

On peut observer que plus le coefficient a est proche de 1 est plus la bande de coupage est étroite, mais demande une précision des coefficients A et B plus important.
On peut observer que la différence des gains statiques avec ou sans la troncature des coefficients A et B est négligeable.
mais après la fréquence à éliminer, le gain statique n’est pas égal à 1.

Pour fe=500Hz et une fréquence de 50hz à éliminer, sur les figures suivantes en simulation, on peut observer qu’il y a bien l’atténuation pour la fréquence de 50Hz. Notre programme affiche l’atténuation sur l’afficheur LCD et les coefficients A et B.


Voici le code utilisés qui donne directement les gains en fonction du choix de la période d’échantillonnage

D’ailleurs, voilà les résultats en pratiques qui sont identiques aux résultats de la simulation
Capture4

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>


#define PWM5   5      //   timer2   
#define LED13    13     

LiquidCrystal lcd(9, 8, 4, 3, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables


unsigned    int    temps=0;

            float  entree=0;
            float  entree1=0;
            float  entree2=0;
            float  entree3=0;
            float  entree4=0;
       
            float  sortie=0;
            float  sortie1=0;
            float  sortie2=0;
            float  sortie3=0;
            float  sortie4=0;
           
            float  out=0;
            float A,B;

           
            float Gain=1.5;

float entreeMax;
float outMax;
float attenuation;


const float fo=50;
const float fe=500;
const float a=0.9;
                   

void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM5,OUTPUT);


A=2*cos(2*PI*fo/fe);
B=2*a*cos(2*PI*fo/fe);
Gain=(2-A)/(1-B+a*a);

  Timer1.initialize(1000000/fe);            // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000 
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt   500=>2KHz    5000=>200Hz
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4

  Serial.begin(9600);
/*  
 Serial.print("A=");Serial.println(A); 
 Serial.print("B=");Serial.println(B);
 Serial.print("G=");Serial.println(Gain);
*/
  
//  TCCR2B = (TCCR2B & 0b11111000) | 0x01;      //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
TCCR0B = (TCCR0B & 0b11111000) | 0x01;         //pin 5  64khz 

}


// Interruptions   par le timer1    ***********************************
void callback()  {
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption

entree2=entree1;      //entree(n-2)
entree1=entree;       //entree(n-1)
entree=analogRead(A0); //convertisseur 10 bits sous 5V
entree=entree/4;       //PWM 8 bits mais calcul en float

sortie2=sortie1;      //sortie(n-2)
sortie1=sortie;       //sortie(n-1)
// ---- filtre coupe-bande 50Hz------   
sortie=entree-A*entree1+entree2+B*sortie1-(a*a*sortie2);
out=(sortie/(Gain));
        
if (out<0) {out=0;}
if (out>255) {out=255;}           
analogWrite(PWM5,out); 

 temps++;  
if (temps>=300)   {entreeMax=127;outMax=127;temps=0;}
if (entree> entreeMax) {entreeMax = entree; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }
digitalWrite(LED13,LOW);

}//fin routine

///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {
   lcd.setCursor(0,0);
   lcd.setCursor(0,0);   
  lcd.print("CoupeBande50Hz a=");
  lcd.print(a,1);
  lcd.setCursor(0,1);
  attenuation=((outMax-127)/(entreeMax-127));   
  lcd.print("A=");
  lcd.print(attenuation,2);
  lcd.print("  ");
  lcd.setCursor(0,2);
  lcd.print("A=");lcd.print(A,4); 
  lcd.print(" B=");lcd.print(B,4);
  lcd.setCursor(0,3);
 lcd.print("G=");lcd.print(Gain,4);
} // fin loop

Mathématiquement, si on prend une fréquence d’échantillonnage de 4 fois plus important que la fréquence à éliminer. Les coefficients A et B sont nuls. Donc, c’est intéressant car le processeur aura peu de calculs mais il n’y aura que 4 échantillons.
Est-ce que cela sera pertinent ?

Les résultats du coupe bande avec la fréquence de 4 fois, la fréquence à éliminer donne d’assez bon résultat mathématiquement.

Malgré le peu d’échantillon, on peut apercevoir le signal de sortie sur la figure suivante

En pratique, l’atténuation correspond à la figure suivante

Capture4

Si la fréquence d’échantillon est de 1KHz, le signal de sinusoïdale même à 100Hz avec 10 échantillons et cela élimine bien la fréquence de 50Hz

En analogique, il est possible de faire des filtres d’ordre relativement grand à partir de premier ordre en cascade.
Peut-on transposer, ces filtres analogiques en numérique ?
En numerique, l'avantage de la fonction de transfert du 1er ordre est d’avoir un seul coefficient et de multiplier les fonctions de transfert entre elle.
Donc de n’avoir qu’une seule entrée en mémoire mais doit-on expand le dénominateur en programmation ?

Sur la figure suivante, on peut observer cette fonction de transfert pour un filtre du premier ordre et 2 filtres 1er ordre en cascade, mais la fréquence de coupure n’est pas la même à cause de la convolution.


Voici l’équation empirique qui donne approximativement le coefficient en fonction de l’ordre désirée par rapport à la fréquence de coupure et de la fréquence d’échantillonnage.
Remarque : Plus le rapport en fc/fe est petit et plus le coefficient devra être précis.


Sur les courbes suivantes, on peut observer que l’atténuation vérifie bien la fréquence de coupure désirée malgré l’ordre qui augmente, ainsi que l’augmentation de l’atténuation en fonction de l’ordre à partir de l’équation précédente

En codage, il faut développer la fonction de transfert en Z en fonction de l’ordre désirée, il faudra choisir l’équation et mettre les autres en commentaire.

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>


#define PWM5   5      //   timer2   
#define LED13    13     

LiquidCrystal lcd(9, 8, 4, 3, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables


unsigned    int    temps=0;

float  entree=0;
       
float  sortie=0;
float  sortie1=0;
float  sortie2=0;
float  sortie3=0,sortie4=0;
        
float  out=0;


float entreeMax;
float outMax;
float attenuation;


const float fc=10;
const float fe=1000;
float a;  //coeficient du filtre passe bas
float N=4;  //ordre du fitre
float coef;
                   

void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM5,OUTPUT);


//a=(fe-fc*PI)/(fe+fc*PI);
a=1-(2*fc*PI/fe)+(2*fc*fc*PI*PI/(fe*fe));
coef=pow(N,-0.08);
a=a*coef;

  Timer1.initialize(1000000/fe);            // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000 
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt   500=>2KHz    5000=>200Hz
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4

  Serial.begin(57600); 

TCCR0B = (TCCR0B & 0b11111000) | 0x01;         //pin 5  64khz 

}


// Interruptions   par le timer1    ***********************************
void callback()  {
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption

entree=analogRead(A0); //convertisseur 10 bits sous 5V
entree=(entree)/4;       //PWM en 8 bits, calcul unilaterale

sortie4=sortie3;
sortie3=sortie2;
sortie2=sortie1;
sortie1=sortie;       //sortie(n-1)
// ---- filtre passe bas ---   
//sortie=entree*(1-a)+sortie1*a;    // order=1

//sortie=(entree*(1-a)*(1-a))+(2*sortie1*a)-(sortie2*a*a);  // order=2 ok
//sortie=(entree*(1-a)*(1-a))+(sortie1*sortie1*a*a);      // order=2 no ok
//sortie=(entree*pow((1-a),3))+(3*sortie1*a)-(3*sortie2*a*a)+(sortie3*a*a*a);      // order=3 ok
sortie=(entree*pow((1-a),4))+(4*sortie1*a)-(6*sortie2*a*a)+(4*sortie3*pow(a,3))-sortie4*pow(a,4);    // order=4 ok
out=sortie;
        
if (out<0) {out=0;}
if (out>255) {out=255;}           
analogWrite(PWM5,(out)); 

 temps++;  
if (temps>=3000)   {entreeMax=127;outMax=127;temps=0;}
if (entree> entreeMax) {entreeMax = entree; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }
digitalWrite(LED13,LOW);


}//fin routine

///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {
Serial.print(entree,2);Serial.print(";"); Serial.println(sortie,2);
  lcd.setCursor(0,0);   
  lcd.print("passe bas ordre 1");
  lcd.setCursor(0,1); 
  lcd.print("cascade N=");
  lcd.print(N,0);
  lcd.setCursor(0,2);
  lcd.print("a=");
  lcd.print(a,3);
  
  lcd.setCursor(0,3);
  attenuation=((outMax-127)/(entreeMax-127));   
  lcd.print("Attenuat=");
  lcd.print(attenuation,3);


} // fin loop

Voici la simulation qui permet de vérifier le code, en utilisant une fréquence d’échantillonnage de 1kHz.
L’ordre 3 demande un temps de calcul de 0.5ms et l’ordre 4 demande un temps de calcul de 1ms donc la fréquence d’échantillonnage doit être diminué pour cet ordre.


Voici les résultats des atténuations pour différents ordres en fréquentiel avec une fréquence d’échantillonnage de 1kHz et une fréquence de coupure désirée de 10Hz.

Avec l’ordre 4, la fréquence d’échantillonnage passe de à 500Hz, car le calcul est trop long pour l’ATmega 328.

Pour les autres courbes, la pratique est identique à la théorie.


Quelles sont les différences dans les résultats entre le filtre précèdent et un filtre numérique un Butter Worth ou un Cheby ?

Pour connaitre les avantages et les inconvénients de chacun des filtres numeriques :
rien de mieux que de les tester avec les limites possibles d’un petit processeur avec son codage

D’ailleurs, il y a un article très intéressant qui compare différents filtres numériques mais qui ne dit pas comment il les codes…….

« An optimal filter for short photoplethysmogram signals »

https://www.nature.com/articles/sdata201876

pas facile de partager son savoir, de le démocratiser et d’optimiser un filtrage.