State machine

Dependencies:   HIDScope QEI biquadFilter mbed

Fork of State_machine by Kilian Buitenhuis

main.cpp

Committer:
CasperK
Date:
2018-11-07
Revision:
7:af586102d80c
Parent:
6:344e075c1221

File content as of revision 7:af586102d80c:

#include "mbed.h"
#include <stdio.h>
#include "QEI.h"
//#include "HIDScope.h"
//#include "MODSERIAL.h"
#include "BiQuad.h"
#include "math.h"
#include "Constants.h"

#define IGNORECOUNT 100
#define PI 3.141592f //65358979323846  // pi 

void yDirection() {
    //direction of the motion
    if (emg0Bool && !emg1Bool) { //if a is pressed and not d, move to the left
        direction = -1;
    }
    else if (!emg0Bool && emg1Bool) { //if d is pressed and not a, move to the right
        direction = 1;
    }

    if (emg0Bool || emg1Bool){  
        //correction from motor 1 to keep x position the same
        
        
        //calculating the motion
        old_y_position = y_position;
        y_position = old_y_position + (0.0003f * direction);
        
            
        old_motor1_angle = motor1_angle;
        motor1_angle = asin( y_position / length )* C1;     //saw tooth motor angle in rad
        if (y_position > 0.29f){
            y_position = 0.29f;
        }
        else if(y_position <0){
            y_position = 0;
        }
        //correction on x- axis
        old_x_correction = x_correction;
        x_correction = old_x_correction - (length * cos(old_motor1_angle / C1)- length * cos(motor1_angle/ C1)); // old x + correction
        if (x_position > 0.35f- x_correction ){
            x_position = 0.35f- x_correction;
        }
        else if (x_position-x_correction < 0.0f){
            x_position = 0.0f;
        }
        
        motor2_angle = ( x_position + x_correction - (length * cos(motor1_angle /C1 ))) / C2;
        //reset the boolean, for demo purposes
        emg2Bool = false; 
    }
}

void xDirection () {      
    //if the button is pressed, reverse the y direction
    if (!button2) {
        x_direction = !x_direction;
        wait(0.5f);
        }     
        
    if (emg2Bool) { //if w is pressed, move up/down
        //direction of the motion
        if (x_direction) { 
            direction = 1.0f;
        }
        else if (!x_direction) {
            direction = -1.0f;
        }
        
        //calculating the motion
        old_x_position = x_position;
        x_position = old_x_position + (0.0003f * direction);
        if (x_position > 0.35f- x_correction ){
            x_position = 0.35f- x_correction;
        }
        else if (x_position-x_correction < 0.0f){
            x_position = 0.0f;
        }

        motor2_angle =( x_position + x_correction - (length * cos( motor1_angle / C1 ))) /C2 ; // sawtooth-gear motor angle in rad
        
        //reset the boolean, for demo purposes
        emg2Bool = false; 
    }
}

void PIDController1() {  
    P1 = motor1.getPulses() / 8400.0f * 2.0f * PI;    //actual motor angle in rad  
    e1 = e2;
    e2 = e3;
    e3 = motor1_angle - P1;
    de3 = (e3-e2)/Timestep;
    ie3 = ie3 + e3*Timestep;
    Output1 = Kp * e3 + Ki * ie3 + Kd * de3;
    
    Y1 = 0.5f * Output1;
    
    if (Y1 >= 1){
        Y1 = 1;
    }
    else if (Y1 <= -1){
        Y1 = -1;
    }       
}

void PIDController2() {   
    P2 = motor2.getPulses() / 8400.0f * 2.0f*PI; // actual motor angle in rad
    f2 = f3;
    f3 = motor2_angle - P2;
    df3 = (f3-f2)/Timestep;
    if3 = if3 + f3*Timestep;
    Output2 = Kp * f3 + Ki * if3 + Kd * df3;
    Y2 = 0.5f * Output2;
     
    if (Y2 >= 1){
        Y2 = 1;
    }
    else if (Y2 <= -1){
        Y2 = -1;
    }   
}

void ControlMotor1() {
    if (Y1 > 0.0f) {
        Y1 = 0.6f * Y1 + 0.4f;
        directionpin1 = false;
    } 
    else if(Y1 < -0.0f){
        Y1 = -0.4f + 0.6f * Y1;
        directionpin1 = true;
    }    

    pwm_value1 = fabs(Y1);
}

void ControlMotor2() {
    if (Y2 > 0.0f) {
        Y2 = 0.6f * Y2 + 0.4f;
        directionpin2 = true;
    } 
    else if(Y2 < -0.0f){
        Y2 = -0.4f + 0.6f * Y2;
        directionpin2 = false;
    }

    pwm_value2 = fabs(Y2);
}

/*
* Calibration functions
*/
void positionCalibration() {
    while(!button1) {
        directionpin2 = true;
        pwmpin1 = 0.5f;
    }
    while(!button2) {
        directionpin1 = true;
        pwmpin2 = 0.4f;
    }
    pwmpin1 = 0.0f;
    pwmpin2 = 0.0f;
    
    if(!next_switch) {
        CurrentState = Movement;
        motor1.reset();
        motor2.reset();
    }
}

void CalibrateEMG0(){          // calibrates only one muscle activities as testing concluded that it was unnessecary to do this for all muscles.
    ledred = !ledred;
    int C = 500;           // half a second at 1000Hz
    double A0=0, A1=0, A2=0, A3=0, A4=0;
    double emg0FiAbs;
    while (C > 0){
        emg0FiAbs = fabs( emg1bqc.step(emg1.read()));
        if (C==500){                        //first instance make all values the first in case this is the highest
            A0=A1=A2=A3=A4=emg0FiAbs;
        }
        else if(emg0FiAbs > A0){            // if there is a higher value change the inputs to be the highest 5
            A4=A3;
            A3=A2;
            A2=A1;
            A1=A0;
            A0=emg0FiAbs;
        }
        C--;
        wait(0.001f);
    }
    Calibration0 = (A0+A1+A2+A3+A4)/5*0.4;  // average of the 5 highest values x 0,4 to create the threshold
    ledred = !ledred;
}  

void CalibrateEMG1(){          // calibrates only one muscle activities as testing concluded that it was unnessecary to do this for all muscles.
    ledred = !ledred;
    int C = 500;           // half a second at 1000Hz
    double A0=0, A1=0, A2=0, A3=0, A4=0;
    double emg1FiAbs;
    while (C > 0){
        emg1FiAbs = fabs( emg1bqc.step(emg1.read()));
        if (C==500){                        //first instance make all values the first in case this is the highest
            A0=A1=A2=A3=A4=emg1FiAbs;
        }
        else if(emg1FiAbs > A0){            // if there is a higher value change the inputs to be the highest 5
            A4=A3;
            A3=A2;
            A2=A1;
            A1=A0;
            A0=emg1FiAbs;
        }
        C--;
        wait(0.001f);
    }
    Calibration1 = (A0+A1+A2+A3+A4)/5*0.4;  // average of the 5 highest values x 0,4 to create the threshold
    ledred = !ledred;
}  

void CalibrateEMG2(){          // calibrates only one muscle activities as testing concluded that it was unnessecary to do this for all muscles.
    ledred = !ledred;
    int C = 500;           // half a second at 1000Hz
    double A0=0, A1=0, A2=0, A3=0, A4=0;
    double emg2FiAbs;
    while (C > 0){
        emg2FiAbs = fabs( emg2bqc.step(emg2.read()));
        if (C==500){                        //first instance make all values the first in case this is the highest
            A0=A1=A2=A3=A4=emg2FiAbs;
        }
        else if(emg2FiAbs > A0){            // if there is a higher value change the inputs to be the highest 5
            A4=A3;
            A3=A2;
            A2=A1;
            A1=A0;
            A0=emg2FiAbs;
        }
        C--;
        wait(0.001f);
    }
    Calibration2 = (A0+A1+A2+A3+A4)/5*0.4;  // average of the 5 highest values x 0,4 to create the threshold
    ledred = !ledred;
}    

/*
* filter functions
*/

bool emg0Filter(void){  
    double emg0filteredAbs = fabs( emg0bqc.step(emg0.read())); // Filter and make absolute, 
    /* this is the threshhold */ 
    if (emg0filteredAbs > Calibration0) {  // when above threshold  set bool to 1, here can the parameters be changed using global variables
        emg0Bool = 1;
        emg0Ignore = IGNORECOUNT;   // here is the counter reset to catch sudden jolts
        }
    else if (emg0Ignore < 0){ // if the ignore-counter is down to zero, set the bool back to 0
         emg0Bool = 0;
        }
    else {
        emg0Ignore--;      // else decrease counter by one each time has passed without threshold being met
        }  
    return emg0Bool;
}

bool emg1Filter(void){  
    double emg1filteredAbs = fabs( emg1bqc.step(emg1.read())); // Filter and make absolute
    /* this is the threshhold */ 
    if (emg1filteredAbs > Calibration1) {  // when above threshold  set bool to 1 here can the parameters be changed using global variables
        emg1Bool = true;
        emg1Ignore = IGNORECOUNT;   // here is the counter increased ( at 1000 Hz, this is 0.1 sec)
        }
    else if (emg1Ignore < 0){ // if the ignore-counter is down to zero, set the bool back to 0
         emg1Bool = false;
        }
    else {
        emg1Ignore--;      // else decrease counter by one each time has passed without threshold being met
        }
    return emg1Bool;
}

bool emg2Filter(void){  
    double emg2filteredAbs = fabs( emg2bqc.step(emg2.read())); // Filter and make absolute
    /* this is the threshhold */ 
    if (emg2filteredAbs > Calibration2) {  // when above threshold  set bool to 1 here can the parameters be changed using global variables
        emg2Bool = true;
        emg2Ignore = IGNORECOUNT;   // here is the counter increased ( at 1000 Hz, this is 0.1 sec)
        }
    else if (emg2Ignore < 0){ // if the ignore-counter is down to zero, set the bool back to 0
        emg2Bool = false;
        }
    else {
        emg2Ignore--;      // else decrease counter by one each time has passed without threshold being met
        }

    return emg2Bool;
}

void sample() {  
    //run the filter functions for each emg signal
    emg0Filter();
    emg1Filter();
    emg2Filter();
}

/*
* main function for the emg calibration
*/
void emgCalibration() {
    // combining biquad chains is done in main, before the ticker
    emg0bqc.add( &emg0bq1 ).add( &emg0bq2 ).add ( &emg0bq3 );       
    emg1bqc.add( &emg1bq1 ).add( &emg1bq2 ).add ( &emg1bq3 );
    emg2bqc.add( &emg2bq1 ).add( &emg2bq2 ).add ( &emg2bq3 );
    
    bool u = true;
    CalibrationState = EMG0;
    while (u){                      // !!! this is a place holder for the calibration!!! 
        switch(CalibrationState) {
            case EMG0:
            if(!next_switch) {   
                CalibrateEMG0(); // calibration function
                CalibrationState = EMG1;
            }
            break;
            case EMG1:
            if(!next_switch) {   
                CalibrateEMG1(); // calibration function
                CalibrationState = EMG2;
            }
            break;
            case EMG2:
            if(!next_switch) {      
                CalibrateEMG2(); // calibration function
                CurrentState = PositionCalibration;
                sample_timer.attach(&sample, 0.001);    //ticker at 1000Hz
                ledred = false;     // to indicate filter is working
                ledgreen = false;
                ledblue = false;
                u = false;
            }
            break;
        }
    }     
}

/*
* function that runs the Kinematics and PID controller
*/
void movement() {
    xDirection(); //call the function to move in the y direction
    yDirection(); //call the function to move in the x direction
    PIDController1();
    PIDController2();
    ControlMotor1();
    ControlMotor2();
}

void move_motors() {
    // assign the caltulates pwm values to the pwm pins
    pwmpin1 = pwm_value1;
    pwmpin2 = pwm_value2;
}

/*
* the "main" function that runs the state machine
*/
void StateFunction() {
    switch(CurrentState) {            
    case EmgCalibration:
    ledgreen = false; //turn on the green light
    emgCalibration();
    break;
    
    case PositionCalibration:
    ledred = true;
    ledgreen = true;
    ledblue = false; //turn on the blue light
    positionCalibration();
    break;
    
    case Movement:
    ledred = true;
    ledgreen = false; //turn on a cyan light
    ledblue = false;
    movement();
    break;
    }
}

void KillInterrupt() {
    //detach all the tickers
    MotorsTicker.detach();
    sample_timer.detach();
    stateticker.detach();
    
    //turn off the motors
    pwm_value1 = 0.0f;
    pwm_value2 = 0.0f;
    pwmpin1 = 0.0f;
    pwmpin2 = 0.0f;
    
    //burn the red light
    ledred = false;
    ledgreen = true;
    ledblue = true;
    
    bool b = true;
    
    //have the program get stuck in a while loop 
    //to be sure that when an interrupt changes the pwmvalues somehow, they get turned off immediately
    while(b){ 
        pwm_value1 = 0;
        pwm_value2 = 0;
        if (!next_switch){
            CurrentState = PositionCalibration;
            b = false;
        }
    }
    wait(1.5f);
    MotorsTicker.attach(&move_motors, Timestep);
    stateticker.attach(StateFunction, Timestep);
}

int main()
{
    pwmpin1.period_us(60);
    pwmpin2.period_us(60);
    directionpin1 = true;
    directionpin2 = true;
    
    ledred = true;
    ledgreen = true;
    ledblue = true;
        
    kill_switch.fall(KillInterrupt); //attach the kill switch to the KillInterrupt function
    MotorsTicker.attach(&move_motors, Timestep); //ticker at 50Hz
    stateticker.attach(StateFunction, Timestep);

    CurrentState = EmgCalibration; //start at the calibration state
    
    while (true) {      
    }
}