Академический Документы
Профессиональный Документы
Культура Документы
Group 10
Table of Contents
Executive Summary ..................................................................................................................................... 3
Executive Summary
We developed a device that will automatically dispense liquid and make a mixed drink in
a 1 oz. glass, depending on the user inputs. The user will enter which liquids they want to use
and then how many of that particular drink they would like, repeating a drink combination to up
to 5 times. Entered through a keypad, the user has the option of four different liquid choices and
can select up to a combination of 10 inputs. The device is intended to use with liquids of
different specific gravities. With liquids of different gravities, the user would be able to make
layered drinks, with the “heavier” liquid settling on the bottom of the glass.
For each liquid or input that is selected, an incremental counter keeps track of how many
inputs are used. With the number of inputs known, the time it takes to fill a 1 oz. glass is then
divided by this number to allow for equal dispensation of each liquid type.
The 1 oz. glasses are placed upon a tray, which is attached to a step motor, and will rotate
to the position underneath the drink dispenser nozzles. Our device uses a light sensor to detect
whether there is a glass underneath the drink nozzles.
4
1.0 Introduction
The final project for ME 445 was to create a device that integrated electrical and
mechanical components and was controlled by an Arduino. Our group decided on creating an
automatic drink dispenser that dispenses liquids effortlessly and effectively. With our limited
experience with C and electronics, we decided on a project that was within what we could
realistic accomplish.
We wanted to work on a project that would be useful to us after the class ended. Because
one of our group members makes mixed drinks on the weekends, this project was suggested. The
group member commented on how long it takes to make layered drinks and when there is a high
volume of people, these layered drinks cause the line to back up. The intention of this project is
to allow the creation of mixed drinks automatically and quickly, while the user can attend to
other actions.
We then thought of a system that would do one drink at a time and have it securely
fastened to an arm that would move linearly and be able to raise the container closer to the
dispensers, but this idea would later be rejected because of it not falling into the original plans of
making several drinks quickly.
We created our final design when we changed our minds on how the dispensers would be
located. Once we moved all the dispensers to one location it was then simple to decide on the
current design of a tray that would simply rotate each container into position once it was time to.
5
3.0 Hardware
3.1 Bill of Materials for Major Components
To control the drink maker, we used an Arduino Uno to act as the brains for the device.
The keypad, from Sparkfun, allows the user to select which liquids they want to use, how much
of each liquid they want their drink to be made up of, and how times they want that drink to be
repeatedly made. The peristaltic pumps, one for each type of liquid, will dispense the liquids
based on which one the user selected, and in the order that the user selected. The step motor turns
a tray, which the 1 oz. glasses are placed upon, and allows for the repeated creation of the
particular drink. The CdS light sensor detects whether there is a glass underneath the liquid
dispenser. If the light sensor detects a glass, the peristaltic pumps will proceed to dispense the
liquids. However, if the light sensor does not detect a glass, the tray will continue to rotate until a
glass is detected.
This is the hardware that allows for user interaction. For this project, we selected the
Sparkfun 12 button keypad. We selected this keypad due to the availability of the part in the lab
and because there were wiring diagrams online. We allotted the keys 1 through 4 to correspond
to liquids 1 through 4. We also designated key ‘*’ to being a button one could press to move the
stepper motor 5 steps clockwise for realignment of the tray, ‘0’ to prep the liquids in the
peristaltic tube so that the liquids are at the face of the entrance, and ‘#’ to run the program after
the inputs were selected.
The keypad is wired directly to the Arduino, using seven digital pins. Four of the pins
correspond to a row and the remaining three pins correspond to a column. With this setup, if a
key is pressed, the combination of the two inputs, row and column, allow the program to
recognize which keystroke is pressed. To use a keypad in the program, we had download the
‘keypad’ library from the Arduino site. With this library, we used the functions ‘getKey()’,
which returns the key that is pressed into the Arduino, and ‘waitForKey()’, which causes the
program to wait forever until the user presses a key.
6
The Sparkfun keypad and keypad pins with its correlating Arduino digital pin
After we downloaded the keypad library, we discovered that the keystrokes were returned
to the Arduino as character data types. Because of this, to use the keystrokes inputted, we had to
convert the characters into integer data types. We had to use this when the user was inputting
how many repeated drinks they wanted to make.
We believed that the best way to do repeated drinks was to have the glasses on a
rotating tray. With a rotating tray, after one drink is made in one glass, the tray will rotate and
move the already made drink out of the way, while bringing a new glass to the liquid dispensing
position. We decided to use a stepper motor as the method to physically rotate the tray because
with a stepper motor, within the program, one can adjust the speed of the stepper easily and
program how many steps the stepper should move. Our particular stepper motor requires 200
steps to rotate a full 360 degrees. Therefore, since our tray had ten potential spaces to put a 1 oz.
glass in, we had our stepper motor rotate 20 steps to each space. We also set the stepper motor
speed to 15, within the program. Anything above this value, as the stepper speed, caused the
liquid with the glass to spill as the tray rotated.
7
A peristaltic pump is a type of displacement pump that incorporates a rotor with “lobes”
attached to the external circumference of the rotor. Inside this particular pump is a ‘clover’
pattern of rollers. As the motor turns, the clover presses on the tube to press the fluid through.
This allowed for a consistent and precise dispensation of a predetermined volume of fluid. For
our project, we found that running a peristaltic pump, connected to a 15 volt power source, on
high would dispense 1 oz. of liquid in 25 seconds.
We used MOSFET transistors to control the four peristaltic pumps used in the project.
With this motor control, we were able to direct when the pumps should be turned to on or off,
and for how long the pumps should be running for. Also, we could have used PWM to control
the speed of the pumps. Connected to the MOSFET transistors were flyback protection diodes to
eliminate sudden voltage spikes across and inductive load, if the supply voltage is suddenly
removed or reduced.
A cadmium sulfide (CdS) or light dependent resistor, this sensor provides resistance that
is inversely dependent on the light falling on it. This sensor is also known as a photoresistor or a
8
photoconductive cell. We connected one side of the photocell to 9V power and the other side to
an Arduino’s analog pin. Then we connected a 330 ohm pull-down resistor from that analog pin
to ground. With this set up, we were able to detect the voltage/light values when there was a 1
oz. glass in front of the sensor, and when there was no glass in front of the sensor.
Within the program, there is a while loop that indicates that if the light sensor reading is
above 50, the analog input reading we measured when there was no glass in front of the sensor,
the stepper motor will continue to turn 20 steps, to the next potential glass position. For our 1 oz.
glasses, we measured a light sensor reading of 20-35. Therefore, if the light sensor reading is
below 50, a glass has been detected and the peristaltic pumps can begin to dispense liquid.
4.0 Software
Our project’s code is split into two main while loops. The first part runs in a while loop
where the user can input the liquids they want dispensed. While the program is running this loop,
up to 10 inputs can be selected. The inputs are stored into an array and will be returned when the
‘run’ button is pressed. When each input button was pressed, an incremental counter in the code
would keep track of how many inputs were selected. When the amount of inputs is known, the
program will know to divide the volume of 1 oz. of liquid by the amount of inputs. Also, while
in this loop, the user can press the ‘*’ button to move the stepper motor 5 steps to realign itself
underneath the liquid dispenser and the ‘0’ button to prep the peristaltic pumps by having all the
pumps run on high for 5 seconds. When connected to the serial monitor on the computer, text
would appear after pressing each key to confirm to the user what was pressed.
When the ‘run’ button was pressed, designated as the ‘#’ key, the program would exit out
of the first while loop and enter the second while loop. First in this second while loop, the phrase
“# of drinks?” is displayed on the serial monitor. With the keypad library function,
waitForKey(), the program halts and doesn’t do anything until the number of drinks is selected.
Allowing for up to 5 repeats of a drink to be made, once keypad button 1 through 5 is selected,
the program will start to dispense the liquids. Before the project dispenses the liquids, a CdS
light sensor detects whether there is a glass underneath the liquid dispenser position. In the
9
program, we set the light sensor reading of a glass to be beneath 50. When light reading is below
50, the program will read the user selected inputs stored in the array and return them. Depending
on the specific order of the inputs selected, the coinciding peristaltic motors will run in the same
order. When the project makes the designated number of drinks, the program returns to the first
while loop and the counter value reverts back to zero, erasing the previous data stored in the
array. Finally, the stepper motor moves a final 20 steps clockwise to exit the position underneath
the liquid dispenser, to allow the user to pick up the glass easily.
5.0 Discussion
From the design process to the final construction, we constantly saw ways to improve
upon the project. The first step for improvement would be the overall construction of the
automatic drink maker. The tray we used is poorly connected to the stepper motor, attached by
hot glue and stabilized by electric tape. This hasty construction allowed for the stepper motor to
be misaligned often and angled. An angled tray would cause problems because it would lead to
spillage of the drinks. With a stronger tray and a flat, stable connection to the stepper motor, the
glasses would have a better chance at aligning with the liquid dispenser. Also, alongside the tray
improvements, there would be permanent holder for the glasses. Currently for our project, we
used plastic medicine cups to hold the 1 oz. glasses. The medicine cups were slightly larger than
the glasses and consequently, when the stepper motor moved, the glasses would reposition in the
medicine cups leading to possible misalignment.
Other improvements to the project would be on the hardware side. The light sensor, while
simple to implement, turned out to be temperamental. The light sensor range to detect the glasses
regularly changed depending on the overall brightness of the room and whether or not lights
were directly overhead. Because of this, the light sensor would only detect the glasses if the
setting was unchanged. If the light sensor worked previously, but then the project moved to
another area with different light brightness, the light sensor would not be able to detect the glass.
An improvement would be to use an infrared sensor to detect the glasses. With an IR sensor,
there would be consistent detections of glasses, regardless of room brightness. Another piece of
hardware to implement would be an LCD display. While connected to the serial monitor, the
user was able to confirm what keypad strokes were being pressed and what action the project
was on. However, without the serial monitor, the user is left unsure to what action the project is
on. With an LCD display, the user can see what inputs are being selected, how many inputs were
selected, and what action is being done.
10
6.0 Conclusion
Overall, we are pleased with our final project. We were satisfied that with our limited
knowledge in the field of mechatronics, we were still able to create a working project. There is
still a lot that there could be improved on with the automatic drink maker, however the idea of
the project is established.
At the beginning of the semester, we went into this project with little knowledge of how
to successfully integrate mechanical and electrical systems into a functional product. However,
during the process of creating our automatic drink maker, we gained invaluable information
related to our field of study that can only benefit our future professional endeavors. Through the
process of designing, implementing and completing our final product, we have not only given
ourselves an edge over career-related competition, but most importantly, we get to go home with
a free automatic drink maker.
11
#include <Keypad.h>
#include <Stepper.h>
int x = 0;
int val = 0;
int num = 0;
int inputType = 0;
int buttonPress = 0;
int drinkRun = 0;
//---------------------------------------------------------------------------------------------------------
12
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 13}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad
void setup()
pinMode(input1Motor, OUTPUT);
pinMode(input2Motor, OUTPUT);
pinMode(input3Motor, OUTPUT);
}//end of setup
void loop()
if(key == '0'){ //if the 0 key was pressed turn all motors high for 5 seconds
Serial.println("Run Motors");
digitalWrite(input1Motor, HIGH);
digitalWrite(input2Motor, HIGH);
digitalWrite(input3Motor, HIGH);
delay(5000);
digitalWrite(input1Motor, LOW);
digitalWrite(input2Motor, LOW);
digitalWrite(input3Motor, LOW);
if(key == '*'){ //if key * is pressed, turn the stepper motor 5 steps
Serial.println("Turn 5 Steps");
myStepper.step(5);
if(key == '1'){
Serial.print("input #1 ");
liquid[counter] = 1;
if(key == '2'){
14
Serial.print("input #2 ");
liquid[counter] = 2;
if(key == '3'){
Serial.print("input #3 ");
liquid[counter] = 3;
if(key == '4'){
Serial.print("input #4 ");
liquid[counter] = 4;
}
15
if (key == '#'){ //if the # key is pressed, exit the while loop and run the run() function
drinkRun = 1;
run();
void run(){
Serial.println("# of Drinks?");
if (repeat != NO_KEY){
Serial.println(repeat);
if (repeat == '1'){
num = 1;
}
if (repeat == '2'){
num = 2;
}
if (repeat == '3'){
num = 3;
}
if (repeat == '4'){
num = 4;
}
if (repeat == '5'){
num = 5;
}
val = analogRead(analogPin);
while (val > 50){ //light sensor threshold, beneath this would detect a glass
delay(500);
val = analogRead(analogPin); //reads and prints light sensor data for user benefit
Serial.println(val);
Serial.println(val);
Serial.println(liquid[i]);
if (liquid[i] == 1){
delay(100);
digitalWrite(input1Motor, HIGH);
delay(25000/counter);
digitalWrite(input1Motor, LOW);
delay(100);
if (liquid[i] == 2){
delay(100);
digitalWrite(input2Motor, HIGH);
delay(25000/counter);
digitalWrite(input2Motor, LOW);
delay(100);
17
if (liquid[i] == 3){
delay(100);
digitalWrite(input3Motor, HIGH);
delay(25000/counter);
digitalWrite(input3Motor, LOW);
delay(100);
if (x == num){ //if x reaches the number of repeated drinks made, exit the while loop
drinkRun = 0;
myStepper.step(20);
}
18
Bill of Materials
Stepper Motor
21
MOSFET Transistor
Autonomously
Navigating Car with
Obstacle Avoidance
By Ramya Cheruvu
ME 445: Microcomputer Interfacing
Prepared for: Dr. Henry J. Sommer III and Mike Robinson
Dec 17, 2013
1
Abstract
This report details the modification of a small scale automobile which will enable the vehicle to
maneuver around obstacles autonomously while traversing the distance towards a user set destination.
The objective of this project is to replace the outdated control system. The car will be fit with a
programmed Arduino which uses feedback from GPS sensor and Ultrasonic Rangefinder to gather
information about the surroundings. The micro controller will then plan the course and provide required
actuation to the motors. Motion of the car is monitored by an IMU sensor which checks if the course is
in line with the planned trajectory. Also, manual override capability has been provided to the end user
by introducing a IR sensor which generates the appropriate command according to the signal sent by the
a remote.
2
Table of Contents
Contents
Abstract ........................................................................................................................................................................................ 2
Introduction ............................................................................................................................................................................... 4
Overall design ........................................................................................................................................................................... 4
Body............................................................................................................................................................................... 4
Electronic Hardware............................................................................................................................................................... 5
Dual motor Driver.................................................................................................................................................... 5
MPU6050 ..................................................................................................................................................................... 6
Servo motor………………........................................................................................................................................... 6
Ultrasonic RangeFinder......................................................................................................................................... 7
GPS logger………………….......................................................................................................................................... 7
IR receiver………………….......................................................................................................................................... 8
Software. ..................................................................................................................................................................................... 9
Schematic................................................................................................................................................................................... 10
Integration................................................................................................................................................................................ 11
Conclusions and Future Work........................................................................................................................................... 11
Bill of Materials........................................................................................................................................................................ 12
Appendix................................................................................................................................................................................... 12
3
Introduction
The goal of this project falls under the category of find goal problem in sensor based motion planning. In
general, classical path planning algorithms assume prior knowledge of the configuration space and are
not amenable to sensor information. This is disadvantageous in the cases where the surroundings are
unknown or changing. Additionally, acquiring information about surroundings sometimes requires path
planning. In such cases, sensor based motion planning becomes important. The vehicle in this case does
not have prior knowledge of the surroundings. It uses the on-board information to obtain information
about the surroundings. The car has to find a collision free path from its current location to a user set
destination based on the information from sensors.
A basic algorithm is considered for controlling the course of the vehicle. The slope of an imaginary line
between current location and goal configuration is computed at every point to obtain the bearing of the
car. Distance from the goal point is also computed and these values are then translated into motion by
proportional control of actuators.
Goal Point(xf,yf)
-x x
Overall Design
The design of the vehicle in this case is not of critical importance. Any reasonably scaled vehicle which
has two degrees of freedom is sufficient for this project.
Body
4
The metal chassis of a toy car with wheels actuated by DC motors has been chosen for this purpose. It
can be directed to any point in x-y plane by control and variation of the speed between left and right
wheels. It is also equipped with five 1.5V battery holders which give approximately 5~6V and can be
used to power the motors.
Connection
to DC
motor
Electronic Components
Motor Drivers
Dual motor drivers have been used to control the speed and direction of the wheels. Two motor drivers
have been used to control the four wheels and Arduino provides the necessary analogue inputs to
drivers. Arduino provides two analogue inputs, one for both right wheels and left wheels each. The
motor 5V power needed for both logic inputs and running the motor (M+ and GND pins) are provided by
batteries in the car.
5
Fig 3: Dual Motor Driver
Now that actuators of car can be controlled, the car can be made to follow the course by running the car
at a particular speed till it reaches the goal, i.e., distance computed from algorithm becomes zero. More
difficult to account for is the direction in which the car is heading. To ensure that the car moves in the
correct direction, feedback of car bearing is of considerable importance. For this purpose, an Inertial
Measurement Unit sensor is introduced into the circuit to enable PID control of the bearing based on
sensor feedback.
MPU-6050
MPU-6050 is an inertial measurement unit combining both accelerometer and gyroscope. The breakout
board GY-521 is used for this project. It gives acceleration in x, y, z directions and rotation about x, y, z
axes. For control of bearing, rotation about z-axis is needed. Accelerometer does not give this value. It
has to be obtained from raw gyroscope data. MPU6055 gives angular velocity about z axis. We obtain
the angle by integrating raw angular velocity with respect to time. The raw sensor values have to be
calibrated before use. Calibration has been done by subtracting the raw values with average of first ten
readings before integration. Since we are integrating, the value of angle drifts with time and it it is not
accurate in long-term. Therefore, the car is made to stop and turn to the required angle every time it
has to change direction and then run straight when direction is correct. Since angle from gyroscope is
used for a short period of time during proportional control of bearing, the increasing drift is negligible.
6
Ultrasonic Range Finder
An ultrasonic rangefinder by MaxBotix is used for obstacle detection. This sensor determines the
distance by sending out a pulse and measuring the length of the reflected echo. It is good for long range
applications. The minimum distance measured by ultrasonic sensor is 30cm. Its readings fluctuate a lot,
so to get accurate readings the values have been filtered by using a mode/median filter. The mode filter
sorts out a set of readings and returns the value that has been read maximum number of times. In case
there is no common value, the middle value among the sorted readings is taken as the distance. The
ranges of values are taken at an interval of 30ms to prevent reading the same value twice. A range of 4
consecutive readings give an accurate estimation of distance. However, since this sensor does not give
information about width of the obstacle, mounting this sensor on a servo motor has been considered to
get collision free path.
Servo motor
Ultrasonic rangefinder is mounted on a servo motor which is made to rotate through angles 0 to 90 deg.
This enables the range finder to sweep the entire area and the micro controller computes the obstacle
free direction closest to the current direction based on information from the sensor. The result is in
terms of angle (of servo motor) of course which has no obstacles. If the rotation of the servo is too fast,
same distance is recorded for all the angles. A delay of one second between angles has been found to
give accurate readings.
GPS sensor
A GPS sensor has been selected for feedback about current location of the vehicle. The GPS logger shield
from Adafruit is included in the circuit. The sensor returns information about time, temperature, date,
latitude, longitude etc. Latitude and longitude are relevant to this project as they provide feedback of
current location. The latitude and longitude can be logged and retrieved either in XML format or KML
format.
7
Fig 6: Adafruit GPS logger shield
IR receiver
An IR receiver has been included to allow for manual override by operator. This receives modulated IR
signals transmitted by a remote. The remote is equipped with IR led which blinks according to the
8
button pressed. The value of the signal for each button has been decoded using an Arduino script and
micro controller performs certain functions when a particular button is pressed. This remote transmits
random codes other than the value of the button. These codes will not fire the written sequence in
Arduino. The button has to be pressed 2-3 times to fire an expected operation.
Straight
#include <IRremote.h>
void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
}
void loop()
{
if (irrecv.decode(&results))
{
Serial.println(results.value, DEC); // Print the Serial 'results.value'
irrecv.resume(); // Receive the next value
}
9
Software
10
Schematic
Integration
There have been setbacks in integrating these components. Ultrasonic sensor with Servo and IR receiver
has been successfully incorporated in the main circuit. However, the first problem arose when trying to
use MPU6050 along with the remaining setup. Arduino hangs whenever the car is started. The same
problem has been observed with GPS sensor. Running both car (motor drivers) and GPS sensor at once
halts the execution of code and reduces the speed of car wheels. Several trouble shooting methods have
been employed to determine the source of the issue:
1. Manual checking of the schematic and circuits has been repeated to prevent mistake due to
wiring. Debugging of the code resulted in no errors. Even the basic code for obtaining raw values
for IMU sensor halts when DV motors are switched on. So, the problem seemed to be with the
circuit.
2. It was concluded that malfunction may be due to insufficient power supply as failure occurred
when starting the motor and also a corresponding decrease in motor speed was observed.
Separate power sources have been used for IMU, GPS and motors without success.
3. Capacitors were added to power sources to filter out any disturbances. Also, physical damping
was applied to rule out the possibility of effects due to physical disturbances.
4. The pins for PWM used for the motors in Arduino have been changed to avoid serial outputs on
the GPS shield without any results.
11
Conclusion and Future Work
The malfunction has finally been assumed to be caused by the way GPS shield is wired. Next course of
action would be to have IMU tested with different motors (car), or without GPS shield. Another option is
using a different position sensor. Testing with different GPS sensor also might allow for pinpointing the
source of the error.
Bill of Materials
Component Quantity Cost
GY-521 1 $39.95
MaxBotix MB 1023 1 $5.95
Adafruit ultimate GPS logger 1 $39.95
shield
IR receiver and remote 1
RC car 1
Servo Motor 1
Dual motor drivers 2
Appendix
/******Code for Autonomous Navigation********/
//For GPS
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);
#define GPSECHO false
12
//For IR
#include <IRremote.h>
int IRpin = 11;
IRrecv irrecv(IRpin);
decode_results results;
//For IMU
#include "Wire.h"//
#include "I2Cdev.h"
#include "MPU6050.h"
13
float axBase;
float ayBase;
float azBase;
float gxBase;
float gyBase;
float gzBase;
unsigned long t;
// Use the following global variables and access functions to help store the overall
// rotation angle of the sensor
unsigned long tPrev = 0;
float axPrev = 0;
float ayPrev = 0;
float azPrev = 0;
float gxPrev = 0;
float gyPrev = 0;
float gzPrev = 0;
float gzAngle;
void setup()
{
Serial.begin(115200);
//For Car
pinMode(rightsidePin, OUTPUT);
pinMode(rightdirectionPin, OUTPUT);
pinMode(leftsidePin, OUTPUT);
pinMode(leftdirectionPin, OUTPUT);
digitalWrite(leftsidePin,LOW);
digitalWrite(rightsidePin,LOW);
//For IR
irrecv.enableIRIn(); // Start the receiver
//For Servo
sensingServo.attach(servoPin);
//For GPS
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
GPS.sendCommand(PGCMD_ANTENNA);
14
// Serial.println(" no response :(");
// delay(1000);
//For IMU
// join I2C bus (I2Cdev library doesn't do this automatically)
Wire.begin();
// initialize device
accelgyro.initialize();
calibrate_sensors();
}
{
translateIR();
irrecv.resume(); // receive the next value
} //end of if IR
else{
driveStraight();
readSensor();
delay(100);
if(distance>=45){//Check for Obstacles before proceeding
driveStraight();
//delay(100);
// read data from the GPS
char c = GPS.read();
15
if (GPS.fix) {
// Serial.print("Location: ");
// Serial.print(GPS.latitude, 4); Serial.print(GPS.lat);
// Serial.print(", ");
// Serial.print(GPS.longitude, 4); Serial.println(GPS.lon);
float x = (GPS.latitude,4);
float y = (GPS.longitude,4);
angle = atan((yf-y)/(xf-x));
Serial.print("angle");
Serial.print(" ");
Serial.print(angle);
Serial.print(" ");
Serial.print(distance);
xd = sqrt(sq((xf-x)+sq(yf-y)));
if(xd>0){
if (angle>0){
goToAngle((90-angle));
driveStraight();
}
else if(angle<0){
goToAngle(-(90-angle));
driveStraight();
}
}
else{driveStop();}
16
}//End of Loop
//Function to adjust bearing without IMU based on a scale of running one side for 300ms allows the car
to turn by one degree
void goToAngle(float angle){
float carAngle = angle;
int scale = carAngle*300;
if(carAngle>0){
turnLeft(255);
delay(scale);
driveStop();
}
else{
turnLeft(255);
delay(scale);
driveStop();
}
}
17
{
//int motorSpeed = map(abs(Speed), 0, 100, 0, 255);
Serial.print("Straight");
digitalWrite(rightdirectionPin, LOW);
digitalWrite(leftdirectionPin, LOW);
analogWrite(leftsidePin, 255);
analogWrite(rightsidePin, 255);
}
void driveReverse()
{
//int motorSpeed = map(abs(Speed), 0, 100, 0, 255);
digitalWrite(rightdirectionPin, HIGH);
digitalWrite(leftdirectionPin, HIGH);
analogWrite(leftsidePin, 255);
analogWrite(rightsidePin, 255);
}
void driveStop(){
Serial.println("Stop");
analogWrite(leftsidePin, 0);
analogWrite(rightsidePin, 0)
;
}
/******************End of Methods for driving the motors*************************/
18
/*******Methods for IR Receiver**************************************************/
// takes action based on IR code received
void translateIR()
// Numbers in case statement correspond to values of IR Remote
{
switch(results.value)
{
case 16605343:
driveStop();
Serial.println(" Stop ");
break;
case 16621663:
driveStraight();
Serial.println("Straight");
break;
case 16625743:
driveReverse();
Serial.println("Reverse");
break;
case 16584943:
turnLeft(100);
delay(10);
driveStop();
Serial.println(" Left ");
break;
case 16601263:
turnRight(100);
delay(10);
driveStop();
Serial.println(" Right ");
break;
default:
Serial.println(" other button ");
delay(500);
} //END translateIR
19
void readSensor()
{
// move servo into position
sensingServo.writeMicroseconds(pos);
//Sorting function
// sort function (Author: Bill Gentles, Nov. 12, 2010)
void isort(int *a, int n){
// *a is an array pointer function
for (int i = 1; i < n; ++i)
{
int j = a[i];
int k;
for (k = i - 1; (k >= 0) && (j < a[k]); k--)
{
a[k + 1] = a[k];
}
a[k + 1] = j;
}
}
//End Sort
int i = 0;
int count = 0;
int maxCount = 0;
int mode = 0;
int bimodal;
int prevCount = 0;
while(i<(n-1)){
20
prevCount=count;
count=0;
while(x[i]==x[i+1]){
count++;
i++;
}
if(count>prevCount&count>maxCount){
mode=x[i];
maxCount=count;
bimodal=0;
}
if(count==0){
i++;
}
if(count==maxCount){//If the dataset has 2 or more modes.
bimodal=1;
}
if(mode==0||bimodal==1){//Return the median if there is no mode.
mode=x[(n/2)];
}
return mode;
}
}//End Mode filter for ultrasonic
int chooseDirection()
{
// filter out directions with obstacles
for(int i,j = 0; i < directionsAndDistancesLength; i++)
{
if (directionsAndDistances[i][1] >= minimumDistance)
{
obstacleFreeDirections[j][0] = directionsAndDistances[i][0];
obstacleFreeDirections[j][1] = directionsAndDistances[i][1];
}
else
{
obstacleFreeDirections[j][0] = 0;
obstacleFreeDirections[j][1] = 0;
}
obstacleFreeDirections[j][2] = 0;
j++;
}
// calculate the discrepancy between the waypoint direction and each direction
for(int i = 0; i < directionsAndDistancesLength; i++)
{
if(obstacleFreeDirections[i][0] != 0)
{
21
obstacleFreeDirections[i][2] = abs(waypointDirection - obstacleFreeDirections[i][0]);
}
}
// choose the direction closest to the direction of the goal with no obstacles
// find the first obstacle free direction
int solutionDirectionReference = 0;
while(obstacleFreeDirections[solutionDirectionReference][0] == 0)
{
solutionDirectionReference++;
}
// find the obstacle free direction closest to the direction of the goal
for(int i = 0; i < directionsAndDistancesLength; i++)
{
if(obstacleFreeDirections[i][0] != 0 && obstacleFreeDirections[i][2] <
obstacleFreeDirections[solutionDirectionReference][2])
{
solutionDirectionReference = i;
}
}
if(obstacleFreeDirections[solutionDirectionReference][0] > 15000)
{
return 0;
}
else
{
return obstacleFreeDirections[solutionDirectionReference][0];
}
}
/***********End of Methods for Obstacle detection from ultrasonic sensors*********************/
22
gz_standard += gz;
delay(100);
}
ax_standard /= num_readings;
ay_standard /= num_readings;
az_standard /= num_readings;
gx_standard /= num_readings;
gy_standard /= num_readings;
gz_standard /= num_readings;
float measureAngle(){
accelgyro.getRotation(&gx, &gy, &gz);//Get raw values from IMU
t=millis();
//Divide by 131 to convert to deg
gzAngle = (((gz-gzBase)/131)*((float)t-(float)tPrev)/1000)+gzPrev;
tPrev = t;
gzPrev = gzAngle;
return(gzAngle);
}
23
Final Project – Educational
Arduino Based Robots
ME 445: Microcomputer Interfacing
Team 12
Brad Wile and Mike Coia
Background
For this project, Team 12 decided to integrate two different areas of interest. The team members
are part of the Engineering Ambassadors here at Penn State, a group of students who promote
engineering interest to high school and middle school students. The group goes into these
classrooms with a presentation of an engineering topic and a related activity. One example is a
presentation of biomimicry (using nature’s design in developed products), and then performing
an activity of creating a prototype prosthetic leg. This allows the students to understand a little
bit about what engineers do and some insight into the design process they use.
The group is always expanding its array of activities, so they entered an application to an IBM
and IEEE grant to promote engineering to the younger generations. This grant earned the
Engineering Ambassadors around $500 to purchase supplies needed to create the robots,
discussed below.
Objective
The objective of this project was to create another activity for the Engineering Ambassadors to
use in a school setting to demonstrate engineering. The activity needed to be informative,
showing the students what engineers can do, but not overly complicated, so as not to confuse the
students with too many engineering concepts and technology thrown at them at the same time.
The students that perform these activities should see how a little competition, creativity and
effort can create awesome results.
In order to complete these goals, two different robots were created to accomplish different tasks.
A demonstration robot that will be used by the Engineering Ambassadors will be a wireless robot
that will be controlled by an accelerometer in a glove worn by the user. This would allow them
to drive and turn the robot with just slight movements of their hands. The other set of robots
would be a set of robots used in the activity the students perform. Their objective is to get a
robot through a simple maze in as fast as possible.
Materials
The materials needed to construct the robot were easily available and found online. The list of
materials can be found below.
Arduino Uno 1
Acrylic Body 1
Acrylic Wheels 2
Ball Bearing 1
Continuous Servo 2
IR Sensor 1
Battery Pack 1
AA Batteries 4
Mounting Screws -
Connecting Wires -
Construction
Once the design and the materials for the robots were located, construction could begin.
Acrylic Body
Using the laser cutter located in the learning factory, the acrylic sheets that were purchased were
used to create the body of the robot and the power wheels. The body joined together without
hardware, but there was some trouble at points because acrylic is very brittle and can snap very
easily. Custom fitting was necessary for each robot using a belt sander and rubber mallet. While
Using acrylic were somewhat aesthetically pleasing, it would have been more beneficial to use a
stronger structural base for these robots. This would have also better aligned with our design
drivers that were previously mentioned.
Servos
The continuous servos were used to drive the power wheels, and were attached using pre
cut holes in the acrylic and screws. The non-continuous servo was used to swivel the IR sensor,
which sat atop the robot and could be used for a variety of applications. The ball bearing acted
as the front wheel, just stabilizing the robot and allowing it to roll easily.
Arduino/Power Connection
On the back of the robot was where the Arduino sat, which allowed easy access for all the wires
and other components to attach to it. The battery pack sat inside the robot, with a cutout for the
power switch cut into the bottom the robot for easy power control. The whole robot is powered
by 4 AA batteries to supply 6V to the Arduino.
Materials
Hardware Modifications
Using the preconstructed robots as a base, a ARD-0119 protoshield KIT was attached to
the arduino in order to deliver the necessary power to drive the robots. Thess protoshields
contained power and ground pins running across the circuit board. This shield allowed us to
power multiple components in an organized manner. For our specific purpose, both continuous
servos, the IR sensor, and the non continuous servo was connected to this protoshield
Hardware Troubleshooting
Soldering these protoshields together proved to be one of the most important parts of this
project. Improperly soldering a wire of connection to this board has the potential to cause the
arduino to incorrectly interface with the servos and IR sensor. There were multiple times where
the IR sensor was relaying incorrect data. Similar problems occurred when the arduino would
attempt to interface with the continuous servos.
Figure 3: Final Assembly of Autonomous Robots
Software Modifications
A simple maze navigation algorithm was designed to roughly navigate the robot around a
maze. When the arduino is powered on, the robot is commanded to move forward. While in
forward motion, the robots is continuously scanning it’s path in the forward facing direction.
Once the IR sensor detects an object in it’s path it ceases all motion. The algorithm then instructs
the non continuous servo to rotate in the 90 degrees in both directions (relative to the forward
direction). The robot then will then determine which direction contains the most open path. Once
this has been determined, the robot will make a 90 degree turn in that direction.
Software Troubleshooting
There will be between 5-7 Robots running this control algorithm when in these
classrooms. One issue that needed to be corrected was the minor speed adjustments for each
robot. The continuous servos are motionless when they are programed to have a speed of zero.
However, these servos are not perfectly accurate. Making sure each servo has been set it it’s
individual equilibrium point is crucial. The turning code also needs to be tailored to each robot.
Modifying the speeds for the continuous servos will ensure each robot rotates 90 degrees. This is
a tedious task, however, it is essential in order for this activity to run smoothly in a high school
environment.
Discussion
Completing these robots was successful, and they work just as expected. The only things that
could be added are potentiometers to adjust the gain of the IR sensor and the speed of the car.
This would allow the students to be able to tweak the car to allow it to traverse a maze and race
their classmates.
Wirelessly Motion Controlled Robot Design
Materials
Part Quantity
Prototyping Board 1
Soccer Glove 1
Hardware Modifications
After creating the standard robots, the wirelessly controlled one could be created from a
modified version. The IR sensor was removed, as it is not needed. The Xbee Shields needed
headers to be soldered to them, and then were connected with the Xbees and Arduinos. The
Shields were extremely helpful as they could just be dropped on the Arduino and then all of the
pins were still usable. The Xbee could then be dropped on the shield and act as a transmitter or a
receiver depending on the code; the shield allowed each to be used in both ways. Because of our
design however, each Xbee had its own specific role as one acted as the receiver and the other as
the transmitter.
In order to create the motion-controlled glove, the transmitter Xbee, battery pack, and
accelerometer were all physically attached to the soccer glove that was used. They were
velcroed on to allow for removal and easier storage and transportation. The accelerometer was
only used with 2-Axis, as the Z direction was unnecessary for our design. It was connected to
two of the Arduino’s Analog Inputs to send the data it collected from the movement of the glove
for the X- and Y-axis. The X-axis controls the forward and backward motion of the car, tilting
one’s hand forward would allow it to move forward, and tilting it backwards allows the car to
move backwards. If the hand is kept level, the car is stationary. The Y-axis controls the turning
of the car and its bearing. Rolling the hand to the left would turn the car in that direction, and the
opposite is true as well.
Figure 4. Arduino with Shield and Xbee
As the hand is more steeply moved, the speed of the car would increase. This means it would
move in the X-axis faster, or turn faster in the Y-axis. Turning would first be accomplished by
increasing the speed of the opposite wheel. As the roll of the hand is increased, the wheel on the
side of the turn would slowly decrease its speed, until it stops. This turns the car faster and
allows for tighter turns. Eventually, the wheel would eventually start going the opposite
direction to increase the turn speed even more.
With the accelerometer connected to the Arduino, the Xbee would then send the data obtained to
the car itself. The Xbee uses the IEEE 802.15.4 protocol to communicate short-range with the
other XBee. The range on the Xbee is advertised as 300ft, which is more than enough to
communicate with the car in a classroom setting.
Figure 6. XBee Module Series 1
Hardware Troubleshooting
Initially connecting the XBee was a bit of a problem at first. The XBee need to be set to the
same baud rate in order send and receive information effectively. This required the use of
special boards with USB connections to connect to the computer and the use of a data terminal
program to send specialized codes to the XBees.
Software Modification
As there were many hardware changes from the original robot, the software needed to be
different from the original as well. The code for the transmitter is not very complicated, just
taking the data from the accelerometer and printing out to the XBee which sends it to the other.
The code for receiving is a little more complicated however. The Xbees send info as strings, so
it must converted to an integer number in order to be read by the servos. In order for this to be
accomplished, the string must be read into the Arduino, then the string is read to a character
array. This then allows it to be broken down number by number into their ACSII codes. This
code can then be converted back into the number as an integer number. It was a little hiccup in
the process of using the Xbees, but it was an easy fix that allowed for easy communication.
These numbers were then scaled to work with the servos, as they accept numbers from 0-180,
while the accelerometer would send numbers typically between 315 and 480. This was taken
care of with the map function, and then the numbers were sent to the servos using the included
libraries. The map function was also used to convert the number to the opposite which could be
sent to the other servo, since positive for one is negative for the other based on the hardware
installation. This allowed both servos to move at the same speed in the same direction when
needed.
Software Troubleshooting
Unfortunately, the code for controlling the car wirelessly was a bit tricky, and something was
stopping both servos from working together. As seen in the in-class demo, only one wheel
would spin , causing the car to just spin in place. This was less than impressive, but did show
that the wireless communication was in fact working. The servos would work together when
simple number were inputted into them, but when the variables containing the data from the
XBee were used, one of the servos did not work. This problem could not be diagnosed but some
possible causes that need to be addressed would be the conversion from string to integers from
the XBee, or the fact that the integer was not “reconnected” after the string was converted. The
problem does not seem to be a hardware issue as both servos will work when the variables are
replaced with numbers.
Discussion
As the robot was not fully functional, there is definitely more work to be done. The code must
be exhaustively searched for any discrepancies that are preventing the servos from working
properly. Creating this robot was full of problems, from the Xbees not communicating, to the
servos not working, to the necessity of another Arduino and power supply, but it allowed the
team to learn from our mistakes and almost come up with a finished product. Fixing the code is
the biggest thing that needs to be shown attention, but there are other improvements that could
make it even better of a project. First, the XBee are only transmitting at 9600 bps. This causes
the robot to not move very fast when it is being controlled. It would make for an even better
activity if it was moving at normal speed. This could be improved by increasing the baud rate to
57600 bps. Also, the glove was bulky and not aesthetically pleasing. If the wiring connecting
the accelerometer to the Arduino was made using a PCB, it would reduce the size of the
hardware dramatically and perhaps could be an internal system inside of the glove. Also, using
motors instead of servos would have increased the speed and power of the robot, but the
design/material constraints did not allow for that.
References/Parts
www.BobbyLeary.com/r3
http://store.nkcelectronics.com/Protoshield-KIT-for-Arduino-UNO-R3_p_308.html
http://www.adafruit.com/products/163#Learn
https://www.sparkfun.com/products/10854
https://www.sparkfun.com/datasheets/Wireless/Zigbee/XBee-Datasheet.pdf
Appendix A: Code Appendix
Autonomous Code
#include <Servo.h>
Servo first; // create servo object
Servo second; // create servo object
Servo third; // create servo object;
void setup() {
Serial.begin(9600);
first.attach(firstservopin); // attach the servo to digital output 8
second.attach(secondservopin); // attach the servo to digitalt output 7
third.attach(thirdservopin); // attach the servo to digital output
first.write(93); // center the servo
second.write(94); // center the servo
third.write(90);
delay(1000);
}
void loop() {
sensor = analogRead(A2);
delay(1000);
Serial.println(sensor);
if(sensor > 300) {
first.write(93); // center the servo
second.write(94); // center the servo
third.write(0);
i = analogRead(A2);
delay(1000);
third.write(180);
j = analogRead(A2);
delay(1000);
if(i > j) {
first.write(85);
second.write(91);
delay(1000);
first.write(93); // center the servo
second.write(94);
}
else
{
first.write(111);
second.write(97);
delay(1000);
first.write(93); // center the servo
second.write(94);
}
third.write(90);
delay(1000);
}
else {
first.write(84);
second.write(105);
}
}
while (Serial.available()) {
delay(10);
if (Serial.available() >0) {
if (readString.length() >2) {
char b = Serial.read();
readString2 += b;
}
else {
char c = Serial.read();
readString += c;
}
}
}
if (readString.length() >0) {
Serial.print(readString);
Serial.println(readString2);
readString.toCharArray(char1Array, (readString.length()+1));
val1 = atoi(char1Array);
readString2.toCharArray(char2Array, (readString2.length()+1));
val2 = atoi(char2Array);
val2 = map(val2, 315,395, 60,126);
//Serial.print(val1);
//Serial.println(val2);
one = val2;
two = map(one, 60,126,126,60);
Serial.print(one);
Serial.println(two);
if (one > 120){
one = 120;}
if (one < 60){
one = 60;}
}
left.write(one);
right.write(two);
//delay(50);
readString="";
readString2="";
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
void loop() {
// put your main code here, to run repeatedly:
delay(250);
int x = analogRead(A3);
int y = analogRead(A2);
Serial.print(x);
Serial.println(y);
}
The Stacker Game
Date: 12/17/13
Abstract
This ME 445 Fall 2013 project sets out to create a micro-controller operated arcade game
commonly known as Stacker. The main Stacker arcade components include a panel of eight by
eight addressable RGB LEDs, an ATMega328P which is the micro-controller in an Arduino
Uno, a 16MHz resonator to provide a clock signal to the Atmega, two 10 micro-Farad capacitors
that act as power decouplers, a 7805 power regulator, a big button to trigger the LEDs, and a 9V
battery. When combined together, the components work together like an Arduino Uno to
interface the LEDs and trigger button to line the LEDs up on the box display.
In order to enable a different skill level for each row of the game, different speeds were
incorporated into the LED panel. Considering that human interaction with the game does take
some time and experience, the first level is set for a beginner at a very slow LED speed while the
last level is set for an expert at a very high LED speed.
Adding complexity to our project, the human triggered Stacker arcade game competes
against our simulated robot triggered Stacker arcade game. The robot works by using eight
phototransistors, a solenoid, two 9V batteries, a power MOSFET and eight resistors. Each of the
phototransistors line up with a row of the LED panel. By lining up the phototransistors, the
Stacker game will time each row of the LED panel, triggering the solenoid which presses the
button, ultimately, winning the game. This timing is imperative as the solenoid must be triggered
only when the phototransistor voltage output is highest. This occurs when the LED light is
directly below the phototransistor. If the LED were ahead or behind the phototransistor when the
solenoid triggered, the LED rows of the game would not be aligned, losing the game.
However, it should be noted that with such a high voltage power source the solenoid
should not be turned on at all times. This would cause the solenoid to become very hot over time.
Therefore, a power MOSFET, along with several resistors and a flyback diode, were utilized in
the circuit operating the solenoid. The power MOSFET will turn the solenoid on and off through
a digital Arduino pin. The flyback diode will be placed in parallel to the solenoid within the
circuit to minimize flyback, or the sudden voltage spike from an inductive load created when the
power supply is suddenly removed.
Both the robot triggered Stacker and human triggered Stacker arcade games were
designed inside 6” by 10” wooden box in order for easy transport. The human triggered Stacker
arcade was designed with intent as a portable arcade durable for people of any age group.
Contents
Abstract ......................................................................................................................................2
Introduction...............................................................................................................................4
Project Description ................................................................................................................4
Objective ................................................................................................................................4
LED Display ..............................................................................................................................4
Library ...................................................................................................................................5
Micro-controller ........................................................................................................................6
ATMega328P .........................................................................................................................6
16 MHz Resonator.................................................................................................................7
7805 Power Regulator ...........................................................................................................7
Power Decoupling Capacitor ................................................................................................8
Robot Trigger ............................................................................................................................9
MOSFET ...............................................................................................................................9
Solenoid................................................................................................................................ 10
Phototransistor .................................................................................................................... 11
Conclusion ............................................................................................................................... 12
Appendix ................................................................................................................................. 13
Stacker Project Code........................................................................................................... 13
Robot Code .......................................................................................................................... 35
Introduction
Project Description
A Stacker arcade game is a game whose goal is to align the rows of moving LED blocks
on top of each other. A player who can stack eight rows wins the game. However, the key is to
beat the robot that will be playing against the human player. The robot will work by using a
solenoid to trigger the button on the Stacker whenever the rows are properly aligned.
The two Stacker arcade boxes will race against one another being human and robot
triggered, respectively. Regardless of the player, the Stacker will display green light if the player
is successful in defeating the game or the Stacker will display a red light if the player is defeated
by the game.
Objective
This project will explore the following concepts: using the components of an Arduino to
simulate an Arduino as a controller, interfacing the button with a solenoid, understanding an
eight by eight LED panel of LEDs and their library, and learning about individual electronic
components and their various uses. There were several main objectives in order to fully
understanding how the LEDs worked and the robot would trigger and play the Stacker game.
Create two Stacker arcade games within a 6” by 10” wooden box
Develop a code that runs each of the LED rows at different times; the initial rows will be
run at a slower rate than the later rows
Create a robot that simulates a “perfect human” playing the arcade game Stacker
Develop a timing system for the trigger on the robot in order to succinctly trigger the
button
LED Display
“Picture of Illuminated LED on Game”
The LED display consists of an eight by eight LED matrix with each pixel being
individually addressable with assistance from the LED library. Therefore, only one micro-
controller pin is required to control all 64 LEDs. The LED display is connected with two three-
pin connection ports. These connections are 5VDC, ground and the digital input pin to the micro-
controller. Each of the 64 LED can draw as much as 60 mA.
Depending on the brightness of the R, G and B of the LED, different colors will be
displayed. For example, if the brightness were 200, 0, 0, the LED panel would show only Red
whereas if the brightness were 0,0,200 the LED panel would reveal only blue light. In order to
use all functions of the RGB LEDs, the three different colors must be initialized within the
coding. For this project specifically, a white light was deemed the most visible under a black
tinted piece of Plexiglas which would be utilized to protect the LED panel.
It should also be noted that there is a single data line that contains a very timing-specific
protocol. Therefore, it is necessary that the micro-controller must read in real-time. This is
imperative as the micro-controller must vary the speed of each line of the LED panel in order to
coordinate with each different row level.
Library
Within this project, the LED panel utilizes an LED library to address specific pins of the
LED panel. Libraries are files that provide sketches that help enable the control of an LED
matrix or help read an encoder for an Arduino. Existing libraries in a sketch can be used by
choosing “Import Library” and picking from the available libraries which will insert an #insert
statement at the top of each header file in the library’s folder. By inserting these #include
statements, the statements will become public functions and constants that are now available to
the Arduino sketch that a user writes. Also, the # include statements indicate to the Arduino the
link to that library’s code with the sketch compiled by the user.
Though the LED library can provide significant assistance in programming the LED
panel to operate in a manner that coordinates with the Stacker game, many different challenges
can arise. One specific challenge includes how fast the neo pixels can be refreshed. One neo
pixel requires 24 bits which lasts for approximately 30 microseconds while the last neo pixel can
take up to 50 microseconds. However, it should be noted that the actual refresh rate cannot be
estimated like this for all cases. Instead, it depends on how the frame of the program that you
have written to coordinate with the LED array. In this project specifically, the length of the
Stacker code will negatively impact the speed of the neo pixels. In order to avoid this issue, the
neo pixel can be run in parallel.
Micro-controller
A micro-controller is a small computer that contains memory and input/output ports that
can be embedded in another device. Typically, micro-controllers support only one task or one
specific program. In the case of this project, our micro-controller contains the program that
enables the Stacker game to run. This program is stored inside the micro-controller’s memory
and does not change. Within this program, the micro-controller controls the light intensity and
timing of the LED array.
The micro-controller that controls the Stacker game was created to have the same basic
circuitry as the Arduino Uno. For this project, three Arduinos would have to be purchased to
operate both Stacker games and the robots. By creating a separate a device similar to the Arduino
Uno, there was a substantial cost savings.
ATMega328P
The ATmega series consists of micro-controllers that contain a program memory, pin
package, extended instruction set, and extensive peripheral set. The ATMega329P component is
part of this ATmega series. Today, the ATmega328P is commonly used throughout the
electronics industry as a simple, low-powered, low-cost micro-controller.
Within the Stacker project, the micro-controller cannot handle voltage levels greater than
5V. However, the power supply utilized for the Stacker game is a 9V battery. By using a power
regulator, the voltage can be limited to stay within the constrained 5V.
The robot part of this project operates by using an 18V power supply, solenoid,
MOSFET, phototransistors, several resistors, and a flyback diode. The MOSFET controls
whether the solenoid will be turned on or off. The flyback diode will eliminate flyback which is
also known as the sudden inductive spike that occurs when the supply voltage is suddenly
reduced or removed. The power supply provides the power for the solenoid to operate the button
trigger on the Stacker game.
MOSFET
Solenoid
The electromechanical solenoid used in this project contains a coil that is shaped such
that the armature moves in and out with the changing inductance. This armature used provides
enough mechanical force to trigger the button of the Stacker. The force applied to the armature is
proportional to the change in position of the armature to the change in inductance. This force
will always move the armature in the direction that increases the coil’s inductance.
Phototransistor
Figure 8: Phototransistors in Proper Placement for Being Mounted onto Stacker Plexiglas
Phototransistors are devices that change with light when more or less light shines on the
phototransistor. When more current passes through the resistor, the voltage output by the
phototransistor will be higher. In order to trigger the solenoid at the correct time, phototransistors
were placed on top of the LED panel on the Stacker game. When the Stacker LEDs flash by the
phototransistor, the voltage output to the micro-controller will change depending on how close
the LED light is to the phototransistor. In order to trigger the solenoid at the correct time, the
phototransistor will send a signal to the solenoid when the maximum voltage is output to the
micro-controller. Each level of the Stacker game contains one phototransistor to correlate with
the single trigger necessary to defeat each level. Below depicts the schematic of the
phototransistor.
To ensure that the solenoid triggers instantaneously with the maximum voltage output by
the phototransistor, a wooden arm was designed to mount all eight phototransistors directly on
top of the LED panel. Therefore, proper placement was essential for the robot to successfully
defeat the Stacker.
Conclusion
Overall, the project worked effectively as an arcade game, however, the project did not
simulate how the Stacker arcade game is actually rigged. After further research was compiled, it
was found that the Stacker arcade game allows the real arcade games to allow a one in five
hundred games to be won. This implies that during the games that were not won, the LEDs were
not properly aligned with the trigger of the game even if the human interacting with the game
was able to properly align the LEDs.
Improvements can be made for the both the Stacker arcade game and the coding of the
robot. In order for the robot to run more efficiently, the lit LEDs could be identified quicker if
the Arduino were able to run more quickly, resulting in more accurate game-play. The following
bullets outline ways of improve the robot’s reaction time.
Use a microcontroller that operates faster than an Arduino
Use a more succinct code in the Stacker code when simulating with the LEDs
Appendix
int a, b, c = 0;
int a1,b1,c1 = -99;
int a2,b2,c2 = -99;
int a3,b3,c3 = -99;
int a4,b4,c4 = -99;
int a5,b5,c5 = -99;
int a6,b6,c6 = -99;
int a7,b7,c7 = -99;
int a8,b8,c8 = -99;
int button = 0;
int i = 0;
int place = 0;//holdes place for "run"
void setup()
{
strip.begin(); //prepares NeoPixel for use
strip.show(); //initially sets all pixels to off
Serial.begin(9600);
}
//****************************************************
//START THE LOOOOOOOOOOOOP!!!!!!!!!
//****************************************************
void loop()
{
int reset = 0;
int width = 3;//determines width of run
int game = 1;//determines if game continues
if(game == 1){
//****************************************************
//row 1
//****************************************************
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,1,80,white,black);
a1 = a;
b1 = b;
c1 = c;
button = digitalRead(ButtonPin);
}
strip.setPixelColor(a1, white);
strip.setPixelColor(b1, white);
strip.setPixelColor(c1, white);
strip.show();
while(button == HIGH)
{
delay(1);
button = digitalRead(ButtonPin);
}
width = 0;
if ((a1 != -99) && (a1 != -100)){
width++;
}
if ((b1 != -99) && (b1 != -100)){
width++;
}
if ((c1 != -99) && (c1 != -100)){
width++;
}
}
//****************************************************
//row 2
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,2,75,white,black); //scrolls until button is pushed
a2 = a; //sets 2nd row placeholders equal to lit LED when button was pushed
b2 = b;
c2 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
if (((a2 == a1+8) || (a2 == b1+8) || (a2 == c1+8)) && (a2 != -99))//checks for vertical alignment
{
strip.setPixelColor(a2, white);
}//turns LED on if vertically aligned
else
{
a2 = -100;
}
if (((b2 == a1+8) || (b2 == b1+8) || (b2 == c1+8)) && (b2 != -99))
{
strip.setPixelColor(b2, white);
}
else
{
b2 = -100;
}
if (((c2 == a1+8) || (c2 == b1+8) || (c2 == c1+8)) && (c2 != -99))
{
strip.setPixelColor(c2, white);
}
else
{
c2 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}
width = 0;
if ((a2 != -99) && (a2 != -100)){
width++;
}
if ((b2 != -99) && (b2 != -100)){
width++;
}
if ((c2 != -99) && (c2 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}
//****************************************************
//row 3
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,3,70,white,black); //scrolls until button is pushed
a3 = a; //sets 2nd row placeholders equal to lit LED when button was pushed
b3 = b;
c3 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a3 == a2+8) || (a3 == b2+8) || (a3 == c2+8)) && (a3 != -99))//checks for vertical alignment
{
strip.setPixelColor(a3, white);
width++;
}//turns LED on if vertically aligned
else
{
a3 = -100;
}
if (((b3 == a2+8) || (b3 == b2+8) || (b3 == c2+8)) && (b3 != -99))
{
strip.setPixelColor(b3, white);
width++;
}
else
{
b3 = -100;
}
if (((c3 == a2+8) || (c3 == b2+8) || (c3 == c2+8)) && (c3 != -99))
{
strip.setPixelColor(c3, white);
width++;
}
else
{
c3 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}
width = 0;
if ((a3 != -99) && (a3 != -100)){
width++;
}
if ((b3 != -99) && (b3 != -100)){
width++;
}
if ((c3 != -99) && (c3 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}
//****************************************************
//row 4
//****************************************************
if (width == 3){ //makes runner only 2 wide if user still has all 3
width = 2;
}
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,4,65,white,black); //scrolls until button is pushed
a4 = a; //sets 3nd row placeholders equal to lit LED when button was pushed
b4 = b;
c4 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a4 == a3+8) || (a4 == b3+8) || (a4 == c3+8)) && (a4 != -99))//checks for vertical alignment
{
strip.setPixelColor(a4, white);
width++;
}//turns LED on if vertically aligned
else
{
a4 = -100;
}
if (((b4 == a3+8) || (b4 == b3+8) || (b4 == c3+8)) && (b4 != -99))
{
strip.setPixelColor(b4, white);
width++;
}
else
{
b4 = -100;
}
if (((c4 == a3+8) || (c4 == b3+8) || (c4 == c3+8)) && (c4 != -99))
{
strip.setPixelColor(c4, white);
width++;
}
else
{
c4 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}
width = 0;
if ((a4 != -99) && (a4 != -100)){
width++;
}
if ((b4 != -99) && (b4 != -100)){
width++;
}
if ((c4 != -99) && (c4 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}
//****************************************************
//row 5
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,5,60,white,black); //scrolls until button is pushed
a5 = a; //sets 4nd row placeholders equal to lit LED when button was pushed
b5 = b;
c5 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a5 == a4+8) || (a5 == b4+8) || (a5 == c4+8)) && (a5 != -99))//checks for vertical alignment
{
strip.setPixelColor(a5, white);
width++;
}//turns LED on if vertically aligned
else
{
a5 = -100;
}
if (((b5 == a4+8) || (b5 == b4+8) || (b5 == c4+8)) && (b5 != -99))
{
strip.setPixelColor(b5, white);
width++;
}
else
{
b5 = -100;
}
if (((c5 == a4+8) || (c5 == b4+8) || (c5 == c4+8)) && (c5 != -99))
{
strip.setPixelColor(c5, white);
width++;
}
else
{
c5 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}
width = 0;
if ((a5 != -99) && (a5 != -100)){
width++;
}
if ((b5 != -99) && (b5 != -100)){
width++;
}
if ((c5 != -99) && (c5 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}
//****************************************************
//row 6
//****************************************************
if (width == 2){
width = 1;
}
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,6,55,white,black); //scrolls until button is pushed
a6 = a; //sets 5nd row placeholders equal to lit LED when button was pushed
b6 = b;
c6 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a6 == a5+8) || (a6 == b5+8) || (a6 == c5+8)) && (a6 != -99))//checks for vertical alignment
{
strip.setPixelColor(a6, white);
width++;
}//turns LED on if vertically aligned
else
{
a6 = -100;
}
if (((b6 == a5+8) || (b6 == b5+8) || (b6 == c5+8)) && (b6 != -99))
{
strip.setPixelColor(b6, white);
width++;
}
else
{
b6 = -100;
}
if (((c6 == a5+8) || (c6 == b5+8) || (c6 == c5+8)) && (c6 != -99))
{
strip.setPixelColor(c6, white);
width++;
}
else
{
c6 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}
width = 0;
if ((a6 != -99) && (a6 != -100)){
width++;
}
if ((b6 != -99) && (b6 != -100)){
width++;
}
if ((c6 != -99) && (c6 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}
//****************************************************
//row 7
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,7,50,white,black); //scrolls until button is pushed
a7 = a; //sets 6nd row placeholders equal to lit LED when button was pushed
b7 = b;
c7 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a7 == a6+8) || (a7 == b6+8) || (a7 == c6+8)) && (a7 != -99))//checks for vertical alignment
{
strip.setPixelColor(a7, white);
width++;
}//turns LED on if vertically aligned
else
{
a7 = -100;
}
if (((b7 == a6+8) || (b7 == b6+8) || (b7 == c6+8)) && (b7 != -99))
{
strip.setPixelColor(b7, white);
width++;
}
else
{
b7 = -100;
}
if (((c7 == a6+8) || (c7 == b6+8) || (c7 == c6+8)) && (c7 != -99))
{
strip.setPixelColor(c7, white);
width++;
}
else
{
c7 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}
width = 0;
if ((a7 != -99) && (a7 != -100)){
width++;
}
if ((b7 != -99) && (b7 != -100)){
width++;
}
if ((c7 != -99) && (c7 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}
//****************************************************
//row 8
//****************************************************
if (game == 1){
button = digitalRead(ButtonPin);
while(button == LOW)
{
run(width,8,45,white,black); //scrolls until button is pushed
a8 = a; //sets 7nd row placeholders equal to lit LED when button was pushed
b8 = b;
c8 = c;
button = digitalRead(ButtonPin);
}
button = digitalRead(ButtonPin);
width = 0;
if (((a8 == a7+8) || (a8 == b7+8) || (a8 == c7+8)) && (a8 != -99))//checks for vertical alignment
{
strip.setPixelColor(a8, white);
width++;
}//turns LED on if vertically aligned
else
{
a8 = -100;
}
if (((b8 == a7+8) || (b8 == b7+8) || (b8 == c7+8)) && (b8 != -99))
{
strip.setPixelColor(b8, white);
width++;
}
else
{
b8 = -100;
}
if (((c8 == a7+8) || (c8 == b7+8) || (c8 == c7+8)) && (c8 != -99))
{
strip.setPixelColor(c8, white);
width++;
}
else
{
c8 = -100;
}
strip.show();
while(button == HIGH)
{
delay(1);//pauses program until button is released
button = digitalRead(ButtonPin);
}
width = 0;
if ((a8 != -99) && (a8 != -100)){
width++;
}
if ((b8 != -99) && (b8 != -100)){
width++;
}
if ((c8 != -99) && (c8 != -100)){
width++;
}
if (width == 0) {
game = 0;
}
else {
game = 1;
}
}
//****************************************************
//end of row check
//****************************************************
//****************************************************
//FUNTIONSSSSSSSSSSS
//****************************************************
//****************************************************
//scrolls 1 lights at a time
//****************************************************
void run (int width, int line, int Speed, long color1, long color2)
{
if (width == 1)
{
a = 0;
b = -99;
c = -99;
if (place == 14){
place = 1;
}
else {
place++;
}
if (place == 1){
a = (line-1)*8+0;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 2){
a = (line-1)*8+1;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 3){
a = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 4){
a = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 5){
a = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 6){
a = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 7){
a = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 8){
a = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 9){
a = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 10){
a = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 11){
a = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 12){
a = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 13){
a = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 14){
a = (line-1)*8+1;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}//closes place 14 if
}//closes width 1 if
//*************************************************************************************
//scrolls 2 lights at a time
//*************************************************************************************
if (width == 2)
{
a = 0;
b = 0;
c = -99;
if (place == 16){
place = 1;
}
else {
place++;
}
if (place == 1){
a = (line-1)*8+0;
b = -99;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 2){
a = (line-1)*8+0;
b = (line-1)*8+1;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 3){
a = (line-1)*8+1;
b = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 4){
a = (line-1)*8+2;
b = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 5){
a = (line-1)*8+3;
b = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 6){
a = (line-1)*8+4;
b = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 7){
a = (line-1)*8+5;
b = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 8){
a = (line-1)*8+6;
b = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 9){
a = (line-1)*8+7;
b = -99;
strip.setPixelColor(a,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.show();
}
if (place == 10){
a = (line-1)*8+6;
b = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 11){
a = (line-1)*8+5;
b = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 12){
a = (line-1)*8+4;
b = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 13){
a = (line-1)*8+3;
b = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 14){
a = (line-1)*8+2;
b = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 15){
a = (line-1)*8+1;
b = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 16){
a = (line-1)*8+0;
b = (line-1)*8+1;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}//closes place 16 if
}//closes width 2 if
//*************************************************************************************
//scrolls 3 lights at a time
//*************************************************************************************
if (width == 3)
{
a = 0;
b = 0;
c = 0;
if (place == 14){
place = 1;
}
else {
place++;
}
if (place == 1){
a = (line-1)*8+0;
b = (line-1)*8+1;
c = -99;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 2){
a = (line-1)*8+0;
b = (line-1)*8+1;
c = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 3){
a = (line-1)*8+1;
b = (line-1)*8+2;
c = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 4){
a = (line-1)*8+2;
b = (line-1)*8+3;
c = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 5){
a = (line-1)*8+3;
b = (line-1)*8+4;
c = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 6){
a = (line-1)*8+4;
b = (line-1)*8+5;
c = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 7){
a = (line-1)*8+5;
b = (line-1)*8+6;
c = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 8){
a = (line-1)*8+6;
b = (line-1)*8+7;
c = -99;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.show();
}
if (place == 9){
a = (line-1)*8+5;
b = (line-1)*8+6;
c = (line-1)*8+7;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 10){
a = (line-1)*8+4;
b = (line-1)*8+5;
c = (line-1)*8+6;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 11){
a = (line-1)*8+3;
b = (line-1)*8+4;
c = (line-1)*8+5;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 12){
a = (line-1)*8+2;
b = (line-1)*8+3;
c = (line-1)*8+4;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 13){
a = (line-1)*8+1;
b = (line-1)*8+2;
c = (line-1)*8+3;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}
if (place == 14){
a = (line-1)*8+0;
b = (line-1)*8+1;
c = (line-1)*8+2;
strip.setPixelColor(a,color1);
strip.setPixelColor(b,color1);
strip.setPixelColor(c,color1);
strip.show();
delay(Speed);
strip.setPixelColor(a,color2);
strip.setPixelColor(b,color2);
strip.setPixelColor(c,color2);
strip.show();
}//closes place 14 if
}//closes width 3 if
}//closes void run
Robot Code
int push = 0; // placeholder to see if the button has been pushed yet for each level of the stacker game
int Solenoid = 10; // designating digital pin 10 to drive the solenoid
int light1 = 2; // designating digital pin 2 for first phototransistor
int light2 = 3; // designating digital pin 3 for second phototransistor
int light3 = 4; // designating digital pin 4 for third phototransistor
int light4 = 5; // designating digital pin 5 for fourth phototransistor
int light5 = 6; // designating digital pin 6 for fifth phototransistor
int light6 = 7; // designating digital pin 7 for sixth phototransistor
int light7 = 8; // designating digital pin 8 for seventh phototransistor
int light8 = 9; // designating digital pin 9 for eighth phototransistor
void setup()
{
Serial.begin(9600); // begin serial communication
pinMode(Solenoid, OUTPUT);
}// closes void setup
void loop()
{
int read1 = digitalRead(light1);
Serial.println(read1);
while(push == 0) // while push = 0, run the following while loop
{
if (digitalRead(light1) == LOW) // run code within if statement if digital pin 2 is high
{
digitalWrite(Solenoid,HIGH); // solenoid is turned on
delay(500);
digitalWrite(Solenoid, LOW);
delay(1500);
push = push + 1;
} // closes if
} // closes while
1
Acknowledgements
The team would like to thank Dr. Sommer and Mike Robinson for the help and guidance
through the semester. This project and class introduced us to many of the concepts we have
used in this project and hopefully we will continue to use afterwards.
2
Contents
Introduction .................................................................................................................................................. 4
Concept Development .................................................................................................................................. 4
Detailed Design ............................................................................................................................................. 5
Mechanical ................................................................................................................................................ 6
Electrical Design ........................................................................................................................................ 8
MOSFET transistor ................................................................................................................................ 8
7805 Voltage Regulator ........................................................................................................................ 9
Motor Drivers ........................................................................................................................................ 9
PS2 Controller ..................................................................................................................................... 10
Complete Circuit Diagram………………………………………………………………………………………………………………………11
3
Introduction
For the final project, Team 8 wanted to develop a tank that would be able to drive freely and
shoot a gun mounted above the chassis. In developing this tank, we realized there were many
goals we would have to achieve. One goal was to develop a running chassis for the tank which
would include interfacing DC drive motors on the front of the chassis to make it mobile. The
second goal was to develop a cannon that would be placed above the tank and be able to pivot
up and down.
Concept Development
The concept was developed by looking at two specific tanks used during World War II. This was
inspired by two tanks developed during World War II. The M2 Stuart was the inspiration for the
chassis used in the development of the tank which was used by the Allied Forces. The M2
Stuart was a light tank developed before World War II in 1935. It was used during the Battle of
Guadalcanal in 1942 and was used by the British in during the North African front of World War
II.
The Hummel was another tank used by the Axis Forces from 1943 to the end of World War II.
The Hummel was an inspiration for the gun of the tank robot, which had a 15 centimeter
Howitzer. A Howitzer is an artillery piece which was specifically used in the Hummel as mobile
artillery support.
The Arduino needed to also be interfaced wirelessly. The team decided to use a PS2 controller
with the Arduino to make the tank move and pivot. The PS2 controller is wireless and can be
programmed using a predefined library resource.
4
Detailed Design
After a large team effort, the tank robot was successfully created and has gone through
rigorous testing. Using the chassis foundation, we built a mobile artillery piece to be mounted
above the chassis.
Bill of Materials
Practice Golf Balls 12.99 1
Squash Balls 9.99 1
1/2" Sch. 40 Elbow 0.25 2
1-1/2" Sch. 40 Coupler 0.77 1
1-1/2" x 2' Sch. 40 Pipe 2.90 2
1-1/2" Sch. 40 Cap 0.96 1
Air Pressure Guage 7.99 1
3/4" Auto Sprinkler Valve 12.58 1
3/4" x 1/2" Sch. 40 Bushing 0.41 2
1/4" Air Intake Valve 4.99 1
1-1/2" x 1/2" Sch. 40 Reducer 1.26 2
1/4" Acrylic n/a 1.5' x 2'
1/8" Acrylic n/a 6" x 8"
Air Compressor n/a 1
3/4" Wood Dowel n/a 1
3/4" Bolt n/a 1
Pololu Motor Driver n/a 1
7805 Voltage Regulator n/a 1
100 Kohm Resistor n/a 2
330 Ohm Resistor n/a 1
LED n/a 1
PS2 Controller/Reciever n/a 1
Arduino Uno n/a 1
Terminal Block n/a 1
Proto Board n/a 1
Tank Chassis n/a 1
Mosfet n/a 2
Switch n/a 1
7.2 Volt Battery n/a 1
9V Battery n/a 1
AA Battery n/a 2
Hinge n/a 2
Wiring n/a n/a
5
Mechanical Design
The mechanical design of the entire tank robot falls under two main categories which are the
chassis and the gun. The chassis is a pre-owned 1/6th scale replica of the M2 Stuart chassis. We
used the motors provided from the chassis to run the vehicle. The DC drive motors run from
the front of the vehicle on the front gears in order for the tracks to turn at a functional rate.
The tracks are then carried on from the front to the back of the chassis in order for it to be
functional in all terrain. The power is enough to make the motors drive 1 MPH. The team used
Traxxas speed controllers provided by the lab in order to program the motors through the
Arduino Uno.
After the chassis was built, the team moved onto the upper base of the tank. The tank needed
a cover for all of the wiring and a base for the gun foundation to be placed on. As a result of
this necessity, an acrylic sheet was cut to cover the entire base of the chassis by using the Shop
in the basement of the Reber Building. The next step was building a cover for the front DC drive
motors. This was built in the same fashion as the acrylic sheet for the foundation. A hinge was
placed on the ends of the cover and attached to the front of the acrylic foundation sheet. This
allowed an ammunition pocket to be introduced to our design for the practice golf balls and
squash balls we used.
6
The next main step in building this tank was for the team to build a foundational pivot base for
the gun. This was built by using the shop in the Reber Building basement again to cut out
pieces of plywood to act as a foundational base. This along with holes on the two ends of the
plywood allowed the base to be used as a pivot point. After building the foundational pivot
base, a pivot point for the gun had to be made. This was made by using a gearbox attached to a
DC stepper motor for the pivot point. After attaching the gearbox to the pivot base, a threaded
hex rod was press fit into the gearbox to attain a pivot for the gun itself. The other side was
attached to a large hex bolt that threaded into the connector on the side of the gun. On the
gun itself, there is a solenoid that is attached to the sprinkler valve to release the air. This is
crucial for releasing the air and firing the gun. A pump is also connected to the air tank for
additional air pressure. The gun is the most crucial part of the tank because that is what a tank
is in essence.
7
Electrical Design
The tank would not be possible without the brains, which is the Arduino Uno microcontroller.
This piece of hardware was used to activate the switches for multiple transistors described
below. It was also used for the PS2 controller interfacing in order for the tank to be controlled
wirelessly. This Arduino Uno was placed underneath the layer of acrylic and mounted inside of
the chassis.
MOSFET transistor
The MOSFET transistors inside of the circuit built were used to control whether the pump and
solenoid were triggered. These transistors were made so that if there is no voltage supplied
from the Arduino digital input, the gate would be opened causing no voltage to travel across
from the battery to the pump or solenoid. This causes the solenoid or pump to be controlled
digitally and wirelessly using the Arduino interfaced with the PS2 controller. These are shown
in the detailed circuit diagram.
8
7805 Voltage Regulator
The Voltage regulator used in this application was hooked up to a 9 Volt battery source. This
voltage regulator takes the 9 Volts from the battery source and cuts it down to 5 Volts in order
to power the Arduino without USB assistance.
Motor Drivers
There were two different components used in driving the motor itself. The first motor driver
used to drive the main motors of the tank was the Traxxas XL-1 speed controller. This was
connected to the DC drive motors and the Arduino allowing control of drive speeds through
Arduino programming. They were connected to the positive and negative terminals of the
motor and the NiCd car battery used for the project. This allowed PWM control between the
speed controllers and Drive motors.
The second motor driver used was the Pololu Dual Motor Driver. This was used to drive the
pivot DC motor which was responsible for changing the elevation and depression of the
mounted gun. In retrospect, the Pololu Driver could have been used for all of the motors in the
tank because of its 3 Amp limit.
9
PS2 Controller
The PS2 controller is wireless and has a RF Transmitter and Receiver that can be hooked up
straight to the Arduino. This is done by using the external library for PS2 commands from Bill
Porter’s PS2 controller Arduino Library. The PS2 RF receiver uses 3.3 Volt logic rather than 5
volt logic. By using an Arduino 3.3 Volt output, the RF Receiver was wired to the tank and
microcontroller.
10
Complete Circuit Diagram
Drive Motors
7.2V
Elevation/
Depression
Valve
Pump
Testing
Testing started with the individual parts of the project and branched out to the complete build.
11
After testing these commands, the team noticed slight interference between the transmitter
and receiver. This was caused by the analog button being activated. To be more descriptive,
the analog button causes the analog sticks in the middle of the controller to be activated and
four different analog inputs to be read. The larger amount of information to be transmitted to
the receiver caused slight interference and misfire of buttons not pressed.
12
enable pin to control speed and counterclockwise and clockwise pins for direction. After wiring
the circuit, the motor was set to a 50 Percent duty cycle where it was sufficiently elevated and
depressed for our application.
Final Testing
After all of the components were combined, the overall tank was tested in the computer lab as
a dry shot with no projectiles. The drive motors, elevation and depression motor, and the
solenoid and pump had been tested separately. The Arduino code had been programmed in
order for every button to handle a certain command allowing full control of the tank. After
using a digital input to test the solenoid and pump connected to a MOSFET transistor, the team
started to activate the pump to fill the air tank. After filling the tank up to 100 psi, the main
goal was to elevate the gun to a satisfying level and releasing the air using the solenoid. After
moving the tank using the drive motors to test the speed and reliability, the pump was turned
off and the solenoid was activated. After activating the solenoid, the air released and the test
was successful. The test was reproduced using live ammunition, a practice golf ball, and was
successful once again.
13
Conclusion
During this course, many of the principles and concepts learned through class and lab helped
the team be able to achieve all that was necessary for our success in this project. After many
trials and tribulations, the tank itself ran along with the execution of shooting the gun. This was
our main goal starting this project, and it has been achieved.
If the team had more time to work on the project, an additional servo could have been placed
below the pivot base in order to rotate the gun to the left and right for additional degrees of
freedom for the tank itself. This would have allowed increased maneuverability for the gun and
the function of the tank itself.
14
Appendix A: Arduino Code
#include <PS2X_lib.h>
PS2X ps2x; // This designates the function from the library to begin
int solenoid = 8; // The solenoid variable relates to the digital input the solenoid is
connected to
int pump = 7; // The pump variable relates to the digital input the pump is connected to
int enable= 3; // The enable pin is for the Pololu motor driver and the pivot motor
int up = 2; // The up pin designates the digital pin relating to elevating the pivot motor
int down = 4; // The down pin designates the digital pin relating to depressing the pivot
motor
int error = 0; // This error variable relates to whether or not the PS2 controller is read
byte vibrate = 0; // This is for vibrate commands for the controller if necessary
int motor1 = 5; // This is for the left drive motor of the tank
int motor2 = 6; // This is for the right drive motor of the tank
int triangle = 1; // This is a variable for prototyping drive speed changes in the drive motors
int select = 0; // This variable turns on and off access to all functions of the tank
int motorspeedf = 0; // This is the drive speed prototyping PWM values for testing
int motorspeedr = 0; // This is the drive speed prototyping PWM values for testing
int start= 0; // When this variable is 1, the functions of the tank are accessible
int time= 0; // For prototyping purposes
void setup(){
15
pinMode(down, OUTPUT);
error = ps2x.config_gamepad(9,11,10,12, true, true); //setup pins and settings:
GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error
void loop(){
ps2x.read_gamepad(false, vibrate);
// This command turns off vibrate
if(ps2x.Button(PSB_START)) // This function makes all other functions accessible at the press
of the start button
{
start= 1;
}
if(start == 1)
{
if(ps2x.ButtonPressed(PSB_R1))
{
digitalWrite(pump, HIGH);
// This makes the pump turn on with the press of the R1 Button
}
if(ps2x.ButtonPressed(PSB_L1))
{
16
digitalWrite(pump, LOW);
digitalWrite(solenoid, LOW);
// This makes the pump and solenoid turn off with the press of the L1 Button
}
if(ps2x.ButtonPressed(PSB_R2))
{
digitalWrite(solenoid, HIGH);
// This activates the solenoid and releases the air at the press of the R2 Button
}
if(ps2x.Button(PSB_PAD_RIGHT))
{
Serial.print("Right held this hard: ");
analogWrite(motor1, 95);
analogWrite(motor2, 250);
// This function acts as a Right turn driving function if the Right button is pressed on the
dpad
}
if(ps2x.Button(PSB_PAD_LEFT))
17
{
Serial.print("LEFT held this hard: ");
analogWrite(motor1, 250);
analogWrite(motor2, 95);
// This function acts as a Left turn driving function if the Left button is pressed on the dpad
}
if(ps2x.Button(PSB_PAD_DOWN))
{
Serial.print("DOWN held this hard: ");
analogWrite(motor1, 250);
analogWrite(motor2, 250);
// This function acts as a Reverse driving function if the Down button is pressed on the dpad
}
18
if(ps2x.ButtonPressed(PSB_GREEN)){
analogWrite(enable, 54);
digitalWrite(up, HIGH);
digitalWrite(down, LOW);
// This function makes the pivot motor elevate at the designated speed with the press of the
Triangle button
}
if(ps2x.ButtonPressed(PSB_BLUE)){
analogWrite(enable, 96);
digitalWrite(up, LOW);
digitalWrite(down, HIGH);
// This function makes the pivot motor depress at the designated speed with the press of the X
button
}
}
19
An Autonomous Vehicle
for Collecting Small Objects
List of Figures
Figure 1. The several major components of our project. The compass and Arduino are hidden
from view. ....................................................................................................................................... 1
Figure 2. The assembled Zumo robot, which our project is built upon [1]. ................................... 2
Figure 3. Rotation of the car, achieved by motor control. .............................................................. 3
Figure 4. The Sharp GP2Y0A21YK0F infrared sensor [2]. ........................................................... 4
Figure 5. Representation of the workings of a typical IR sensor [3]. ............................................. 4
Figure 6. The IR sensor attached to the back of the Zumo robot.................................................... 5
Figure 7. A schematic of the switch used to identify when a Lego is within reach. ...................... 5
Figure 8. On the left, a side view of the switch, where you can see how the switch is triggered.
On the right, an overall view of the switch, which was attached to the front of the car. ................ 6
Figure 9. Collector subsystem attached to Zumo robot. ................................................................. 7
Figure 10. A single arm and grip pad used to pick up small objects. ............................................. 7
Figure 11. DSM44 servos with balsa arms, attached to the same balsa piece. ............................... 8
Figure 12. Arms attached to servos, and back servo to lift the system. .......................................... 8
Figure 13. Top-down view of the bin used to hold Lego blocks. ................................................... 9
Figure 14. Process of collecting a Lego block. ............................................................................. 10
Figure 15. Pins used for connections with Arduino [7] and the Zumo’s battery connections [1].
Components labeled with * designate connections made directly through the Zumo shield (no
physical wires are connected to those pins). ................................................................................. 11
Figure 16. Autonomous robot collecting multiple Lego blocks. .................................................. 12
i
Introduction
Our goal for this project was to create an autonomous vehicle that could find and collect
small objects. The motivation behind this chosen topic was the relevance of autonomous drones
in a modern society, not only for military use, but potentially consumer use in the future.
However, we wanted to focus on the basics of this topic to maximize learning and build
something that was cheaper than most drones. To achieve our goal, we made a small,
autonomous car that found nearby Legos, picked the Legos up, and dropped them in an attached
compartment. Our car first rotated in a full circle while searching for the closest object. Then, it
rotated to the closest object’s position. After that, it moved forward until it reached the Lego,
where it stopped. Once stopped, the car picked up the Lego, lifted it over an attached bin, and
dropped the Lego. Then, the process repeated as the car found and collected the next closest
object.
Several components were used in the collection process, labeled in Figure 1. Our car was
built on a Zumo robot and controlled by an Arduino Uno. The Zumo robot included motored
wheels for movement and an internal compass for determining direction. Additional components
used were an IR sensor, a switch made of wire and a resistor, servo motors attached to Balsa
wood arms, a platform to place the collection device on the Arduino, and a bin to hold the Legos.
Servo-Powered
Arms
Switch
Motor-Powered
Wheels IR Sensor
Figure 1. The several major components of our project. The compass and Arduino are hidden from view.
The remainder of this report goes into more detail on our project. First, we include more
detail on the components of the project and their use. This includes sections for the Zumo robot
(including motor control and the compass), the IR sensor, the wire and resistor switch, and the
collection device. After that, we discuss how the components operate together and evaluate the
performance of the autonomous robot. Following that, the appendices include a bill of materials,
our Arduino code, and relevant datasheets.
Page 1
Zumo Robot
Our project was built upon an existing product—the Zumo robot by Pololu [1], shown in
Figure 2. The Zumo robot is a small car that includes two motors to control movement. The
Zumo shield is used to control a variety of the Zumo’s functions, including the motors. To send
signals to the shield and the robot, an Arduino can be placed on top of the Zumo shield. For our
project, we used two major functions of the Zumo robot: the motor control system and the
internal compass.
Figure 2. The assembled Zumo robot, which our project is built upon [1].
The Zumo robot includes two motors that are controlled by built in motor drivers. These
motor drivers use Arduino pins 7 and 8 as inputs to control the direction of the two motors, and
they use Arduino PWM pins 9 and 10 to control the speed of the two motors. Although these
motors could be controlled by programming directly to pins 7-10, we used the Zumo Motors
library to control the motors. After the Zumo Motor library was initialized, the left motor was
controlled with the command motors.setLeftSpeed(+/- speed) , and the right motor
was controlled with the command motors.setRightSpeed(+/- speed). In the
commands’ parenthesis, a positive speed between 1 and 400 moved the motor forward, and a
negative speed between -1 and -400 moved the motor backward. An input of 0 stopped the
motors.
We used the motor control for rotation and forward movement. Initially, our car rotated
in a full circle while scanning the area for nearby objects, as shown in Figure 3. For this
function, we set one motor to move forward while the other moved backward. After, the car
needed to rotate again, this time to get to the position where an object was sensed. Then, the car
needed to move forward until it reached the Lego. For forward movement, we set both motors to
the same direction and speed.
Page 2
Figure 3. Rotation of the car, achieved by motor control.
Especially during rotation, we wanted our car to move slowly so that many sensor
readings could be taken. However, if we set the motor speeds too low, we found that they would
not move. Furthermore, if we set the motor speeds high enough that they could move but still
very low, we found that our car would slip while rotating, which we did not want. Based on
these trade-offs, we chose to use motor speeds of 85 out of 400 (about 21%) for rotation and 150
out of 400 (about 37%) for forward motion.
The Zumo shield includes a built-in compass, which we used to find an absolute angular
position. To find absolute position, the compass uses a 3-axis magnetometer [1]. Before the
compass could be used, it needed to be calibrated. We used the Calibrate example file from the
LSM303 Arduino library. After running the code and opening the serial monitor, we rotated our
car in a full circle where we planned to run the Lego collection. While we rotated the car, the
Calibrate file kept measurements of the minimum and maximum compass values with respect to
each axis. These minimum and maximum calibration values were used at the beginning of our
project’s Arduino code. For accurate readings, we found the compass needed to be calibrated
each day.
To use the compass, we used two libraries: Wire and LSM303. Both the wire and the
compass were initialized, and then we took readings using commands similar to ones found in
example files. To read the compass, the command compass.read() was used. Then, to
extract the position, the command compass.heading((LSM303::vector){0,-1,0})
was used, where the heading was taken with respect to the {0,-1,0} vector. In this manner,
we were able to keep track of the angular position of the car.
Many aspects of our project relied on rotating to a target position. For instance, when the
car initially rotated in search of nearby Legos, the car needed to end at the same position it
began. Coding this section was fairly straightforward. As the car rotated, it constantly took
compass readings. When the compass reading was within the set tolerance of the initial compass
reading, the car stopped. Another use of rotating to a target position is when the car rotated to
the position of the nearby object. For this part of the code, we first evaluated whether it was
Page 3
more efficient to rotate clockwise or counterclockwise. Then, we could rotate to the target in the
same manner as the initial rotation.
IR Sensor
There were several options that could have been used to detect object location, such as
ultrasound-based sensors and physical devices. An infrared (IR) sensor was ultimately chosen,
as most are compact and cheap devices that are easy to implement in our system. As shown in
Figure 4, most modern IR sensors have several different components. The emitter and detectors
are the components with lenses, and are the most important parts of the sensor.
The sensor chosen for implementation was the Sharp GP2Y0A21YK0F infrared sensor,
which is shown in Figure 4. The sensor has a range of approximately 10 to 80 cm, and outputs
an analog voltage. It is suitable to be powered by the Arduino board, as it requires a 4.5 to 5.5
Page 4
volt supply, and 30 milliamps [4]. Figure 6 shows the sensor attached to the back of the vehicle.
The sensor’s location on the back of the vehicle was chosen as to avoid measurement error from
the switch on the front of the vehicle, which is covered in the next section.
For our project, the output of the sensor was connected to analog pin 0 of the Arduino
board. Using a simple analogRead() command for that pin, the sensor’s output was read.
As we only required relative measurements to find the closest object, we compared the output
signal at each position against the output signal at other positions. At the initial position, the
sensor’s output was recorded, as well as that location. During the full 360O rotation of the robot,
the Arduino would constantly receive inputs from the sensor. If, at any point, the sensor’s output
was greater than the previously found maximum output, the variable associated with this new
maximum output, as well as its heading, replaced the previous values. Once the full rotation was
complete, the vehicle was rotated to face the location of the maximum sensor output value. As
the sensor was on the back of the vehicle, the target heading was 180O plus the location the
sensor found. The number found from this was converted into a value between 0 O and 360O in
order to rotate properly, using the function AbsoluteHeading().
Switch
To let the car know when it reached a Lego, a switch was used. This switch was made
from wire and a 330 Ω resistor. The schematic for the switch is shown in Figure 7.
330
+5 V Signal
(Pin 13) (Pin 12) (GND)
Figure 7. A schematic of the switch used to identify when a Lego is within reach.
Page 5
Before the car reached a Lego, the switch was open. This prevented current from
traveling from the voltage source to ground. Because there was no current flow, the signal’s
voltage was +5 V. When the car reached the Lego, the force from the car moving towards the
Lego closed the switch and a connection from +5 V to ground was made. Because current
flowed when the switch was closed, the voltage dropped over the resistor, and the signal’s
voltage changed to 0 V. By keeping track of the signal’s voltage, we kept track of when the car
reached a Lego.
For the car’s design, the switch was positioned a few inches in front of the car so that
when triggered, the arms were within reach of picking the Lego up. For the switch, one piece of
wire was soldered to a resistor. One end of this wire was connected to Arduino pin 13, which
was used as a +5 V voltage source; the other end of this wire was connected to Arduino pin 12,
which was a digital input and sensed the switch’s position. Shown in Figure 8, the middle of this
wire was curled into a loop, and a section of this loop had exposed wire. The end of the ground
wire then fit into this loop. When the car ran into a Lego, the ground wire was forced backward,
hitting the exposed wire and closing the switch.
signal
+5 V
0V
Figure 8. On the left, a side view of the switch, where you can see how the switch is triggered. On the right, an
overall view of the switch, which was attached to the front of the car.
One of the biggest advantages of the switch design is that it is simple. Only inexpensive
materials (wire and a resistor) were used in its construction, and only digital pins were required
to run it. One of the biggest challenges associated with the switch was its sensitivity. Although
we wanted to avoid false readings, we needed the switch to be sensitive enough that it was
triggered when the car hit a Lego, instead of pushing the Lego along with the car. We found that
breaking the ground wire in two and soldering the parts together, as shown in Figure 8, increased
the sensitivity of the switch more than simply bending the wire. Still, making sure a Lego would
trigger the switch was a main concern, and we needed to make sure the switch was aligned
properly before running the program. Overall, the switch was a successful way of sensing a
Lego and triggering the collection process.
Collector Subsystem
In order to collect the objects, we required a system that could physically grab and lift a
Lego block, and then store multiple blocks on the body of the Zumo robot. A three servo system
with balsa wood arms and grip pads was used to pick up the Lego block. A balsa wood bin was
used to store collected objects. Both of these were attached to a balsa wood platform, which was
Page 6
secured to the Zumo robot. Figure 9 shows this subsystem assembled and attached to the
autonomous vehicle.
In order to grip the Lego blocks, arms were designed to attach to two servo motors, and
reach the ground. The arms, one of which is shown in Figure 10, had pads attached to the ends,
as it gave more contact area between the block and the arms. Rectangular pieces were attached
to the pads to prevent the block from slipping out of the grips while moving upward. Balsa
wood was chosen for these arms and pads, as it is light, which was necessary for the small servos
used. In addition, Balsa wood is fairly cheap, which kept the overall cost of the project down.
Cyanoacrylate glue was used to connect joints and to attach the balsa wood to the plastic
replaceable pieces of the servos, preventing permanent damage.
Figure 10. A single arm and grip pad used to pick up small objects.
Page 7
The two servos used for each arm were chosen to be DSM44 for their small size and are
shown in Figure 11. These servos require 4.8-6.0 volts to operate, which means the Arduino
would not be an ideal source to use for power [5]. Instead, the batteries used to power the car
were also used to power the servos. The Zumo robot’s shield had junctures to draw this power.
The DSM44 servos were taped onto a balsa wood piece in order to avoid damage to the servo
casing. An HTX900, another small servo, was used to lift the gripping arms and servos, as
shown in Figure 12.
Figure 11. DSM44 servos with balsa arms, attached to the same balsa piece.
Back Servo
Arm
Arm Servos
Figure 12. Arms attached to servos, and back servo to lift the system.
A storage bin was made of balsa wood to hold the Legos collected, as shown in Figure
13. Approximately four Lego blocks of the size shown in the picture could be held in the bin.
The bin was glued to a balsa wood platform, which rested on the Ardunio board and Zumo robot
securely, while not being glued to the system. The servo system was also glued to this,
completing the system shown in Figure 9.
Page 8
Figure 13. Top-down view of the bin used to hold Lego blocks.
Arduino has a built in servo control library that normally makes controlling multiple
servos an easy task. However, this library removes the capability to use pulse width modulation
on digital pins 9 and 10 [6]. As previously mentioned, the Zumo motors library uses these pins
for controlling the direction and speed of the motors. The overlap in use of these pins correlates
to both libraries making use of Timer 1, which is similar to a clock. As a result, an alternative
code had to be used to control the servo motors. The alternate code had to use Timer 2 to control
the servos, as outlined by the makers of the Zumo Robot. They had previously designed a servo
control method using Timer 2, which we implemented into our code [1].
The process for collecting the Lego was implemented using a function PickUp() after
the switch was triggered. Figure 14 shows the steps involved. First, the back servo moved
down, and placed the arms on the ground (Figure 14a). The arm servos would slowly close in on
the block, in a slow manner to avoid the block being pushed out of the proper position (Figure
14b). After gripping the Lego tightly, the back servo lifted the arms into an upright position
(Figure 14c). The block was then released, and dropped into the storage bin (Figure 14d). This
process required us to determine the proper limiting angles to set the servos at, such that the
object would be held tightly, but the servos would not cause the arms to crash together in case
the block was missed altogether.
Page 9
Figure 14a. Back servo rotates down. Figure 14b. Arm servos clamp Lego.
Figure 14c. Back servo lifts arms and Lego. Figure 14d. Arm servos release Lego into bin.
Page 10
Figure 15. Pins used for connections with Arduino [7] and the Zumo’s battery connections [1]. Components labeled
with * designate connections made directly through the Zumo shield (no physical wires are connected to those pins).
Page 11
Figure 16a. Search Figure 16b. Rotate to position Figure 16c. Move to target
Figure 16d. Grip Lego Figure 16e. Lift Lego Figure 16f. Store Lego
Once the Lego block was reached, it forced the switch to close, sending a signal to the
Arduino to stop the motors. The collection subsystem routine was then used to pick up the Lego
block and drop it into the storage bin. The entire process repeated until all the Lego blocks in
range were collected. As the robot was not programmed to stop collecting, the user needed to
turn it off manually.
The autonomous vehicle performed as designed, and, as show in Figure 16, was
successful in collecting multiple Lego blocks. This was tested multiple times to a high success
rate. However, there were a few minor flaws during a limited number of tests. The compass
required calibration nearly every day and every different location it was used. This was a
problem we did not realize at first, and caused several issues with the performance of the vehicle
in earlier tests. In addition, the switch also needed to be physically fixed if it was knocked out of
place. This never occurred during tests but occasionally happened while we were handling the
robot.
In one test, when rotating to point to the closest Lego block, the robot spun around a full
360 degrees before pointing to the object. This was caused by the robot not sending the data
quickly enough to the Arduino board, and the robot missed the angle it was supposed to stop at.
Giving a larger tolerance to the target angle was not an option, as the switch would not be
triggered properly. The motor was set to a lower rotation speed, in order to minimize the chance
of missing a target angle. The issue is uncommon, and only affects the speed at which the robot
picks up objects, and not how well it does so.
Page 12
The IR sensor used was cheap, and met our needs, but also had a very limited range. As
a result, the objects to be collected could only be placed within a short distance of the vehicle.
This was a problem that was best solved by limiting operation of the robot to the range of the
sensor. Another solution would be to replace the IR sensor with a more expensive and higher
quality one, though this would be unnecessary for the learning experience we sought.
We feel that our project was an incredible learning experience about the operation of
autonomous robots, and we recommend this or a similar project as starting point for engineers to
learn about this topic. The robot built is representative of more advanced autonomous robots that
have become more relevant recently. The vehicle’s performance was successful and consistent
with our expectations.
References
[1] Pololu, “Pololu Zumo Shield for Arduino User’s Guide,”
http://www.pololu.com/docs/0J57/all (Pololu Corporation, 2013).
[2] Sharp, “GP2Y0A21YK/GP2Y0D21YK: General Purpose Type Distance Measuring
Sensors,” https://www.sparkfun.com/products/242 (SHARP Corporation).
[3] Acroname Robotics, “Sharp IR Rangers Information,” http://www.acroname.com/robotics/
info/articles/sharp/sharp.html (Acroname, Inc., March 2011).
[4] Sharp, “GP2Y0A21YK0F: Distance Measuring Sensor Unit Measuring Distance,”
http://www.sharpsma.com/webfm_send/1489 (SHARP Corporation, December 2006).
[5] Power HD, “Digital Servo, Model DSM44,”
http://www.robotgear.com.au/Cache/Files/Files/186_DSM44.pdf.
[6] Arduino, “Servo library,” http://arduino.cc/en/reference/servo.
[7] Arduino, “Arduino Uno,” http://arduino.cc/en/Main/arduinoBoardUno.
Page 13
Appendix A – Bill of Materials
Note: This cost reflects building this project from scratch. Most materials we used were already
available in the mechatronics lab.
Page 14
Appendix B – Arduino Code
// Donald Docimo and Michelle Kehs
// ME445 Final Project
// Set-up of Libraries
#include <Wire.h> //for compass inside Zumo shield
#include <LSM303.h> //compass inside Zumo shield
LSM303 compass; //compass inside Zumo shield
#include <ZumoMotors.h> //include the Zumo Motors library
ZumoMotors motors; //initialize the motors from the Zumo Motors library
// Declare Pins
// Pin 7 Right motor direction control line
// Pin 8 Left motor direction control line
// Pin 9 Right motor PWM control line
// Pin 10 Left motor PWM control line
int sensorpin = A0; //A0 is connected to the Sharp IR sensor
int switchPin = 12; //pin read for the switch
int switchPower = 13; //set to high to power switch
// Global Variables
int OriginalHeading; //For rotating 360 deg, this is the starting position
int CurrentHeading; //Current position
int DiffHeading; //Difference between current position and target position
int TargetHeading; //Target position of where to move to
int CloseHeading; //Based on the IR sensor, heading of the closest object
int switchval; //value (high/low) for the switch (if object is in reach)
uint16_t volatile servoTime = 0; //Servo control: time since last rising edge
//in units of 0.5us.
uint16_t volatile servoHighTime = 3000; //Servo control: pulse width we want
//in units of 0.5us.
boolean volatile servoHigh = false; //Servo control: true if the servo pin
//is currently high.
int SERVO_PIN; //Servo control: initialize servo pin
void setup(){
pinMode(switchPin,INPUT); //initialize the switch's input
pinMode(switchPower,OUTPUT); //initialize the switch's output
digitalWrite(switchPower,HIGH); //set the power to +5V
Page 15
Serial.begin(9600); //begin serial communication, baud rate = 9600 Hz
void loop(){
delay(2000); //wait 2 seconds before starting
Page 16
motors.setRightSpeed(-RotSpeedMid); //set the motors to rotate CW
Page 17
else if (DiffHeading>240){ //if the target is middle, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-5));} //calibrated
else { //if the target is far, adjust target
TargetHeading = AbsoluteHeading((TargetHeading-5));} //calibrated
Page 18
}
Page 19
void PickUp(){ //control the servos to grip, pick up, and drop a Lego
// Start at OPEN and UP
servocontrol(BServoSignal,-30); delay(500); //back servo - up
servocontrol(LServoSignal,65); delay(100); //left servo - open
servocontrol(RServoSignal,120); delay(1000); //right servo - open
// Move DOWN
servocontrol(BServoSignal,180); delay(1000); //back servo - down
// CLOSE (slowly)
int r = 120; //right (r) starts at 120 - open
for(int l=65;l<=115;l){ //counter l changes each loop
servocontrol(LServoSignal,l); delay(100); //left servo - close a little
servocontrol(RServoSignal,r); //right servo - close a little
r = r - 5; l = l + 5; //update values for r (decrease) and l (increase)
delay(700); //pause before closing more
}
servocontrol(LServoSignal,123); delay(100); //finish closing left
servocontrol(RServoSignal,62); delay(700); //finish closing right
// Move UP
servocontrol(BServoSignal,-30); delay(1000); //back servo - up
// OPEN (slowly)
int l = 120; //left (l) starts at 120 - closed
for(int r=65;r<=115;r){ //counter r changes each loop
servocontrol(LServoSignal,l); delay(100); //left servo - open a little
servocontrol(RServoSignal,r); //right servo - open a little
r = r + 5; l = l - 5; //update values for r (increase) and l (decrease)
delay(300); //pause before opening more
}
servocontrol(LServoSignal,65); delay(100); //finish opening left
servocontrol(RServoSignal,120); delay(700); //finish opening right
}
Page 20
// after the rising edge will be smaller so we can achieve
// the desired pulse width.
ISR(TIMER2_COMPA_vect)
{
// The time that passed since the last interrupt is OCR2A + 1
// because the timer value will equal OCR2A before going to 0.
servoTime += OCR2A + 1;
static uint16_t highTimeCopy = 3000;
static uint8_t interruptCount = 0;
if(servoHigh)
{
if(++interruptCount == 2)
{
OCR2A = 255;
}
// The servo pin is currently high.
// Check to see if is time for a falling edge.
// Note: We could == instead of >=.
if(servoTime >= highTimeCopy)
{
// The pin has been high enough, so do a falling edge.
digitalWrite(SERVO_PIN, LOW);
servoHigh = false;
interruptCount = 0;
}
}
else
{
// The servo pin is currently low.
if(servoTime >= 40000)
{
// We've hit the end of the period (20 ms),
// so do a rising edge.
highTimeCopy = servoHighTime;
digitalWrite(SERVO_PIN, HIGH);
servoHigh = true;
servoTime = 0;
interruptCount = 0;
OCR2A = ((highTimeCopy % 256) + 256)/2 - 1;
}
}
}
void servoInit()
{
digitalWrite(SERVO_PIN, LOW);
pinMode(SERVO_PIN, OUTPUT);
// Turn on CTC mode. Timer 2 will count up to OCR2A, then
// reset to 0 and cause an interrupt.
TCCR2A = (1 << WGM21);
// Set a 1:8 prescaler. This gives us 0.5us resolution.
TCCR2B = (1 << CS21);
// Put the timer in a good default state.
TCNT2 = 0;
OCR2A = 255;
TIMSK2 |= (1 << OCIE2A); // Enable timer compare interrupt.
sei(); // Enable interrupts.
Page 21
}
void servoSetPosition(uint16_t highTimeMicroseconds)
{
TIMSK2 &= ~(1 << OCIE2A); // disable timer compare interrupt
servoHighTime = highTimeMicroseconds * 2;
TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
}
Page 22
Appendix C – Datasheets
Website Link: http://www.sharpsma.com/webfm_send/1489
Page 23
Website Link: http://www.pololu.com/file/download/LSM303DLHC.pdf?file_id=0J564
Page 24
Website Link: http://www.robotgear.com.au/Cache/Files/Files/186_DSM44.pdf
Page 25
Website Link: http://www.pololu.com/docs/pdf/0J57/zumo_shield_for_arduino.pdf
Page 26
Page 27
Page 28
Page 29
Page 30
Page 31
Interactive LED Dance Floor
Prepared By:
David Manley & Matthew Duffy
Prepared For:
Dr. Sommer and Michael Robinson
Contents
Introduction .................................................................................................................................................. 4
Construction.................................................................................................................................................. 4
Lid .............................................................................................................................................................. 4
Base ........................................................................................................................................................... 5
Support ..................................................................................................................................................... 6
Power and Signal Strips............................................................................................................................. 6
Control Board ............................................................................................................................................ 7
Electronics ..................................................................................................................................................... 7
Adafruit NeoPixel Digital RGB LED Strip.................................................................................................... 7
MSGEQ7 Graphic Equalizer Display Filter ................................................................................................. 8
Force-Sensitive Resistors .......................................................................................................................... 8
Potentiometers and Switches ................................................................................................................... 8
Programming ................................................................................................................................................ 8
Inter-Integrated Circuit ............................................................................................................................. 8
Solid Color ................................................................................................................................................. 9
Touch Sensitive ......................................................................................................................................... 9
Tap Tempo ................................................................................................................................................ 9
Audio Responsive .................................................................................................................................... 10
Conclusion ................................................................................................................................................... 10
Appendix A: Code........................................................................................................................................ 12
Tile Code ................................................................................................................................................. 12
Control Board Code................................................................................................................................. 14
Appendix B: Controller Circuit Diagram ...................................................................................................... 19
Appendix C: MSGEQ7 Data Sheet ............................................................................................................... 20
Introduction
For our mechatronics project we decided to create an interactive LED dance floor. We both
love to dance, and love finding the latest equipment for dance parties. After seeing LED
dance floors on line, we thought that it would be a great project to try and create a dance
floor of our own, which is more cost effective, allows for increased user interfacing, and
increased modes of operation. The current products which are available are over 300
dollars per square. These squares are also only able to change colors, and are several feet
off of the ground making it dangerous to dance on. Another company offers a product which
is much more advanced and much sleeker, but costs 8,000 dollars to rest an 8’x8’ floor from
this company. Because of this we wanted to make a product which was much cheaper,
since currently this type of product is not reasonable for purchasing.
Beyond reducing cost, we came up with several goals for our product. The first mode that
we wanted to implement was a mode in which the tiles can be set to a solid color. However,
unlike our competitors, we wanted to make this option more interactive by making the colors
fully customizable so that users can mix their own colors. We also wanted to create a mode
in which the tile would change color when they were stepped on. We thought that this would
be a feature which could be used for dancing and for other game applications. This was
another goal, to not only create a dance floor, but also a game floor, for games similar to
“whack a mole,” “Simon says,” and other games. This would transform our product from
simply a dance floor to an entire party floor. Finally we wanted to create tiles which were
sensitive to audio inputs. This was a unique feature that we thought would separate our
design from that of other competitors. We wanted to achieve this by using the tiles to create
a large graphic equalizer, and to make them pulse with audio inputs in a way in which they
would be audio responsive.
Throughout the following paper we will discuss our individual components, materials,
construction, and programming process in order to explain the development of our product.
Construction
In order to complete the design of this project we first began by selecting various materials
to use for production. Since our project is a dance floor it is important to make sure that all
materials are durable and will not cause injury to the user if it breaks during use. In this
section we will also discuss how we constructed the various physical components using
these materials.
Lid
The first component of our project that we began constructing was the lid which houses all
of the internal components and is also the surface on which the user will stand. When
analyzing the material choices of competitors we noticed that the industry as a whole used
acrylic for the top surface of their designs. However, we wanted to steer away from this
material due to the fact that it can shatter, which would cause serious harm to the user. To
avoid this we decided to use a ⅛” Lexan, or polycarbonate, sheet. These sheets are
guaranteed to not break and can even be cold worked without snapping. The only downside
to these sheets is that they are able to flex, which makes the surface feel less safe even
though it is in fact a safer design, and that Lexan is much more expensive than acrylic.
Because of the increased cost of Lexan we decided to construct the walls of the lid out of
acrylic. Since it was crucial for the walls to be straight for the lid to sit flush with the bottom
surface, we decided to make a dxf file for the walls and to have them laser cut. We then
took the acrylic strips and bonded them to the Lexan sheet and to one another using a two
part epoxy to finish the construction of the lid.
Another way that we minimized our costs was by purchasing a large sheet of Lexan with no
surface finish. We began by cutting the large sheet into square feet. We then began to play
around with the surface finish of the material. We first thought that a “frosted” finish would
be the best design, so we began by purchasing a frosting spray paint. However, after
several coats the frosting reached a max limit and you could still see all of the individual
components and the individual LEDs which was not ideal. We then attempted to sandblast
the top of the sheets to intensify this frosted look. However, this only removed the spray
paint and began to turn the Lexan brown. In addition to this the effects of the sandblasting
were so minimal that it made it impossible to provide a consistent effect on the sheet. After
this be decided to attempt to scuff the inside of the Lexan. We used varying grits of
sandpaper to determine which was the most ideal and used a powered sander to make a
more uniform product. This made the sheets frosted, however, we realized that a
translucent finish was more appropriate than a frosted finish. In order to quickly modify our
tiles after this discovery, we decided to attach paper to the inside of the Lexan. Finally we
attached a thick piece of acrylic to the middle of the sheet to provide a support for the lid,
and to contact the force sensitive resistor beneath.
Base
After constructing the lid, we then turned our efforts to constructing the base of the tiles. We
had trouble at first selecting a base material which would help us to easily integrate our
electrical components, would not slide on the ground when placed, would be lightweight,
and would be inexpensive. After analyzing many different base materials including metal,
rubber, wood, and particle board, we decided to use particle board for our design. We
chose the particle board because it was very inexpensive and also came in a large sheet
with a thickness of ¾” which was ideal for our design. We began by cutting the sheet of
particle board into square feet. After this we began working on the modular element of our
design.
After creating the modular component of our board we then began focusing on supporting
the lid and creating a mounting structure for the LEDs. We decided to use the excess
particle board pieces to achieve this goal. We strips of particle board which we the same
length as our LED strips, and positioned them in the middle of each wall. In addition to this
we moved these strips in from the edge of the board so that the lid would fit on the outside
of these structures. This provided support for the walls of the lid and ensured that the lid
was unable to slide laterally when being danced on. We also cut these strips to the exact
height of the acrylic walls of the lid so that they would provide additional support for the top
of the lid. The other use for these strips was to mount the LEDs. In order to optimize the
number of LEDs used per square we positioned the LED strips in a way so that they would
all face in towards the middle, horizontal with the board. In this way the light was able to
spread out and achieve a more uniform appearance before exiting the top of the board. We
also spray painted the board white to increase reflectivity. We also added a padded contact
material to the corners of the boards to increase the traction of the tiles.
Support
Unfortunately, with all these modifications, the structure still did not feel stable. We do not
think that the tiles would have broken under use, but we wanted to ensure that the user
“felt” safe while using our product. In order to increase support we cut additional pieces of
acrylic, similar to the one previously used for support and for contacting the force sensitive
resistor, and placed them throughout the structure to bear the load of the user.
Electronics
This section will discuss the different electronic components of our project; however, most
of the detail on how these components were implemented will be discussed in a later
section. The electronics of this project were based around the capabilities of Arduino Uno
microcontrollers. We used an Uno in the control box, giving users the power to control the
behavior of the entire LED grid; and Unos were used in each of the tiles to run all the
processing for each tile. In addition to the Arduino Uno we used LED strips, a graphic
equalizer chip, force-sensitive resistors, potentiometers and push-button switches to provide
functionality and user control of the floor tiles.
For our design we separated our LED strips into segments of 7 LEDs and placed them on
the mounts inside each of our tiles. Each tile then had a total of 28 LED pixels (four
segments) all facing toward the center of the box. These four strips were all wired together
and controlled by a PWM digital pin on the Arduino inside each tile.
Force-Sensitive Resistors
The base of each floor tile has a force-sensitive resistor in the center. Force-sensitive
resistors provide varying resistance based on the force applied to them; the higher the force
the lower the resistance. These are wired in series with 100 ohm resistors, and an analog
input on the tile Arduino is used to measure the voltage in between the two resistors. As
force is applied to the force-sensitive resistor, the voltage being read by the Arduino
increases. This allows the tiles to respond to the act of someone stepping on them.
Programming
In this section we will first give an explanation of I2C which was used to communicate
between our multiple Arduinos. After this we will provide a brief explanation of each of our
modes of operations and then go into more detail as to how we programmed our Arduinos
to achieve these results.
Inter-Integrated Circuit
Inter-Integrated Circuit, commonly referred to as I2C, allows for the communication between
multiple devices. I2C makes it possible for our controller to send commands to all of the
tiles. The basic idea of I2C is that there is one device that is designated as the “master”; all
other devices are “slaves”. The master device is in charge of all the communication that
occurs on the communication lines. Each slave has an ID, and if the master has data to
send to a particular slave it will first send the ID of that slave followed by the data. All the
slaves listen for their ID, and if the master calls their ID, they collect the data that follows.
Because the user controls take up most of the digital and analog pins on the control
Arduino, the benefit of I2C is that it only uses two lines on the Arduino: one for data (SDA)
and one for clock (SCL). The controller Arduino and all of the tile Arduinos are connected to
these two lines. This was crucial for our project because we did not want the number of
output leads on our control Arduino to be a function of the number of tiles used. In addition,
Arduino has an already constructed I2C function library called “Wire” making implementing
this into our project relatively simple. In our application the control Arduino sent 4 bytes to
each tile Arduino; the first byte contained the selected mode of operation, and the last three
bytes contained the color data for that mode of operation.
Solid Color
For our first mode of operation we decided to create a mode which would allow the user to
set all of the tiles to a constant color. Because of the nature of our LEDs we were given the
ability to make the color fully customizable for the user. The color for our LEDs is controlled
by varying the intensity of the Red, Green, and Blue inputs. To make this fully customizable
we attached three potentiometers to the circuit which were used to control the intensity of
these spectrums. The analog inputs on the control Arduino read the input voltage intensity
of the potentiometers from 0 to 1023. The voltage intensity of each potentiometer is then
mapped from 0 to 255, the minimum and maximum intensity for each of the color bands. In
this way the user is able to actually “mix” the colors to achieve literally any desired
color. For increased usability we attached three LEDs onto the control box, so that the user
can mix the inputs live and visibly see the results before sending the signal to the
tiles. After the desired color is achieved the user can simply press the solid color button to
send this color to all of the tiles.
Touch Sensitive
For our second mode, we made a touch sensitive application. This was the mode which first
inspired us to attempt to create this product. We placed a force sensitive resistor in the
middle of the tile, which was impacted by the support of the lid when a user stepped onto
the tile. This force sensor had a threshold for the force input, and above this force, the LED
input would be switched to a corresponding color based off of the previously set color for
the tiles. The color for this mode is set in the same way as for the solid color mode using
the potentiometer voltages. The color that the tile alternated to when the switch was
pressed was calculated as the complementary color of the set color using this equation:
Complementary Color = [255 - Red Intensity, 255 - Green Intensity, 255 - Blue Intensity]
The color for the tiles alternates between the set color and this complementary color as the
user steps on the tiles.
Tap Tempo
For our next mode, we decided to make an application where the user would be able to
input a tempo in the form of beats per minute, or bpm, at which the LEDs would pulse. This
application could be used if the user is listening to music and wants to tap in the bass
tempo. When the user taps the large red button in the center of the control board the control
Arduino calculates the difference between the current time and the time that the button was
last tapped. This value is stored in an integer array, and the Arduino calculates the bpm by
averaging the most recent four values in the array. When the user reaches a desired bpm,
they can then send this to all the tiles by pressing the tap tempo select button. The control
Arduino sends five bytes of information, containing the mode selection, the color, and the
bpm, to each of the tile Arduinos. After the tiles have been set to this function they all
individually calculate the time until the next beat using the bpm, and flash on and off to this
tempo.
Audio Responsive
For our final mode we wanted to create a mode which would enable the user to fully
experience the music by creating a graphic equalizer which would cause the LEDs to react
to the input audio. We began by implementing a MSGEQ7 chip into our circuitry. We used
this chip to break the audio inputs into its given frequency bands. We then attenuated low
band levels and boosted high ones, in order to intensify the desired effect. Unlike all other
modes of operation, the audio responsive mode does not allow users to set a color using
the potentiometer knobs. We instead assigned certain frequency ranges to color values; for
example, low frequencies were assigned to red. The controller Arduino then sets the
intensity of each color based off of the magnitude of the audio signal for each frequency
range. While in this mode of operation, the control Arduino is continuously communicating
with the tile Arduinos on each iteration in order to provide instantaneous response to the
music.
Conclusion
Overall we were able to achieve a product which was able to match many of our desired
goals. We were able to create all of the desired dance floor modes that we set out to create,
however due to time constraints we were unable to create the game interfaces as intended.
With additional time we hope to be able to create these additional games. With our set up
and I2C capabilities it should be a simple addition to our platform. In the future we would
like to add a games similar to “whack a mole” and “Simon says.”
In addition to this there are several mechanical components which were would change if we
were to take this product to production. First we would change the lid material. We would go
to an acrylic which is much thicker and already has the desired translucent finish. In addition
to this we would either retrofit the modular design by using larger hinges to increase the
ease of assembly, or we would create a new feature such as a rotational latch. We would
also seek to improve the touch sensitive setting, by either changing the backing of the force
sensitive resistor, or by using a piezo or other component for this sensor.
Also in the future we would most likely change some of our electrical components. One of
the changes that we would likely implement would be to add parallel computing for the tap
tempo setting. Currently there is drift in the LED response due to variable processing
speeds. This change would help to make a more uniform final product. In addition to this we
would consider using an Arduino with more inputs to allow for more modes.
Overall, we were very pleased with the results of our product and think that it is a very
marketable design. In fact we plan to try to market our design next semester in our new
venture creation class. Beyond this, this project has given us experiences outside of our
usual mechanical engineering classes, and has equipped us with unique skills and
understand of electronic systems which are not typical for mechanical engineers. This
experience will help us to succeed in our future careers allowing us to work on more diverse
projects, and also to be able to understand electrical designs, and to interface more
effectively with electrical engineers in the future.
Appendix A: Code
Tile Code
//DanceFloorSlave is the code for each of the floor tiles
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#define PIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(28, PIN, NEO_GRB + NEO_KHZ800);
void setup()
{
Wire.begin(2); // join i2c bus with address #2 (must change address for each arduino)
Wire.onReceive(incomingMessage); // register event
strip.begin();
//strip.show(); // Initialize all pixels to 'off'
Serial.begin(9600); // start serial for output
}
void loop()
{
}
}
void touchSensitive(){
int forceMagnitude = analogRead(forcePin); //reads the force sensor voltage
Serial.println(forceMagnitude);
if (forceMagnitude > forceThreshold && forceReset){ //if the button is being pressed and has been
reset
forceReset = 0; //trigger has to be reset
R = 255 - R; // switch the value of the colors to their complement to 255
G = 255 - G;
B = 255 - B;
colorFill(strip.Color(R,G,B)); //set the color of the tile
}else if(forceMagnitude < 10){ //no longer has force on it
forceReset = 1; //reset the trigger
}
}
void colorFill(uint32_t c) { //function to turn all the pixels in the strip to a set color
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
}
strip.show();
}
Control Board Code
//DanceFloorMaster is the code for the control board of the dance floor
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#define rgbLED 6
#define msg7RESET 11 //terminal 7 on chip
#define msg7Strobe 12 //terminal 4 on chip
#define msg7DCout 0 //terminal 3 on chip
void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
strip.begin();
Serial.begin(9600);
pinMode(msg7RESET, OUTPUT);
pinMode(msg7Strobe, OUTPUT);
pinMode(solidModePin, INPUT);
pinMode(touchModePin, INPUT);
pinMode(tapModePin, INPUT);
pinMode(tapPin, INPUT);
pinMode(beatModePin, INPUT);
void loop(){
//check the color pots
int potValue = analogRead(RPin); //check red
R = map(potValue,0,1023,0,255);
potValue = analogRead(GPin); //check green
G = map(potValue,0,1023,0,255);
potValue = analogRead(BPin); //check blue
B = map(potValue,0,1023,0,255);
colorFill(strip.Color(R,G,B)); //set color on master's LEDs
//checkbuttons
solidModeOn = digitalRead(solidModePin);
touchModeOn = digitalRead(touchModePin);
tapModeOn = digitalRead(tapModePin);
beatModeOn = digitalRead(beatModePin);
buttonState = digitalRead(tapPin);
void changeMode(){ //only runs when a button is pressed to change the mode
Serial.println("Sent Signal");
if(mode == 1){ //solid color function
byte message[] = {1,R,G,B}; //message is 1 for solid, then the color
for (byte i=0; i<numTiles; i++){ //send it to all the tiles
Wire.beginTransmission(slaveIDs[i]); //transmit to device
Wire.write(message,4); //send 4 bytes
Wire.endTransmission(); //stop transmitting
}
} else if(mode ==2){ //touch sensitive function
byte message[] = {2, R, G, B}; //message is 2 for touch, then the color
for (byte i=0; i<numTiles; i++){ //send it to all the tiles
Wire.beginTransmission(slaveIDs[i]); //transmit to device
Wire.write(message,4); //send 4 bytes
Wire.endTransmission(); //stop transmitting
}
} else if(mode == 3){ //tap tempo function
byte message[] = {3,R,G,B,BPM};
for (byte i = 0; i<numTiles; i++){
Wire.beginTransmission(slaveIDs[i]); //transmit to device
Wire.write(message,5); //send 4 bytes
Wire.endTransmission(); //stop transmitting
}
} //end of mode checker
} //end of changeMode
void BassBeat(){
digitalWrite(msg7RESET, HIGH); // reset the MSGEQ7's counter
delay(5);
digitalWrite(msg7RESET, LOW);
byte PWMvalues[7];
for (int x = 0; x < 7; x++){
digitalWrite(msg7Strobe, LOW); // output each DC value for each freq band
delayMicroseconds(35); // to allow the output to settle
int spectrumRead = analogRead(msg7DCout);
Executive Summary
The inspiration for this design came from an individual with a nerve injury resulting in the
loss of use of his left arm. This made it difficult for him to use a bike with conventional gear
shifters on both sides of the handlebars. The goal was to make an electronic gear shifter
with push button switches on the right side of the handlebars so the individual could initiate
the gear change sequence with only his right hand. Additionally, an automatic gear
changing system was implemented to move the rear derailleur using a Hall Effect sensor
and two magnets placed on the rear tire to sense changes in rotational speed. Two linear
actuators, one attached to each gear shifter cable, converts the electrical signal to
mechanical motion. While all code and electrical signals worked correctly, the linear
actuators used on this project were not strong enough to move the derailleur on the bike.
Replacing the linear actuators with a model strong enough to effectively change the bike
gears would result in a fully functioning electronic gear shifter with an automatic
transmission on the rear derailleur.
2
Table of Contents
1.0 Introduction ............................................................................................................................................ 3
2.0 Concept ................................................................................................................................................... 3
2.1 Function .............................................................................................................................................. 4
3.0 Components ............................................................................................................................................ 6
3.1 L293D Motor Driver Chip .................................................................................................................... 6
3.2 Linear Actuator ................................................................................................................................... 6
3.3 Arduino ............................................................................................................................................... 7
3.4 Hall Effect Sensor ................................................................................................................................ 7
3.5 Bike...................................................................................................................................................... 8
4.0 Initial Testing ........................................................................................................................................... 8
5.0 Fabrication .............................................................................................................................................. 8
5.1 Actuator Mounting.............................................................................................................................. 8
5.2 Circuit Construction........................................................................................................................... 10
6.0 Results and Discussion .......................................................................................................................... 12
References .................................................................................................................................................. 13
Appendix A – BOM ...................................................................................................................................... 14
Appendix B - Code ....................................................................................................................................... 15
Appendix C – Datasheets ............................................................................................................................ 23
3
1.0 Introduction
The project was to fabricate an electronic bicycle gear shifter system. The gear shifter could
be initiated by the bicyclist manually with two sets of switches or automatically controlled by
sensing the change in speed of the bike.
The motivation for creating an electronic bicycle gear shifter came from Jon Rhone’s Senior
Design project, which was to design a bicycle for a handicapped individual suffering from
brachial plexopathy. Brachial plexopathy is an injury to the brachial plexus, a network of
nerves connecting the spine to the shoulder, arm, and hand. The brachial plexus injury
occurred from a construction accident where a piece of drywall fell onto the top of his
shoulder. The trauma caused the nerves to detach from the spine, rendering his arm
effectively useless. With only the use of one arm, it is impossible to shift both the front and
rear gears using the traditional setup of a gear shifter lever on each handlebar. By having
an electronic gear shifter either controlled by a set of switches or automatically, the
individual would have an enhanced ability to shift gears while also controlling the bicycle.
2.0 Concept
The basic concept for the project was to replace the conventional bicycle gear shifter
system with one that is electronically controlled. The end result, increasing or decreasing
the tension on the gear shifter cable to move a derailleur to push the chain onto a new
sprocket, does not change by implementing an electronically controlled system. However,
by using a linear actuator as the core concept of the project the manner by which the
tension on the gear shifter cable is changed was to now be completed using electronics.
4
2.1 Function
The concept flow chart showing how the code works can be found in Figure 2.1. The full set
of code is shown in Appendix B. Following powering up the system, the current gear for
both the front and rear sprockets is calculated by reading the potentiometer built-in to each
linear actuator and comparing the voltage reading to an associated speed. Knowing the
initial gear setting was critical for later displacing the actuator by the correct amount to
achieve the desired gear setting. Once this initial step is completed, the program waits.
Figure 2.1: Concept flow chart. Note that the loop denoted by orange boxes is repeated until the target
position is achieved.
There are two ways that changing a gear can be initiated: a user command via a push-
button switch and an automatic signal based upon on the bicycle’s speed.
The push-button switch is very similar to the manual gear shifter in its use. The rider
presses a button switch to shift the bike to the desired gear ratio. The front and rear gear
settings are controlled independently by two sets of two switches, shown in Figure 2.2.
Once the switch has been pushed the Arduino increases or decreases the gear setting
number. Based upon the new gear setting number, the Arduino determines the target
position of the actuator for the new gear from a lookup table.
Once the target position is known the Arduino executes the following loop. First, the current
position is calculated from the actuator potentiometer. The difference between the current
5
position and target position is then calculated and is used as the basis for a proportional
motor controller. For a relatively large delta between current position and target position the
Arduino sends maximum voltage to the motor driver chip, the L293D, using a PWM motor
control function. As the delta decreases the voltage signal decreases to slow the actuator’s
displacement very close to the target position. Once the program runs through this set of
commands once, the current position of the actuator is recalculated and then the loop is
repeated until the target position is achieved. The program then waits until the next gear
change signal is given.
Rear Up
Front Up
Rear Down
Front Down
Figure 2.2: Pushbutton switch mount. Each set of two switches controls the front and rear gear settings.
Alternatively, the gear change can be initiated based upon the rotational speed of the bike
wheel using a Hall Effect sensor. The Hall Effect sensor, mounted on the rear of the bike
frame, reads a change in magnetic field caused by a magnet mounted on the wheel
sweeping past the sensor. The Arduino calculates the rotational speed of the bike wheel by
measuring the time interval between Hall Effect readings and dividing that into a half
revolution (two magnets spaced 180° apart). The program stores the most recent five
values of wheel speed. The difference between the most recent and oldest rotational
speeds is calculated every time a reading is taken. If this difference is outside of a range of
±10 RPM, the Arduino sends a command to initiate a gear setting change on the rear set of
gears The bike design has implemented an automatic gear switch on the rear gears to
account for small changes in speed. This takes away the need for the rider to constantly
change gears when only a small change in the gear ratio is needed. However, we have
reserved the front gears for manual control only. That way the rider has control over large
changes in speed which would be useful when going up or down steep hills.
Once the gear change command is initiated the Arduino executes the same set of
commands to reach the target actuator position as previously discussed.
6
3.0 Components
The components used to complete this project were the L293D motor driver chip, two
Firgelli linear actuators, a Hall Effect sensor, the Arduino Micro board, and a bicycle. The
datasheets for all electronic components are shown in Appendix C.
The L293D motor driver chip was used to drive the two linear actuators. The advantage of
this chip is that it can control two motors simultaneously. This is ideal for this application
because it is possible that both actuators will be moving at the same time in order to attain
the desired gear ratio. The chip can also drive each actuator both forward and backwards,
an obvious need for driving an actuator. The datasheet can be found in Appendix C.
Two Firgelli PQ12-100-12-P actuators were used to move the gear shifter cables. The
actuators are powered from DC, which is ideal for use on a bicycle powered by a typical
battery. The nominal voltage for each actuator is 12V, which was supplied by stacking 8 AA
batteries in series. The actuators feature a 100:1 gear ratio providing relatively high output
force, approximately 35 N. The maximum stroke length of approximately 0.787 inches
makes the actuator compact enough to be easily stored. Another important feature of this
actuator is that it features potentiometer position feedback. This provides the Arduino with
feedback on the current actuator position, making accurate proportional control possible.
7
3.3 Arduino
The Arduino Uno was selected as the microcontroller for this project because of the team
member’s familiarity with it. It is used to read the potentiometers on the actuators, read the
switches, read the Hall Effect sensor and, based upon the code, calculate the rotational
speed of the bike, determine the current bike gear ratio, control the actuators.
The Hall Effect sensor detects when the magnets pass by due to the high magnetic field.
This varies the output from the Hall Effect sensor. By simultaneously calculating the time
between each magnet passing by the Hall Effect sensor, the rotational speed can be
calculated. The velocity of the bicycle can then be calculated by multiplying the rotational
speed by the radius of the wheel.
Magnet
Hall Effect
Sensor
Magnet
Figure 3.4: Rear bicycle wheel showing Hall Effect sensor and magnet mounting.
8
3.5 Bike
The bicycle that was used was a Mongoose Crossway, which was donated by team
member Jon Rhone. It is similar in style to the bicycle used by the handicapped individual.
Table 4.1. Position change required to move from one speed to another relative to first speed.
Speed Front (in.) +/- .01 in. Rear (in.) +/- .01 in.
1 0 0
2 .14 .12
3 .14 .08
4 .3 .08
5 – .10
6 – .08
7 – .12
5.0 Fabrication
The team initially constructed a prototype of the project using the bicycle as the base. The
physical mounting of the hardware, the circuits, and the code were all prototyped and then
utilized their troubleshooting abilities to evolve towards the final prototype. This section
details the initial work that was performed to complete the first prototype.
To determine if the prototype would function, the actuators were temporarily mounted to the
bike. To accomplish this, the original gear shifter cable was removed from the bike. The
actuators were mounted to the bicycle frame with zip ties on the lower center tube, as
9
shown in Figure 5.1. New gear shifter cables were connected from each actuator to the
respective derailleur, as shown in Figure 5.2. This enabled the team to test the circuitry and
code with the actuators attached to the bicycle.
Figure 5.1: Side view of linear actuator temporarily mounted to bicycle frame. The gear shifter cable is
attached to the actuator.
Figure 5.2: Bottom view of bike showing both linear actuators temporarily connected to bicycle frame.
10
The circuit was initially constructed on a breadboard, shown in Figure 5.3, so the team
could easily troubleshoot it. The circuit consisted of the L293D motor driver chip connected
to the two actuator wiring harnesses. Some of the Arduino digital pins were connected to
the L293D to send the signals to turn on the correct motor driver pins. Four pushbutton
switches were wired in to the Arduino digital pins as well to send the signal to initiate the
gear change sequence. Alternatively, the output from the Hall Effect sensor was connected
to digital pin 2 on the Arduino so an interrupt could occur every time the magnet swept by
the sensor.
Figure 5.3: Actuator driver circuits connected to Arduino board on the breadboard. Pushbutton switches
are not shown.
Once it was determined that circuit system and code (Appendix B) functioned as expected,
the next step was to determine if the actuators could supply enough force on the gear
shifter cable to move the derailleur. The actuators were rigidly constrained to the bicycle
and the system was performance tested. Testing confirmed that the linear actuators were
not strong enough to move either the front or rear derailleur. However, this test also
confirmed that all of the systems functioned as anticipated. With an actuator that could
supply greater force, derailleur would have moved.
Other than the actuators not being able to supply the necessary force to the derailleur, the
prototyped system worked. The next phase of the project was to disassemble the circuits
from the breadboard and attach them to the bicycle. Using a through-hole breadboard the
circuit was transferred to the bicycle. Figure 5.4 shows the package containing the through-
hole breadboard with the soldered connections, the Arduino, and the power supply–8 AA
batteries supplying around 12 V DC.
11
The switches were attached to the bicycle by constructed a wooden mount that clamps on
the handlebar, as shown in Figure 2.2. The switches fit snugly in holes drilled through the
mount.
Figure 5.4: Arduino, through-hole breadboard, and battery pack mounted on bicycle
After mounting the circuit and components on the bicycle one of the actuators was not
functioning due to a bad connector at the end of the ribbon wire. The connection failure did
not occur until shortly before the project was due, therefore a new part could not be ordered
and delivered in time. Attempts were made to adjust the connector, however this ultimately
broke the wire leads going from the L293D to the connector. In an attempt to fix the
connection problem, the connector was removed and the ribbon wire was cut length-wise to
isolate each of the five lines leading to the actuator. Wire leads were soldered to each of the
ribbon wire lines to manually make connections to the rest of the circuit. However, the
ribbon cable is susceptible to fracture due to bending, and soldering wire leads to the ribbon
cable proved unsuccessful.
The project as a whole was a success in the fact that the concept functions as expected.
The user can initiate the gear change sequence by hitting a switch or the sequence can be
initiated automatically by sensing the change in rotational speed. The actuators are able to
go to a set location based upon the gear setting. The only two factors impeding the project
from being fully functional are the actuators not supplying enough force to move the
derailleur and the bad actuator connector that could easily be replaced if there was
sufficient time.
13
References
[1] “L293D Motor Driver”. http://commons.wikimedia.org/wiki/File:L293D_Motor_Driver.jpg
Appendix A – BOM
AA Battery 9.00 8
Appendix B - Code
//LinearActuatorControl
/*Control the position of a linear actuator using 12V on pin 8,
L293D controlled. */
//Variables
float motorspeedfront;
float motorspeedrear;
float motorvoltagefront;
float motorvoltagerear;
float value;
float currentpositionrear;
float currentpositionfront;
float targetpositionrear;
float targetpositionfront;
float refrear;
float reffront;
float potvoltage;
int gain;
int gearrear;
int gearfront;
float deltafront;
float deltarear;
float highrange = .02;
float lowrange = -.02;
int readswitchfrontup;
int readswitchfrontdown;
int readswitchrearup;
int readswitchreardown;
float i;
int rpmcount;
float rpm1 = 5;
float rpm2 = 15;
float rpm3 = 20;
float rpm4 = 25;
float rpm5 = 25;
float rpmdiff;
float timeold;
//pins
int pwm2rear = 9;
int pwm2front = 6;
int pwm7rear = 10;
int pwm7front = 3;
int rearread = A2;
16
void setup() {
delay(4000);
pinMode(rearup, INPUT_PULLUP);
pinMode(reardown, INPUT_PULLUP);
pinMode(frontup, INPUT_PULLUP);
pinMode(frontdown, INPUT_PULLUP);
Serial.begin(115200);
gain = 10000;
refrear = analogRead(rearread);
reffront = analogRead(frontread);
}
else {
gearfront = 4;
}
void loop() {
if (rpmcount >= 2) {
//Update RPM every 1 counts, increase this for better RPM resolution,
//decrease for faster update
rpm1 = rpm2;
rpm2 = rpm3;
rpm3 = rpm4;
rpm4 = rpm5;
rpm5 = 30*1000/(millis() - timeold)*rpmcount;
timeold = millis();
rpmcount = 0;
rpmdiff = rpm5 - rpm1;
Serial.println(rpm5);
if (rpmdiff > 10 && gearrear <7) {
gearrear = gearrear + 1;
Serial.println("moveup");
18
delay(700);
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
reartargetpositionsequence();
deltarear = (currentpositionrear-targetpositionrear);
rearsequence();
}
readswitchfrontup = digitalRead(frontup);
readswitchfrontdown = digitalRead(frontdown);
readswitchrearup = digitalRead(rearup);
readswitchreardown = digitalRead(reardown);
//Serial.println(readswitchfrontup);
// Serial.println(readswitchfrontdown);
//rear gear up 1
else if (readswitchrearup == LOW && gearrear<7 ) {
gearrear = gearrear + 1;
delay(700);
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
reartargetpositionsequence();
deltarear = (currentpositionrear-targetpositionrear);
rearsequence();
}
//Clockwise Rotation
if (motorspeedrear>0) { //motor rotates CW for positive values
analogWrite(pwm7rear,0); //L293D pin 7 LOW
value = value*2.55; //converts from 0 to 100% scale to 0 to 255 scale, the input for PWM
analogWrite(pwm2rear,motorspeedrear); //sends this duty cycle of PWM to enable
}
//Counter-Clockwise Rotation
else if (motorspeedrear<0) { //motor rotates CCW for negative values
analogWrite(pwm2rear,0); //L293D pin 2 LOW
value = -value*2.55; //converts from 0 to -100% scale to 0 to 255 scale, the input for PWM
analogWrite(pwm7rear,motorspeedrear); //sends this duty cycle of PWM to enable
20
}
else {
analogWrite(pwm2rear,0);
analogWrite(pwm7rear,0);
}
}
//Clockwise Rotation
if (motorspeedfront>0) { //motor rotates CW for positive values
analogWrite(pwm7front,0); //L293D pin 7 LOW
value = value*2.55; //converts from 0 to 100% scale to 0 to 255 scale, the input for PWM
analogWrite(pwm2front,motorspeedfront); //sends this duty cycle of PWM to enable
}
//Counter-Clockwise Rotation
else if (motorspeedfront<0) { //motor rotates CCW for negative values
analogWrite(pwm2front,0); //L293D pin 2 LOW
value = -value*2.55; //converts from 0 to -100% scale to 0 to 255 scale, the input for PWM
analogWrite(pwm7front,motorspeedfront); //sends this duty cycle of PWM to enable
}
else {
analogWrite(pwm2front,0);
analogWrite(pwm7front,0);
}
}
//controls the front linear actuator until it reaches the proper position
void frontsequence() {
for (i=1;i<10000; i++) {
if ((deltafront>highrange) || (deltafront<lowrange)) {
reffront = analogRead(frontread); //reads the pot on the front actuator
currentpositionfront = reffront*(.00076856); //.787 in/ 1024 bins
motorvoltagefront = (currentpositionfront-targetpositionfront)*gain; //keeps out to be positive
input
if (motorvoltagefront>100) {
motorspeedfront = 100;
}
else if (motorvoltagefront<-100) {
motorspeedfront = -100;
}
else {
motorspeedfront = motorvoltagefront;
21
}
motorfront(motorspeedfront);
reffront = analogRead(frontread);
currentpositionfront = reffront*(.00076856); //.787 in/ 1024 bins
deltafront = (currentpositionfront-targetpositionfront);
}
else {
motorspeedfront = 0;
motorfront(motorspeedfront);
}
}
}
//controls the rear linear actuator until it reaches the proper position
void rearsequence() {
for (i=1;i<10000;i++) {
if ((deltarear>highrange) || (deltarear<lowrange)) {
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
motorvoltagerear = (currentpositionrear-targetpositionrear)*gain; //keeps out to be positive
input
if (motorvoltagerear>100) {
motorspeedrear = 100;
}
else if (motorvoltagerear<-100) {
motorspeedrear = -100;
}
else {
motorspeedrear = motorvoltagerear;
}
motorrear(motorspeedrear);
refrear = analogRead(rearread);
currentpositionrear = refrear*(.00076856); //.787 in/ 1024 bins
deltarear = (currentpositionrear-targetpositionrear);
}
else{
motorspeedrear = 0;
motorrear(motorspeedrear);
}
}
}
22
// determines the target position of the front linear actuator depending upon which gear the user
is in
void fronttargetpositionsequence() {
if (gearfront == 1) {
targetpositionfront = .68;
}
else if (gearfront == 2) {
targetpositionfront = .54;
}
else if (gearfront == 3) {
targetpositionfront = .40;
}
else {
targetpositionfront = .10;
}
}
// determines the target position of the rear linear actuator depending upon which gear the user is
in
void reartargetpositionsequence() {
if (gearrear == 1) {
targetpositionfront = .68;
}
else if (gearrear == 2) {
targetpositionrear = .56;
}
else if (gearrear == 3) {
targetpositionrear = .48;
}
else if (gearrear == 4) {
targetpositionrear = .40;
}
else if (gearrear == 5) {
targetpositionrear = .30;
}
else if (gearrear == 6) {
targetpositionrear = .22;
}
else {
targetpositionrear = .10;
}
}
void rpm_fun()
{
rpmcount++;
//Each rotation, this interrupt function is run twice
}
23
Appendix C – Datasheets
First page of L293D data sheet. [4]
24
1
Introduction
For the ME 445 final project, Team 11 designed “the ultimate useless box”. As a simple wooden
box with a hinged top and single input switch, the useless box can be boiled down to a novel
toy that turns itself off. Whenever the switch is triggered, the machine performs one of its
several sequences, ultimately resulting in an automated arm striking the switch to the off
position. The project presented an opportunity to enhance learning by implementing a variety
of mechatronic concepts in a user-friendly system. In addition, project goals were to expand
upon current online functions for a useless box, creating a device that is fun and amusing for
those of all ages.
Concept Development
The idea for this project was inspired by the team’s discovery of a video of a useless box online.
The box in this video was small, and the top surface was cut into two halves. A switch was also
located on one of the halves of the top surface. When the user pushed the switch, the
opposing part of the lid would lift up, and a
mechanical arm would come out of the box
and press the switch back into its original
position. In Figure 1 to the right, the original
electircal circuit for a useless box can be seen.
Here the electrical layout is simple and thus
can utilize a 555 timer chip to control the servo
motion. The team enjoyed watching the video
of this original useless box, but felt that
interesting improvements and functions could
be made.
With the current design, the single servo is the
only component that can move and the
electrical components are relatively basic. For
the “ultimate useless box,” it was desired to
keep the single input component, but greatly Figure 1. Frivolous Engineering original useless box design
expand on the responses the box can perform. Therefore, Team 11 set out to add a variety of
new features and different sequences that the useless box can output every time the switch is
triggered.
A key component required in the development of the ultimate useless box is an Arduino Uno.
Incorporation of this microcontroller allows for more advanced sequences and box
performance. From initial brainstorming, Team 11 set out to include a series of motors that can
translate the box on a flat surface during certain output sequences. Coupled with the motors,
distance or infrared sensors will be able to obtain certain distance values when the user
reaches their hand in to hit the switch. These distance values can be interpreted by the
2
Arduino and converted into unique motor outputs to prevent stagnant sequences. LEDs are
also to be integrated, illuminating the interior of the box and creating a variety of lighting
conditions. Furthermore, three servos are to be used instead of one. Alternate design plans to
Figure 1 call for a second servo to lift the lid while the first servo strikes the switch. Besides
simulating this alternate two servo design, a third servo will be added to the ultimate useless
box for some trivial sequence.
Success in creating the ultimate useless box is contingent on a variety of factors. First and
foremost, appropriate mechanical and electrical components need to be selected. Proper
communication among team members will allow for smart design and assembly of the system.
Finally, testing the servos, sensors, and other internal components will ensure smooth
performance of the ultimate useless box.
Design
Components
The various components utilized in this project are listed below in the Bill of Materials.
Table 1. Bill of Materials
Mechanical Design
Although the mechanical components seem relatively simple, the construction of the box and
its interior components was quite difficult. It required that the assembly of the box was well-
planned to easily implement and integrate the internal components. From the outside, the box
looks like a simple wooden box with a lid enclosed by four side walls, which are fixed to the
bottom face (See Figure 2). In order to easily access the internal components, one side wall is
3
completely removable from the assembly, while the other three walls are screwed in through
the bottom. Holes were drilled into the bottom of the sidewall to allow the side wall to rest
over the screws without fastening, which can be seen in Figure 14 of Appendix B. This led to
the sidewall being relatively loose, so it is fastened to the adjacent interior walls with rubber
bands during final assembly.
When creating the lids, both lids were designed to sit flush with the top of the side walls. One
lid also had to pivot to allow for the servo arm to strike the switch. Internal wooden posts were
glued onto the side walls to act as supports for the lids. Since there would be considerable
adjustments after construction, it was desired that both lids could be removed (eliminating the
possibility of hinging the lids on the side walls) to allow for easier maintenance on internal
components. The team decided to attach small screw hooks onto both lids and onto the back
walls. Rubber bands were strung between each pair of hooks. The rubber bands hold the lids
and walls together and allow the lid to pivot as desired at the rear support. Choice in different
strengths of rubber bands allowed for variability in the sturdiness of the box construction.
Another issue with mechanical construction of the box was determining how to attach the box
to the chassis of the Zumo Bot. The team hoped to make the box sit around the Zumo Bot, with
the bottom of the box as close to the ground as possible. Therefore, a hole slightly larger than
the Zumo was cut in the bottom of the box to allow the majority of the Zumo to be hidden
within the box. However, this initially caused a struggle as the team searched for ways to
physically attach the box. It was determined that the best course of action was to fasten a
wood board inside the box approximately one inch above the previously cut hole. The Arduino
4
board could then be bolted to this wood, and the Zumo Bot would attach directly to the
Arduino. This design allowed for the bottom of the box to sit approximately ½” above the
ground, satisfying the team’s initial design hopes.
The final mechanical parts that
needed to be manufactured
were the two arms that are
attached to the servos inside the
box. One arm is used to lift the
lid, while the other arm is
utilized to rotate through the
opening of the lid and hit the
switch back to the original
position. These arms were also
manufactured using the ½” thick
laminate wood, like the exterior
box.
Figure 3. Switch-hitting arm
Electrical Design
The box is driven and controlled by an Arduino Uno with an ATMEGA_328 chip produced by
Atmel. The Arduino microcontroller connects directly to the top of the Zumo Bot by mating the
pins from the Arduino to the corresponding pinholes of the Zumo. The Bot also has a 4xAA
battery case that the team utilized to provide power to the Arduino. The four AA batteries
inserted into the Zumo Bot provide enough power to fuel every component within the circuit.
5
The Zumo Bot also has several internal
components that have interesting capabilities that
will be utilized by the useless box. The most
important function of the Zumo is the two internal
motors. As the schematic diagrams show, these
motors use digital pins 7, 8, 9, 10 of the Arduino
for direction control and speed control for each
motor. The speed of the motors is determined
using pulse-width-modulation, with a maximum
speed value of 400. Since the motors use pins 7-
10, these pins are unavailable for any other
function in the system. Each motor is connected Figure 5. Individual Zumo Bot Motor
to a tread that rotates to move the Bot. Another component of the Zumo Bot that the team
used was the buzzer function. There is a small buzzer that generates sounds that the team can
utilize to play simple musical sequences. To utilize this function, a jumper connects the two
pins around the buzzer, and the buzzer is attached to digital pin 3.
With the Arduino placed upside down into the Zumo Bot, all pin-outs from the Zumo relate
directly to the pin-outs of the Arduino. Therefore all other components are to be connected
directly to the Zumo Bot’s pins. The first component that was interfaced with the Arduino and
Zumo was the switch. One of the switch’s leads is connected to ground, while the other is
attached to digital pin 2. The switch utilizes the Arduino’s internal pull-up resistor on pin 2 in
order to give the board an accurate reading about the state of the switch.
The most important electronic components of the box are the two servos that lift the lid and
flip the switch on the top of the box. If these servos were to function incorrectly, the box would
be unable to turn itself off, leaving the system continuously running programmed sequences.
Another small servo was integrated into the system for one of the box’s specific sequences.
To gain a greater capability for the box to interact with the user, the team added two infrared
sensors to the box’s electronic system. With these sensors, the box can “see” how far away the
user’s hand is, and they have a range of up to 80cm. The signal output wires for the sensors
were placed in the first two analog pins of the Arduino, allowing the Arduino to read an analog
voltage from the distance sensor. The voltage that is read by Arduino correlates to the distance
of the object that the sensor is reading. The addition of these sensors allowed the team to
create sequences for the box that adjust based on the position of the user. The final set of
internal components to be interfaced in the box was the four RGB LEDs, wired in parallel to
make two pairs of LEDs. These LEDs provide color to the inside of the box while the lid is open.
The servos and the LEDS were integrated into the system using an Adafruit 16-Channel Servo
Driver. With the addition of the servo driver, all three servos and the two sets of LEDs could be
controlled simultaneously while using only two analog pins, A4 and A5. The driver has 16
channels that are controlled through I2C, so the signal is communicated to the entire servo
6
driver. However, each signal has an address, and only the desired channels will accept the
signal and complete the action. In the electronic system of this project, the Arduino board is
considered the master, while the servos and LEDs are the slaves. The use of I2C made the
required communication between the Arduino and the other components much simpler.
The servo driver allows the team to control the servos and LEDS using a PWM signal, which
could have a minimum value of 0 and a maximum of 4095. Each individual servo must be
calibrated to find the PWM values that correlate to their minimum and maximum angles. For
the LEDs, the PWM defines the intensity of each color within the LED. For instance, setting
channel 6 to a PWM value of 4095 would light the LED to green at maximum intensity, and
setting channel 6 and 7 to 7 to a PWM of 4095 would create a blended blue-green color.
Another nice feature of the servo driver is its implementation of internal resistors. Each
channel has a 220-ohm internal resistor, which helps limit the current that enters each of the
components. The internal resistors made it very convenient to attach the LEDS and servos to
the board.
Figures 7 and 8 show the internal layout of the box with each component labeled. A full
schematic diagram can be found in Appendix A.
7
1. Arduino (underside of wood)
2. Pololu Zumo Shield
3. Adafruit 16-Channel Servo Driver
4. Servo and lid-lifting arm
5. Servo and switch-hitting arm
6. Two Infrared Distance Sensors
7. Small Servo for Flag
8. Switch
9. RGB LEDs
Programming
The major difficulty the team faced with the programming was related to the timing of the
servo movements for each sequence. If the timing of a sequence is incorrect, the arm that
turns off the switch could collide with other internal components. This action could break the
arm or cause a different failure within the box. With the inclusion of the library for the Adafruit
servo driver, the command “pwm.setPWM(channel, PWM_on, PWM_off)” will move the servo
from the position related to the on PWM to the position related to the off PWM. However, this
did not give the team any control over the speed of movement of the servos. To resolve this
issue and give the team the desired control, the following for loop was developed:
pwm.setPWM(3,0,i);
delay(2);
This code was utilized throughout the project because it allowed the team to easily control the
speed of the servos by adjusting the delay within the loop. Delays were typically set between 1
and 10 milliseconds for the rotation of the three servos.
In order to organize the coding of the project, each sequence is set as its own function named
“SequenceX,” in which X is the order number of the sequence. These sequences are called
8
based on a digitalRead function for the switch located in digital pin 2 of the Arduino. If the
switch is in its original position, the Arduino reads the value as HIGH. When the switch is
flipped, the Arduino reads the pin as LOW, which triggers a sequence being called. The
sequence that is called is determined by a random number generator through Arduino’s
“random(min,max)” function. Please see Appendix E to find the Arduino code for how the
program calls sequences, as well as the rest of the project.
Performance
Each programmed sequence has a unique response before closing the switch. The following
table identifies how well the box executes each sequence with respect to the team’s
expectations.
Sequence – Desired Result Sequence Performance
Sequence 1 –Simple first progression of opening Once the team calibrated the sensors for use
the box, hitting the switch, and closing the box. with the servo driver, the sequence performed
well without any issues.
Sequence 2 – Flips the switch back to its original The distance sensors function well in this
position, but then leave the box’s lid open. The sequence and can easily detect where the user’s
readings of the two distance sensors determine hand is located. However, the box does not
which side of the box the user’s hand is coming always rotate back to the exact initial position as
from. When one of the sensors reads a voltage programmed.
much larger than the other sensor, the box will
rotate away from the user’s hand. This occurs
three times followed by the box rotating to its
original position.
Sequence 3 – The lid fluctuates up and down This sequence performs up to the anticipated
several times before closing. standards.
Sequence 4 – The box spins 180°, closes, and The box is successful in completing this sequence.
then continues to spin back to the user.
Sequence 5 – The box’s lid peaks up and down Sequence is completely successful.
several times. With each peak, the box pulses
forward, then drifts backwards while emitting
different colors.
Sequence 6 – The switch-arm oscillates multiple Sequence is completely successful.
times before finally pressing the switch.
Sequence 7 – Utilizes proportional control by This sequence works extremely well when the
using the IR sensors to determine distance user’s hand is near the box. If the user’s hand is
between the box and user. The box moves either not in view of the sensors, the sensors read a
forward or backward with velocity determined by voltage correlating to a distance of over 3 meters,
an equilibrium distance set in the code. causing the control to be slightly unsteady.
Sequence 8 – The box moves forward while The Zumo’s buzzer is successful in playing the
playing the melody of “Can’t Touch This” through song while the motors spin.
the Zumo’s buzzer.
Sequence 9 – The final sequence yields a waving The sequence is successful.
white flag from the interior of the box.
9
Issues and Difficulties
The team had a few difficulties and problems throughout the project. The first main problem
occurred when initially trying to interface all of the components in the electronic system
together. The team attempted to attach each individual component directly to the
Arduino/Zumo through the necessary analog or digital pins. During these first tests, all
components would try to run at the same time, resulting in the box shaking uncontrollably. In
these situations, the box would not run any of the sequences or the functions that the team
desired. It was this issue that caused the team to decide to use a servo driver to control many
of the system’s electronic components. With the addition of the Adafruit 16-Channel Servo
Driver, the entire system ran much smoother and easily allowed the team to control the box’s
functions.
The other major problem that the team faced occurred a few days
before the final project due date. While testing some of the
designed sequences on the box, the tread on the left side of the
Zumo rubbed up against the body of the Zumo. In an attempt to
pull the wheel outwards away from the Zumo body, the left motor
was accidentally dislodged and the connection severed. To alleviate
this problem, the Zumo had to be opened up to gain access to the
connections. Wires were then soldered between the motor leads
and the internal dual motor pin holes where the motor was initially
Figure 9. Zumo motor fix detached (See Figure 9). The Zumo was then reassembled and
attached to the box. After reattachment, battery performance was extremely poor. This
suggests a short in the circuit might have been created when repairing the Zumo’s motor.
Fixing this problem and replacing the AA batteries with a rechargeable 6V battery is a future
step that would greatly improve on the existing battery performance.
An annoying issue with our ultimate box is the design
of the assembly. Currently, the Arduino is bolted onto
the bottom of the box. In this location, there is no USB
cord access to upload code. Therefore, the entire box
needs to be disassembled to upload new code after
testing. Another problem with this setup is that the
power switch on the Zumo, which provides power to
the entire box, is located in a difficult to reach position
in the interior of the box (See Figure 10).
Next steps for the project would include drilling out a
hole through the box to allow for the Arduino cord to
reach the Arduino. This, paired with the addition of an
on-off switch, would prevent the need to open the box
Figure 10. Limited access to power switch and
Arduino USB port
10
to turn the power on or upload updated code. Also, incorporating the built in compass on the
Zumo shield could help refine several of our sequences. In the current useless box, sequences
where the box rotates or spins are based off of timing delays. These timing delays were
calibrated at a specific battery voltage. When the batteries dip in voltage, the spinning
sequences often do not reach the desired angle. Thus, if the compass was implemented, the
box could spin based upon angle instead of time, providing more accurate final results.
Conclusions
For Team 11, the ultimate useless box represented the culmination of mechatronic knowledge
gained during the duration of ME 445. Not only did the final project reinforce mechatronic
concepts, but it also illuminated the complexities and time required to create a project
controlled by a microcomputer interface. The useless box incorporated features from
mechanical, electrical, and computer programming disciplines. Meshing these three fields
within one project was a considerably difficult obstacle that promoted troubleshooting,
problem solving, and hands-on learning. Though the finished useless box’s appearance was
relatively simple, constructing the box was far from elementary. Countless programming,
electrical, and structural problems were encountered and overcome through persistent
troubleshooting and research. Even though the useless box is a novel toy, the experience
gained during the project can be applied to countless real world applications.
11
Appendix A: Schematic Diagram
12
Appendix B: System Flow Chart
13
Figure14. Removable Side Wall
14
Figure 16. Connection of Zumo Shield to Bottom of Box
60 R² = 0.9947
50
40
30
20
10
0
0 0.5 1 1.5 2 2.5 3 3.5
Voltage Reading
15
Appendix E: Arduino Code
//Call libraries
#include <ZumoMotors.h> // Assigns notes
ZumoMotors motors; unsigned char note[CANT_TOUCH] =
#include <ZumoBuzzer.h> {
#include <Pushbutton.h> NOTE_D(4), NOTE_C(4),NOTE_B(3),
NOTE_A(3),NOTE_A(4), NOTE_A(4),
#include <Wire.h>
NOTE_E(3), NOTE_G(3),NOTE_A(5),
#include <Adafruit_PWMServoDriver.h> NOTE_A(5),NOTE_C(4), NOTE_A(3),
Adafruit_PWMServoDriver pwm = /*NOTE_C(5), NOTE_D(5),NOTE_B(4),*/
Adafruit_PWMServoDriver(); SILENT_NOTE,
NOTE_D(4), NOTE_C(4),NOTE_B(3),
NOTE_A(3),NOTE_A(4), NOTE_A(4),
//Define distance variables
NOTE_E(3), NOTE_G(3),NOTE_A(5),
int LsensorValue; NOTE_A(5),NOTE_C(4), NOTE_A(3)
int RsensorValue; };
16
//Initial Setup protocol pwm.setPWM(11,0,0);
Serial.begin(9600);
motors.flipRightMotor(true); //Sets sequence to zero to prevent repetetive
sequences
pinMode(2, INPUT_PULLUP); //Internal
pullup for switch int seq=0;
pwm.begin(); //Begins pwm for int switchVal = digitalRead(2); //reads the
adafruit value of the switch as HIGH or LOW
pwm.setPWMFreq(60); // This is the //Serial.println(switchVal);
average analog frequency
//int pot=analogRead(A2); //reads the
uint8_t twbrbackup = TWBR; // save I2C amount of voltage through the potentiometer
bitrate
//int potmult=pot/1024; //finds the
// must be changed after calling Wire.begin() percentage of the maximum 5 volts that passes
(inside pwm.begin()) through the pot
TWBR = 12; // upgrade to 400KHz! //Serial.println(switchVal);
currentIdx = 0;
//Creates sequence number when switch is
triggered
//Set initial servo positions
if (switchVal == LOW) { //if the switch is
pwm.setPWM(0, 0, lidd); in the on position
pwm.setPWM(3, 0, armd); randomSeed(analogRead(5)); //reads an
pwm.setPWM(15,0, flagd); unused pin to make random numbers different
for each use
if (n==10)
{
delay(2000);
n=1;
}
}
seq=n; //random(1,7); //calls a random
void loop() { number to pick the sequence
//Sets pwm on LEDs to zero Serial.println(seq);
pwm.setPWM(5,0,0); //Sequence6();
pwm.setPWM(6,0,0); }
pwm.setPWM(7,0,0); if (seq==1){ //calls sequence 1
pwm.setPWM(9,0,0); Sequence1();
pwm.setPWM(10,0,0); delay(100);
17
n=n+1; n=n+1;
} }
if (seq==2){ //calls sequence 2 if (seq==8){ //calls sequence 2
Sequence2(); Sequence8();
delay(100); delay(100);
n=n+1; n=n+1;
} }
if (seq==3){ //calls sequence 2 if (seq==9){ //calls sequence 2
//Sequence3(); Sequence9();
delay(100); delay(100);
n=n+1; n=n+1;
} }
if (seq==4){ //calls sequence 2 delay(100);
Sequence4(); //Serial.println(seq);
delay(100); }
n=n+1;
} //Simple open close sequence
if (seq==5){ //calls sequence 2 void Sequence1(){
Sequence5(); int i;
delay(100);
n=n+1; //Open box
} for (i=lidd; i>lidu; i--)
if (seq==6){ //calls sequence 2 {
Sequence6(); pwm.setPWM(0,0,i);
delay(100); delay(5);
n=n+1; }
}
if (seq==7){ //calls sequence 2 //Lift arm
Sequence7(); for (i=armd; i>armu;i--)
delay(100); {
18
pwm.setPWM(3,0,i); for (i=lidd; i>lidu+10; i--)
delay(2); {
} pwm.setPWM(0,0,i);
}
//Close arm delay(500);
for (i=armu; i<armd;i++) //Lift arm
{ for (i=armd; i>armu;i--)
pwm.setPWM(3,0,i); {
delay(2); pwm.setPWM(3,0,i);
} }
//Close arm
//Close box for (i=armu; i<armd;i++)
for (i=lidu ; i<lidd; i++) {
{ pwm.setPWM(3,0,i);
pwm.setPWM(0,0,i); delay(10);
delay(5); }
}
} //Rotates box based on direction of hand
coming in
while (counttot<3)
{
//Box moves away depending on direction the
operator moves towards //Read sensors
void Sequence2(){ int LsensorValue = analogRead(A1);
//Define count variables int RsensorValue = analogRead(A0);
int i;
int counttot=0; //If closer to right sensor
int countL=0; if (RsensorValue > LsensorValue + 150)
//Accounts in variability of sensors
int countR=0;
{
//Turns on specific lights and motor
//Open box direction for a second
19
pwm.setPWM(9,0,0); delay(10); //Delay in between
reads for stability
pwm.setPWM(6,0,0);
counttot=countR+countL; //Creates
pwm.setPWM(11,0,4000); total count
pwm.setPWM(7,0,4000); }
motors.setLeftSpeed(-250); //Turns off LEDs
motors.setRightSpeed(250); pwm.setPWM(9,0,4000);
delay(1000); pwm.setPWM(6,0,4000);
motors.setLeftSpeed(0); pwm.setPWM(11,0,4000);
motors.setRightSpeed(0); pwm.setPWM(7,0,4000);
pwm.setPWM(11,0,0);
pwm.setPWM(7,0,0); //Returns box to initial position after moving
countL = countL +1; //Adds to above
count if (countL>0)
} {
if (LsensorValue > RsensorValue + 150) //If motors.setLeftSpeed(250);
something is closer to left comp than right one
motors.setRightSpeed(-250);
{
delay(countL*1000);
pwm.setPWM(9,0,4000);
motors.setLeftSpeed(0);
pwm.setPWM(6,0,4000);
motors.setRightSpeed(0);
pwm.setPWM(11,0,0);
countL=0;
pwm.setPWM(7,0,0);
}
motors.setLeftSpeed(250);
if (countR>0)
motors.setRightSpeed(-250);
{
delay(1000);
motors.setRightSpeed(250);
motors.setRightSpeed(0);
motors.setLeftSpeed(-250);
motors.setLeftSpeed(0);
delay(countR*1000);
pwm.setPWM(9,0,0);
motors.setRightSpeed(0);
pwm.setPWM(6,0,0);
motors.setLeftSpeed(0);
countR = countR +1; //Adds to
count countR=0;
} }
20
//Shuts off all LEDs {
pwm.setPWM(9,0,0); pwm.setPWM(3,0,i);
pwm.setPWM(6,0,0); delay(15);
pwm.setPWM(11,0,0); }
pwm.setPWM(7,0,0); for (int n=0; n<6; n++)
pwm.setPWM(10,0,0); {
pwm.setPWM(5,0,0); //Turn off red lights
//Close box pwm.setPWM(10,0,0);
for (i=lidu+10 ; i<lidd; i++) pwm.setPWM(5,0,0);
{ //Vibrate lid
pwm.setPWM(0,0,i); for (i=lidu; i>250; i--)
} {
} pwm.setPWM(0,0,i);
}
delay(100);
//Vibrating lid with flashing red lights pwm.setPWM(10,0,4000);
void Sequence3(){ pwm.setPWM(5,0,4000);
int i; for (i=250; i<lidu; i++)
{
//Open box and turn on red lights pwm.setPWM(0,0,i);
pwm.setPWM(10,0,4000); }
pwm.setPWM(5,0,4000); delay(100);
for (i=lidd; i>lidu; i--) }
{
pwm.setPWM(0,0,i); delay(1050);
delay(1); //Hit switch with arm
} for (i=460; i>360;i--)
{
//Lift arm, but not all the way pwm.setPWM(3,0,i);
for (i=650; i>460;i--) delay(2);
21
} pwm.setPWM(0,0,i);
//Turn off red lights delay(6);
pwm.setPWM(10,0,0); }
pwm.setPWM(5,0,0); delay(100);
//Close arm
for (i=360; i<650;i++) //Lift arm
{ for (i=armd; i>armu;i--)
pwm.setPWM(3,0,i); {
delay(2); pwm.setPWM(3,0,i);
} delay(2);
}
//Close box delay(100);
for (i=lidu ; i<540; i++) //Close arm
{ for (i=armu; i<armd;i++)
pwm.setPWM(0,0,i); {
} pwm.setPWM(3,0,i);
} }
delay(100);
//Close box
//Sequence spins box and hit switch for (i=lidu ; i<lidd; i++)
void Sequence4(){ {
int i; pwm.setPWM(0,0,i);
//Spins the box delay(4);
motors.setLeftSpeed(-200); }
motors.setRightSpeed(200); delay(100);
delay(800); //Turn off Motors
motors.setLeftSpeed(0);
//Opens the box motors.setRightSpeed(0);
for (i=lidd; i>lidu; i--) }
{
22
//Box peeks up and down //Sets colors and peeks the box up/down
void Sequence5(){ for (int n=0; n<2; n++)
int i; {
delay(300); delay(1500);
//Open box if (n==0)
for (i=lidd; i>250; i--) {
{ pwm.setPWM(9,0,0);
pwm.setPWM(0,0,i); pwm.setPWM(6,0,0);
} pwm.setPWM(11,0,4000);
delay(100); pwm.setPWM(7,0,4000);
pwm.setPWM(10,0,0);
//Lift arm pwm.setPWM(5,0,0);
for (i=armd; i>420;i--) }
{ if (n==1)
pwm.setPWM(3,0,i); {
} pwm.setPWM(9,0,4000);
pwm.setPWM(6,0,4000);
//Close arm pwm.setPWM(11,0,0);
for (i=420; i<armd;i++) pwm.setPWM(7,0,0);
{ pwm.setPWM(10,0,0);
pwm.setPWM(3,0,i); pwm.setPWM(5,0,0);
delay(10); }
}
//Set motors in motion while lid lifts
//Close box motors.setLeftSpeed(-300);
for (i=250 ; i<lidd; i++) motors.setRightSpeed(-300);
{ //Peek box up
pwm.setPWM(0,0,i); for (i=lidd; i>300; i--)
delay(10); {
} pwm.setPWM(0,0,i);
23
}
delay (200); //Lift arm
motors.setLeftSpeed(0); for (i=armd; i>380;i--)
motors.setRightSpeed(0); {
delay(700); pwm.setPWM(3,0,i);
motors.setLeftSpeed(100); }
motors.setRightSpeed(100); //Hold and move forward
//Peek box down motors.setLeftSpeed(-100);
for (i=300; i<lidd; i++) motors.setRightSpeed(-100);
{ delay(1000);
pwm.setPWM(0,0,i); motors.setLeftSpeed(0);
delay(10); motors.setRightSpeed(0);
} //Close arm
motors.setLeftSpeed(0); for (i=380; i<armd;i++)
motors.setRightSpeed(0); {
} pwm.setPWM(3,0,i);
//Set red lights to on delay(10);
pwm.setPWM(9,0,0); }
pwm.setPWM(6,0,0);
pwm.setPWM(11,0,0); //Close box
pwm.setPWM(7,0,0); for (i=lidu ; i<lidd; i++)
pwm.setPWM(10,0,4000); {
pwm.setPWM(5,0,4000); pwm.setPWM(0,0,i);
delay(5);
//Open box }
for (i=lidd; i>lidu; i--)
{ //Turns off lights
pwm.setPWM(0,0,i); pwm.setPWM(9,0,0);
} pwm.setPWM(6,0,0);
delay(10); pwm.setPWM(11,0,0);
24
pwm.setPWM(7,0,0); }
pwm.setPWM(10,0,0);
pwm.setPWM(5,0,0); //Box uses sensors to move towards or away
from the operator
}
void Sequence7(){
//Simultaneous movement, and the trigger arm
vibrates //Define step variable
void Sequence6(){ int i;
pwm.setPWM(0,0,220);
delay(100); //Open box
pwm.setPWM(3,0,420); //pwm.setPWM(0,lidd,lidu);
delay(400); pwm.setPWM(0,0,lidd);
pwm.setPWM(3,0,450); delay(300);
delay(200); for (i=lidd; i>lidu; i--)
pwm.setPWM(3,0,420); {
delay(200); pwm.setPWM(0,0,i);
pwm.setPWM(3,0,490); delay(5);
delay(200); }
pwm.setPWM(3,0,420); delay(500);
delay(200);
pwm.setPWM(3,0,450); //Record sensor values to create proportional
control
delay(200);
for(int count=0; count<=100; count++)
pwm.setPWM(3,0,420);
{
delay(200);
float LpsensorValue = analogRead(A1);
pwm.setPWM(3,0,450); //Read left sensor value on pin A0
delay(200); float RpsensorValue = analogRead(A0);
pwm.setPWM(3,0,360); //Read right sensor value on pin A1
25
} if (diff<=0)
else { pwm.setPWM(9,0,0);
{TsensorValue<LpsensorValue; pwm.setPWM(6,0,0);
} pwm.setPWM(11,0,4000);
float Tvolt = TsensorValue/1024*5; //Finds pwm.setPWM(7,0,4000);
voltage of sensor
}
float Tpow = pow(Tvolt,-1.12);
//Intermediate calculation else{
if (Tdist>=200){
motors.setLeftSpeed(-200); delay(50);
motors.setRightSpeed(-200); }
} //Stop motors
if (Tdist>=100){ motors.setLeftSpeed(0);
motors.setLeftSpeed(-100); motors.setRightSpeed(0);
motors.setRightSpeed(-100);
} //Lift arm
{ {
motspeed=diff*kp; pwm.setPWM(3,0,i);
motors.setLeftSpeed(-motspeed); }
motors.setRightSpeed(-motspeed);
} //Close arm
//Set colors for LED when moving in certain for (i=armu; i<armd;i++)
direction {
26
pwm.setPWM(3,0,i); //motors.setRightSpeed(100); //sets speed
of right motor to defined value
delay(10);
}
buzzer.playNote(note[currentIdx],
duration[currentIdx], 15);
//Close box currentIdx++;
for (i=lidu ; i<lidd; i++) }
{
pwm.setPWM(0,0,i); // Insert some other useful code here...
} // the melody will play normally while the rest
} of your code executes
// as long as it executes quickly enough to
keep from inserting delays
void Sequence8(){
// between the notes.
motors.setLeftSpeed(80);
motors.setRightSpeed(80);
// For example, let the user pushbutton
pwm.setPWM(0,0, 200); function as a stop/reset melody button
delay (300); if (button.isPressed())
int num=0; {
buzzer.stopPlaying(); // silence the buzzer
//Plays the notes if (currentIdx < CANT_TOUCH)
while(num<1800) currentIdx = CANT_TOUCH; // terminate
{ the melody
Serial.println(ZUMO_BUTTON); else
27
pwm.setPWM(7,0,4000); {
pwm.setPWM(9,0,4000); pwm.setPWM(0,0,i);
pwm.setPWM(10,0,4000); delay(10);
pwm.setPWM(11,0,4000); }
delay(300); delay(1000);
pwm.setPWM(3,0,340);
delay(500); //Raise flag
motors.setLeftSpeed(-100); pwm.setPWM(15,0,flagu+50);
motors.setRightSpeed(-100); //Wave flag
pwm.setPWM(3,0,600); for (count=0; count<3; count++)
delay(3000); {
pwm.setPWM(0,0,500); for (i=flagu+50; i>300 ;i--)
pwm.setPWM(5,0,0); {
pwm.setPWM(6,0,0); pwm.setPWM(15,0,i);
pwm.setPWM(7,0,0); delay(7);
pwm.setPWM(9,0,0); }
pwm.setPWM(10,0,0); delay(100);
pwm.setPWM(11,0,0); for (i=300; i<flagu+50;i++)
motors.setLeftSpeed(0); {
motors.setRightSpeed(0); pwm.setPWM(15,0,i);
} delay(7);
}
//Flag wave delay(100);
void Sequence9(){ }
//Define count variables //Lower flag
int i; for (i=flagu+50; i>flagd; i--)
int count = 0; {
pwm.setPWM(15,0,i);
//Open box delay(1);
for (i=lidd; i>lidu; i--) }
28
//Lift arm partially
for (i=armd; i>460;i--)
{
pwm.setPWM(3,0,i);
delay(15);
}
//Fully raise arm
for (i=460; i>armu;i--)
{
pwm.setPWM(3,0,i);
delay(2);
}
//Close arm
for (i=armu; i<armd;i++)
{
pwm.setPWM(3,0,i);
delay(2);
}
//close box
for (i=lidu ; i<lidd; i++)
{
pwm.setPWM(0,0,i);
delay(2);
}
}
29
Final Report
Linear Course Adjusting Hovercraft
Brushless Motoring and Arduino Processor
Nam Pham
Ben Gorenc
EXECUTIVE SUMMARY
Our team has developed a unique design of a remote control hovercraft. The
proposed designed only required one motor powerful enough to control both the
throttle and steering of the hovercraft in addition to providing enough power for lift.
Such a unique design essentially prevents the need to have two separate motors to
control the throttle and lift. With the usage of a digital proportional device, the
controller will be able to command the hovercraft to travel as desired.
In addition, the hovercraft also has an implementation of an additional feature that
enable the hovercraft to auto adjust its linear course of travel. If no steering signal
is received by the arduino, the hovercraft will automatically adjust itself to travel in
a straight path to mitigate drift.
1
ME 445
Final Report: Remote Control Hovercraft
ACKNOWLEDGEMENT
Our team would like to thank Dr. Sommer for providing us with the necessary
resources outside of class and in lecture to complete our project. We would also like
to thank Michael Robinson for helping us with our laboratories and project. His
guidance and resources were necessary in troubleshooting and completing the
hovercraft. Without such guidance from both, none of this would be made possible
and we would have never realized how important mechatronics are to the world.
2
ME 445
Final Report: Remote Control Hovercraft
TABLE OF CONTENTS
3
ME 445
Final Report: Remote Control Hovercraft
1. INTRODUCTION
The intention of the project is to build a remote control hovercraft that has the
capability of traveling across almost any surface. The basic idea of a hovercraft is its
ability to significantly reduce friction by generating a cushion of air between the
craft and a surface.
Normally, the design of a hovercraft consists of two motors that provide the
necessary power and lifting. One motor would be focusing on the maneuvering and
acceleration of the hovercraft, while the other motor would focus on providing
enough air to inflate the skirt and produce lift. Instead of using the standard two
motor design, our team opted to use only one. This means that the motor needs to
have enough power to generate lift and accelerate the hovercraft. A stepping motor
would not provide the necessary power that the team needed. Thus, using a
brushless motor to duct the air flow between thrust, lift, and skirt inflation is the
most sensible option. The final design of the hovercraft is shown below in Figure 1.
4
ME 445
Final Report: Remote Control Hovercraft
The initial design and construction objectives in which our team set to complete
were as follow:
1. Functional one motor design that is as effective as possible
2. Produce enough power to lift and accelerate the hovercraft
3. Effectively using the ESC, Wireless Receiver, and RC controller to
communicate with the hovercraft
Throughout the course of the designing, constructing, and testing the prototype, our
team set and met additional objectives:
1. Usage of a gyroscope to aid in the automatic straightness maneuvering of the
hovercraft while traveling in a straight path
a. Find the best location for the installation of the gyroscope
b. Reduce vibration transmitted to the gyroscope to get accurate readings
2. Usage of Arduino to effectively control the steering of the servo
There are two main categories existing within the system. The physical category
which involves components such as the materials used for construction of the body
and the physical form of the circuit in the hovercraft. The other category within the
system is the electrical process which contains the communication between the
coded Arduino processor and the physical electrical components.
2.1 Materials
The materials that are required to complete the design and construction of the
hovercraft are as follow:
1. Finishing Plywood – Use to create the majority of the main structure of the
hovercraft due to its lightweight/sturdy feature.
2. Polystyrene Styrofoam Board – Shroud cover for the fan of the motor. This
helps to focus the air flow which increases the efficiency of the power used to
lift and propel the vehicle.
3. White Cardboard Paper – Lightweight material used for rudders
4. Parachute Nylon Fabric – Light, flexible, and tough fabric used for the skirt
5. Heavy Duty Tape – For fixture and tie down components
6. Seven Blade Computer Fan 120mm – Generate a more evenly distributed air
airflow at a higher efficiency than traditional props.
7. Epoxy Glue – For fixture of hovercraft structure
5
ME 445
Final Report: Remote Control Hovercraft
6
ME 445
Final Report: Remote Control Hovercraft
Figure 3. A view of the circuitry with completed and assembled wiring and connection
2.3 Cost
Many of the materials and electronic components were found within the lab.
Therefore, the actually cost is a lot less than what is presented in the bill of
materials as seen from Table 1. Nevertheless, the estimated costs for these
components are shown to give a rough estimate of the overall cost to build this
particular prototype if one were have to buy the majority of these materials and
components.
7
ME 445
Final Report: Remote Control Hovercraft
Some items are discontinued, therefore, the prices are not presented. In this
particular case, the LISY 300AL 3-Axis Gyroscope has been discontinued and the
price could not be estimated. This is the same for the digital proportional controller.
Bill of Materials:
Material
Item(s) Quantity Vendor Price(s)
Balsa Hard Wood Sheet (1/4") 1 Lowes $ 14.99
Lightweight Polystyrene Foam Insulation 1 McMaster-Carr $ 18.11
Recycled Nylon Tarp 1 -- $ -
Recycled White Cardboard Paper 1 -- $ -
Heavy Duty Tape 1 -- $ -
Recycled Computer Fan 120mm 1 -- $ -
Wood Glue 1 -- $ -
Electronic Components
Item(s) Quantity Vendor Price(s)
Futaba Digital Proportional Controller 1 -- $ -
Swift 20A ESC 1 Ebay $ 16.73
Futaba R168DF Wireless Receiver 1 Ebay $ 29.99
Arduino Uno 1 Spark Fun $ 29.95
Brushless Motor 1 -- $ -
LISY 300AL 3-Axis Gyroscope 1 -- $ -
Traxxas 2018 Servo 1 Active Powersport $ 15.17
Tenergy NiMH 9.6V 2000mAh Battery 1 All-Battery $ 14.99
Futaba NR-4QB 4.8VDC 600mAh Battery 1 Tower Hobbies $ 17.99
330 uF Capacitor 1 Amazon $ 0.36
1k Ohms Resistor 1 Amazon $ 0.05
Total: $ 176.33
Table 1. An estimated bill of materials. The prices may vary depending on the chosen vendors
As presented in the bill of materials, the total price comes out to be roughly $175.
This total may vary depending on the chosen vendors. According to our
interpretation, the gyroscope would be the one of the most expensive parts to buy.
Unfortunately, we could not find a reasonable vendor that gives a reasonable price
estimate. In addition, this particular gyroscope model has been discontinued as
mentioned above.
Our team was fortunate enough to find a spare brushless motor in the lab. So the
total cost is drastically reduced. Without any type of brand/model number, the
estimated price of the brushless motor could not be predicted.
8
ME 445
Final Report: Remote Control Hovercraft
3. PROTOTYPE DESIGN
Figure 4. Air flow schematic and mechanical damping system. [1] represents the airflow from front
to rudders. [2] represents the airflow distribution from the front to the tarp and the air hole at the
bottom. [3] shows the mechanical damping system.
Ideally the proposed design consists of having one brushless motor to both throttle
and lift the hovercraft. The air flow schematic shown above in Figure 4 shows the
theory behind the design. Air intake from the top half of the motor [1] will be
utilized to produce thrust. The attached rudders on the back allow the hovercraft to
angle the airflow and rotate the hovercraft.
The air intake from the bottom half of the motor [2] will be used to inflate the skirt,
as well as providing lift through a vent in the bottom of the hovercraft. Both
functions work together to produce the necessary lift for the hovercraft.
9
ME 445
Final Report: Remote Control Hovercraft
prevented the hovercraft from generating any actual lift. In addition, the skirt was
not large enough, which limited the inflation size of the skirt. This hindered the
creation of an air pocket under the hovercraft.
Through further improvements, the design became more efficient and came close to
completing our initial objectives. This can be seen from Figure 6.
As one can see from the provided figure, our team has made several drastic
additions to the initial design. The motor area now has a shroud, which acts like a
funnel, to focus air intake making the movement of air much more efficient.
The rudder system is installed behind the motor. As a result, the hovercraft is now
able to turn left and right in response to the remote controller. The servo is fixed
behind the motor, and serves to rotate the two rudders. This system is connect to
both rudders, and as the servo rotate to one side, the connected arm, while moving
with the servo rotation, rotates both rudders at the same time and at the same rate.
Using two rudders as oppose to one will give us better control over the steering of
the hovercraft as it will displace more air to the desireable side.
10
ME 445
Final Report: Remote Control Hovercraft
11
ME 445
Final Report: Remote Control Hovercraft
The silicon tube attachments shown from the figure are the main components
within of the isolation system. The vibration is absorbed in the four silicon tubes
which are soft and flexible. The vibration is transferred to the tubes from the
bottom base, and is drastically reduced by the time it reaches the gyro. Therefore,
the noise in the gyroscope is greatly reduced.
Aside from the gyroscope implementation, the skirt was also replaced to a more
desirable material. Instead of hard plastic, the new skirt is made out of parachute
nylon material. This alternative is much lighter, less stiff, and stronger. In addition,
our team was able to make the skirt bigger without having more material to work
with. Consequently, these changes allow more air to build up within the inflated
skirt and increase the hovercraft lifting functionality. The method the team used to
attach the skirt to the structure has also been improved, drastically reducing and
preventing air leakage.
4. ELECTRICAL DESIGN
12
ME 445
Final Report: Remote Control Hovercraft
Unfortunately, there existed a lot of noise. Such noise caused the motor to operate
on its own without the user’s input. A low pass filter was implemented in attempt to
eliminate some noise. We were unable to completely eliminate the noise in our
circuit but did manage to eliminate enough of it to make our circuit functional.
As we improve the circuit design, our team were able to narrow down the exact
objectives in which we want to accomplish. The following flow-chart (Figure 8)
represents the command hierarchy of the communication between the electronic
components. The communication starts when we connect the batteries and turn on
the proportional controller. This is when the signal is on.
Power Arduino
Source Code
Servo Gyroscope
Record
Rotate L/R
Coordinates
Motor
Throttle
13
ME 445
Final Report: Remote Control Hovercraft
4.2 Result
The finalize circuitry design consists of using two batteries to power the devices.
The first battery, which is the Tenergy 9.6V 2000mAh NiMH, is used as the main
power source for the brushless motor. The reason our team decided to go with such
method is due to the large power consumption of the brushless motor. The other
smaller capacity battery (Futaba 4.8VDC 600mAh) is the main power source for the
Arduino. The servo and the gyroscope pull the necessary power from this smaller
capacity battery.
While in operation, the servo tended to twitch to extreme positions sometime. This
is mostly due to unaccountable noises from the surrounding. To tackle such
problem, our team decided to add in a capacitor (330 uF) parallel to that of the
source and ground to filter out some of the noise. As a result of such filtering, the
noises are reduced drastically and the communication between the controller and
the servo are much more responsive.
For further detail on the circuit of this project, please refer to Appendix B.
5.1 Servo
With using the built-in servo library, we were able to successfully communicate the
proportional controller with the installed servo on the hovercraft. We assigned a pin
to read the pulse signal from the controller that is then sent to the wireless receiver
(input), and declared a variable for the pin that is connected to the signal line
(output) of the servo. The servo pulse-in signal ranges between values of 1100 to
1900. We decided to increase the range from 1000 to 2000 to cover all potential
values in the case of noise affecting the source code commands.
Since our pulse-in signal ranges between values in thousands, we converted the
signal to values in which is easier to understand. In this case, the converted values
are in degrees. “Serial.println()” command was used during the process of testing
the servo, but later taken out after the calibration is completed.
The code also consist of a function with several “if” statements to satisfy the
mechanical steering. One goal of the steering is to have the rudders reset to the
14
ME 445
Final Report: Remote Control Hovercraft
neutral (center) position if no command is sent. The other goal is to have the
rudders turn to the desired position, limiting its ability to over-steer and damage
the fragile rudders.
The communication between the controller and the servo produced a lot of noise
even while in neutral position and no command is sent. The noises range between
85-95 degrees. As a result of this, the servo twitched back and forth between the
mentioned values. To combat this noise, we put in “else-if” statements to set the
servo at the neutral position if the readings fall between the range of these two
values. We increased the high noise value to 97 and low to 83 to prevent possible
signal spiking from affecting the desired modifications.
5.2 Gyroscope
The gyro was simple to implement because we only desired the angular rate in the
horizontal plane. All that was required as a 3.3V input, a ground, and an out signal
from the gyro. The signal from the gyro was then converted into an angle that was
used to set the parameters in our code that would force the hovercraft to self-align
when attempting to move straight.
There was initially a large amount of noise in the gyro data from the vibration of
the brushless motor. This was greatly reduced with the addition of the mechanical
isolator and made the data readable from the gyro.
However, an issue that came about with the gyro was that the gyro gave more
sensitive readings in the CCW direction by a factor of roughly 3. This made it
incredibly difficult to write a code to effectively make the hovercraft move in a
straight line. This is because the gyro would never return to a zero position after
drifting off course. Unfortunately, this discovery was made too late in the course of
the project to order a new gyro. That being said the hovercraft is capable of moving
relatively straight without being steered for roughly 10-15 feet.
6. CONCLUSION
Overall, we were both very happy with the outcome of our project. We tackled a
unique project, overcame many unforeseen obstacles, and gained incredible
knowledge in the field of mechatronics.
In addition, we have learned how to read through data sheets, interface and
program an Arduino with a multitude of components. This knowledge is absolutely
going to benefit us in the future through many facets of our life. In fact, both my
partner and I are going to purchase Arduinos so that we can continue to explore the
field of mechatronics. We both are pleased with the outcome of our project and
thoroughly enjoyed this class and thankful that we took it.
15
ME 445
Final Report: Remote Control Hovercraft
// servo
int pulse_servo = 8; // set servo pin for wireless transmitter
int val_servo;
int PWMval_servo;
#include <Servo.h> // calls servo library
Servo servoMain;
// gyro
int gyroPin = A0; //Gyro is connected to analog pin A0
float gyroVoltage = 3.3; //Gyro is running at 3.3V
float gyroZeroVoltage = 1.0; //Gyro is zeroed at 1.0V
float gyroSensitivity = .0033; //3.3mV/deg/sec
float rotationThresholdHigh = 40; //Minimum deg/sec to keep track of - helps with gyro drifting
float rotationThresholdLow = 5; //Minimum deg/sec to keep track of - helps with gyro drifting
void setup ()
{
Serial.begin(9600);
void loop()
{
val_servo = pulseIn(pulse_servo, HIGH); // used to send wireless signal to servo
PWMval_servo = map(val_servo, 1000, 2000, 0, 179); // scale it to use it with the servo (value between
0 and 180)
steer(PWMval_servo); // calls steer function and sends servo value to it
}
void steer(int b)
{ // steers servo based on value passed to it
16
ME 445
Final Report: Remote Control Hovercraft
currentAngle = 0;
}
else if (b > 152 && b < 179)
{ // limit maximum servo angle CCW
servoMain.write(152);
currentAngle = 0;
}
else if (b < 42 && b > 0)
{ // limit maximum servo angle CW
servoMain.write(42);
currentAngle = 0;
}
else if (b < 83 && b > 0)
{ // Turn the servo steering CW
servoMain.write(PWMval_servo);
currentAngle = 0;
}
else if (b > 83 && b < 97) // moving straight no direction sent from controller
{ // align function used to mitigate hovercraft drift
align();
}
else
{
}
void align()
{
for(int i=0;i<10;i++) // used to find average gyro reading
{
//This line converts the 0-1023 signal to 0-5V
float gyroRate = (analogRead(gyroPin) * gyroVoltage) / 1023;
//Ignore the gyro if our angular velocity does not meet our threshold
if (gyroRate >= rotationThresholdHigh || gyroRate <= -rotationThresholdLow)
{
//This line divides the value by 100 since we are running in a 10ms loop (1000ms/10ms)
gyroRate /= 100;
17
ME 445
Final Report: Remote Control Hovercraft
avgAngle += gyroRate;
}
currentAngle = currentAngle + avgAngle/10; //adds average angle to current angle
avgAngle=0;
}
//DEBUG prints current angle
Serial.println(currentAngle);
else
{ // reset angle to catch errors
currentAngle = 0;
}
}
18
ME 445
Final Report: Remote Control Hovercraft
19
ME445 F2013
Submitted to
Dr. H.J. Sommer
as a part of
ME 445
by
We would like to give special thanks to the following individuals for their support and guidance
throughout this project for without them this project would not have been possible:
The turrets main components are two servo gearboxes mounted in the channels that make up the
turret arms. The horizontal gearbox is attached directly to the turret’s tripod core while the
vertical gearbox is connected to a custom bracket that holds the paintball gun in place. The turret
frame and mount were constructed mainly from machined Aluminum components. The paintball
mounting bracket and tripod core we hand machined from Aluminum stock while the turret arms
and tripod legs were purchased. The power source for the system is a 6 volt sealed lead acid
battery.
The complete design included a control box that would allow the user to perform more advanced
functions. Two toggle switches controlled the power and firing safety, while two standard push
button switches control the electronic firing mechanism and change the firing mode. In order to
fire the gun remotely an electronic switch was inserted into the gun handle and connected to the
Arduino system. Initially the code allowed for three firing modes: semi-automatic, three shot
burst and fully automatic. However, due to coding errors only the semi-automatic and three shot
burst mode could be demonstrated. The control box also supports an LCD feedback screen that
shows the user which firing method and control mode they had switched to, as well as the
number of shots they had fired. This system functioned properly, although the number of shots
fired was not always accurately displayed. The control box also contained a LED ring that would
track the motion of the motor as it rotated. However, near the end of the constructions process it
was apparent that the LED ring’s Arduino library created a non-negligible amount of
interference in the motors, causing them to oscillate.
The motion of this turret was intended to by controlled through three different methods: Joystick
input, Wii Nunchuck motion control and External Motion Tracking Software. The joystick
contained two potentiometers that sent control signals to the Arduino; these controls were then
coded and sent to the motors. This system provided effective control for the system and allowed
the turret to rotate 180 degrees horizontally and 120 degrees vertically. Due to time constraints,
it was not possible to incorporate the other two motion control systems. However, the control
box was built with the necessary components and the control methods would not be difficult to
add to the system given more time.
Page v
CHAPTER 1. INTRODUCTION
The first aircraft to utilize the turret concept was the British Royal Airforce’s Boulton & Paul
Overstrand in 1933. Since then, aircraft turret designs, like those used on naval vessels, have
been modified and improved allowing for the use of larger bore cannon and more effective
control systems. Last but not least, the ground combat vehicle put the turret concept into use in
1914 on the Rolls-Royce armored car. The first tracked vehicle to use a turret was the French
Renault FT light tank which debuted in the latter part of World War I.
The original inspiration for this project was the result of a variety of interests and hobbies. Most
importantly, an interest in both mechatronics, paintball and modern next generation military
technology was the deciding factor in the pursuit of this project. The idea for a turret capable of
moving on two axis with user controlled inputs came from the AH-64 Apache’s Hughes M320
Chain Gun targeting system. That control system for the M320 is integrated into the gunner’s
flight helmet to provide real time targeting information allowing the gunner to simply turn his
head and look at his target to aim the cannon.
Sporting grade turrets such as those used to aim and shoot a paintball marker differ from
traditional military turrets in that they are intended for non-lethal applications and do not enclose
or protect the paintball marker or user. These turrets are generally non-commercialized and very
unique to the person who designed/built the turret. As a result, the availability of a turret
platform that can be easily modified for additional functionality is severely limited.
Page 1
CHAPTER 2. BENCHMARKING
During our initial research we found several paintball turret designs available. Of the designs
found, only one was a commercially available product. Most of the designs found were just the
final product with some discussion on performance and construction. Of the designs found, the
following three provided enough information to give some insight into their construction and
performance. These three designs represent some of the more advanced concepts which utilize
various forms of targeting systems via a video camera and computer interface.
This turret uses two geared servo motors to control the yaw and pitch motions of the gun. Unlike
other designs, the control system for this turret does not utilize an arduino platform. This system
uses a pololu servo controller which receives commands from a computer based motion tracking
software program. A base structure made from iron pipe was utilized to which the yaw direction
servo mounter was fixed. From there the vertical upright holding the pitch direction motor and
paintball gun was attached. Targeting and firing for this turret was controlled using the motion
tracking program developed by RealSentryGun.com. This turret platform is also the only design
found that can be purchased commercially.
Page 2
CHAPTER 3. Turret Design
2-axis rotation
Target acquisition and tracking
Multiple firing rates
Simplicity of control input
System feedback to the user
The security system consists of 3 levels: a key, a keypad and an RFID card scanner. The key is
inserted next to the power button and closes the connection between the control box and the
battery when turned. After the key has been inserted, the power button lights up, and the user can
press it activate the control systems. Once the power button has been pressed, the user will be
prompted to enter a 3 digit passcode using the 3x4 membrane keypad. Finally, if the correct
passcode has been entered, the user will be prompted to swipe an RFID entry card over the box.
After the correct card has been scanned the user will be granted access to the turret’s control
systems.
The 2-axis motion of the turret is controlled by the small joystick in the lower left hand corner of
the box. This component has two potentiometers that send analog signals to either the vertical or
horizontal motor. Whenever one of these signals causes motion in the motors, it also affects the
small ring of LED’s in the upper left hand corner of the box. The right half of this ring is
composed of red LED’s that indicate the vertical position of the gun, while the top half is
Page 3
composed of blue LED’s that indicate the horizontal position of the gun. This allows the user to
get some sense of where the gun is pointed if they cannot see it.
The final aspect of the control system is the LCD feedback and the firing control buttons. Once
the system has been activated and the security code has been entered, the screen will indicate
how many shots have been fired, which control mode is being used and which firing mode the
gun is set too. Three buttons on the right side box control the firing code: a red toggle switch, a
white push button and a green push button. The green push button fires the gun, the red toggle
switch acts as the safety, preventing the gun from being fired if it isn’t pressed, and the white
push button switches between the firing modes. The three firing modes for this system are semi-
automatic, three shot burst and fully automatic. However, if the mode button is pressed a fourth
time and the motion control toggle switch is flipped, the turret will shift to motion tracking mode
and both the motion and firing controls will be overwritten by an external program until the
switch is flipped again.
Page 4
CHAPTER 4. Materials and Construction
Page 5
it was determined that the turret would need to be capable of rotating 78.13 degrees/s in order to
keep up with a target.
Figure 4: Central Hub with Channel and Motors Mounted. Figure 3: Servo Gearbox
In choosing the motors for our project we decided to determine the feasibility of the channel
mount servo gearboxes available from ServoCity. These motors were available with various
gear ratio options. Furthermore, there were three options available for the motor in the gearbox
assembly giving a wide range of torque options. It was found that a gear ration of 3.8:1 would
be satisfactory as this yielded a rotation rate of 78.9474 degrees/second with a torque value of
680.2 oz-in. This motor was chosen for use with the horizontal direction. A smaller motor also
using a 3.8:1 gear ratio providing 338.2 oz-in. at 6.0 volts was chosen for the vertical motion
control.
Page 6
4.4. Electronics Selection
LCD Screen
This 3x4 keypad was chosen because it could be attached easily to the surface of
control box with its adhesive backing and interface with the Arduino through a
seven pin system. It also runs off a library that seemed to cause little interference
with the rest of the system.
These switches connected easily to the pin system of the Arduino and allowed
for a choice between toggle or push button switches without a difference in cost.
Figure 8: Control Switch
2-Axis Joystick
This component provided some slight difficulty as the joystick returned to its
original position and reset its potentiometer values to zero. However, this problem
was easily solved by some slight adjustment to the Arduino code and the wiring on
the board made it much easier to connect to the Arduino than some of the library
Figure 9: Joystick based controls that were considered.
Page 7
CHAPTER 6. RISK AND SAFETY
The main risks of operating a paintball gun come from unintentional discharge. This can be
prevented by ensuring that the safety is on unless you are preparing to fire. Furthermore,
protective clothing and head gear should always be worn when operating a system like this as
paintballs can cause minor injury to unprotected skin.
Safety concerns for this particular system include possible electrocution from the battery and
accidental contact with the turret as it rotates. In order to prevent electrical injury be sure that the
battery is disconnected before attempting any rewiring of the control box. In most cases, contact
with the turret is also not a large concern. However, when the system is activated the turret will
reset and rotate to its resting position. For this reason, turret users should step at least 10 feet
away from the system when turning on the main power switch.
Page 8
REFERENCES
1. http://www.tippmann.com/SafetyTips.aspx
2. http://mne.psu.edu/brennan/ME545/2012/FinalProjects/Swanson_WorkingPortalTurr
et/M%20E%20597D%20Final%20Project.htm
3. http://www.instructables.com/id/Autonomous-Paintball-Sentry-Gun/
4. http://realsentrygun.com/
5. http://projectsentrygun.rudolphlabs.com/home
6. http://www.instructables.com/id/Autonomous-Paintball-Sentry-Gun/
7. http://en.wikipedia.org/wiki/Gun_turret
8. http://en.wikipedia.org/wiki/Rolls-Royce_Armoured_Car
9. http://en.wikipedia.org/wiki/Battleship
Page 9
APPENDIX A. Arduino Code
//Declare Included Libraries------------------------------------------------
#include <Servo.h>
#include <SoftwareSerial.h>
#include <Keypad.h>
#include <Adafruit_NeoPixel.h>
const int FireLED = 25; //Pin Number for Fire Switch LED
const int SafetyLED = 31; //Pin number for Safety Switch LED
const int ModeLED = 45; //Pin number for ModeSwitch LED
const int VoltGreenLED = 11; //Pin mumber for Green Battery LED indicator
const int VoltYellowLED = 12; //Pin mumber for Yellow Battery LED indicator
const int VoltRedLED = 13; //Pin mumber for Red Battery LED indicator
const int ToggleLED = 9; //Pin number for Toggle Switch LED
const int RFIDPin = 38; //Pin mumber for RFID trigger input
//Declare Objects-----------------------------------------------------------
Servo YawServo; //Create servo object for rotation about z-axis
(Left/Right)
Servo PitchServo; //Create servo object for rotation about y-axis (Up/Down)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, NeopixelPIN, NEO_GRB +
NEO_KHZ800);
Page 10
//Declare Global Variables---------------------------------------------------
int KeyInput; //Accepts Keypad Input for Security loop
int CodeKey1; //1st digit input for passcode
int CodeKey2; //1st digit input for passcode
int CodeKey3; //1st digit input for passcode
float RFID; //Detects change in RFID signal
int sec; //Changes with RFID signal to open second
security loop
int j; //Counting variable (security loop)
int HorzPos=90;
int VertPos=90;
int LCDInitial = 1;
//Keypad Setup---------------------------------------------------------------
//Keypad Prep Code (KeyPad Pins: Keypad Face Up --> 2 through 8, right to
left)
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
{1,2,3},
{4,5,6},
{7,8,9},
{0,0,0}
};
byte rowPins[ROWS] = {34, 32, 30, 28}; //connect to the row pinouts of the
keypad
byte colPins[COLS] = {26, 24, 22}; //connect to the column pinouts of the
keypad
Page 11
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void setup(){
Serial.begin(9600); //Initilize Serial Communication
lcd.begin(9600); //Initialize LCD Communication
strip.begin(); //Initialize LED Ring
strip.show(); // Initialize all pixels to 'off'
//Define Interrupts
attachInterrupt(4, ModeSelection, RISING); //Mode switch
//set the size of the display if it isn't 16x2 (you only have to do this
once)
Page 12
lcd.write(0xFE);
lcd.write(0xD1);
lcd.write(16); // 16 columns
lcd.write(2); // 2 rows
delay(10);
// we suggest putting delays after each command to make sure the data
// is sent and the LCD is updated.
// clear screen
lcd.write(0xFE);
lcd.write(0x58);
delay(10); // we suggest putting delays after each command
// go 'home'
lcd.write(0xFE);
lcd.write(0x48);
delay(10); // we suggest putting delays after each command
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void loop(){
if(LCDInitial == 1){
LCD(); //Set initial LCD readout
LCDInitial = 0; //Prevent LCD() from being called in void loop again
}
Page 13
digitalWrite(SafetyLED, HIGH);
digitalWrite(FireLED, HIGH);
//For a valid fire mode call function to initiate firing
if(SystemMode < 3 && SystemMode > 0){
FireControl = Fire(SystemMode, FireControl); //Call Fire to begin
firing.
}
}else{
digitalWrite(SafetyLED, LOW);
digitalWrite(FireLED, LOW);
}
motion();
Voltage = BatteryCheck(Voltage);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void security_loop(){
KeyInput = customKeypad.getKey();
if (KeyInput){
j = j + 1;
}
lcd.print(CodeKey1);
lcd.print(CodeKey2);
lcd.println(CodeKey3);
}
lcd.print(CodeKey1);
lcd.print(CodeKey2);
lcd.println(CodeKey3);
}
lcd.print(CodeKey1);
lcd.print(CodeKey2);
lcd.println(CodeKey3);
}
Page 14
CodeKey1 = 0;
CodeKey2 = 0;
CodeKey3 = 0;
lcd.println("Incorrect");
delay(1000);
lcd.print(CodeKey1);
lcd.print(CodeKey2);
lcd.println(CodeKey3);
}
while(sec != 7){
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void ModeSelection(){
//PRE: SystemMode mut have an integer value [0, 4].
//POST: Changes the value of SystemMode by 1 with values between 1 and 4.
unsigned long currentMillis = millis(); //check the local time stamp in
milliseconds
ToggleState = digitalRead(TogglePin);
Page 15
}else if(ToggleState == HIGH){
if(SystemMode < 2){
SystemMode = SystemMode +1; //Update SystemMode
}else{
SystemMode = 1;
}
}
LCD();
tracking_ring();
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//Perform for
if (State == HIGH){
switch(Mode){
Page 16
previousMillis = currentMillis; //Save the last time a round was
fired
digitalWrite(FiringPin, HIGH); //Send Fire Signal
//delay(Dwell); //Delay is used here because code will not exit
loop until counter is satisfied and the timing must be kept to turn off the
fire command
//digitalWrite(FiringPin, LOW); //Output Low - no fire value
i = i + 1; //Update counter
//Output for debugging
Serial.print(" i = ");
Serial.print(i);
}
if(currentMillis - previousMillis > Dwell){
digitalWrite(FiringPin, LOW); //Output Low - no fire value
}
return PreState;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
int motion(){
int Vertical = analogRead(A1); //Read in value for vertical motion
int Horizontal = analogRead(A0); //Read in value for horizontal motion
int HorzVal;
int VertVal;
Page 17
HorzPos = HorzPos +1;
}else if(HorzVal < 60 and HorzPos < (YawUpperBound - 4)) { //For
moderate stick motion change position faster so long as the pos change
doesn't exceed bounds
HorzPos = HorzPos + 3;
}else if(HorzVal>60 and HorzPos < (YawUpperBound - 9)) { //For large
stick motion change position fast so long as the pos change doesn't exceed
bounds
HorzPos = HorzPos + 10;
}else { //If
stick position otherwise exceed bounds, adjust by one
HorzPos = HorzPos + 1;
}
}
}else if (HorzVal < -5 && HorzPos > YawLowerBound) {
if(HorzPos > YawLowerBound){
if(HorzVal > -20) { //For
slight stick motion change position slowly
HorzPos = HorzPos - 1;
}else if(HorzVal > -60 and HorzPos > (YawLowerBound + 4)) { //For
moderate stick position change position faster so long as the pos change
doesn't exceed bounds
HorzPos = HorzPos - 3;
}else if(HorzVal < -60 and HorzPos > (YawLowerBound + 9)){ //For
large stick motion change position fast so long as the pos change doesn't
exceed bounds
HorzPos = HorzPos -10;
}else { //If
stick position otherwise exceed bounds, adjust by one
HorzPos = HorzPos -1;
}
}
}
Page 18
if(VertPos > PitchLowerBound){
if(VertVal > -20) { //For
slight stick motion change position slowly
VertPos = VertPos - 1;
}else if(VertVal > -40 and VertPos > (PitchLowerBound + 4)) { //For
moderate stick position change position faster so long as the pos change
doesn't exceed bounds
VertPos = VertPos - 3;
}else if(VertVal < -40 and VertPos > (PitchLowerBound + 9)){ //For
large stick motion change position fast so long as the pos change doesn't
exceed bounds
VertPos = VertPos -10;
}else { //If
stick position otherwise exceed bounds, adjust by one
VertPos = VertPos -1;
}
}
}
PitchMotion(VertPos); //Call Motion funciton
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//Function to output signal to Servo controlling Yaw (Left/Right)
void YawMotion(int Position){
//PRE: Takes in a posiiton signal in degrees.
//POST: Maps the input variable to a desired PWM value in microseconds and
outputs the position as a microsecond PWM signal to motor
if(Position > YawLowerBound && Position < YawUpperBound){
Position = map(Position, 0, 180, 1350, 1820); //Map degree position to a
microsecond value and correct for central position
YawServo.writeMicroseconds(Position);
}
}
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//Function to output signal to Servo controlling Pitch (Up/Down)
void PitchMotion(int Position){
//PRE: Takes in a position value in degrees.
//POST: Maps the input variable to a desired PWM value in microseconds and
outputs the position as a microsecond PWM signal to motor
Page 19
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
int BatteryCheck(float Volts){
const int R1 = 5280000; //Resistor 1 value
const int R2 = 5240000; //Resistor 2 value
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
void LCD(){
switch(SystemMode){
case 1:
lcd.println();
lcd.print("Shots Fired: ");
lcd.print(NumShots);
lcd.print(" "); //If shots go to double digits, make " ",
cannot go triple digits
lcd.print(" Manual"); //"Manual" or "Auto " (MUST be 6 characters)
lcd.print("-");
lcd.print("Single"); //"Normal", "FullAuto" or "SemiAuto" (must be 8
characters or less)
break;
case 2:
lcd.println();
lcd.print("Shots Fired: ");
lcd.print(NumShots);
lcd.print(" "); //If shots go to double digits, make " ",
cannot go triple digits
lcd.print(" Manual"); //"Manual" or "Auto " (MUST be 6 characters)
lcd.print("-");
lcd.print("Burst"); //"Normal", "FullAuto" or "SemiAuto" (must be 8
characters or less)
Page 20
break;
case 3:
lcd.println();
lcd.print("Shots Fired: ");
lcd.print(NumShots);
lcd.print(" "); //If shots go to double digits, make " ",
cannot go triple digits
lcd.print(" Motion"); //"Manual" or "Auto " (MUST be 6 characters)
lcd.print(" ");
lcd.print("Track"); //"Normal", "FullAuto" or "SemiAuto" (must be 8
characters or less)
break;
}
}
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
void tracking_ring(){
if (LED_BackHor != SenValVert){
colorSet(strip.Color(0, 0, 0), LED_BackHor);
}
if (LED_FrontHor != SenValVert){
colorSet(strip.Color(0, 0, 0), LED_FrontHor);
}
if (LED_BackVert != SenValHor){
colorSet(strip.Color(0, 0, 0), LED_BackVert);
}
if (LED_FrontVert != SenValHor){
colorSet(strip.Color(0, 0, 0), LED_FrontVert);
Page 21
}
}
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
//---------------------------------------------------------------------------
-----------------------------------------------------
// Fill the dots one after the other with a color
void colorSet(uint32_t c, uint16_t i) {
uint8_t bright=25;
strip.setPixelColor(i, c);
strip.setBrightness(bright);
strip.show();
Page 22
Appendix B. 2D Technical Part Drawings
Page 23
Page 24
FINAL IMAGES
Page 25
Final Project Report
Hybridized RC Toy Car
Submitted By: Sergio Mendoza
Ji Liu
ME 445
Section II........................................................................................................................................................ 7
2. Subsystems ....................................................................................................................................... 7
Section III..................................................................................................................................................... 22
Section IV .................................................................................................................................................... 26
Appendix A .................................................................................................................................................. 28
Bill of Material......................................................................................................................................... 28
Appendix B .................................................................................................................................................. 29
Code ........................................................................................................................................................ 29
Matlab – Battery Capacity test ........................................................................................................... 29
Appendix C .............................................................................................................................................. 47
Cutsheets ................................................................................................................................................ 47
4
a. Series HEV b. Parallel HEV c. Power Split HEV
Figure 1.1 Schematic of the different components and connections in the different types of hybrid
electric vehicle architectures. [3]
5
two servos that control throttling and the steering capabilities of the car and displays
information such as speed, temperature and receiver battery voltage. In this project the
original radio communication of the base model was used to control steering, ignition and read
temperature and angular speed of the main axle. The signal to the high torque servo is hacked
and transmitted to an Arduino Uno to control a Brushless DC motor, implement regenerative
braking and charge a Lithium polymer battery pack.
6
Section II
2. Subsystems
2.1 Motor selection
The alternatives in the market for electric machines include steppers, servo motors,
brushed and brushless electric motors (BLDC). The high accelerations and speeds that
characterize an RC vehicle require the motor to deliver a large amount of torque to the final
drive. Furthermore, it is desired to minimize the maintenance requirements. Based on these
objectives, steppers and servomotors are not appropriate choices for this work. DC motors,
both brushed and brushless, represent sound alternatives to meet the desired requirements of
the toy car. The main advantage of brushed DC motors is the existence of simple strategies for
speed control and regenerative breaking. Additionally, brushed motors do not require
commutation; thus, requiring no sensors for operation. However, the tradeoff between torque
and angular speed of the shaft is large due to brush friction. Friction also causes brushes to
wear out and maintenance for this type of motors is usually higher. In the absence of brushes,
BLDC motors demand less maintenance. The main benefit of a flatter torque versus speed curve
on BLDC motors is that power output can be approximated as constant for a larger range of
angular speeds. Based on these characteristics, the motor chosen was BLDC motor.
2.2 DC Brushless Motor
In a series hybrid electric vehicle, there are two electric machines serving different purposes
on the car. The inverter is mechanically connected to the internal combustion engine and
electrically connected to the battery pack and the motor. The purpose of the inverter is to
convert the mechanical energy into electrical energy. The second electric machine, commonly
referred as the motor is powered by the battery pack and connected to the final drive through
the differential. The motor is used to generate the torque required for vehicle propulsion, and
also to convert the vehicle’s kinetic energy into electrical energy to be stored in the battery
pack during regenerative breaking.
A BLDC motor is composed of a permanent magnet rotor and wire wound state poles.
Electrical energy is transformed to mechanical energy by the torque generated by the magnetic
interaction between the rotor and the coils of wire on the stator. The simplified BLDC motor
7
diagram shown in Figure 2.1 reduces the BLDC motor to a three coils with star connection
representation with three pairs of North and South magnetic poles. A motor with this type of
configuration is set in motion by energizing two phases simultaneously [5].
8
Figure 2.2 Sensor Voltage Output versus Drive Time for a Three Phase BLDC Motor
The top axis in Figure 2.2 indicates the current phase as designated in Figure 2.1. From this
diagram it can be inferred that the output of the three sensors give a unique combination of
high or low level for each drive phase. The bottom axis in Figure 2.2 designates the sensor
position code matching with those subscribed around the motor in Figure 2.1. This three-bit
code is used to feed the position of the rotor back to the motor controller. For instance, when
the digital level of sensor A, B, and C are high, low and high respectively, that is the bit number
is 101, the terminals A and B are energized. So the rotor rotates into a new position where
sensors output is 001, then the terminal B is disconnected and terminal C is energized while
terminal A is still energized. In this way, continuous rotation is achieved.
2.4 Design of a BLDC Motor Control
As discussed earlier, BLDC motors require the coils in the stator to be activated in a logic
sequence to maintain continuous rotation. The overall circuit architecture for motor speed
control used in this project is shown in Figure 2.3. The commutation circuit design is based on
the work earlier done by [6]. This circuit is composed of N-channel and P-channel transistors
(or Darlingtons), resistors and diodes. The diodes are used to reroute current flow required for
regenerative braking, which will be discussed in Section 2.5.
9
Figure 2.3 Overall Architecture of a Speed Control Circuit for a BLDC Motor
Figure 2.4 shows a more detailed image of the circuit design for energizing a single coil. The
circuits for the other two coils are identical.
Figure 2.4 Commutating Circuit Design for a Single Coil of a BLDC Motor
Commutation in this circuit is done by switching the corresponding A High, B High, and C
High transistors by using pulse width modulation (PWM) while keeping all of the A Low, B Low,
and C Low transistors open all the time, or the other way around.
Figure 2.5 shows the current flow path when the sensor signal is 101. In this case, coils A
and B are energized. The A High transistor is turned on with a PWM determined by the position
of the gas pedal. The B Low transistor is kept open until the sensor signal changes.
10
Figure 2.5 Current Flow through the Commutation Circuit When Sensor Signal is 101
2.5 Regenerative Breaking
Regenerative braking improves fuel consumption by harvesting the kinetic of the HEV and
storing it in the form of electrochemical energy in the battery pack. In modern HEV,
regenerative braking is done by reversing the current path in the motor-battery circuit while
the car decelerates. During this operation, the motor plays the role of a generator and redirects
the current back to the battery pack. To accomplish regenerative breaking, previous authors in
the literature [7] modify the commutating circuit by adding components (diodes) that allow
current switching.
Figure 2.6 shows an example of the current trajectory during regenerative braking. In this
case, current flows through the diode on A High, through the battery and through B Low. This
allows current to be stored in the battery.
11
The essential part for regenerative braking is to switch A Low, B Low, and C Low transistors
on and off with a PWM signal, while A High, B High, and C High transistors are off. The diodes
are placed to prevent the current from the battery to flow into the coils and allow current to
flow through when A High, B High, and C High transistors are off. During regenerative braking,
the current should flow in the opposite direction to allow current to be stored in the battery
pack. Regenerative braking works only if the motor slows down faster than in the absence
regenerative braking, and if current is allowed to flow into the battery.
2.6 Battery Pack
Table 2.1. Battery Parameters
Parameter Value
Nominal Capacity 5.3 A·hr
Nominal Voltage 7.4 V
Max Discharge Voltage 8.4 V
Max Charge Voltage 6.2 V
This project uses a 5.3 A·hr (nominal capacity) lithium-polymer battery pack with
specifications listed in Table 2.3. Lithium-polymer batteries bend easily to the power demands
of a hybrid electric vehicle. To design an efficient power management strategy, it is incumbent
to have a deeper understanding of the battery SOC dynamics. In the context of this work, SOC is
defined as the ratio of the electrochemical energy stored in the battery to the maximum
storage capacity of the pack. To achieve adequate knowledge of the battery pack while
maintaining simplicity, this work uses a first order equivalent circuit as shown in Figure 2.7 and
Characterized by Equation 2.1. In this type of model the battery open circuit voltage, OCV, is a
function of the battery’s SOC. The internal resistance of the battery is a function of the pack’s
SOC and direction of the current.
R
+
+ I
V V
_
_
Figure 2.7 First Order Equivalent Circuit of a Battery
12
The first step in constructing a battery model is to determine the pack electrochemical
energy storage capacity. To determine battery capacity an experiment was conducted. In this
experiment, the batteries were charged and discharged at constant current, constant using an
Arbin cycler. Figure 2.8 below shows a schematic of the experimental setup. Battery cycling
was done at a slow rate of 0.2C rate for one cycle. The rate of charge and discharge wass
measured as the applied current relative to the battery capacity. [5] Table 2.2 below
summarizes the experiment schedule used for capacity estimation.
Voltage sensors
Battery pack
13
Battery charge and discharge capacities vary depending on chemistry of the positive and
negative electrodes and also on the charge and discharge rates. To minimize deviation in charge
and discharge capacities and accurately approximate measured voltage to OCV, this experiment
was done at low current rates. Figure 2.9.a shows the battery pack voltages during charge and
discharge at 0.2 C-rate. Figure 2.9.b shows the current and voltage profiles applied to the
battery pack during the battery capacity test.
8
Voltage (V)
7.5
Pack#1
6.5
Pack#2
6
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
SOC, Charging
8
Voltage (V)
7.5
Pack#1
6.5
Pack#2
6
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
SOC, Discharging
Figure 2.9.a Voltage curves for a 5.3A-h nominal capacity Lipo pack at 0.2 charge-discharge rates
14
Experimental Voltage Profile for a LiPo Battery Pack
8.5
Pack#1
Pack#2
8
Voltage(V)
7.5
6.5
6
0 5 10 15
time(h)
0.5
-0.5
-1
-1.5
-2
0 5 10 15
time (h)
Figure 2.9.b Constant-current and constant-voltage charge and discharge curves for a Lipo pack
The charge and discharge capacity of the battery pack is estimated by integrating the
area under the current versus time profiles. In order to approximate the area under the curve,
the numerical method used was the trapezoidal rule. Once the charge and discharge capacities
have been obtained, the battery pack capacity is taken as the average of these two capacities.
After conducting charge-discharge measurements, the battery pack capacity was found to be
5.24 A·hr. The data collected for the pack capacity experiment also serves to generate a look
up table relating OCV to the SOC of the battery pack. The sampling frequency for this
experiment was 100 Hz. To generate a look up table at predetermined values of SOC, a cubic
spline is function is fitted to the raw voltage signal. Figure 2.10 shows the experimental
voltages measured during charge and discharge, the smooth polynomial fitted to the raw signal,
and the average voltage between the charge and discharge measured voltage or OCV.
15
8.5
6.5
6
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
SOC
Figure 2.10 Open circuit voltage as a function of SOC curves during charge and discharge
As seen in Equation #2.1, battery voltage in a first order equivalent circuit model is a
function of open circuit voltage and battery internal resistance. To identify the internal
resistance of the battery, a second experiment was conducted. In this experiment current
pulses with a magnitude of 1.05 were applied to the battery after the battery had been allowed
to relax for a 30 minute period. The relaxation period was chosen after observing that this
particular chemistry does not seem to display large relaxation dynamics. This behavior can be
seen both in Figure 2.9b and 2.11. As shown in these figures, the voltage open circuit voltage of
the battery does not show significant immediately after charge or discharge.
Current is applied until the battery reaches a desired state of charge and allowed to
relax for 30 minutes before a new pulse is applied. This procedure was repeated for SOC 0-
100%. The battery internal resistance is calculated as the change in voltage divided by the
magnitude of the current applied at each SOC. The resistance value found in this experiment
does not vary much within the SOC operating window and the value used for SOC estimation is
0.011 Ohm. Figure 2.11 shows the current and voltage profiles applied to the battery pack to
identify battery internal resistance.
16
Experimental Voltage Profile for a LiPo Battery Pack
8.5
8
7.5
Voltage(V)
7.5
7
7
Pack#1
6.5 6.5
Pack#1
Pack#2 Pack#2
6 6
0 2 4
0 2 4 66 8
8 10
10 12
12 14
14 16
16 18 18
time(h)
time(h)
Experimental Current Profile for a LiPo Battery Pack
1.5
Experimental Current Profile for a LiPo Battery Pack
1.5
1
1 0.5
Current (A)
0
0.5
Current (A)
-0.5
0
-1 Pack#1
Pack#2
-0.5 -1.5
0 2 4 6 8 10 12 14 16 18
-1 time (h) Pack#1
Pack#2
-1.5
0 2 4 6 8 10 12 14 16 18
time (h)
Figure 2.11 Experimental Current and Voltage Profiles for a Pulse Test
2.7 Battery Charger
The Lithium Polymer battery pack used in this project is prone to cell imbalance during
cycling. Over time, cell imbalances lead to capacity fade and thermal gradients within the
battery which can result in thermal runaway. To avoid cell imbalances, the terminals of the
generator were connected to a charging circuit before any charge was stored. The battery
charger implemented uses an LT 1513-IR Single-ended Primary-Inductor Converter (SEPIC)
circuit designed by Linear Technology as presented in Figure 2.12.
17
Figure 2.12 Schematic of the Charging Mechanism for the Battery Pack.
The portion of the circuit within the green dotted lines is the SEPIC topology which allows
the voltage output to match a desired reference point for input voltages greater than, less than
or equal to the reference voltage. The R5C5 pair connected to pin 1 of the LT1513 chip enforces
loop frequency compensation. Similarly, the R4C4 filter connected to pin 3 convert the current
sense to a smooth signal and feeds it back to the chip current feedback pin. The charging
current is controlled by pin 3 and is equal to the regulating voltage (100mV) over R 3, in this case
1.25A. The voltage limit is set by the ratio of R1 and R2. R2 is selected according to the
suggested value by Linear and R1 is calculated based on the maximum charging voltage
recommended by the manufacturer of 8.4 volts. Figure 2.13 is a picture of the battery charger
being tested at different input voltages.
18
2.8 Hardware Modifications
The layout of the modified Losi model in this project is shown in Figure 2.14. As discussed
earlier, a series HEV requires additional mechanical and electrical components in the vehicles
drive train. To accommodate the new parts, it is necessary to make changes to the structure of
the chassis and manufacture additional gears to connect the electric machines to the internal
combustion engine and the differential center gear. The gears used in the original mode are
custom made. In this work, a new pinion gear for the IC engine and center differential were
designed using Solid Works and manufactured using 3D printing. Figure 2.15 shows the four
manufactured gears for this project.
1
Chassis Layout
1 Anodized Aluminum Chassis 2
2 On Board Starting System 3
3 Internal Combustion Engine 4
4 Centrifugal Clutch 5
5 Fuel Throttling Servo
6 Steering Servo 6
19
Adding a second platform to the existing model is the main modification done to the
original chassis. The second platform serves as a base for the internal combustion engine, the
fuel throttling servo, the battery pack and the circuitry required for motor control and battery
charging The differential and the electric motor are installed on the original chassis alone with
the telemetry equipment, gas tank, tail-pipe, suspension and transmission systems. This
platform is of acrylic and is connected to the original chassis by three sustaining rods. The rods
are drilled in to the car chassis at key locations to maintain the chassis strength.
2.9 Power management Strategy
When operating a car, the user has basic input commands such as pedal position for speed
control. The driver has no control about the engine operation or battery charge-discharge
scheduled. To guarantee that the performance demanded by the user is met, a power
management strategy is needed.
The goal of HEV power management is to maximize the performance of the vehicle’s power
train while meeting the user’s power demands. The literature in HEV is rich in sophisticated
and complex power management strategies for this type of vehicles. This work is based on the
primacy that series HEV allow the controller to run each component at its most efficient point
separately. This strategy is limited by the accuracy of SOC estimation of the equivalent circuit
approximation used to model the battery pack. The two main objectives of this power
management are to minimize the consumption of fuel by the internal combustion engine, and
to eliminate the risk of battery over charge/discharge and thus reduce battery degradation.
Figure 2.16 captures the logic used to code the power management algorithm in the Arduino.
The SOC range of operation for this work is limited to a minimum of 20% and a maximum 80%
of the battery capacity. This limitation imposed by the accuracy of the battery model being
used to estimate SOC, but it also serves to minimize battery aging.
20
Figure 2.16 Power Management Algorithm Logic Diagram
This section presented the basis for motor selection, the circuit design for motor control
and modifications needed for regenerative braking. Also, two experimental procedures were
discussed to characterize a battery model to capture SOC dynamics and implement a power
management algorithm. Furthermore the required hardware modifications required to
accommodate additional components into the car structure were discussed. Finally, a simple
multi-objective power management strategy to meet the user’s demand was introduced.
21
Section III
3. Results and Discussion
This project is divided in six stages to facilitate progress tracking. Each stage is assigned a
main task as follows:
1- Diagnosis of mechanical and electrical components.
2- Design and build circuitry to operate all subsystems on the vehicle.
3- Create a Power management strategy.
4- Build any additional circuitry.
5- Coupling of the mechanical and electrical components and implement control
strategy.
6- Test & troubleshoot.
During the first stage of this project, the main components and parts from an
abandoned RC car are collected. A conventional RC car was then assembled to test the gear
coupling in the drivetrain and the operability of the internal combustion engine and the
automatic starting system. At this point the RC car running on the internal combustion engine
was successfully tested. It was during this stage that the motor was selected. The brushless DC
motors were tested using a motor control manufactured by Novak. This motor control was
discarded because such controller does not allow regenerative braking.
The second phase of this project involved the design and construction of different
circuits. The first step was to identify an architecture that allowed regenerative braking. The
commutation circuit for a BLDC motor requires an estimate of the rotor’s position. The first step
taken in writing the code to operate the motor was testing the output of Hall Effect sensor to
obtain a relationship between rotor position and sensor output. The next step was to turn on
the transistors in the right sequence according to the relationship obtained in step one. The
transistor is turned on by giving a digital high level signal from the Arduino. To allow
regenerative braking, the commutation circuit was modified by adding diodes. This modification
permits charge to be stored in the battery by switching the direction of the current flow. At this
point, a simple test was conducted to test the regenerative braking capabilities by comparing
the time it takes the motor to go from full speed to zero with and without regenerative braking.
22
When regenerative braking was enforced this time was found to be smaller. The pedal position
in this case was simulated with a potentiometer varying the PWM signal that controls motor
speed. Another indication that the regenerative braking is operational is that when the power
supply to the motor was cut and the motor was allowed to decelerate, a voltage with a
magnitude around 6 volts was measured across the motor terminals. Within this stage the
signal from the radio communication system of the Losi-Ten to the throttling servo was hacked
and routed to the Arduino. The Arduino read the pulse width of the signal and map it to a PWM
signal that controls the speed of the electric motor. The final deliverable of this stage was the
selection and implementation of a battery charger.
Once the circuitry needed for each of the subsystem was selected and tested, in stage
three, a simple power management strategy based on available the sensor signals and
actuators was determined. As part of this stage, the experiments for battery characterization
were conducted. Such experiments required the construction of special battery connectors to
hook the battery packs to an Arbin cycler for testing. The experiment to measure battery
capacity took 15 hours and the experiment to identify the internal resistance of the battery
lasted approximately 18 hours. From these experiments, SOC- VoC tables needed for SOC
estimation were obtained and included in the Arduino code.
The next stage involved modifying the terminals of the BLDC motor to connect to the
breadboard and Arduino to measure each of the Hall Effect sensors and the temperature
sensors. Additionally at this point in the project, the need to design and build additional gears
was identified. Most threads and gears on the original RC car are custom made which
represented a major set-back. At this point new gears were designed and prototyped using a 3D
plotter. However, the strength of the material used in 3D plotting is not optimal and the
components started to show wear after simple testing. Gears continue to be an issue and need
to be manufactured using a different material.
The fifth stage of the project demanded the coupling of the mechanical and electrical
systems. All mechanical connections were tested for the RC car to run on the IC engine on stage
one. The electrical motor was tested individually and its capability to produce enough torque
was assessed in stage one as well, however due to the issues with the prototyped gears; it was
23
not possible to run the car using the electric motor. The last step in this stage was to find a
strategy to fit the additional components on the car. Holes were drilled on metallic L-shaped
brackets as the one shown in Figure 3.1 to hold the electric machines and bolt down on the car
chassis.
Figure 3.1 Simpson Strong-Tie 4 9/16 in. x 4 3/8 in. x 1-1/2 in. Angle
An acrylic plate was to serve as a platform that holds the internal combustion engine, the
throttling servo and the generator. This platform is held by three ¼” threaded rods that tie on
to the car chassis. All the required parts to mount the additional components are ready at this
time.
The final stage of this project comprises testing and troubleshooting; currently there are
still some bugs associated with the motor commutation control. Particularly motor start up is
not always smooth. The main limitation on the mechanical part continues to be the gears and
using an alternative material is required before the mechanical connections are finalized. At this
point the time to complete this project is approximately two weeks. Table 3.1 summarizes each
one of the tasks and subtasks as well as the deliverables. The items shown as green in the
deliverables column have been completed and those in orange still need work.
24
Table 3.1 Project Matrix
Main Task Sub tasks Deliverables
• Diagnosis of existing electromechanical
• Test gear coupling in the drivetrain.
Diagnosis of mechanical and electrical components.
• Test DC motor
components • Inventory of all required circuitry
• Test IC engine and automatic starter
components for coupling
• Individual circuits to operate the DC
• Design and build circuitry to control:
motor
• Hack into the starter
Design and build circuitry to operate • Ability to turn on and off the IC engine
• Brushless DC motor
all subsystems in the vehicle with an external controller
• Design corresponding part for remote control
• Modified and implemented a battery
• Select a circuit architecture for battery charging
charger
• Create a simple power management strategy to
determine usage of battery vs. IC engine • SOC estimator
Create a power management strategy
• Develop an SOC estimator • Power management strategy
• Design a circuit for regenerative breaking
• Build circuits for throttle control
Build additional circuitry and • Operational circuits that can be controlled
• Build circuits for sensor monitoring
mechanical from an Arduino or radio receiver.
• Build Circuit for regenerative circuit
25
Section IV
4. Conclusions & Future Work
1. This project is intended to be an educational tool for students with different backgrounds to
understand the different systems in a hybrid electric car.
2. A systematic process including the design considerations to transform a commercial RC car in to
a series hybrid electric toy is presented.
3. A motor control using an Arduino, transistors, resistors and diodes is built to control the speed
and implement regenerative braking.
4. A series of experiments are run to characterize the battery pack being used in this application.
5. A first order equivalent circuit model is used to understand the SOC dynamics of the battery
pack and enforce more effective power management.
6. A simple multi-objective power management strategy is designed and implemented.
7. A battery charger is built to safely charge the battery pack via the regenerative breaking and the
internal combustion engine.
8. Complete all mechanical coupling required and test fully functional model
9. Design PCB board for motor control and regenerative braking
10. Configure Wi-Fi shield
11. Manufacture gears using a titanium alloy
12. Add a flow meter to the gas lines to estimate gas consumption
26
Works Cited
[1] U. D. o. Energy, "Energy Efficiency and Renewable Energy," Alternative Fuel Data Center, [Online].
Available: http://www.afdc.energy.gov/data/?q=electricity. [Accessed 14 December 2013].
[2] U. D. o. Energy, "Energy Efficiency & Renewable Energy," Alternative Fuels Data Center, [Online].
Available: http://www.afdc.energy.gov/data/10301. [Accessed 14 December 2013].
[3] M. Rothenberger, An Interactive Framework for Education on Vehicle Electrification, State College,
PA: The Pennsylvania State University, 2012.
[4] A. Mendoza and J. Argueta, "Performance Characterization GM EV-1," Southern California Edison -
Electric Transportation Division, 2000.
[5] W. Brown, "Brushless DC Motor Control Made Easy," Microchip Technology, Inc.
[6] J.-X. Chen , J.-Z. Jiang and X.-Y. Wang, "Research of Energy Regeneration Technology in Electric
Vehicle," Journal of Shanghai University, vol. 7, no. 2, pp. 173-177, 2003.
[7] J. Cody, G. Ozdemir , Z. Nedic, A. Nafalski and Aaron Mohtar, "Regenerative Nraking in an Electric
Vehicle," Zeszyty Problemowe Maszyny Elektryczne , vol. 81, pp. 113-118, 2009.
[8] C. Rahn and C.-Y. Wang, Battery Systems Engineering, John Wiley & Sons, 2013.
27
Appendix A
Bill of Material
Total $ 907.22
28
Appendix B
Code
Matlab – Battery Capacity test
% This script imports the Excel file generated by an Arbin cycler during
% testing of lithium polymer cells and calculates its capacity
% Arbin Data:
% Col 1: Data_Point
% Col 2: Test_Time(s)
% Col 3: Date_Time
% Col 4: Step_Time(s)
% Col 5: Step_Index
% Col 6: Cycle_Index
% Col 7: Current(A)
% Col 8: Voltage(V)
% Col 9: Charge_Capacity(Ah)
% Col 10: Discharge_Capacity(Ah)
% Col 11: Charge_Energy(Wh)
% Col 12: Dischare_Energy(Wh)
% Col 13: dV/dt(V/s)
% Col 14: Internal_Resistance(Ohms)
% Col 15: Is_FC_Data
% Col 16: AC_Impedance(Ohm)
% Col 17: ACI_Phase_Angle(Deg)
%%
clc
clear all
close all
%% Loading the file from Excel
tic
filename = 'ME_445_Battery'; %Name of the file being imported
channels = [2 4]; % Channels being analyzed
sheets = 5 ; %Number of sheets per channel in the original Excel file
num_ofsteps = zeros(1,length(channels)); % Preallocates space to have a
vector with the number of steps per channel
29
%% Imports the data from the Excel file first into a cell containing all
steps per channel and then creates an array containing where each row is a
step and each column a channel
for i=1:length(channels)
Arbin_data(1,i) = arbin_excelimport_ver4(filename, channels(i), sheets);
num_ofsteps(1,i) = max(Arbin_data{i}(:,5));
StepData(:,i) = arbin_bystep(Arbin_data(1,i));
end
%% Generating Matrices for Time, Voltage and Current
for j = 1:length(channels)
time_SOC{j} = Arbin_data{j}(:,2);
current_SOC{j} = Arbin_data{j}(:,7); % time, current and voltage cells for
the entire duration of the experiment
voltage_SOC{j} = Arbin_data{j}(:,8);
end
%% plots
color = [0 0 1;
0 .5 0];
figure(1)
subplot (2,1,1) % Voltage Subplot
hold on
for plots = 1:length(channels)
plot(time_SOC{plots}./3600,voltage_SOC{plots},'Color',color(plots,:))
title ('Experimental Voltage Profile for a LiPo Battery Pack','FontSize',12)
xlabel('time(h)','Color','Red','FontSize',12)
ylabel('Voltage(V)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2')
end
30
% Preallocating space for current - voltage - charge arrays
current_ch = cell(1,length(channels));
time_ch = cell(1,length(channels));
current_ch = cell(1,length(channels));
SOC_ch = cell(1,length(channels));
SOC_dis = cell(1,length(channels));
q_dis = cell(1,length(channels));
q_ch = cell(1,length(channels));
% At the end of step 7, SOC is 1 and q_ch at that point is the max
for channel = 1:length(channels)
SOC_ch{channel} = q_ch{channel}/max(q_ch{channel});
end
figure(2)
subplot(2,1,1)
grid on
hold on
for channel = 1:length(channels)
plot(SOC_ch{channel},[StepData{5,channel}(:,8);StepData{6,channel}(:,8)],'Col
or',color(channel,:))
end
xlabel('SOC, Charging','Color','Red','FontSize',12)
ylabel('Voltage (V)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2','Location','EastOutside')
title ('Voltage During Charge at Different SOC Levels for a LiPo Pack',
'FontSize',12)
% Calculation 2: SOC, Discharging
31
% Discharging: Steps 8-10
% At the beginning of step 8, SOC = 1 and q is the same as at the end of
% step 7.
current_dis = cell(1,length(channels));
time_dis = cell(1,length(channels));
% At the end of step 7, SOC is 1 and q_ch at that point is the max
for channel = 1:length(channels)
SOC_dis{channel} = (q_dis{channel}-
min(q_dis{channel}))/(max(q_dis{channel})-min(q_dis{channel}));
end
subplot(2,1,2)
hold on
grid on
for channel = 1:length(channels)
plot(SOC_dis{channel},[StepData{8,channel}(:,8);StepData{9,channel}(:,8)],'Co
lor',color(channel,:))
end
xlabel('SOC, Discharging','Color','Red','FontSize',12)
ylabel('Voltage (V)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2','Location','EastOutside')
title ('Voltage During Discharge at Different SOC Levels for a LiPo Pack',
'FontSize',12)
32
%% Calculation 3: Charge/Discharge Capacity
charge_cap = zeros(1,length(channels));
discharge_cap = zeros(1,length(channels));
current_ch = cell(1,length(channels));
capacity = zeros(1,length(channels));
cap = [charge_cap.',discharge_cap.',capacity.']
%% Renaming Variables
discharge_VoC = dis_voltage{1,1};
charge_VoC = ch_voltage{1,1};
SOC_dis = SOC_dis{1,1};
SOC_ch = SOC_ch{1,1};
toc
save('BattTestRawData_File','time_SOC','current_SOC','voltage_SOC')
save('BattDataFile','SOC_ch','SOC_dis','ch_voltage','dis_voltage','cap')
save('VoC_data','SOC_ch','SOC_dis','discharge_VoC','charge_VoC')
33
Matlab – Look up Tables
% This script loads the charge and discharge measured voltages of a Plazma
% 5.3 Ah (nominal capacity) and generates a table of VoC as a function of
% SOC
clear all
close all
clc
% Due to the high sampling rate used during this experiment,there are
% repeated SoC values with non-unique voltage values. To avoid any
% conflicts, we are taking every ten SoC points and its corresponding
% voltage value.
rawSOC_ch = SOC_ch(1:10:end);
rawSOC_dis = SOC_dis(1:10:end);
rawVoC_ch = charge_VoC(1:10:end);
rawVoC_dis = discharge_VoC(1:10:end);
n = 20;
SOC_chVec = linspace(0,1,n);
SOC_disVec = linspace(1,0,n);
SOC_vec = SOC_chVec;
VoC = zeros(1,length(SOC_vec));
for i=1:length(SOC_vec)
VoC(i) = mean([VoC_ch(1,i) VoC_disbar(1,i)]);
end
%% Figures
figure(1)
grid on
hold on
plot(SOC_ch, charge_VoC,'ob')
plot(SOC_chVec, VoC_ch,'g')
plot(SOC_dis,discharge_VoC,'or')
plot(SOC_disVec, VoC_dis,'y')
34
plot(SOC_vec,VoC,'k','LineWidth',2)
xlabel('SOC')
ylabel('Voltage [Volts]')
save('parameter_data','SOC_vec','VoC')
35
Battery Resistance Test
% This script imports the Excel file generated by an Arbin cycler during
% testing of lithium polymer cells and calculates its capacity
% Arbin Data:
% Col 1: Data_Point
% Col 2: Test_Time(s)
% Col 3: Date_Time
% Col 4: Step_Time(s)
% Col 5: Step_Index
% Col 6: Cycle_Index
% Col 7: Current(A)
% Col 8: Voltage(V)
% Col 9: Charge_Capacity(Ah)
% Col 10: Discharge_Capacity(Ah)
% Col 11: Charge_Energy(Wh)
% Col 12: Dischare_Energy(Wh)
% Col 13: dV/dt(V/s)
% Col 14: Internal_Resistance(Ohms)
% Col 15: Is_FC_Data
% Col 16: AC_Impedance(Ohm)
% Col 17: ACI_Phase_Angle(Deg)
%%
clc
clear all
close all
%% Loading the file from Excel
tic
filename = '445Resistance'; %Name of the file being imported
channels = [2 4]; % Channels being analyzed
sheets = 3 ; %Number of sheets per channel in the original Excel file
num_ofsteps = zeros(1,length(channels)); % Preallocates space to have a
vector with the number of steps per channel
%% Imports the data from the Excel file first into a cell containing all
steps per channel and then creates an array containing where each row is a
step and each column a channel
for i=1:length(channels)
36
Arbin_data(1,i) = arbin_excelimport_ver4(filename, channels(i), sheets);
num_ofsteps(1,i) = max(Arbin_data{i}(:,5));
StepData(:,i) = arbin_bystep(Arbin_data(1,i));
end
%% Generating Matrices for Time, Voltage and Current
for j = 1:length(channels)
time_SOC{j} = Arbin_data{j}(:,2);
current_SOC{j} = Arbin_data{j}(:,7); % time, current and voltage cells for
the entire duration of the experiment
voltage_SOC{j} = Arbin_data{j}(:,8);
end
%% plots
color = [0 0 1;
0 .5 0];
figure(1)
subplot (2,1,1) % Plot of the voltage profile during the entirety of the
experiment
hold on
for plots = 1:length(channels)
plot(time_SOC{plots}./3600,voltage_SOC{plots},'Color',color(plots,:))
title ('Experimental Voltage Profile for a LiPo Battery Pack','FontSize',12)
xlabel('time(h)','Color','Red','FontSize',12)
ylabel('Voltage(V)','Color','Blue','FontSize',12)
legend('Pack#1','Pack#2')
end
37
voltageH = zeros(10,2);
voltageL = zeros(10,2); % Preallocates vector space
current = zeros(10,2);
Resistance = zeros(10,2);
Resistance = DeltaV./current;
%% Spline fit
SOC = [0:0.1:1];
n = 20;
SOCvector = linspace(0,1,n);
ResistanceFit = spline(SOC,Resistance(:,2),SOCvector);
figure(2)
hold on
grid on
plot(SOC,Resistance(:,2),'bo')
plot(SOCvector, ResistanceFit,'Color',[0 0.5 0])
38
toc
%Saving variables
save('Resistance_data','SOCvector','ResistanceFit')
39
Arduino Code for Motor Control – Power Management
/* Hybrid RC car Controller
ME 445 Final Project
Ji Liu
Sergio Mendoza
PIN Connection
2 pwm input signal from telemetry receiver (have to be pin 2, since we need to use the interrupt
3-5 Hall Effect Sensors
6-8 motor control high end
9-11 motor control low end
12 battery voltage read in
13 IC engine throttle servo in
A1 current sensor read in
connection:
sensors
green-- 3
orange--4
white-- 5
wires connecting to high end of the circuit
blue----6
orange--7
yellow--8
wires connecting to low end of the circuit
blue----9
orange--10
yellow--11
//#include <MemoryFree.h>
// PINS
40
const byte throt = 13;
const int inputpwm = 2;
const byte hall1 = 3;
const byte hall2 = 4;
const byte hall3 = 5;
const int battery_vol=12; // the pin used to read voltage for estimation
// voltage divider resistors
const int Rmeasure=1; // the resistor measured in the voltage divider
const int Rnotmeasure=1;// the one not measured
// charging parameters
int fixed_speed = 100; //the voltage for the servo to get IC engine's fixed speeds
const int idle_speed = 10;
const float soc_lb = 0.3;
const float soc_ub = 0.7;
const float Internal_R = 0.012; // we assume the internal resistance is not changing with time
float soc = 0;
float Voc = 0;
float I = 0;
const int current_sensor = A1;
float Vread=0;
unsigned long time;
const int delaytime = 100;
// interrupt paremeters
volatile unsigned long pwm_width;// variable to record the pwm signal
volatile unsigned long Time1;
volatile unsigned long Time2;
void setup()
{ //set pins to be inputs and outputs, initialize libraries, etc.
// pinmode for motor driver
pinMode(throt, OUTPUT);
pinMode(current_sensor, INPUT);
pinMode(hall1, INPUT);
pinMode(hall2, INPUT);
pinMode(hall3, INPUT);
41
pinMode(battery_vol, INPUT); // pinmode for the voltage lookup table
attachInterrupt(0, pwm_and_motor_drive, CHANGE);
Serial.begin(9600);
}
void loop () {
// first run the motor
// Serial.print("freeMemory()=");// used to check free memory since we have a lot of data
// Serial.println(freeMemory());
// sanety check: the pwm signal sent from remote controller ranges approximately from 1300 to 1900
microseconds
if (pwm_width>1900) pwm_width = 1900;
if (pwm_width<1300) pwm_width = 1300;
42
float
lookup_V[]={6.2539,7.1793,7.3976,7.4535,7.5054,7.5429,7.5738,7.5985,7.6238,7.6557,7.6959,7.7473,7.8097,7.87
52,7.9353,7.9858,8.1094,8.1960,8.2894,8.3913};
float lookup_soc[]={0, 0.0526, 0.1053, 0.1579, 0.2105, 0.2632, 0.3158, 0.3684, 0.4211, 0.4737, 0.5263, 0.5789,
0.6316, 0.6842, 0.7368, 0.7895, 0.8421, 0.8947, 0.9474, 1.0000 };
int size = 20; // size of the lookup table
// float re = lookup_table( Vread, V, soc, size);
soc = FmultiMap( Vread, lookup_V, lookup_soc, size);
// Serial.println("one more time");
// Serial.println(soc,5);
/***********************************************
then check the SOC and determine the IC engine speed
if the soc<30%, the ic engine should be speeded up to the fixed, which should
be the most efficient speed in the IC engine's BSFC map. Here, we just choose a fixed rpm to
pretend that this speed is the most efficient speed.
*/
if (soc >= soc_lb) analogWrite (throt,idle_speed);
/* we want to charge the battery when soc<soc_lb and stop charging when soc>soc_ub */
if (soc < soc_lb)
{
if (soc < soc_ub)
{
43
{
fixed_speed = fixed_speed +10;
analogWrite (throt,fixed_speed);
delay(delaytime);
}
}
}
}
void pwm_and_motor_drive()
{
if ( digitalRead( inputpwm ) == HIGH )
{
Time1 = micros(); //get time of pulse going down
}
else
// if ( digitalRead( inputpwm ) == HIGH )
{
Time2 = micros(); //get time of pulse going up
pwm_width = Time2 - Time1; //measure time between down and up
}
Serial.println(pwm_width);
}
44
bitWrite(x, 2, hallin3);
//Serial.print("hall position "); Serial.println(x);
return x; //return hall sensor reading.s
45
// Serial.print(coilin[hall]); Serial.println(" "); Serial.println(coilout[hall]);
analogWrite(coillow[hall], dc); // turn on the current in transistor with the proper duty cycle
delay(delaytime);
digitalWrite(coillow[hall], LOW); //turn current out transistor off
}
// function for lookup table.note: the in array should have increasing values
float FmultiMap(float val, float * _in, float * _out, byte size)
{
delay(delaytime);
// take care the value is within range
// val = constrain(val, _in[0], _in[size-1]);
46
Appendix C
Cutsheets
47
Losi-Ten Exploded View
48
B3536
A6956 A6956 A6277
B3570 B2403
B3563 B3570
B4210
B3568 B3553
B3542 B3542 A6299 A6229
B3571 B3568 B2504
B3569 B4210
Front B3553
B3572 B3542 B4023
Rear B3536
B3553 B4023
B3542 B4210
B3542 B4210
B3553 B3542 B3613 A6278
B3542 B3613
B3568
B3542 B3568 B3542
B3563 B3568
B3563 B4210 B3613
B3553
B3542
B3569 B2504 B3555 B3568 B3612
B3556 A6306 B2504
A8200 A6302
B4210 A6956 B3613 B3536 A6229
A6277 A6278
B2504 B3556
B3563 A6229
A6278 B2170
B3542 A6256 B4023
A6282 B2220 B3536
B2163 A6277 B4210
B3568 B3612 B2906
B5010
A6277 A6256 A8200
B3563 A6302 A6278
A6275
B3568 B2278 A2278 A6302 B2504 B2211
B3563 B2906 B3104 B2211 A6907 B4210 B4210 A2278 A6272
A6278 B2213
B2124 B3104 B2222
B6947 B2213 A6282 A6272
B4022 A6254
B2163 B3571 A6947
B2123 A6302 B4023
A6302 B3104
A6286 B2222
A6278 A6275 B3572 A6302
A6907
B4003 B5010 A6947
B4022
B2123
A6272 B4023
B4023 B4023
B3565 B2124 A6302 B3564 B4003
B3564
B2222 B3564 B2123 A6298 B4022
B3104 B4003 A6947 B4023
B3566 B3104 B3556
A6254 B3565 A3565
A6227 B4022 A6907 B3554
B2222 B3564 B4023 A6295 B3564 A6957
B2213 A6240 B2279 B2124 A6279
A6295 A6302 A6272 A6279
B2211 B4022 B2220 B3556
A6282 A6286
B2222 A6278 B3104 B2103
B4022 B5071
B2100 B4023 B2213 A6958
A6302 A6271
B2100 B2279 A6270 B2211
A6958 B3556
A6275 B4022 B4109
B4109 B4003 A6302
B2021 A6264 A6286 B2023
A6957 A6271 B3566 B3519
A6286 B4023 B2278 B4109
A6270 A6271
B2021 B4109 B3519
A6290 A6302 A6270
B3566
B2360 B2906
B2906
A6206 B5120
B2360
B5116
B2845 B5116
B2845 B2960 B5117
B2960
Soft
Soft
B2360 B2961 B5118
B2961 B2826 B5116
Medium
B0899 Medium
B5116
B2360 B2825 B2962 B5116
B2962
A6204 Firm B2906
B5121 Firm
B2360
B2906 B2904
B5116
B2904 B5116
A6350 B5118
SPM1452 B5117
A4006
B4210
B2905
B2360 B2905
SPMSR3300T
B2360 B7213
A2360 B2360
B0818 B2904
B2904 B2904
B2904
B2360
B0819
B7413
TEN-T Exploded View and Price List
Ten-T Replacement Parts LOSB3312 Flywheel and Collet: 10-T........................................................9.99
B5070 B3991 LOSB3313 Clutch Nut and Hardware: 10-T.............................................4.99
LOSB2021 Front Suspension Arm Set: 10-T............................................9.99
LOSB2023 Rear Suspension Arm Set: 10-T.............................................9.99 LOSB3314 Clutch Shoes and Spring: 10-T...............................................9.99
LOSB2100 Front Spindles and Carriers: 10-T....................................... 10.99 LOSB3348 17T Clutch Pinion Gear: 10-T..................................................4.99
LOSB2103 Rear Hubs and Spacers: 10-T..................................................7.99 LOSB3370 Clutch Bell (Only): 10-T.......................................................... 10.99
LOSB2123 Steering Bellcrank Set: 10-T....................................................5.99 LOSB3519 Wheel Hex Drive Set: 10-T . ....................................................4.99
LOSB2124 Steering Posts/Tube and Hardware: 10-T..........................9.99 LOSB3536 Center Differential Mount and Shock Tool Set: 10-T......4.99
A9151 LOSB3542 Differential Housing and Seal Set: 10-T..............................5.99
LOSB2163 Front Shock Towers and Caps: 10-T.....................................9.99
A6270 LOSB2170 Rear Shock Tower: 10-T......................................................... 11.99 LOSB3553 Center Differential Outdrive Set: 10-T.................................8.99
B5033 LOSB2211 Front and Rear Pin Mount Cover Set: 10-T........................3.99 LOSB3554 Center Rear CV Driveshaft Assmembly: 10-T................. 11.99
A9150 B3312 LOSB3555 Center Front CV Driveshaft Assmembly: 10-T . ............ 11.99
B2403 A6271 LOSB2213 Pivot Pin Mount Set (4): 10-T..................................................8.99
LOSB2220 Side Guard Set: 10-T..................................................................6.99 LOSB3556 Center CV Driveshaft Couplers: 10-T...................................4.99
B5033 LOSB2222 Front and Rear Sway Bar Set: 10-T..................................... 16.99 LOSB3563 F/R Differential Outdrive and Hard Set (2) .......................8.99
LOSB2278 Chassis Brace and Spacer Set (3): 10-T................................5.99 LOSB3564 F/R CV Driveshafts and Couplers (2): 10-T...................... 21.99
LOSB2279 Steering Drag Link and Hardware: 10-T.............................7.99 LOSB3565 F/R CV Couplers (4): 10-T.........................................................5.99
LOSB2286 Main Chassis Plate – Aluminum: 10-T.............................. 64.99 LOSB3566 F/R Axles (2): 10-T.......................................................................5.99
B5069
LOSB2360 Radio Tray and Hardware Set: 10-T................................... 12.99 LOSB3568 Differential Seals/Pin and Gasket: 10-T...............................4.99
B3370 LOSB2403 Fr Bumper and Tank Guard Set: 10-T...................................4.99 LOSB3569 Differential Gear Set with Hardware: 10-T.........................9.99
B3314 LOSB2504 Wing and Body Mount Set: 10-T...........................................4.99 LOSB3570 48T Center Spur Gear and Screws: 10-T.............................4.99
LOSB2825 Front Shock Body Set: 10-T.................................................. 13.99 LOSB3571 Front Ring and Pinion Gear Set: 10-T............................... 23.99
B3313 A6264
B3313 LOSB2826 Rear Shock Body Set: 10-T.................................................... 14.99 LOSB3572 Rear Ring and Pinion Gear Set: 10-T................................. 23.99
B5068 A6264 LOSB2845 Front and Rear Shock Shaft Set: 10-T..................................9.99 LOSB3612 Brake Disc Set (2): 10-T.............................................................7.99
A6264 A6270
LOSB2904 Front and Rear Shock Plastics & Ball Set (4): 10-T...........6.99 LOSB3613 Brake Pads, Cams, and Hardware: 10-T............................ 14.99
LOSB2905 Front and Rear Shock Boots (8): 10-T..................................5.99 LOSB3991 M5 Locknuts (6) RH Thread: 10-T..........................................3.49
A6954 A6250 LOSB2906 Shock Rebuild Set (2): 10-T.....................................................4.99 LOSB4003 F/R Camber/Steering Turnbuckle Set: 10-T.................... 12.99
B3348 LOSB2961 Front and Rear Spring Set (4)–Med (Black): 10-T.............6.99 LOSB4023 Rod End Set (15): 10-T...............................................................5.99
LOSB3104 Front and Rear Gearbox Set: 10-T.........................................8.99 LOSB4022 Camber and Steering Ball Set (12): 10-T.......................... 11.99
Spektrum Radio Communication System
51
DX3S Quick Start Guide 4. Bind Transmitter to the Receiver
A. Insert the bind plug in the receiver’s BIND port.
6. Check Servo Direction
Turn on the transmitter followed by the receiver and check that the
B. Power the receiver through any port that is not a 3.3V telemetry port. direction of each channel is correct by rotating the steering wheel,
1. Install Receiver Warning: Do not power the SR3300T through the LAP, TEMP, or slightly pulling the throttle trigger and switching the aux channel, if
RPM port. The receiver will be damaged! applicable. (Nitro engines should not be running.) Use the REVERSE
screen of the DX3S to change the direction of any channel.
Nitro - Rx uses a receiver pack for power. Electric - Rx uses the ESC for power.
Note: If servo reverse direction is changed, rebind to re-establish the
fail-safe positions.
D. With the steering wheel, throttle trigger and aux channel (if
applicable) in the desired preset failsafe positions (normally full brake
and straight steering), press the rolling selector to initiate the bind
3. Using the Rolling Selector process and to store the failsafe positions.
The Rolling Selector is pressed to access functions and rolled to select E. BIND will flash for a few seconds then stop, indicating the process is
specific features or to change settings or values. Pressing and holding complete. The LED on the receiver should now be solid, indicating a
the Rolling Selector for more than 3 seconds returns the display to the successful bind has taken place.
main screen. F. Once the bind process is complete and before power is cycled on the
receiver, remove the bind plug and store it in a convenient place.
Failure to do so will result in the receiver going back into bind mode.
5. Plug in Servos to the Receiver
Plug in servos to the appropriate port on the receiver. Be sure the
polarity of the connections is correct. For information on installing
telemetry sensors (SR3300T only), please see the “Installing the
Telemetry Sensors in Your Vehicle” section of the DX3S User Guide.
AUX
Throttle Leaders in Spread Spectrum Technology
Steering
Bind The Spektrum trademark is used with permission of Bachmann Industries, Inc.
13955
Novak Brushless DC Motor
53
Plazma LiPo Battery Pack
58
12/17/13 #101942 Plazma 7.4v 5300mah 30c Lipo Battery Pack
We use cookies to help make this website better. You can change your cookie settings at any time. Otherwise, we'll assume you're OK to continue. For more
information about this site's cookies, read our Privacy Policy
The range of HPI Plazma batteries is designed to be the ideal match for all
HPI kits that require a long-lasting, reliable battery pack featuring reliable
lithium-polymer (LiPo) cell technology. These LiPo battery packs are ideal #101943 PLAZMA 11.1V
for kits that demand loads of power, like the E-Firestorm Flux, Sprint 2 3800mAh 30C LIPO BATTERY
PACK
Flux as well as kits like the Cyclone S, Cyber, and others!
RTR APACHE SC OPT Check out the full range of HPI Plazma batteries!
RTR Sprint 2 Flux w/ Porsche 911... OPT
Plazma Ni-MH Range
RTR Sprint 2 Flux w/ BMW M3 OPT #101940 PLAZMA 7.4V 3000mAh
#106388 PLAZMA 7.2V 4700mAh Ni-MH Battery Pack 20C LIPO BATTERY PACK
RTR Bullet ST Flux w/ 2.4GHz OPT #106390 PLAZMA 8.4V 3300mAh Ni-MH Battery Pack
RTR Bullet MT Flux w/ 2.4GHz OPT #101935 HPI Plazma 4.8V 4300mAh NiMH Stick Pack
RTR Savage XS OPT #101931 HPI Plazma 7.2V 2400mAh NiMH Stick Pack
#101932 HPI Plazma 7.2V 3300mAh NiMH Stick Pack
RTR E-Firestorm 10T Flux OPT
#101933 HPI Plazma 7.2V 4300mAh NiMH Stick Pack
Savage Flux HP RTR 2.4GHz OPT
Don't forget to check out our receiver packs which fit all nitro-powered
vehicles, plus our special Baja pack!
#101936 HPI Plazma 6.0V 1600mAh Nimh Receiver Pack
#101937 HPI Plazma 6.0V 4300mAh Nimh Baja Receiver Pack
www.hpiracing.com/en/part/101942 1/2
12/17/13 #101942 Plazma 7.4v 5300mah 30c Lipo Battery Pack
What is RC? About HPI Suppor t F.A.Q. Jobs at HPI Ter ms and Conditions Pr ivacy Policy
Copyr ight 2013 HPI Racing. All r ights r eser ved. Images may not be used without expr ess per mission.
www.hpiracing.com/en/part/101942 2/2
LT 1513 Battery Charger
61
LT1513/LT1513-2
SEPIC Constant- or
Programmable-Current/
Constant-Voltage Battery Charger
U
FEATURES DESCRIPTION
■ Charger Input Voltage May Be Higher, Equal to or The LT ®1513 is a 500kHz current mode switching regula-
Lower Than Battery Voltage tor specially configured to create a constant- or program-
■ Charges Any Number of Cells Up to 20V mable-current/constant-voltage battery charger. In addition
■ 1% Voltage Accuracy for Rechargeable Lithium to the usual voltage feedback node, it has a current sense
Batteries feedback circuit for accurately controlling output current
■ 100mV Current Sense Voltage for High Efficiency of a flyback or SEPIC (Single-Ended Primary Inductance
(LT1513) Converter) topology charger. These topologies allow the
■ 0mV Current Sense Voltage for Easy Current current sense circuit to be ground referred and completely
Programming (LT1513-2) separated from the battery itself, simplifying battery switch-
■ Battery Can Be Directly Grounded ing and system grounding problems. In addition, these
■ 500kHz Switching Frequency Minimizes topologies allow charging even when the input voltage is
Inductor Size lower than the battery voltage. The LT1513 can also drive
■ Charging Current Easily Programmable or Shut Down a CCFL Royer converter with high efficiency in floating or
U grounded mode.
APPLICATIONS Maximum switch current on the LT1513 is 3A. This allows
■ Charging of NiCd, NiMH, Lead-Acid or Lithium battery charging currents up to 2A for a single lithium-ion
Rechargeable Cells cell. Accuracy of 1% in constant-voltage mode is perfect
■ Precision Current Limited Power Supply for lithium battery applications. Charging current can be
■ Constant-Voltage/Constant-Current Supply easily programmed for all battery types.
■ Transducer Excitation , LTC and LT are registered trademarks of Linear Technology Corporation.
■ Universal Input CCFL Driver
U
TYPICAL APPLICATION Maximum Charging Current
WALL L1A*
• 2.4
ADAPTER C2**
INPUT + C3 7 4.7µF D1 † 2.2
22µF SINGLE Li-Ion CELL
VIN 5
25V VSW 1.25A 2.0 (4.1V)
CELL (8.2V)
SYNC 6 2 1.6
S/S VFB •
AND/OR
1.4
SHUTDOWN SHUTDOWN GND VC IFB
R4 R2
+ C1
12V
22µF
4 TAB 1 3 39Ω 25V 1.2
×2 16V
R5 1.0
270Ω 20V
C4 R3 0.8
C5 0.22µF 0.08Ω
0.1µF 0.6 BATTERY
VOLTAGE
* L1A, L1B ARE TWO 10µH WINDINGS ON A
LT1513 • TA01 0.4
0 5 10 15 20 25 30
COMMON CORE: COILTRONICS CTX10-4
** CERAMIC MARCON THCR40EIE475Z OR TOKIN 1E475ZY5U-C304 INPUT VOLTAGE (V)
† MBRD340 OR MBRS340T3. MBRD340 HAS 5µA TYPICAL INDUCTOR = 10µH
ACTUAL PROGRAMMED CHARGING CURRENT WILL BE
LEAKAGE, MBRS340T3 50µA TYPICAL INDEPENDENT OF INPUT VOLTAGE IF IT DOES NOT
EXCEED VALUES SHOWN LT1513 • TA02
Figure 1. SEPIC Charger with 1.25A Output Current
sn1513 1513fas
1
Preventive Braking and Safety Control RC Car
ME 445 Final Project
12/16/2013
Table of Contents
1.0 Acknowledgments 3
2.0 Introduction 3
3.0 Overall Design 3
3.1 Mechanical Setup 3
3.2 Electrical Setup and Components 5
3.3 Software Programming 8
4.0 Testing and Results 12
5.0 Discussion 15
6.0 Conclusion 15
Appendix A Code 17
Appendix B Data Sheets 23
2
1.0 Acknowledgments
We would like to thank our professor Dr. Sommer for teaching us the various
aspects of mechatronics during ME 445, as well as our TA Michael Robinson for his help
throughout the semester with our labs and final project.
2.0 Introduction
The primary goal of this project was to create an RC car implementing
preventative breaking using an ultrasonic sensor, proportional throttle control, and three
driving modes. Our motivation was to apply the recent surge of research and
development within the automotive field in preventative braking and safety systems on a
smaller scale using our knowledge of mechatronics and Arduino that we developed
during the semester.
Volvo has been developing technologies to increase car safety called “Volvo Auto
Brake Technology,” and our goal was to use an ultrasonic sensor to replicate emergency
stopping by using proportional throttle control, as well as mimic accident prevention
technology when approaching an object. By using a potentiometer, another form of
proportional control for three different driving modes was implemented. These three
driving modes were economy, normal, and sport, which were inspired by the Honda CR-
Z, which had recently developed such driving modes. Economy mode would be
analogous to energy saving mode in a vehicle, and resulted in the lowest throttle response
when simulating the vehicle. Normal mode represented moderate throttle control, which
would be analogous to an everyday setting in a vehicle. The last mode employed was
sport, which would be similar to a performance setting in a vehicle, which allowed
quicker acceleration and throttle response.
Our results showed that while there was some limitation due to sensor delay and
low sampling frequency of the ultrasonic sensor, throttle proportional control at each
driving mode was implemented and functioned correctly during testing. By recording the
revolutions per minute of the RC car wheels with each driving mode by using a
tachometer at various distances from the sensor, we confirmed that the coupled driving
modes and proportional control worked seamlessly. While testing indoors was difficult
due to the large sensor range due surrounding object interference, the implementation of a
brief burst or reverse throttle aided in stopping the vehicle consistently before both static
and moving objects.
3
proportional control. The RC servo motor was connected to each shaft of the front wheels
to rotate and turn the wheels, which was also processed through the Arduino code and
microcontroller. As for the tires, they are low profile rubber tires. For easy access to
change the driving modes, the potentiometer was mounted on the main base plate portion.
Lastly, a prototyping board was used to complete the various circuit connections.
Servo
4
3.2 Electrical Setup and Components
Figure 2 above shows the schematic used in this project. The 7.6V NiMH batter
powers up the DC motor via the Pololu motor driver, as well as the Arduino to power it
remotely. Common 5V and GND pins were used to power the RC receiver, ultrasonic
sensor, motor driver, and steering servo of the vehicle.
5
Source:
http://www.pololu.com
Figure 3 - Pololu VNH 5019 Motor Driver
Typical electrical speed control is used along for this project with an RC receiver
to control the steering and throttle of the vehicle. However, the team wanted to invest
more time implementing a code that would work based on PWM values with proportional
control used in basic motor controls using Arduino. This component could be described
as being a more advanced version of the L293d, or similar to the other Pololu drivers
used in lab that could provide up to 30A of current along with up to a 24 V supply.
Source:
www.hobbyman.com.au
Figure 4 - HiTEC HFD-08RO FM Receiver
6
The team originally planned to use a Bluetooth interface to control the vehicle and
the software wirelessly. Due to the complexities of applying this kind of communication
between the Arduino and an Android cell phone, the traditional RC receiver was used..
Two channels for throttle and steering coupled with two outputs signals were connected
to the interrupt pins on the on the Arduino for processing.
Source:
http://arduino.cc/
Figure 5 - Arduino Uno
The Arduino UNO was implemented since it provided enough processing power
and input pins needed to complete this project. If another interrupt pin had been required,
or more PWM digital pins were needed, another microprocessor with better interrupt
capabilities and available pin would need to be used.
7
Source: http://www.adafruit.com
Figure 6 - HRLV-MaxSonar Ultrasonic Sensor
An ultrasound sensor was used to as it provides a larger sensing area and it also
can take measurements in different light levels. This model uses two different modes of
operation, which either sends pulses with varying lengths or analog voltage proportional
to the distance. The analog voltage mode was used as the team faced coding issues using
several pulse in codes that greatly limited sampling frequency. During testing, a major
disadvantage to this sensor was discovered that this component has a low sampling
frequency, which caused delays in distance measurements. Also, it had a minimum
reading of 28cm when a 5V power source was used which was another important factor
to consider while implementing proportional control
8
Figure 7 – Flow Diagram
The team aimed to create a reliable code that could be calibrated easily to fully
implement a safe driving controller for the RC car. The main portion of the Arduino
code involved the processing of the RC signal received by the interrupt pins on the
microcontroller chip. The RC remote controller sent pulses to the FM receiver, and the
width of these pulses determined the level of throttle in one channel, while the other
channel controlled steering with the use of an electric servo. The RC controller works by
sending a series of radio pulses about 50 times a second, with each pulse lasting between
one and two milliseconds. Typically, there is a gap of ten times the length of the longest
pulse in between each peak. The Arduino code receives the pulse train, processes it, and
generates a digital output to control the servo and DC motor on the vehicle.
The first version of the code created to control the vehicle steering and throttle
was done using the Arduino “pulseIn” function, which essentially measures the time of a
designated pin in its HIGH state. It is a very useful function used to measure the pulse
9
widths of many different types of pulsating signals. However, when more functions and
loops were implemented in the main code, the Arduino UNO failed to execute all the
loops at the same time. With the ability of the Arduino to perform 32,000 cycles in a two
millisecond time period, and the fact that the continuous “pulseIn” function is running
while the pulse is LOW, 320,000 cycles were used to only send a single useful full
throttle pulse. Nearly half of the processing power of the UNO was wasted by just
waiting for the next pulse to arrive and be completed, and was an unacceptable approach.
Considering two pulse functions, motor drivers and other loops executed to control the
vehicle dynamics, the entire code wasn’t able to perform at this stage without a solution.
In order to save a majority of the raw processing power and make the software
run smoothly, the interrupt capabilities for the Arduino was used. Our approach was to
use an interrupt function that calculated the pulse width only when the pin is set in HIGH
state. A separate timing function was created to measure the length of time whenever the
interrupt pin changes from LOW to HIGH and HIGH to LOW. These state changes were
used in the timing function that calculates the width of the incoming signal in HIGH
state. Figure 8 below illustrates the three different states of throttle where the pulse is in
HIGH state for a very short period.
Source:
http://rcarduino.blogspot.co.uk/2012/01/how-‐to-‐read-‐rc-‐receiver-‐with.html
Figure 8 - The shape of the pulse received by the microcontroller
In order to fully digitize the driving mode of the vehicle, a high performance
Pololu VNH 5019 motor driver was used to replace the traditionally used pulse width
based electronic speed control. This component requires a voltage source between 2.5V
to 5V, and used the same range for logic levels. The motor driver code used for this
project was very similar to the motor functions we created for the L293d motor drivers
created for previous lab work. The enable pin on the driver receives the PWM value
corresponding to a pulse width. The “Map” function is used to convert the pulse width
into the corresponding PWM value (i.e. Map(throttle_pulse_width, 1000, 2000, -255,
255)). Since the radio signal received by the Arduino generates some noise, this caused
the natural pwm throttle level to vary between -4 and 4 at natural level. However, this
10
noise is not noticeable when the DC motor is running at PWM values greater than 10.
The motor driver code was modified to make the motor run at values greater than 10 and
lower than -10 to reduce the electrical noise that this natural level.
The three different types driving modes were implemented to vary the maximum
PWM value delivered to DC motor, and this helps the user determine the maximum
speed and performance for the vehicle by simply rotating the knob of the potentiometer.
The potentiometer reads a value between 0-1024 which is mapped to the three
multiplying factors of 1, 2 and 3. These numbers are multiplied by a reduced PWM value
to provide maximum PWM values of 76, 153 and 230. With the addition of proportional
control within the code controlled by the distance to a target object from the front of the
vehicle, the PWM values was scaled further.
The steering of the remote controlled vehicle is controlled using “Servo.h” from
the Arduino library. Similar to the interpolation used for converting the pulse width into
corresponding PWM values to run the motor, the “Map” function converts the pulse
width into the corresponding angle turn of the wheels ((steer_pulse_width, 1000, 2000, 0,
179)). At the natural level (90 degrees) noise similar to the generated while the throttle
channel was observed. A similar noise reduction technique was used by employing “if”
statements that only rotates the servo above 95 and below 85 degrees. Noise levels were
also significantly reduced after the interrupt function were employed instead of “pulseIn.”
This happens because the microcontroller interrupt pin is only activated when the pulse is
received, which greatly reduces the number of cycles run on the designated pin, and
therefore provides more refined output. Additionally, an unexpected glitch was caused
when calling the servo library which disables pins 9 and 10 that were originally used to
attached the leads of the to a motor driver.
The ultrasound range detector used in the assembly has two different modes of
operation. It uses the shared 5V from the microcontroller as a source of power, and has
two main outputs of pulse width and proportional analogue voltage. Since the main part
of the code implemented two interrupt functions instead of the less desired pulse in
function, the analogue voltage output was used to measure the distance. The 5V supply
yields approximately 9.8mV/cm, and since the Arduino analog pin goes from 0 to 1024,
the output value has to be divided by 2 to get the actual distance in centimeters. To
increase the accuracy of the reading from the analog pin, the calculation was placed in a
loop that runs each distance calculation 20 times and then takes the average of the total
number and prints the average reading. Although the Arduino executes the loop
immediately this ultrasound detects change in distances in noticeable lag, which will be
discussed further within the testing portion of the report.
Below, Figure 9 shows the range of ultrasound range detection for the sensor. The
maximum range in width and length was suitable for our use. Several “if” statements
were used to implement a gradual reduction of vehicle speed until it stops. This portion of
the code was designed so that the vehicle would adjust its speed based on either a static
or a moving obstacle in front of it. Tests were also performed to recalibrate the settings so
that the vehicle would maintain a safe distance and stop to prevent an unwanted collision.
Automatic reversed throttle for breaking was included in the same portion of code as the
speed reduction portion. It works by applying a pulse of high throttle in reversed
direction in a narrow range to help the vehicle stop immediately as it approaches an
obstacle.
11
Source: http://www.maxbotix.com/Ultrasonic_Sensors
Figure 9 – Range of Ultrasonic Sensor
12
Figure 10 - Tachometer Test
3000
2500
2000
1500
1000
500
0
40
50
60
70
80
90
100
110
120
130
13
performed by using both static and moving obstacles to test a more realistic scenario for
preventative braking. This was performed by driving the RC towards a shoe box to
simulate an obstacle (seen in Figure 12 below), and allowed a soft surface to impact if the
car slide during the troubleshooting process. We found after a few tests that while each
distance range and associated proportional scaling was correct, as well as the brief period
of revered throttle for breaking (35cm-30cm), the low sampling frequency of the
ultrasonic sensor was the only cause of error. The team conducted a few hours worth of
testing to choose the distance range for proportional control, and the main point of error
was the sensor sampling too slowly. Even with correct code to have the RC slow down
and brake before striking an object, the unreliability of the sensor resulted in some slow
collisions with the shoe box. Another issue was the slick tires and dusty floors of the
Reber building that caused a large amount of wheel slip. This is logical due to the high
power to weight ratio of RC cars, but testing on rougher surfaces like asphalt would have
been able to negate this factor during testing. A more refined method for braking could be
implemented by using derivative control, but after research into this idea, the team
determined that undertaking would be well out of the scope of this course. In subsequent
attempts of this project, this would be included, as well as an improved distance sensor
(ultrasonic or infrared).
14
5.0 Discussion
In the preliminary testing of our vehicle, we encountered a few problems with our
servo and subsequent servo code that resulted in changes that improved performance. We
noticed the servo was grinding during preliminary tests, and after opening it up,
determined that the spring used to protect the gears during rotation had fallen out, which
we then replaced to improve function and ensure future reliability.
Another issue that was resolved was from the servo code library that resulted in
the steering and throttle not running at the same time. Through debugging the motor
driver and subsequent code, it was determined that the servo code within the library
disabled digital pins 9 and 10, which were the pins we had originally used for the motor.
Once the pins were rearranged, we were able to drive and steer the RC using the
controller.
While running the code for servo control, we found that due to various factors of
noise in the system and surrounding, that the wheels would turn slightly back and forth
from the central resting position for forward movement while using the pulseIn function.
This issue was resolved by implementing the two available interrupt pins, as well as
limiting a range of central PWM values at the neutral position for the motor. As discussed
in the software programming section, these interrupt pins, along with the interrupt
functions, allowed the software to run more smoothly as it required less cycles to
measure the incoming widths of pulses, and aided in minimizing noise within out system.
Originally, we had considered using Bluetooth technology and applications from
and Android phone to primarily control the RC car, but due to program limitations,
control was ultimately transitioned to RC control and receivers. Currently, no application
that was applicable to Bluetooth communication could steer and provide throttle to the
vehicle at the same time. Due to this issue, and the need for seamless steering and driving
of an RC test vehicle, this preliminary idea was neglected.
It was unfortunate that the main issue the team had with this project was the
sensor not being able to sample at a high enough frequency with a more focused
detection field. Aside from the delay issues of the distance reading not being recorded
quickly enough to engage the various ranges of proportional control, another troublesome
aspect during testing indoors was the sensor detecting surrounding objects within the
laboratory. With this knowledge know, if our team were to revamp this project, a much
more powerful ultrasonic or infrared sensor would be used with a high sampling
frequency. However, we also considered other improvements that could be made to this
project in the future.
While the pulse of reverse throttle was helpful in stopping the vehicle, it was a
rather primitive approach, but was within the scope of our current understanding of
mechatronics. Derivative control for braking had been discussed, but upon some external
research, we had determined that this solution was far too complex with our current
knowledge. However, this would be a challenging and beneficial addition to further this
project in future adaptations.
6.0 Conclusion
Overall, the team was very pleased with the final result, and proud that the RC car
was able to implement our main objects of proportional throttle control, preventative
braking, and driving modes. Unfortunately, the ultrasonic sensor was a major limiting
15
factor in the overall performance during testing, and a sensor with a more focused visual
field and higher sampling frequency would alleviate the issues of delayed reading and
exogenous sensor interference. However, the vehicle slowed down each trial and
normally braked as planned during the approach of obstacles with the current sensor.
With the addition of a better sensor, the team is confident that the RC car with associated
software could perform as planned each trial.
Upon completion of this project, the team has gained a deeper appreciation for the
field of mechatronics, and felt that we gained a valuable skill set in microcomputer
interfacing throughout the semester. With the various labs where were learned important
debugging techniques and Arduino processes, as well as lectures to gain understanding
about the components we used for the project, we were able to gain confidence in
interfacing with electronics as mechanical engineers.
16
Appendix A: Code
//ME 445 FINAL PROJECT CODE
//KEVIN MOYER - ANIL SENTURK
//INTRUCTOR : Dr. Sommer
//TEACHING ASSISTANT: Michael Robinson
volatile int nThrottleIn = NEUTRAL_THROTTLE; //Set in the Interrupt and read within loop
once it is declared volatile
volatile unsigned long ulStartPeriod = 0; //Set interrupt
volatile boolean bNewThrottleSignal = false; //Set interrupt within loop
volatile int nSteerIn = NEUTRAL_STEER; //Set in the Interrupt and read within loop once
it is declared volatile
volatile unsigned long ulStartPeriod1 = 0; //Set interrupt
volatile boolean bNewSteerSignal = false; //Set interrupt within loop
17
//This loop sets the function calcInput to be called whenever digital pin 2 changes from
HIGH/LOW or LOW/HIGH
void setup()
{
pinMode(5,OUTPUT); //Motor B
pinMode(6,OUTPUT); //Motor A
pinMode(11,OUTPUT); //Enable Pin (11)
attachInterrupt(THROTTLE_SIGNAL_IN,calcInput,CHANGE); //Track changes in throttle
signal
attachInterrupt(STEER_SIGNAL_IN,calcInput1,CHANGE); //Track changes in steering
signal
myservo.attach(9);
Serial.begin(9600); //Serial communication at 9600 bits per second
}
void loop()
{
{
int sensorValue = analogRead(A0); //Read input on analog pin 0
limit=map(sensorValue, 0, 1023, 1, 3); //Set for 3 driving modes
}
//Servo
val = map(nSteerIn, 1000, 2000, 0, 179);
stir(val);
/*Throttle proportional control was implimented within this loop, in which the pwm value was
scaled by set length ranges as well as driving mode. To account for
the inertia of the car approaching an obstacle, a brief 5 cm range in which full throttle reverse was
implemented while approaching the wall to aid in breaking. The resolution of the sensor limied
repeatability regardless of range or sample rate*/
//Motor
pwm = map(nThrottleIn, 1000, 2000, -255, 255); //PWM value throttle range for motor
pwm=pwm*0.3*limit; //PWM scaling according to driving mode and sensor
distance
//The following ranges scaled the pwm value that determined throttle power
18
if (cm > 120)
{pwm=pwm;
}
else if (cm<120 && cm>105)
{pwm=pwm*0.8;
}
else if (cm<105 && cm>90)
{pwm=0.6*pwm;
}
else if(cm<90 && cm>75)
{pwm=0.5*pwm;
}
else if(cm<75 && cm>50)
{pwm=0.3*pwm;
}
else if(cm<50 && cm>35)
{pwm=0.2*pwm;
}
else if(cm<35 && cm>30) //Brief reverse throttle portion for braking
{pwm=-65;
}
else if(cm<30)
{pwm=0;
}
function1(pwm);
//Serial.println(cm);
if(bNewThrottleSignal)
{
bNewThrottleSignal = false; //While nThrottleIn is true calcInput will not update
}
if(bNewSteerSignal)
{
bNewSteerSignal = false; //While bNewSteerSignal is true calcInput will not
update
}
}
19
//Throttle interrupt portion
//This function calculates the width of a HIGH state in microseconds which essentially returns a
pulse width
//between the values of 1000 and 2000
void calcInput()
{
if(digitalRead(THROTTLE_SIGNAL_IN_PIN) == HIGH) //Interrupt starts when pin is HIGH
{
ulStartPeriod = micros(); //Calculate time using micros
}
else
{
if(ulStartPeriod && (bNewThrottleSignal == false)) //ulStartPeriod time difference
calculation
{
nThrottleIn = (int)(micros() - ulStartPeriod);
ulStartPeriod = 0;
bNewThrottleSignal = true; //Communicates new throttle signal when set back
to false
}
}
}
20
analogWrite(11, b);
digitalWrite(5, LOW);
digitalWrite(6, HIGH);
}
else if(b < -10)
{ b = abs(b);
analogWrite(11, b);
digitalWrite(6, LOW);
digitalWrite(5, HIGH);
}
else //When zero the motor is set to stop
{ digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
}
}
}
void function2(int c)
{//This portion turns the motor CCW
for(int k=1; k<500;k++)
{
if (c > 10)
{
analogWrite(11, c);
digitalWrite(5, LOW);
digitalWrite(6, HIGH);
}
else if(c < -10)
{ b = abs(b);
analogWrite(11, c);
digitalWrite(6, LOW);
digitalWrite(5, HIGH);
}
else //When zero the motor is set to stop
{ digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
}
}
}
//A separate void was created to reduce noise created the in servo
void stir(int b)
{
for(int j=1; j<500;j++)
{
if (b > 95)
{
myservo.write(val);
}
else if(b < 85)
{
myservo.write(val);
21
}
else
{
myservo.write(90);//stop
}
}
}
/* Unused portion used for development of yaw control for a safer and more controlled driving
experience to be incorporated during second attempt at this project:
const int xpin = 0;
//const int ypin = 1;
//const int zpin = 2;// analog: Z axis output from accelerometer
void setup() {
Serial.begin (9600);
Serial.print ("x axis g's");
Serial.print ("\t");
Serial.println();
}
void loop() {
float voltage_x = (analogRead(xpin)) * arduino_power_supply / 1024;
float x = (voltage_x - zero_g_bias) * 1000 / 330;
Serial.print(x);
Serial.print ("\t");
Serial.println();
}
*/
22
Appendix B: Data Sheets
23
24
25
26
PENNSTATE
College of Mechanical Engineering
Matt Tarabulski
Todd Troutman
Executive Summary
Team “Snakes in 2 Planes” was asked by Air Products Inc. to design and build a device capable
of measuring and storing/transmitting the outer diameter (OD) of pipes in a natural gas reforming
plant. The data the device collects will be used to study creep effects over the operating life of
the pipes to prevent catastrophic failure.
Our team gathered customer needs information on a site visit on Sept. 10th, 2013. The device will
be used on “pigtails”. Pigtails are pipes with many bends designed to accommodate expansion
due to high-temperature operation, with bend radii as small as 6.5 inches. Ideally the device will
not protrude more than 3 inches off of the surface of the pipe so that it will not be obstructed by
pigtail supports. The device must be able to climb vertically and measure OD within +/- 0.001
inches. Measurements must be taken in two planes. Data should be transmitted to a PC or stored
in the device in a format that is widely useable. The device should be battery powered.
The team surveyed patents related to pipe OD measurement and developed design concepts. Our
official brainstorming meeting occurred on September 18th. We later scored the concepts using
the Analytical Hierarchy Process to determine the optimal concept. The concept selected was a
device which uses rolling shafts which clamp onto the pipe using a spring suspension. The travel
of this suspension is measured by modified digital calipers, which allows for computation of the
OD. Solidworks was used to design the prototype from initial concept to the final machining and
construction drawings.
We took steps to mitigate risk which might have lead to unfavorable outcomes for both our team
and our sponsor. Technical risk was also reduced by creating an alpha prototype that exposed
some unforeseen problems before construction on the final prototype began. Risk of schedule
slip was mitigated by weekly meetings and weekly progress reports. Before making purchases
for the prototype, we consulted with Dr. Catanach to prevent budgetary losses.
Test Results of the prototype revealed a some target specifications were met. The device was
capable of very effective wireless communication, exporting to excel, a high level of accuracy of
measurement in 2 planes, as well as being under the 5 lb. goal weight, and within the 3" radius
specified by the customer. Due to unforeseen frictional effects and an overemphasis on power
conservation, the device was unable to climb vertically using the motors shown in this report.
Suggestions for improvement in further development cycles are included in Section 10.2
Overall, the project was a success and a valuable learning experience. A working prototype was
shown at the Design Showcase on December 12th, and afterwards it was delivered on-time and
on-budget to the Air Products sponsors, Eric Blanco and Ross Beidleman.
2
Table of Contents
Table of Contents ............................................................................................................................ 3
1.0 Introduction ............................................................................................................................... 5
1.1 Initial Problem Statement ............................................................................................................ 5
1.2 Objectives .................................................................................................................................... 5
2.0 Concepts .................................................................................................................................... 6
2.1 Concept Generation ..................................................................................................................... 6
2.2 Concept Selection ........................................................................................................................ 7
3.0 Final Design .............................................................................................................................. 9
3.1 Manufacturing Process ................................................................................................................ 9
3.2 Components and Component Selection ..................................................................................... 10
3.2.1 Electrical........................................................................................................................ 10
3.2.2 Drive Components......................................................................................................... 11
3.3 Schematics ................................................................................................................................. 12
3.3.1 Electrical Components .................................................................................................. 12
3.5 Testing ....................................................................................................................................... 13
3.5.1 Test Procedures ............................................................................................................. 13
4.0 Final Discussion ...................................................................................................................... 14
4.1 Construction Process ................................................................................................................. 14
4.1.1 Electrical........................................................................................................................ 14
Motor wiring .......................................................................................................................... 17
Wheel Encoder ....................................................................................................................... 17
4.2 Test Results and Discussion ...................................................................................................... 18
4.2.1 Accuracy........................................................................................................................ 18
4.2.2 Run Time Test ............................................................................................................... 18
4.2.3 Wireless Range Test ...................................................................................................... 19
5.0 Conclusions and Recommendations ....................................................................................... 19
5.1 Project Summary ....................................................................................................................... 19
References ..................................................................................................................................... 21
Appendix A: Gantt Chart .............................................................................................................. 22
Appendix B: Bill of Materials....................................................................................................... 23
Appendix C: Software Block Diagram ......................................................................................... 25
Appendix D: Code ........................................................................................................................ 26
Communication Protocols ............................................................................................................... 26
3
Arduino Code .................................................................................................................................. 27
Visual Basic Code ........................................................................................................................... 36
4
1.0 Introduction
1.1 Initial Problem Statement
Pipe systems that operate at a high temperature are susceptible to creep damage. One of the
methods of evaluating the onset of creep is by taking outside diameter measurements. This is
typically done by the use of "go/no-go" gages. These gages wear with use which renders them
inaccurate and do not allow real time OD data that can be tracked over time. A multidisciplinary
team of mechanical and electrical engineering students will design and prototype an automated
pipe crawler that can take OD measurements and crawl around bends.
1.2 Objectives
Physical Ideally less than 3" high, radially from pipe surface
Constraints Must negotiate pipe bends with radius as small as 6.5"
Must be able to climb pipe vertically
Measures orthogonally to axis of pipe even in bends, avoiding "out-of-
plane"
5
2.0 Concepts
2.1 Concept Generation
Concept A consists of two separable halves held tight to
the pipe with a spring force. Each half has a wheel that is
driven along the pipe.
As the outer diameter changes, the spacing between the
two halves increases as well. This distance will be
measured and recorded using a linear displacement sensor
such as a capacitive distance sensor, commonly found in
digital callipers, or a slide potentiometer.
A.
Concept B has two rings with grips on each ring. The
device will move when one ring has grasped onto the
pipe, the other ring will slide towards or away from the
ring depending on its location. Then the other ring will
grip the pipe while the first lets go, allowing it to move
forward. While it has gripped the pipe, it will measure
how far it had to move the grips to touch the pipe in order
to get a diameter measurement.
B.
Concept C consists of a hinged pair of rollers, which are
sprung together to provide a clamping force on the pipe.
When the spring, (seen on bottom right) is compressed,
the device will be able to slide onto the pipe. It uses steel
measurement wheels and urethane wheels for drive. A
potentiometer measures displacement of the wheels, on a
C. moment arm which magnifies the reading mechanically.
The center of gravity would be positioned as close as
possible to the center axis of the pipe, to provide stability
and traction.
6
2.2 Concept Selection
Ease of
Cost Accuracy Speed Ease of Use
Construction
Minimal
Maintenance OD Measurements Intuitive user
Welding/mach
Cost Measurement / minute interface
ining
Construction Simple
Cost Circuitry
Figure 2.1
In figure 2.1 the major design needs are listed in a hierarchy which allows for scoring the relative
importance of each need using a weighted average procedure called the Analytical Hierarchy
Process.
Ease of Construction
Ease of Use
Accuracy
Low Cost
Speed
Summary
Final Score
Weighting
Weighting
Weighting
Weighting
Weighting
Score
Score
Score
Score
Spool Concept 0.146 0.275 0.346 0.502 0.171 0.389 0.182 0.323 0.154 Score
0.377 0.398
Scissor Concept 0.146 0.233 0.346 0.245 0.171 0.389 0.182 0.409 0.154 0.353 0.314
Caterpillar Concept 0.146 0.383 0.346 0.180 0.171 0.069 0.182 0.143 0.154 0.211 0.189
Hinge-Wormgear Concept 0.146 0.110 0.346 0.073 0.171 0.153 0.182 0.125 0.154 0.059 0.099
Table 2.1
Table 2.1 shows the results of scoring using pairwise comparison, where each criteria was
compared with every other criteria. For example, A) Accuracy vs. B) Low Cost would produce a
score of 5 out of 10, which means A is significantly more important than B. This is reflected in
the weighting of accuracy which has the highest importance weighting at 0.346. Each concept is
then scored in terms of these criteria, and the weights are applied to produce a final score. The
spool roller concept won out because it was seen as easy to build, low cost and had the potential
for high accuracy.
7
Low Cost
0.600
0.500
0.400
0.300
Ease of Construction 0.200 Accuracy
0.100
0.000 Spool Concept
Scissor Concept
Caterpillar Concept
Ease of Use Speed
Hinge-Wormgear
Concept
Figure 2.2
Figure 2.2 shows a visual representation of the AHP Scoring process. The concepts with the
largest pentagon area scored the highest. The strength of the spool concept is clearly visible in
this visual.
8
3.0 Final Design
3.1 Manufacturing Process
Chassis
Left Vertical - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut to shape on water jet
Drill and tap #10-24 hole
Apply lock tight to hole and insert thread rod until seated
Install stationary roller bearings and drill rod with 2 bolts and nuts each
Right Vertical - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut to shape on water jet
Drill and tap #10-24 hole
Apply lock tight to hole and insert thread rod until seated
Slide linear bearings into place and fasten with 4 bolts and nuts each
Insert measuring assembly into place
Bottom Piece - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut to shape on water jet
Cut and tap (4) #10-24 holes
Top Piece - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut to shape on water jet
Body Assembly
Screw left and right verticals onto the bottom piece with the top of the bearings facing
inward using .5 in #10-24 fasteners
Drive System
Torsional Suspension Arm - Raw stock 12” x 12” x 3/8” plate 6061 aluminium
Cut main arm with water jet
Cut out motor mount with band saw
TIG Weld bracket to main arm
Drill through holes for hinge and wheel axle - 1/4"
Drill clearance holes for motor bracket through motor mount - #2
Motor
Mount motor to motor mount using motor bracket with #2 machine screws
Transmission
Mount drive-shaft adapter to wheel hub using wood screws
Affix drive-shaft to adapter using set screw
Wheels
Using belt sander, flatten the rounded tread profile of the stock wheels and affix to wheel
hub, and support using drive axle
9
3.2 Components and Component Selection
3.2.1 Electrical
Legend
Drive Drive Caliper T indicates
7.2 measurement in the
Motor Motor
5VV
perpendicular plane
Caliper II represents the
1.5 parallel plane
V
Logic
Battery Arduino
Block
Digital Calipers
The digital calipers selected have a resolution of 0.0005" and are accurate to 0.001”. They
provide a serial output that can be interfaced with the Arduino to take readings.
2N3904 Transistors
The caliper signal output is a normal low 1.5 Vpp square wave. Through the use of a BJT
transistor and some pull up resistors output signal is inverted and the voltage is raised to a 5 Vpp
signal that registers on the Arduino. The 2N2222 transistor is also a viable candidate for the
application but the 2N3904 was selected on convenience since some were already on hand.
10
with either the wireless USB or Bluetooth it will be very transferable and able to be operated
from almost any computer. A selection of the code is provided in Appendix G.
Batteries
We selected 1200mAh Lithium polymer batteries available from Pololu.com. Batteries were
selected on the basis of size and energy storage density. Lithium polymer batteries were selected
due to high storage capacity and light weight.
Motor
We selected the Micro Metal Gear Motor with gear ratio 298:1 available from pololu.com.
Drive
Arm Motor Mount
Figure 3.4 Full Assembly with drive arms, Arduino, and calipers
11
3.3 Schematics
3.3.1 Electrical Components
Figure 8.4 shows the logic bock circuitry. This is the array of transistors that will convert the 1.5
Vpp caliper signal into a 5 Vpp signal that the Arduino can read. As the caliper signal goes high,
it will turn on the BJT pulling the Arduino pin to ground. When the caliper signal falls, the BJT
will turn off and the pull up resistor will pull the Arduino pin to a high signal of 5V.
Figure 3.5 shows the Visual Basic GUI that will handle the graphical display of the crawler’s
status, control options, and data handling and export. The current control scheme will be a
simple GO button that will put the crawler into measure mode and it will proceed to measure the
entire pigtail. The GUI will include a visual graph that will show a cross section of the pipe
alerting the operator of any large bulges immediately. The data exporting will also be handled
inside the app and will export the data into a CSV file that can be readily manipulated by most
data handling software packages.
12
3.5 Testing
Metrics to test:
1. Accuracy
2. Repeatability
3. Speed
4. Run Time
5. Wireless Range
6. Signal Conversion
1. Accuracy
a. Randomly distribute 15 test points along a pipe.
b. Record the diameter at each mark using a digital caliper or comparable measuring
device accurate to 1/10000th of an inch.
c. Place Pipe Surfer at the first mark and record the reading.
d. Repeat until all marks have been measured.
e. All measurements taken must be within 1/1000th of the accepted diameter found
by the control measurement.
2. Repeatability
a. Using the same marks as test 1, take 30 measurements at each mark moving the
Pipe Surfer off of the spot by a minimum of 2 in and then back onto the
measurement spot.
b. Compare measurements to the actual values measured by a device accurate to
1/10000th of an inch. The difference should be no greater than 1%.
3. Speed
a. Run Pipe surfer at maximum speed setting of motors controller chip
b. Record time for a round trip along pigtails
c. Run the Pipe Surfer along pigtails at 10% of maximum speed setting and take
measurements
d. Run it along the pigtails 4 different speeds, (25%, 50%, 75%, Max) the final run
being the max speed of the motors. Run the pigtail along the pipe 5 times for each
speed level.
e. Compare the data of each run to that of the 10% run speed. The highest speed that
maintains accurate and repeatable data based off of previous tests will be the
maximum operating speed of the device.
4. Run Time
a. Run pipe surfer up and down the test pipe at set max speed until only 20%
remains in battery.
b. Recharge the battery and run test 9 more times.
c. Take the lowest time and set that as the run time.
13
5. Wireless Range
a. Connect pipe surfer to computer through wireless connection.
b. Move pipe surfer one foot away, linearly, from the computer every thirty seconds.
c. Watch for the connection to drop, record distance.
d. Repeat 4 more times for verification.
e. Wireless USB standard is 50 feet; the real world distance should be more than 45
feet.
6. Signal Conversion
a. Connect caliper to logic block.
b. Attach oscilloscope to both output and input side of logic block.
c. Compare input and output signals.
i. Correct operation will be an inverted 5 Vpp signal.
d. Repeat for all four circuits.
There are three main electrical components that need to be assembled; the shield, the motors and
accessories. There are two shields that need to be assembled, the Mega Protoshield that houses
the motor components and the Wixel shield that is has the wirelessUSB module and caliper
modules.
14
3
4
2 5
6
The first step in building the Mega Shield is to lay out the components. The wires, connectors
and components are then soldered into their respective places. The first item to solder is the 6
position right angle headers for the motor wires. These are placed at the end of the proto shield
angled to the top as shown. Next the motor control wires are soldered from pins 9, 10, 11, and
12 to the M1IN1, M1IN2, M2IN1, andM2IN2 pins on the motor driver chip, respectively. After
the motor control wires, the motor driver chip and motor power wires can be connected. The
pinout of the motor driver chip is as follows, top to bottom, M2OUT2, M2OUT1, Vin, Gnd,
M1OUT2, and M1OUT1. The pin out of the motor wire from left to right is; M1, M2, Vcc,
Encoder A, NC, and Gnd. The last component of the Mega shield is the voltage divider that the
Arduino to read the high 7-8.5V motor voltage. This is placed on the remaining three pads on
the bottom row of 4 hole pads.
15
3 4
1 (Hidden)
2
1
The wixel shield hosts the caliper connections, logic block, and wirelessUSB module. The
wirelessUSB module comes preassembled and simply needs to be plugged into its socket on the
right side of the shield. The caliper connections are two simple male 4 position connectors these
are soldered into the three position pads in the middle of the board on the left side. The pin out
from left to right is Gnd, Clock, Data, 1.5V. The logic block is to be repeated four times for both
clock pins and both data pins. The center of the block is the transistor. The base of the transistor
is connected to the signal pin through a 20 kOhm resistor the emitter is connected directly to
ground. On the collector pin, one connection is made to the Arduino digital pin corresponding to
the appropriate signal and a second one is made through a 20 kOhm pull up resistor to the 5 V
bus. The last component on the Wixel shield is the 1.5 V supply for the calipers. This is built
under the wirelessUSB module with a voltage divider that creates a 2.2 V level on the base of a
transistor. The transistor then clamps the voltage of the emitter to 1.5 V through a pull down
resistor to ground. The collector is connected directly to the 5 V bus.
There are several programming steps in the project including the Arduino, Visual Basic
application and the wireless USB modules. The code to program both the Visual Basic
application and Arduino microcontroller is located in Appendix H. The GUI layout and button
names are also included in Appendix H. The wirelessUSB modules are programmed with the
basic serial communication apps provided by Pololu.com. The only modification is the serial
data rate was changed to 115200 baud to enable wireless Arduino programming.
Two 3.7V 1200 mAh LiPo batteries were used to power the pipe surfer. They were wired in
series to provide a total of 7.2V and 1200 mAh and are the main source of power for the entire
device. The batteries were wired to a polarized connector to make it impossible to accidently
plug them in with reverse polarity and a standard LiPo charger was used to safely recharge them.
16
Motor wiring
The motor was driven using a Dual MC33926 from Freescale semiconductor purchased from
pololu.com. The team used jumpers to change the default states of the disable pins, and the
enable pin, so that PWM could be used on the logic inputs (M1 IN1/IN2 and M2 IN1/IN2) to
control the speed of the motor. Vin was supplied from the batteries at approximately 8.4V when
fully charged. Vdd (+5V) and GND came from the Arduino to run the TTL Logic on the chip.
Finally, the motors were connected via the outputs on the MC33926 to connections on the wheel
encoder chip which was mounted on the motor.
Wheel Encoder
The wheel encoder boards were connected via 6 pins each using a ribbon cable: 2 motor power
lines, +5V and ground, and encoder A and B outputs. These were read using interrupts which
triggered an increment function, allowing position and direction tracking of the device.
17
4.2 Test Results and Discussion
4.2.1 Accuracy
The accuracy test procedures were followed to gain an understanding of how accurate the pipe
surfer is. Fifteen spots were marked on the pipe with chalk and then measure accurately with a
pair of unmodified calipers. The pipe surfer was then moved to each location and a measurement
was taken. This data was then analyzed to quantify the amount of error. It was calculated that the
pipe surfer has an average error of 0.11%. This is within the limits set for the device.
Accepted Device
Test Measurement Measurement
Point # (inches) (inches) Error (%)
1 1.682 1.6836 0.100744
2 1.688 1.6872 -0.06182
3 1.689 1.6917 0.189714
4 1.684 1.6846 0.024905
5 1.683 1.6857 0.143331
6 1.690 1.6931 0.172911
7 1.682 1.6801 -0.08532
8 1.685 1.6874 0.134704
9 1.686 1.6896 0.223822
10 1.682 1.6838 0.119099
11 1.689 1.6902 0.043235
12 1.689 1.6869 -0.10547
13 1.682 1.6812 -0.04153
14 1.685 1.6862 0.048873
15 1.689 1.6869 -0.10487
The pipe surfer is powered by two 3.7V 1200mAh lithium polymer batteries in series. This
supplies 8.4V when fully charged. The test procedure was followed using a voltage divider to
measure battery voltage. The Arduino was coded to stop the surfer and throw up a warning when
the minimum battery voltage was reached which is 7V. Although these batteries provided
enough power to run the device, the run time was disappointing. A final average runtime was
calculated to be 9 minutes. It takes two and a half hours to charge the battery packs when
charged at 1000mA. With a pipe measurement time of 5 minutes a pipe, this only allows for 1
pipe before the batteries must be replaced and recharged. This is not practical for the real world
and a higher capacity battery would be needed. To run for 1 hour, 12 pipes, between battery
changes, an estimated capacity of 8400 mAh would be needed.
18
4.2.3 Wireless Range Test
This test was conducted in two environments to take into account the different location within
the HYCO plant it will operate. The first was inside a building in a long hallway. With the
surfer’s transceiver facing away from the controlling computer, a distance of 105’ was obtained
with only the body of the device and a human body in between. This was much better than the
listed 50’ of range listed on the transceiver.
The second location was in a parking lot full of cars. This was chosen because it allowed for long
distance line of sight testing in an open space and metal interference by holding the device
behind cars at varying distances. With interference from cars, the device was able to work
reliably at a distance of 45’. This is an acceptable distance and passes the needed range
requirement. When tested line of sight, nothing in between the surfer and the controlling
computer, the surfer reliably worked up to 216’.
The team began brainstorming shortly after the visit to Air Products. Many concepts were
created without any restrictions, allowing a full sample of the design space. When selecting the
final concept, the most difficult consideration was the sensor type to choose to take the
measurement. Optical sensors were considered the ideal solution due to the lack of wear due to
use, and therefore long, repeatable lifetime. When evaluating these sensors for accuracy, weight
and cost, the team found that optical distance sensors had very high price tags and required
expensive electronics to process the signals they produce. The team moved forward with the
capacitive measurement sensor contained in a digital calliper that could be easily interfaced, was
inexpensive, and had the required accuracy.
Patents were researched to prevent infringement and to inspire new ideas for concept generation.
The team found no patents that directly competed with the device Air Products requested. The
Air Products device would perform many more functions than any of the devices shown, and
therefore the team moved forward with little fear of patent infringement.
The final concept was selected using the AHP scoring method and the design was refined over
the course of several weeks. The rationale for the design decisions can be found in section 8.1-
8.4. Solidworks provided the virtual design space. The team started by working with geometric
components without regard for the off-the shelf components that would be used for construction.
Using these placeholder components, the general size and spacing was determined. Components
19
were selected to be generally very robust as to provide a rigid measurement platform which
could perform very accurate measurement for the customer.
The team then converted this generalized designed into a design which considered the
construction process more fully. Off-the-shelf components were selected for the mechanical
design and calipers were selected. 3D models gathered from McMaster-Carr were then
incorporated into the final Solidworks model. To build the chassis upon which the measurement
and drive components would be mounted, the team chose to use the water jet machine, because it
allowed for the automated drilling of holes, very close tolerance, and the ability to reduce weight
by removing unnecessary material, all from a Solidworks drawing and a single plate of stock
6061 Aluminum. A Bill of Materials was drafted and the plan for construction was completed.
The electrical design was completed in parallel with the mechanical design. Interfacing the
callipers presented a challenge because they operate on a 1.5 V logic circuit whereas the Arduino
operates on 5V logic. A circuit was designed to convert the signal from 1.5 to 5V and Arduino
code was written to interpret the serial output of the calipers through the use of interrupts. Motor
drivers and optical encoders were integrated using code adapted from the motor control lab from
ME 445 Lab.
Finally, Testing was performed to identify any issues and to compare performance with the
objectives of the project. The performance of the device was acceptable. The motors turned out
to be underpowered due to unforeseen frictional effects, rendering the crawler incapable of
climbing vertically. The accuracy of the device, a key design goal, was highly successful. The
level of error when the device was properly calibrated was very close to the ambitious goal of +/-
0.001”. Overall, the team worked together well and learned a variety of skills which combined
the disciplines of mechanical engineering, controls engineering, and electrical design.
20
References
Note: That for the author-date system, references are listed in alphabetical order.
Muriru, P.K. and Daewoo, R., “Prediction of the Heat Transfer Characteristics of a Multi-
Flame Injector'”, Combustion and Flame, vol. 100, no. 2, pp. 123-135, 2002.
Peters, L., Johnson, M., and Davidson, K., “A Novel Approach to Four-Bar Synthesis'”, 10th
ASME Design Automation Conference, pp. 234-250, Pittsburgh, PA, 2001.
Swanson Inc., “Online Users Manual for ANSYS 5.0'', http://www.ansys.com/ manual, viewed
on March 1999.
Wen-Cheng, C., “Electric Bicycle'”, US Patent no. 5,368,122, November 29, 1994.
Zacharia, M. and Daudi, P.K., The Effect of Multi-materials on Conventional Finite Element
Formulations, New York: Wiley and Sons, 2001.
21
Appendix A: Gantt Chart
22
Appendix B: Bill of Materials
Item Item # Source Description Quantit Price Per Total
y Unit
Drive
Components
298:1 Micro 2218 Pololu 100 RPM 70 oz-in 2 $15.95 $31.90
Metal Gearmotor
HP
Drive Shafts 1257K68 McMast 24"L .25"D 1 $15.75 $15.75
er
Polyurethane 2337T2 McMast 2" Diameter 15/16" 4 $2.33 $9.32
Wheels er hub, 7/8" Width
Micro Metal Gear 989 Pololu Set of 2 1 $4.99 $4.99
Holder
Potentiometer $0.00
angular Position
Sensor
Battery/Power $0.00
Supply
Micro Metal Gear 2590 Pololu Set of 2, 3 and 5 1 $8.95 $8.95
Optical Encoder spokes
Cut to Width $0.00
Polyurethane
Wheels
6 Pin Connector - 30K6870 Newark TE - Amp - 2 $1.02
Male - Through Through hole
6 Pin Connector - 52K4340 Newark Te- Amp - 2 $0.35
Female- Wire Wire/Crimp
Music Wire 9271K53 McMast 90 Degree Angle, 1 pack $7.63
Torsion Spring er .484" Spring OD,
.054" Wire, Left-
Hand
Music Wire 9271K52 McMast 90 Degree Angle, 1 pack $7.63
Torsion Spring er .484" Spring OD,
.054" Wire, Right-
Hand
23
4 Pin, TE, Male- 08N3025 Newark 4 Pin Connector - 3 $0.68 $2.04
Through, .1" Male - Through
4 Pin, JST, Male - 87R0314 Newark 4 Pin Connector - 3 $0.40 $1.20
SMD, 1mm Male - SMD
4 Pin, TE, Female 52K4345 Newark 4 Pin Connector - 3 $0.15 $0.45
- Wire, .1" Female -
Wire/Crimp
4 Pin, JST, 33P8106 Newark 4 Pin Connector - 3 $0.40
Female - Wire, Female -
1mm Wire/Crimp
5MOhm resistor - battery 2
meter
$0.00
$0.00
Physical $0.00
Components
Sheet Aluminum 9246K23 McMast 12" x 12", 3/8" 1 $36.92 $36.92
er thick
Measurement 9657K31 McMast OD .66", Length 1" 1 $8.38 $8.38
Suspension 7 er (1 Pack of 6)
Spring
Linear Bearing 6483K51 McMast 1" height 1/4" Shaft 4 $26.87 $107.4
er 8
Roller Bearing 2342K18 McMast OD 5/8" ID 1/4" 4 $17.66 $70.64
3 er High precision
Mini Aluminum 8600N3 McMast Aluminum Base- 4 $15.14 $60.56
Mounted Bearing er Mount l Ball
Bearing
$0.00
Donated by ME $0.00
445
Digital Calipers N/A Amazon hornady digital 3 $25.73 $77.19
calipers
Wixel Pair + 1339 Pololu Wireless USB 1 $39.95 $39.95
USB Cable
Wixel Shield for 2500 Pololu Wireless USB 1 $12.95 $12.95
Arduino Protoboard
24
Appendix C: Software Block Diagram
NO
YES
NO
YES
NO YES
25
Appendix D: Code
Communication Protocols
The Visual Basic App and Arduino Microcontroller communicate over a basic USB connection
using the serial protocol. While all of the commands are built into the system, they are also
outlined below to aid in troubleshooting.
26
Arduino Code
/*Arduino Code
Pipe Surfer
/*
struct dataPacket
{
int distance;
int Idiameter;
int Tdiameter;
};
dataPacket measurements[1000];
*/
//Instructions
const int ST = 48; //0
const int MS = 49; //1
const int FW = 50; //2
const int RV = 51; //3
const int GO = 52; //4
const int CT = 53; //5
//Control Commands
const int CAL = 67; //C
const int RST = 82; //R
const int BAT = 66; //B
const int r1 = 977;
const int r2 = 507;
const float vRatio = 5.0 / 1023;
//Pin Definitions
const int calClkI = 2;
const int calClkT = 3;
const int calDataI = 4;
const int calDataT = 5;
const int LEDpin = 13;
const int encoderA = 19;
const int encoderB = 18;
const int pwmAA = 9;
const int pwmAB = 10;
const int pwmBA = 11;
const int pwmBB = 12;
27
volatile int bitsIn = 0;
volatile int odd = 0;
volatile int neg = 0;
volatile unsigned int data;
//Flags
volatile int flag = LOW;
volatile int tail = HIGH;
volatile int autoMeasure = LOW;
//Rotational Int Vars
volatile int encoderRev = 0;
volatile int wheelRev = 0;
const int encoderLimit = 3 * 298;
volatile int running = LOW;
//Data vars
int valueT = -1;
int valueI = -1;
int traverse = 0;
int inst = -1;
int calibrationI = 0;
int calibrationT = 0;
String dataOut;
float batteryVoltage;
int batteryDelay = 0;
void setup()
{
Serial.begin(115200);
// Data in pins
pinMode(calClkI, INPUT); // Clock I pin = 2
pinMode(calClkT, INPUT); // Clock T pin = 3
pinMode(calDataI, INPUT); // Data I pin = 4
pinMode(calDataT, INPUT); // Data T pin = 5
pinMode(LEDpin, OUTPUT); // LED Pin
pinMode(encoderA, INPUT); // Encoder A pin = 19
pinMode(encoderB, INPUT); // Encoder B pin = 18
Serial.println("C:Power On!");
void loop()
{
delay(100);
//read serial for command
while(Serial.available() > 0) //If only two characters in
{
//Serial.println("Data In");
inst = Serial.read();
//Serial.println(inst);
28
//Switch case for instruction
switch (inst)
{
case MS:
// Take spot measurement
//Serial.println("C:Spot Measurement.");
spotMeasure();
break;
case FW:
// Forward
//Serial.println("C:Move forward.");
mForward();
break;
case RV:
// Reverse
//Serial.println("C:Move reverse.");
mReverse();
break;
case ST:
// Stop
//Serial.read("C: Stop.");
mStop();
break;
case GO:
// GO - Enter Auto Mode
//Serial.read("C: Auto Mode.");
autoMode();
break;
case CT:
//control mode
//Serial.println("C:In Control Mode.");
control();
break;
default:
// Ask for retransmission?
Serial.println("C:Incorrect Command. Please Try again.");
Serial.flush();
}
Serial.flush();
}
//Check battery
if(batteryDelay > 599)
{
batteryDelay = 0;
checkBattery();
//if less than 7
if(batteryVoltage < 7.25)
Serial.println("C:Low Voltage!");
}
else
batteryDelay++;
void spotMeasure()
{
29
// Measure - Take measurement
takeMeasure();
//Send Measurement
dataOut = "M:" + (String)valueI + ":" + (String)valueT;
Serial.println(dataOut);
}
void autoMode()
{
// GO - Enter Auto Mode
Serial.println("C:Auto Mode");
//attach position interupt?
// Encoder A
traverse = 0;
//clearData();
//autoMeasure = HIGH;
do
{
//initilize motors
running = HIGH;
attachInterrupt(4, rotate, FALLING);
mForward();
//wait while running
while(running);
//Serial.println("PC");
//change position
traverse++;
Serial.println(traverse);
//take measurement
takeMeasure();
delay(100);
void control()
{
// Control Items
while(Serial.available())
{
//Serial.println("Data In");
inst = Serial.read();
//Serial.println(inst);
30
//Switch case for instruction
switch (inst)
{
case CAL:
int newCalibrationI;
int newCalibrationT;
//Calibration value
//intake calibration value
newCalibrationI = readInt() * 10;
newCalibrationT = readInt() * 10;
//set calibration
takeMeasure();
case RST:
traverse = 0;
Serial.println("C:Traverse Reset");
break;
case BAT:
//Serial.println("C:Check Battery");
checkBattery();
Serial.print("C:Battery Voltage is ");
Serial.print(batteryVoltage);
Serial.println(" V.");
break;
default:
Serial.flush();
}
}
int readInt()
{
//Function to read int off serial
boolean negative = false;
int numberIn = 0;
while(Serial.available())
{
byte in = Serial.read();
switch (in)
{
case ':':
if (negative)
return (-1 * numberIn);
else
return (numberIn);
31
break;
case '-':
negative = true;
break;
}
}
if (negative)
return (-1 * numberIn);
else
return (numberIn);
void checkBattery()
{
// Simple voltage divider to check battery level.
//Serial.println("C:Todd's Battery Code Here");
void rotate()
{
encoderRev++;
if(encoderRev > encoderLimit)
{
mStop();
detachInterrupt(4);
//Serial.println("IR");
//set flag
running = LOW;
encoderRev = 0;
//wheelRev++;
}
void mForward()
{
// motor forward
analogWrite(pwmAA, 127);
digitalWrite(pwmAB, LOW);
analogWrite(pwmBA, 127);
digitalWrite(pwmBB, LOW);
void mReverse()
{
32
// motor stop
analogWrite(pwmAB, 127);
digitalWrite(pwmAA, LOW);
analogWrite(pwmBB, 127);
digitalWrite(pwmBA, LOW);
}
void mStop()
{
// motor stop
//Serial.println("C:Stop!");
digitalWrite(pwmAA, LOW);
digitalWrite(pwmAB, LOW);
digitalWrite(pwmBA, LOW);
digitalWrite(pwmBB, LOW);
}
void readCaliper()
{
if(tail)
{
//Check for starting bit
now = millis();
//Serial.println(now);
if((now - lastPulse) > 100)
{
tail = LOW;
odd = !digitalRead(dataIn);
}
else
lastPulse = now;
}
else
{
bitsIn++;
//Serial.print(bitsIn);
data = digitalRead(dataIn); // read DATA value as soon as possible
33
{
neg = 1;
}
}
else if (bitsIn > 23)
{
if(dataIn == calDataI)
detachInterrupt(0);
else if (dataIn == calDataT)
detachInterrupt(1);
//Serial.println("Interrupt Removed!");
//Serial.print("N: ");
//Serial.println(neg);
// reset variables
tail = HIGH;
flag = HIGH;
bitsIn = 0;
//Serial.println(value, BIN);
//Serial.println(value, DEC);
}
}
}
void takeMeasure()
{
//Serial.println("Taking Measurement");
int valid = LOW;
int unstable = 0;
//reset measurements
valueI = -1;
valueT = -1;
// Signal starting measurement
digitalWrite(13, HIGH);
//Test data
//valueI = random(500, 1000);
//valueT = random(500, 1000);
// Set value
valueI = mmTOin(value) + calibrationI;
//Serial.print("I Value: ");
//Serial.println(valueI, DEC);
34
// Measure Second Calliper
dataIn = calDataT;
tail = HIGH;
lastPulse = millis();
attachInterrupt(1, readCaliper, FALLING);
//wait for reading
flag = LOW;
while(!flag);
flag = LOW;
// Set value
valueT = mmTOin(value) + calibrationT;
//Serial.print("I Value: ");
//Serial.println(valueI, DEC);
//End of measurement
digitalWrite(13, LOW);
/*
void clearData()
{
for(int x = 0; x < 1000; x++)
{
measurements[traverse].distance = 0;
measurements[traverse].Idiameter = 0;
measurements[traverse].Tdiameter = 0;
}
}
*/
int mmTOin(int mm)
{
return mm * 3.94;
}
35
Visual Basic Code
' Pipe Surfer
' EDSGN 460W
' Air Products OD Measurement Crawler
' Team Snakes in 2 Planes
' "The Pipe Surfer"
' Chris Dickson
' Adam Menko
' Matt Tarabulski
' Todd Troutman
Imports System.Drawing
End Structure
End Sub
36
Public Sub newMeasurement()
'read serial port and update label
Dim newData As String
Dim trans As Integer
Dim valueI As Double
Dim valueT As Double
Dim part() As String
Dim controlChar As Char
If (Not test) Then
newData = ArduinoPort.ReadLine
Else
newData = txbDataTest.Text
End If
Case "M"
'M - spot measurement
MessageBox.Show("Spot Measurement" & vbCrLf & "Distance: " & vbCrLf & "I Diameter: " &
part(1).ToString & vbCrLf &
"T Diameter: " & part(2).ToString, "Spot Measurement", MessageBoxButtons.OK,
MessageBoxIcon.Information)
Case "C"
'C - control/special message
MessageBox.Show(part(1), "Control Message", MessageBoxButtons.OK, MessageBoxIcon.Hand)
End Select
End Sub
37
End Sub
End Sub
End Sub
End Sub
38
grpAdvanced.Visible = False
mnuAdvanced.Checked = False
Else
grpAdvanced.Visible = True
mnuAdvanced.Checked = True
End If
End Sub
MsgBox(fileName)
'Header
dataOut.WriteLine("Linear, T Diameter, I Diameter")
'write data
'iterate on measurement struct
For x As Integer = 1 To 10
'measurement, I diam, T diam
dataOut.WriteLine(dataIn(x).exportData)
Next
39
'close file
dataOut.Close()
Else
'cancelled
End If
End Sub
#Region "Advanced Buttons"
Private Sub btnTestData_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnTestData.Click
test = True
newMeasurement()
test = False
End Sub
'MsgBox(message)
sendMessage(message)
End Sub
#End Region
40
End Class
'Things to do!
'Decipher Data
'Store Data
'Export Data
'Clear Data
'Graphingz
'Calibration? This end?
41
42
ME
445
FINAL
REPORT:
THE
LIGHTRIDER
Katie
Vaughan
Jared
Yarnall-‐Schane
Abstract
The
purpose
of
this
project
was
to
create
an
animated
picture
on
the
spokes
of
a
bike
wheel.
To
accomplish
this,
three
sets
of
LED
strips
containing
11
LED
lights
each
were
attached
to
the
bike
wheel
at
120-‐degree
intervals.
These
lights
were
controlled
by
information
obtained
from
an
accelerometer/gyroscope
combination.
The
raw
data
obtained
from
the
gyroscope
was
converted
into
a
readable
format
to
obtain
the
orientation
of
the
bike
wheel,
and
thus
position
of
each
LED
light,
at
any
point
in
time.
A
Hall-‐Effect
sensor
was
used
to
prevent
gyroscope
drift.
The
lights
turn
off/on
at
a
given
time
determined
by
the
position
of
the
wheel
to
display
a
specific
picture
from
the
corresponding
code.
Table
o f
C ontents
ABSTRACT
..............................................................................................................................................................
0
1.0
INTRODUCTION
.............................................................................................................................................
2
1.1
MOTIVATION
......................................................................................................................................................................
2
2.0
ELECTRICAL
CIRCUIT
..................................................................................................................................
3
2.1
LED
STRIPS
........................................................................................................................................................................
3
2.2
HALL
EFFECT
SENSOR
......................................................................................................................................................
3
2.3
GYROSCOPE
.........................................................................................................................................................................
4
3.0
MECHANICAL
SETUP
....................................................................................................................................
4
3.1
BILL
OF
MATERIALS
..........................................................................................................................................................
5
4.0
COMPUTER
PROGRAMMING
.....................................................................................................................
6
5.0
DISCUSSION
....................................................................................................................................................
7
6.0
CONCLUSION
&
FUTURE
CONSIDERATIONS
........................................................................................
9
APPENDIX
A:
CIRCUIT
DIAGRAM
..................................................................................................................
11
APPENDIX
B:
SYSTEM
PHOTOGRAPHS
.......................................................................................................
12
APPENDIX
C:
SOLIDWORKS
MODEL
PHOTOS
...........................................................................................
17
APPENDIX
D:
ARDUINO
CODE
........................................................................................................................
22
APPENDIX
E:
DATA
SHEETS
...........................................................................................................................
28
1
1.0
Introduction
Since
a
young
age,
many
bicycle
riders
have
used
various
methods
of
decorating
their
bikes.
From
baseball
cards
to
Christmas
lights
and
everything
in
between,
cyclists
have
prided
themselves
in
their
ornaments.
Our
group
wanted
to
take
this
tradition
a
step
further
and
set
a
new
standard
for
bicycle
decorations.
Not
only
is
an
animated
picture
on
a
bike
wheel
fun
and
attractive,
it
is
also
a
safety
method
for
nightriders.
Given
their
limited
visibility
to
vehicles
on
the
road,
LED
lights
would
provide
an
excellent
way
to
see
the
cyclist
in
the
dark.
The
system
consists
of
three
LED
strips
connected
to
an
Arduino
Uno
powered
by
a
single
9V
battery.
After
the
LEDs
are
turned
on,
a
GY-‐521
MPU-‐6050
gyroscope/accelerometer
breakout
board
will
calculate
the
orientation
of
the
wheel
at
any
point
in
time,
and
therefore
the
position
of
the
LED
lights
at
any
point
in
time.
The
raw
data
acquired
from
the
gyroscope
is
then
converted
by
the
Arduino
code
into
information
that
can
be
read
by
the
group,
which
is
one
of
the
most
difficult
aspects
of
the
project.
Once
the
data
is
converted,
it
is
used
to
turn
on/off
the
individual
LED
lights
depending
on
the
angular
velocity
of
the
bike
wheel
so
as
to
keep
the
rotating
image
in
the
proper
orientation.
A
Hall-‐Effect
sensor
was
used
to
reset
the
position
every
180°
so
as
to
prevent
gyroscope
drift.
Once
the
mechanical
and
electrical
setup
was
complete,
our
group
decided
to
create
a
product
based
on
the
project.
We
named
our
product
the
LightRider
and
conducted
a
comparison
of
our
product
to
others
currently
on
the
market.
This
analysis
can
be
found
in
the
Discussion
section.
1.1
Motivation
The
motivation
for
this
project
came
from
a
combination
of
interests
in
business
and
innovation.
Our
group
wanted
to
create
a
product
that
could
be
sold
for
cheaper
than
what
is
currently
on
the
market,
but
that
is
innovative
enough
to
be
an
enjoyable
challenge.
Our
2
combined
interests
directed
us
to
a
moving
component
that
lights
up
with
LED
strips.
From
there
we
considered
an
LED
propeller
clock,
a
light
display
based
on
musical
input,
and
various
other
options.
We
decided
on
the
LED
bicycle
GIF
because
one
of
our
group
members
is
an
avid
rider
and
found
deep
interest
in
the
subject.
3
on.
This
provides
a
zero
point
for
the
gyroscope,
which
makes
initializing
the
orientation
of
the
picture
on
the
bike
wheel
much
easier.
It
also
provides
a
backup
position
data
source.
While
the
gyroscope
is
an
excellent
method
of
acquiring
the
angular
velocity
and
position
of
the
wheel,
there
tends
to
be
a
slight
drift
in
the
data
over
time.
By
using
the
Hall-‐Effect
sensor
as
a
reset
for
the
gyroscope,
we
can
eliminate
this
error.
2.3
Gyroscope
Once
the
LED
lights
are
turned
on,
a
GY-‐521
MPU-‐6050
gyroscope
is
used
to
determine
the
location
and
speed
of
each
of
the
LED
strips.
(The
data
sheet
can
also
be
found
in
Appendix
E.)
The
raw
data
outputted
by
the
gyroscope
is
unable
to
be
read
by
a
human
without
first
converting
the
data
to
something
meaningful.
Using
the
I2Cdevlib
library,
we
successfully
converted
the
code
and
were
able
to
retrieve
the
angular
speed
of
the
bike
wheel
in
degrees/second.
Using
the
angular
velocity,
we
were
able
to
integrate
this
data
to
obtain
the
position.
For
our
particular
project,
a
picture
of
Pacman
was
used
to
demonstrate
how
certain
LEDs
could
be
turned
off
a
specific
location
(see
Figure
7
in
Appendix
B).
With
the
position
of
the
LEDs
known,
we
were
able
to
turn
the
LEDs
off
for
a
60-‐degree
interval
for
every
rotation,
creating
the
Pacman
image
on
the
bicycle
wheel.
A
photo
of
the
image
can
be
found
in
Appendix
B.
The
image
is
slightly
inaccurate
because
of
the
camera
used
to
take
the
photo.
We
would
have
required
a
camera
with
a
longer
exposure
time,
and
such
a
camera
was
unavailable.
4
superglue
(Figure
4).
A
hole
was
then
cut
into
each
of
the
wooden
boards
to
feed
the
wires
through
so
they
are
hidden
from
sight
and
are
protected
from
exogenous
conditions.
Next
the
Arduino
and
battery
pack
were
mounted
perpendicular
to
the
wooden
boards
(Figures
3
&
5).
This
was
done
so
the
gyroscope
was
in
the
correct
orientation
to
retrieve
the
desired
data
and
so
less
force
from
air
drag
was
exerted
on
the
Arduino
and
electrical
components.
After
the
Hall-‐Effect
sensor
was
positioned
(Figure
6),
the
magnets
were
glued
to
the
inner
part
of
the
bike
frame
at
a
distance
that
was
acceptable
for
the
sensor
to
still
experience
the
magnetic
fields.
For
the
purpose
of
the
class
project,
superglue
and
electrical
tape
were
used
to
attach
most
of
the
components.
For
our
LightRider
product
we
would
instead
use
the
SolidWorks
model
located
in
Appendix
C
to
create
the
parts
needed.
This
model
can
easily
be
modified
to
fit
any
bike
size,
and
is
made
to
attach
to
spokes
rather
than
to
the
type
of
wheel
used
in
our
project
(see
Figure
8
in
Appendix
C).
The
LightRider
includes
two
wooden
pieces
shaped
to
fit
the
curve
of
the
bike
spokes
(Figure
11).
These
pieces
are
attached
via
rod-‐
shaped
cutouts
in
the
boards.
Back
plates
(not
shown)
would
then
be
screwed
on
to
ensure
the
wooden
boards
are
secure
to
the
bike.
On
the
front
wooden
board
a
housing
compartment
for
the
Arduino
is
built
on,
which
will
be
screwed
into
the
other
wooden
piece
once
they
are
attached
to
the
spokes
(Figure
11).
Also
located
on
the
back
of
the
front
board
is
a
battery
compartment
(Figure
12),
which
will
already
include
the
battery
terminal
attachments
inside
to
create
less
setup
work
for
the
consumer.
On
the
front
of
each
wooden
board
the
LED
strips
are
located,
whose
wires
are
fed
through
three
separate
holes
cut
into
the
boards
and
then
through
hollowed
channels
to
the
Arduino
housing.
This
ensures
the
wires
are
protected
from
outside
conditions.
5
Table
1.
Bill
of
Material
6
mentioned
above,
using
the
sensors
to
reset
the
gyroscope
information
ensures
that
data
drift
does
not
occur.
This
process
is
repeated
until
the
Arduino
is
powered
off.
Figure
1.
Computer
Program
Flowchart
5.0
Discussion
There
are
a
few
criteria
set
to
determine
whether
our
project
is
a
success
or
not.
These
criteria
include
functionality
and
economic
dominance.
7
The
LightRider
worked
as
intended.
The
Hall-‐Effect
sensor
turned
the
LED
strips
on
at
the
correct
time,
and
the
information
received
from
the
gyroscope
was
successfully
read
to
obtain
the
location/speed
of
the
LEDs
at
any
point
in
time
while
riding
the
bike.
Using
this
information,
the
LEDs
were
coded
to
turn
on/off
at
specific
times
to
create
the
rotating
image.
Overall
the
LightRider
was
a
success
in
the
criteria
of
functionality.
To
determine
if
our
product
was
an
economic
success,
we
compared
the
LightRider
to
other
products
on
the
current
market
using
a
piecewise
design
matrix.
The
criteria
for
comparison
included
cost,
ease
of
setup,
the
ability
to
add
different
pictures,
and
the
ability
to
be
designed
for
different
bike
sizes.
Our
weighting
analysis
and
design
matrix
can
be
seen
below
in
Tables
2
and
3,
respectively.
Table
2 .
Design
Matrix
Weighting
Analysis
8
Note
that
the
cost
for
our
product
was
calculated
in
Table
1.
The
cost
of
machining
the
product
was
omitted
from
the
analysis
because
the
group
did
not
conduct
a
first-‐year
profit
analysis.
We
assumed
that
the
necessary
equipment
to
create
the
product
would
be
purchased
and
this
cost
would
be
returned
through
profit.
From
the
design
matrix,
it
can
be
observed
that
our
product
would
excel
in
the
current
market
and
could
be
a
successful
company
when
compared
to
the
Monkey
Light
Pro
and
the
Adafruit
SpokePOV
products,
which
were
the
only
two
products
found
that
are
similar
to
the
LightRider.
9
were
glitches
in
the
image
due
to
bumps,
we
would
need
to
modify
our
project
to
account
for
this
problem.
10
Appendix
A:
Circuit
Diagram
11
Appendix
B:
System
Photographs
12
Figure
4.
LED
Strip
Setup
13
Figure
5.
Arduino
Setup
14
Figure
6.
Hall-‐Effect
Position
15
Figure
7.
Long-‐Exposure
Pacman
Photo
16
Appendix
C:
SolidWorks
Model
Photos
17
Figure
9.
LightRider
without
Wheel
18
19
Figure
11.
Arudino
Housing
20
Figure
12.
Battery
Compartment
21
Appendix
D:
Arduino
Code
Setup
//
Begin
Gyroscope
setup
//
I2Cdev
and
MPU6050
must
be
installed
as
libraries,
or
else
the
.cpp/.h
files
//
for
both
classes
must
be
in
the
include
path
of
your
project
#include
"I2Cdev.h"
#include
"MPU6050.h"
//
Arduino
Wire
library
is
required
if
I2Cdev
I2CDEV_ARDUINO_WIRE
implementation
//
is
used
in
I2Cdev.h
#if
I2CDEV_IMPLEMENTATION
==
I2CDEV_ARDUINO_WIRE
#include
"Wire.h"
#endif
//
class
default
I2C
address
is
0x68
//
specific
I2C
addresses
may
be
passed
as
a
parameter
here
//
AD0
low
=
0x68
(default
for
InvenSense
evaluation
board)
//
AD0
high
=
0x69
MPU6050
accelgyro;
//MPU6050
accelgyro(0x69);
//
<-‐-‐
use
for
AD0
high
int16_t
ax,
ay,
az;
int16_t
gx,
gy,
gz;
//
uncomment
"OUTPUT_READABLE_ACCELGYRO"
if
you
want
to
see
a
tab-‐separated
//
list
of
the
accel
X/Y/Z
and
then
gyro
X/Y/Z
values
in
decimal.
Easy
to
read,
//
not
so
easy
to
parse,
and
slow(er)
over
UART.
#define
OUTPUT_READABLE_ACCELGYRO
//
uncomment
"OUTPUT_BINARY_ACCELGYRO"
to
send
all
6
axes
of
data
as
16-‐bit
//
binary,
one
right
after
the
other.
This
is
very
fast
(as
fast
as
possible
//
without
compression
or
data
loss),
and
easy
to
parse,
but
impossible
to
read
//
for
a
human.
//#define
OUTPUT_BINARY_ACCELGYRO
#define
LED_PIN
13
bool
blinkState
=
false;
//
End
Gyroscope
setup
//
Begin
LED
setup
22
#include
<Adafruit_NeoPixel.h>
#define
PIN
6
#define
PIN2
5
#define
PIN3
9
//
Parameter
1
=
number
of
pixels
in
strip
//
Parameter
2
=
pin
number
(most
are
valid)
//
Parameter
3
=
pixel
type
flags,
add
together
as
needed:
//
NEO_KHZ800
800
KHz
bitstream
(most
NeoPixel
products
w/WS2812
LEDs)
//
NEO_KHZ400
400
KHz
(classic
'v1'
(not
v2)
FLORA
pixels,
WS2811
drivers)
//
NEO_GRB
Pixels
are
wired
for
GRB
bitstream
(most
NeoPixel
products)
//
NEO_RGB
Pixels
are
wired
for
RGB
bitstream
(v1
FLORA
pixels,
not
v2)
Adafruit_NeoPixel
strip
=
Adafruit_NeoPixel(11,
PIN,
NEO_GRB
+
NEO_KHZ800);
Adafruit_NeoPixel
strip2
=
Adafruit_NeoPixel(11,
PIN2,
NEO_GRB
+
NEO_KHZ800);
Adafruit_NeoPixel
strip3
=
Adafruit_NeoPixel(11,
PIN3,
NEO_GRB
+
NEO_KHZ800);
//
End
LED
setup
//
Begin
declaring
variables
const
int
hallPin
=
2;
//
the
number
of
the
hall
effect
sensor
pin
float
change=0;
//
used
in
the
interrupter
to
determine
the
actual
location
of
the
wheel
float
angle=0;
//
degpsec
times
the
difference
in
time
between
the
samples
=
angle
of
the
tire
with
respect
to
initial
position
float
angle1=0;
//
Used
to
add
angles
onto
each
other
in
void
loop
float
degpsec=0;
//
Variable
holding
the
raw
data
divided
by
a
scaling
factor
float
t1=0;
//
takes
the
time
right
after
finding
the
degpsec
float
t2=0;
//
t2
is
equal
to
t1
at
the
end
of
the
loop,
thus
it
holds
the
place
of
the
last
time
taken
int
light_delay=100;
//Setslightdelay
delay
int
brightness=64;
//sets
brightness
//
End
declaring
variables
void
setup()
{
//
Gyroscope
//
join
I2C
bus
(I2Cdev
library
doesn't
do
this
automatically)
#if
I2CDEV_IMPLEMENTATION
==
I2CDEV_ARDUINO_WIRE
Wire.begin();
#elif
I2CDEV_IMPLEMENTATION
==
I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400,
true);
#endif
23
//
initialize
serial
communication
//
(38400
chosen
because
it
works
as
well
at
8MHz
as
it
does
at
16MHz,
but
//
it's
really
up
to
you
depending
on
your
project)
Serial.begin(38400);
//
initialize
device
Serial.println("Initializing
I2C
devices...");
accelgyro.initialize();
//
verify
connection
Serial.println("Testing
device
connections...");
Serial.println(accelgyro.testConnection()
?
"MPU6050
connection
successful"
:
"MPU6050
connection
failed");
//
Hall
effect
pinMode(hallPin,
INPUT);
//
initialize
the
hall
effect
sensor
pin
as
an
input:
attachInterrupt(0,
timing,
CHANGE);
//
Sets
an
interrupt
to
trigger
whenever
the
pin
changes
value
//LED
//
Initialize
all
pixels
to
'off'
strip.begin();
strip.show();
strip2.begin();
strip2.show();
strip3.begin();
strip3.show();
}
void
loop(){
//
read
raw
accel/gyro
measurements
from
device
//accelgyro.getMotion6(&ax,
&ay,
&az,
&gx,
&gy,
&gz);
//
these
methods
(and
a
few
others)
are
also
available
//accelgyro.getAcceleration(&ax,
&ay,
&az);
accelgyro.getRotation(&gx,
&gy,
&gz);
#ifdef
OUTPUT_READABLE_ACCELGYRO
//
display
tab-‐separated
accel/gyro
x/y/z
values
//Serial.print("a/g:\t");
//Serial.print(ax);
Serial.print("\t");
//Serial.print(ay);
Serial.print("\t");
//Serial.print(az);
Serial.print("\t");
//Serial.print(gx);
Serial.print("\t");
//Serial.print(gy);
Serial.print("\t");
24
//Serial.println(gz);
#endif
#ifdef
OUTPUT_BINARY_ACCELGYRO
//
Serial.write((uint8_t)(ax
>>
8));
Serial.write((uint8_t)(ax
&
0xFF));
//
Serial.write((uint8_t)(ay
>>
8));
Serial.write((uint8_t)(ay
&
0xFF));
//
Serial.write((uint8_t)(az
>>
8));
Serial.write((uint8_t)(az
&
0xFF));
Serial.write((uint8_t)(gx
>>
8));
Serial.write((uint8_t)(gx
&
0xFF));
Serial.write((uint8_t)(gy
>>
8));
Serial.write((uint8_t)(gy
&
0xFF));
Serial.write((uint8_t)(gz
>>
8));
Serial.write((uint8_t)(gz
&
0xFF));
#endif
degpsec=gz/131;
//converts
raw
data
to
degrees/sec
//Serial.println(degpsec,6);
//prints
degree/sec
t1=millis()/1000.00;
//Time
at
collecting
the
degpsec
value
angle1
=
degpsec
*
(t1-‐t2);
//Integrating
to
find
the
angular
position
angle
=
angle1
+
angle;
//Adding
the
new
angular
position
onto
the
old
angle,
thus
displaying
the
current
location
of
the
wheel
angle
=
abs(angle);
//Ensures
that
the
angle
is
always
a
positive
value
Serial.println(angle);
//Prints
out
angle
//Resets
the
angle
to
0
if
the
angle
is
larger
than
360
if
(angle
>
360){
angle=0;
}
//
LED
light
code
for
Pac-‐Man
Mouth
Open
//
Code
is
setup
so
the
lights
will
always
turn
off
for
the
same
60
degree
position
when
viewing
the
bike.
if
(angle
<=
300){
strip.setBrightness(brightness);
strip.setPixelColor(0,255,238,0);
//
Pac-‐Man
yellow
strip.setPixelColor(1,255,238,0);
strip.setPixelColor(2,255,238,0);
strip.setPixelColor(3,255,238,0);
strip.setPixelColor(4,255,238,0);
strip.setPixelColor(5,255,238,0);
strip.setPixelColor(6,255,238,0);
strip.setPixelColor(7,255,238,0);
strip.setPixelColor(8,255,238,0);
strip.setPixelColor(9,255,238,0);
strip.setPixelColor(10,255,238,0);
strip.show();
}
else{
25
strip.setPixelColor(0,0,0,0);
strip.setPixelColor(1,0,0,0);
strip.setPixelColor(2,0,0,0);
strip.setPixelColor(3,0,0,0);
strip.setPixelColor(4,0,0,0);
strip.setPixelColor(5,0,0,0);
strip.setPixelColor(6,0,0,0);
strip.setPixelColor(7,0,0,0);
strip.setPixelColor(8,0,0,0);
strip.setPixelColor(9,0,0,0);
strip.setPixelColor(10,0,0,0);
strip.show();
}
if
(angle
>=
60){
strip2.setBrightness(brightness);
strip2.setPixelColor(0,255,238,0);
strip2.setPixelColor(1,255,238,0);
strip2.setPixelColor(2,255,238,0);
strip2.setPixelColor(3,255,238,0);
strip2.setPixelColor(4,255,238,0);
strip2.setPixelColor(5,255,238,0);
strip2.setPixelColor(6,255,238,0);
strip2.setPixelColor(7,255,238,0);
strip2.setPixelColor(8,255,238,0);
strip2.setPixelColor(9,255,238,0);
strip2.setPixelColor(10,255,238,0);
strip2.show();
}
else{
strip2.setPixelColor(0,0,0,0);
strip2.setPixelColor(1,0,0,0);
strip2.setPixelColor(2,0,0,0);
strip2.setPixelColor(3,0,0,0);
strip2.setPixelColor(4,0,0,0);
strip2.setPixelColor(5,0,0,0);
strip2.setPixelColor(6,0,0,0);
strip2.setPixelColor(7,0,0,0);
strip2.setPixelColor(8,0,0,0);
strip2.setPixelColor(9,0,0,0);
strip2.setPixelColor(10,0,0,0);
strip2.show();
}
if
(angle
>=
180
||
angle
<=
120){
strip3.setBrightness(brightness);
26
strip3.setPixelColor(0,255,238,0);
strip3.setPixelColor(1,255,238,0);
strip3.setPixelColor(2,255,238,0);
strip3.setPixelColor(3,255,238,0);
strip3.setPixelColor(4,255,238,0);
strip3.setPixelColor(5,255,238,0);
strip3.setPixelColor(6,255,238,0);
strip3.setPixelColor(7,255,238,0);
strip3.setPixelColor(8,255,238,0);
strip3.setPixelColor(9,255,238,0);
strip3.setPixelColor(10,255,238,0);
strip3.show();
}
else{
strip3.setPixelColor(0,0,0,0);
strip3.setPixelColor(1,0,0,0);
strip3.setPixelColor(2,0,0,0);
strip3.setPixelColor(3,0,0,0);
strip3.setPixelColor(4,0,0,0);
strip3.setPixelColor(5,0,0,0);
strip3.setPixelColor(6,0,0,0);
strip3.setPixelColor(7,0,0,0);
strip3.setPixelColor(8,0,0,0);
strip3.setPixelColor(9,0,0,0);
strip3.setPixelColor(10,0,0,0);
strip3.show();
}
t2=t1;
//Sets
the
t2=t1
so
that
we
know
how
long
each
loop
takes
and
thus
can
integrate
to
find
the
angle
}
//
Function
for
fixing
the
gyroscopes
drift
void
timing(){
change
=
digitalRead(hallPin);
//Sets
the
angle
to
0
whenever
the
hall
effect
sensor
changes
to
a
LOW
voltage
reading
if
(change
==
0){
angle=0;
}
//Sets
the
angle
to
180
whenever
the
hall
effect
sensor
changes
to
a
HIGH
voltage
reading
else
{
angle=180;
}
}
27
Appendix
E:
Data
Sheets
28
29