MODSERIAL PID controller edit

Dependencies:   HIDScope MODSERIAL QEI biquadFilter mbed

Fork of PID_controller_Motor by BioBitches TN

main.cpp

Committer:
MBroek
Date:
2016-10-28
Revision:
30:457e42514c47
Parent:
29:2b711fc9a5b2

File content as of revision 30:457e42514c47:



// HET DEFINIEREN VAN ALLES ==========================================================================================

// Includen van alle libraries ---------------------------------------
#include "mbed.h"
#include "MODSERIAL.h"
#include "QEI.h"
#include "math.h"
#include "HIDScope.h"
#include "BiQuad.h"

// Definieren van de HIDscope ----------------------------------------
HIDScope scope(3);               


// Definieren van de Serial en Encoder -------------------------------
MODSERIAL pc(USBTX, USBRX);
#define SERIALBAUD 115200

QEI encoder_motor1(D10,D11,NC,64, QEI::X4_ENCODING);
QEI encoder_motor2(D12,D13,NC,64, QEI::X4_ENCODING);


// Definieren van de Motorpins ---------------------------------------
DigitalOut motor1_direction_pin(D7);
PwmOut motor1_speed_pin(D6);

DigitalOut motor2_direction_pin(D4);
PwmOut motor2_speed_pin(D5);


//Definieren van bord-elementen --------------------------------------
InterruptIn motor_switch_button(D3);
InterruptIn state_switch_button(D2);
DigitalIn EMG_sim1(SW2);
DigitalIn EMG_sim2(SW3);

//AnalogIn pot1(A1);                              // Dit is de gesimuleerde emg1
//AnalogIn pot2(A2);                              // Dit is de gesimuleerde emg2


DigitalOut ledred(LED_RED, 1);
DigitalOut ledgreen(LED_GREEN, 1);
DigitalOut ledblue(LED_BLUE, 1);

AnalogIn    emg0( A0 );         //  right biceps
AnalogIn    emg1( A1 );         //  left biceps

AnalogIn init_pot(A3);          // POT meter 


//Definieren van de tickers ------------------------------------------
Ticker motor_ticker;                        // Deze ticker activeert het motor_runs programma, dit leest motor pos en ref pos uit, rekent de PID uit en stuurt met dit de motoren
Ticker hidscope_ticker;


// Het definieren van de filters ----------------------------------------
BiQuadChain bqc1;       //  Combined filter: high-pass (10Hz) and notch (50Hz)
BiQuad bq1( 9.21171e-01, -1.84234e+00, 9.21171e-01, -1.88661e+00, 8.90340e-01 );
BiQuad bq2( 1.00000e+00, -2.00000e+00, 1.00000e+00, -1.94922e+00, 9.53070e-01 );
BiQuad bq3( 9.93756e-01, -1.89024e+00, 9.93756e-01, -1.89024e+00, 9.87512e-01 );

BiQuadChain bqc2;
BiQuad bq4 = bq1;
BiQuad bq5 = bq2;
BiQuad bq6 = bq3;

BiQuadChain bqc3;       //  Low-pass filter (5Hz)
BiQuad bq7( 5.84514e-08, 1.16903e-07, 5.84514e-08, -1.94264e+00, 9.43597e-01 );
BiQuad bq8( 1.00000e+00, 2.00000e+00, 1.00000e+00, -1.97527e+00, 9.76245e-01 );

BiQuadChain bqc4;
BiQuad bq9 = bq7;
BiQuad bq10 = bq9;

BiQuadChain bqc5;       //  Low-pass filter (10Hz) voor PID
BiQuad bq11( 1.32937e-05, 2.65875e-05, 1.32937e-05, -1.77831e+00, 7.92447e-01 );
BiQuad bq12( 1.00000e+00, 2.00000e+00, 1.00000e+00, -1.89342e+00, 9.08464e-01 );






// HET VASTSTELLEN VAN ALLE TE GEBRUIKEN VARIABELEN EN CONSTANTEN ======================================================

enum states_enum{off, init, init_fish, run};                   // De states waar de main loop doorheen loopt, off is uit, init is intialiseren en run is het aanturen vd motoren met emg.
states_enum states = off;

double ref_pos_prev_m1 = 0.0;                          // De initiele ref_pos_pref
double ref_pos_prev_m2 = 0.0;

int counts1;                                // Pulses van motoren
int counts2;

const double pi = 3.14159265358979323846264338327950288419716939937510;     // pi
const double rad_per_count = 2.0*pi/8400.0;       // Aantal rad per puls uit encoder

const double radius_tandwiel = 0.008;
const double meter_per_count = rad_per_count * radius_tandwiel;           // Het aantal meter dat het karretje aflegt per puls, DIT IS NOG ONBEKEND!!!

const double v_max_karretje = 8.4*radius_tandwiel;                  // Maximale snelheid karretje, gelimiteerd door de v_max vd motor

double error1_int = 0.00000;                 // Initiele error integral waardes
double error2_int = 0.00000;

const double T_motor_function = 0.01;           // Periode van de frequentie van het aanroepen van onder andere positiechecker (get_position) en de rest vd motor functie

volatile bool flag1 = false, flag2 = false;   // De flags voor de functies aangeroepen door de tickers

const double Kp_1 = 0.2;                    // De PID variablen voor motor 1
const double Kd_1 = 0.01;
const double Ki_1 = 0.01;

const double Kp_2 = 1.0;                    // De PID variablen voor motor 2
const double Kd_2 = 0.4;
const double Ki_2 = 0.1;

const double vrijheid_m1_rad = 0.4*pi;      // Dit is de uiterste postitie vd arm in radialen
const double vrijheid_m2_meter = 0.25;         // Dit is de helft vd arm, dit is wat het karretje heen en weer kan bewegen

const double initial_pos_m1 = vrijheid_m1_rad;                // Startin posities van de motoren
const double initial_pos_m2 = vrijheid_m2_meter; 

double position_motor1;

double position_motor2; 
double position_motor2_prev = initial_pos_m2;    

double PID_output_1 = 0.0;                      // De eerste PID_output, deze is nul anders gaan de motoren draaien
double PID_output_2 = 0.0;

double error_prev_1 = 0.0;                      // De initiele previous error
double error_int_1 = 0.0;                       // De initiele intergral error

double error_prev_2 = 0.0;
double error_int_2 = 0.0;

double reference_pos_m2;                      // De reference positie waar de motor heen wil afhankelijk van het EMG signaal
double reference_pos_m1;

enum which_motor{motor1, motor2};               // enum van de motoren
which_motor motor_that_runs = motor1;

int  digital0;               //  Define digital signals fot EMG
int  digital1;

double  threshold0 = 0.3;       //  Define thresholds (signal is normalized, so 0.3 = 30% of maximum value) fot EMG
double  threshold1 = 0.3;

double max_out0 = 0;            //  Set initial maximum values of the signals to 0, the code will check if there's a new maximum value every iteration
double max_out1 = 0;






// ALLE FUNCTIES BUITEN DE MAIN-LOOP ====================================================================================


// De statechanger -----------------------------------------------------------
void state_changer(){
    if(states == off){
        states = init;
    }
    else if(states == init){
        states = init_fish;
    }
    else if(states == init_fish){
        states = run;
    }
    else{
        states = off;
    }
}


// Functie voor het switchen tussen de motors ------------------------------
void motor_switch(){
    if(motor_that_runs == motor1){
        motor_that_runs = motor2;
        }
            else{
                motor_that_runs = motor1;
            }
}


// Functie voor het vinden van de positie van motor 1 ---------------------
double get_position_m1(const double radpercount){          //returned de positie van de motor in radialen (positie vd arm in radialen)
    counts1 = encoder_motor1.getPulses();           // Leest aantal pulses uit encoder af
    return radpercount * counts1 + initial_pos_m1;      // rekent positie motor1 uit en geeft deze als output vd functie
}


// Functie voor vinden van de positie van motor 2 -----------------------------
double get_position_m2(const double meterpercount, double mot_pos_prev){     // returned de positie van het karretje in meter
    double mot_pos;
    counts2 = encoder_motor2.getPulses();               // leest aantal pulses vd encoder af
    mot_pos = meterpercount * counts2 + mot_pos_prev;
    encoder_motor2.reset();
    mot_pos_prev = mot_pos;
    return mot_pos_prev;                     // rekent de positie van het karretje uit en geeft dit als output vd functie
}


// Functie voor het uitlezen van het EMG signaal ------------------------------------------
int get_EMG()
{
    double in0 = emg0.read();                   //  EMG signal 0 (right biceps)
    double in1 = emg1.read();                   //  EMG signal 1 (left biceps)
    
    double filtered0 = bqc1.step(in0);          //  Filter signals   (high pass and notch)
    double filtered1 = bqc2.step(in1);
    double abs_filtered0 = abs(filtered0);      //  Absolute value of filtered signals
    double abs_filtered1 = abs(filtered1);
    double out0 = bqc3.step(abs_filtered0);     //  Filter the signals again to remove trend  (low pass)
    double out1 = bqc4.step(abs_filtered1);
        
    if (out0 > max_out0)                        //  Check if the signals have a new maximum value
        {   max_out0 = out0;    }
    if (out1 > max_out1)
        {   max_out1 = out1;    }
    
    double normalized_out0 = out0 / max_out0;   //  Normalize the signals (divide by maximum value to scale from 0 to 1)
    double normalized_out1 = out1 / max_out1;
    
    if (normalized_out0 > threshold0)           //  If the signal value is above the threshold, give an output of 1
        {   digital0 = 1;   }
    else                                        //  If the signal value is below the threshold, give an output of 0
        {   digital0 = 0;   }
            
    if (normalized_out1 > threshold1)           //  Do the same for the second EMG signal
    {   digital1 = 1;   }
    else
    {   digital1 = 0;   }
    
    /*
    scope.set(0,in0);           //  EMG signal 0    (right biceps)
    scope.set(1,in1);           //  EMG signal 1    (left biceps)
    scope.set(2,digital0);      //  Signal 0 after filtering, detrending, rectification, digitalization
    scope.set(3,digital1);      //  Signal 1 after filtering, detrending, rectification, digitalization
    scope.set(4,digital0 - digital1);         //  Final output signal
    
    scope.send();               //  Send all channels to the PC at once
    */

    return digital0 - digital1;         //  Subtract the digital signals (right arm gives positive values, left arm give negative values)
}



// Functie voor het vinden van de reference position van motor 1 --------------------
double get_reference_position_m1(double &ref_pos_prev, const double vrijheid_rad){
    double ref_pos;
    // int final_signal = get_EMG();     // Het uiteindelijke signaal uit de emg als output zit is -1, 0 of 1.
    int final_signal = EMG_sim1.read() - EMG_sim2.read();
    switch(final_signal){
        case 0 :
            ref_pos = ref_pos_prev;
            break;
        case 1 :
            ref_pos = ref_pos_prev + T_motor_function*vrijheid_rad*0.3;         // Dit dwingt af dat de ref pos veranderd met ong 0.5 rad/s
            break;
        case -1 :
            ref_pos = ref_pos_prev - T_motor_function*vrijheid_rad*0.3;
            break;
    }
    if(ref_pos >= vrijheid_rad){
        ref_pos = vrijheid_rad;
    }
    if(ref_pos <= -vrijheid_rad){
        ref_pos = -vrijheid_rad;
    }
    ref_pos_prev = ref_pos;
    return ref_pos_prev;            // vrijheid_rad is de uiterste positie vd arm in radialen                                                                    
}


// Functie voor het vinden van de reference position van motor 2 --------------------
double get_reference_position_m2(double &ref_pos_prev, const double vrijheid_meter){
    double ref_pos;
    int final_signal = EMG_sim1.read() - EMG_sim2.read();     // Het uiteindelijke signaal uit de emg als output zit is -1, 0 of 1.
    switch(final_signal){
        case 0 :
            ref_pos = ref_pos_prev;
            break;
        case 1 :
            ref_pos = ref_pos_prev + T_motor_function*v_max_karretje;           // Hierdoor veranderd de ref_pos met de maximale snelheid vd motor (karretje)
            break;
        case -1 :
            ref_pos = ref_pos_prev - T_motor_function*v_max_karretje;
            break;
    }
    if(ref_pos >= vrijheid_meter){
        ref_pos = vrijheid_meter;
    }
    if(ref_pos <= 0){
        ref_pos = 0;
    }
    ref_pos_prev = ref_pos;
    return ref_pos_prev;                                                                                
}


// HIDScope functie ----------------------------------------------------------------------
void set_scope(double input1, double input2, double input3){
    scope.set(0, input1); 
    scope.set(1, input2);
    scope.set(2, input3);
    scope.send();
}


// De PID-controller voor de motors -------------------------------------------------
double PID_controller(double ref_pos, double mot_pos, double &e_prev, double &e_int, const double Kp, const double Kd, const double Ki){
    double error = ref_pos - mot_pos;       // error uitrekenen
    double error_dif = (error-e_prev)/T_motor_function;      // De error differentieren
    //scope.set(error_dif,0); 
    //error_dif = bqc5.step(error_dif);       // Filter biquad
    //scope.set(error_dif,1); scope.send();
    e_prev = error;                                 // Hier wordt de error opgeslagen voor de volgende keer
    e_int = e_int + T_motor_function*error;             // De error integreren
    return Kp*error + Ki*e_int + Kd*error_dif;       // De uiteindelijke PID output
}
       

// Motor 1 besturen ---------------------------------------------------------------------
void set_motor1(double motor_input){        // functie die de motor aanstuurt mt als input de PID-output
    if (motor_input >= 0){                  // Dit checkt welke kant de motor op moet draaien
        motor1_direction_pin = 1;           // Clockwise
    }
        else {
            motor1_direction_pin = 0;       // CounterClockwise
        }
    if (fabs(motor_input)>1){               // Dit fixed de PwmOutput op maximaal 1
        motor_input = 1;
    }
    motor1_speed_pin = fabs(motor_input);   // Dit geeft de uiteindelijke input door aan de motor
}
 
 
// Motor 2 besturen ---------------------------------------------------------------------
void set_motor2(double motor_input){        // functie die de motor aanstuurt mt als input de PID-output
    if (motor_input >= 0){                  // Dit checkt welke kant de motor op moet draaien
        motor2_direction_pin = 1;           // Clockwise
    }
        else {
            motor2_direction_pin = 0;       // CounterClockwise
        }
    if (fabs(motor_input)>1){               // Dit fixed de PwmOutput op maximaal 1
        motor_input = 1;
    }
    motor2_speed_pin = fabs(motor_input);   // Dit geeft de uiteindelijke input door aan de motor
}


// De go-flag functies -----------------------------------------------------------------
void flag1_activate(){flag1=true;}           // Activeert de flag
void flag2_activate(){flag2=true;}   


// Dit doet de robot als hij niets doet ------------------------------------------------------
void robot_is_off(){
    ledgreen.write(1);              
    ledred.write(0);               // Indicator ik ben uit
    motor1_speed_pin = 0;           // Uitzetten vd motoren
    motor2_speed_pin = 0;
}


// De initialisatie functie --------------------------------------------------------------------
void initialise(){
    ledred.write(1);
    ledblue.write(0);
    if (flag1){
        double init_m2_speed = init_pot;
            set_motor2(init_m2_speed);
        }
}


// De initialiseren beeindigen functie ------------------------------------------------------------
void initialise_finish(){       // Deze functie laat de motor van een vaste uiterste positie (fysisch limiet) naar het middelpunt draaien.
    ledblue.write(1);
    ref_pos_prev_m1 = initial_pos_m1;
    ref_pos_prev_m2 = initial_pos_m2;
    position_motor1 = initial_pos_m1;
    position_motor2 = initial_pos_m2;
    position_motor2_prev = initial_pos_m2;
    PID_output_1 = 0.0;                      
    PID_output_2 = 0.0;
    ledgreen.write(0);
    ledred.write(0);
    encoder_motor1.reset();
    encoder_motor2.reset();             
    motor1_speed_pin = 0;
}  


// De functie die de motoren aanstuurt -------------------------------------------------
void running_motors(){              // Deze functie kijkt welke motor moet draaien en rekent dan de PID uit en geeft dit door aan de motor input functie
    if (flag1){
            flag1 = false;
            ledred.write(1);
            switch (motor_that_runs){
                case motor1 :      // In deze case draait alleen motor 1
                    reference_pos_m1 = get_reference_position_m1(ref_pos_prev_m1, vrijheid_m1_rad);
                    position_motor1 = get_position_m1(rad_per_count);
                    PID_output_1 = PID_controller(reference_pos_m1, position_motor1, error_prev_1, error_int_1, Kp_1, Kd_1, Ki_1);
                    //PID_output_2 = PID_controller(-reference_pos_m1, get_position_m1(rad_per_count), error_prev_1, error_int_1, Kp_1, Kd_1, Ki_1); // Zorgt er voor dat motor2 meedraait met motor1 
                    set_motor1(PID_output_1);
                    //set_motor2(-PID_output_1);
                    encoder_motor2.reset();
                    break;
                case motor2 :
                    reference_pos_m2 = get_reference_position_m2(ref_pos_prev_m2, vrijheid_m2_meter);
                    position_motor2 = get_position_m2(meter_per_count, position_motor2_prev);
                    PID_output_2 = PID_controller(reference_pos_m2, position_motor2, error_prev_2, error_int_2, Kp_2, Kd_2, Ki_2);
                    PID_output_1 = PID_controller(reference_pos_m1, get_position_m1(rad_per_count), error_prev_1, error_int_1, Kp_1, Kd_1, Ki_1);   // Zorgt ervoor dat motor1 op de dezelfde positie blijft als motor 2 draait.
                    set_motor2(PID_output_2);
                    set_motor1(PID_output_1);
                    position_motor2_prev = position_motor2;
                    break;
            }
    }
}


// De HIDscope debug functie ----------------------------------------------------------------
void call_HIDscope(double input_1, double input_2, double input_3){             // Deze functie roept de HIDscope aan
        if (flag2){
            flag2 = false;
            set_scope(input_1, input_2, input_3);
        }
}




// DE MAIN =================================================================================================================
int main()
{
    pc.baud(SERIALBAUD);
    
    bqc1.add( &bq1 ).add( &bq2 ).add( &bq3 );
    bqc2.add( &bq4 ).add( &bq5 ).add( &bq6 );
    bqc3.add( &bq7 ).add( &bq8 );
    bqc4.add( &bq9 ).add( &bq10 );
    bqc5.add( &bq11 ).add( &bq12 );
    
    
    
    
    state_switch_button.fall(&state_changer);        // Switcht states
    motor_switch_button.fall(&motor_switch);        // Switcht motors
    

    
    motor_ticker.attach(&flag1_activate, T_motor_function);                  // Activeert de go-flag van motor positie 
    hidscope_ticker.attach(&flag2_activate,0.01);   // Leest ref pos en mot pos uit, rekent PID uit en stuurt motoren aan.
    
    while(1){
        switch(states){
            case off :                    // Dan staat het programma stil en gebeurt er niks
                robot_is_off();
                break;
            case init :          // Hier voert hij alleen het initialiseren van motor 1 uit
                initialise();      
                break;
            case init_fish :
                initialise_finish();
                break;
            case run :
                running_motors();       // Hier voert hij het programma voor het aansturen vd motors uit
                break;
            
        }
        call_HIDscope(PID_output_1, reference_pos_m1, position_motor1);
    }  
}