Cirkit Designer LogoCirkit DesignerYour all-in-one circuit design IDE Launch Cirkit Designer App

Arduino UNO Controlled Self-Balancing Robot with MPU-6050 and L298N Motor Driver

View and Edit Project In Cirkit DesignerImage of Arduino UNO Controlled Self-Balancing Robot with MPU-6050 and L298N Motor Driver

Circuit Documentation

Summary

This circuit is designed to control a pair of hobby gearmotors using an Arduino UNO microcontroller and an L298N DC motor driver. The system is powered by two 18650 Li-ion batteries connected through a rocker switch. An MPU-6050 sensor is included to provide gyroscope and accelerometer data to the Arduino, which uses this data to adjust the motor speeds through a PID control loop. The purpose of this circuit is likely to create a self-balancing or position-controlled mechanism, such as a robot or a vehicle.

Component List

Arduino UNO

L298N DC Motor Driver

Rocker Switch

Hobby Gearmotor with 48:1 Gearbox (x2)

18650 Li-ion Battery x 2

MPU-6050

Wiring Details

Arduino UNO

L298N DC Motor Driver

Rocker Switch

Hobby Gearmotor with 48:1 Gearbox

18650 Li-ion Battery x 2

MPU-6050

Documented Code

#include <Arduino.h>
#include "Wire.h"
#include <MPU6050_light.h>

// Motor A control pins
#define MOTOR_A_IN1  4
#define MOTOR_A_IN2  5
#define MOTOR_A_ENA  10

// Motor B control pins
#define MOTOR_B_IN1  6
#define MOTOR_B_IN2  7
#define MOTOR_B_ENA  9

// Initialize MPU6050 sensor
MPU6050 mpu(Wire);
unsigned long timer = 0;

// Gyroscope readings
float angle_x, angle_y, angle_z;

// PID controller variables
float Kp = 40.0;    // Proportional gain
float Kd = 1.0;     // Derivative gain
float Ki = 0.2;     // Integral gain
float target = 0.0; // Desired angle
float error, integral, derivative, last_error;

float prev_steer;

// Function prototypes
float steerPid(float error);
void steerToMotorsSpeed(float steer, float &l_wheel_speed, float &r_wheel_speed);
void move(int val_l, int val_r);

// Speed variables
float min_speed = 80.0;
float max_speed = 150.0;
float mid_speed = (max_speed - min_speed) / 2.0;
float l_wheel_speed = 150.0, r_wheel_speed = 150.0;
float p_l, p_r;
float max_steer_pid_output = 2000.0;

void setup() {
  // Initialize serial communication
  Serial.begin(9600);
  // Initialize I2C communication for MPU6050
  Wire.begin();
  
  // Start MPU6050 sensor
  byte status = mpu.begin();
  Serial.print(F("MPU6050 status: "));
  Serial.println(status);
  // Wait if MPU6050 is not connected
  while (status != 0) {
    // Stop everything if unable to connect to MPU6050
  }
  
  Serial.println(F("Calculating offsets, do not move MPU6050"));
  delay(1000);
  // Calculate gyro and accelerometer offsets
  mpu.calcOffsets();
  Serial.println("Done!\n");

  // Gradually increase speed to start moving smoothly
  float cur_speed = 100.0;
  while (cur_speed < max_speed) {
    String log = "Moving with " + String(cur_speed) + " speed.";
    Serial.println(log);

    move(cur_speed, cur_speed);

    cur_speed += 20.0;
    delay(1000);
  }
}

void loop() {
  // Update sensor readings
  mpu.update();

  // Execute every 10 milliseconds
  if ((millis() - timer) > 10) {
    // Get current angles from MPU6050
    angle_x = mpu.getAngleX();
    angle_y = mpu.getAngleY();
    angle_z = mpu.getAngleZ();

    // Log current angles
    String log = "Angles - X: " + String(angle_x) + ", Y: " + String(angle_y) + ", Z: " + String(angle_z);
    Serial.println(log);

    // Calculate steering adjustment using PID controller
    float new_steer = steerPid(angle_z - target);

    // Adjust motor speeds based on steering value
    steerToMotorsSpeed(new_steer, l_wheel_speed, r_wheel_speed);
    
    // Move motors with adjusted speeds
    move(l_wheel_speed, r_wheel_speed);

    // Log previous and current steering values
    String log1 = "Prev Steer value: " + String(prev_steer) + " New Steer value: " + String(new_steer);
    Serial.println(log1);

    // Log previous and current motor speeds
    String log3 = "Prev Speed value: L" + String(p_l) + ", R " + String(p_r) +
                  " New Speed value: L" + String(l_wheel_speed) + ", R " + String(r_wheel_speed);
    Serial.println(log3);

    // Update timer and previous values
    timer = millis();  
    prev_steer = new_steer;
    p_l = l_wheel_speed;
    p_r = r_wheel_speed;
  }
}

// PID controller function
// Calculates the steering adjustment based on error
float steerPid(float error) {
  derivative = error - last_error;  // Calculate derivative
  last_error = error;               // Update last error
  integral += error;                // Accumulate integral

  // Return PID output
  return (error * Kp) + (derivative * Kd) + (integral * Ki);
}

float speed_adjust;
float normalized_steer;

// Maps the steering value from PID to motor speeds
void steerToMotorsSpeed(float steer, float &l_wheel_speed, float &r_wheel_speed) {
  // Normalize steer value to range between 0 and 1
  normalized_steer = abs(steer) / max_steer_pid_output;
  speed_adjust = mid_speed * normalized_steer;

  if (steer > 0) {
    // Steering to the left
    l_wheel_speed = constrain(l_wheel_speed + speed_adjust, min_speed, max_speed);
    r_wheel_speed = constrain(r_wheel_speed - speed_adjust, min_speed, max_speed);