DIY Self Balancing Robot Using Arduino

In this project, we are going to make a DIY Self Balancing Robot using Arduino. In this, we can learn the balancing concept and how to control the motors. Its working is very simple.

Here we use the MPU6050 accelerometer which interfaces with Arduino and sends some analog signals to the Arduino which could be x-axis, y-axis, and z-axis.

So lets get started to build…

Circuit Diagram

Self Balancing Robot

Components Required

  • Arduino
  • Geared DC motors
  • L298N Motor Driver Module
  • MPU6050 Accelerometer
  • Two Wheels
  • 9V Battery
  • Connecting Wires
  • Chassis

About Parts

Microcontroller

The microcontroller that I have used here is Arduino NANO because it is simple to use. You can also use an Arduino UNO or Arduino mini but I would recommend you stick with NANO since it is small than a UNO, so it can easily fit in a small chassis.

Motors

The best choice of motor that you can use for a Self Balancing Robot, without a doubt will be Stepper motor. But To keep things simple I have used a DC geared motor. Yes, it is not mandatory to have a stepper motor; the bot works fine with these cheap yellow-colored DC geared motors as well.

Motor Driver

If you have selected the DC geared motors then you can either use the L298N driver module or an L293D driver module. Both are similar in work. The Difference is that L298N is more powerful than L293D. But in that case, we can use any of them.

Wheels

I had a tough time figuring out that the problem was with my wheels. So make sure your wheels have a good grip over the floor you are using. Watch closely, your grip should never allow your wheels to skit on the floor.

Accelerometer

The best choice of Accelerometer and Gyroscope for your robot will be the MPU6050.

Battery

We need a battery that is as light as possible and the operating voltage should be more than 5V so that we can power our Arduino directly without a boost module. So the ideal choice will be a 9V battery.

Chassis

Another place where you should not compromise is with your robot’s chassis. You can use cardboard, wood, plastic anything that you are good with the self balancing robot. But, just make sure the chassis is sturdy and should not wiggle when the robot is trying to balance. I have designed my own chassis with cardboard.

Circuit Connection

Making the connections for this Self Balancing Robot using Arduino is pretty simple. We just have to interface the MPU6050 accelerometer with Arduino and connect the motors through the Motor Driver Module. The whole set-up is powered by the 9V battery.

The Arduino and the L298N Motor Driver Module are directly powered through the Vin pin and the 12V terminal respectively. The on-board regulator on the Arduino board will convert the input 9V to 5V and the ATmega IC and MPU6050 will be powered by it. The DC motors can run from voltage 5V to 12V. But we will be connecting the 9V positive wire from battery to 12V input terminal of Motor Driver Module. This will make the motors operate with 9V.

The following connection chart of the self balancing robot will list how the MPU6050 and L298N Motor Driver Module is connected with Arduino.

  • Connect MPU6050 Vcc pin to +5V pin of Arduino Nano
  • Connect MPU6050 Ground pin to GND pin of Arduino Nano
  • Connect MPU6050 SCL pin to A5 pin of Arduino Nano
  • Connect MPU6050 SDA pin to A4 pin of Arduino Nano
  • Connect MPU6050 INT pin to D2 pin of Arduino Nano
  • Connect L298N IN1 pin to D6 pin of Arduino Nano
  • Connect L298N IN2 pin to D9 pin of Arduino Nano
  • Connect L298N IN3 pin to D10 pin of Arduino Nano
  • Connect L298N IN4 pin to D11 pin of Arduino Nano

Working of Self Balancing Robot

In this self balancing robot project, once you are ready with the hardware, you can upload the code to your Arduino board. Make sure the connections are proper since we are using a 9V battery, extreme caution is needed. So double-check for short circuits and ensure that the terminals won’t come into contact even if your robot experiences some small impacts. Power up your module and open your serial monitor, if your Arduino could communicate with MPU6050 successfully and if everything is working as expected you should see the following screen. If the robot is perfectly balanced, the value of output will be 0. The input value is the current value from the MPU6050 sensor.

During the initial stages of PID, I recommend leaving your Arduino cable connected to the robot, so that you can easily monitor the values of input and output and also it will be easy to correct and upload your program for Kp, Ki and Kd values.

Hope this helps to build your own self balancing robot.

Arduino Code

For establishing this code of self balancing robot, we need two libraries. The library is developed by Br3ttb and Jrowberg respectively. Before proceeding download their libraries form the following link and add them to your Arduino lib directory.

Arduino-PID-Library

MPU6050 Library

Next for the Arduino code for the self balancing robot

//Electro Gadget - circuitdiagrams.in

#include "I2Cdev.h"
#include <PID_v1.h> //From https://github.com/br3ttb/Arduino-PID-Library/blob/master/PID_v1.h
#include "MPU6050_6Axis_MotionApps20.h" //https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050

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

/*****Tune these 4 values for your ROBOT*****/
double setpoint= 176; //set the value when the robot is perpendicular to ground using serial monitor. 
//Read the project documentation on circuitdiagrams.in to learn how to set these values
double Kp = 21; //Set this first
double Kd = 0.8; //Set this secound
double Ki = 140; //Set this third 
/******End of values setting*********/

double input, output;
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);

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

void setup() {
  Serial.begin(115200);

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

     // verify connection
    Serial.println(F("Testing device connections..."));
    Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

    // load and configure the DMP
    devStatus = mpu.dmpInitialize();

    
    // supply your own gyro offsets here, scaled for min sensitivity
    mpu.setXGyroOffset(220);
    mpu.setYGyroOffset(76);
    mpu.setZGyroOffset(-85);
    mpu.setZAccelOffset(1688); 

      // 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(")"));
    }

//Initialise the Motor outpu pins
    pinMode (6, OUTPUT);
    pinMode (9, OUTPUT);
    pinMode (10, OUTPUT);
    pinMode (11, OUTPUT);

//By default turn off both the motors
    analogWrite(6,LOW);
    analogWrite(9,LOW);
    analogWrite(10,LOW);
    analogWrite(11,LOW);
}

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();   
        
        //Print the value of Input and Output on serial monitor to check how it is working.
        Serial.print(input); Serial.print(" =>"); Serial.println(output);
               
        if (input>150 && input<200){//If the Bot is falling 
          
        if (output>0) //Falling towards front 
        Forward(); //Rotate the wheels forward 
        else if (output<0) //Falling towards back
        Reverse(); //Rotate the wheels backward 
        }
        else //If Robot not falling
        Stop(); //Hold the wheels still
        
    }

    // 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); //get value for q
        mpu.dmpGetGravity(&gravity, &q); //get value for gravity
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); //get value for ypr

        input = ypr[1] * 180/M_PI + 180;

   }
}

void Forward() //Code to rotate the wheel forward 
{
    analogWrite(6,output);
    analogWrite(9,0);
    analogWrite(10,output);
    analogWrite(11,0);
    Serial.print("F"); //Debugging information 
}

void Reverse() //Code to rotate the wheel Backward  
{
    analogWrite(6,0);
    analogWrite(9,output*-1);
    analogWrite(10,0);
    analogWrite(11,output*-1); 
    Serial.print("R");
}

void Stop() //Code to stop both the wheels
{
    analogWrite(6,0);
    analogWrite(9,0);
    analogWrite(10,0);
    analogWrite(11,0); 
    Serial.print("S");
}

Visit our site for more awsome tutorial Electro Gadget

Leave a comment