Mesure de vitesse par codeur

Bonjour,
Je souhaite réaliser des mesures de vitesses sur un petit véhicule électrique (vmax environ 30 / 40 km/h)
Pour cela j'ai envisagé d'utiliser un codeur incrémental que j'avais à disposition (360 pt/tr, voie A et B accessible)
J'ai écrit un code qui "fonctionne" dans un sens de rotation (à confirmer par expérimentation pour contrôler les valeurs reçues dans le moniteur série) mais cela ne marche pas dans l'autre et j'avoue sécher.
Viendra ensuite d'essayer d'afficher cela sur un écran (certainement oled).

Merci d'avance pour votre aide.

`#define encoder0PinA  2
#define encoder0PinB  3

volatile long encoder0Pos=0;
long newposition;
long oldposition = 0;
unsigned long newtime;
unsigned long oldtime = 0;
float vel; //nb de pt codeur par seconde
float trs; //vitesse rotation en tr/s
float trmin; //vitesse rotation en tr/min
float vitesse; //vitesse en m/s
float circonference=2; //circonference en m
float kmh; //vitesse en km/h

void setup()
{
  pinMode(encoder0PinA, INPUT);
  pinMode(encoder0PinB, INPUT);

  attachInterrupt(0, doEncoder,CHANGE);  // lance la fonction void doEncoder ; change = front montant et descendant
  Serial.begin (115200);

}

void loop()
{
newposition = encoder0Pos;
newtime = millis();
vel = (newposition-oldposition) * 1000 /(newtime-oldtime);

trs=vel/720;  //codeur 360 pt/tour, avec CHANGE -> 360*2=720

trmin=trs*60; 

vitesse=trs * circonference;

kmh= vitesse * 3.6;



Serial.print ("pts = ");
Serial.print (vel);

Serial.print ("   tr/s = ");
Serial.print (trs);

Serial.print ("   tr/min = ");
Serial.print (trmin);

Serial.print ("   m/s = ");
Serial.print (vitesse);

Serial.print ("   km/h = ");
Serial.println (kmh);


oldposition = newposition;
oldtime = newtime;
delay(500);
}

void doEncoder()
{
  if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
    encoder0Pos++;
  } else {
    encoder0Pos--;
  }
}`

Bonjour speedmatt

Tu te simplifierai le vie en utilisant une librairie comme encoder.h.

Cordialement
jpbbricole

Il y a un problème ici, mais je ne pense pas trop que ce soit l'erreur cherchée.
Avec la ligne ci-dessus, comme il s'agit de long, le transfert se fait en 4 fois, et si on n'a pas de chances une modification de encoder0Pos peut intervenir pendant le transfert. Il faut empêcher cette modification pendant le transfert, par exemple en n'autorisant pas les interruption.

On peut remplacer la ligne par:

nointerrupts(); // Pas d'interruption -> pas de changement de encoder0Pos
newposition = encoder0Pos;
interrupts(); // Retour à la normale

En marche arrière, le calcul ne donne pas toujours une vitesse négative si
à T0, position = -50
à T1, position = -150
-50 - -150 = 100 tu vas donc avoir une vitesse positive.

De même, si on va en marche avant mais que :
à T0, position = -50
à T1, position = 150
-50 - 150 = -200 on a une vitesse négative

Il faut tester le signe des arguments avant de faire le calcul pour le réintroduire ensuite.

Hello
J'ai un gros doute sur la pertinence du choix d'un codeur incremental pour des vitesses de 30/40 km/h. Un top par tour de roue suffirait.

on souhaite être précis sur les très basses vitesses, c'est pour cela que nous nous sommes orienté vers un codeur, peut être une erreur ?

Bonjour speedmatt
Non, ça dépend du diamètre de la roue, donc du nombre de tours/minute à vitesse maximum.
Mais, je me répète, avec une bibliothèque (post #2) , tu n'as pas besoin de t'inquièter de tout ça.

Cordialement
jpbbricole

360 pions/tour de roue en K/heure...

Prends une calto et calcule le nombre de pions pour un déplacement rapide et un déplacement lent.
Ton micro va passer son temps à faire des interruptions.
Une précision de 1 tour de roue me paraît largement suffisante.

Précis, c'est-à-dire?
Donne des chiffres.

  • gamme de vitesse
  • précision attendu
  • dimension de la roue

Entre 1 et 360 pulse/tour il y a de la place pour d'autres options.

On était partit sur ce codeur car on l'avait, mais nous restons ouvert à toutes idées ou propositions surtout si c'est plus simple, fiable et performant.

Ce véhicule devrait en général évoluer autour des 25 km/h, Vmax environ 40 km/h
On souhaite obtenir une précision de l'ordre du dixième de km/h.
Les roues sont sur des jantes de 16 pouces, je vais mesurer la circonférence exacte.
On cherche a travailler autour du pt de fonctionnement optimal du moteur afin d'avoir le moins mauvais rendement. En parallèle nous allons travailler sur faire les mesures U, I ->P électrique, E=P*t pour essayer d'obtenir un rapport E électrique au km. (pas simple pour nous mais nos allons essayer)

Bonjour speedmatt

Concernant ton programme et ton montage, as-tu mis des résistances de PULLUP ? les codeurs n'en ont pas, en général.

si non, initialises tes entrées ainsi:

pinMode(encoder0PinA, INPUT_PULLUP);|
pinMode(encoder0PinB, INPUT_PULLUP);|

Cordialement
jpbbricole

Oui, nous en avons mis mais après essai (avec et sans) nous n'avons pas vu la moindre différence.

Il faudrait savoir si ton codeur incrémental sort en collecteur-ouvert ou en totem-pole.
Dans le premier cas, si tu ne mets pas de pullups, tu risques de rencontrer des problèmes d'impulsions manquées car les entrées ne retourneront pas franchement au niveau haut.

C'est un projet scolaire?

Bonsoir speedmatt

Ces codeurs nécessitent des résistances.
J'ai testé ton programme, le comptage se fait très bien, 720 ticks par tour, du fait que tu n'utilises que le port A, avec A et B, c'est 1440 ticks par tour.
Un truc à bannir dans ce genre d'application, est delay(), c'est mieux de travailler avec millis(). Je t'ai fait l'exemple avec ton application. Ca fonctionne avec 2 variables:

unsigned long mesureTempo = 500;     // Rythme de mesure
unsigned long mesureMillis = millis();     // Rythme de mesure, chrono

Essaies ça:

#define encoder0PinA  2
#define encoder0PinB  3

volatile long encoder0Pos=0;
long newposition;
long oldposition = 0;
unsigned long newtime;
unsigned long oldtime = 0;
float vel; //nb de pt codeur par seconde
float trs; //vitesse rotation en tr/s
float trmin; //vitesse rotation en tr/min
float vitesse; //vitesse en m/s
float circonference=2; //circonference en m
float kmh; //vitesse en km/h

unsigned long mesureTempo = 500;     // Rythme de mesure
unsigned long mesureMillis = millis();     // Rythme de mesure, chrono

void setup()
{
	pinMode(encoder0PinA, INPUT_PULLUP);
	pinMode(encoder0PinB, INPUT_PULLUP);

	attachInterrupt(digitalPinToInterrupt(encoder0PinA), doEncoder,CHANGE);  // lance la fonction void doEncoder ; change = front montant et descendant
	Serial.begin (115200);

}

void loop()
{
	if (millis() - mesureMillis >= mesureTempo)     // Si c'est le temps de la mesure
	{
		newposition = encoder0Pos;
		newtime = millis();
		vel = (newposition-oldposition) * 1000 /(newtime-oldtime);

		trs=vel/720;  //codeur 360 pt/tour, avec CHANGE -> 360*2=720

		trmin=trs*60;

		vitesse=trs * circonference;

		kmh= vitesse * 3.6;

		
			Serial.print ("pts = ");
			Serial.print (vel);
		
			Serial.print ("   tr/s = ");
			Serial.print (trs);
		
			Serial.print ("   tr/min = ");
			Serial.print (trmin);
		
			Serial.print ("   m/s = ");
			Serial.print (vitesse);
		
			Serial.print ("   km/h = ");
			Serial.println (kmh);


			oldposition = newposition;
			oldtime = newtime;

		mesureMillis = millis();     // Redémarrage du chrono
	}
	//delay(500);
}

void doEncoder()
{
	if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
		encoder0Pos++;
		} else {
		encoder0Pos--;
	}
}

A+
Cordialement
jpbbricole

Il faudrait savoir si ton codeur incrémental sort en collecteur-ouvert ou en totem-pole.
Dans le premier cas, si tu ne mets pas de pullups, tu risques de rencontrer des problèmes d'impulsions manquées car les entrées ne retourneront pas franchement au niveau haut.

Sur le datasheet il est inscrit à output circuit : Open collector NPN, Push pull, voltage, line driver (5V DC only)

C'est pour cela que nous avions mis une résistance de 10 k entre le 5v et le port A et pareil pour le port B. Mais sans explication cela fonctionne visiblement à l'identique sans ...

Un truc à bannir dans ce genre d'application, est delay() , c'est mieux de travailler avec millis().

Oui en effet, ce sera bien mieux ainsi on évite de mettre en pause inutilement.

Maintenant il faut que je me penche sur le soucis de la marche arrière (autre sens de rotation) qui doit en effet être lié au soustraction.

Bonsoir speedmatt

De ce côté là, ta fonction void doEncoder() fonctionne très bien, il faut voir ça dans tes autres calculs.
Question subsidiaire, est ce que tu dois indiquer la vitesse en marche arrière soit -15,7 km/h ou indiquer 15,7 km/h et un indicateur de marche arrière comme << ?

Cordialement
jpbbricole

Question subsidiaire, est ce que tu dois indiquer la vitesse en marche arrière soit -15,7 km/h ou indiquer 15,7 km/h et un indicateur de marche arrière comme << ?

Ca j'aurai tendance à dire peut importe, le conducteur s'apercevra de lui même si il avance ou recule, donc un - un signe quelconque ou rien fera l'affaire.

En tout cas je remercie tous ceux qui prenne le temps de lire et d'essayer de m'aider.
Cordialement.

Bonjour speedmatt

C'est assez simple à gérer, si vel < 0.0, marche arrière, si vel > 0.0 = marche avant. Une fois ce sens enregistré, il suffit de mettre vel en positif avec vel = abs(vel);, ainsi tout le reste ne change pas.

Du fait d'une boucle d'affichage réglée par ces 2 variables:

unsigned long mesureTempo = 500;     // Rythme de mesure
unsigned long mesureMillis = millis();     // Rythme de mesure, chrono

ces variables

unsigned long newtime;
unsigned long oldtime = 0;

ne sont plus nécessaires, c'est mesureTempo qui est utilisée dans les calculs.

Essaies ça:

#define encoder0PinA  2
#define encoder0PinB  3

volatile float encoder0Pos=0;
float newposition;
float oldposition = 0;
//unsigned long newtime;
//unsigned long oldtime = 0;
float vel; //nb de pt codeur par seconde
float trs; //vitesse rotation en tr/s
float trmin; //vitesse rotation en tr/min
float vitesse; //vitesse en m/s
float circonference=2; //circonference en m
float kmh; //vitesse en km/h

unsigned long mesureTempo = 500;     // Rythme de mesure
unsigned long mesureMillis = millis();     // Rythme de mesure, chrono

void setup()
{
	pinMode(encoder0PinA, INPUT_PULLUP);
	pinMode(encoder0PinB, INPUT_PULLUP);

	attachInterrupt(digitalPinToInterrupt(encoder0PinA), doEncoder,CHANGE);  // lance la fonction void doEncoder ; change = front montant et descendant
	Serial.begin (115200);

}

void loop()
{
	if (millis() - mesureMillis >= mesureTempo)     // Si c'est le temps de la mesure
	{
		boolean marcheAvant = true;
		String sensDeMarche;

		newposition = encoder0Pos;

		vel = (newposition-oldposition) * 1000.0 /mesureTempo;

		if (vel < 0.0)
		{
			marcheAvant = false;
			sensDeMarche =  " << ";
		}
		else if (vel > 0.0)
		{
			marcheAvant = true;
			sensDeMarche =  " >> ";
		}
		else
		{
			marcheAvant = true;
			sensDeMarche =  " -- ";
		}
		
		vel = abs(vel);     // vel en positif

		trs=vel/720.0;  //codeur 360 pt/tour, avec CHANGE -> 360*2=720

		trmin=trs*60.0;

		vitesse=trs * circonference;

		kmh= vitesse * 3.6;

		
		Serial.print (sensDeMarche + " pts = ");
		Serial.print (vel);
		
		Serial.print ("\ttr/s = ");
		Serial.print (trs);
		
		Serial.print ("\ttr/min = ");
		Serial.print (trmin);
		
		Serial.print ("\tm/s = ");
		Serial.print (vitesse);
		
		Serial.print ("\tkm/h = ");
		Serial.print (kmh);

		Serial.println("");

		oldposition = newposition;
		mesureMillis = millis();     // Redémarrage du chrono
	}
}

void doEncoder()
{
	if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
		encoder0Pos++;
		} else {
		encoder0Pos--;
	}
}

A+
Cordialement
jpbbricole