You are on page 1of 12

Self-Balancing Boxing Robot

Parts List:
 2x DC Motor with Wheels
 2x Micro Servo Motor 9G SG92R
 2x IR Proximity Sensor
 2x Ultrasonic Sensor HC-SR04
 1x Triple Axis Accelerometer and Gyro Breakout MPU6050
 Wood Framework
 Shrinking Tube
 Wood glue
 Recycled/Reused parts (cartons, newspapers, folder, paper cups, multipurpose
glue, varnish, paint, styro foam)
 240p and 1000 sandpaper
 L293D IC
 Presensitized PCB
 Ferric Chloride
 Connecting Wires
 Laptop
 Arduino UNO
 Double sided foam tape
 Electrical tape
 9V battery

Step 1: Short Introduction


If you are holding a stick vertically on your palm, it starts falling on one side and you
move your hand to that direction to maintain its balance. This is done to maintain the
center of gravity within the vertical line. The top and bottom of the stick are the limit
points. If COG lies outside the line joining this line then it will lose balance. For a robot
to maintain its balance, PID must be controlled. For the robot be able to punch, an
ultrasonic sensor is used to sense whether an opponent is on the reach. 

The self-balancing robot is similar to an upside down pendulum. Unlike a normal


pendulum which keeps on swinging once given a nudge, this inverted pendulum cannot
stay balanced on its own. It will simply fall over. Then how do we balance it? Consider
balancing a broomstick on our index finger which is a classic example of balancing an
inverted pendulum. We move our finger in the direction in which the stick is falling.
Similar is the case with a self-balancing robot, only that the robot will fall either forward
or backward. Just like how we balance a stick on our finger, we balance the robot by
driving its wheels in the direction in which it is falling. What we are trying to do here is to
keep the center of gravity of the robot exactly above the pivot point. To drive the motors
we need some information on the state of the robot. We need to know the direction in
which the robot is falling, how much the robot has tilted and the speed with which it is
falling. All these information can be deduced from the readings obtained from
MPU6050. We combine all these inputs and generate a signal which drives the motors
and keeps the robot balanced.

Step 2: Building
We first made a prototype circuit using a breadboard. For the motor driver, we have
used L293D IC. We designed and fabricated our circuit in EagleCAD and print it out on
a presensitized PCB afterwards. Eventually, we drilled and soldered all the components
into our PCB.
Step 3: Making the Robot Frame
In making our frame, a thin type of wood is used. Everything that was made on the
chassis is recycled such as newspapers, used folder and paper cups. Hence, no money
was spent for the framework aside from the adhesives.The materials were bind together
using wood glue and/or multipurpose glue.
Step 4: Adding all the Electronics
We first test all the materials to be used if they are all working (like motors and sensors).
All the circuits, wires and components used were later attached to the framework.
Step 5: PID Tuning
Tuning the PID is the most important factor in balancing the robot. The increase P until
your bot starts oscillating. It may oscillate wildly or even swiftly. It should be between
these two. Increase D to reduce the oscillations. Increase I to increase its response.
Higher is the I value, quicker is the response time when it's out of balance. Larger the I
value, less is the inclination limits.

Our PID values:


Kp: _60__
Ki: _270_
Kd: _2.2_

Step 6: Punch!
We have designed the punching mechanism of our robot which is upper cut. Initially, the
arms of the bot are both punching upward once it sensed an opponent. The arms are
made up of recycled carton while the fist is made from a reused styro foam. The fist was
painted yellow. The arms are therefore attached to the servo motors on each side of the
robot’s body.
Step 7: Source Code

#include <PID_v1.h> // orientation/motion vars


#include <LMotorController.h> Quaternion q; // [w, x, y, z]
quaternion container
#include "I2Cdev.h"
VectorFloat gravity; // [x, y,
#include z] gravity vector
"MPU6050_6Axis_MotionApps20.h"
float ypr[3]; // [yaw, pitch,
roll] yaw/pitch/roll container
#if I2CDEV_IMPLEMENTATION == and gravity vector
I2CDEV_ARDUINO_WIRE
#include "Wire.h" //PID
#endif double originalSetpoint =
172.50;

#define MIN_ABS_SPEED 30 double setpoint =


originalSetpoint;
double movingAngleOffset = 0.1;
MPU6050 mpu;
double input, output;

// MPU control/status vars


//adjust these values to fit
bool dmpReady = false; // set your own design
true if DMP init was successful
double Kp = 60;
uint8_t mpuIntStatus; // holds
actual interrupt status byte double Kd = 2.2;
from MPU
uint8_t devStatus; // return double Ki = 270;
status after each device
operation (0 = success, !0 = PID pid(&input, &output,
error) &setpoint, Kp, Ki, Kd, DIRECT);
uint16_t packetSize; // expected
DMP packet size (default is 42
bytes) double motorSpeedFactorLeft =
0.6;
uint16_t fifoCount; // count of
all bytes currently in FIFO double motorSpeedFactorRight =
0.5;
uint8_t fifoBuffer[64]; // FIFO
storage buffer
//MOTOR CONTROLLER
int ENA = 5;
int IN1 = 6; mpu.initialize();
int IN2 = 9;
int IN3 = 11; devStatus =
mpu.dmpInitialize();
int IN4 = 3;
int ENB = 10;
// supply your own gyro offsets
LMotorController here, scaled for min sensitivity
motorController(ENA, IN1, IN2,
ENB, IN3, IN4, mpu.setXGyroOffset(220);
motorSpeedFactorLeft,
motorSpeedFactorRight); mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);

volatile bool mpuInterrupt = mpu.setZAccelOffset(1788); //


false; // indicates whether MPU 1688 factory default for my test
interrupt pin has gone high chip

void dmpDataReady()
{ // make sure it worked (returns
0 if so)
mpuInterrupt = true;
if (devStatus == 0)
}
{
// turn on the DMP, now that
it's ready
void setup() mpu.setDMPEnabled(true);
{
// join I2C bus (I2Cdev library // enable Arduino interrupt
doesn't do this automatically) detection
#if I2CDEV_IMPLEMENTATION == attachInterrupt(0,
I2CDEV_ARDUINO_WIRE dmpDataReady, RISING);
Wire.begin(); mpuIntStatus =
mpu.getIntStatus();
TWBR = 24; // 400kHz I2C clock
(200kHz if CPU is 8MHz)
#elif I2CDEV_IMPLEMENTATION == // set our DMP Ready flag so
I2CDEV_BUILTIN_FASTWIRE the main loop() function knows
it's okay to use it
Fastwire::setup(400, true);
dmpReady = true;
#endif
// get expected DMP packet size // wait for MPU interrupt or
for later comparison extra packet(s) available
packetSize = while (!mpuInterrupt &&
mpu.dmpGetFIFOPacketSize(); fifoCount < packetSize)
{

//setup PID //no mpu data - performing PID


calculations and output to
pid.SetMode(AUTOMATIC); motors
pid.SetSampleTime(10); pid.Compute();
pid.SetOutputLimits(-255, 255); motorController.move(output,
} MIN_ABS_SPEED);

else
{ }

// ERROR!
// 1 = initial memory load // reset interrupt flag and get
failed INT_STATUS byte

// 2 = DMP configuration mpuInterrupt = false;


updates failed mpuIntStatus =
// (if it's going to break, mpu.getIntStatus();
usually the code will be 1)
Serial.print(F("DMP // get current FIFO count
Initialization failed (code "));
fifoCount = mpu.getFIFOCount();
Serial.print(devStatus);
Serial.println(F(")"));
// check for overflow (this
} should never happen unless our
} code is too inefficient)
if ((mpuIntStatus & 0x10) ||
fifoCount == 1024)
{
void loop()
// reset so we can continue
{ cleanly
// if programming failed, don't mpu.resetFIFO();
try to do anything
Serial.println(F("FIFO
if (!dmpReady) return; 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);
mpu.dmpGetGravity(&gravity,
&q);
mpu.dmpGetYawPitchRoll(ypr, &q,
&gravity);
input = ypr[1] * 180/M_PI +
180;
}
}
Final Output

You might also like