Control code for Triforce robot.

Dependencies:   triforce-esc PwmInRC mbed

main.cpp

Committer:
IonSystems
Date:
2017-05-05
Revision:
2:4e086a77e769
Parent:
1:026f79cfd378

File content as of revision 2:4e086a77e769:

/* Includes */
#include "bno055.h"
#include "esc.h"
#include "PwmIn.h"
#include "mbed.h"
#include "Triforce.h"
#include "math.h"

/* USB */
Serial pc(USBTX, USBRX);

/* 8 Channel RC input */
PwmIn rc_channel_1(p5); // Roll
PwmIn rc_channel_2(p6); // Pitch
PwmIn rc_channel_3(p7); // Throttle
PwmIn rc_channel_4(p8); // Yaw
PwmIn rc_channel_5(p11); // Switch R front
PwmIn rc_channel_6(p12); // Dial R 
PwmIn rc_channel_7(p13); // Dial L
PwmIn rc_channel_8(p14); // Switch L Back

/* 5 channel ESC output */
ESC esc_omni_1(p21, 20, 1500);
ESC esc_omni_2(p22, 20, 1500);
ESC esc_omni_3(p23, 20, 1500); //LED4
ESC esc_weapon_1(p24); //LED3
ESC esc_weapon_2(p25); //LED2

/* LEDs */
DigitalOut led1(LED1);

/* Initial control positions */
static struct rc_controls controls = {
    .channel_1 = 50,
    .channel_2 = 50,
    .channel_3 = 50,
    .channel_4 = 0,
    .channel_5 = 0,
    .channel_6 = 0,
    .channel_7 = 0,
    .channel_8 = 0
};

/* Initial output positions */
static struct rc_outputs outputs = {
    .wheel_1 = 50,
    .wheel_2 = 50,
    .wheel_3 = 50,
    .weapon_motor_1 = 0,
    .weapon_motor_2 = 0
};

/* Direction vector */
static struct direction_vector direction = {
    .rotation = 50,
    .x_translation = 50,
    .y_translation = 50
};


/******************************************************************************/


/** Init
 * 
 * Sets up devices
 * Will loop init for bno055 untill it starts correctly.
 */
void init(){
    #ifdef ORIENTATION
    init_bno055:
    /* Initialise the sensor */
    if(!initBNO055()){
        
        /* There was a problem detecting the BNO055 ... check your connections */
        #if defined (PC_DEBUGGING) && defined (DEBUG_ORIENTATION)  
        pc.printf("BNO055 not detected\r\n");
        #endif        
        goto init_bno055;
        
    } else {   
        #if defined (PC_DEBUGGING) && defined (DEBUG_ORIENTATION)   
        pc.printf("BNO055 was detected!\r\n");
        #endif
    }
    #endif
}

void reset_outputs(){
    outputs.wheel_1 = 50;
    outputs.wheel_2 = 50;
    outputs.wheel_3 = 50;    
}


/** Read control positions from RC receiver
 * 
 * Converts control input pulsewidths read in seconds to throttle % 
 */
void read_inputs(){    
    
    
    /* Check for armed status */
    
    
    if(!armed || just_armed){
        #if defined (PC_DEBUGGING) && defined (DEBUG_INPUT) 
        if(arming_count == 0){
            
            pc.printf("MOVE STICKS TO ARMING POSITIONS AND HOLD TO ARM \r\n");
        }    
        #endif
        
        failsafe = true;
        int c1 = convert_pulsewidth(rc_channel_1.pulsewidth());
        int c2 = convert_pulsewidth(rc_channel_2.pulsewidth());
        int c3 = convert_pulsewidth(rc_channel_3.pulsewidth());
        int c4 = convert_pulsewidth(rc_channel_4.pulsewidth());
        pc.printf("Input: C1: %i \t C2: %i \t C3: %i \t C4: %i \r\n", c1, c2, c3, c4);
        
        if(c1 > RC_ARM_CHANNEL_1 && c2 > RC_ARM_CHANNEL_2 && c3 < RC_ARM_CHANNEL_3 && c4 < RC_ARM_CHANNEL_4){       
            
            if(!armed){
                #if defined (PC_DEBUGGING) && defined (DEBUG_INPUT) 
                pc.printf("ARMING %i\r\n", arming_count);    
                #endif
                arming_count++;
                wait_ms(1000);
                if(arming_count > RC_ARM_DELAY){
                    armed = true;
                    just_armed = true;
                }
            }else{
                #if defined (PC_DEBUGGING) && defined (DEBUG_INPUT) 
                pc.printf("ARMED - CENTER CONTROLS \r\n");    
                #endif
            }
        } else if(just_armed){
            /* Wait for controls to be centered to avoid unexpected movement */
            if(BETWEEN(c1, 45, 55) && BETWEEN(c2, 45, 55) && c3 < RC_ARM_CHANNEL_3 && BETWEEN(c4, 45, 55)){     
                #if defined (PC_DEBUGGING) && defined (DEBUG_INPUT) 
                pc.printf("CONTROLS CENTERED DISABLING ARM LOCK\r\n");    
                #endif
                just_armed = false;
                failsafe_timer = 0;
            }
              
        }else {
            arming_count = 0;
        }
        
        
    }else{
        
            
            
            
            
        #if defined (PC_DEBUGGING) && defined (DEBUG_INPUT) 
        pc.printf("******************ARMED****************** \r\n");    
        #endif

        controls.channel_1 = convert_pulsewidth(rc_channel_1.pulsewidth());
        controls.channel_2 = convert_pulsewidth(rc_channel_2.pulsewidth());
        controls.channel_3 = convert_pulsewidth(rc_channel_3.pulsewidth());
        controls.channel_4 = convert_pulsewidth(rc_channel_4.pulsewidth());
        controls.channel_5 = convert_pulsewidth(rc_channel_5.pulsewidth());
        controls.channel_6 = convert_pulsewidth(rc_channel_6.pulsewidth());
        controls.channel_7 = convert_pulsewidth(rc_channel_7.pulsewidth());
        controls.channel_8 = convert_pulsewidth(rc_channel_8.pulsewidth());
        
        /* Check if rc is stalled and failsafe output */
        
        if(rc_channel_1.stallTimer.read_ms() > 200){            
            failsafe = true;
            #if defined (PC_DEBUGGING) && defined (DEBUG_INPUT) 
            pc.printf("NO SIGNAL - FAILSAFE ACTIVATED \r\n");    
            #endif
            failsafe_timer++;
            wait_ms(100);
            if(failsafe_timer > NO_SIGNAL_TIMEOUT){
                armed = false;
            }
        }else{
            failsafe_timer = 0;
            failsafe = false;
        }
        
        
        
        direction.rotation = controls.channel_4;    
        direction.x_translation = controls.channel_2;
        direction.y_translation = controls.channel_1;   
        
        /* Heading lock enable switch */
        if(controls.channel_8 > 75 && !heading_lock_enabled){
            heading_lock = orientation.heading;
            heading_lock_enabled = true;
        }else if(controls.channel_8 < 75 && heading_lock_enabled){
            heading_lock_enabled = false;
        }
        
        /* Speed for heading lock */
        heading_lock_speed = (int) controls.channel_7;
        
        #if defined (PC_DEBUGGING) && defined (DEBUG_INPUT)  
        pc.printf("Input: C1: %i \t C2: %i \t C3: %i \t C4: %i \t C5: %i \t C6: %i \t C7: %i \t C8: %i\r\n", controls.channel_1, controls.channel_2, controls.channel_3, controls.channel_4, controls.channel_5, controls.channel_6, controls.channel_7, controls.channel_8);
        
        pc.printf("Heading Lock: %s \r\n", heading_lock_enabled ? "true" : "false");
        pc.printf("Heading Lock Speed: %i \r\n", heading_lock_speed);
        #endif
   }
}


/** Calculate Orientation
 * 
 * Calculates orientation using the BNO055 sensor
 */
void calculate_orientation(){    
    
    /* If there is an error then we maintain the same 
     * orientation to stop random control flipping */
    if(!bno055Healthy()){
        #if defined (PC_DEBUGGING) && defined (DEBUG_ORIENTATION) 
        pc.printf("ERROR: BNO055 has an error/status problem!!!\r\n");
        #endif
    }else{       
        
        /* Read in the Euler angles */
        orientation = getEulerAngles();    
        
        /* We are upside down in range -30 -> -90  
         * the sensor will report -60 when inverted */
        if(orientation.roll < -30 && orientation.roll > -90){
            inverted = true;   
        }else{
            inverted = false; 
        }
        #if defined (PC_DEBUGGING) && defined (DEBUG_ORIENTATION)     
        pc.printf("Inverted= %s \t (%7.2f) \r\n", inverted ? "true" : "false", orientation.roll);
        #endif
    }        
    
    
}

/** Calculate controls
 * 
 * Calculates controls based on if the robot is inverted or not
 * TODO: Add inverted support
 */
void calculate_controls(){
       
    #ifdef OMNI_MIXER
    
    float x = direction.x_translation -50;
    float y = direction.y_translation -50;    
    
    float theta = (float)atan2((double)x, (double)y);
    float magnitude = (float)sqrt((double)((x*x)+(y*y)));
    
    if(magnitude > 5.0f){  
    
        float vx = magnitude * sin(theta);
        float vy = magnitude * cos(theta);
        const float sqrt3o2 = 1.0*sqrt(3.0)/2.0;
    
        float w0 = -vx;                   // v dot [-1, 0] / 25mm
        float w1 = 0.5*vx - sqrt3o2 * vy; // v dot [1/2, -sqrt(3)/2] / 25mm
        float w2 = 0.5*vx + sqrt3o2 * vy; // v dot [1/2, +sqrt(3)/2] / 25mm           
        #if defined (PC_DEBUGGING) && defined (DEBUG_CONTROLS) 
        pc.printf("Calculated Controls: (%7.2f) \t (%7.2f) \t (%7.2f) \r\n", w0, w1, w2);
        #endif
        float w0_speed =  map(w0, -70, 70, 0, 100);
        float w1_speed =  map(w1, -70, 70, 0, 100);
        float w2_speed =  map(w2, -70, 70, 0, 100);
        
        /* Add in rotation */
        #if defined (PC_DEBUGGING) && defined (DEBUG_CONTROLS) 
        pc.printf("Mapped Controls: (%7.2f) \t (%7.2f) \t (%7.2f) \r\n", w0_speed, w1_speed, w2_speed);
        #endif
        outputs.wheel_1 += w0_speed -50;
        outputs.wheel_2 += w1_speed -50;
        outputs.wheel_3 += w2_speed -50;
        
                      
    }else{
        outputs.wheel_1 = 50;
        outputs.wheel_2 = 50;
        outputs.wheel_3 = 50;
    }   
    
     outputs.wheel_1 += direction.rotation -50;
     outputs.wheel_2 += direction.rotation -50;
     outputs.wheel_3 += direction.rotation -50;
    
    #else
    
    outputs.wheel_1 = direction.rotation;
    outputs.wheel_2 = direction.x_translation;
    outputs.wheel_3 = direction.y_translation;
    
    #endif
    
    /* Clamp outputs to correct range */
    outputs.wheel_1 = clamp(outputs.wheel_1, 0, 100);
    outputs.wheel_2 = clamp(outputs.wheel_2, 0, 100);
    outputs.wheel_3 = clamp(outputs.wheel_3, 0, 100);
    
    #if defined (PC_DEBUGGING) && defined (DEBUG_CONTROLS)     
    pc.printf("Final Controls: %i \t %i \t %i \r\n", outputs.wheel_1, outputs.wheel_2, outputs.wheel_3);
    #endif        
    
}

/** Heading correction
 *
 *  Works out which way to spin to return to set heading
 *  Based on tiberius turnto code
 *
 *  @param speed speed to rotate in percent
 */
void calculate_heading_correction(){
    if(heading_lock_enabled){            
                
        /* Normalize headings */
        float n_current_heading = normalize(orientation.heading);
        float n_heading_lock = normalize(heading_lock);    
        
        #if defined (PC_DEBUGGING) && defined (DEBUG_HEADING_LOCK) 
        pc.printf("Current heading: %f \t Desired Heading: %f \r\n", n_current_heading, n_heading_lock);
        #endif
        
        /* Calculate error */
        float error = n_current_heading - n_heading_lock;    
        
        /* Normalize error */
        error = normalize(error);
        
        /* Figure out which way to turn */
        if(abs(error) > heading_lock_deadband){  
            
            /* Speed is 0 -> 100
             * error is -180 -> 180
             * output is 0 -> 100
             */         
            //error = error * (heading_lock_speed / 100.0);    
            #if defined (PC_DEBUGGING) && defined (DEBUG_HEADING_LOCK) 
            pc.printf("ERROR: %7.2f", error);
            #endif
            error = map(error, -180, 180, 1.0, -1.0);         
          
            
            float amount = error * 50;
            
            amount *= heading_lock_speed / 100.0;
                       
            direction.rotation += amount;
          
        }else{
            
            /* In deadband so do nothing */
            direction.rotation = 50;
        }
        #if defined (PC_DEBUGGING) && defined (DEBUG_HEADING_LOCK)     
        pc.printf("Heading error: (%7.2f) \t Rotation: (%7.2f) \t Speed: (%7.2f) \r\n", error, direction.rotation, heading_lock_speed);
        #endif 
        
    }
}

/** Output to OmniMixer and ESCs 
 * 
 * Sends calculated output values to devices
 */
void output_stage(){
    if(!failsafe){
    //outputs.wheel_1 -= 18;
    //outputs.wheel_2 -= 27;
    //outputs.wheel_3 -= 14;
    
    esc_omni_1.setThrottle(outputs.wheel_1);
    esc_omni_2.setThrottle(outputs.wheel_2);
    esc_omni_3.setThrottle(outputs.wheel_3);
    //esc_weapon_1.setThrottle(outputs.weapon_motor_1);
    //esc_weapon_2.setThrottle(outputs.weapon_motor_2);
    }else{
        esc_omni_1.failsafe();
        esc_omni_2.failsafe();
        esc_omni_3.failsafe();        
    }
    
    #if defined (PC_DEBUGGING) && defined (DEBUG_OUTPUT)  
    pc.printf("OUTPUT M1: %i \t M2: %i \t M3: %i \t W1: %i \t W2: %i \r\n", outputs.wheel_1, outputs.wheel_2, outputs.wheel_3, outputs.weapon_motor_1, outputs.weapon_motor_2);
    #endif
}

/******************************************************************************/

/** Main
 * 
 * Main Loop
 */
int main() {
    #ifdef PC_DEBUGGING
    pc.baud(115200);
    pc.printf("Triforce Control System \r\n");
    #endif
    
    init();
    
    while(true){
        led1 = armed;
        reset_outputs();
        
        #ifdef PC_DEBUGGING
        pc.printf("\x1b[2J\x1b[H");
        #endif
        
        read_inputs(); 
        if(armed && !just_armed){     
            led1 = armed;
            #ifdef ORIENTATION  
            calculate_orientation();
            #endif
            
            #ifdef HEADING_LOCK
            calculate_heading_correction();
            #endif
            calculate_controls();
                
            output_stage();
        }
        #ifdef PC_DEBUGGING
            wait_ms(50);
        #endif
    }     
}