product packet

21
ELECTRICAL ENGINEERI NG 2011 ~ 2012 THE QUADCOPT ER ROGER TANG

Upload: roger-tang

Post on 18-Mar-2016

227 views

Category:

Documents


0 download

DESCRIPTION

ISM Final Product Packet

TRANSCRIPT

Page 1: Product Packet

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 QUADCOPT

ERR O G E R TA N G

Page 2: Product Packet

Product Completion Summary

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

Page 3: Product Packet

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.

Page 4: Product Packet

Representation

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.

Page 5: Product Packet

Drafts, Pictures, and Related Items

Page 6: Product Packet
Page 7: Product Packet
Page 8: Product Packet

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)

Page 9: Product Packet

{ 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); }

Page 10: Product Packet

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); }

Page 11: Product Packet

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);

Page 12: Product Packet

// 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(" ");

Page 13: Product Packet

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;

Page 14: Product Packet

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(); */

Page 15: Product Packet

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;

Page 16: Product Packet

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'))

Page 17: Product Packet

{ 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)

Page 18: Product Packet

{ 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);

Page 19: Product Packet

} 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'); }

Page 20: Product Packet
Page 21: Product Packet