Aller au contenu


Photo
- - - - -

BurnBot - Self Balancing Robot


151 réponses à ce sujet

#141 Budet

Budet

    Habitué

  • Membres
  • PipPip
  • 256 messages
  • Gender:Male
  • Location:60

Posté 02 mars 2018 - 08:52

La methode de tunning marche bien ! Je lai testé rapidement et jai une tres bonne réponse aux perturbations, et il tiens parfaitement à lequiLibre.

Par contre il tremble beaucoup, il nest pas stable. Je vais essayé de recommencer le process plus sérieusement pour voir si ca réduit les vibrations.

Je peux aussi jouer sur le temps de computation, ca a lair de beaucoup jouer sur ce problème

#142 Budet

Budet

    Habitué

  • Membres
  • PipPip
  • 256 messages
  • Gender:Male
  • Location:60

Posté 06 mars 2018 - 09:43

Bonjour à tous,

 

Je pense avoir atteint le mieux que je puisse faire pour la partie "Balancement". BurnBot tient vraiment bien à l'équilibre, j'ai amélioré sa stabilité, il ne tremble presque plus :D

 

Grâce au traceur j'ai pu me rendre plus fidèlement compte du comportement de mon PID et j'ai trouvé les bons paramètre que je vous partage ici :

 

Temps de computation : 5 ms

OutputLimits : -3000, 3000 (quasiment les valeurs max de mes moteurs pas à pas)

SetPoint : 0.2

Kp : 500

Ki : 15000

Kd : 0.8

 

Voici une petite vidéo encore pour illustrer tout ça : 

 

 

Maintenant je me concentre sur comment le déplacer et comme je vous l'ai dit j'ai quelques difficultés sur ce point la. Pour le faire tourner à droite ou a gauche c'est facile, il me suffit de créer une différence entre la vitesse des deux moteurs. Comme vous pouvez le voir dans la vidéo ça marche plutôt bien, je vais juste créer un décalage plus important pour qu'il tourne plus vite. 

 

Pour avancé et reculé c'est vraiment plus compliqué ! J'ai essayé en mettant +500 (par exemple) aux vitesses des deux moteurs, sauf que le robot compense tout seul en baissant la vitesse pour maintenir sa consigne d'équilibre (bah oui je l'ai trop bien conçu ;) ). 

 

Du coup l'idée que j'ai depuis le début c'est de jouer sur la consigne (setpoint). Là elle est à 0.2° (consigne d'équilibre) et pour moi si je la met à 10° le robot devrait se déplacer vers l'avant car il serait constamment entrain de tomber. Et pourtant comme vous pouvez le voir dans la vidéo, je l'incline à +10° et -10° et la seule chose qu'il fait s'est de se pencher sans reculer n'y avancer....

 

Est-ce les câbles qui l’empêche de bouger ou mon idée qui n'est carrément pas la bonne ? Pas facile en tout cas :)



#143 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 3 296 messages
  • Gender:Male

Posté 06 mars 2018 - 02:14

Ah oui, c'est vrai !
Ce sont les mêmes moteurs qui font avancer le robot et qui le maintiennent en équilibre.
C'est peut-être stupide ce que je vais dire, mais pour moi, il faut d'abord avancer puis rétablir l'équilibre.
Quand je dis avancer, je pense à 5° ou 10°.
Ma chaine vidéo YouTube : https://www.youtube....wMz-IzZqyvqEUek

#144 Budet

Budet

    Habitué

  • Membres
  • PipPip
  • 256 messages
  • Gender:Male
  • Location:60

Posté 07 mars 2018 - 04:13

Bonjour à tous,

 

C'est bon, j'arrive en fin à faire déplacer BurnBot ! Mon idée de jouer sur le SetPoint de mon PID était bonne, il fallait juste qu'au lieu de passer radicalement d'une valeur à une autre (de 0 à 10° d'un coup par exemple), que j'incrémente/décrémente le Setpoint progressivement pour avoir un comportement souple (et non brusque). 

 

Du coup ça marche plutôt bien comme vous pouvez le voir dans cette vidéo :

 

 

Bon pour le coup les cables gènes un peu et empêche d'avoir quelque chose de parfaitement fluide, et il faudrait que je peaufine un peu tout ça mais je suis satisfait du résultat.

 

J'ai encore travaillé/amélioré la stabilité du robot et j'ai vraiment un très bon comportement aux perturbations :

 

 

 

 

 

 

C'est ainsi que je pense clore mon expérience avec BrunBot :) J'ai atteins tous les objectifs que je mettais fixé, je suis super satisfait du résultat et je ne souhaite pas pousser le robot plus loin pour l'instant. J'ai commandé de quoi faire un futur robot de type "Self balancing" que je détaillerais dans un autre post. Les objectifs seront de miniaturiser au maximum le robot, de le munir d'une batterie (plus aucun cable) et de le commander à distance grâce à une télécommande home made.

 

Voilà, BurnBot m'a permit de me familiariser avec les PID, les moteurs pas à pas, l'impression 3D et arduino en générale, je le retravaillerais surement un jour mais je préfère tourner mes futurs objectifs vers un robot encore plus efficaces, qui aura de plus une réelle fonction (autre que de rouler et tenir à l'équilibre) mais ça je vous en parlerez plus tard :P

 

 

PS : Je ferais surement une dernière vidéo ou je jouerais avec des parois inclinées, ou il franchira des obstacles etc...



#145 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 3 296 messages
  • Gender:Male

Posté 07 mars 2018 - 05:49

Bravo Budet !
Super projet, mené à son terme !
Ma chaine vidéo YouTube : https://www.youtube....wMz-IzZqyvqEUek

#146 Path

Path

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 2 109 messages
  • Gender:Male
  • Location:Paris

Posté 07 mars 2018 - 06:49

Hé hé génial :)
Du coup, tu peux faire un schéma du pid que tu as au final stp ?

Podcast Made By Humans

Je cherche des volontaires de tous niveaux pour nos petites conversations entre hobbyistes.

Accès aux salles secrètes

 


#147 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 3 296 messages
  • Gender:Male

Posté 07 mars 2018 - 07:15

Moi, j'aimerais bien jeter un petit coup d'œil au code, complet... :thank_you:


Ma chaine vidéo YouTube : https://www.youtube....wMz-IzZqyvqEUek

#148 Budet

Budet

    Habitué

  • Membres
  • PipPip
  • 256 messages
  • Gender:Male
  • Location:60

Posté 07 mars 2018 - 09:08

Je vous met le code demain :) vous allez voir il est pas si complexe :)

#149 Path

Path

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 2 109 messages
  • Gender:Male
  • Location:Paris

Posté 07 mars 2018 - 09:17

Super !! merci


Podcast Made By Humans

Je cherche des volontaires de tous niveaux pour nos petites conversations entre hobbyistes.

Accès aux salles secrètes

 


#150 Budet

Budet

    Habitué

  • Membres
  • PipPip
  • 256 messages
  • Gender:Male
  • Location:60

Posté 08 mars 2018 - 09:35

Pour ceux que ça intéresse ;)

//Ce programme permet de régler le premier PID de BurnBot, je me suis rendu compte qu'il fonctionnait très bien avec seulement un PID pour le moment donc j'ai abandonné l'idée d'en combiner deux


#include "I2Cdev.h"
#include<Wire.h>  // librairie pour le gyroscope
#include <PID_v1.h> // librairie pour le PID
#include "MPU6050_6Axis_MotionApps20.h" // librairie pour le gyroscope
#include <AccelStepper.h> // librairie pour les moteurs pas à pas
 
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif
 
// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 mpu;
//MPU6050 mpu(0x69); // <-- use for AD0 high

float vitesse;          //Vitesse des moteurs
int incomingByte = 0;   //Byte utilisé dans le serial.read
char CharByte;
int Controle = 0;       //Variable contenant l'instruction de direction

double i = 0; //Coefficient pour le controle du robot

int vit1 = 0; //Différence de vitesse pour faire tourner le robot
int vit2 = 0;
unsigned long temps = 0; //Variable utilisée dans certains calcul sur excel

AccelStepper roueD(1, 9, 6); //9 sortie digital avec laquelle on contrôle la vitesse, le 6 la même pour le sens de rotation.
AccelStepper roueG(1, 8, 5);

double Setpoint, Input, Output; //Variales du PID

double Kp=300, Ki=15000, Kd=1; //Coefficients du PID //500 600 2.85 //500 750 5 //450, 15000, 0.8 (valeur très correct)
float Pas3 = 0.1;   //Variables permettant de régler le PID
float Pas2 = 5;
float Pas1 = 5;

PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT); //Définition du PID

 
 
#define OUTPUT_READABLE_YAWPITCHROLL

 #define ARDUINO_BOARD
 
 
#define LED_PIN 13      // (Galileo/Arduino is 13)
bool blinkState = false;
 
// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
 
// orientation/motion vars
VectorFloat gravity;    // [x, y, z]            gravity vector
Quaternion q;           // [w, x, y, z]         quaternion container
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
 
 
 
// ================================================================
// ===               INTERRUPT DETECTION ROUTINE                ===
// ================================================================
 
// This function is not required when using the Galileo 
volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}
 
 
 
// ================================================================
// ===                      INITIAL SETUP                       ===
// ================================================================
 
void setup() {
    // join I2C bus (I2Cdev library doesn't do this automatically)
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
        int TWBR; // 400kHz I2C clock (200kHz if CPU is 8MHz)
        TWBR = 24;
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif
 
    Serial.begin(115200);
    while (!Serial);
 
    // initialize device
    Serial.println(F("Initializing I2C devices..."));
    mpu.initialize();
 
    // verify connection
    Serial.println(F("Testing device connections..."));
    Serial.println(F("MPU6050 connection "));
    Serial.print(mpu.testConnection() ? F("successful") : F("failed"));
 
    // wait for ready
    Serial.println(F("\nSend any character to begin DMP programming and demo: "));
    //while (Serial.available() && Serial.read()); // empty buffer
   // while (!Serial.available());                 // wait for data
   // while (Serial.available() && Serial.read()); // empty buffer again
 
    // load and configure the DMP
    Serial.println(F("Initializing DMP..."));
    devStatus = mpu.dmpInitialize();
 
    // supply your own gyro offsets here, scaled for min sensitivity
    mpu.setXGyroOffset(45);                                                                             //A qualibrer avec l'autre programme pour l'offset du MPU
    mpu.setYGyroOffset(38);
    mpu.setZGyroOffset(29);
    mpu.setZAccelOffset(1758); // 1688 factory default for my test chip
 
    // make sure it worked (returns 0 if so)
    if (devStatus == 0) {
        // turn on the DMP, now that it's ready
        Serial.println(F("Enabling DMP..."));
        mpu.setDMPEnabled(true);
 
        // enable Arduino interrupt detection
        Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
        attachInterrupt(0, dmpDataReady, RISING);
        mpuIntStatus = mpu.getIntStatus();
 
        // set our DMP Ready flag so the main loop() function knows it's okay to use it
        Serial.println(F("DMP ready! Waiting for first interrupt..."));
        dmpReady = true;
 
        // get expected DMP packet size for later comparison
        packetSize = mpu.dmpGetFIFOPacketSize();
    } else {
        // ERROR!
        // 1 = initial memory load failed
        // 2 = DMP configuration updates failed
        // (if it's going to break, usually the code will be 1)
        Serial.print(F("DMP Initialization failed (code "));
        Serial.print(devStatus);
        Serial.println(F(")"));
    }
 
    // configure LED for output
    pinMode(LED_PIN, OUTPUT);

    roueD.setMaxSpeed(3500); // Vitesse max du moteur en nombre de pas par seconde
    //roueD.setAcceleration(1000); //en step par seconde

    roueG.setMaxSpeed(3500); // Vitesse max du moteur en nombre de pas par seconde
    //roueG.setAcceleration(1000); //en step par seconde
    
    vitesse = 0;  //Initialisation de la vitesse à 0

    Setpoint = 0.2;   //Point d'équilibre parfait

    myPID.SetMode(AUTOMATIC); //Initialisation du PID en mode automatique
    myPID.SetOutputLimits(-3500, 3500); //définition des limites du PID
    myPID.SetTunings(Kp, Ki, Kd); //Application des coefficients
    myPID.SetSampleTime(5); //La computation est executé toutes les 5ms

    delay(5000);  //Delai de 5 seconde pour attendre que le MPU se calibre
}
 
 
 
// ================================================================
// ===                    MAIN PROGRAM LOOP                     ===
// ================================================================
 
void loop() {

roueD.runSpeed();
roueG.runSpeed();
          
  if (Serial.available() > 0) {             //Changement Manuel des coefficients du PID + ordre de déplacement
          incomingByte = Serial.read();

          CharByte = incomingByte;

          if (CharByte == 49)
          {
            Kp = Kp + Pas1;
          }

          if (CharByte == 50)
          {
            Kp = Kp - Pas1;
          }

          if (CharByte == 52)
          {
            Ki = Ki + Pas2;
          }

          if (CharByte == 53)
          {
            Ki = Ki - Pas2;
          }

          if (CharByte == 55)
          {
            Kd = Kd + Pas3;
          }

          if (CharByte == 56)
          {
            Kd = Kd - Pas3;
          }
          
           if (CharByte == 122)
          {
  
              Controle = 1; //avant
          }

          if (CharByte == 115)
          {
              Controle = 2; //arrière
          }

          if (CharByte == 113)
          {
            Controle = 4; //droite
          }

          if (CharByte == 100)
          {
            Controle = 3; //gauche
          }

          if (CharByte == 97)
          {
            Controle = 0; //rien faire
          }

          //Serial.println(incomingByte);

          myPID.SetTunings(Kp, Ki, Kd); //On applique les nouveau coefficients
roueD.runSpeed();
roueG.runSpeed();
      }
      
    // if programming failed, don't try to do anything
    if (!dmpReady) return;
roueD.runSpeed();
roueG.runSpeed(); 
    // wait for MPU interrupt or extra packet(s) available
 
    #ifdef ARDUINO_BOARD
        while (!mpuInterrupt && fifoCount < packetSize) {
roueD.runSpeed();
roueG.runSpeed();
        }
    #endif
 
    // reset interrupt flag and get INT_STATUS byte
    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();
 
    // get current FIFO count
    fifoCount = mpu.getFIFOCount();
 
    // check for overflow (this should never happen unless our code is too inefficient)
    if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
roueD.runSpeed();
roueG.runSpeed();
        // reset so we can continue cleanly
        mpu.resetFIFO();
        //Serial.println(F("FIFO overflow!"));                                                                              //C'est moi qui l'ai mis en commentaire
 
    // otherwise, check for DMP data ready interrupt (this should happen frequently)
    } else if (mpuIntStatus & 0x02) {
roueD.runSpeed();
roueG.runSpeed();
        // wait for correct available data length, should be a VERY short wait
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
roueD.runSpeed(); 
roueG.runSpeed();
        // read a packet from FIFO
        mpu.getFIFOBytes(fifoBuffer, packetSize);
        
        // track FIFO count here in case there is > 1 packet available
        // (this lets us immediately read more without waiting for an interrupt)
        fifoCount -= packetSize;
 
 
        #ifdef OUTPUT_READABLE_YAWPITCHROLL
            // display Euler angles in degrees
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
roueD.runSpeed();
roueG.runSpeed();       
            
        #endif

        if (Controle == 2) {                  //Toute cette partie avec les if sert au contrôle du robot
          i = 0;
          Setpoint = Setpoint + 0.05;
          
          if (Setpoint >= 6)Setpoint = 6;

        }
        if (Controle == 1) {
          i = 0;
          Setpoint = Setpoint - 0.05;
          if (Setpoint <= -6)Setpoint = -6;

        }
        if (Controle == 3) {
          vit1 = 700;
          vit2 = -700;
            
        }
        if (Controle == 4) {
          vit1 = -700;
          vit2 = 700;
        }
        if (Controle == 0) {
          vit1 = 0;
          vit2 = 0;
          Setpoint = Setpoint-i*Setpoint;

          i = i + 0.01;
          if(i >= 1)i = 1;
          
        }
        
        //Serial.print(Controle); Serial.print("  "); Serial.println(Setpoint);
 
        // blink LED to indicate activity
        blinkState = !blinkState;
        digitalWrite(LED_PIN, blinkState);

        Input = (ypr[1] * 180/M_PI); //On change l'input
       
        myPID.Compute();  //On compute le pid
        vitesse = Output; // On applique l'output à la vitesse

        roueD.setSpeed(vitesse+vit1);
        roueD.runSpeed();

        roueG.setSpeed(-1*(vitesse+vit2)); //On inverse la vitesse du deuxième moteur
        roueG.runSpeed();

        Serial.print(roueD.currentPosition()); Serial.print("\t");
        Serial.print(roueG.currentPosition()); Serial.print("\t");
        Serial.print(Input); Serial.print("\t");
        Serial.print(vitesse); Serial.print("\t");
        Serial.print(Kp); Serial.print("\t");
        Serial.print(Ki); Serial.print("\t");
        Serial.println(Kd);


temps = millis();

        //Serial.print(temps); Serial.print("\t");
       // Serial.print(Setpoint); Serial.print(", ");
       // Serial.print(Input-Setpoint);
      //  Serial.println();

        
    }
}


#151 Oracid

Oracid

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 3 296 messages
  • Gender:Male

Posté 08 mars 2018 - 09:46

Super ! Il n'y a plus qu'à étudier tout ça...
Ma chaine vidéo YouTube : https://www.youtube....wMz-IzZqyvqEUek

#152 Path

Path

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 2 109 messages
  • Gender:Male
  • Location:Paris

Posté 08 mars 2018 - 12:53

C'est beau. Merci. Un bel exemple d'utilisation de la lib PID. Je vais regarder plus en détail ta manière de gérer le setpoint. Surtout quand tu arrête le robot.

Podcast Made By Humans

Je cherche des volontaires de tous niveaux pour nos petites conversations entre hobbyistes.

Accès aux salles secrètes

 




Répondre à ce sujet



  


0 utilisateur(s) li(sen)t ce sujet

0 members, 0 guests, 0 anonymous users