16
Team Helitronics Anthony Calce Gordon Klein Vince Wu Kyle Watters

Team Helitronics Anthony Calce Gordon Klein Vince Wu Kyle Watters

Embed Size (px)

Citation preview

Team Helitronics

Anthony Calce

Gordon Klein

Vince Wu

Kyle Watters

Overview• Background• Risk Management• Final Design• Algorithms

– Stability– Optical Flow– Servo / Sensor ISR Code

• Progress to Date• Current Budget• Deliverables

Background• Project goal: Autonomous flying helicopter

• Onboard vision processing (Beagleboard)

• Stabilizing flight controller (Arduino)

• Extendable guidance interface

Risk Management• Limited budget, no room for malfunction

– Flight tether system has been developed– Extra caution taken when performing tests

• Limited precision of sensors can make flight algorithms unstable– Camera provides a secondary guidance

source. Sensors are low-pass filtered

• 6-channel RC helicopter– 4 servos are manipulated to control helicopter

• Layered guidance architecture– HelicopterArduinoBeagleboardBase Station

• Hardware– Servos: PWM controller, 45.5hz, interrupt– Sensors: Analog signal, interrupt (300mV/g)

(3.33mV·s/deg)– PWM interrupts override ADC interrupts, ensures

smooth servo motion

Final Design

Servo Manipulation• Take three points (120° apart) on a

circle and calculate their new location based off of rotations

void leftRoll(int amount){

int val1, val2;

val1 = incPulse( servoAil.read(), amount);

val2 = incPulse( servoAux.read(), amount);

servoAil.write(val1);

servoAux.write(val2); }

void forwardElevation(int amount){

int val1, val2, val3;

val1 = incPulse( servoEle.read(), amount);

val2 = decPulse( servoAil.read(), amount/2);

val3 = incPulse( servoAux.read(), amount/2);

servoEle.write(val1);

servoAil.write(val2);

servoAux.write(val3); }

void bladePitchUp(int amount){

int val1, val2, val3;

val1 = decPulse( servoAil.read(), amount);

val2 = incPulse( servoAux.read(), amount);

val3 = decPulse( servoEle.read(), amount);

servoAil.write(val1);

servoAux.write(val2);

servoEle.write(val3); }

Algorithms - Stability

Elevator vs. Throttle

0.000

0.500

1.000

1.500

2.000

2.500

0.000 2.000 4.000

Throttle servo pos width (ms)

Ele

vato

r se

rvo

po

s w

idth

(m

s)

max (ms)

neut (ms)

min (ms)

Aileron vs. Throttle

0.000

0.500

1.000

1.500

2.000

2.500

0.000 2.000 4.000

Throttle servo pos width (ms)

Ele

vato

r se

rvo

po

s w

idth

(m

s)

min (ms)

max (ms)

neut (ms)

AUX vs. Throttle

0.000

0.500

1.000

1.500

2.000

2.500

0.000 2.000 4.000

Throttle servo pos width (ms)

Ele

vato

r se

rvo

po

s w

idth

(m

s)

min (ms)

max (ms)

neut (ms)

• Depending on orientation readings, a combination of previous functions are called to keep helicopter stable

• Motion (forward, sideways) will change an offset value in the “stable” orientation – PID Controller

Optical Flow• Optical flow method used is based on Lucas-

Kanade optical flow• Uses Shi-Tomasi corner detection for finding

features• Three assumptions:

– Motion is Small– Pixel intensity is constant– Local flow is constant

• Using a 30fps camera will allow good estimation of motion

Optical Flow• Three steps

– Obtain two temporally separated frames

– Find features (corners) in the first image

– Use Lucas-Kanadeto calculate flow field

• Shi-Tomasi FeaturescvGoodFeaturesToTrack(frame1_1C, eig_image, temp_image, frame1_features, &number_of_features, 0.01, 0.01, NULL, 7, 7, 0);

• Lucas-Kanade Opt. FlowcvCalcOpticalFlowPyrLK(frame1_1C, frame2_1C, pyramid1,

pyramid2, frame1_features, frame2_features, number_of_features, optical_flow_window, 5, optical_flow_found_feature, optical_flow_feature_error, optical_flow_termination_criteria, 0 );

Servo Control ISR CodeInterrupt Vector Function

ISR (TIMER2_OVF_vect){

++ISRCount; // increment the overlflow counter

if (ISRCount == servos[Channel].counter ) { // are we on the final iteration for this channel

TCNT2 = servos[Channel].remainder; // yes, set count for overflow after remainder ticks

}

else if(ISRCount > servos[Channel].counter){

// we have finished timing the channel so pulse it low and move on

if(servos[Channel].Pin.isActive == true) // check if activated

digitalWrite( servos[Channel].Pin.nbr,LOW); // pulse this channel low if active

Channel++; // increment to the next channel

ISRCount = 0; // reset the isr iteration counter

TCNT2 = 0; // reset the clock counter register

if( (Channel != FRAME_SYNC_INDEX) && (Channel <= NBR_CHANNELS) ){ // check if we need to pulse this channel

if(servos[Channel].Pin.isActive == true) // check if activated

digitalWrite( servos[Channel].Pin.nbr,HIGH); // its an active channel so pulse it high

}

else if(Channel > NBR_CHANNELS){

Channel = 0; // all done so start over

} } }

Write Out to Servo Function

static void writeChan(uint8_t chan, int pulsewidth)

{

if( (chan > 0) && (chan <= NBR_CHANNELS) ) // ensure channel is valid {

if( pulsewidth < MIN_PULSE_WIDTH ) // ensure pulse width is valid

pulsewidth = MIN_PULSE_WIDTH;

else if( pulsewidth > MAX_PULSE_WIDTH )

pulsewidth = MAX_PULSE_WIDTH;

pulsewidth -=DELAY_ADJUST; // subtract the time it takes to process the start and end pulses (mostly from digitalWrite)

servos[chan].counter = pulsewidth / 128;

servos[chan].remainder = 255 - (2 * (pulsewidth - ( servos[chan].counter * 128))); // the number of 0.5us ticks for timer overflow

}}

Sensor ISR CodeInterrupt Vector FunctionISR(ADC_vect){ switch(channel){ case 0b00000001: totalZ += ((ADCL) | ((ADCH)<<8)); set(ADMUX, MUX0); channel<<=1; break;…case 0b00000100: totalY += ((ADCL) | ((ADCH)<<8)); set(ADMUX, MUX0); channel<<=1; break; case 0b00001000: totalGyroY += ((ADCL) | ((ADCH)<<8)); clr(ADMUX, MUX0); clr(ADMUX, MUX1); set(ADMUX, MUX2); channel<<=1; break; case 0b00001000:

totalGyroYArray[3] = totalGyroYArray[2];

totalGyroYArray[2] = totalGyroYArray[1];

totalGyroYArray[1] = totalGyroYArray[0];

totalGyroYArray[0] = ((ADCL) | ((ADCH)<<8));

totalGyroY = totalGyroY + 0.1666*totalGyroYArray[0] + 0.3333*totalGyroYArray[1] + 0.3333*totalGyroYArray[2] + 0.1666*totalGyroYArray[3];

channel<<=1;

break; …

default: channel = 0b00000001; // bad state restart break; }

• Interrupt driven code guarantees us fresh data at 333kHz

• Gyros are integrated using the Runge-Kutta– Achieves the effect of low pass

filtering.

Safety Tether• New design consists of a 35° swivel

ball joint – Allows for safer testing of stability code

Progress to Date

Component Proposed Expenditure Current Expenditure Difference

Electrical

Beagleboard Microcontroller $150 $158.21 $8.21

Arduino Microcontroller - $20.10 $20.10

LAN USB Adapter $50 -  -

USB Hub $5 In-Kind ($5.00)

Voltage Regulators - In-Kind  -

SD Card - In-Kind  -

Multiplexers - $10.88 $10.88

Helicopter

Blade 400 3D Helicopter $470 $489.99 $19.99

Replacement Blade $15 $13.99 ($1.01)

Tail Rotor Shaft $15 $5.49 ($9.51)

Sensors

2.0MP Camera $100 -  -

IMU 6DOF Acc/Gyro $90 $95.43 $5.43

GPS Controller $90 $63.60 ($26.40)

IR Sensors x 2 - In-Kind -

Other

Webhosting $8 $8  -

Presentation Materials $100 -  -

Programming Cable - $22.66 $22.66

Pins - In-Kind  -

PSU - In-Kind  -

Shipping - $43.81 $43.81

Taxes $30 $86.64 $56.64

Total $1,123 $1018.80 $145.80

Currently $145.80 Over Proposed Expenditure Available Funds: $1400 Buffer Fund Left: $131.20

Current Test Setup

Arduino and IMU Sensor BeagleBoard

(See video)

A fully autonomous helicopter capable of advanced image processing and navigation.

Deliverable