Tachymètre très instable!

Bonjour,
je viens de réaliser le tachymètre dont il est question dans le post ci-dessous qui est actuellement fermé.

J'ai fait tout ce qui est recommandé et globalement la mesure fonctionne mais pas correctement à mon sens.
Chaque passage devant mon capteur IR génère plusieurs révolutions, je fais l'exercice en passant un obstacle toute les secondes et le nombre de "rev" renvoyé peut varie et peut aller jusqu'à une dizaine de détections.
J'ai regardé à l'oscillo et mon capteur IR ne génère pas de rebond !
Je suis perdu :sleepy:
Ce qui m'interpelle c'est que toutes les vidéos que je visionne n'ont pas ce problème.

Merci pour vos conseils à venir

:warning:
Post mis dans la mauvaise section, on parle anglais dans les forums généraux. déplacé vers le forum francophone.

Merci de prendre en compte les recommandations listées dans Les bonnes pratiques du Forum Francophone

postez le code (avec les balises de code), le schéma de votre circuit, décrivez comment c'est alimenté et donnez des liens vers les composants choisis

Ci-joint le code

#include <Arduino.h>

#include <LiquidCrystal.h>
#include <Softwareserial.h>

LiquidCrystal lcd(12,11,6,5,4,3);

float value=0;
volatile float rev=0;
int rpm=0;
int oldtime=0;
int time=0;

const byte interruptPin = 2;

void isr() 
{
rev++;
Serial.print("rev isr : ");Serial.print(rev); //pour voir ce que lit le compteur
}

void setup()
{
lcd.begin(16,2);                //initialize LCD
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin),isr,FALLING); // mon capteur passe de 1 à 0 lors de la détection

Serial.begin(9600);
}

void loop()
{
delay(1000);
detachInterrupt(digitalPinToInterrupt(interruptPin));
time=millis()-oldtime;        //finds the time 
Serial.print("rev : ");Serial.println(rev);
rpm=((rev/time)*60000);         //calculates rpm
oldtime=millis();             //saves the current time
lcd.clear();
lcd.setCursor(0,0);
lcd.print("rev ");lcd.print(rev);
Serial.print("tr/mn : ");Serial.println(rpm);
lcd.setCursor(0,1);
lcd.print(     rpm);
lcd.print("     tr/mn");
rev=0;
attachInterrupt(digitalPinToInterrupt(interruptPin),isr,FALLING);//**********************

}

puis le schéma du circuit (la seule différence est que j'utilise un Arduino UNO au lieu du NANO et l'alimentation est par l'intermédiaire du câble USB de l'Arduino

image

Le capteur IR est un module HW-201
image

Pin "OUT" reliée à "PIN 2" de l'Arduino

Cordialement

Bonjour,

  • Pas de print dans la routine d'interruption.
  • Pourquoi ne pas utiliser un int ou un long int pour rev plutôt qu'un float.
    Combien de révolution penses-tu avoir par seconde (pour choisir le type de la variable le mieux adapté)?
    Il faudrait alors faire ceci

rpm=(((float) rev/time)*60000.0);

  • il faudrait déplacer la ligne suivante juste avant de réautoriser les interruptions.

oldtime=millis();

  • As-tu vérifié que le seuil est bien réglé sur le module IR?
  • Pas de print dans la routine d'interruption :
    --> je l'ai mis pour voir quelles valeurs prenaient la variable "rev", maintenant supprimé.

  • Pourquoi ne pas utiliser un int ou un long int pour rev plutôt qu'un float.
    ---> Je ne sais pas pourquoi mais si je mets int ou long , la valeur de rpm reste à "0" ! Il n' y plus de valeur de vitesse affichée.

  • Combien de révolution penses-tu avoir par seconde (pour choisir le type de la variable le mieux adapté)? :
    --> 50 rev/s (mesure d'une vitesse de rotation de 3000 tr*min maxi

  • il faudrait déplacer la ligne suivante (oldtime=millis() juste avant de réautoriser les interruptions --> pas de changement sur le fonctionnement.

Valeurs de rev et rpm retournées lorsque je fait une détection par seconde environ. Je ne comprends pas pourquoi les valeurs de rev ne sont pas de l'ordre de 1 ou 2.
rev : 0.00
tr/mn : 0
rev : 0.00
tr/mn : 0
rev : 9.00
tr/mn : 536
rev : 13.00
tr/mn : 773
rev : 14.00
tr/mn : 833
rev : 15.00
tr/mn : 893
rev : 31.00
tr/mn : 1847
rev : 19.00
tr/mn : 1132
rev : 22.00
tr/mn : 1309
rev : 0.00
tr/mn : 0

Pourquoi faire ??

tout ce qui a trait au temps devrait être en unsigned long (time, oldTime ) et pour avoir un peu de précision ce serait mieux de mettre

oldtime=millis();             //saves the current time

juste avant de réactiver les interruptions

Bonjour,

Ce type de capteur est très sensible à la lumière ambiante. Est ce qu'il est protégé de la lumière?

#include <Softwareserial.h>
--> je pensais que c'était un pré requis pour utiliser le moniteur série (je programme avec VS Code. Je l'ai mis en commentaire et ça va encore.

tout ce qui a trait au temps devrait être en unsigned long (time, oldTime )
--> modifié.

oldtime=millis();
--> déjà mis avant la réactivation des interruptions (post de fufnews)

postez le nouveau code "propre"

Je viens de protéger le capteur de la lumière mais pas de changement significatif sur la mesure des rev.
Pour les tests, je fais de détections avec un intervalle de temps de l'ordre de 60 à 120 rev/min (je m'aide d'un méttronome pour avoir ce rythme de 60 à 120 rev/s). Comme dit précedemment, en même temps j'ai visualisé le signal à l'oscilloscope et il n'y a pas de rebond ou de arasite de la mesure, je vois bien les variations de seuils haut à bas au rythme cité précedemment.

Bonjour daniel57330

J'ai essayé ton programme avec un générateur sur 50 Hz.
Globalement, il fonctionne correctement.

Pour que tes valeurs "sautent" autant, c'est possible la liaison GND du capteur avec le GND de l'Arduino ne soit pas bonne.

Dans ton programme, je n'ai pas vu l'initialisation de la pin d'interruption, pour la clareté de lecture du programme c'est préférable de la mettre;
pinMode(interruptPin, INPUT);
ou
pinMode(interruptPin, INPUT_PULLUP);
Je ne connais pas ton capteur, mais essaies avec PULL_UP, ca ne risque rien et ça assure.

Cordialement
jpbbricole

#include <Arduino.h>

#include <LiquidCrystal.h>

LiquidCrystal lcd(12,11,6,5,4,3);

float value=0;
volatile float rev=0.0;
int rpm=0;
unsigned long oldtime=0;
unsigned long time=0;
const byte interruptPin = 2;

void isr() 
{
rev++;
}

void setup()
{
lcd.begin(16,2);                //initialize LCD
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin),isr,FALLING); // mon capteur passe de 1 à 0 lors de la détection

Serial.begin(9600);
}

void loop()
{
delay(1000);
detachInterrupt(digitalPinToInterrupt(interruptPin));
time=millis()-oldtime;        //finds the time 
rpm=((rev/time)*60000);         //calculates rpm
//oldtime=millis();             //saves the current time -------> deplace avant attachInterrupt
lcd.clear();
lcd.setCursor(0,0);
lcd.print("rev ");lcd.print(rev);
lcd.setCursor(0,1);
lcd.print(     rpm);
lcd.print("     tr/mn");
Serial.print("rev : ");Serial.print(rev);
Serial.print("   tr/mn : ");Serial.println(rpm);
rev=0;
oldtime=millis();
attachInterrupt(digitalPinToInterrupt(interruptPin),isr,FALLING);//**********************

}

Ce code fonctionne parfaitement.
Résultat affiché entre 2997 et 3003 pour un générateur à 50Hz.
La mesure fonctionne correctement jusque vers 100kHz en déclarant rev en unsigned long.
Donc il faudrait peut-être creuser du coté de ton capteur.

EDIT: comme le dit @jpbbricole, la mesure est plus stable en supprimant les attach/detachInterrupt()

Merci à tous pour le temps passé.

Bonjour fdufnews

Je suis revenu sur mes constations, l'amélioration n'est pas si évidente j'ai supprimé cette partie, n'empêche que je n'utilise pas ces fonctions dans mes programmes compte/tours ou compteurs de vitesse, sans conséquences.

Navré pour mes hésitations :woozy_face:

Cordialement
jpbbricole

Bonjour fdufnews

Pour que ça "saute" autant, j'y vois un problème de contacts entre les GND.

Cordialement
jpbbricole

Je viens de changer de capteur et là je trouve une nette amélioration en stabilité.
Je vais pouvoir passer à une réalisation plus propre pour la mise en oeuvre sur la machine dont je veux suivre la vitesse de rotation.
Encore merci.

Bonsoir daniel57330

Ici il fait un temps à bricoler dedans :wink:

Dans ton programme, tu fais un delay(1000); et tu chronomètre cette même période avec tes 2 variables:

unsigned long oldtime=0;
unsigned long time=0;

Autant supprimer ce delay() et tout faire avec des millis(), ainsi:

#include <Arduino.h>

#include <LiquidCrystal.h>

LiquidCrystal lcd(12,11,6,5,4,3);

float value=0;
volatile float rev=0.0;
int rpm=0;
//unsigned long oldtime=0;
//unsigned long time=0;
const byte interruptPin = 2;

unsigned long mesureTempo = 1000;
unsigned long mesureMillis = millis();
void isr()
{
	rev++;
}

void setup()
{
	lcd.begin(16,2);                //initialize LCD
	pinMode(interruptPin, INPUT_PULLUP);
	attachInterrupt(digitalPinToInterrupt(interruptPin),isr,FALLING); // mon capteur passe de 1 à 0 lors de la détection

	Serial.begin(9600);
}

void loop()
{
	//delay(1000);
	if (millis() - mesureMillis >= mesureTempo)
	{
		//detachInterrupt(digitalPinToInterrupt(interruptPin));
		//time=millis()-oldtime;        //finds the time
		rpm=((rev/mesureTempo)*60000);         //calculates rpm
		//oldtime=millis();             //saves the current time -------> deplace avant attachInterrupt
		lcd.clear();
		lcd.setCursor(0,0);
		lcd.print("rev ");lcd.print(rev);
		lcd.setCursor(0,1);
		lcd.print(     rpm);
		lcd.print("     tr/mn");
		Serial.print("rev : ");Serial.print(rev);
		Serial.print("   tr/mn : ");Serial.println(rpm);
		rev=0;
		//oldtime=millis();
		//attachInterrupt(digitalPinToInterrupt(interruptPin),isr,FALLING);//**********************

		mesureMillis = millis();
	}
}

Pour changer le tempo de mesure, il suffit de changer la valeur de mesureTempo.
Ainsi ton programme ne sera plus bloqué, quasiment, en permanence.

Le problème avec la méthode de comptage des impulsions pendant un temps imparti, 1 seconde dans ton cas et une fréquence basse, un variation d'une impulsion dans la seconde fait varier le résultat des RPM de 60

rev : 50.00   tr/mn : 2940
rev : 50.00   tr/mn : 3000
rev : 51.00   tr/mn : 3000
rev : 50.00   tr/mn : 2940
rev : 50.00   tr/mn : 3000
rev : 50.00   tr/mn : 3000
rev : 51.00   tr/mn : 3000

C'est mieux de calculer les RPM d'après le temps d'une période, mais ce sera pour la prochaîne version.

A+
Cordialement
jpbbricole

Pour les mesures de fréquences relativement basses, on obtient de meilleurs résultats en mesurant la période plutôt que la fréquence.

#include <Arduino.h>


unsigned long per = 0.0;
float rpm = 0;
volatile unsigned long oldtime = 0;
volatile unsigned long time = 0;
const byte interruptPin = 2;
boolean newtime = false;

void isr() {
  oldtime = time;
  time = micros();
  newtime = true;
}

void setup() {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), isr, FALLING);

  Serial.begin(115200);
}

void loop() {
  if(newtime){
    Serial.print("Periode : ");
    Serial.print(per = time - oldtime);
    Serial.print("   Frequence : ");
    Serial.println(1e6/(float) per, DEC);
    delay(500);
    newtime = false;
  }
}