Description:
For Project 2 I created a boxing bag that measures acceleration when it is hit. The bag is mobile so that it can be set up at different heights and angles. This allows it to be used for a variety of muay thai techniques including: jabs (front straight punch), crosses (back straight punch), hooks, upper cuts, round kicks, teeps (push kicks), knees, and elbows. I used the acceleration output to create a game that requires the player to hit the bag within a given range of a goal value. When the goal value is achieved the user scores a point and a new goal value is set. The range is determined by a difficulty setting. Easy has a high range (+/- 0.45g), moderate has a lower range (+/- 0.35g), and difficult has the lowest range (+/- 0.25g). I also made all the navigation for the game accessible though hits. This way if the player is wearing boxing gloves then they would not have to take them off in order to play multiple rounds of the game. When the user scores 10 points they win the game. The purpose of the game is to help the player learn control. Control is very important for muay thai because having good control while sparing/training can save your partner. Elbows and knees in particular can easily injure a person even if you hit them lightly. Additionally, this game can be used to practice the timing of your hits. Even professional fighters only hit about 3 consecutive strikes at full force. They throw many lighter hits in between the full force hits. This game helps the user practice both full force hits and lighter hits that they need to set up for full force hits.
Process:
I started by putting my circuit on a breadboard. This was fairly simple as the circuitry for my project is not very complicated. The SparkFun website had very helpful instructions for how to set up and use the triple axis accelerometer.
Next, I took the circuit off the breadboard and soldered it to protoboard. It was somewhat difficult to solder to the accelerometer becuase the pins are very close together.
Next I build the bag frame from wood and a spring door hinge. I used the ban saw and the drill to make the frame. I had to counter sink the holes in the bottom of the frame to hold the post onto the top. At this point I also determined how to angle the device so that it could be used for knees, upward elbows, and round kicks. I used two pipe brackets that can be placed on the top of the base when it need to lie flat and the bottom of the base when the bag needs to be angled down.
Then I mounted the accelerometer to the top of the bag frame. I drilled a small hole behind the accelerometer to make room for the pins.
Next, I began covering the frame. I used upholstery nails to attach the leather to the base. At this point I also started programing the Processing for the game. Having picked out all the fabric for the exterior I could now make a game that matched that exterior. I found the fist logo online then I changed the fill color to green, yellow, red, and none in illustrator. Other than those colors, I kept to red, white, and black so that the bag and the game would match.
Then, I started adding padding to the post. Unfortunately, this made the post two heavy for the door hinge spring to pull the post back up.
To solve this problem I added a spring to the front of the bag as well. This allowed the bag to hold a lot of padding and still spring back after it is hit.
Having solved the weight problem, I continued to pad the post with insulation pads until it could be punched at full force without being painful. I covered the padding with tape so that it could be easily removed and the Arduino and accelerometer could be accessed if need be.
Lastly, I sewed a custom cover for the bag complete with a zipper, elastic, and lettering. The zipper allows the cover to be easily removed. The elastic keeps the cover tight at the bottom of the bag so that it fits snugly. The lettering on the front provides the user with a hint as to which direction the bag has to be hit from. To keep the bottom of the base from scratching furniture, I put sticky felt pads on the bottom of the base. I also added a level select to my game and an instructions screen.
I put the bag in the Atlas Expo which was a really cool experience in addition to being useful testing for my boxing bag. The frame stood up very well to being hit fairly continuously for an hour and a half. However, the padding was too lose at the bottom and would fly off the post if it was hit too hard. I solved this problem by simply tying the bottom of the padding tight with some string. With the cover on the bag looked just the same but the padding stayed on the post.
Schematic:
Code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Name: Project02Arduino | |
//By: Claudia Escue | |
//Purpose: send the values from an accelerometer to a processing file | |
#include <Wire.h> // Must include Wire library for I2C | |
#include <SFE_MMA8452Q.h> // Includes the SFE_MMA8452Q library | |
MMA8452Q accel; // Default MMA8452Q object create. (Address = 0x1D) | |
//create variables for the x, y, and z acceleration | |
float xAcceleration; | |
float yAcceleration; | |
float zAcceleration; | |
char val; | |
void setup() { | |
Serial.begin(9600); | |
accel.init(); //initialize the accelerometer | |
} | |
void loop() { | |
//check if the accelerometer is available | |
if (accel.available()) { | |
accel.read(); // First, use accel.read() to read the new variables: | |
printCalculatedAccels(); | |
Serial.println(); // Print new line every time. | |
} | |
} | |
void printCalculatedAccels() { | |
if(Serial.available() > 0){ | |
char input = Serial.read(); | |
val = Serial.read(); | |
//display accelerations as "x, y, z" so that it can be read in processing | |
xAcceleration = accel.cx; | |
Serial.print(xAcceleration, 3); | |
Serial.print(","); | |
yAcceleration = accel.cy; | |
Serial.print(yAcceleration, 3); | |
Serial.print(","); | |
zAcceleration = accel.cz; | |
Serial.print(zAcceleration, 3); | |
delay(250); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Name: BTU Boxing | |
//By: Claudia Escue | |
//Purpose: * Creates a game that is navigated through with acceleration of a punching bag | |
// * Teaches control of strikes | |
//import serial library | |
import processing.serial.*; | |
//create a variable to control what screen is displayed | |
int start = 0; | |
//create a variable to hold the number of points scored | |
int points = 0; | |
//create a maximum value the goal to be set to | |
float goalMax = 4.000; | |
//create sensor values measured in units of g | |
float x; | |
float y; | |
float z; | |
//create a value to hold the total acceleration | |
float total; | |
//create and initialzies variables for the maximum hit, difficulty setting, and goal hit | |
float maxNum = 0; | |
float difficulty = .45; | |
float goal = random(0.500, goalMax); | |
//create images for the background and for the point display | |
PImage imgGreen; | |
PImage imgYellow; | |
PImage imgRed; | |
PImage imgWhite; | |
//create and instance of the serial libaray | |
Serial myPort; | |
//create an instance of a font | |
PFont f; | |
void setup() { | |
//set the size of the game screen | |
size(500, 500); | |
//my port is: /dev/tty.usbmodem1411 | |
String portName = "/dev/tty.usbmodem1411"; | |
myPort = new Serial(this, portName, 9600); | |
//create a font of size 16 Helvetica | |
f = createFont("Helvetica", 16, true); | |
//load images | |
imgGreen = loadImage("fistGreen.png"); | |
imgYellow = loadImage("fistYellow.png"); | |
imgRed = loadImage("fistRed.png"); | |
imgWhite = loadImage("fistWhite.png"); | |
} | |
void draw() { | |
//set the background color to black the the font to size 36 | |
background(#000000); | |
textFont(f,36); | |
//calculate the total acceleration | |
total = sqrt(sq(x) + sq(y) + sq(z)); | |
//determine whether the bag is speeding up or slowing down my comparing the total to the maxNum | |
maxNum = max(total, maxNum); | |
//call the score update function | |
ScoreUpdate(); | |
//determine the color of the image based on the goal and the size of the fist based on the number of points scored | |
imageMode(CENTER); //orrient the image based on the center point | |
if (goal <= 1.666) { | |
image(imgGreen, 250, 245, 110+(30*points), 110+(30*points)); //display the green fist (light hit) | |
} else if (goal > 1.666 && goal <= 3.333) { | |
image(imgYellow, 250, 245, 110+(30*points), 110+(30*points)); //display the yellow fist (medium hit) | |
} else { | |
image(imgRed, 250, 245, 110+(30*points), 110+(30*points)); //display the red fist (hard hit) | |
} | |
//set the text fill color to white, display the goal and hit texts, and the goal and acceleration to 1 decimal | |
fill(#ffffff); | |
String goalText = "Goal: " + nf(goal, 1, 1); | |
text(goalText, 10, 40); | |
String hitText = "Hit: " + nf(maxNum, 1, 1); | |
text(hitText, 380, 40); | |
//create the strings for the difficulty setting | |
String difEasy = "Difficulty: Easy"; | |
String difMed = "Difficulty: Moderate"; | |
String difDif = "Difficulty: Difficult"; | |
//determine which difficulty setting string to display based on the difficulty selected on the instructions screen | |
if (difficulty == .45) { | |
text(difEasy, 10, 480); | |
} else if (difficulty == .35) { | |
text(difMed, 10, 480); | |
} else if (difficulty == .25) { | |
text(difDif, 10, 480); | |
} | |
//call the screen function | |
Screen(); | |
} | |
//determing what screen to show | |
void Screen() { | |
if (start == 0) { | |
Main(); //shown first becuase the start variable is initialized to 0 | |
} else if (start == 2) { | |
Instructions(); //move to instructions screen | |
} else if (points == 10) { | |
YouWin(); //move to win screen | |
} | |
} | |
//create the main screen | |
void Main() { | |
//create a black background with a white fist | |
background(#000000); | |
image(imgWhite, 250, 250, 490, 490); | |
//show the title of the game | |
textFont(f,78); | |
fill(#bc091b); //red fill color | |
String BTUText = "BTU BOXING"; | |
text(BTUText, 10, 230); | |
//show instructions to get to the next screen | |
textFont(f, 25); | |
String startText = "Hit to see instructions and select level!"; | |
text(startText, 35, 260); | |
//if the bag is hit set the start variable to 2 to move to the instructions screen | |
if (total > .5 && x < 0 && y < 0 && z < 0) { | |
delay(1000); | |
start = 2; | |
} | |
} | |
//creates the display for the instructions screen | |
void Instructions() { | |
background(#000000); | |
//set font size to 30 | |
textFont(f,30); | |
fill(#bc091b); //red fill color | |
String instructText = "Instructions:"; | |
text(instructText, 175, 100-30); | |
//set text size to 25 | |
textFont(f,25); | |
//display line by line instructions | |
String instructText1 = "1. Try to hit the bag to match the goal"; | |
text(instructText1, 10, 130-30); | |
String instructText2 = "2. The fist will grow when you score a point"; | |
text(instructText2, 10, 160-30); | |
String instructText3 = "3. The fist will change color based on how"; | |
text(instructText3, 10, 190-30); | |
String instructText4 = " hard you must hit it"; | |
text(instructText4, 10, 220-30); | |
//display small green fist and text | |
image(imgGreen, 50, 240-30, 25, 25); | |
String instructText6 = ":light hit"; | |
text(instructText6, 70, 250-30); | |
//display small yellow fist and text | |
image(imgYellow, 50, 270-30, 25, 25); | |
String instructText8 = ":meduium hit"; | |
text(instructText8, 70, 280-30); | |
//display small red fist and text | |
image(imgRed, 50, 300-30, 25, 25); | |
String instructText10 = ":hard hit"; | |
text(instructText10, 70, 310-30); | |
//display more instructions | |
String instructText11 = "4. You win when the fist wont fit the screen"; | |
text(instructText11, 10, 340-30); | |
//display instructions for how to select level | |
String instructText12 = "5. Hit to select level:"; | |
text(instructText12, 10, 370-30); | |
String instructText13 = " Hit light for easy"; | |
text(instructText13, 10, 400-30); | |
String instructText14 = " Hit meduium for moderate"; | |
text(instructText14, 10, 430-30); | |
String instructText15 = " Hit hard for difficult"; | |
text(instructText15, 10, 460-30); | |
//set the difficulty and start points at 0 | |
if (total > .25 && total <= 1.666 && x < 0 && y < 0 && z < 0) { | |
delay(1000); | |
start = 1; | |
points = 0; | |
difficulty = 0.45; //easy difficulty | |
} else if (total > 1.666 && total <= 3.333 && x < 0 && y < 0 && z < 0) { | |
delay(1000); | |
start = 1; | |
points = 0; | |
difficulty = 0.35; //moderate difficulty | |
} else if (total > 3.333 && x < 0 && y < 0 && z < 0) { | |
delay(1000); | |
start = 1; | |
points = 0; | |
difficulty = 0.25; //difficult difficulty | |
} | |
} | |
//Updates the score when a point is scored | |
void ScoreUpdate() { | |
//compare the maxNum to the goal +/- the difficulty | |
//if x, y, and z are less than 0 then the bag is moving back | |
if (maxNum > (goal-difficulty) && maxNum < (goal+difficulty) && x < 0 && y < 0 && z < 0){ | |
points += 1; | |
delay(1000); //something to delay for a moment so the bag does not hit the goal on the way back down | |
maxNum = total; | |
goal = random(0.500, goalMax); //create a new goal | |
} maxNum = total; //reset the maxNum | |
} | |
//create the win screen | |
void YouWin() { | |
//create a balck background with a white fist | |
background(#000000); | |
image(imgWhite, 250, 250, 490, 490); | |
//create and display win text | |
textFont(f,78); | |
fill(#bc091b); | |
String winText = "YOU WIN!"; | |
text(winText, 60, 230); | |
//display how to play again | |
textFont(f, 25); | |
String returnText = "Hit to play again!"; | |
text(returnText, 155, 260); | |
//return to main screen if the bag is hit harder than 1.50g | |
if (total > 1.50 && x < 0 && y < 0 && z < 0) { | |
delay(1000); | |
points = 0; | |
start = 0; | |
Screen(); | |
} | |
} | |
//listening for serial events | |
void serialEvent(Serial myPort) { | |
String input = myPort.readStringUntil('\n'); | |
if (input != null) { | |
input = trim(input); | |
float[] sensors = float(split(input, ',')); //create an array for the sensor values | |
println(sensors); | |
// if the length of the sensors array is longer than 2 | |
if (sensors.length > 2) { | |
//set each variable to its given value from the potentiometer | |
x = sensors[0] – 0.98; //subtract gravity | |
y = sensors[1] – 0.06; //start at about 0 | |
z = sensors[2] – 0.25; //start at about 0 | |
} | |
} | |
myPort.write('x'); | |
} |
Final Photos:
Video: