Programme faire tourner un moteur brushless à vitesse constante.

Bonjour. Avant de vous présenter mon problème je m'excuse par avance pour la répétition de mes messages sur le même sujet depuis quelques mois.

Je suis donc toujours cet étudiant en deuxième année de prépa PSI qui essaye de faire tourner un moteur brushless. L'objectif est simplement de le faire tourner avec un programme Arduino à une vitesse constante et de pouvoir mesurer sa vitesse.

Ce que j'ai réussi à faire :

-> Utiliser un capteur à effet hall pour mesurer la vitesse : En plaçant un petit aimant de poids très négligeable et sur l'axe du moteur, lorsque l'information transmise par le capteur passe de 0 à 1 ou inversement (lecture digitale), ce qui correspond à la détection du pôle nord magnétique par le capteur, on peut compter le nombre de tours sur un intervalle de temps.

-> Donner une vitesse constante à mon moteur brushless. Il s'agit là de la première partie du programme qui utilise notamment l'information analogique d'un potentiomètre. En prenant identifiant l'esc comme un servo, j'arrive à obtenir une vitesse constante en communiquant l'information à mon ESC.

Mon problème qui ammène ce post :

-> Ce que je souhaite, c'est connaître la vitesse de mon moteur en fonction de la tension transmise par l'ESC. (pour pouvoir comparer avec le Kv constructeur en fait).

Je dois l'avouer j'ai un peu bidouillé jusqu'à ce que le moteur tourne. Je ne comprends pas très bien le rôle de la fonction servo() pour l'ESC.

Ce que je sais c'est que l'ESC est alimenté par une tension continue qu'il va transmettre au moteur + ou - à l'aide d'un signal PWM. Je ne comprends pas très bien en revanche comment ce fonctionnement se traduit dans mon programme...

Du coup je ne comprends pas très bien la valeur de tension reçue par mon moteur, mes calculs de Kv sont aberrants

-> J'ai également du mal à comprendre la fonction map() que j'utilise. Selon moi c'est un simple coefficient de proportionnalité en fait qui permet de parcourir deux intervalles de longueur différentes avec le même pas.

-> Je doute de la fiabilité de mon travail :

-J'obtiens bien un nombre de détections de l'aimant (donc un nombre de tours pour un intervalle de temps choisi), mais dans mon programme il y'a une ligne delay(1) qui semble être une pause nécessaire de 1ms avant répétition éventuelle de la boucle mais qui induirait de grosses erreurs en nombre de tours par la suite (si par exemple un tour n'est pas compté pendant cette milliseconde)

-Le potentiomètre à été une solution facile mais il est évident que je n'obtiens jamais la même vitesse suivant comment je le tourne. Je suis ouvert à toute proposition autre pour améliorer mon programme...

Je vous laisse mon programme pour mieux comprendre mon problème. Je m'excuse pour le vocabulaire un peu imprécis que j'emploie. Je pourrai si vous le souhaitez insérer une image par la suite pour vous montrer le montage. Bonne soirée à tous et merci d'avance pour vos réponses.

#include <Servo.h>
int valeur = 0; // Variable potentiomètre
Servo esc ;
int capteurpin = 2;
int sensorValue;
int lastsensorValue = LOW;
int compteur = 0 ;
unsigned long temps = 0;
unsigned long duree = 1000;

void setup() {
  esc.attach(8);
  esc.writeMicroseconds(1000); // On attribue la valeur min
  Serial.begin(9600);
  temps = millis();
  }

void loop() {
  // Lecture du potentiomètre pour régler la vitesse
  valeur = analogRead(A0);
  int valeuresc = map (valeur , 0 , 1023, 1000, 2000);
  esc.writeMicroseconds(valeuresc); // On attribue la variable mappée qu'on attribue à l'esc
  sensorValue = digitalRead(capteurpin);
  if (( sensorValue == HIGH) && (lastsensorValue == LOW)) compteur++;
  lastsensorValue = sensorValue;
  delay(1);

  if (millis() - temps > duree) {
    temps = millis();
    Serial.println(compteur);
    Serial.println(valeur);
    compteur = 0;
  }
 
}

e dois l'avouer j'ai un peu bidouillé jusqu'à ce que le moteur tourne. Je ne comprends pas très bien le rôle de la fonction servo() pour l'ESC.

ces ESC sont conçus pour réagir a un signal analogue à celui envoyés aux servos....(rapport cyclique variable) d'où l'utilsation de la commande servo()...

Un oscilloscope te permettrait de constater les tensions envoyées au moteur, en particulier leur fréquence qui détermine la vitesse de rotation.
Les calculs de Kv sont aberrants si tu préjuges un fonctionnement analogue à un moteur CC à balais

Mesurer la vitesse dans loop n'est pas une bonne idée. Tu peux très facilement louper des impulsions.
Il vaudrait mieux utiliser une interruption pour cela.
Voir par exemple:
http://forum.arduino.cc/index.php?topic=314029.0
Mais il y a d'autres exemples. Une petite recherche sur le forum devrait te permettre de résoudre ton problème.

Oui je comprends... Merci pour la précision par rapport à l ESC. Mais vous êtes sur que je ne peux pas réorganiser différemment ma boucle afin de pouvoir tout de même utiliser l'aimant et le capteur à effet hall ?

al1fch:
ces ESC sont conçus pour réagir a un signal analogue à celui envoyés aux servos....(rapport cyclique variable) d'où l'utilsation de la commande servo()...

Un oscilloscope te permettrait de constater les tensions envoyées au moteur, en particulier leur fréquence qui détermine la vitesse de rotation.
Les calculs de Kv sont aberrants si tu préjuges un fonctionnement analogue à un moteur CC à balais

Bonjour,

Pour contrôler un ESC, ce n'et pas la fréquence qui détermine la vitesse de rotation mais la durée du niveau haut du signal PWM. La fréquence est de l'ordre de 50Hz. (comme pour un servo)
Le minimum se situe pour une durée de niveau haut de 1ms et le max vers 2ms.
La fréquence étant constante l'information vitesse et alors aussi contenue dans la valeur moyenne du signal PWM

Serge .D

.......
Du coup je ne comprends pas très bien la valeur de tension reçue par mon moteur, mes calculs de Kv sont aberrants

Il faut considérer le dipôle "contrôleur-moteur" (le moteur brushless n'est pas un dipôle, il possède trois bornes)

La détermination de Kv peut être approchée dans la situation"moteur à vide" c'est à dire ne fournissant aucune puissance mécanique et signal de commande du contrôleur au max (vers 2ms). Dans ce cas la vitesse de rotation est proche de KvV (V est la tension continue d'entrée du contrôleur)
Dans cette situation, les autres paramètres du modèle électrique (résistance interne, couple mécanique résistant ...) sont alors d'influence faible et la vitesse de rotation est assez proche de Kv
V

La séquence de détermination de l'ordre de grandeur de Kv pourrait être :

  • Envoyer un signal de commande max pour le contrôleur ( impulsion de 2ms)
  • Mesurer la vitesse de rotation du moteur sans charge mécanique
  • Déduire l'ordre de grandeur de Kv

Serge .D

Corentin49:
Oui je comprends... Merci pour la précision par rapport à l ESC. Mais vous êtes sur que je ne peux pas réorganiser différemment ma boucle afin de pouvoir tout de même utiliser l'aimant et le capteur à effet hall ?

Je ne remets pas en cause le capteur mais la façon de l'utiliser. Ce que je dis c'est que faire un digitalRead() dans loop() pour connaitre l'état du capteur ce n'est pas du tout efficace. Il faut raccorder la sortie de ton capteur sur l'une des entrées d'interruption et faire le comptage dans la routine d'interruption comme dans l'exemple que j'ai donné en lien.

Ah oui j'avais mal compris fdufnews je vais essayer de me renseigner plus sur les interruptions et grace à l'exemple fourni

Aligote une frequence à 50Hz correspondrait à un PWM de rapport cyclique 100% pour lArduino c'est bien ça ?

Corentin49:
Aligote une frequence à 50Hz correspondrait à un PWM de rapport cyclique 100% pour lArduino c'est bien ça ?

Le signal de commande et à fréquence 50Hz fixe, c'est un PWM dont le rapport cyclique variable représente la vitesse souhaitée.

Pour 50 Hz la période est de 20 ms

  • La vitesse min est associée à un niveau haut de 1ms (donc 19 ms pour le niveau bas)
  • La vitesse max (100%) correspond à un niveau haut de 2ms

Serge .D

Ok donc on reprend sur ce qui ne marche pas dans mon programme c'est à dire la façon dont on compte les tours. J'ai essayé de me documenter un peu sur les interruptions avant de me lancer et c'est parti.

Bon ça n'a pas marché du premier coup. J'ai dans le contexte (ou le setup je ne suis pas encore calé sur le vocabulaire n'hésitez pas à me corriger) défini mon interruption sur mon capteur hall attaché sur le pin 2 comme ceci :
attachInterrupt(2,increment,CHANGE);

J'enlève ensuite l'ancienne méthode avec une boucle conditionnelle et finalement je dois incrémenter mon compteur lorsque un tour de moteur est effectué c'est à dire définir une fonction qui incrémente ce compteur et à laquelle on fait appel lors de l'interruption comme ceci :

void increment() {
  compteur++;
  }

Je continue mes recherches mais déjà ça me fait progresser donc merci pour ça. Si vous voyez où je me trompe hésitez pas. Je laisse mon code entier ci-dessous et aussi une tite photo pour essayer de rendre le tout un peu plus concret :smiley:

PS : Quelqu'un pour m'expliquer le fonctionnement précis de la fonction map() ? J'essaye de comprendre la définition donnée par le site mais je bug un peu.

#include <Servo.h>
int valeur = 0; // Variable potentiomètre
Servo esc ;
int capteurpin = 2;
int sensorValue;
int lastsensorValue = LOW;
int compteur = 0 ;
unsigned long temps = 0;
unsigned long duree = 1000;

void setup() {
  esc.attach(8);
  esc.writeMicroseconds(1000); // On attribue la valeur min
  Serial.begin(9600);
  temps = millis();
  digitalPinToInterrupt(2);
  attachInterrupt(2,increment,CHANGE);
  }


void loop() {
  // Lecture du potentiomètre pour régler la vitesse
  valeur = analogRead(A0);
  int valeuresc = map (valeur , 0 , 1023, 1000, 2000);
  esc.writeMicroseconds(valeuresc); // On attribue la variable mappée qu'on attribue à l'esc
  sensorValue = digitalRead(capteurpin);

  if (millis() - temps > duree) {
    temps = millis();
    Serial.println(compteur);
    Serial.println(valeur);
    compteur = 0;
  }
 }
void increment() {
  compteur++;
  }

Comme compteur est utilisé sous interruption il faut déclarer la variable comme volatile
Comme c'est un compteur et que tu ne l'utilises qu'en compteur (et non en décompteur) autant déclarer la variable en unsigned

volatile unsigned int compteur;

Ah oui tu veux dire comme compteur est juste un entier que l'on incrémente et rempli juste cette fonction là on déclare la variable comme volatile et en gros ensuite en fonction de son rôle dans la fonction increment() par la suite c'est juste défini comme un entier

unsigned = non signé, la valeur sera toujours positive puisqu'on fait uniquement +1. Il n'y a aucune raison de définir un entier signé.

volatile, parce que la variable est modifiée dans un gestionnaire d'interruption. Volatile demande au compilateur de ne pas faire d'optimisation sur cette variable car elle peut-être modifiée à n'importe quel moment (les interruptions tombent à des moments imprévisible)

Merci pour le renseignements sur les variables non signées :slight_smile:

Bon aujourd'hui j'ai essayé de bidouiller mon programme en conséquence de l'interruption car du coup le compteur ne veut pas s'incrémenter... (Enfin le programme fait ce que l'on lui demande je me doute bien que c'est une erreur de ma part :slight_smile: :slight_smile: )

De ce que je constate c'est surement lié aux remises à zéro dans le loop du compteur (qui permet à la base de connaître un nombre de tours par intervalle de temps...).

#include <Servo.h>
int valeur = 0; // Variable potentiomètre
Servo esc ;
int capteurpin = 2;
int sensorValue;
int lastsensorValue = LOW;
unsigned long temps = 0;
unsigned long duree = 1000;
volatile unsigned int compteur = 0 ;

void setup() {
  esc.attach(8);
  esc.writeMicroseconds(1000); // On attribue la valeur min
  Serial.begin(9600);
  temps = millis();
  digitalPinToInterrupt(2);
  attachInterrupt(2,increment,CHANGE);
  }


void loop() {
  // Lecture du potentiomètre pour régler la vitesse
  valeur = analogRead(A0);
  int valeuresc = map (valeur , 0 , 1023, 1000, 2000);
  esc.writeMicroseconds(valeuresc); // On attribue la variable mappée qu'on attribue à l'esc
  sensorValue = digitalRead(capteurpin);

  if (millis() - temps > duree) {
    temps = millis();
    Serial.println(compteur);
    Serial.println(valeur);
    compteur = 0;
  }
 }
void increment() {
  compteur++;
  }

Là il y a une petite embrouille

Corentin49:

#include <Servo.h>

iint capteurpin = 2;
  digitalPinToInterrupt(2);
  attachInterrupt(2,increment,CHANGE);

il faut écrire

#include <Servo.h>
iint capteurpin = 2;

  attachInterrupt(digitalPinToInterrupt(2),increment,CHANGE);

ce serait d'ailleurs mieux d'écrire comme ça

#include <Servo.h>
iint capteurpin = 2;

  attachInterrupt(digitalPinToInterrupt(capteurpin),increment,CHANGE);

Cette ligne ne sert plus à rien

  sensorValue = digitalRead(capteurpin);

Ah oui un grand merci pour ton aide fdufnews. Pour la ligne qui ne servait plus à rien quel étourdit en effet.
Ok pour l'écriture de l'interruption cette fois-ci ça marche bien.

On en apprend tous les jours sur Arduino c'est cool :smiley: .

Il ne me reste plus qu'à bien vérifier que les valeurs trouvées correspondent.

Normalement j'ai réussi à bien comprendre le rôle de la fonction map() dans mon programme qui en fait permet de "transformer" les valeurs de 0 à 1023 que peut transmettre le potentiomètre en 1000 à 2000 qui sera en fait la durée à l'état haut sur une période de 2 ms pour mon PWM envoyé à l'ESC.

J'obtiens 400 tours/1seconde pour 1070 (valeuresc dans mon programme).

Il me reste juste à vérifier que cela puisse concorder avec le Kv, même si je me doute d'un écart puisque je ne suis pas exactement à vide en fait. Je travaille avec une batterie qui délivre un continu de 14.8V si quelqu'un veut s'amuser à faire des calculs.

Mes questions s'arrêtent ici merci de m'avoir aidé à résoudre mon problème. Je vais sans doute tenter d'améliorer ce programme en utilisant un oscilloscope.

Merci encore :smiley:

Bonjour, excusez moi de relancer mon propre sujet mais maintenant que mon programme est correct je souhaite connaître finalement la vitesse du moteur.

J'obtiens par exemple environ 216 de valeur affiché pour le nombre de tours sur 1seconde ce qui me parait beaucoup pour une valeur envoyé à l'ESC de 1111

Mon moteur à un Kv théorique de 2200 et j'alimente ici l'ESC en 14,8 V et 1300mAh.

Selon moi l'ESC transmet un certain pourcentage de cette valeur de tension grâce à un signal PWM dont le pourcentage est justement déterminé par la valeur envoyée (1111) dans mon exemple...

Mais 216 tours ca ferait du 12920 tours/minute soit une valeur de tension envoyée de déjà 5,87Volts avec 1111....

Trouvez vous cela cohérent ?

Tu affiches la valeur envoyée à l'ESC, tu affiches la valeur du compteur mais par contre tu n'affiches pas le temps écoulé donc il te manque une info pour déterminer la vitesse de rotation précisément.

Il y a aussi une info manquante c'est la fonction de transfert de l'ESC. Quelle valeur de PWM en sortie de l'ESC pour une valeur donnée de la consigne entre 1000 et 2000ms. Sans cette cette info tu ne peux pas recouper la valeur de la vitesse de rotation que tu calcules avec ton programme.

fdufnews:
.......
Il y a aussi une info manquante c'est la fonction de transfert de l'ESC. Quelle valeur de PWM en sortie de l'ESC pour une valeur donnée de la consigne entre 1000 et 2000ms. Sans cette cette info tu ne peux pas recouper la valeur de la vitesse de rotation que tu calcules avec ton programme........

A propos de l'esc, il y a deux choses à vérifier :

  • 1 Que la transfert de l'Esc "valeur du signal PWM" / "Vitesse de rotation" est bien parfaitement linéaire. (grandeurs proportionelles)
  • 2 Que ce transfert est bien calibré. (Dans la plupart des Esc une procédure de calibration logicielle de ce transfert existe)
    Il n'est pas sûr que, par défaut, le démarrage de la rotation se fasse précisément pour un signal de 1 ms

C'est pourquoi je proposais (post #5) dans un premier temps de faire la détermination de Kv pour le signal PWM maximum.

Serge .D