How to Build a Self-Balancing Bot Using Arduino and MPU6050
How to Build a Self-Balancing Bot Using Arduino and MPU6050: My Journey to Defying Gravity
For years, the concept of the inverted pendulum, a naturally unstable system defying gravity has fascinated me. It's the perfect challenge to truly master real-time control systems. This project documents my journey building a powerful, 12V two-wheeled balancing robot using the classic combination of the Arduino Nano and the highly precise MPU6050 IMU. The heart of this stability lies in a carefully tuned PID control loop. I'll walk you through the hardware, the physics, the code, and most importantly, the painstaking process of tuning the controller that finally brought my robot to life!
Hardware Note: For the mechanical components (chassis, motors, wheels, screws, screwdriver, Allen keys, clamps, spacers and frame), I used the Combo Kit from Tomson Electronics. This pre-selected kit saved a lot of time on mechanical sourcing and ensured compatibility.
🛠️ The Hardware: What's Under the Hood
The shift to a 12V power system was crucial for giving the motors the necessary torque and responsiveness to counteract a sudden lean. Here is the list of components I used for the control system, and the mechanical components are generally covered by a standard kit (like the one I used from tomsonelectronics.com).
| Component | Specification | My Personal Experience |
|---|---|---|
| Microcontroller | Arduino Uno or Nano (ATmega328P) | I chose the Nano for its small footprint on the chassis. |
| IMU Sensor | MPU6050 GY-521 Module | Essential for its DMP as it does the heavy angle calculations so the Arduino can focus on PID. |
| Motor Driver | L298N Dual H-Bridge | A reliable, though large, driver that easily handles the 12V motors. |
| Power System | 12V DC Battery Pack | Don't skimp here. The motors draw serious current when correcting. I chose a 3S Li-Ion Battery 18650 pack. |
| Motors | Two 12V DC Gear Motors | High torque is more important than high speed for balancing. I have used a 330RPM gear motor |
| Self-Balancing Robot Kit | Chassis, Wheels, Hardware | I used the Self-Balancing Robot Body Kit for the robot body and mechanical structure. |
📐 Calibrating the MPU 6050
While most MPUs are having near zero offsets, some might have significant values which could affect the system performance. So, I used the Example code from the MPU6050 library to find the offset values and add it to the Arduino sketch. Be aware that measuring the values from the MPU can take some time.

🔌 My Setup: A Precise Pin Map is Key
When dealing with fast control loops, every millisecond counts. That's why the dedicated Interrupt Pin (D2) connection from the MPU6050 is so vital, it tells the Arduino exactly when new, processed angle data is ready.
My Wiring Pinout Table
| Component | Pin | Arduino Pin | Function |
|---|---|---|---|
| Motor Driver | ENA (Left PWM) | D5 | Left Speed Control (PWM) |
| Motor Driver | ENB (Right PWM) | D10 | Right Speed Control (PWM) |
| Motor Driver | Direction Pins | D6, D7, D12, D11 | Motor Direction Control |
| IMU Sensor | I2C Pins (SCL/SDA) | A5, A4 | Standard communication for setup and raw data fallback |
| IMU Sensor | INT | D2 | The most critical connection for the DMP's high-speed data. |

📐 The Secret Sauce: Tuning My PID Controller
This was, by far, the hardest and most rewarding part of the build. The Proportional-Integral-Derivative (PID) controller is the mathematical brain that takes the angle error and converts it into a motor command.
My process involved a lot of trial and error (and a few crashes):
- Finding Kp (The Force): I started with Ki and Kd at zero and slowly increased Kp. Initially, the robot would just fall, but eventually, it started trying to balance, oscillating wildly. My sweet spot before chaos was around 23.
- Adding Kd (The Brakes): Kd is the dampener. I introduced Kd at 0.8 to tame the Kp oscillations. This instantly stabilized the system. When Kd is right, the robot stops overshooting and settles quickly.
- Taming Drift with Ki (The Long-Term Corrector): The robot still had a slight, persistent lean. I added a significant Ki of 100 to eliminate this steady-state error. Be warned: too much Ki leads to slow, mysterious oscillations.
My Final Stable Parameters
| Parameter | Value | Role in the System |
|---|---|---|
| Setpoint (S) | 180.67 degrees | My robot's specific upright angle. You must calibrate yours. |
| Proportional Gain (Kp) | 23 | Provides instant, powerful correction. |
| Derivative Gain (Kd) | 0.8 | Damps oscillations and prevents overshooting. |
| Integral Gain (Ki) | 100 | Eliminates long-term lean (steady-state error). |
| Sample Time | 10 ms | Frequency of PID computation. |
💻 The Final Firmware (Source Code)
The code is the culmination of countless hours of tuning and testing. It features the essential MPU6050 DMP setup, my final offsets, and the core PID logic running at a high frequency.
To view the complete and final Arduino sketch (self_balancing_bot.ino) with the PID and MPU logic, please visit the official project repository on GitHub.
In the code, you will find detailed implementations of:
- The DMP initialization and accelerometer/gyro calibration offsets.
- The interrupt-driven angle acquisition from the MPU6050 roll axis.
- The motor control function (
setMotorSpeed) that translates the PID output (-255 to 255) into the necessary PWM and direction signals for the L298N driver.
A Final Note on Tuning
If you use different motors, chassis, or battery weight, you must re-tune the Kp, Kd and Ki values. Start with my values and adjust them slowly until you achieve your robot's perfect, steady balance. Happy Building 🤖