Botched
8Oct/1313

Picopter Dissertation

I figured I should finally share the dissertation I produced for the Picopter project. It outlines the design of most of the platform, going into greater detail in regards to the design and implementation of the quaternion extended Kalman filter I implemented. I'd be happy to answer any questions in regards to this filter.

Link to dissertation temporarily removed

Comments (13) Trackbacks (0)
  1. Hi!
    I did not understand what is that black antenna and how the RPI is powered!
    you can explain it to me please?

  2. Hello man,
    Im an computer engennering student and i really like this kind of things…
    Thanks for sharing this work i can’t really do such a great thing because of my low code knowledge(as my english skills) but one day maybe when i graduate i will be able to do a project like this one.
    Just one question what is the language your code is written on?
    Congratulations for your awesome work 😉

  3. Thank you for the thorough description of the project . If you haven’t already, it would be a nice idea to show it off in some of the quadcopter forums and/or news sites . It deserves the recognition, cheers !

  4. What I didn’t see in the dissertation or the blog are schematics for the electronics . In other words – what does your (I’d guess) custom board with the dspic30f microcontroller do ? I’ve started a thread here http://aeroquad.com/showthread.php?8765-Designing-a-RPi-powered-low-price-computer-controlled-quad-tri-copter-drone and copied it here too http://www.raspberrypi.org/phpBB3/viewtopic.php?f=37&t=63092&e=0 . I think the idea is pretty identical to what you did, but your custom board is going to be replaced with an arduino+MPU5060 or a multicopter control board (MultiWii NanoWii) . I’d appreciate your opinion on the project .

    • The Microchip MCU simply reads the receiver PWM channels and outputs four motor PWM channels, all managed by the RPi. I just stumbled upon a pdf of the board, uploaded here. I think your project sounds perfectly doable, the MCU has such a simple job I imagine you could replace it with pretty much any controller and be fine.

  5. hELLO matt,
    I AM AN ELECTRICAL ENGINEERING STUDENT

    I AM MAKING A QUADCOPTER FOR MY PROJECT.
    THE HARDWARE IS COMPLETE.
    NOW IAM WORKING WITH THE SOFTWARE.

    IAM INCORPORATING YOUR PREVIOUS QUADCOPTER CODE

    ACTUALLY MY JOB IS TO HOVER IT WITHOUT A REMOTE AND RECIEVER THAT IS TO MAKE IT AUTONOMOUS

    IAM USING PIC32MX795F512L PIC MCU.
    I HAVE A 30A HOBBYKING ESC(UBEC) (BLUE IN COLOR)
    AND THE MOTORS ARE NTM SERIES ALSO FROM HOBBYKING (385W AND 1100KV)

    SO CAN YOU PLEASE HELP ME WITH THE “MOTOR MAX” AND “MOTOR MIN” AND SUCH CONSTANTS SINCE IAM NOT SUPPOSE TO USE A REMOTE AND A RECIEVER

    WILL I BE ABLE TO PROGRAM THE MCU WITHOUT A REMOTE AND RECIEVER??

    NOTE:
    IAM ALSO USING GY-521 BREAKOUT BOARD OF MPU6050 INTERFACED WITH PIC MCU THROUGH I2C
    AND A COUPLE OF SONARS TOO

    GOOD JOB ON YOUR NEW PICOPTER !!

  6. WHAT WILL BE THE CHANGES IN THE FUNCTION TO JUST GIVE DIRECT PWM USING TIMER3 TO THE MOTORS MENTIONED ABOVE THROUGH THE 30A ESC MENTIONED ABOVE WITHOUT USING INPUT CAPTURE(THAT IS WITHOUT USING THE REMOTE AND RECIEVER)
    I WANT TO HOVER IT AT 25% OF THE TOTAL THROTTLE THAT THIS MOTOR CAN PRODUCE!
    THE FUNCTIONS ARE AS FOLLOWS :

    //timer3.C

    #include
    #include
    #include “commontoall.h”

    //Output pulse timer
    void Setup_Timer3()
    {
    IEC0bits.T3IE = 0; //Disable timer3 interrupt
    T3CONbits.ON = 0; //Disable timer
    T3CONbits.SIDL = 0; //Continue operation in idle mode
    T3CONbits.TGATE = 0; //Timer gate accumulation disabled
    T3CONbits.TCKPS = 0b000; //Timer prescale 1:1, 1:8, 1:64, 1:256
    //T3CONbits.T32 = 0; //32 bit timer disabled
    T3CONbits.TCS = 0; //Internal clock source

    PR3 = 4499; //Period register

    TMR3=0;
    //printf(“\nTimer3 Setup Complete”);
    }

    void enable_timer3()
    {
    TMR3=0;
    T3CONbits.ON = 1;
    }

    void disable_timer3()
    {
    TMR3=0;
    T3CONbits.ON = 0;
    }

    //PWM.C

    #include
    #include

    void Setup_OC1_PWM()
    {
    T3CONbits.ON = 0; //Disable timer

    IEC0bits.OC1IE = 0; //Disable OC1 interrupt
    IEC0bits.OC2IE = 0; //Disable OC2 interrupt
    IEC0bits.OC3IE = 0; //Disable OC3 interrupt
    IEC0bits.OC4IE = 0; //Disable OC4 interrupt

    TRISDbits.TRISD0 = 0;
    TRISDbits.TRISD1 = 0;
    TRISDbits.TRISD2 = 0;
    TRISDbits.TRISD3 = 0;

    OC1CONbits.SIDL = 0; //OC continues in idle mode
    OC1CONbits.OCFLT = 0; //Fault status bit, N/A as fault pin is disabled
    OC1CONbits.OCTSEL = 1; //Timer3 source
    OC1CONbits.OCM = 0b110; //PWM mode, no fault pin

    OC2CONbits.SIDL = 0; //OC continues in idle mode
    OC2CONbits.OCFLT = 0; //Fault status bit, N/A as fault pin is disabled
    OC2CONbits.OCTSEL = 1; //Timer3 source
    OC2CONbits.OCM = 0b110; //PWM mode, no fault pin

    OC3CONbits.SIDL = 0; //OC continues in idle mode
    OC3CONbits.OCFLT = 0; //Fault status bit, N/A as fault pin is disabled
    OC3CONbits.OCTSEL = 1; //Timer3 source
    OC3CONbits.OCM = 0b110; //PWM mode, no fault pin

    OC4CONbits.SIDL = 0; //OC continues in idle mode
    OC4CONbits.OCFLT = 0; //Fault status bit, N/A as fault pin is disabled
    OC4CONbits.OCTSEL = 1; //Timer3 source
    OC4CONbits.OCM = 0b110; //PWM mode, no fault pin

    OC1RS = 700;
    OC2RS = 700;
    OC3RS = 700;
    OC4RS = 700;

    TMR3=0;
    T3CONbits.ON = 1; //Enable timer

    // printf(“\nPWM Setup Complete”);

    }

    void output_compare_fire()
    {
    OC1CONbits.OCM = 0b110;
    OC2CONbits.OCM = 0b110;
    OC3CONbits.OCM = 0b110;
    OC4CONbits.OCM = 0b110;
    enable_timer3();
    }

    // MOTORS.C

    #include
    #include
    #include “commontoall.h”
    //#define FCY 40000000UL //Required for built in delay function
    #include

    #define MOTOR_MIN 5350.0
    #define MOTOR_MAX 9500.0
    #define MOTOR_RANGE 4150.0

    float PREVIOUS_XERROR = 0;
    float PREVIOUS_YERROR = 0;
    float PREVIOUS_ZERROR = 0;
    float XDIFFERENTIAL = 0;
    float YDIFFERENTIAL = 0;
    float ZDIFFERENTIAL = 0;

    /*
    positive x axis is forward
    Motor 1 is left of x
    Motor 4 is right of x
    Motor 2 is next to 1
    Motor 3 is next to 4
    */

    /*void update_motors_PWM()
    {
    if(throttle==0.0)
    {
    OC1RS = 700;
    OC2RS = 700;
    OC3RS = 700;
    OC4RS = 700;
    }
    else
    {
    OC1RS = 0.5*PID_XOUTPUT + -0.5*PID_YOUTPUT + MOTOR_MIN + MOTOR_RANGE*throttle;
    OC4RS = -0.5*PID_XOUTPUT + -0.5*PID_YOUTPUT + MOTOR_MIN + MOTOR_RANGE*throttle;
    OC2RS = 0.5*PID_XOUTPUT + 0.5*PID_YOUTPUT + MOTOR_MIN + MOTOR_RANGE*throttle;
    OC3RS = -0.5*PID_XOUTPUT + 0.5*PID_YOUTPUT + MOTOR_MIN + MOTOR_RANGE*throttle;

    if(OC1RS > 1.5*throttle*MOTOR_RANGE + MOTOR_MIN)
    {OC1RS = 1.5*throttle*MOTOR_RANGE + MOTOR_MIN;}
    if(OC2RS > 1.5*throttle*MOTOR_RANGE + MOTOR_MIN)
    {OC2RS = 1.5*throttle*MOTOR_RANGE + MOTOR_MIN;}
    if(OC3RS > 1.5*throttle*MOTOR_RANGE + MOTOR_MIN)
    {OC3RS = 1.5*throttle*MOTOR_RANGE + MOTOR_MIN;}
    if(OC4RS > 1.5*throttle*MOTOR_RANGE + MOTOR_MIN)
    {OC4RS = 1.5*throttle*MOTOR_RANGE + MOTOR_MIN;}

    if(OC1RS > MOTOR_MAX) {OC1RS = MOTOR_MAX;}
    if(OC2RS > MOTOR_MAX) {OC2RS = MOTOR_MAX;}
    if(OC3RS > MOTOR_MAX) {OC3RS = MOTOR_MAX;}
    if(OC4RS > MOTOR_MAX) {OC4RS = MOTOR_MAX;}

    if(OC1RS < MOTOR_MIN) {OC1RS = MOTOR_MIN;}
    if(OC2RS < MOTOR_MIN) {OC2RS = MOTOR_MIN;}
    if(OC3RS < MOTOR_MIN) {OC3RS = MOTOR_MIN;}
    if(OC4RS < MOTOR_MIN) {OC4RS = MOTOR_MIN;}
    }
    }*/

    void Calibrate_ESC_Endpoints()
    {
    int x = 0;
    for (x=0; x<400; x++)
    {
    OC1R = MOTOR_MAX;
    OC2R = MOTOR_MAX;
    OC3R = MOTOR_MAX;
    OC4R = MOTOR_MAX;
    output_compare_fire();
    Delayms(3);
    LATGbits.LATG6 = !LATGbits.LATG6;

    }
    for (x=0; x MOTOR_MAX) {OC1_output = MOTOR_MAX;}
    if(OC2_output > MOTOR_MAX) {OC2_output = MOTOR_MAX;}
    if(OC3_output > MOTOR_MAX) {OC3_output = MOTOR_MAX;}
    if(OC4_output > MOTOR_MAX) {OC4_output = MOTOR_MAX;}

    if(OC1_output < MOTOR_MIN) {OC1_output = MOTOR_MIN;}
    if(OC2_output < MOTOR_MIN) {OC2_output = MOTOR_MIN;}
    if(OC3_output < MOTOR_MIN) {OC3_output = MOTOR_MIN;}
    if(OC4_output 0.5) {XINTEGRAL = 0.5;}
    else if(XINTEGRAL 0.5) {YINTEGRAL = 0.5;}
    else if(YINTEGRAL 1000){PID_ZOUTPUT = 1000;}
    else if (PID_ZOUTPUT < -1000){PID_ZOUTPUT = -1000;}
    }

    //TIMER1.C

    #include
    #include “commontoall.h”
    #include “LCD.h”
    // Muneeb’s setting of timer1 according to PIC32
    //400Hz control loop timer
    void Setup_Timer1()
    {
    IEC0bits.T1IE = 0; //Disable timer1 interrupt
    T1CONbits.ON = 0; //Disable timer
    T1CONbits.SIDL = 0; //Continue operation in idle mode
    T1CONbits.TGATE = 0; //Timer gate accumulation disabled
    T1CONbits.TCKPS = 0b00; //Timer prescale 1:1, 1:8, 1:64, 1:256
    //T1CONbits.T32 = 0; //32 bit timer disabled
    T1CONbits.TCS = 0; //Internal peripheral clock source if = 0 (FPB=36Mhz)
    IPC1bits.T1IP = 0b100; //Priority 4

    //Frequency of 400Hz
    PR1 = 45000; //Period register

    TMR1=0;
    T1CONbits.ON = 1; //Enable timer

    //printf(“\nTimer1 Setup Complete”);
    }
    //void __ISR( 0, ipl4) T1Interrupt( void)
    //void __ISR(_Timer_1_Vector, ipl4)Timer1Handler (void)

    //void __ISR(_TIMER_1_VECTOR, ipl4) T1_IntHandler (void)
    void __attribute__ (( interrupt(ipl4),vector(4))) InterruptHandler( void)
    {

    IFS0bits.T1IF = 0; //Clear interrupt flag
    IEC0bits.T1IE = 0; //Disable timer1 interrupt
    LATGbits.LATG6 = !LATGbits.LATG6;

    Get_Gyro_Rates();
    Get_Accel_Values();
    Get_Accel_Angles();

    complementary_filter();
    second_order_complementary_filter();

    update_PID();
    update_motors_single_shot();
    IEC0bits.T1IE = 1; //Enable timer1 interrupt

    }

    PLEASE I NEED YOUR GUIDANCE!

  7. TIMER2 IS USED FOR THE DELAY

    DELAYMS(1) MEANS DELAY OF 1ms

  8. // myinput.c

    #include
    #include
    #include “MPU6050gyro.h”
    #include “commontoall.h”
    //#define FCY 40000000UL
    #include
    #include
    //#include
    #include

    //These values are specific to my hobbyking 6ch reciever
    #define THROTTLE_MAX 9306
    #define THROTTLE_MIN 5528
    #define THROTTLE_THRESHOLD 5800
    #define YAW_MAX 9616
    #define YAW_MID 7543
    #define YAW_MIN 5544
    #define PITCH_MAX 9320
    #define PITCH_MID 7524
    #define PITCH_MIN 5737
    #define ROLL_MAX 9639
    #define ROLL_MID 7697
    #define ROLL_MIN 5740

    //The desired quad angle range, 30 = 15 degrees either way
    #define XANGLE_RANGE 40.0
    #define YANGLE_RANGE 40.0
    #define ZRATE_RANGE 100.0

    void myinput()
    {

    yaw_input =5544 ;
    TARGET_ZRATE = ZRATE_RANGE*(yaw_input – YAW_MID)/(YAW_MAX – YAW_MIN);
    Delayms(1000);

    throttle_input = 5528;
    if(throttle_input <= THROTTLE_THRESHOLD)
    {
    throttle = 0.0;
    }
    else
    {
    throttle = (float)(throttle_input – THROTTLE_MIN)/(THROTTLE_MAX-THROTTLE_MIN);
    }

    Delayms(1000);

    roll_input =5740 ;
    TARGET_XANGLE = XANGLE_RANGE*(roll_input – ROLL_MID)/(ROLL_MAX – ROLL_MIN);

    Delayms(1000);

    pitch_input =5737 ;
    TARGET_YANGLE = YANGLE_RANGE*(float)((float)(pitch_input – PITCH_MID)/(PITCH_MAX – PITCH_MIN));

    }

  9. THE VALUES U ARE USING FOR YOUR 6CHANNEL RECIEVER WHAT IS THAT I WILL USE FOR MINE IN THE ABOVE CODE??(SINCE I AM NOT USING ANY RECIEVER)


Leave a comment

No trackbacks yet.