Ros with esp32

 avatar
unknown
c_cpp
2 years ago
19 kB
4
Indexable

#include <ros.h>
#include <geometry_msgs/Twist.h>


// Pour afficher des messages à la console selon le contexte à tester
#define DEBUGMOT    false
#define DEBUGMOUV   false
#define DEBUGACCEL  false

// CABLAGE DU MOTEUR 1 sur l'ESP
#define Step1   19     // signal de micropas du moteur 1 à chaque front montant
#define Dir1    18     // sens de rotation du moteur HAUT : trigo BAS : horaire
#define Step2   5
#define Dir2    17
#define Step3   16
#define Dir3    4
#define Step4   2
#define Dir4    15
#define ENABLE  13      // mise en service de l'ensemble des moteurs pas à pas

#define Temoin  14      // indiquera que l'on a recu une trame ros

//--- PARTIE MOTEURS
struct Pap          // définition de la structure d'un moteur pas à pas
{
    uint8_t StepPin;  // numéro de sa broche Step
    uint8_t DirPin;   // numéro de sa broche Dir
    uint16_t TPas;    // multiple de THPas durée entre deux fronts sur Step
    uint8_t NumHPas;  // numéro de la période THPas dans cette durée de Step
    uint16_t NumPas;  // numéro du pas Step en cours
    uint16_t nbPas;   // nombre de micropas attendus pour ce mouvement (signé au départ)
    bool sens;        // sens de la rotation (vrai si nbPas < 0)
    bool tourne;      // indique que ce moteur est actionné dans ce mouvement pas fini
}; 
volatile Pap Moteur1, Moteur2, Moteur3, Moteur4;

//--- ASPECTS TEMPORELS
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
#define THPasDep  200  // nombre de us entre deux interruptions timer : 2IT 
#define TpasDep   20   // multiple de THPas assurant le démarrage 4ms
#define THPasMin  50  // nombre de us de THPas pour vitesse la plus rapide 500us pour 1/4 pas
/*  Le moteur le plus rapide d'un mouvement démarrera donc à un step de 10 ms.  
    Les autres démarre plus lentement en fonction du rapport des déplacements en dx et dy
    L'accel/deccelération consistera à faire évoluer THPas entre 200 et 50 = THPasMin
    pour un rythme de pas le plus rapide possible en fin accélération de 1ms          
    Avec cette valeur, 1/4 pas = tour/800 en 1ms --> 1tr = 0.8s --> 48 tr/mn = 54cm/s */
#define DeltaRythme  30     // nombre de ms que dure un pas d'accel/decceleration
uint16_t THPas;             // valeur du rythme des IT Timer en us
float TPas;                 // valeur flottante pour calcul accel/deccelerations
volatile long nbIt;         // nombre d'interruptions ayant eu lieu
unsigned long int depart;   // moment du changement de rythme et d'affichage moteurs
uint8_t nbPasAcc;           // nombre de coup d'accélération -> deccélèration de DeltaRytme
uint8_t nbPas;              // on compte les périodes DeltaRythme d'accélération
bool Accel, Deccel;         // indique chacun des sens de l'évolution de la vitesse faux si constante
uint16_t TempsMvt;          // nombre de ms que devait durer le mouvement


//--- ASPECTS MECANIQUES
// #define PI        3.1415926535       // inutile ici car PI est défini dans le C++
#define diamRoue  82    // roue de 82mm
#define nbPasTour 200   // nombre de pas complet par tour de roue
#define microPas  4     // on travaille en 1/4 de pas
#define impParMm  ((microPas * nbPasTour) / (PI * diamRoue)) // (4 * 200) / (82 * 3.1416) = 3.11
#define rayonRobot 125  // en mm
#define tourRobot  (2*PI*rayonRobot)
#define impParDeciDegre (impParMm*tourRobot/3600)   // 0.65 imp par déci degré
#define impParmRad (impParMm*tourRobot/(2000*PI))   // 0.37 imp par unité traduite de ROS

//--- ASPECT DEPLACEMENTS
int dx, dy, dc;        // quantité de déplacement de moteurs 3 et 4 (Y), et 1 et 2 (X) en impulsions 
int dcap;              // changement de cap attendu, plus tard, on établiera l'unité en milli radians (mrd)
bool dirx, diry;       // sens du déplacement --> dir des paires moteurs tant que l'on ne tourne pas sur soi
bool rot;              // indique que l'on tourne sur soi-même (détecté par dx = dy = 0 et dcap != 0)
bool roule;            // indique qu'un mouvement est en cours
uint8_t numMvt;        // numéro du mouvement en cours dans le tableau
uint16_t stepX, stepY; // compteurs des IT entre chaque step : va de 0 à THPasX ou THPasY
uint16_t PasDeccel;    // numéro du pas du moteur le plus actif pour décider de deccélérer 
uint8_t MotPlusActif;  // numéro du moteur le plus actif (celui qui se déplace le plus)
volatile int count;    // declencheur de compteur
volatile int nbPas1, nbPas2, nbPas3, nbPas4;    // odométrie des déplacements

//--- ASPECT MOUVEMENTS
struct Mvt          // définition de mouvements, 
{                   // on suppose ici que si dx ou dy, pas de dc et si dc, pas de dx ou dy
  int dx;           // nombre de mm à faire sur les moteurs 'X' du robot
  int dy;           // nombre de mm à faire sur les moteurs 'Y' du robot
  int dcap;         // angle de rotation du robot sur lui-même en déci-degrés
};
Mvt mvt;
bool nouveau;

//---  ASPECT  ROS
ros::NodeHandle nh;
volatile bool recuROS;
volatile int intDX;
volatile int intDY;
volatile int intDZ;

/*************     FONCTION D'INTERRUPTION TEMPORELLE    ******************/

// Code avec section critique en RAM -> + rapide actuellement dure 1us !!!
void IRAM_ATTR ITPas() 
{
    portENTER_CRITICAL_ISR(&timerMux);
    // digitalWrite (Temoin, HIGH);            // pour mesurer le temps de l'interruption
    // digitalWrite (Temoin, !digitalRead(Temoin));    // pour mesurer la période des IT
    if (Moteur1.tourne)      // on ne s'occupe que de ceux qui tournent
    { 
        Moteur1.NumHPas--;     // comptage de cette IT dans la durée du pas
        if (Moteur1.NumHPas == 0)   // si on est arrivé à 0 : micro-pas suivant ?
        {
            if (digitalRead(Moteur1.StepPin) == LOW) // il va y avoir un PAS à compter
            {
                Moteur1.NumPas++;                           // on compte ce pas    
                Moteur1.NumHPas = Moteur1.TPas << 1;        // on relance la tempo
            }
            // basculement de STEP : il faut en faire 2 pour faire un micro pas
            digitalWrite (Moteur1.StepPin, ! digitalRead(Moteur1.StepPin));
        }            
    }  
    if (Moteur2.tourne)      
    {
        Moteur2.NumHPas--;     
        if (Moteur2.NumHPas == 0)   
        {
            if (digitalRead(Moteur2.StepPin) == LOW) 
            {
                Moteur2.NumPas++;                             
                Moteur2.NumHPas = Moteur2.TPas << 1;             
            }
            digitalWrite (Moteur2.StepPin, ! digitalRead(Moteur2.StepPin)); 
        }   
    }       
    if (Moteur3.tourne)      
    { 
        Moteur3.NumHPas--;     
        if (Moteur3.NumHPas == 0)  
        {
            if (digitalRead(Moteur3.StepPin) == LOW)
            {
                Moteur3.NumPas++;                            
                Moteur3.NumHPas = Moteur3.TPas;             
            }
            digitalWrite (Moteur3.StepPin, ! digitalRead(Moteur3.StepPin));  
        }            
    }  
    if (Moteur4.tourne)     
    {
        Moteur4.NumHPas--;     
        if (Moteur4.NumHPas == 0)   
        {
            if (digitalRead(Moteur4.StepPin) == LOW)
            {
                Moteur4.NumPas++;                            
                Moteur4.NumHPas = Moteur4.TPas;
            }   
            digitalWrite (Moteur4.StepPin, ! digitalRead(Moteur4.StepPin));        
        }   
    }          
    // digitalWrite (Temoin, LOW);                 // pour mesurer le temps de l'interruption
    portEXIT_CRITICAL_ISR(&timerMux);
}

/******************** FONCTIONS       MOTEURS   *******************/

void creationTableauMoteurs() 
{
    // vérifier ici le bon cablage des moteurs x1, x2, y1 et y2
    Moteur1.StepPin = Step1;
    Moteur1.DirPin = Dir1;
    Moteur2.StepPin = Step2;
    Moteur2.DirPin = Dir2;  
    Moteur3.StepPin = Step3;
    Moteur3.DirPin = Dir3;
    Moteur4.StepPin = Step4;
    Moteur4.DirPin = Dir4;
}

void initSignaux ()
{
    // on doit fixer les sens des signaux STEP et DIR et mettre au repos STEP
    pinMode (Moteur1.StepPin, OUTPUT);  
    pinMode (Moteur1.DirPin, OUTPUT);
    digitalWrite (Moteur1.StepPin, LOW);   
    pinMode (Moteur2.StepPin, OUTPUT);  
    pinMode (Moteur2.DirPin, OUTPUT);
    digitalWrite (Moteur2.StepPin, LOW); 
    pinMode (Moteur3.StepPin, OUTPUT);  
    pinMode (Moteur3.DirPin, OUTPUT);
    digitalWrite (Moteur3.StepPin, LOW); 
    pinMode (Moteur4.StepPin, OUTPUT);      
    pinMode (Moteur4.DirPin, OUTPUT);
    digitalWrite (Moteur4.StepPin, LOW);       
    pinMode(ENABLE, OUTPUT);
    digitalWrite(ENABLE,  LOW);    
    pinMode(Temoin, OUTPUT);
    digitalWrite(Temoin,  LOW);  
}

void cdeMoteurs ()
{
    if (recuROS)
    {
        recuROS = false;
        // on lit et on stocke les nouvelle consignes, on dit qu'il y a à faire
        if (mvt.dx != intDX)
        {
            mvt.dx = intDX;
            nouveau = true;
        }
        if (mvt.dy != intDY)
        {
            mvt.dy = intDY;
            nouveau = true;
        }
        if (mvt.dcap != intDZ)
        {
            mvt.dcap = intDZ;
            nouveau = true;
        }
        digitalWrite(Temoin, !digitalRead(Temoin));     // marquer qu'on a recu
    }
}

/****************** Fonctions MOUVEMENT  ***************************/

void initMvt()
{
    THPas = THPasDep;          // on commence avec un rythme de step assurant le démarrage
    // Configure le Prescaler a 80 le quart de l ESP32 est cadence a 80Mhz
    // 80000000 / 80 = 1000000 tics / seconde
    timer = timerBegin(0, 80, true);                
    timerAttachInterrupt(timer, &ITPas, true);    
    // Regle le declenchement d'une interruption chaque 20ms
    timerAlarmWrite(timer, THPas, true);           
    timerAlarmEnable(timer);
}

void sens()
{
    // prise en compte des signes de déplacement pour le sens
    if (dx < 0)
    {
        Moteur1.sens = true;     // true pour un sens rétrograde, trigonométrique
        Moteur3.sens = false;    // moteur antagoniste, sens complémentaire
        dx = - dx;      // remise en positif du nombre d'impulsions
    }           
    else 
    {
        Moteur1.sens = false;    // false pour le sens horaire
        Moteur3.sens = true;     // moteur antagoniste, sens complémentaire
    }  

    if (dy < 0)
    {
        Moteur2.sens = true;
        Moteur4.sens = false;   
        dy = - dy;
    }           
    else 
    {
        Moteur2.sens = false;
        Moteur4.sens = true;   
    }    

    if (dc)                         // dc exclu et est exclu d'avec dx et/ou dy
    {
        Moteur1.sens = (dc > 0);     // true pour un sens rétrograde, trigonométrique
        Moteur2.sens = (dc > 0);     // false pour un sens rétrograde : horaire 
        Moteur3.sens = (dc > 0);     
        Moteur4.sens = (dc > 0);     
    }
    // et on pose les signaux 
    digitalWrite (Moteur1.DirPin, Moteur1.sens);
    digitalWrite (Moteur2.DirPin, Moteur2.sens);
    digitalWrite (Moteur3.DirPin, Moteur3.sens);
    digitalWrite (Moteur4.DirPin, Moteur4.sens);    
}

void rythmes()
{
    uint16_t THPasX, THPasY;   // rythme des impulsions sur les moteurs en X et en Y en THPas
    // on commence par aucun pas pour les moteurs et tous arretés         
    Moteur1.NumHPas = Moteur1.TPas = 0;   
    Moteur2.NumHPas = Moteur2.TPas = 0;
    Moteur3.NumHPas = Moteur3.TPas = 0;   
    Moteur4.NumHPas = Moteur4.TPas = 0;    
    Moteur1.tourne = false;    
    Moteur2.tourne = false;   
    Moteur3.tourne = false;    
    Moteur4.tourne = false;      
    if ((dx != 0) && (dy != 0))        // si les deux axes sont non nuls
    { // il faut lire le plus grand entre dx et dy
        // donner THPasDep au plus grand parcours 
        // et calculer le TPas de l'autre en rapport des dx/dy
        if (dx > dy)
        {
            THPasX = TpasDep;                 // le plus rapide forcé de démarrer
            float corr = dx / dy;             // tjs > 1
            THPasY = int (TpasDep * corr);    // autre moteur plus lent
            MotPlusActif = 1;
        }
        if (dy > dx)
        {
            THPasY = TpasDep;                 // le plus rapide forcé de démarrer
            float corr = dy / dx;             // tjs > 1
            THPasX = int (TpasDep * corr);    // autre moteur plus lent
            MotPlusActif = 2;
        }    
        if (dx == dy)
        {
            THPasX = THPasY = TpasDep;
            MotPlusActif = 1;
        }
        Moteur1.TPas = THPasX; 
        Moteur3.TPas = THPasX;
        Moteur2.TPas = THPasY; 
        Moteur4.TPas = THPasY;    
        // on indique qu'ils ont à faire tous les 4
        Moteur1.tourne = true;
        Moteur2.tourne = true;
        Moteur3.tourne = true;
        Moteur4.tourne = true;                        
        // et on relance les tempos de chaque moteur au double du nombre de pas        
        Moteur1.NumHPas = Moteur1.TPas << 1;   
        Moteur2.NumHPas = Moteur2.TPas << 1; 
        Moteur3.NumHPas = Moteur2.TPas << 1;  
        Moteur4.NumHPas = Moteur4.TPas << 1;  
        roule = true;        
    }    

    if ((dx == 0) || (dy == 0))   // si l'un des deux est nul
    {
        if (dx) 
        {      
            Moteur1.TPas = TpasDep; 
            Moteur3.TPas = TpasDep;      
            Moteur1.tourne = true;
            Moteur3.tourne = true;        
        }  
        if (dy) 
        {       
            Moteur2.TPas = TpasDep; 
            Moteur4.TPas = TpasDep;      
            Moteur2.tourne = true;
            Moteur4.tourne = true;         
        } 
        roule = true;
        // et on relance les tempos de chaque moteur qui doit tourner 
        if (Moteur1.tourne)      
            Moteur1.NumHPas = Moteur1.TPas << 1; // 2 périodes par micropas
        if (Moteur2.tourne)      
            Moteur2.NumHPas = Moteur2.TPas << 1; 
        if (Moteur3.tourne)      
            Moteur3.NumHPas = Moteur3.TPas << 1; 
        if (Moteur4.tourne)      
            Moteur4.NumHPas = Moteur4.TPas << 1;                              
    }    
  // et si on tourne sur soi-même 
    if (dc)
    {
        THPasX = THPasY = TpasDep;     // tous les moteurs au même rythme
        MotPlusActif = 1;              // on prend celui-l comme référence
        Moteur1.TPas = TpasDep;  // tous les moteur au rythme de départ
        Moteur2.TPas = TpasDep;
        Moteur3.TPas = TpasDep;         
        Moteur4.TPas = TpasDep; 
        Moteur1.tourne = true;
        Moteur2.tourne = true;              
        Moteur3.tourne = true;
        Moteur4.tourne = true;     
        roule = true;
        // et on relance les tempos de chaque moteur qui doit tourner 
        if (Moteur1.tourne)      
                Moteur1.NumHPas = Moteur1.TPas << 1;    
        if (Moteur2.tourne)      
                Moteur2.NumHPas = Moteur2.TPas << 1;    
        if (Moteur3.tourne)      
                Moteur3.NumHPas = Moteur3.TPas << 1;    
        if (Moteur4.tourne)      
                Moteur4.NumHPas = Moteur4.TPas << 1;                 
    }
    TPas = THPasDep; 
    digitalWrite (ENABLE, HIGH);        // on met en service les moteurs PaP
    if ((dx == 0) && (dy == 0) && (dc == 0))     // si les trois sont nuls
        roule = false;                // on ira chercher le mouvement suivant ... 
}

void arret()
{
  // si on a plus de moteur qui tourne  
  if (Moteur1.tourne || Moteur2.tourne || Moteur3.tourne || Moteur4.tourne);
  else  
  {
    timerAlarmDisable(timer);       // arrêt des interruptions
    roule = false;                  // et on l'indique
    if (DEBUGMOUV)
      Serial.println (" C'est fini pour ce mouvement");
    digitalWrite(ENABLE, LOW);      // on libère les moteurs
  }  
}

void mvtSuivant()
{
    dx = int (mvt.dx * impParMm); // dx est traduit en impulsions
    dy = int (mvt.dy * impParMm); // dy est traduit en impulsions
    dc = int (mvt.dcap * impParmRad); // dc traduit en impulsions
    sens();          // définition des sens et pose des signaux dx et dy devenus > 0
    // init des nombres de step des moteurs et du fait qu'il doit tourner
    if (dx)
    {
        Moteur1.nbPas = dx;
        Moteur1.tourne = true;    
        Moteur3.nbPas = dx;
        Moteur3.tourne = true;              
    }     
    if (dy)
    { 
        Moteur2.nbPas = dy;
        Moteur2.tourne = true;   
        Moteur4.nbPas = dy;
        Moteur4.tourne = true;               
    }       
    if (dc)
    {
        Moteur1.nbPas = dc;
        Moteur2.nbPas = dc;
        Moteur3.nbPas = dc;
        Moteur4.nbPas = dc;            
        Moteur1.tourne = true;
        Moteur2.tourne = true;   
        Moteur3.tourne = true;
        Moteur4.tourne = true;                   
    }
    // et init des numeros de pas des moteurs
    Moteur1.NumPas = 0;
    Moteur2.NumPas = 0;
    Moteur3.NumPas = 0;
    Moteur4.NumPas = 0;

    rythmes();              // calcul des pas sur chaque moteur qui doit tourner
    nbPas = 0;              // on a encore fait aucun pas
    //Accel = true; Deccel = false;   // on passe en accélération en début de mouvement
    //if (dc) Accel = false;  // pas d'accélération en rotation sur soi-même
    // on programme les interruptions timer correspondantes
    initMvt();  
    roule = true;
}

void gereAvance()
{
    if (Moteur1.tourne)    // celui-ci doit-il tourner ?
    {
        if (Moteur1.NumPas >= Moteur1.nbPas) // si les pas sont fini pour ce mouvement 
        Moteur1.tourne = false;                 // on signal qu'il ne tourne plus
    }
    if (Moteur2.tourne)    
    {
        if (Moteur2.NumPas >= Moteur2.nbPas) 
        Moteur2.tourne = false;
    }
    if (Moteur3.tourne)   
    {
        if (Moteur3.NumPas >= Moteur3.nbPas) 
        Moteur3.tourne = false;
    }
    if (Moteur4.tourne)    
    {
        if (Moteur4.NumPas >= Moteur4.nbPas) 
        Moteur4.tourne = false;
    }    
}

/******************    FONCTIONS    ROS   *******************************/

void twistMessageReceived(const geometry_msgs::Twist& twist)
{
    // Récupérer les valeurs linéaires et angulaires en mm et mrad
    float DX = 1000 * twist.linear.x;
    float DY = 1000 * twist.linear.y;
    float DZ = 1000 * twist.angular.z;
    // Transformer les valeurs en entier
    intDX = (int)DX;    // information entière en mm
    intDY = (int)DY;    // information en mm
    intDZ = (int)DZ;    // information en milli radian
    // Utiliser les valeurs de intDX, intDY et intDZ pour contrôler les moteurs, etc.
    recuROS = true;
}

ros::Subscriber<geometry_msgs::Twist> twist_sub("cmd_vel", twistMessageReceived);

// Initialiser ROS
void initROS()
{
    nh.initNode();
    nh.subscribe(twist_sub);
}

/****************   PROGRAMME  PROPREMENT DIT   ***********************/

void setup()
{
    initROS();          // ouverture du port série 
    creationTableauMoteurs();
    initSignaux();
}

void loop()
{
    nh.spinOnce();
    if (recuROS)
        cdeMoteurs();
    if (nouveau)
        mvtSuivant();
    if (roule)
    {
        gereAvance();
        arret();
    }
    delay (3);
}
Editor is loading...