PID pour moteur BLDC drivé en Spwm

Bonjour à tous,

je travailles sur un projet consistant à faire tourner un moteur BLDC très lentement avec un asservissement.

J'utilise un arduino DUE avec une résolution de sortie pwm en 12 bits.

La vitesse de rotation du moteur dépend de la fréquence de la boucle principale puisque l'on applique sur chaque phase un signal déphasé de 120°.
Les valeurs de chaque pwm sont issues d'un tableau sur 7200 valeurs pour une phase complete (0 - 4095). Elles sont calculées depuis un fichier excel.

J'ai une roue codeuse 2400 tick par tour connectée à la même carte.

Je souhaiterai mettre en place un asservissement basé sur le nombre de tick mesuré toutes les 100ms.

Donc ma fonction est exécuté avec un timer toutes les 100ms, mesure le nombre de tick depuis la dernière fois et ajuste la fréquence de la boucle principale.

Seulement voilà ça ne fonctionne pas.
Le moteur se colle aux seuils Maxi ou mini défini.

Je fixe par exemple une consigne à 137 tick par 100ms (33 tours minutes)

A partir de la je souhaiterai appliquer une valeur au delayMicroseconds pour atteindre la vitesse voulue.

Voici le code que j'ai utilisé : (j'ai volontairement supprimé les valeurs de pwm du tableau car 7200 ça fait beaucoup dans le poste

#include <Streaming.h>
#include <DueTimer.h>

const int EN1 = 4;
const int IN1 = 6;
const int IN2 = 7;
const int IN3 = 8;
const int freq = 10;

const byte enc1 = 12;
const byte enc2 = 13;
unsigned int ticksCodeur = 0;
const int consnbticks = 137;

int cmd = 200;
float erreur_precedente = consnbticks;
float somme_erreur = 0; // Somme des erreurs pour l'int�grateur
float kp = 5; // Coefficient proportionnel
float ki = 1; // Coefficient int�grateur
float kd = 0; // Coefficient d�rivateur
const int tickpt = 2400;

//#define PWM_FREQUENCY 20000
//#define PWM_MAX_DUTY_CYCLE 4095
//#define PWM_MIN_DUTY_CYCLE 0
//#define PWM_RESOLUTION 12

// SPWM (Sine Wave)
const int pwmSin[] = {7200 valeurs de pwm...};

int currentStepA;
int currentStepB;
int currentStepC;
int sineArraySize;
int increment = 0;
boolean direct = 1; // direction true=forward, false=backward

//////////////////////////////////////////////////////////////////////////////

void setup() {

Serial.begin(115200);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(EN1, OUTPUT);
digitalWrite(EN1, HIGH);

//----------------------------interrupt----------------
pinMode(enc1, INPUT);
pinMode(enc2, INPUT);
attachInterrupt(enc1, encpin1, CHANGE);
attachInterrupt(enc2, encpin2, CHANGE);

//Timer3.attachInterrupt(asservissement).start(655040);
Timer3.attachInterrupt(asservissement).setFrequency(freq).start();

//--------------------------------tableau valeur SinPWM----------------
sineArraySize = sizeof(pwmSin)/sizeof(int); // Find lookup table size
int phaseShift = sineArraySize / 3; // Find phase shift and initial A, B C phase values
currentStepA = 0;
currentStepB = currentStepA + phaseShift;
currentStepC = currentStepB + phaseShift;

sineArraySize--; // Convert from array Size to last PWM array number
}

//////////////////////////////////////////////////////////////////////////////

void loop() {

analogWriteResolution(12);
analogWrite(IN1, pwmSin[currentStepA]);
analogWrite(IN2, pwmSin[currentStepB]);
analogWrite(IN3, pwmSin[currentStepC]);

if (direct==true) increment = 1;
else increment = -1;

currentStepA = currentStepA + increment;
currentStepB = currentStepB + increment;
currentStepC = currentStepC + increment;

//Check for lookup table overflow and return to opposite end if necessary
if(currentStepA > sineArraySize) currentStepA = 0;
if(currentStepA < 0) currentStepA = sineArraySize;

if(currentStepB > sineArraySize) currentStepB = 0;
if(currentStepB < 0) currentStepB = sineArraySize;

if(currentStepC > sineArraySize) currentStepC = 0;
if(currentStepC < 0) currentStepC = sineArraySize;

/// Control speed by this delay
delayMicroseconds(cmd);

}

void encpin1()
{
if( digitalRead(enc2) == 0 ) {
if ( digitalRead(enc1) == 0 ) {

ticksCodeur--;
} else {

ticksCodeur++;
}
} else {
if ( digitalRead(enc1) == 0 ) {

ticksCodeur++;
} else {

ticksCodeur--;
}
}
}
void encpin2(){
if ( digitalRead(enc1) == 0 ) {
if ( digitalRead(enc2) == 0 ) {

ticksCodeur++;
} else {

ticksCodeur--;
}
} else {
if ( digitalRead(enc2) == 0 ) {

ticksCodeur--;
} else {

ticksCodeur++;
}
}
}

void asservissement()
{

int tick = ticksCodeur;
ticksCodeur = 0;
//Serial << tick << endl;

// Calcul de l'erreur
somme_erreur += erreur;
float delta_erreur = erreur - erreur_precedente;
erreur_precedente = erreur;

//PID : calcul de la commande
int pid = kperreur + kisomme_erreur + kd*delta_erreur;

// Normalisation et contr�le du moteur
if (pid < 1) pid = 1;
else if (pid > 200) pid = 200;
cmd = 1000 - pid;
Serial << tick << ";" << nb_tour_par_sec << ";" << erreur << ";" << cmd << endl;
}

Voilà si vous avez une idée elle sera la bienvenue.
ça fait quelques jours que je suis la dessus.
Je pense qu'il y a un soucis dans l'échelle des valeurs car quand mon PID vaut 1, le moteur tourne trop vite.

Merci
William

Bonjour,

Je ne suis pas entré dans les détails du code, mais..

On doit toujours déclarer les variables utilisées dans une ISR "volatile" pour empêcher le compilateur d'optimiser le code en gardant si il peut, des variables dans les registres du µP. Ce qui peut avoir pour effet de perdre la valeur pendant une ISR.
Donc tu dois déclarer "volatile unsigned int ticksCodeur = 0;".

C'est une piste.

salut,

merci ! en effet, je vais corriger cela et jeter un oeil à l'ensemble de mes variables.

Salut,

bon ça ne résout pas mon soucis...

j'étais parti sur quelque chose comme ça avec un calcul de valeur moyenne mais ça ne serait pas très réactif comme system

if (tick > consigne) cmd++;
else if (tick < 15) consigne--;

Je ne trouve pas où tu calcule "erreur".

Salut,

désolé, j'ai tellement fait d'essai que je crois que la ligne a sauté avant mon copier/collé

void asservissement()
{

int tick = ticksCodeur;
ticksCodeur = 0;

// Calcul de l'erreur
erreur = consnbticks - tick;
somme_erreur += erreur;

float delta_erreur = erreur - erreur_precedente;
erreur_precedente = erreur;

//PID : calcul de la commande
int pid = kperreur + kisomme_erreur + kd*delta_erreur;

En fait le résultat de PID tend vers 0 et plus le moteur accélère et plus il tend vers +infini et plus il ralenti.
LE fait qu'il gère la fréquence de la boucle cela inverse l'effet.

William

Grrrrr rien a faire je n'arrive pas à trouver la règle pour faire fonctionner cela.

La commande est inversement proportionnel à la valeur de l'erreur.

J'ai donc traduit cela par cmd = 1/pid x 1000 mais non...

cmd est la valeur de delayMicroseconds.