Download - Team Helitronics Anthony Calce Gordon Klein Vince Wu Kyle Watters.

Transcript

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