Vitesse et capteur Hall

Bonjour,

J'ai comme projet de faire un tableau de bord assez complet (température ambiante/moteur, heure, date, km parcouru, etc ...). L'ensemble est quasi fonctionnel (affichage sur écran graphique 128*64).

Cependant l'affichage de la vitesse n'est pas assez rapide et pas précise au km/h près.

Après de nombreuses recherches je ne trouve pas mieux que le code que j'ai fait ci-dessous, le problème de celui-ci est qu'il rafraîchit toutes les 3 sec seulement et n'est pas vraiment précis (marge de 3 km environ, par ex il va afficher 6km/h ou 10km/h mais pas les valeurs intermédiaires).

Pour faire simple je calcul les rpm en comptant le nombre de tour pendant une période (3 sec) et en fonction de ceux-ci avec un calcul j'en déduis la vitesse... (Le code est assez simple et bien commenté pour être compréhensible je pense :slight_smile: ).

Je cherche donc à calculer la vitesse avec un rafraîchissement d'une seconde au plus et si possible précis au km/h près (à savoir qu'une fois ce problème résolu j'aimerais calculer la distance parcouru etc dans l'optique de la stocker sur sd (tripmaster)...).

Avez vous une solution à mon problème ? :smiley:
Je sèche et ça fait des heures que je cherche et trouve des solutions très souvent incomplète ou incompréhensible sur les forums et autre c'est pour cela que je me permet de créer un nouveau poste.

unsigned int vitesse = 0; // Vitesse de la roue (du véhicule)
unsigned int rpm = 0; // Trs/min de la roue
unsigned int chrono = 0; // Valeur courante du chrono
unsigned int chrono_depart = 0; // Valeur de départ du chrono
unsigned int duree_test = 3000; // Durée du test de "vitesse" en ms (ex : 3 sec = 3000ms)
int capteur = 2; //Pin d'entrée du capteur hall
unsigned int compteur = 0; //Valeur de compteur (compte chaque passage de l'aimant devan le capteur hall)
float diametreR = 0.6; // Diametre de la roue /10 (ex: diamètre = 60cm /10 = 0.6)


void compteurInc()
{
  compteur++; //Incrémentation du compteur
}

void setup()
{
  Serial.begin(9600);

  chrono_depart = millis();

  //La fonction compteurInc sera appelée sur chaque front descendant (FALLING) de "capteur" (pin 2=interuption 0)
  attachInterrupt(0, compteurInc, FALLING);
  digitalWrite(capteur, HIGH);
}

void loop()
{
  chrono = millis(); 

  if (chrono - chrono_depart > duree_test) { 
    
   rpm=compteur*20;// Ex : Si mesure sur 3 secondes : il faut multiplier le nb de chgt par 20 pour avoir le nb de changement par minute
   vitesse=(rpm*3.14/60)*diametreR*3.6; // Calcul de la vitesse en km/h -> (rpm*pi/60)*diametredelaroue*3,6"(pour passer de m/s en km/h -> 3600sec...)"
   Serial.print("RPM : ");
   Serial.print(rpm);
   Serial.print(" trs/min     ");  
   Serial.print("Vitesse : ");
   Serial.print(vitesse,1);         
   Serial.println(" km/h");     
   chrono_depart = millis(); // Mise à zéro du chrono de départ
   compteur=0; // Mise à zéro du compteur
   
  }     
  
}

PS : Cette personne à réussi à faire un truc qui semble être précis et rapide, je lui est posé la question et il m'a envoyé une partie de son code (voir commentaire) mais je pige pas grand chose à son affaire :stuck_out_tongue: [Projet #1] Compteur de vitesse pour vélo avec Arduino - YouTube

Bonjour,
Sans regarder ton soft, juste une petite remarque.
Tu fais le calcul de la vitesse en float et tu l'affectes à int donc pertes d'informations

Serial.print(vitesse,1);

Sera toujours un entier.

Effectivement, quand j'ai test avec la vitesse en float j'ai oublié de retirer la ,1 ;p.
Ce n'est ça le problème mais merci de l’avoir remarquer :smiley:

Salut,
que ce passe t'il si tu réduit duree_test à 1000 et que tu multiplie par 60 ?

Si je réduit la durée du test, l'affichage est plus rapide (1 / sec) mais c'est encore moins précis... :confused:

J'ai un peu regardé le code du compteur proposé par ElectroniZon et il semble assez similaire au tien. Il fait des mesures toutes les 4 secondes apparemment. Sauf que lui il spécifie bien des float partout en forçant le typage même pour les millis.

float vitesse = 0;
float distance = 0;
float tps = 0;

....etc..

void calcule() {
  float timems = float(timer[1] - timer[0]);
  float s = float(timems / 1000);
  vitesse = (2.05 / s) * 3.6;
  distance = distance + (2.05 * 2);
  if (timems < 4000) {
    tps = tps + ((timems * 0.001) * 2);
  }
}

Walex68:
.....................
Pour faire simple je calcul les rpm en comptant le nombre de tour pendant une période (3 sec) et en fonction de ceux-ci avec un calcul j'en déduis la vitesse... (Le code est assez simple et bien commenté pour être compréhensible je pense :slight_smile: ).
.................

Bonsoir,

Je pense que le problème se situe ici.
Avec le principe retenu, la précision est mauvaise pour les faibles vitesses.

Par exemple dans le cas de 1200 t/mn cela fait 20 t/s en faisant un comptage toutes les s l'imprécision sera de 5%..... et rafraichir l'affichage toutes les secondes n'est pas confortable.
Il vaudrait mieux compter la durée d'une période (1 tour)
En reprenant l'exemple précédent 1 tour correspond à 50 ms, si le chronométrage est précis avec une erreur de 5µs (fonction micros() par exemple) alors l'imprécision ne serait que de 5/50.000 soit 0.01%
Pour cela l'usage d'un fonctionnement en interruption est obligatoire avec un s/p d'interruption qui tourne de façon autonome pour mettre à jour la variable période. l'affichage se faisant de façon asynchrone, par exemple en affichant 10 fois par s.

En résumé réaliser un période mètre à la place d'un fréquencemètre.

Serge .D

J'ai essayé avec tout en float et ça ne change rien, dans tout les cas le rafraîchissement est de 3 sec (durée "d'analyse") :confused:

Par ex : avec 1000ms au lieu de 3000, ça rafraîchis toute les secondes mais on perd en précisions..

  • 1 sec = palier de 6 km/h

Bonne idée Serge ! Je pensais justement à un résonnement comme ça... Je vais étudier ça demain et vous tiens au jus :wink:

Après réflexion j'ai fait un code qui compte le nombre de micro secondes en 1 tour, ça fonctionne bien... Maintenant le problème est de convertir le temps en fonction de la distance que parcourt la roue en 1 tour (1,8m environ, celle ci fait 556 tours pour faire 1km...) et là ça fait mal au cerveau de réfléchir à une conversion entre 2 unités différente haha :p, je n'arrive pas à trouver ce foutu calcul qui pourtant ne doit pas être bien compliqué ! Avez vous une idée ? :smiley:

//float vitesse = 0; // Vitesse de la roue (du véhicule)
//float rpm = 0; // Trs/min de la roue
unsigned int chrono = 0; // Valeur courante du chrono
unsigned int chrono_depart = 0; // Valeur de départ du chrono
//unsigned int duree_test = 3000; // Durée du test de "vitesse" en ms (ex : 3 sec = 3000ms)
int capteur = 2; //Pin d'entrée du capteur hall
unsigned int compteur = 0; //Valeur de compteur (compte chaque passage de l'aimant devan le capteur hall)
//float diametreR = 0.6; // Diametre de la roue /10 (ex: diamètre = 60cm /10 = 0.6)
boolean x = false;

void compteurInc()
{
  compteur++; //Incrémentation du compteur
}

void setup()
{
  Serial.begin(9600);

  chrono_depart = micros();

  //La fonction compteurInc sera appelée sur chaque front descendant (FALLING) de "capteur" (pin 2=interuption 0)
  attachInterrupt(0, compteurInc, FALLING);
  digitalWrite(capteur, HIGH);
}

void loop()
{
  x = false;
  
  while (x == false){

    if (compteur == 1) {
      chrono = micros();
    }
     
    if (compteur == 2) {
      detachInterrupt(capteur);
      Serial.println(chrono - chrono_depart);
      x = true;
      attachInterrupt(0, compteurInc, FALLING);
    }
  
  }

   chrono_depart = micros(); // Mise à zéro du chrono de départ
   compteur=0; // Mise à zéro du compteur
   
}

Bonjour,

La vitesse c'est d/t: la distance que divise le temps
Pour calculer la vitesse en km/h tu divise la distance en km par le temps en heure

distance en km 1.8[m]/1000
temps en heure T[µs]/1000000/3600

D’où vitesse [km/h]:1.8/1000/(T/1000000.0/3600)

Ta méthode pour mesurer la période est compliquée (et très imprécise). Tu peux mesurer la période directement en interruption.
de plus chrono et chrono_depart doivent être des unsigned long

Merci ! J'ai trouvé un calcul un peut plus simple qui donne le même résultat :slight_smile: :

vitesse = (3600000/chrono)*1.88

D'accord, j'ai fait ce code en fonction de mes connaissances :D.
Après test avec le calcul, celui ci fonctionne en revanche le calcul de temps est en effet très imprécis et aléatoire :/.
Donc le "chrono" est à revoir..

Ce n'est pas plus simple c'est la même chose en regroupant les constantes. J'avais fait un calcul didactique.
Il faut faire le calcul en flottant
vitesse = (3600000.0/(chrono-chrono_depart))*1.88

Il faut mesurer la période en interruption.

Avec ce calcul les vitesses affiché par rapport au temps sont très précises en revanche le calcul de la période souffre de bug (freeze ou temps incorrect).

Je ne comprend pas trop ce que vous voulez dire par "mesurer la période en interruption", je les utilises déjà non ? Il y a t il une autre méthode ou fonction ?

float vitesse = 0; // Vitesse de la roue (du véhicule)
unsigned long chrono = 0; // Valeur courante du chrono
unsigned long chrono_depart = 0; // Valeur de départ du chrono
int capteur = 2; //Pin d'entrée du capteur hall
unsigned int compteur = 0; //Valeur de compteur (compte chaque passage de l'aimant devan le capteur hall)
boolean x = false;
float tps;

void compteurInc()
{
  compteur++; //Incrémentation du compteur
}

void setup()
{
  Serial.begin(9600);

  chrono_depart = micros();

  //La fonction compteurInc sera appelée sur chaque front descendant (FALLING) de "capteur" (pin 2=interuption 0)
  attachInterrupt(0, compteurInc, FALLING);
  digitalWrite(capteur, HIGH);
}

void loop()
{
  x = false;
  
  while (x == false){

    if (compteur == 1) {
      chrono = micros();
    }
     
    if (compteur == 2) {
      detachInterrupt(digitalPinToInterrupt(capteur));
      vitesse = (3600000.0/(chrono - chrono_depart))*1.88;
      Serial.print("Temps (microsec) : ");
      Serial.println(chrono - chrono_depart);
      Serial.print(vitesse);
      Serial.println(" km/h");
      x = true;
      attachInterrupt(0, compteurInc, FALLING);
    }
  
  }

   chrono_depart = micros(); // Mise à zéro du chrono de départ
   compteur=0; // Mise à zéro du compteur
   
}

Dans ton programme tu incrémentes un compteur en interruption, tu ne mesures pas la période.
Tu mesures la période dans la loop() et comme tu initialises chrono_depart dans la loop() il n'est pas synchronisé avec le signal.

Exemple de programme qui mesure la période en interruption:

unsigned long chrono; // Valeur courante du chrono
unsigned long chrono_depart = 0; // Valeur de départ du chrono
unsigned long periode;
bool flagMesure = false;

void compteurInc()
{
  chrono = micros();
  periode = chrono - chrono_depart;
  chrono_depart = chrono;
  flagMesure = true;    // on a une mesure de période valide
}

void setup()
{
  Serial.begin(9600);

  //La fonction compteurInc sera appelée sur chaque front montant de "capteur" (pin 2=interuption 0)
  attachInterrupt(0, compteurInc, RISING);
  //  digitalWrite(capteur, HIGH);
}

void loop()
{
  if (flagMesure)
  {
    noInterrupts();
    unsigned long t = periode;
    flagMesure = false;
    interrupts();
    float vitesse=1.88/1000/(t/1e6/3600);
    Serial.print(t);
    Serial.print(" -> ");
    Serial.println(vitesse);
  }
}

J'ai suivi un peu de loin la discussion sur le calcul de la vitesse par rapport au temps. Par contre n'ayant pas d’expérience dans les capteurs a effet hall j'ai navigué un peu sur le web pour comprendre.

J'ai trouvé un code sur ce post qui me semble bien plus simple et qu'il serait sûrement intéressant de tester et/ou d'adapter. Le calcul de la vitesse est réalisée dans le programme d’interruption et il utilise une astuce intéressante avec millis() sans doute reproductible avec micros(). Il reprend pas mal l'aspect de celui précédent de Kamill

Voilà si cela peux rendre service:

//
// Débutant: Tachymètre à sonde effet Hall
// http://forum.arduino.cc/index.php?topic=78333.0
//
volatile unsigned long time = 0; // time en ms depuis le démarrage de l'arduino
volatile unsigned long rpm = 0; // vitesse en tr/min

void setup() { // Fonction éxécuté au démarage de l'arduino
 Serial.begin(9600); // Configuration du port série en 9600bauds
 pinMode(2, INPUT); // Broche D2 en entrée
 attachInterrupt(0, int_cb, FALLING); // Configuration de l'interruption INT0(D2) pour pointer sur la fonction int_cb en cas de front descendant
}

void loop() { // Fonction éxécuté en boucle jusqu'a l'arret de l'arduino
 Serial.println(rpm); // Affiche la vitesse de rotation
 delay(1000); // Attend une seconde
}

void int_cb() { // Fonction appelé lors d'un front descendant (HIGH -> LOW) sur la broche D2 (INT0)
 rpm = (millis() - time) / 60000; // Calcul de la vitesse par rapport au temps précédant
 time = millis(); // Astuce: millis() est "freezé" lors du interruption, sa valeur ne s'incrémentera pas, inutile de crée une variable pour stocker le nouveau temps
}

Bonjour landid,

C'est un programme très (trop ?) simplifié que tu as trouvé.
Il fonctionne sans doute pour des vitesses faibles (mais peut être que ça suffit à notre ami Walex68)

Il y a quand même plusieurs inconvénients

  • calcul en ms
  • calcul en entier -> la résolution peut être insuffisante à haute vitesse
  • pas de synchronisation entre le programme loop et le programme d'interruption. La valeur à peut avoir été rafraichie 10 fois entre chaque affichage.
  • pas de protection si la valeur rpm est modifiée pendant qu'on l'affiche
  • il faut essayer d'avoir des interruptions les plus courtes possibles or les multiplications/divisions prennent pas mal de temps.

Merci pour vos réponses, le programme de kamill me semble une bonne base, je l'est testé et il fonctionne...
Je vais l'adapter à mes besoins, notamment la remise à zéro de la valeur vitesse passé 2 ou 3 sec d'inactivité pour ne pas afficher une valeur positive alors que le véhicule est à l'arrêt et pour pouvoir calculer les distances parcouru...

Je verrais ça demain, je vous tiens au jus, merci encore pour votre aide !

Effectivement comme tu l'as vu, il faut ajouter le cas de l'arrêt ou vitesse très faible.

Des news ! Je n'ai pas eu le temps de m'en occuper hier mais je poste le code "fini" de la partie calcul de la vitesse avec mise à zéro de l'affichage au bout d'un temps donné (3 sec). Il ne reste plus qu'à faire les calculs de distances etc.. :wink:

Merci pour votre aide ! Je pense que ce code va en aider beaucoup, souvent quand la solution est trouvé le code final et fonctionnel n'est pas posté :confused: !

unsigned int capteur = 2; // Pin capteur à effet Hall
unsigned long chrono; // Valeur courante du chrono
unsigned long chrono_depart = 0; // Valeur de départ du chrono
unsigned long chrono_2; // Valeur courante du chrono_2
unsigned long chrono_depart_2 = 0; // Valeur de départ du chrono_2
unsigned long periode; // Periode calculé pour 1 tour de roue
unsigned int duree_inactivite = 3000; // Durée d'inactivité de la vitesse (si le véhicule n'avance plus pendant 3 sec)
float vitesse = 0; // Varriable de vitesse
float periR = 1.884; // Périmètre de la roue (en mètre) -> diamètre*3.14/100 (ex: diamètre = 60cm *3.14 /100 = 1,884m)
bool flagMesure = false; // "Concerne la mesure de vitesse"

void compteurInc()
{
  chrono = micros();
  periode = chrono - chrono_depart;
  chrono_depart = chrono;
  flagMesure = true;    // On a une mesure de période valide
}

void setup()
{
  Serial.begin(9600);

  chrono_depart_2 = millis();

  //La fonction compteurInc sera appelée sur chaque front descendant (FALLING) de "capteur" (pin 2 = interuption 0)
  attachInterrupt(0, compteurInc, FALLING);
  digitalWrite(capteur, HIGH);
 
}

void loop()
{
  chrono_2 = millis();

  if (flagMesure == false && (chrono_2 - chrono_depart_2 > duree_inactivite)){
    vitesse = 0;
    Serial.print(vitesse,0);
    Serial.println(" km/h");
    delay(500);
  }
  else if (flagMesure)
  {
    noInterrupts();
    unsigned long t = periode;
    flagMesure = false;
    interrupts();
    vitesse=periR/1000/(t/1e6/3600); //Calcul de la vitesse en fonction de la période et de la distance que parcour la roue en 1 tour (périmètre). Autre méthode : (3600000.0/t)*periR
    Serial.print(vitesse,1);
    Serial.println(" km/h");
    chrono_depart_2 = millis(); // Mise à zéro du chrono de départ 2 (compteur d'inactivité)
  }
  
}