[Non Résolu] Utilisation de l'approximation d'une intégrale pour calculer la vitesse d'une "voiture"

Bonjour,

Je précise que je suis débutant en arduino et que en général j'utilise des codes déjà écrient, que j'essaye de comprendre, afin de les utiliser dans mon travail.

Dans le cadre de mon TIPE j'ai choisi de travailler sur le freinage d'urgence d'une voiture autonome. J'ai donc pour l'instant asservis le système de freinage avec un capteur à ultrasons pour la distance à l'obstacle. Cependant je rencontre un problème pour asservir le système par la vitesse. Je tiens aussi à préciser que la "voiture" ne dispose pas de moteur et sera donc lancé à la main pour tester le freinage.

En effet, je possède un accéléromètre (Triple Axis Accelerometer BMA220) que j'utilise avec le code suivant trouvé sur [Triple_Axis_Accelerometer_BMA220_Tiny__SKU_SEN0168-DFRobot] :

#include <Wire.h>
byte Version[3];
int8_t x_data;
int8_t y_data;
byte range=0x00;
float divi=16;
float x,y; 
int vitesse;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  Wire.beginTransmission(0x0A); // address of the accelerometer
  // range settings
  Wire.write(0x22); //register address
  Wire.write(range); //can be set at"0x00""0x01""0x02""0x03", refer to Datashhet on wiki
  // low pass filter
  Wire.write(0x20); //register address
  Wire.write(0x05); //can be set at"0x05""0x04"......"0x01""0x00", refer to Datashhet on wiki
  Wire.endTransmission();
}

void AccelerometerInit()
{
  Wire.beginTransmission(0x0A); // address of the accelerometer
  // reset the accelerometer
  Wire.write(0x04); // Y data
  Wire.endTransmission();
  Wire.requestFrom(0x0A,1);    // request 6 bytes from slave device #2
  while(Wire.available())    // slave may send less than requested
  {
    Version[0] = Wire.read(); // receive a byte as characte
  }
  x_data=(int8_t)Version[0]>>2;

  Wire.beginTransmission(0x0A); // address of the accelerometer
  // reset the accelerometer
  Wire.write(0x06); // Y data
  Wire.endTransmission();
  Wire.requestFrom(0x0A,1);    // request 6 bytes from slave device #2
  while(Wire.available())    // slave may send less than requested
  {
    Version[1] = Wire.read(); // receive a byte as characte
  }
  y_data=(int8_t)Version[1]>>2;

   x=(float)x_data/divi;
   y=(float)y_data/divi;
   Serial.print("X=");
   Serial.print(x);         // print the character
   Serial.print("  ");
   Serial.print("Y=");
   Serial.print(y);         // print the character
   Serial.print("  ");
  
// Partie pour calculer la vitesse (à modifier et à compléter)
  vitesse = (x - (x-1)) (;
  Serial.print("Vitesse=");
  Serial.print(vitesse);
}

// 

void loop()
{
  switch(range)  //change the data dealing method based on the range u've set
  {
  case 0x00:divi=16;  break;
  case 0x01:divi=8;  break;
  case 0x02:divi=4;  break;
  case 0x03:divi=2;  break;
  default: Serial.println("range setting is Wrong,range:from 0to 3.Please check!");while(1);
  }
  AccelerometerInit();
 delay(100);
}

J'aimerais pouvoir calculer la vitesse avec la technique d'approximation des rectangles, même si cela n'est pas précis. Néanmoins, je n'arrive pas à coder la formule suivante : ( (t) - (t-1) ) ( (a(t) + a(t-1)) / 2 ) en utilisant les valeurs de l'accélération préalablement donné par le code ci-dessus à des temps donnés.

Merci d'avance pour votre aide.

Vous n'aviez pas mis le code entre balises., je l'ai fait.
Il semblerait que celui-ci soit incomplet. La ligne suivante semble tronquée.

// Partie pour calculer la vitesse
  vitesse = (x - (x-1)) (;

Oui effectivement cette partie, que j’ai rajouté par rapport au code de départ, est complètement à modifier et à compléter. J’avais juste procédé à un début de réflexion mais je ne voyais pas comment continuer.

Bonsoir

  1. je suppose que x e y sont les accelerations selon x et y ?

  2. Je ne vois pas le temps apparaitre. Il y a bien en delay(1000) à la fin de loop. on peut s'en servir pour la valeur de dt, à condition que la boucle AcclerometreInit soit suffisament rapide

  3. Il faut penser à ce que la fonction AccelerometreInit() prend comme argument et renvoie comme retour. Perso, je partirais sur :

void AccelerometerInit( ){

// on enregistre les valeurs à t-dt
float prevX = x ; // acceleration à t-dt selon X
float prevY = y;

// on reprends ton code ensuite
// qui donne les nouvelles valeurs de x et y
// penser à verifier les unités de l'acceleration envoyée par l'accéléromètre
// notamment le temps

float vX = 1 * (x + prevX) / 2; // en prenant dt = 1000ms (d'après le delay) = 1s
float vy = 1 * ( x + prevY) / 2;

v= sqrt(vX * vX + vY * vY)
}

Ca serait sans doute plus propre de mesurer le temps pour avoir une valeur de (t - (t-dt)) plus correcte, voire même d'ajouter un paramètre en début de programme pour pouvoir changer la vitesse d'acquisition du programme (dans le delay(1000))

Tu voulais dire 'y'?

Je ne vois pas l'intérêt de mettre un delay dans une fonction qui doit être sécuritaire.

Si le besoin de freinage apparaît au début du delay, tu perds 100ms ce qui peut représenter plusieurs mètres pour une vraie voiture.
Usain Bolt parcourt plus de 10 mètres pendant ce temps de réaction...

(Sujet déplacé)

Concernant l'information temps, il suffirait de prendre la valeur de millis() à chaque itération et le conserver pour calculer l'écart de temps dans l'itération suivante.
Pour que ce soit plus juste, il vaudrait mieux prendre ce temps lors de l'acquisition de l'accélération.

La fonction qui calcule la vitesse pourrait conserver cette valeur dans une variable déclaréee static ou dans une variable globale pour la retrouver plus tard.

Avant même les problèmes de code, il y a un gros problème de maths.
Ton intégrale est définie à une constante près:
V(t) = Intégrale de 0 à t de Acceleration.dt + V(t=0)
Or tu ne connais pas V0 (car mobile lancé à la main).
Dans mon job j'ai fait un jour cette manip, mais pour un corps chutant à partir de V0=0, et c'était OK.
Mais là, sans connaissance de V0 -> ???????????
Le seul moment où la vitesse est connue, c'est quand le mobile s'arrête, mais c'est trop tard pour freiner !

Ah oui je n'avais même pas verifié la formule, j'ai fait confiance, je l'avoue.

@lesept c'était bien vY effectivement
@fdufnews je suis tout à fait d'accord qu'il manque la base de temps, et que mesurer dt ce serait mieux que de le supposer constant. Et 1s, ça peut être beaucoup pour une intégrale. Mais j'avais choisi de respecter le code de t1412

@t1412 J'ajouterais aussi que mesurer une vitesse avec un accéléromètre, ce n'est pas idéal. En effet c'est très imprécis, car toute erreur sur l'accélération va se reporter fortement sur le calcul de la vitesse. (voir ce site sur les erreurs de trajectoire calculées avec les accéléromètres) Si ça t'est possible, il vaudrait mieux utiliser un tachymètre, comme par exemple un aimant sur une roue et son capteur ou une fourche optique. Les deux solutions sont faciles à mettre en oeuvre et bien documentées sur internet

quant au code corrrigé :

// avant setup, definition des variables :
float x, y; // acceleration selon x et y
float vX, vY; // vitesse selon x et y
unsigned long t; // date t

void AccelerometerInit( ){

// on enregistre les valeurs à t-dt
float prevX = x ; // acceleration à t-dt selon X
float prevY = y;
float prevVX = vX; // vitesse selon x à t-dt
float prevVY = vY; // vitesse selon y à t-dt
unsigned long prevT = t; // date à t-dt

// les valeurs à la date t :
t = millis(); // la nouvelle date t

// on reprends ton code ensuite
// qui donne les nouvelles valeurs de x et y
// penser à verifier les unités de l'acceleration envoyée par l'accéléromètre
// notamment le temps

float vX = prevVX + (t - prevT) * (x + prevX) / 2;
float vy = prevVY + (t - prevT) * ( y + prevY) / 2;

v= sqrt(vX * vX + vY * vY)
}

Le capteur à ultrason donne une distance.
La fonction millis() donne une information de temps.
Deux valeurs consécutives de distance et de temps permettent de déterminer la vitesse relative du mobile par rapport à l'obstacle qui renvoie l'écho. Je pense que c'est suffisant pour couvrir le besoin.
Le problème pour moi c'est plutôt le champ du capteur de distance et la récurrence possible des mesures. Pour cette fonction, j'aurais plutôt utilisé un capteur qui utilise la mesure du temps de vol genre VL53L0X

C'est pour un TIPE, nous sommes presque fin mai donc aucune possibilité de changer de matériel. Il faut faire au mieux avec le matériel disponible.
Cela n'empêchera pas @t1412 d'expliquer pourquoi un autre capteur aurait été plus adapté : cela fait aussi parti du travail de recherche.

@t1413 :
Regardes la vitesse du son, 330m/s, le principe de mesure par réflexion sur l'obstacle, le trajet est doublé.
Cela va imposer un temps incompressible entre deux mesures.
Le capteur à US envoi une salve de 8 impulsions à 40 kHz et attend que ces impulsions reviennent. Il contient un petit microcontroleur qui délivre un signal proportionnel au temps de trajet des ondes à ultra son.

En fonction de ce que tu trouve, pour la démonstration il suffira de ne pas lancer la voiture trop vite et de ne pas oublier de donner des explications à ton professeur.

On peut très bien faire ça sans capteur de vitesse, en utilisant la différence entre deux distances échantillonnées avec un pas de temps constant pour avoir la vitesse. La régulation du frein peut se faire en linéaire, avec un PD ou un PID, ou encore en non linéaire avec une loi de type bang-bang, la limite étant une parabole dans le plan distance-vitesse : v = racine (2 * gamma * x).

Je suis bien conscient que le projet est un petit peu bancale notamment niveau précision et vu mon faible niveau en programmation, la difficulté est augmenté.

@biggil J'avais pensé à ce problème de taille mais je pensais pouvoir le contourner avec un calcul de vitesse plutôt rapide .

@Fantoche Comme l'a indiqué @68tjs, je suis un peu en maque de temps et de plus mon installation ne permet pas de mesurer la vitesse avec un tachymètre même si je suis conscient que cela est beaucoup plus précis.

En tous cas merci, à tous, pour les indications sur le programme.

@68tjs @fdufnews si je n'arrivais pas à des résultats concluants avec l'accéléromètre, je pensais partir sur l'idée de mesurer la vitesse avec le capteur US.

@JiPe38 Il faudrait donc utiliser un GPS, que je n'ai pas, ou encore une caméra mais il serait compliqué d'asservir les servomoteurs qui actionnent les freins. Il me semble que l'utilisation de régulateurs est une bonne idée mais je n'en possède pas.

Si le frein est "tout ou rien", alors la solution optimale est la commande bang-bang. C'est fastoche à programmer:
Si x est la distance restant à parcourir (> 0) et v la vitesse (calculée par différence entre deux positions successives), gamma (accélération dans les unités qui vont bien) une constante à ajuster, pour calculer une variable booléenne de freinage faire :
booleean frein = (v * v - 2 * gamma * x > 0) ;
Augmenter petit à petit la valeur de gamma jusqu'à ce qu'on n'ait plus trop de secousses au freinage.

A la réflexion, s'il y a réellement un obstacle, partir avec un gamma élevé et diminuer petit à petit. Of course, tenir compte de la distance mini souhaitée entre le capteur et l'obstacle à l'arrêt.

Bonsoir,

J'ai vérifié que le programme fonctionnait :

#include <Wire.h>
byte Version[3];
int8_t x_data;
int8_t y_data;
int8_t z_data;
byte range = 0x00;
float divi = 16;
float ax, ay, az;
// int t = 200;
unsigned long t;
float vX, vY; // vitesse selon x et y
float V;
float prevX;
float prevY;
float prevT;
float g = 9.81; //l'accéléromètre renvoie des valeurs en g

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  Wire.beginTransmission(0x0A); // address of the accelerometer
  // range settings
  Wire.write(0x22); //register address
  Wire.write(range); //can be set at"0x00""0x01""0x02""0x03", refer to Datashhet on wiki
  // low pass filter
  Wire.write(0x20); //register address
  Wire.write(0x05); //can be set at"0x05""0x04"......"0x01""0x00", refer to Datashhet on wiki
  Wire.endTransmission();
}

void AccelerometerInit()
{
  Wire.beginTransmission(0x0A); // address of the accelerometer
  // reset the accelerometer
  Wire.write(0x04); // Y data
  Wire.endTransmission();
  Wire.requestFrom(0x0A, 1);   // request 6 bytes from slave device #2
  while (Wire.available())   // slave may send less than requested
  {
    Version[0] = Wire.read(); // receive a byte as characte
  }
  x_data = (int8_t)Version[0] >> 2;

  Wire.beginTransmission(0x0A); // address of the accelerometer
  // reset the accelerometer
  Wire.write(0x06); // Y data
  Wire.endTransmission();
  Wire.requestFrom(0x0A, 1);   // request 6 bytes from slave device #2
  while (Wire.available())   // slave may send less than requested
  {
    Version[1] = Wire.read(); // receive a byte as characte
  }
  y_data = (int8_t)Version[1] >> 2;

  Wire.beginTransmission(0x0A); // address of the accelerometer
  // reset the accelerometer
  Wire.write(0x08); // Y data
  Wire.endTransmission();
  Wire.requestFrom(0x0A, 1);   // request 6 bytes from slave device #2
  while (Wire.available())   // slave may send less than requested
  {
    Version[2] = Wire.read(); // receive a byte as characte
  }
  z_data = (int8_t)Version[2] >> 2;

  ax = (float)x_data / divi;
  ay = (float)y_data / divi;
  az = (float)z_data / divi;
  Serial.print("aX=");
  Serial.print(ax);         // print the character
  Serial.print("  ");
  Serial.print("aY=");
  Serial.print(ay);         // print the character
  Serial.print("  ");
  Serial.print("aZ=");           // print the character
  Serial.println(az);


  float prevX = ax * g; // acceleration à t-dt selon X
  float prevY = ay * g;
  float prevVX = vX; // vitesse selon x à t-dt
  float prevVY = vY; // vitesse selon y à t-dt
  unsigned long prevT = t; // date à t-dt

  // les valeurs à la date t :
  t = millis(); // la nouvelle date t


  float vX = prevVX + (t - prevT) * (ax * g + prevX) / 2;
  float vY = prevVY + (t - prevT) * (ay * g + prevY) / 2;

  V = sqrt(vX * vX + vY * vY);
  Serial.print("Vitesse =");
  Serial.print(V);
  Serial.print("  ");

}


void loop()
{
  switch (range) //change the data dealing method based on the range u've set
  {
    case 0x00: divi = 16;  break;
    case 0x01: divi = 8;  break;
    case 0x02: divi = 4;  break;
    case 0x03: divi = 2;  break;
    default: Serial.println("range setting is Wrong,range:from 0to 3.Please check!"); while (1);
  }
  AccelerometerInit();
  delay(t);
}

Ce qui me renvoie les valeurs suivantes :

Vitesse =ovf aX=-0.44 aY=-0.12 aZ=1.00
Vitesse =26.78 aX=-0.44 aY=-0.19 aZ=1.00
Vitesse =214.79 aX=-0.44 aY=-0.19 aZ=1.00
Vitesse =354.88 aX=-0.44 aY=-0.19 aZ=1.06
Vitesse =625.70 aX=-0.44 aY=-0.19 aZ=1.00
Vitesse =1251.40 aX=-0.50 aY=-0.19 aZ=1.06
Vitesse =2813.10 aX=-0.44 aY=-0.19 aZ=1.00
Vitesse =5010.29 aX=-0.44 aY=-0.19 aZ=1.00
Vitesse =10015.91 aX=-0.44 aY=-0.19 aZ=1.00
Vitesse =20036.49 aX=-0.19 aY=0.12 aZ=1.06
Vitesse =18974.04

Outre les valeurs de vitesse beaucoup trop importantes, la vitesse globale augmente alors que j'effectuais des à-coups, ce qui me semble bizarre. De plus, le programme prend de plus en plus de temps à être réaliser. J'ai donc essayé avec un delay différent (500 ms) ce qui donne les valeurs suivantes :

Vitesse =ovf aX=-0.69 aY=-0.19 aZ=0.88
Vitesse =3509.33 aX=-0.69 aY=-0.19 aZ=0.81
Vitesse =3516.32 aX=-0.69 aY=-0.19 aZ=0.88
Vitesse =3523.31 aX=-0.75 aY=-0.06 aZ=0.75
Vitesse =3713.65 aX=-0.75 aY=-0.06 aZ=0.75
Vitesse =3728.42 aX=-0.69 aY=-0.31 aZ=0.81
Vitesse =3726.43 aX=-0.69 aY=-0.37 aZ=0.88
Vitesse =3864.26 aX=-0.69 aY=-0.31 aZ=0.81
Vitesse =3733.84 aX=-0.69 aY=-0.31 aZ=0.81
//
Vitesse =3406.41 aX=-0.69 aY=-0.06 aZ=0.88
Vitesse =3413.18 aX=-0.69 aY=-0.06 aZ=0.88
Vitesse =3419.95 aX=-0.69 aY=-0.25 aZ=0.88
Vitesse =3609.75 aX=-0.69 aY=-0.25 aZ=0.88
Vitesse =3616.93 aX=-0.63 aY=-0.25 aZ=1.12
Vitesse =3321.59 aX=-0.44 aY=-0.31 aZ=0.94
Vitesse =2658.25 aX=-0.44 aY=-0.31 aZ=0.94
Vitesse =2663.52 aX=-0.56 aY=-0.12 aZ=0.88
Vitesse =2843.32 aX=-0.31 aY=-0.25 aZ=1.06
Vitesse =1978.66 aX=-0.44 aY=-0.19 aZ=0.88
Vitesse =2344.05 aX=-0.44 aY=-0.19 aZ=0.88
Vitesse =2358.06 aX=-0.44 aY=-0.25 aZ=1.12
Vitesse =2491.36 aX=-0.31 aY=-0.25 aZ=1.00

Celles-ci semblent plus cohérentes malgré les valeurs de vitesses très importantes. Il me semble que la fonction millis() mesure le temps depuis la mise en tension de la carte. Je pense que l'utilisation de millis(), dans le code précèdent, permet de découper des intervalles de temps mais je ne sais pas si cela a fonctionné.

@JiPe38 D'accord, je vois mais il faudrait donc utiliser un autre capteur, comme par exemple un GPS, pour donner la position à chaque instant.

Tu devrais te renseigner sur les balises code <>
ton code est illisible.

et de plus les balises sont obligatoires.

PS : apprend à indenter ton code ( ctrl T dans l'IDe avant de copier)
Tu verras que c'est largement plus lisible.