Sommario:

Robot autobilanciante di Magicbit: 6 passaggi
Robot autobilanciante di Magicbit: 6 passaggi

Video: Robot autobilanciante di Magicbit: 6 passaggi

Video: Robot autobilanciante di Magicbit: 6 passaggi
Video: Back to the Future Hoverboard test #shorts #backtothefuture #hoverboard #martymcfly 2024, Luglio
Anonim

Questo tutorial mostra come realizzare un robot autobilanciato utilizzando la scheda di sviluppo Magicbit. Stiamo usando magicbit come scheda di sviluppo in questo progetto basato su ESP32. Pertanto qualsiasi scheda di sviluppo ESP32 può essere utilizzata in questo progetto.

Forniture:

  • magicbit
  • Driver del motore L298 a doppio ponte H
  • Regolatore lineare (7805)
  • Batteria Lipo 7.4V 700mAh
  • Unità di misura inerziale (IMU) (6 gradi di libertà)
  • motoriduttori 3V-6V DC

Fase 1: Storia

Storia
Storia
Storia
Storia

Ehi ragazzi, oggi in questo tutorial impareremo cose un po' complesse. Si tratta di robot di autobilanciamento che utilizzano Magicbit con Arduino IDE. Quindi iniziamo.

Prima di tutto, diamo un'occhiata a cos'è il robot autobilanciante. Il robot autobilanciato è un robot a due ruote. La particolarità è che il robot può bilanciarsi da solo senza utilizzare alcun supporto esterno. Quando l'alimentazione è accesa, il robot si alza in piedi e quindi si bilancia continuamente utilizzando movimenti di oscillazione. Quindi ora hai solo un'idea approssimativa del robot di autobilanciamento.

Fase 2: Teoria e Metodologia

Teoria e Metodologia
Teoria e Metodologia

Per bilanciare il robot, per prima cosa otteniamo dati da un sensore per misurare l'angolo del robot rispetto al piano verticale. A tale scopo abbiamo utilizzato MPU6050. Dopo aver ottenuto i dati dal sensore, calcoliamo l'inclinazione sul piano verticale. Se il robot è in posizione diritta e bilanciata, l'angolo di inclinazione è zero. In caso contrario, l'angolo di inclinazione è un valore positivo o negativo. Se il robot è inclinato in avanti, il robot dovrebbe spostarsi in avanti. Inoltre, se il robot è inclinato per invertire il lato, il robot dovrebbe spostarsi in direzione inversa. Se questo angolo di inclinazione è elevato, la velocità di risposta dovrebbe essere elevata. Viceversa l'angolo di inclinazione è basso, quindi la velocità di reazione dovrebbe essere bassa. Per controllare questo processo abbiamo utilizzato un teorema specifico chiamato PID. PID è un sistema di controllo utilizzato per controllare molti processi. PID sta per 3 processi.

  • P-proporzionale
  • I- integrale
  • D- derivata

Ogni sistema ha input e output. Allo stesso modo anche questo sistema di controllo ha degli input. In questo sistema di controllo questa è la deviazione dallo stato stabile. L'abbiamo chiamato errore. Nel nostro robot, l'errore è l'angolo di inclinazione rispetto al piano verticale. Se il robot è bilanciato, l'angolo di inclinazione è zero. Quindi il valore dell'errore sarà zero. Pertanto l'uscita del sistema PID è zero. Questo sistema include tre processi matematici separati.

Il primo è moltiplicare l'errore dal guadagno numerico. Questo guadagno è solitamente chiamato Kp

P=errore*Kp

La seconda è generare l'integrale dell'errore nel dominio del tempo e moltiplicarlo per parte del guadagno. Questo guadagno chiamato come Ki

I= Integrale(errore)*Ki

La terza è la derivata dell'errore nel dominio del tempo e la moltiplica per una certa quantità di guadagno. Questo guadagno è chiamato come Kd

D=(d(errore)/dt)*kd

Dopo aver aggiunto le operazioni sopra, otteniamo il nostro output finale

USCITA=P+I+D

A causa della parte P, il robot può ottenere una posizione stabile che è proporzionale alla deviazione. I parte calcola l'area di errore rispetto al grafico del tempo. Quindi cerca di portare il robot in una posizione stabile sempre in modo accurato. La parte D misura la pendenza nel tempo rispetto al grafico dell'errore. Se l'errore è in aumento, questo valore è positivo. Se l'errore è in diminuzione, questo valore è negativo. Per questo motivo, quando il robot si sposta in una posizione stabile, la velocità di reazione sarà ridotta e ciò sarà utile per rimuovere le sovraelongazioni non necessarie. Puoi saperne di più sulla teoria PID da questo link mostrato di seguito.

www.arrow.com/it/ricerca-ed-eventi/articoli/base-del-controller-pid-e-tutorial-implementazione-pid-in-arduino

L'uscita della funzione PID è limitata all'intervallo 0-255 (risoluzione PWM a 8 bit) e verrà alimentata ai motori come segnale PWM.

Passaggio 3: configurazione hardware

Configurazione hardware
Configurazione hardware

Ora questa è la parte della configurazione hardware. Il design del robot dipende da te. Quando hai progettato il corpo del robot devi considerarlo simmetrico rispetto all'asse verticale che giace nell'asse del motore. Il pacco batteria situato sotto. Pertanto il robot è facile da bilanciare. Nel nostro design fissiamo la scheda Magicbit verticalmente al corpo. Abbiamo usato due motoriduttori a 12V. Ma puoi usare qualsiasi tipo di motoriduttore. questo dipende dalle dimensioni del tuo robot.

Quando discutiamo del circuito, è alimentato da una batteria Lipo da 7,4 V. Magicbit usava 5V per l'alimentazione. Perciò abbiamo usato il regolatore 7805 per regolare la tensione della batteria a 5V. Nelle versioni successive di Magicbit quel regolatore non è necessario. Perché si alimenta fino a 12V. Forniamo direttamente 7.4V per il driver del motore.

Collegare tutti i componenti secondo lo schema sottostante.

Passaggio 4: configurazione del software

Nel codice abbiamo usato la libreria PID per calcolare l'output PID.

Vai al seguente link per scaricarlo.

www.arduinolibraries.info/libraries/pid

Scarica l'ultima versione di esso.

Per ottenere letture migliori del sensore abbiamo utilizzato la libreria DMP. DMP sta per processo di movimento digitale. Questa è una funzionalità integrata di MPU6050. Questo chip ha un'unità di elaborazione del movimento integrata. Quindi ci vuole lettura e analisi. Dopo aver generato output precisi e silenziosi al microcontrollore (in questo caso Magicbit (ESP32)). Ma ci sono molti lavori sul lato del microcontrollore per prendere quelle letture e calcolare l'angolo. Quindi, semplicemente, abbiamo usato la libreria DMP MPU6050. Scaricalo andando al seguente link.

github.com/ElectronicCats/mpu6050

Per installare le librerie, nel menu Arduino vai su strumenti-> includi libreria-> libreria add.zip e seleziona il file della libreria che hai scaricato.

Nel codice è necessario modificare correttamente l'angolo di setpoint. I valori delle costanti PID sono diversi da robot a robot. Quindi, nell'accordatura, prima imposta i valori di Ki e Kd a zero e poi aumenta Kp fino a ottenere una migliore velocità di reazione. Più Kp causa più overshoot. Quindi aumentare il valore di Kd. Aumentalo sempre di pochissimo. Questo valore è generalmente basso rispetto ad altri valori. Ora aumenta il Ki fino ad avere un'ottima stabilità.

Selezionare la porta COM corretta e il tipo di scheda. caricare il codice. Ora puoi giocare con il tuo robot fai-da-te.

Passaggio 5: schemi

Schematico
Schematico

Passaggio 6: codice

#includere

#include "I2Cdev.h" #include "MPU6050_6Axis_MotionApps20.h" #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif MPU6050 mpu; bool dmpReady = false; // imposta true se DMP init ha avuto successo uint8_t mpuIntStatus; // mantiene il byte di stato dell'interrupt effettivo dalla MPU uint8_t devStatus; // restituisce lo stato dopo ogni operazione del dispositivo (0 = successo, !0 = errore) uint16_t packetSize; // dimensione prevista del pacchetto DMP (il valore predefinito è 42 byte) uint16_t fifoCount; // conteggio di tutti i byte attualmente in FIFO uint8_t fifoBuffer[64]; // Buffer di memorizzazione FIFO Quaternion q; // [w, x, y, z] contenitore di quaternioni VectorFloat gravità; // [x, y, z] vettore gravità float ypr[3]; // [imbardata, beccheggio, rollio] contenitore imbardata/beccheggio/rollio e vettore di gravità double originalSetpoint = 172,5; doppio setpoint = originaleSetpoint; doppio spostamentoAngleOffset = 0,1; doppio ingresso, uscita; int moveState=0; doppio Kp = 23;//imposta P primo doppio Kd = 0,8;//questo valore generalmente piccolo doppio Ki = 300;//questo valore dovrebbe essere alto per una migliore stabilità PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);//pid inizializza int motL1=26;//4 pin per azionamento motore int motL2=2; int motR1=27; int motR2=4; volatile bool mpuInterrupt = false; // indica se il pin di interruzione MPU è andato alto void dmpDataReady() { mpuInterrupt = true; } void setup() { ledcSetup(0, 20000, 8); //pwm setup ledcSetup(1, 20000, 8); ledcSetup(2, 20000, 8); ledcSetup(3, 20000, 8); ledcAttachPin(motL1, 0);//pinmode dei motori ledcAttachPin(motL2, 1); ledcAttachPin(motR1, 2); ledcAttachPin(motR2, 3); // entra nel bus I2C (la libreria I2Cdev non lo fa automaticamente) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); Wire.setClock(400000); // Orologio I2C a 400kHz. Commenta questa riga se hai difficoltà di compilazione #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire::setup(400, true); #endif Serial.println(F("Inizializzazione dispositivi I2C…")); pinMode(14, INGRESSO); // inizializza la comunicazione seriale // (115200 scelto perché è richiesto per l'output di Teapot Demo, ma dipende // davvero da te a seconda del tuo progetto) Serial.begin(9600); mentre (!Seriale); // attendi l'enumerazione di Leonardo, gli altri continuano immediatamente // inizializza il dispositivo Serial.println(F("Inizializzazione dispositivi I2C…")); mpu.initialize(); // verifica connessione Serial.println(F("Verifica connessioni dispositivo…")); Serial.println(mpu.testConnection() ? F("Connessione MPU6050 riuscita"): F("Connessione MPU6050 non riuscita")); // carica e configura la DMP Serial.println(F("Inizializzazione DMP…")); devStatus = mpu.dmpInitialize(); // fornisci qui i tuoi offset giroscopici, scalati per la sensibilità minima mpu.setXGyroOffset(220); mpu.setYGyroOffset(76); mpu.setZGyroOffset(-85); mpu.setZAccelOffset(1788); // 1688 default di fabbrica per il mio chip di prova // assicurati che funzioni (restituisce 0 in caso affermativo) if (devStatus == 0) { // attiva il DMP, ora che è pronto Serial.println(F("Abilitazione DMP… ")); mpu.setDMPEnabled(true); // abilita il rilevamento degli interrupt di Arduino Serial.println(F("Abilitazione del rilevamento degli interrupt (Arduino external interrupt 0)…")); attachInterrupt(14, dmpDataReady, RISING); mpuIntStatus = mpu.getIntStatus(); // imposta il nostro flag DMP Ready in modo che la funzione main loop() sappia che va bene usarlo Serial.println(F("DMP ready! Waiting for first interrupt…")); dmpReady = vero; // ottiene la dimensione del pacchetto DMP prevista per un confronto successivo packetSize = mpu.dmpGetFIFOPacketSize(); //imposta PID pid. SetMode(AUTOMATIC); pid. SetSampleTime(10); pid. SetOutputLimits(-255, 255); } else { // ERRORE! // 1 = caricamento della memoria iniziale non riuscito // 2 = aggiornamenti della configurazione DMP non riusciti // (se si interrompe, di solito il codice sarà 1) Serial.print(F("Inizializzazione DMP non riuscita (codice ")); Serial. print(devStatus); Serial.println(F(")")); } } void loop() { // se la programmazione fallisce, non provare a fare nulla se (!dmpReady) return; // attendi l'interruzione MPU oi pacchetti aggiuntivi disponibili while (!mpuInterrupt && fifoCount < packetSize) { pid. Compute(); // questo periodo di tempo viene utilizzato per caricare i dati, quindi puoi usarlo per altri calcoli motorSpeed(produzione); } // resetta il flag di interrupt e ottiene INT_STATUS byte mpuInterrupt = false; mpuIntStatus = mpu.getIntStatus(); // ottiene il conteggio FIFO corrente fifoCount = mpu.getFIFOCount(); // controlla l'overflow (questo non dovrebbe mai accadere a meno che il nostro codice non sia troppo inefficiente) if ((mpuIntStatus & 0x10) || fifoCount == 1024) { // resetta così possiamo continuare in modo pulito mpu.resetFIFO(); Serial.println(F("Fifo overflow!")); // in caso contrario, controlla l'interruzione di DMP data ready (questo dovrebbe accadere frequentemente) } else if (mpuIntStatus & 0x02) { // attendi la corretta lunghezza dei dati disponibili, dovrebbe essere un'attesa MOLTO breve while (pacchetto fifoCount 1 disponibile // (questo ci consente di leggere immediatamente di più senza attendere un'interruzione) fifoCount -= packetSize; mpu.dmpGetQuaternion(&q, fifoBuffer); mpu.dmpGetGravity(&gravity, &q); mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); #if LOG_INPUT Serial. print("ypr\t"); Serial.print(ypr[0] * 180/M_PI);//eule angoli Serial.print("\t"); Serial.print(ypr[1] * 180/M_PI); Serial.print("\t"); Serial.println(ypr[2] * 180/M_PI); #endif input = ypr[1] * 180/M_PI + 180; } } void motorSpeed(int PWM){ float L1, L2, R1, R2; if(PWM>=0){//direzione avanti L2=0; L1=abs(PWM); R2=0; R1=abs(PWM); if(L1>=255){ L1=R1=255; } } else {//direzione all'indietro L1=0; L2=abs(PWM); R1=0; R2=abs(PWM); if(L2>=255){ L2=R2=255; } } //azionamento motore ledcWrite(0, L1); ledcWrite(1, L2); ledcWrite(2, R1*0.97);//0.97 è il fatto di velocità oppure, poiché il motore destro ha un'alta velocità rispetto al motore sinistro, quindi lo riduciamo fino a quando le velocità del motore sono uguali ledcWrite(3, R2*0.97);

}

Consigliato: