Bonjour à tous,
J'essaye de contrôler via Arduino Uno un moteur CC en position. J'ai en plus un codeur magnétique couplé au moto-réducteur.
Voici les références :
Moto-réducteur : Pololu - 298:1 Micro Metal Gearmotor MP 6V with Extended Motor Shaft
Codeur : Pololu - Magnetic Encoder Pair Kit for Micro Metal Gearmotors, 12 CPR, 2.7-18V
Driver : Pololu DRV8835 Dual Motor Driver Shield for Arduino
J'alimente le moteur en 6 VDC et le codeur en 3 VDC.
J'ai tapé le code pour l'asservissement en postion en m'inspirant grandement de ce site :
La librairie DRV8835MotorShield.h est spécifique au driver que j'utilise.
Mon code me permet d'entrer le nombre de tour que je veux effectuer, et permet de calculer le nombre de ticks en entrée à envoyer pour aller à la position voulue via un PID.
En théorie, on a : 297.92 (rapport de réduction) * 12 (nombre de ticks par tour du codeur) = 3575 ticks/tour.
Voici le code :
// Ce code permet d'asservir en position angulaire un moteur à courant continu.
#include <SimpleTimer.h> // bibliothèque
#include <DRV8835MotorShield.h> // bibliothèque
SimpleTimer timer; // Timer pour échantillonnage
//definition des entrées
int encoderPinA = 2;//compteur 1 Moteur 1
int encoderPinB = 3;//compteur 2 Moteur 1
#define LED_PIN 13
DRV8835MotorShield motors;
//int LED=12;
//init echantillonage
unsigned int time = 0;
const int frequence_echantillonnage = 200; // 20 Hz - mesure toutes les 50ms
//init compteur :
int encoder0Pos = 0; //position de départ=0
int lastReportedPos = 0;
boolean A_set = false;
boolean B_set = false;
//consigne
double target_tour = 1; //nombre de tour désiré
double target_deg = 360*target_tour; //nombre de degrès pour atteindre target_tour
int target_ticks; //plus simple d'asservir en ticks car ce sera toujours un nombre entier
// init calculs asservissement PID
int erreur = 0; //erreur
float erreurPrecedente = 0;
float somme_erreur = 0;
//Definition des constantes du correcteur PID
float kp = 0.80; // Coefficient proportionnel // choisis par tatonnement sur le moniteur. Ce sont les valeurs qui donnaient les meilleures performances
float ki = 0; //5.5; // Coefficient intégrateur
float kd = 0;//100; // Coefficient dérivateur
// Routine d'initialisation //
void setup() {
pinMode(LED_PIN, OUTPUT);
// uncomment one or both of the following lines if your motors' directions need to be flipped
//motors.flipM1(true);
//motors.flipM2(true);
target_ticks = target_tour*297.92*12.0; // rapport de réduction = 297.92 et nombre de ticks par tour = 12 // nombre de ticks en entrée pour avoir target_tour en sortie
Serial.begin(9600); // Initialisation port COM
//pinMode(LED, OUTPUT);
pinMode(encoderPinA, INPUT); //sorties encodeurs
pinMode(encoderPinB, INPUT);
digitalWrite(encoderPinA, HIGH); // Resistance interne arduino ON
digitalWrite(encoderPinB, HIGH); // Resistance interne arduino ON
// Interruption de l'encodeur A en sortie 0 (pin 2)
attachInterrupt(0, doEncoderA, CHANGE);
// Interruption de l'encodeur A en sortie 1 (pin 3)
attachInterrupt(1, doEncoderB, CHANGE);
digitalWrite(LED_PIN, 0); // Initialisation sortie moteur à 0
delay(300); // Pause de 0,3 sec pour laisser le temps au moteur de s'arréter si celui-ci est en marche
timer.setInterval(1000/frequence_echantillonnage, asservissement); // Echantillonage pour calcul du PID et asservissement; toutes les 100ms, on recommence la routine
}
void loop(){
timer.run(); //on fait tourner l'horloge
// digitalWrite(LED,HIGH); // met la broche au niveau haut (5V) – allume la LED
//delay(500); // pause de 500 millisecondes (ms)
//digitalWrite(LED,LOW); // met la broche au niveau bas (0V) – éteint la LED
//delay(500); // pause de 500ms
}
// Interruption appelée à tous les changements d'état de A
void doEncoderA(){
A_set = digitalRead(encoderPinA) == HIGH;
encoder0Pos += (A_set != B_set) ? -1 : +1; //modifie le compteur selon les deux états des encodeurs
}
// Interruption appelée à tous les changements d'état de B
void doEncoderB(){
B_set = digitalRead(encoderPinB) == HIGH;
encoder0Pos += (A_set == B_set) ? -1 : +1; //modifie le compteur selon les deux états des encodeurs
}
// Cette fonction est appelée toutes les 20ms pour calcul du correcteur PID
void asservissement()
{
time += 20; // pratique pour graphes excel après affichage sur le moniteur
erreur = target_ticks - encoder0Pos;
somme_erreur += erreur;
// Calcul de la vitesse courante du moteur
int vitMot = kp * erreur + kd * (erreur - erreurPrecedente) + ki * (somme_erreur);
erreurPrecedente = erreur; // Ecrase l'erreur précedente par la nouvelle erreur
// Normalisation et contrôle du moteur
if(vitMot > 25) vitMot = 25; // sachant que l'on est branché sur un DRV8835, vitesse limite du moteur
else if (vitMot < -25) vitMot = -25;
if (vitMot > 0) {
digitalWrite(LED_PIN, HIGH);
}
else {
digitalWrite(LED_PIN, LOW);
}
motors.setM1Speed(vitMot);
float angle_deg = encoder0Pos/297.92/12.0*360.0; //Position angulaire de sortie, pratique pour comparer avec la consigne d'entrée
float tour = encoder0Pos/297.92/12.0;
Serial.print(erreur); // affiche sur le moniteur les données voulues
Serial.print(" ");
Serial.print(encoder0Pos);
Serial.print(" ");
Serial.print(angle_deg);
Serial.print(" ");
Serial.print(tour);
Serial.print(" ");
Serial.println(vitMot);
}
Mon problème est le suivant :
Le code effectue parfaitement bien la fonction, mais en réalité si je demande au moteur de tourner d'un tour dans le sens positif (sens trigo), il ne fait qu'un demi-tour, alors que si je demande un tour dans le sens négatif (sens horaire), il effectue un tour et demi.
Avez-vous des idées sur la cause du problème ? Merci.