Robotik
OKUDUĞUNUZ KONU
Arduino Kendini Dengeleyen Robot
89

Arduino Kendini Dengeleyen Robot

Yazar : Murat Duran25 Ekim 2015
Projeyi Satın Al
Bu projeyi kurulu çalışır halde yada isterseniz demonte halde satın alabilirsiniz.Proje ile birlikle yapım aşaması resimleri bağlantı şemaları ve kodları açık kaynak olarak gönderilir. "Satın Almak İçin Yukarıda Butona Basınız"

Daha önceden kendini dengeleyen robot projesi paylaşmıştık , bu robotumuz çok katlı ve dengede durması biraz daha zor.Bu yüzden kodlamada gelişmiş PID algoritma kullanıldı.Bu tarz robotlarda normalde enkoderli motor kullanılır ama bu motorlar pahalı olduğundan tercih etmedik piyasada 15-16 tl ye bulabileceğiniz ucuz motorları kullandık.Plastik için saklama kabı , demirler içinde M3 gijon kullanıldı.Herhangi bir hırdavatçıdan temin edebileceğiniz malzemeler

Kontrol kartı olarak Arduino UNO , motor sürücü için L298N modeli tercih edildi.Bu model sinyallere gayet iyi tepki veriyor.Ayırıca piyasada bulunur ve ucuz.İvme ölçer için MPU6050 kullanıldı.Kendinden 3 eksen GYRO da var.

Malzemeler

Öncelikle aşağıdaki resimlere bakarak benzer bir iskelet oluşturmaya çalışın.Motorları yerleştirirken denge merkezine koymaya çalışın.Ne kadar düzgün olursa robotunuz o kadar verimli çalışır.

Ortama 20×8 cm boyutlarında 3 tane plastik tabaka kesin.Ardından gijonu 20 cm uzunluklara bölün.Plastik tabakaların köşelerinden delerek giyonları yerleştirin ve robotunuzu 3 kalı şekilde ayarlayın.

Projenin mekanik kısmını hazırladıktan sonra elektronik bölümünü eklemeniz gerekiyor.Arduino , motor sürücü gibi kartları yerleştirirken yine ağırlık merkezine dikkat ederek yerleştirin.L928N Motor sürücü ve MOU6050 ivme ölçer bağlantısı aşağıdaki gibidir.

Ardından gijonların üzerine 3 tane potansiyemetreyi silikonla tutturun ve bu potansiyemetereli arduinoda sırasyıyla A0 , A1 ve A2 girişlerine bağlıyoruz.Ayarlarla oynadığımızda KP , KD ve Kİ değerleri değişmektedir.Robotumuzun son halleri.

Kendini Dengeleyen Robot Kütüphaneleri İndir , Projemizin arduino kodları –>

//Proje Hocam - Kendini Dengeleyen Robot
//www.projehocam.com
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"

#include "MPU6050_6Axis_MotionApps20.h"

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif


#define LOG_INPUT 0
#define MANUAL_TUNING 0
#define LOG_PID_CONSTANTS 0 
#define MOVE_BACK_FORTH 0

#define MIN_ABS_SPEED 30 // Minumum Başlangıç Hızı

//MPU


MPU6050 mpu;

// 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
Quaternion q;           // [w, x, y, z]         quaternion container
VectorFloat gravity;    // [x, y, z]            gravity vector
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector


//PID


#if MANUAL_TUNING
  double kp , ki, kd;
  double prevKp, prevKi, prevKd;
#endif
double originalSetpoint = 174.29;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.3;
double input, output;
int moveState=0; //0 = denge ; 1 = geri ; 2 = ileri

#if MANUAL_TUNING
  PID pid(&input, &output, &setpoint, 0, 0, 0, DIRECT);
#else
  PID pid(&input, &output, &setpoint, 70, 240, 1.9, DIRECT);
#endif


//MOTOR SURUCU


int ENA = 3;
int IN1 = 4;
int IN2 = 8;
int IN3 = 5;
int IN4 = 7;
int ENB = 6;


LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, 0.6, 1);


//Zamanlama


long time1Hz = 0;
long time5Hz = 0;


volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady()
{
    mpuInterrupt = true;
}


void setup()
{
    // join I2C bus (I2Cdev library doesn't do this automatically)
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
        TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif

    // initialize serial communication
    // (115200 chosen because it is required for Teapot Demo output, but it's
    // really up to you depending on your project)
    Serial.begin(115200);
    while (!Serial); // wait for Leonardo enumeration, others continue immediately

    // initialize device
    Serial.println(F("I2C Kuruluyor..."));
    mpu.initialize();

    // verify connection
    Serial.println(F("Suruculer test ediliyor..."));
    Serial.println(mpu.testConnection() ? F("MPU6050 baglanti basarili") : F("MPU6050 baglanti basarisiz"));

    // load and configure the DMP
    Serial.println(F("DMP kuruluyor..."));
    devStatus = mpu.dmpInitialize();

    // supply your own gyro offsets here, scaled for min sensitivity
    mpu.setXGyroOffset(220);
    mpu.setYGyroOffset(76);
    mpu.setZGyroOffset(-85);
    mpu.setZAccelOffset(1788); // 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();
        
        //setup PID
        
        pid.SetMode(AUTOMATIC);
        pid.SetSampleTime(10);
        pid.SetOutputLimits(-255, 255);  
    }
    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(")"));
    }
}


void loop()
{
    // if programming failed, don't try to do anything
    if (!dmpReady) return;

    // wait for MPU interrupt or extra packet(s) available
    while (!mpuInterrupt && fifoCount < packetSize) { //no mpu data - performing PID calculations and output to motors pid.Compute(); motorController.move(output, MIN_ABS_SPEED); unsigned long currentMillis = millis(); if (currentMillis - time1Hz >= 1000)
        {
            loopAt1Hz();
            time1Hz = currentMillis;
        }
        
        if (currentMillis - time5Hz >= 5000)
        {
            loopAt5Hz();
            time5Hz = currentMillis;
        }
    }

    // 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)
    {
        // reset so we can continue cleanly
        mpu.resetFIFO();
        Serial.println(F("FIFO overflow!"));

    // otherwise, check for DMP data ready interrupt (this should happen frequently)
    }
    else if (mpuIntStatus & 0x02)
    {
        // wait for correct available data length, should be a VERY short wait
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount(); // 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;

        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);
            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 loopAt1Hz()
{
#if MANUAL_TUNING
    setPIDTuningValues();
#endif
}


void loopAt5Hz()
{
    #if MOVE_BACK_FORTH
        moveBackForth();
    #endif
}


//move back and forth


void moveBackForth()
{
    moveState++;
    if (moveState > 2) moveState = 0;
    
    if (moveState == 0)
      setpoint = originalSetpoint;
    else if (moveState == 1)
      setpoint = originalSetpoint - movingAngleOffset;
    else
      setpoint = originalSetpoint + movingAngleOffset;
}


//PID Tuning (3 potentiometers)

#if MANUAL_TUNING
void setPIDTuningValues()
{
    readPIDTuningValues();
    
    if (kp != prevKp || ki != prevKi || kd != prevKd)
    {
#if LOG_PID_CONSTANTS
        Serial.print(kp);Serial.print(", ");Serial.print(ki);Serial.print(", ");Serial.println(kd);
#endif

        pid.SetTunings(kp, ki, kd);
        prevKp = kp; prevKi = ki; prevKd = kd;
    }
}


void readPIDTuningValues()
{
    int potKp = analogRead(A0);
    int potKi = analogRead(A1);
    int potKd = analogRead(A2);
        
    kp = map(potKp, 0, 1023, 0, 25000) / 100.0; //0 - 250
    ki = map(potKi, 0, 1023, 0, 100000) / 100.0; //0 - 1000
    kd = map(potKd, 0, 1023, 0, 500) / 100.0; //0 - 5
}
#endif
SİZCE NASIL OLMUŞ?
Beğendim
67%
İlginç
15%
Eh İşte
10%
Anlamadım
8%
Kötü
0%
Berbat
2%
YAZAR HAKKINDA
Murat Duran
Murat Duran
Açık kaynak donanım ve yazılım geliştiricisiyim , mekanik ve robotik sistemler üzerinde çalışmalar yapmaktayım.Ayrıca bir start-up firması olan Proje Hocam 'ın kurucuyum.
89 YORUMLAR
1 2
  • Bahattin Çetin
    6 Mart 2017 at 14:10

    dagu motor yerine normal sarı motor tekerleklerden kullansak olurmu

  • Ahmet AKIŞIK
    7 Mart 2017 at 22:08

    Bize niye cevap yazmadiniz hocam overflow hatasi nasil giderilir

  • İsmail aki
    8 Mart 2017 at 16:17

    Yazilimi atarken sorun yaşıyorum gyroyu tanimliyamiyor bunun icin bir öneriniz var mi

  • Ahmet AKIŞIK
    8 Mart 2017 at 20:12

    Hayır hocam yükleme falan herşey tamam 2-3sn çalışıyor gibi oluyor sonra tam gaz dönüyor.Serialden bakıyorum overflow hatası veriyor neden olabilir acaba.Başka bir arkadaşında da aynı sorunu varmış

  • Sefer Ali DURSUN
    11 Mart 2017 at 19:35

    ardiuno uno yerine ardiuno mega kullansak olur mu ?

  • Mehmet dönmez
    21 Mart 2017 at 18:35

    Owerflow hatasì icin ben asagidaki 64 sayısını 128 yaptım oldu
    Deneyin isterseniz.
    uint8_t fifoBuffer[64]; // FIFO storage buffer

  • Ahmet AKIŞIK
    31 Mart 2017 at 23:03

    Arkadaşım ben bir türlü çalıştıramadım tüm her şey tamam ama dengede durmuyor bir kaç sn titriyor sonra tam gaz dönüyor.Siz denelemek için hangi ayarları yaptınız.

  • Mehmet BOZHÖYÜK
    6 Nisan 2017 at 20:56

    Hocam yazılımda MPU6050 ile ilgili işlemler neden çok fazla .Neden mpu.getMotion6 deyip verileri alıp kullanmıyor .Kalman filtresini mi bu yazılımın içindeki?

  • Mehmet BOZHÖYÜK
    6 Nisan 2017 at 21:16

    Başka bir sorum daha olacaktı:Her ürün için PİD hesaplaması yapabilmek için neleri öğrenmek lazım .(Trigonometri türev integral biliyorum sadece]

  • Ziyaretçi
    8 Nisan 2017 at 18:56

    Çok güzel anlatmışsınız. Ben 12 yaşındayım. Bu konuda biraz acemiyim. Herneyse bizim okulda sergi olucak. Ben de arkadaşım ile birlikte bunu yapmak istedim hocamız da çok beğendi. Fakat buna hocam hiç bakmadığı için bilmiyor. Bu durumda da hocamızdan yardım alamayacağız. Ben bir soru sorcaktım: o 3 potansiyometreyi tam olarak nereye bağlıyoruz ve onları sırasıyla nasıl A0 A1 ve A2 girişlerine bağlıyoruz? Umarım bu sorumu görürsünüz. Uzatığım için özür dilerim. Cevabınızı bekliyorum

  • abdullah karabulut
    30 Nisan 2017 at 22:07

    merhaba hocam..
    bütün mekanik ve elektronik aksamını yaptım arduınoyada programı hatasız bir şekilde attım ama sisteme enerji verdiğimde hiç bir tepki alamıyorum sebebi size nedir? yardımcı olursanız çok sevinirim.

  • idil aze türkdoğan
    16 Mayıs 2017 at 15:29

    merhaba. şimdi benim projem denge robotu ve aklımda birkaç soru var lütfen en yakın zamanda cevap verirseniz çok iyi olur.
    1.) 6v 500 rpm mikro dc motor kullanabilirmiyim? (bu motorları aldım )
    2.) lipo pil yerine 9v pil kullansam olurmu?
    başka yerlerden de baktım ve motor sürücü entegre l293d aldım .lütfen en kısa zamanda cevap verirseniz çok sevinirim.

  • idris yeğ
    20 Ekim 2017 at 16:31

    error: ‘currentMillis’ was not declared in this scope

    time1Hz = currentMillis;

    ^

    dengesiz:170: error: ‘currentMillis’ was not declared in this scope

    if (currentMillis – time5Hz >= 5000)

    ^

    exit status 1
    ‘currentMillis’ was not declared in this scope

    //çözümünü bulan var mı?
    Kütüphaneler yüklü

  • emre duran
    30 Ekim 2017 at 18:47

    bn herseyi yaptım koduda atım ama tekarler donmuyor sorun ne olabilir ?

  • Tevfik Saglam
    8 Kasım 2017 at 20:34

    Arduino: 1.8.5 (Windows 7), Board: “Arduino/Genuino Uno”

    In file included from C:\Users\Mete YAZICI\Desktop\sketch_nov08b\sketch_nov08b.ino:7:0:

    C:\Users\Mete YAZICI\Desktop\arduino-1.8.5\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h: In member function ‘uint8_t MPU6050::dmpGetAccel(int32_t*, const uint8_t*)’:

    C:\Users\Mete YAZICI\Desktop\arduino-1.8.5\libraries\MPU6050/MPU6050_6Axis_MotionApps20.h:580:31: warning: left shift count >= width of type

    data[0] = ((packet[28] << 24) + (packet[29] << 16) + (packet[30] <= width of type

    data[0] = ((packet[28] << 24) + (packet[29] << 16) + (packet[30] <= width of type

    data[1] = ((packet[32] << 24) + (packet[33] << 16) + (packet[34] <= width of type

    data[1] = ((packet[32] << 24) + (packet[33] << 16) + (packet[34] <= width of type

    data[2] = ((packet[36] << 24) + (packet[37] << 16) + (packet[38] <= width of type

    data[2] = ((packet[36] << 24) + (packet[37] << 16) + (packet[38] <= width of type

    data[0] = ((packet[0] << 24) + (packet[1] << 16) + (packet[2] <= width of type

    data[0] = ((packet[0] << 24) + (packet[1] << 16) + (packet[2] <= width of type

    data[1] = ((packet[4] << 24) + (packet[5] << 16) + (packet[6] <= width of type

    data[1] = ((packet[4] << 24) + (packet[5] << 16) + (packet[6] <= width of type

    data[2] = ((packet[8] << 24) + (packet[9] << 16) + (packet[10] <= width of type

    data[2] = ((packet[8] << 24) + (packet[9] << 16) + (packet[10] <= width of type

    data[3] = ((packet[12] << 24) + (packet[13] << 16) + (packet[14] <= width of type

    data[3] = ((packet[12] << 24) + (packet[13] << 16) + (packet[14] <= width of type

    data[0] = ((packet[16] << 24) + (packet[17] << 16) + (packet[18] <= width of type

    data[0] = ((packet[16] << 24) + (packet[17] << 16) + (packet[18] <= width of type

    data[1] = ((packet[20] << 24) + (packet[21] << 16) + (packet[22] <= width of type

    data[1] = ((packet[20] << 24) + (packet[21] << 16) + (packet[22] <= width of type

    data[2] = ((packet[24] << 24) + (packet[25] << 16) + (packet[26] <= width of type

    data[2] = ((packet[24] << 24) + (packet[25] << 16) + (packet[26] <= 5000)

    ^

    exit status 1
    ‘currentMillis’ was not declared in this scope

    This report would have more information with
    “Show verbose output during compilation”
    option enabled in File -> Preferences.

YORUM YAP