Totale script, 2 rotationele joints met middelpunt van swiffer als end-effector die verticaal en horizontaal wordt bestuurd middels EMG-signalen. Automatische kalibratie, boundaries tegen singularity en kapotbreken robot.

Dependencies:   HIDScope MODSERIAL QEI biquadFilter mbed

Fork of EMG_controlled_Inv_Kin_PID_Control by Marlowe Noll

main.cpp

Committer:
willem_hoitzing
Date:
2016-11-08
Revision:
7:601703415d80
Parent:
6:ee20fc281e9d

File content as of revision 7:601703415d80:

#include "stdio.h"
#include "math.h"
#include "mbed.h"
#include "QEI.h"
#include "MODSERIAL.h"
#include "BiQuad.h"
#include "HIDScope.h"

MODSERIAL pc(USBTX, USBRX);

// DEFINE MOTOR + ENCODER
QEI wheel_M1 (D13, D12, NC, 32);
QEI wheel_M2 (D10, D11, NC, 32);
PwmOut pwm_M1 (D6);
PwmOut pwm_M2 (D5);
DigitalOut dir_M1 (D7);
DigitalOut dir_M2 (D4);

// DEFINE EMG INPUTS + OUTPUTS + FILTERS
AnalogIn emgB(A0);      // BICEPS
AnalogIn emgT(A1);      // TRICEPS
AnalogIn emgS(A2);      // SWITCH (other biceps?)
HIDScope scope(6);
BiQuadChain filter1b;
BiQuadChain filter2b;
BiQuadChain filter1t;
BiQuadChain filter2t;
BiQuadChain filter1s;
BiQuadChain filter2s;
// BICEPS FILTER
BiQuad bq1b(8.5977e-01, -1.7195e+00, 8.5977e-01, -1.7347e+00,  7.6601e-01);   // Notch + HP
BiQuad bq2b(1.0000e+00, -1.6182e+00, 1.0000e+00, -1.5933e+00,  9.8217e-01);   // Notch + HP
BiQuad bq3b(1.0000e+00, -1.6182e+00, 1.0000e+00, -1.6143e+00 , 9.8260e-01);   // Notch + HP
BiQuad bq4b(3.4604e-04,  6.9208e-04, 3.4604e-04, -1.9467e+00,  9.4808e-01);   // LP
// TRICEPS FILTER
BiQuad bq1t(8.5977e-01, -1.7195e+00, 8.5977e-01, -1.7347e+00,  7.6601e-01);   // Notch + HP
BiQuad bq2t(1.0000e+00, -1.6182e+00, 1.0000e+00, -1.5933e+00,  9.8217e-01);   // Notch + HP
BiQuad bq3t(1.0000e+00, -1.6182e+00, 1.0000e+00, -1.6143e+00 , 9.8260e-01);   // Notch + HP
BiQuad bq4t(3.4604e-04,  6.9208e-04, 3.4604e-04, -1.9467e+00,  9.4808e-01);   // LP
// SWITCH FILTER
BiQuad bq1s(8.5977e-01, -1.7195e+00, 8.5977e-01, -1.7347e+00,  7.6601e-01);   // Notch + HP
BiQuad bq2s(1.0000e+00, -1.6182e+00, 1.0000e+00, -1.5933e+00,  9.8217e-01);   // Notch + HP
BiQuad bq3s(1.0000e+00, -1.6182e+00, 1.0000e+00, -1.6143e+00 , 9.8260e-01);   // Notch + HP
BiQuad bq4s(3.4604e-04,  6.9208e-04, 3.4604e-04, -1.9467e+00,  9.4808e-01);   // LP

// DEFINE LEDS FOR 
DigitalOut ledg (LED_GREEN);
DigitalOut ledr (LED_RED);
DigitalOut ledb (LED_BLUE);
InterruptIn knop_biceps(SW2);
InterruptIn knop_triceps(SW3);
InterruptIn knop_switch(D9);
InterruptIn knop_calibrate(PTC12);

// DEFINE TICKERS
Ticker emgticker;
Ticker printer_ticker;
Ticker end_calibration_biceps_ticker;
Ticker end_calibration_triceps_ticker;
Ticker end_calibration_switch_ticker;
Ticker PIDcontrol;
Ticker switch_activate_ticker;
Ticker update_ref_ticker;

// DEFINE PARAMETERS/VARIABLES/BOOLEANS-->STATES
volatile double bEMG_max = 0;
volatile double tEMG_max = 0;
volatile double sEMG_max = 0;
const double percentage_threshold_biceps = 0.09/0.171536;       // 0.171536 is max EMG value
const double percentage_threshold_triceps = 0.08/0.203654;      // 0.203654 is max EMG value
const double percentage_threshold_switch = 0.08/0.171536;       // copied, lower for safety of STOP function
volatile double threshold_biceps = 0;
volatile double threshold_triceps = 0;
volatile double threshold_switch = 0;
volatile bool calibrate_biceps = false;
volatile bool calibrate_triceps = false;
volatile bool calibrate_switch = false;
volatile bool calibration_finished = false;
volatile int n = 0;
const double pi = 3.14159265359;
volatile double q1 = 0;
volatile double q2 = 0;
const double l1 = 0.3626;   //m
const double l2 = 0.435;    //m
volatile double J_1;
volatile double J_2;
volatile double J_3;
volatile double J_4;
volatile double q1_v;
volatile double q2_v;
volatile double q1_ref = 0;
volatile double q2_ref = 0;
volatile double q1_ref_prev = 0;
volatile double q2_ref_prev = 0;
volatile double q1_error = 0;
volatile double q2_error = 0;
volatile double q1_error_prev = 0;
volatile double q2_error_prev = 0;
volatile double q1DerivativeError = 0;
volatile double q2DerivativeError = 0;
volatile double q1IntError = 0;
volatile double q2IntError = 0;
volatile double q1_total_error= 0;
volatile double q2_total_error= 0;
double ctrlOutput_M1 = 0;
double ctrlOutput_M2 = 0;
volatile double vx;
volatile double vy;
volatile bool translatie_richting = true; // true is vertical, false is horizontal
const double TS = 0.009;                  // sample time, 111 Hz
const double MotorGain_M1 = 4.3;          // at pwm = 1 (free) motor rotates with 4.3 rad/s -> measured
const double MotorGain_M2 = 4.7;          // measured
volatile bool go_flag_printer = false;
volatile bool go_flag_controller = false;
volatile bool go_flag_emgsample = false;
volatile bool go_flag_update_ref = false;
volatile bool PID_ticker_active = false;
volatile bool switch_active = true;


void flag_printer()
{
    go_flag_printer = true;
}
void printer()
{
    pc.printf("vx = %f \tvy = %f \tq1_r = %f \tq2_r = %f \tq1 = %f \tq2 = %f \tq1_v = %f \tq2_v = %f\n\r",vx,vy,q1_ref,q2_ref,q1,q2,q1_v,q2_v);
}

void end_calibration_biceps()
{
    ledr = 1;
    calibrate_biceps = false;
    end_calibration_biceps_ticker.detach();
}
void end_calibration_triceps()
{
    ledb = 1;
    calibrate_triceps = false;
    end_calibration_triceps_ticker.detach();
}
void end_calibration_switch()
{
    ledg = 1;
    calibrate_switch = false;
    end_calibration_switch_ticker.detach();
    calibration_finished = true;
}

void start_calibration()
{
    calibration_finished = false;
    n++;
    if (n == 1) {
        ledr = 0;
        bEMG_max = 0;
        calibrate_biceps = true;
        end_calibration_biceps_ticker.attach(&end_calibration_biceps, 10);
    }
    if (n == 2) {
        ledb = 0;
        tEMG_max = 0;
        calibrate_triceps = true;
        end_calibration_triceps_ticker.attach(&end_calibration_triceps, 10);
    }
    if (n == 3) {
        ledg = 0;
        sEMG_max = 0;
        calibrate_switch = true;
        end_calibration_switch_ticker.attach(&end_calibration_switch, 5);
        n = 0;          // reset for next calibration
    }    
}

void flag_controller()
{
    go_flag_controller = true;
}

void begin_hoeken()
{
    wait(1.5);          // wait for arms to have finished moving
    q1_ref = wheel_M1.getPulses()/(1334.355/2);
    q2_ref = wheel_M2.getPulses()/(1334.355/2);
    PID_ticker_active = true;
}

void initialize()
{
    dir_M1 = 0; //ccw
    dir_M2 = 1; //cw
    while ( (q1 < 20*2*pi/360) || (q2 > -45*2*pi/360) ) {
        q1 = wheel_M1.getPulses()/(1334.355/2);
        q2 = wheel_M2.getPulses()/(1334.355/2);
        if (q1 < 20*2*pi/360) {
            pwm_M1 = 0.05;
        } else {
            pwm_M1 = 0;
        }
        if (q2 > -45*2*pi/360) {
            pwm_M2 = 0.06;
        } else {
            pwm_M2 = 0;
        }
        wait(0.005f);
    }
    pwm_M1 = 0;
    pwm_M2 = 0;
    begin_hoeken();
}

void biceps()
{
    q1_ref_prev = 0;
    q2_ref_prev = 0;
    q1IntError = 0;
    q2IntError = 0;
    q1_error_prev = 0;
    q2_error_prev = 0;
    if (translatie_richting == true) {      // vertical / up
        vx = 0;
        vy = 0.1;
    } else {                        // horizontal / right
        vx = 0.1;
        vy = 0;
    }
}

void triceps()
{
    q1_ref_prev = 0;
    q2_ref_prev = 0;
    q1IntError = 0;
    q2IntError = 0;
    q1_error_prev = 0;
    q2_error_prev = 0;
    if (translatie_richting == true) {      // vertical / down
        vx = 0;
        vy = -0.1;
    } else {                        // horizontal / left
        vx = -0.1;
        vy = 0;
    }

}

void switcher()
{
    if ( (vx == 0) && (vy == 0) && (translatie_richting == true) ) {
        translatie_richting = false;
    } else if ( (vx == 0) && (vy == 0) && (translatie_richting == false) ) {
        translatie_richting = true;
    } else {
        vx = 0;
        vy = 0;
        q1_ref = q1;
        q2_ref = q2;
        q1_error = 0;
        q2_error = 0;
        q1IntError = 0;
        q2IntError = 0;
        q1_error_prev = 0;
        q2_error_prev = 0;
        q1_total_error = 0;
        q2_total_error = 0;
    }

    if (translatie_richting == 1) {
        ledr = 1;                   // blue - vertical
        ledg = 1;
        ledb = 0;
    } else {
        ledr = 0;                   // red - horizontal
        ledg = 1;
        ledb = 1;
    }
}

void switch_activate()
{
    switch_active = true;
}

void flag_emgsample()
{
    go_flag_emgsample = true;
}

void emgsample()
{
    double bEMG_raw = emgB.read();
    double bEMG_HPfilt = filter1b.step( bEMG_raw );
    double bEMG_rect = abs(bEMG_HPfilt);
    double bEMG_filt = filter2b.step(bEMG_rect);

    double tEMG_raw = emgT.read();
    double tEMG_HPfilt = filter1t.step( tEMG_raw );
    double tEMG_rect = abs(tEMG_HPfilt);
    double tEMG_filt = filter2t.step(tEMG_rect);

    double sEMG_raw = emgS.read();
    double sEMG_HPfilt = filter1s.step( sEMG_raw );
    double sEMG_rect = abs(sEMG_HPfilt);
    double sEMG_filt = filter2s.step(sEMG_rect);
    
    if ((bEMG_filt > bEMG_max) && (calibrate_biceps == true) ) {
        bEMG_max = bEMG_filt;
        threshold_biceps = bEMG_max*percentage_threshold_biceps;
        }
    
    if ((tEMG_filt > tEMG_max) && (calibrate_triceps == true) ) {
        tEMG_max = tEMG_filt;
        threshold_triceps = tEMG_max*percentage_threshold_triceps;
        }
    
    if ((sEMG_filt > sEMG_max) && (calibrate_switch == true) ) {
        sEMG_max = sEMG_filt;
        threshold_switch = sEMG_max*percentage_threshold_switch;
        }
        
    scope.set(0, bEMG_filt);
    scope.set(1, tEMG_filt);
    scope.set(2, sEMG_filt);
    scope.set(3, threshold_biceps);
    scope.set(4, threshold_triceps);
    scope.set(5, threshold_switch);
    scope.send();
    
    // motor control, carry out only when calibration is finished
    if ( calibration_finished == true ) {
        if (sEMG_filt > threshold_switch) {
            if (switch_active == true) {
                switcher();
                switch_active = false;
                switch_activate_ticker.attach(&switch_activate, 0.5f);
            }
        } else if (tEMG_filt > threshold_triceps) {
            triceps();
        } else if (bEMG_filt > threshold_biceps) {
            biceps();
        }
    }
}

void flag_update_ref()
{
    go_flag_update_ref = true;
}

void update_ref()
{
    q1 = wheel_M1.getPulses() / (1334.355/2);       // rad
    q2 = wheel_M2.getPulses() / (1334.355/2);       // rad

    J_1 = -(l2*sin(q1 + q2))/(l2*sin(q1 + q2)*(l2*cos(q1 + q2) + l1*cos(q1)) - l2*cos(q1 + q2)*(l2*sin(q1 + q2) + l1*sin(q1)));
    J_2 = (l2*cos(q1 + q2))/(l2*sin(q1 + q2)*(l2*cos(q1 + q2) + l1*cos(q1)) - l2*cos(q1 + q2)*(l2*sin(q1 + q2) + l1*sin(q1)));
    J_3 = (l2*sin(q1 + q2) + l1*sin(q1))/(l2*sin(q1 + q2)*(l2*cos(q1 + q2) + l1*cos(q1)) - l2*cos(q1 + q2)*(l2*sin(q1 + q2) + l1*sin(q1)));
    J_4 = -(l2*cos(q1 + q2) + l1*cos(q1))/(l2*sin(q1 + q2)*(l2*cos(q1 + q2) + l1*cos(q1)) - l2*cos(q1 + q2)*(l2*sin(q1 + q2) + l1*sin(q1)));

    q1_v = J_1 * vx + J_2 * vy;
    q2_v = J_3 * vx + J_4 * vy;

    if ( (q1 > (135*2*pi/360)) && (q1_v > 0 ) ) {
        q1_v = 0;
        q2_v = 0;
        q1_ref = q1;
        q2_ref = q2;
        q1IntError = 0;
        q2IntError = 0;
        q1_error_prev = 0;
        q2_error_prev = 0;
        pc.printf("BOUNDARY 1\n\r");
    } else if ( (q1 < -(135*2*pi/360)) && (q1_v < 0) ) {
        q1_v = 0;
        q2_v = 0;
        q1_ref = q1;
        q2_ref = q2;
        q1IntError = 0;
        q2IntError = 0;
        q1_error_prev = 0;
        q2_error_prev = 0;
        pc.printf("BOUNDARY 2\n\r");
    } else if ( (q2 < (-2.6)) && (q2_v < 0) ) {        // werkelijke max -2.672452
        q1_v = 0;
        q2_v = 0;
        q1_ref = q1;
        q2_ref = q2;
        q1IntError = 0;
        q2IntError = 0;
        q1_error_prev = 0;
        q2_error_prev = 0;
        pc.printf("BOUNDARY 3\n\r");
    } else if ( (q2 >= -0.05) && (q2_v > 0)) {
        vx = 0;
        vy = 0;
        q1_ref = q1;
        q2_ref = -0.05;
        q1_v = 0;
        q2_v = 0;
        q1_error = 0;
        q2_error = 0;
        q1IntError = 0;
        q2IntError = 0;
        q1_error_prev = 0;
        q2_error_prev = 0;
        q1_total_error = 0;
        q2_total_error = 0;
        pc.printf("BOUNDARY 4\n\r");
    }
    
    if ( q2 == 0 ) {
        vx = 0;
        vy = 0;
        q1_v = 0;
        q2_v = 0;
        q1_ref = q1;
        q2_ref = -0.05;
        q1IntError = 0;
        q2IntError = 0;
        q1_error_prev = 0;
        q2_error_prev = 0;
        pc.printf("BOUNDARY 5\n\r");
        //update_ref_ticker.detach();
    }
    
    double q_v_max = 2;     // Boundary making sure the robot doesn't spin out of control
    if (q1_v > q_v_max) {
        q1_v = q_v_max;
    } else if (q1_v < -q_v_max) {
        q1_v = -q_v_max;
    }
    if (q2_v > q_v_max) {
        q2_v = q_v_max;
    } else if (q2_v < -q_v_max) {
        q2_v = -q_v_max;
    }

    q1_ref_prev = q1_ref;
    q2_ref_prev = q2_ref;

    q1_ref = q1_ref_prev + q1_v*TS;
    q2_ref = q2_ref_prev + q2_v*TS;
}

void PID(double q1,double q1_ref,double q2,double q2_ref,double TS,double &ctrlOutput_M1, double &ctrlOutput_M2)
{
    // linear feedback control
    q1_error = q1_ref - q1;                             // proportional angular error in radians
    q2_error = q2_ref - q2;                             // proportional angular error in radians
    double Kp = 20;

    q1IntError = q1IntError + q1_error*TS;              // integrated error in radians
    q2IntError = q2IntError + q2_error*TS;              // integrated error in radians
    double Ki = 2;

    q1DerivativeError = (q1_error - q1_error_prev)/TS;  // derivative of error in radians
    q2DerivativeError = (q2_error - q2_error_prev)/TS;  // derivative of error in radians
    double Kd = 0;

    q1_total_error = (q1_error * Kp) + (q1IntError * Ki) + (q1DerivativeError * Kd);         //total controller output = motor input
    q2_total_error = (q2_error * Kp) + (q2IntError * Ki) + (q2DerivativeError * Kd);         //total controller output = motor input

    ctrlOutput_M1 = q1_total_error/MotorGain_M1;
    ctrlOutput_M2 = q2_total_error/MotorGain_M2;

    q1_error_prev = q1_error;
    q2_error_prev = q2_error;
}

void controller()
{
    PID(q1,q1_ref,q2,q2_ref,TS,ctrlOutput_M1,ctrlOutput_M2);

    if (ctrlOutput_M1 < 0) {
        dir_M1 = 1;
    } else {
        dir_M1 = 0;
    }
    pwm_M1 = abs(ctrlOutput_M1);
    if (pwm_M1 <= 0) {
        pwm_M1 = 0;
    } else {
        pwm_M1 = pwm_M1 + 0.05;
    }

    if (ctrlOutput_M2 < 0) {
        dir_M2 = 1;
    } else {
        dir_M2 = 0;
    }
    pwm_M2 = abs(ctrlOutput_M2);
    if (pwm_M2 <= 0) {
        pwm_M2 = 0;
    } else {
        pwm_M2 = pwm_M2 + 0.05;
    }
}

int main()
{
    ledr = 1;
    ledg = 1;
    ledb = 1;
    pc.baud(115200);
    wheel_M1.reset();
    wheel_M2.reset();
    filter1b.add(&bq1b).add(&bq2b).add(&bq3b);
    filter2b.add(&bq4b);
    filter1t.add(&bq1t).add(&bq2t).add(&bq3t);
    filter2t.add(&bq4t);
    filter1s.add(&bq1s).add(&bq2s).add(&bq3s);
    filter2s.add(&bq4s);
    knop_biceps.rise(&biceps);
    knop_triceps.rise(&triceps);
    knop_switch.rise(&switcher);
    knop_calibrate.rise(&start_calibration);

    // initialize -> begin positions to escape singularity
    initialize();

    // flag functions/tickers
    emgticker.attach(&emgsample, 0.002f); // 500 Hz --> moet kloppen met frequentie gebruikt voor filter coefficienten
    printer_ticker.attach(&flag_printer, TS);
    update_ref_ticker.attach(&flag_update_ref, TS);

    if (PID_ticker_active == true) {
        PIDcontrol.attach(&flag_controller, TS);
    }

    while(1) {
        // sample EMG
        if (go_flag_emgsample == true) {
            go_flag_emgsample = false;
            emgsample();
        }
        // print variables v, q, q_ref, q_v
        if (go_flag_printer == true) {
            go_flag_printer = false;
            printer();
        }
        // update joint positions/velocities
        if (go_flag_update_ref == true) {
            go_flag_update_ref = false;
            update_ref();
        }
        // controller M1+M2
        if (go_flag_controller == true) {
            go_flag_controller = false;
            controller();
        }
    }
}