160 likes | 263 Views
Anthony Calce Gordon Klein Vince Wu Kyle Watters. Team Helitronics. 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
E N D
Anthony Calce Gordon Klein Vince Wu Kyle Watters Team Helitronics
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
Final Design • 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
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 • 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 Features cvGoodFeaturesToTrack(frame1_1C, eig_image, temp_image, frame1_features, &number_of_features, 0.01, 0.01, NULL, 7, 7, 0); • Lucas-Kanade Opt. Flow cvCalcOpticalFlowPyrLK(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 Code Interrupt 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 Code Interrupt Vector Function ISR(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° swivelball joint Allows for safer testing of stability code
Current Test Setup Arduino and IMU Sensor BeagleBoard (See video)
A fully autonomous helicopter capable of advanced image processing and navigation. Deliverable