Commande PWM moteur CC, lecture encodeur
Bonjour,
Je débute en Arduino, et j'essaie d'identifier expérimentalement un moto-réducteur CC muni d'un encodeur.
La manip de base consiste à imposer une tension d'alimentation et de recueillir les informations du codeur pour tracer la vitesse en fonction du temps.
On en déduit le gain statique et la constante de temps, en assimilant le moteur à un premier ordre.
Moteur CC 6V, réducteur au 1/53, codeur 2 voies à 6 ticks par tour chaque.
Je considère que je ne loupe aucun tick, vu que je ne fais rien dans la loop, mais je fais des trucs dans la gestion de l'interruption
je calcule et affiche la vitesse de l'arbre réducteur en tr/min sur la voie série
Programme ci-dessous :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
// Faire tourner le moteur à une vitesse constante
// Récupérer chaque changement d'état grace à une interruption sur l'une des 2 voies
// Calculer la vitesse du réducteur en tr/min et l'afficher sur la liaison série
const int _MOTEUR = 9; // Digital pin pour commande moteur
unsigned long temps; // variable de temps en microsecondes, car avec micros() direct, ça marche pas !!
unsigned long tempsinit; // instant initial
float vitesse ;
/* Routine d'initialisation */
void setup() {
pinMode(_MOTEUR, OUTPUT); // Configuration de la broche en sortie
analogWrite(_MOTEUR, 255); // Commande PWM de 0 à 255 Max (de 0 à 100% de la "tension d'alimentation")
delay(5000); // Pause de 5 sec pour laisser le temps au moteur de s'arréter si celui-ci est en marche
attachInterrupt(0, compteur, CHANGE); // Interruption sur tick de la codeuse (interruption 0 = pin2 arduino mega)
Serial.begin(230400) ;
tempsinit = micros();
}
/* Fonction principale */
void loop(){
}
/* Interruption sur tick de la codeuse */
void compteur(){
temps = micros();
vitesse = (1000000/float(temps-tempsinit))*60/6/53 ;
Serial.println(vitesse);
tempsinit=temps;
} |
Pb : quand je change la valeur du PWM (typiquement : 127 au lieu de 255)
la vitesse ne change pas en proportion.
Qu'est-ce que je loupe ?
Par avance, merci de votre aide
La même chose avec un motorshield
Suite aux réponses précédentes, je me suis demandé si le problème ne venait pas du matériel.
Jusqu'à présent, je ne m'étais même pas posé la question...
J'ai changé de carte arduino, je l'ai associée à un motorshield
J'ai fait les branchements sur la voie B (alimentation moteur)
Le programme est identique au précédent, aux spécificités du motorshield (Brake, notamment)
...
Les résultats sont les mêmes. Le PWM ne semble pas délivrer une tension proportionnelle au rapport cyclique.
Je me base sur une comparaison simple : l'alimentation en direct, avec PWM=255, en 3, 4.5 et 6V donne
respectivement : 78, 114, 154, soit 50.6%, 74% et 100%
Donc là, la vitesse en RP est presque proportionnelle à la tension d'alimentation.
Ce qui n'est pas le cas quand je commande avec un PWM différent de 255
Par avance, merci pour des avis qui pourraient expliquer ce comportement
Cordialement
Horus68
Autre programme avec le PmodHB5
Pour éviter de faire un Serial.print dans la routine de gestion d'interruption,
j'ai essayé de remplir un tableau avec les valeurs, pour le faire lire seulement après.
J'ai réussi grâce aux conseils avisés de jpbbricole : le "volatile" est la clef
Le comportement ne semble pas très différent. Mais, avec les données remontées, on peut noter une forte différence de temps de réponse,
alors que théoriquement, ça ne dois pas être le cas...
Le programme :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| // Faire tourner le moteur à une vitesse constante, sauf démarrage
// Récupérer chaque changement d'état grace à une interruption chaque voie du codeur
// Calculer la vitesse du réducteur en tr/min et la stocker dans un tableau
// Transmettre a posteriori via la voie série
const int nbTicks = 96;
const int _MOTEUR = 9; // Digital pin pour commande moteur
unsigned int tick_codeuse = 0; // Compteur de tick de la codeuse
unsigned long Old_tick_codeuse = 0; // Ancienne valeur de tick de la codeuse
unsigned long Millis_tick_codeuse = 0; // Ancien instant de tick de la codeuse
unsigned long temps; // variable de temps en microsecondes, car avec micros() direct, ça marche pas !!
unsigned long temps_ant; // instant antérieur
unsigned long timeinit; // instant initial
volatile float vitesse;
volatile float periode;
volatile float vitesses[nbTicks][2];
volatile bool vide = true ;
volatile int i = 0 ;
/* Routine d'initialisation */
void setup() {
pinMode(_MOTEUR, OUTPUT); // Configuration de la broche en sortie
analogWrite(_MOTEUR, 127); // Commande PWM de 0 à 255 Max (de 0 à 100% de la "tension d'alimentation")
attachInterrupt(0, compteur, CHANGE); // Interruption sur tick de la codeuse (interruption 0 = pin2 arduino uno)
attachInterrupt(1, compteur, CHANGE); // Interruption sur tick de la codeuse (interruption 1 = pin3 arduino uno)
Serial.begin(230400) ;
Serial.flush();
delay(5000); // Pause de 5 sec pour laisser le temps au Serial de s'initialiser
timeinit = micros();
temps_ant = timeinit ;
}
/* Fonction principale */
void loop(){
while (vide){
}
if (not vide){
Serial.println("Tableau des vitesses");
for (int j=0 ; j<nbTicks ; j++){
Serial.print(vitesses[j][0]); Serial.print(", ");Serial.println(vitesses[j][1]);
vide = true ;
}
}
}
/* Interruption sur tick de la codeuse */
void compteur(){
// Old_tick_codeuse = tick_codeuse ; // mémorise l'ancien instant
tick_codeuse++; // On incrémente le nombre de tick de la codeuse
temps = micros();
// periode = float(temps-temps_ant)/1000;
if (i<nbTicks){
vitesse = 1000000.0/float(temps-temps_ant)*60/12/53 ;
vitesses[i][0]= (temps-timeinit) ; vitesses[i][1]= vitesse ;
temps_ant=temps;
// Serial.print("i= "); Serial.print(i); Serial.print(", ");
// Serial.println(vitesse);
i++ ;
}
else {
analogWrite(_MOTEUR, 0);
vide = false;
// Serial.println(vitesse);
// Serial.print(" , ");
// Serial.println(tick_codeuse);
}
} |
1 pièce(s) jointe(s)
Non linéarité vitesse/PWM
Bonjour à tous,
J'ai utilisé le programme de Jay M
J'ai réglé le PWM à différentes valeurs >=64, pas de 16
J'ai relevé la vitesse finale (Nb de fronts par sec) en faisant la moyenne sur 30 sec
Voilà ce que ça donne :
Pièce jointe 593352
Pas linéaire du tout !!
Je m'y attendais pour les basses valeurs de PWM, puisqu'en deçà de 64, la rotation n'a plus lieu
A noter, la singularité du PWM à 255 (à cause de l'absence de commutation ?)
Du coup, pour un éventuel asservissement en vitesse, il faudra calculer un gain en linéarisant autour du point de fonctionnement,
en travaillant sur des échelons de vitesse d'amplitude modeste, si on veut avoir une chance de faire matcher calculs et mesures.
Idem pour obtenir la constante de temps.
Comme le disait jpbbricole, il y a de nombreux phénomènes qui rendent cette linéarité caduque, sur de grandes amplitudes.
Cordialement
Horus68
2 pièce(s) jointe(s)
Comparaison de différents ponts
Bonjour,
Après avoir suspecté mon alim, une HQPower 2A, 0 à 12V
et testé le comportement moteur avec une Elix 3A, 0-30V sans constater d'amélioration...
J'ai testé différents hacheurs pour comparaison. Même programme, branchements analogues et même principe de commande :
PWM sur la broche En (Enable) ou sur la "gate" du MosFet
Pont en H L293D du starter kit, avec branchements du livret ;
MosFet IRF520, diode de roue libre, résistance, conforme au schéma ci-dessous :
Pièce jointe 595418
Et ça donne ça :
Pièce jointe 595419
On peut observer une certaine constance des résultats (non linéarité), et des différences importantes...
Aucune idée des causes probables...
Cordialement
Horus68
1 pièce(s) jointe(s)
Correction des imprécisions du codeur par post-traitement
Re,
J'avais dit que je ferai un petit programme python pour voir si on pouvait améliorer les mesures, le voilà
(les puristes y trouveront sans doute beaucoup à redire)
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| ## Repérage des différents poles et calcul des angles entre chaque
# En régime permanent, la vitesse est constante, la durée entre deux poles n'est pas exactement la même
# On importe le fichier texte contenant les intervalles de temps en microsecondes entre 2 ticks
# On prend la moyenne des intervalles sur n tours consécutifs en partant de la fin
# On en déduit une succession d'intervalles angulaires pour faire un tour
# On calcule la vitesse en prenant angle calculé/durée mesurée
import numpy as np
import matplotlib.pyplot as plt
poles = 6 # fronts par voie (paires de poles)
voies = 2 # voies codeur
nbtours = 1 # nb de tours sur lesquels est calculée la moyenne
cycle = poles*voies # nb de fronts sur un tour
fichier = open("essai.csv","r",encoding="utf8")
# temps en millisecondes entre deux fronts
temps = [float(x) for x in fichier.read().split()]
# vitesse en tours par seconde, sachant qu'un tour est fait en "cycle"s fronts
# en supposant que l'écart angulaire entre 2 fronts est absolument régulier
vitesse = np.array([1000/(t*cycle) for t in temps])
# Echelle des temps
T=np.array([sum(temps[:i]) for i in range(len(temps))])/1000
# Ici, on a exclu le dernier tour (ralentissement) du fichier csv
def moyenne(temps) :
""" Moyenne des périodes de chaque "pole", à vitesse supposée constante, appliquée à un fichier 'propre', sans valeur exceptionnelle en fin de colonne"""
temps_sum = temps[-cycle:] # somme des valeurs en vue d'une moyenne, initialisation
for i in range(nbtours-1) :
for j in range(cycle) :
temps_sum[j] += temps[-(2*(i+1)*cycle)+j]
return np.array(temps_sum)/nbtours # Calcul et renvoi de la moyenne
DeltaT_moy = moyenne(temps) # Période moyenne de chaque "pole" en ms
Theta_moy = DeltaT_moy/sum(DeltaT_moy) # angle moyen entre chaque pole (en fraction de tour)
def vitesse_corr(temps) :
""" Vitesse corrigée des variations angulaires des poles"""
i0 = cycle - len(temps)%cycle
return np.array([Theta_moy[(i0+j)%cycle]*1000/temps[j] for j in range(len(temps))])
vitesse2 = vitesse_corr(temps)
plt.plot(T,vitesse,label = "vitesse brute (tr/s)")
plt.plot(T,vitesse2,label = "vitesse corrigee (tr/s)")
plt.legend()
plt.show() |
Mais bon, ça tourne.
On obtient ça sur un fichier test, extrait de mesures en millisecondes, la durée étant un peu courte pour assurer être en régime permanent sur plus d'un tour (le dernier), mais la comparaison est sans équivoque (sauf le premier point qui est une singularité due à une mauvaise maitrise de la communication série, sans doute)
Pièce jointe 595427
C'est pour le moment le travail le plus satisfaisant que j'ai pu effectuer sur ce thème.
Il parait que la démarche d'investigation c'est pédagogiquement super, mais c'est un peu chronophage quand on est pas doué !!
Cordialement
Horus68
1 pièce(s) jointe(s)
Vitesse et précipitation : Au temps pour moi !!
Salut Guesset,
Finalement, la différence entre 980Hz et 490Hz existe, mais elle n'était pas transcendante à mes yeux.
J'ai essayé de pousser la logique jusqu'au bout, puisque sur la notice du PmodHB5, ils parlaient de 2 kHz,
en modifiant la fréquence PWM, ça donne :
Pièce jointe 595879
Seuls les 31250 Hz donnent un résultat avec une linéarité jusqu'à 0,
celle à 3906 ayant une cassure, comme les autres...
L'inconvénient majeur, c'est la faible plage de valeurs permettant de faire tourner le moteur : de 130 à 255,
la moitié de la plage de réglage totale a priori disponible.
Je vais pouvoir essayer de mesurer le transitoire pour différentes valeurs de PWM en échelon,
pour voir si on obtient une vraie constante de temps, ce qui n'est absolument pas le cas avec des fréquences PWM standard.
Cordialement