DIY Self Balancing Robot using Arduino

DIY-Self-Balancing-Robot-using-Arduino

After being inspired by RYNO motors and other self balancing scooters from Segway, I always wanted to build something similar. Thinking for while, I decided to build a Self Balancing Robot using Arduino. This way I would be able to grasp the underlying concept behind all these scooters and also learn how PID algorithm works.

Once I started building, I realised that this bot is a bit of a challenge to build. There are so many options to select from and hence the confusions start right form selecting the motors and remains till tuning PID values. And there are so many things to consider like type of battery, position of battery, wheel grip, type of motor driver, maintaining the CoG (Centre of gravity) and much more.

But let me break it to you, once you build it you will agree that it’s not as hard as it sounds to be. So let’s face it, in this tutorial I will document my experience in building the self balancing robot. You might be an absolute beginner who is just getting started or might have landed up here after a long frustration of not getting your bot to work. This place aims to be your final destination. So let’s get started……

Selecting the Parts for your Bot

Before I tell you all the options for building the bot let me list the items that I have used in this project

  • Arduino UNO
  • Geared DC motors (Yellow coloured) – 2Nos
  • L298N Motor Driver Module
  • MPU6050
  • A pair of wheels
  • 7.4V Li-ion Battery
  • Connecting wires
  • 3D Printed Body

Controller:  The controller that I have used here is Arduino UNO, why because it is simply easy to use. You can also use a Arduino Nano or Arduino mini but I would recommend you to stick with UNO since we can program it directly without any external hardware.

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 gear motor. Yes it is not mandatory to have a stepper; the bot works fine with these cheap commonly available yellow coloured DC gear motors as well.

Motor Driver: If you have selected the DC gear motors like mine then you can either use the L298N driver module like me, or even a L293D should work just fine. Learn more about controlling DC motor using L293D and Arduino.

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

Accelerometer and Gyroscope: The best choice of Accelerometer and Gyroscope for your bot will be the MPU6050. So do not attempt to build one with a normal Accelerometer like ADXL345 or something like that, it just won’t work. You will know why at the end of this article. You can also check our dedicated article on using MPU6050 with Arduino.

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 7.4V Li-polymer battery. Here, since I had a 7.4V Li-ion battery readily available I have used it. But remember a Li-po is advantageous than Li-ion.

Chassis:  Another place where you should not compromise is with your bots chassis. You can use cardboard, wood, plastic anything that you are good with. But, just make sure the chassis is sturdy and should not wiggle when the bot is trying to balance. I have designed by own chassis on Solidworks inferring from the other bots and 3D printed it. If you have a printer then you can also print the design, the design files will be attached in the upcoming heading.

3D Printing and Assembling the Bot

If you have decided to 3D print the same chassis that I am using to build my bot, then the STL files can be downloaded from thingiverse. I have also added the design files along with it so you can also modify it as per your personnel preferences.

The parts have no overhanging structures so you can easily print them without any supports and an infill of 25% will work fine. The designs are pretty plain and any basic printer should be able to handle it with ease. I used the Cura software to slice the model and printed using my Tevo Tarantula, the setting are shown below.

MANUAL~1

You would have to print the body part as well as four motor mounting parts. The assembling is pretty straight forward; use 3mm nuts and bolts to secure the motor and boards in place. After assembling it should look something like this shown in the picture below.

Assembled-DIY-Self-Balancing-Robot-using-Arduino

The actual design was planned with the L298N drive module in the bottom rack the Arduino and battery on top of it as shown above. If you are following the same order you can directly screw the board trough the holes provided and use a wire tag for the Li-po battery. This arrangement should also work, except for the super plain wheels which I had to change later.

In my bot I have swapped the position of battery and Arduino UNO board for ease of programming and also had to introduce a perf board for completing the connections. So my bot did not look as I planned in initial stage. After completing the wiring programming testing and everything, my bot finally looks like this

Circuit-Hardware-for-DIY-Self-Balancing-Robot-using-Arduino

Circuit Diagram

Making the connections for this Arduino based Self balancing Robot is pretty simple. We just have to interface the MPU6050 with Arduino and connect the motors though the Motor driver module. The whole set-up is powered by the 7.4V li-ion battery.  The circuit diagram for the same is shown below.

Circuit-Diagram-for-DIY-Self-Balancing-Robot-using-Arduino

The Arduino and the L298N Motor driver module is directly powered through the Vin pin and the 12V terminal respectively. The on-board regulator on the Arduino board will convert the input 7.4V 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 7.4V positive wire from battery to 12V input terminal of motor driver module. This will make the motors operate with 7.4V. The following table will list how the MPU6050 and L298N motor driver module is connected with Arduino.

Component Pin Arduino Pin
MPU6050
Vcc +5V
Ground Gnd
SCL A5
SDA A4
INT D2
L298N
IN1 D6
IN2 D9
IN3 D10
IN4 D11

The MPU6050 communicates with Arduino through I2C interface, so we use the SPI pins A4 and A5 of Arduino. The DC motors are connected to PWM pins D6,D9 D10 and D11 respectively. We need to connect them to PWM pins because we will be controlling the speed of the DC motor by varying the duty cycle of the PWM signals. If you are not familiar with these two components then it is recommended to read through MPU6050 Interfacing and L298N Motor driver tutorial.

Programming the Self Balancing Robot

Now we have to program our Arduino UNO board to balance the robot. This is where all the magic happens; the concept behind it is simple. We have to check if the bot is leaning towards the front or towards the back using the MPU6050 and then if it’s leaning towards the front we have to rotate the wheels in forward direction and if it is leaning towards the back we have to rotate the wheels in the reverse direction.

At the same time we also have to control the speed at which wheels are rotating, if the bot is slightly disoriented from centre position the wheels rotate slowly and the speed increase as it gets more away from the centre position. To achieve this logic we use the PID algorithm, which has the centre position as set-point and the level of disorientation as the output.

To know the current position of the bot we use the MPU6050, which is a 6-axis accelerometer and gyroscope sensorcombined. In order to get a reliable value of position from the sensor we need to use the value of both accelerometer and gyroscope, because the values from accelerometer has noise problems and the values from gyroscope tends to drift with time. So we have to combine both and get the value of yaw pitch and roll of our robot, of which we will use only the value of yaw.

Sounds bit of head reeling right? But worry not, thanks to the Arduino community we have readily available libraries that can perform the PID calculation and also get the value of yaw from the MPU6050. The library is developed by br3ttb and jrowberg respectively.

Now, that we have the libraries added to our Arduino IDE. Let’s start programming for our Self balancing Robot. Like always the complete code for the Project is given at the end of this page, here I am just explaining the most important snippets in the code. A told earlier the code is build on top of the MPU6050 example code we are just going to optimize the code for our purpose and add the PID and control technique for our self balancing robot.

First we include the libraries that are required for this program to work. They include the in-built I2C library, PID Library and MPU6050 Library that we just downloaded.

#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

Then we declare the variables that are required to get the data from the MPU6050 sensor. We read both the gravity vector and quaternion values and then compute the yaw pitch and roll value of the bot. The float array ypr[3] will hold the final result.

// 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

Next comes the very important segment of the code, and this is where you will be spending a long time in tuning for the right set of values. If you robot is built with a very good centre of gravity and the components are symmetrically arranged (which in most cases is not) then the value of your set-point will be 180. Else connect your bot to Arduino serial monitor and tilt it till you find a good balancing position, read the value displayed on serial monitor and this is your set point value. The value of Kp, Kd and Ki has to be tuned according to your bot. No two identical bots will have the same values of Kp, Kd and Ki so there is no escaping from it. Watch the video at the end of this page to get an idea of how to adjust these values.

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

In the next line we initialise the PID algorithm by passing the input variables input, output, set point, Kp, Ki and Kd. Out of these we have already set the values of set-point Kp,Ki and Kd in the above snippet of code. The value of input will be the current value of yaw that is read from the MPU6050 sensor and the value of output will be the value that is calculated by the PID algorithm.  So basically the PID algorithm will give us an output value which should be used to correct the Input value to being it close to the set point.

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

Inside the void setup function we initialise the MPU6050 by configuring the DMP (Digital Motion Processor). This will help us in combining the Accelerometer data with Gyroscope data and provide a reliable value of Yaw, Pitch and Roll. We will not go much deep into this since it will be far beyond the topic. Anyhow one segment of code that you have to look up in the setup function is the gyro offset values. Each MPU6050 sensor has its own values of offsets you can use this Arduino sketch to calculate the offset value of you sensor and update the following lines accordingly in your program.

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

We also have to initialise the Digital PWM pins that we are using to connect our motors to. In our case it is D6, D9, D10 and D11. So we initialise these pins as output pins make them LOW by default.

//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);

Inside the main loop function we check if the data from the MPU6050 is ready to be read. If yes then we use it to compute the PID value and then display the input and output value of PID on serial monitor just to check how the PID is responding. Then based on the value of output we decide if the bot has to move forward or backward or stand still.

Since we assume that the MPU6050 will return 180 when the bot is upright. We will get correction values positive when the bot is falling towards front and we will get values in negative if the bot is falling towards back. So we check for this condition and call the appropriate functions to move the bot forward or back ward.

< 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 Bot not falling
Stop(); //Hold the wheels still
}

The PID output variable also decides how fast the motor has to be rotated. If the bot is just about to fall then we make minor correction by rotating the wheel slowly. If these minor correction dint work and still if the bot is falling down we increase the speed of the motor. The value of how fast the wheels rotate will be decided by the PI algorithm. Note that for the Reverse function we have multiplied the value of output with -1 so that we can convert the negative value to positive.

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”);
}

Working of Arduino Self Balancing Robot

DIY-Self-Balancing-Robot-in-action

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 Li-ion battery extreme caution is needed. So double check for short circuits and ensure that the terminals won’t come into contact even if your bot 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.

COMMUN~1

Here we see the input and output values of the PID algorithm in the format input => output. If the bot is perfectly balance the value of output will be 0. The input value is the current value from the MPU6050 sensor. The alphabet “F” represents that the bot is moving in forward and “R” represents that the bot in reverse.

During the initial stages of PID I recommend leaving your Arduino cable connected to the bot 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. The video below shows the complete working of the bot and also shows how to correct your PID values.

Hope this helps to build your own self balancing robot if you have any problem in getting it to work, then leave your questions in the comment section below or use the forums for more technical questions.

Code:

/*Arduino Self Balancing Robot
* Code by: B.Aswinth Raj
* Build on top of Lib: https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050
* Website: circuitdigest.com
*/

#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 BOT*********/
double setpoint= 176; //set the value when the bot is perpendicular to ground using serial monitor.
//Read the project documentation on circuitdigest.com to learn how to set these values
double Kp = 21; //Set this first
double Kd = 0.8; //Set this secound
double Ki = 140; //Finally set this
/******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 Bot 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”);
}

Source: DIY Self Balancing Robot using Arduino


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top