product packet v2

20
ELECTRICAL ENGINEERI NG 2011 ~ 2012 THE QUADCOPTER ROGER TANG DICK SWAN

Upload: roger-tang

Post on 27-Mar-2016

228 views

Category:

Documents


0 download

DESCRIPTION

ISM Product Packet

TRANSCRIPT

E L E C T R I C A L E N G I N E E R I N G

2 0 1 1 ~ 2 0 1 2

THE

QUADCOPTER

R O G E R TA N G

D I C K S WA N

PRODUCT COMPLETION PRODUCT COMPLETION PRODUCT COMPLETION PRODUCT COMPLETION

SUMMARYSUMMARYSUMMARYSUMMARY

The overall objective is to build a small robotic quadcopter (i.e. a helicopter with four separate

blades/motors). The quadcopter has an embedded controller programmed using available open source

control software. The project will add functionality by building a custom designed remote control

communicating over standard Wi-Fi with new innovative user interface capabilities. The original plan for

the quadcopter was to have it flying autonomously. This means that the drone would be able navigate

around a room without human interaction. The flight would be stable and a simple command would

have made the quadcopter move to a position precisely.

The final product didn’t end up anywhere near what we planned. There were delays in shipping,

inaccuracies with hardware, and problems with code. As a result, there were constant reassessments on

what we could get done.

During the shipping time, I worked with the ArduIMU (hardware that contains a gyroscope, an

accelerometer, and a magnetometer). We planned to use the three sensors on the board to aid the

control process. For instance, we can theoretically control how far it flies, how many degrees it will turn,

and how fast it will go. Unfortunately, what can be done on paper does not translate perfectly to reality.

We found out that the method that calculates the values that I stated previously gets less and less

accurate as time approaches infinity. If one plotted a percent error versus time graph, the function on

the graph would be exponential. As a result, many of the autonomous functions had to be thrown out

the window.

The compass also posed some very interesting difficulties. The direction (after converting into

data that humans can understand) was never constant. Sometimes, rarely, the compass would be

accurate. Most of the time, the degrees between north and south was 135 (should be 180). Mr. Swan

tried to modify the code to get it to work. It posed to be too time consuming with little output. The

compass is apparently not used among the hobbyist community. Why? The standard quadcopter is

meant for outdoor flying. Therefore, GPS is a better method to figure out where the quadcopter should

go. For us, we planned to fly the robot indoors. The GPS solution therefore is not feasible. If the

quadcopter is off by a hundred feet outdoors, the error is not very noticeable. In an indoor environment,

just a few feet could spell disaster. And there goes another part of our original plan.

So now, we had to totally revisit the drawing board and figure out what we could get done.

Note, this occurred two weeks prior to when the final project was actually due. We used a lot of time to

figure out that some aspects of the project would not work. What we decided that could get finished

was the remote control. While the compass navigation was out of the question, the standard control

worked perfectly. We first used the Boe-Bot to get the initial structure of the program. The first version I

wrote from scratch with no outside help worked a little. It was a brute force way with only maximums

and minimums. When I showed this to Mr. Swan, we conducted a huge makeover. We wanted to have

speeds for the max and min and everything else in between. After all, you don’t want to fly an aircraft

with either max throttle or none at all. It doesn’t work well.

After creating a massive program (for me at least), we found out it didn’t work as intended. We

later found out that the motors range for variable speed is extremely small. So we put in a quick

algorithm to translate the range to a smaller scale. The variable speed worked! With that finished, I put

it on the robot to test it out. To the amusement of my class, the remote control worked great. There

only needed to be a slight modification to allow the quadcopter to be controlled by our own remote.

Meanwhile, testing the quadcopter itself proved to be tedious. No matter how many

modifications we placed in the quadcopter, it would not achieve a stable flight. The broken propellers

are a testament to that. We only connected the quadcopter to our remote without the propellers on. If

flying with a professional control proved to be difficult, our own remote control would only fare worse.

It was only after the due date for the project did we get advice on how to stabilize level flight. We have

tested it though.

The end product then was a pile of incomplete work (in my opinion). I feel we did not get to

discover all the potential that the quadcopter held. We had the parts to make a whole but we lacked the

nuts and bolts. I learned that engineering is not one for accurate deadlines. Problems arise and it takes

time to smooth things out. What looks good on paper does not necessarily mean that it will work

flawlessly. In this line of work, debugging is a commonplace. There is faulty programming, unknown

variables, and other mishaps that prevent a flawless product, especially when starting from scratch.

However, for all the bumps along the way, I was a good experience. I enjoyed making breakthroughs and

learning how to code. I hope to take these experiences to college where I plan to engage in research.

REPRESENTATIONREPRESENTATIONREPRESENTATIONREPRESENTATION

Here is the best I can do with representing my product. This is the property of my mentor so this is

probably the best shot of seeing it.

DRAFTS, PICTURES, AND DRAFTS, PICTURES, AND DRAFTS, PICTURES, AND DRAFTS, PICTURES, AND

RELATED ITEMSRELATED ITEMSRELATED ITEMSRELATED ITEMS

LineFollowing Code #include <Servo.h>

#include <PololuQTRSensors.h>

PololuQTRSensorsAnalog qtra((unsigned char[]) {1, 2, 3, 4}, 4);

int speed = 100, rotate = 8;

int threshhold = 100;

int left = speed, right = speed;

Servo leftServo;

Servo rightServo;

void setup()

{

leftServo.attach(11);

rightServo.attach(10);

Serial.begin(9600);

}

void loop()

{

unsigned int sensors[4];

qtra.read(sensors);

left = speed;

right = speed;

if (sensors[1] > 100 && sensors[2] < 100 && sensors[3] < 100)

{

left = speed + rotate + 5;

right = speed - rotate - 5;

}

else if (sensors[2] < 100 && sensors[3] > 100 && sensors[4] > 100)

{

left = speed - rotate - 5;

right = speed + rotate + 5;

}

else if (sensors[1] > 100 && sensors[3] < 100)

{

left = speed + rotate;

right = speed - rotate;

}

else if (sensors[2] < 100 && sensors[4] > 100)

{

left = speed - rotate;

right = speed + rotate;

}

else

{

left = speed;

right = speed;

}

leftServo.write(left);

rightServo.write(180 - right);

delay(5);

}

IRSensorReadings #include <PololuQTRSensors.h>

#define NUM_SENSORS 5

// number of sensors used

#define NUM_SAMPLES_PER_SENSOR 4

// average 4 analog samples per sensor reading

//sensors 0 through 5 are connected to analog inputs 0 through 5

PololuQTRSensorsAnalog qtra((unsigned char[]) {0, 1, 2, 3, 4},

NUM_SENSORS, NUM_SAMPLES_PER_SENSOR, QTR_NO_EMITTER_PIN);

unsigned int sensorValues[NUM_SENSORS];

void setup(){

Serial.begin(9600);

}

void loop()

{

qtra.read(sensorValues);

unsigned char i;

for (i = 0; i < NUM_SENSORS; i++)

{

Serial.print(sensorValues[i]);

Serial.print(' ');

}

Serial.println(" ");

delay(250);

}

SOSBlinking void setup() {

pinMode(13, OUTPUT);

}

void loop() {

for(int i = 0; i < 3; i++) {

digitalWrite(13,HIGH);

delay(500);

digitalWrite(13,LOW);

delay(500);

}

for(int i = 0; i < 3; i++) {

digitalWrite(13, HIGH);

delay(1500);

digitalWrite(13, LOW);

delay(500);

}

for(int i = 0; i < 3; i++) {

digitalWrite(13,HIGH);

delay(500);

digitalWrite(13,LOW);

delay(500);

}

}

ServoSpeedControl #include <Servo.h>

int speed = 0;

//speed = 0, to make right servo go full speed forward

//speed = 180, to make left servo go full speed forward

//speed = 90, to make both servos stop (need to trim to get perfect stop)

Servo myservo;

void setup()

{

myservo.attach(10);

}

void loop()

{

speed++; // increment speed by one each loop

if (speed > 180) { //set limit to be 180

speed = 0; // return to 0 if limit reached

}

myservo.write(speed);

delay(25);

}

TwinkleTwinkleSongPlayback #include "pitches.h"

// notes in the melody:

int melody[] = {

NOTE_C5,NOTE_C5, NOTE_G5,NOTE_G5, NOTE_A5,NOTE_A5, NOTE_G5,

NOTE_F5,NOTE_F5, NOTE_E5,NOTE_E5, NOTE_D5,NOTE_D5, NOTE_C5,

NOTE_G5,NOTE_G5, NOTE_F5,NOTE_F5, NOTE_E5,NOTE_E5, NOTE_D5,

NOTE_G5,NOTE_G5, NOTE_F5,NOTE_F5, NOTE_E5,NOTE_E5, NOTE_D5,

NOTE_C5,NOTE_C5, NOTE_G5,NOTE_G5, NOTE_A5,NOTE_A5, NOTE_G5,

NOTE_F5,NOTE_F5, NOTE_E5,NOTE_E5, NOTE_D5,NOTE_D5, NOTE_C5,};

// note durations: 4 = quarter note, 8 = eighth note, etc.:

int noteDurations[] = {

4,4,4,4,4,4,2,4,4,4,4,4,4,2,

4,4,4,4,4,4,2,4,4,4,4,4,4,2,

4,4,4,4,4,4,2,4,4,4,4,4,4,2};

void setup() {

// iterate over the notes of the melody:

for (int thisNote = 0; thisNote < 42; thisNote++) {

// to calculate the note duration, take one second

// divided by the note type.

//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.

int noteDuration = 1000/noteDurations[thisNote];

tone(8, melody[thisNote],noteDuration);

// to distinguish the notes, set a minimum time between them.

// the note's duration + 30% seems to work well:

int pauseBetweenNotes = noteDuration * 1.30;

delay(pauseBetweenNotes);

// stop the tone playing:

noTone(8);

}

}

void loop() {

// no need to repeat the melody.

}

Remote Control #include <Servo.h>

char inByte = 0;

int outputPin = 13;

int nMessageCount;

int nJoystickHorizontal = 512;

int nJoystickVertical = 512;

int nHorizontal = nJoystickHorizontal;

int nVertical = nJoystickVertical;

int leftSpeed;

int rightSpeed;

int leftSpeedRaw;

int rightSpeedRaw;

Servo leftServo;

Servo rightServo;

void setup(){

pinMode(outputPin, OUTPUT);

Serial1.begin(9600);

Serial.begin(115200);

leftServo.attach(11);

rightServo.attach(10);

}

void dumpResults()

{

Serial.print(nMessageCount, DEC);

Serial.print(" Raw H/V ");

Serial.print(nJoystickHorizontal, DEC);

Serial.print(" ");

Serial.print(nJoystickVertical, DEC);

Serial.print(" speed L/R ");

Serial.print(leftSpeed, DEC);

Serial.print(" ");

Serial.print(rightSpeed, DEC);

Serial.print(" speed L/R Raw ");

Serial.print(leftSpeedRaw, DEC);

Serial.print(" ");

Serial.print(rightSpeedRaw, DEC);

Serial.println();

}

void blinkLEDForEveryMessage()

{

typedef enum

{

nStateReadyToBlink,

nStateLEDOn,

nStateLEDOff,

} TBlinkStates;

static TBlinkStates nState;

static unsigned long nBlinkTimer;

static int nLastMessageBlinked;

if (nLastMessageBlinked == nMessageCount)

return;

switch (nState)

{

case nStateReadyToBlink:

if (nLastMessageBlinked == nMessageCount)

return;

nState = nStateLEDOn;

digitalWrite(outputPin, HIGH);

nBlinkTimer = millis() + 100;

break;

case nStateLEDOn:

if (millis() < nBlinkTimer)

return;

digitalWrite(outputPin, LOW);

nBlinkTimer = millis() + 400;

nState = nStateLEDOff;

break;

case nStateLEDOff:

if (millis() < nBlinkTimer)

return;

nLastMessageBlinked = nMessageCount;

nState = nStateReadyToBlink;

break;

}

}

void stateMachineLoop()

{

typedef enum

{

stateLookForMessageStart,

stateLookForD,

stateLookForR,

stateLookForCycleCount,

stateLookForJoystickHorizontal,

stateLookForJoystickVertical,

} TStates;

static TStates nState;

static int nTemp;

static bool bNegativeValue;

blinkLEDForEveryMessage();

if (Serial1.available() <= 0)

return;

inByte = Serial1.read();

/*Debug

Serial.print("'");

Serial.write(inByte);

Serial.print("' ");0

Serial.print((int) inByte, DEC);

Serial.print(" ");

Serial.print(nState, DEC);

Serial.println();

*/

switch (nState)

{

default:

case stateLookForMessageStart:

if (inByte == 'H')

nState = stateLookForD;

else

nState = stateLookForMessageStart;

break;

case stateLookForD:

if (inByte == 'D')

nState = stateLookForR;

else

nState = stateLookForMessageStart;

break;

case stateLookForR:

if (inByte == 'R')

{

nTemp = 0;

bNegativeValue = false;

nState = stateLookForCycleCount;

}

else

nState = stateLookForMessageStart;

break;

case stateLookForCycleCount:

if (inByte == '-')

{

bNegativeValue = true;

break;

}

if ((inByte >= '0') && (inByte <= '9'))

{

nTemp *= 10;

nTemp += (inByte - '0');

break;

}

if ((inByte == ',') || (inByte == '\r') || (inByte == '\n'))

{

if (bNegativeValue)

nMessageCount = - nTemp;

else

nMessageCount = nTemp;

nTemp = 0;

bNegativeValue = false;

nState = stateLookForJoystickHorizontal;

}

else

nState = stateLookForMessageStart;

break;

case stateLookForJoystickHorizontal:

if (inByte == '-')

{

bNegativeValue = true;

break;

}

if ((inByte >= '0') && (inByte <= '9'))

{

nTemp *= 10;

nTemp += (inByte - '0');

break;

}

if ((inByte == ',') || (inByte == '\r') || (inByte == '\n'))

{

if (bNegativeValue)

nJoystickHorizontal = - nTemp;

else

nJoystickHorizontal = nTemp;

nTemp = 0;

bNegativeValue = false;

nState = stateLookForJoystickVertical;

}

else

nState = stateLookForMessageStart;

break;

case stateLookForJoystickVertical:

if (inByte == '-')

{

bNegativeValue = true;

break;

}

if ((inByte >= '0') && (inByte <= '9'))

{

nTemp *= 10;

nTemp += (inByte - '0');

break;

}

if ((inByte == ',') || (inByte == '\r') || (inByte == '\n'))

{

if (bNegativeValue)

nJoystickVertical = - nTemp;

else

nJoystickVertical = nTemp;

nTemp = 0;

bNegativeValue = false;

nState = stateLookForMessageStart;

dumpResults();

}

else

nState = stateLookForMessageStart;

break;

}

}

#define kMaxValue 10

#define kMaxValueMinusOne (kMaxValue - 1)

int setMotorSpeed(int speed, bool bReversed)

{

//speed range is from -512 to 512

speed *= (float) kMaxValue/ (float) 512; //change range from -512..512 to -kMaxValue..kMaxValue

if ((speed >= -1) && (speed <= 1)) //deadzone is -1..1

speed = 0;

if (speed >= kMaxValueMinusOne)

speed = 45;

else if (speed <= -kMaxValueMinusOne)

speed = -45;

speed += 90; //servo uses 90 for stop

if(bReversed)

speed = 180 - speed; //left motor is reversed

return speed;

}

void motorControlLoop()

{

//normalize values

nHorizontal = nJoystickHorizontal;

nVertical = nJoystickVertical;

if (nHorizontal > 0)

{

rightSpeed = nVertical;

leftSpeed = rightSpeed * (1 - (nHorizontal / (float)256));

}

else

{

nHorizontal = -nHorizontal;

leftSpeed = nVertical;

rightSpeed = leftSpeed * (1 - (nHorizontal / (float)256));

}

leftSpeedRaw = setMotorSpeed(leftSpeed, false);

leftServo.write(leftSpeedRaw);

rightSpeedRaw = setMotorSpeed(rightSpeed, true);

rightServo.write(rightSpeedRaw);

return;

}

void compassHandlerLoop()

{

return;

}

void loop()

{

stateMachineLoop();

compassHandlerLoop();

motorControlLoop();

}

Remote Control Cont. int outputPin = 2;

char Str1[] = "HDR";

int counter;

int joystickVertical = 58;

int joystickHorizontal = 24589;

unsigned long nextMessageTime;

#define kTimeBetweenMessages 50

#define sensorVerticalPin A0

#define sensorHorizontalPin A1

void setup(){

pinMode(outputPin, OUTPUT);

Serial.begin(9600);

}

void loop(){

unsigned long currentTime;

currentTime = millis();

if(currentTime < nextMessageTime)

return;

nextMessageTime = currentTime + kTimeBetweenMessages;

++counter;

joystickVertical = analogRead(sensorVerticalPin) - 512;

joystickHorizontal = analogRead(sensorHorizontalPin) - 512;

Serial.write(Str1);

Serial.print(counter, DEC);

Serial.write(',');

Serial.print(joystickVertical, DEC);

Serial.write(',');

Serial.print(joystickHorizontal, DEC);

Serial.write('\r');

Serial.write('\n');

Serial.write('\r');

}