Catégories
Algorithme Arduino Asservissement Asservissement Arduino

Asservissement | Arduino #5: Discrétisation et Implémentation de la dérivée seconde

Objectif

Le tuto est pour objectif de discrétiser et implémenter sur Arduino une approximation de la dérivée seconde d’un fonction en utilisant la méthode des différences finies (DF).

Approximation de la dérivée seconde d’une fonction

Ci-dessous la formule mathématique de l’approximation de la dérivée secondes en utilisant la méthode des DF. Le paramètre h indique la période d’échantillonnage inverse de la fréquence d’échantillonnage de notre signal.

Formule de l'approximation de la dérivée seconde

D’après la formule, l’approximation de la dérivée seconde est proportionnelle à f(n-1), f(n) et f(n+1) et inversement proportionnelle à h². On peut réécrire la formule sous forme du produit de convolution entre le filtre [1 -2 1 ] et le vecteur [f(n+1) f(n) f(n-1)]’. On verra dans la suite de la suite comment approximer la dérivée nième d’une fonction et comment calculer les coefficients du filtre.

L’erreur absolue de la méthode est proportionnelle à h²/12. Elle est environ deux fois plus petite par rapport à l’approximation de la dérivée première en utilisant l’approximation centrée (voir le tuto 4).

Comment implémenter la dérivée seconde ?

Principe

Le concept est semblable à celui de la dérivée premier (voir l’article précédent). L’approximation nécessite trois échantillons f(n+1), f(n) et f(n-1) symétriques par rapport à f(n).  Par conséquent, durant l’implémentation on va utiliser un tableau de trois élément pour stocker les échantillons f(n), f(n-1) et f(n+1).

Attention, l’ordre est primordial durant le calcul de la dérivée !

Démarche d’implémentation

On déclare un tableau Tab[3] de trois élément ordonnés de la façon suivante [x(n) x(n-1) x(n-2)]. Pour chaque itération de la boucle on va effectuer les opérations suivantes :

  1. Acquisition de nouveau échantillon x(n+1)
  2. Décaler les éléments du tableaux Tab:

                Tab[2] <= Tab[1]

                Tab[1] <= Tab[0]

  1. Incrustation du nouvel échantillon dans le tableau

                Tab[0] <= x(n+1)

  1. Calcul de l’approximations de la dérivée seconde :

                x2_n=(Tab[0]- 2*Tab[1] + Tab[2])/T

Implémentation des approximations

La fonction diff2( ) sera utilisé pour le calcul de la dérivée seconde. Ci-dessous la déclaration et la définition de la fonction.

double diff2(double x_nn, unsigned long T_mills)

La fonction prend en entrée deux paramètres : le nouvel échantillon x(n+1) et la période d’échantillonnage en milliseconde. Puis, elle renvoie la valeur de l’approximation.  La fonction renvoie un résultat par itération de la boucle. Ci-dessous la définition de la fonction diff2 ( ).

double diff2(double x_nn, unsigned long T_mills)

{

  static double x[3];

  double diff_2=0.0, T=0.0;




  // Mise à jour des échantillons

  x[0]=x[1];  // x(n)   ==> x(n-1)

  x[1]=x[2];  // x(n+1) ==> x(n)

  x[2]=x_nn;  // x_nn   ==> x(n+1)




  // Calcul de la dérivée du 2nd Ordre

  T=(double)T_mills/1000.0;

  diff_2=(x[2]-2.0*x[1]+x[0])/(T*T);




  // Renvoie du résultat

  return diff_2;

}

Le mot static permet de préserver les échantillons du tableau x[ ] entre les appels de la fonction. Sans utilisation du mot static, les échantillons d’avant seront perdus.

Test du programme

Pour tester les approximations on a besoin de générer une fonction f(n), puis on calcul sa dérivée en utilisant les trois approches. L’astuce consiste à générer une fonction triangulaire sous forme d’un compteur qui monte de 0 à une valeur maximale, puis décroit de la même façon à zéro. Du coup on obtient une fonction f(n)=n croissante et décroissante. Le résultat de la dérivée doit être sous forme d’un signal rectangulaire (voir la vidéo pour plus des détails). Ci-dessous un extrait du code dédié à la génération d’un signal triangulaire.



  // La fonction f(n)=n

  if (sign==true)

  {

    n++;

    if(n==n_max)sign=false; 

  }

  else

  {

    n--;

    if (n==0)sign=true;

  }




  x_n=(double)n;

  //x_n=x_n*x_n;      // f(n)=n^2  => f''(n)=2  (constante)

  x_n=x_n*x_n*x_n;  // f(n)=n^3  => f''(n)=6n (droite)

Code Arduino

#define   n_max   100

const unsigned long T_ms=10;

bool sign=true;

byte TypeDiff;

double d2_f_n, x_n;

unsigned long n=0;




void setup()

{

  // Port série: Affichage de la vitesse

  Serial.begin(9600);

}

void loop()

{

  // La fonction f(n)=n

  if (sign==true)

  {

    n++;

    if(n==n_max)sign=false; 

  }

  else

  {

    n--;

    if (n==0)sign=true;

  }




  x_n=(double)n;

  //x_n=x_n*x_n;      // f(n)=n^2  => f''(n)=2  (constante)

  x_n=x_n*x_n*x_n;  // f(n)=n^3  => f''(n)=6n (droite)




  // Calcul de la dérivée 2nd (1 dérivée/cycle)

  d2_f_n= diff2(x_n, T_ms);




  // Affichage

  Serial.print(d2_f_n); Serial.print(",");

  Serial.println(x_n); 







  // Période d'échantillonnage

  delay(T_ms);

}










double diff2(double x_nn, unsigned long T_mills)

{

  static double x[3];

  double diff_2=0.0, T=0.0;




  // Mise à jour des échantillons

  x[0]=x[1];  // x(n)   ==> x(n-1)

  x[1]=x[2];  // x(n+1) ==> x(n)

  x[2]=x_nn;  // x_nn   ==> x(n+1)




  // Calcul de la dérivée du 2nd Ordre

  T=(double)T_mills/1000.0;

  diff_2=(x[2]-2.0*x[1]+x[0])/(T*T);




  // Renvoie du résultat

  return diff_2;

}

Accueil Asservissement avec Arduino

Laisser un commentaire