Balancing Bot – Schematics

Our robot has 4 major components that can be seen in the images below.

1. The gyroscope, the crux of this project. This device reads the tilt of the robot and sends signals to the second component, the Arduino. It is mounted in the top right corner of the robot.

2. The Arduino which was required for this project because the chosen Gyroscope is not compatible with the A3BU. The Arduino communicates to a bridge which interprets the outputs and translates them to the motors.

3. The bridge serves as a middle man between the Arduino and the motors which balance the robot. Without this bridge, the Arduino would be unable to move the motors.

4. The A3BU. The A3BU takes additional outputs from the Arduino to display LED’s that correspond to the directions in which the robot is leaning. This helps test calibration of offsets and the monitor the system during operation.

 

Balancing Bot Code

Our project has reached its final stages. This is the final code for both the arduino and the A3BU. Together, these two programs control the robot balancing and LED switching.


LED CODE (A3BU):
#include
#include
#include

#define J2_PIN0 IOPORT_CREATE_PIN(PORTB, 0)
#define J2_PIN1 IOPORT_CREATE_PIN(PORTB, 1)

int loop_num;

int main (void)
{
struct pwm_config mypwm[4]; //For your PWM configuration –CKH

// Insert system clock initialization code here (sysclk_init()).
sysclk_init();

board_init();

/* Set up a PWM channel with 500 Hz frequency. Here we will output 4 different pulse trains on 4 pins of the A3BU Xplained.*/

pwm_init(&mypwm[0], PWM_TCC0, PWM_CH_A, 500);//this is SDA on J1 on the A3BU Xplained
pwm_init(&mypwm[1], PWM_TCC0, PWM_CH_B, 500);//On A3BU Xplained board, SCL on J1

ioport_enable_pin(ADC_CH0);
ioport_enable_pin(ADC_CH1);

while(1) {
if (ioport_get_pin_level(J2_PIN0) > 0){
//yellow
gpio_set_pin_high(LCD_BACKLIGHT_ENABLE_PIN);
pwm_start(&mypwm[1], 500);
pwm_start(&mypwm[0], 0);
}
else {
//red
gpio_set_pin_low(LCD_BACKLIGHT_ENABLE_PIN);
pwm_start(&mypwm[0], 500);
pwm_start(&mypwm[1], 0);
}
}
}

SELF BALANCING (ARDUINO UNO):
#include
#include
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"

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

#define MIN_ABS_SPEED 20

MPU6050 mpu;

int myLED_1 = 11;

// 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
double originalSetpoint = 173;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.1;
double input, output;

//adjust these values to fit your own design
double Kp = 100; //50
double Kd = 1.0; //1.4
double Ki = 50; //60
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);

double motorSpeedFactorLeft = -0.4;
double motorSpeedFactorRight = -0.4;
//MOTOR CONTROLLER
int ENA = 5;
int IN1 = 6;
int IN2 = 7;
int IN3 = 8;
int IN4 = 9;
int ENB = 10;
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, motorSpeedFactorLeft, motorSpeedFactorRight);

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

mpu.initialize();

devStatus = mpu.dmpInitialize();

// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(68);
mpu.setYGyroOffset(14);
mpu.setZGyroOffset(-22);
mpu.setZAccelOffset(1331); // 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
mpu.setDMPEnabled(true);

// enable Arduino interrupt detection
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();

// set our DMP Ready flag so the main loop() function knows it's okay to use it
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(")"));
}

pinMode(myLED_1, OUTPUT);
}

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 0) {
digitalWrite(myLED_1, HIGH);
}
else {
digitalWrite(myLED_1, LOW);
}

}

// 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 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);
input = ypr[1] * 180/M_PI + 180;
}
}

Balancing Bot with Gyroscope

Finally got our project moving. We are working to create a self balancing robot. This project is a perfect fit for the class as it lets us use code from previous labs and use technology we understand.

Self balancing features have become more and more necessary as robots begin to become commonplace. We hope that our design will be a good example to the class for what could find its way into robotics in the future.