fancy lampje

Dependencies:   mbed QEI HIDScope biquadFilter MODSERIAL FXOS8700Q FastPWM

main.cpp

Committer:
jobjansen
Date:
2019-10-21
Revision:
20:0f6a88f29a71
Parent:
17:d1acb6888b82
Child:
21:a316452da8cd

File content as of revision 20:0f6a88f29a71:

#include "mbed.h"
#include "HIDScope.h"
#include "QEI.h"
#include "MODSERIAL.h"
#include "BiQuad.h"
#include "FastPWM.h"
#include <iostream>
MODSERIAL pc(USBTX, USBRX);
AnalogIn ain2(A2);
AnalogIn ain1(A3);
DigitalOut dir2(D4);
DigitalOut dir1(D7);
//D4,D7 direction of motors 2,1 on board, D5,D6- PWM of motors 2,1 on board
PwmOut motor2_pwm(D5);
PwmOut motor1_pwm(D6);
AnalogIn    emg0( A0 );
AnalogIn    emg1( A1 );
QEI motor1 (D8, D9, NC, 32);
QEI motor2 (D12, D13, NC, 32);

Ticker ticktick;
Timer state_time;
Timeout EMG_peak;
Timeout turn;
Ticker      sample_timer;
HIDScope    scope( 2);
DigitalOut  ledred(LED_RED);
DigitalOut  ledblue(LED_BLUE);
DigitalOut  ledgreen(LED_GREEN);
InterruptIn err(SW2);
InterruptIn button(SW3);

volatile float P0;
volatile float P1;
volatile float EMG_min0=1;
volatile float EMG_max0=0;
volatile float EMG_min1=1;
volatile float EMG_max1=0;
volatile bool ignore_peaks=false;
volatile bool ignore_turn=true;
enum states{Waiting,Position_calibration,EMG_calibration,Homing,Operating,Demo,Failure};
states currentState=Waiting;

static float v_refx; //reference speeds 
static float v_refy;
static float T = 0.002; //measuring period
static float L = 0.35;  //distance between the motor axles, has to be checked
volatile float theta1_old = 0.0; //feedback variables
volatile float theta2_old = 0.0;

int m1_count; 
float m1_angle;
int m2_count;
float m2_angle;
float pi = 3.14;

float error_x;
float error_y;

//PID controller variables
bool q = true;
float error_prev;
static float Kp = 8;
static float Ki = 1.02;
static float Kd = 0.1;
float u;
float u_p;
float u_i;
float u_d;
float error_integral = 0.0;

float u_1;
float u_2;

void read_emg()
{
    //EMG signal 0
    static int count0=0;
    static float RMS_value0=0;
    static float HighPass_value0=0;
    count0+=1;
    static float RMS0[150];
    static float HighPass0[30];
    static BiQuad Notch0(0.9695f,-1.5695f,0.9695f,-1.5695f,0.9391f);
    static BiQuad Notch1(0.9695f,-1.5695f,0.9695f,-1.5695f,0.9391f);
    float I0;
    float If0;
    //signal 1
    static int count1=0;
    static float RMS_value1=0;
    static float HighPass_value1=0;
    count1+=1;
    static float RMS1[150];
    static float HighPass1[30];
    float I1;
    float If1;
    I0=emg0.read(); //read signal
    double notched0=Notch0.step(I0);
    HighPass_value0+=(notched0-HighPass0[count0%30])/30.0;
    HighPass0[count0%30]=notched0;
    If0=pow(I0-HighPass_value0,2.0f); // Highpass-filtered value squared
    RMS_value0+=(If0-RMS0[count0%150])/150.0;
    RMS0[count0%150]=If0;
    /* Set the sampled emg values in channel 0 (the first channel) and 1 (the second channel) in the 'HIDScope' instance named 'scope' */
    P0=sqrt(RMS_value0);
    I1=emg1.read(); //read signal
    double notched1=Notch1.step(I1);
    HighPass_value1+=(notched1-HighPass1[count1%30])/30.0;
    HighPass1[count1%30]=notched1;
    If1=pow(I1-HighPass_value1,2.0f); // Highpass-filtered value squared
    RMS_value1+=(If1-RMS1[count1%150])/150.0;
    RMS1[count1%150]=If1;
    /* Set the sampled emg values in channel 0 (the first channel) and 1 (the second channel) in the 'HIDScope' instance named 'scope' */
    P1=sqrt(RMS_value1);
    scope.set(0, P0 ); // send root mean squared
    scope.set(1, notched0);
    /* Repeat the step above if required for more channels of required (channel 0 up to 5 = 6 channels) 
    *  Ensure that enough channels are available (HIDScope scope( 2 ))
    *  Finally, send all channels to the PC at once */
    scope.send();
    /* To indicate that the function is working, the LED is toggled */
    ledred=1;
    ledgreen=0;
    ledblue=1;
}

void record_min_max(void)
{
    float t=state_time.read();
    if(t>0.4)
    {
        if(P0<EMG_min0)
        {
            EMG_min0=P0;
        }
        else if(P0>EMG_max0)
        {
            EMG_max0=P0;
        }
        if(P1<EMG_min1)
        {
            EMG_min1=P1;
        }
        else if(P1>EMG_max1)
        {
            EMG_max1=P1;
        }
    }
}

void unignore_peaks(void)
{
    ignore_peaks=false;
}
void start_ignore_turn(void)
{
    ignore_turn=true;
}

void set_PWM(void)
{
   static bool motor_on=false;
   float Q0;
   Q0=2.0f*(P0-(EMG_min0+EMG_max0)/2.0f)/(EMG_max0-EMG_min0);
   if (Q0>-0.2f && !ignore_peaks)
   {
        if (motor_on)
        {
            motor1_pwm.write(0.0f);
            EMG_peak.attach(unignore_peaks,0.8);
            turn.attach(start_ignore_turn,1);
            ignore_turn=false;
            ignore_peaks=true;
            motor_on=false;
        }
        else if(ignore_turn)
        {
            motor1_pwm.write(1.0f);
            EMG_peak.attach(unignore_peaks,0.8);
            turn.attach(start_ignore_turn,1);
            ignore_turn=false;
            ignore_peaks=true;
            motor_on=true;
        }
        else
        {
            motor1_pwm.write(1.0f);
            dir1=!dir1;
            EMG_peak.attach(unignore_peaks,1.5);
            ignore_peaks=true;
            motor_on=true;
        } 
    }
   motor2_pwm.write(ain1.read());
}

void error(void){
    m1_count = motor1.getPulses();                                                                                  // Read out both motor counts and convert them to rads.
    m1_angle = (float)m1_count / 4200.0f * (2.0f * pi);
    m2_count = motor2.getPulses();
    m2_angle = (float)m1_count / 4200.0f * (2.0f * pi);
    
    float dvd=L * (tan(m2_angle) * pow(tan(m1_angle),2) - tan(m1_angle) * pow(tan(m2_angle),2));

    float a =  (  -pow(cos(m1_angle),2) * pow((tan(m1_angle)+tan(m2_angle)),2) * pow(tan(m1_angle),2)  )  / dvd;             // inverse matrix components of differential x and differential y
    float b =  (   pow(cos(m1_angle),2) * pow((tan(m1_angle)+tan(m2_angle)),2) * tan(m1_angle)         )  / dvd; 
    float c =  (   pow(cos(m2_angle),2) * pow((tan(m1_angle)+tan(m2_angle)),2) * pow(tan(m2_angle),2)  )  / dvd;
    float d =  (   pow(cos(m2_angle),2) * pow((tan(m1_angle)+tan(m2_angle)),2) * pow(tan(m2_angle),2)  )  / dvd;
    
    float theta1_dot = a*v_refx + b*v_refy;
    float theta2_dot = c*v_refx + d*v_refy;
    
    float theta1_ref = theta1_old+theta1_dot*T;
    float theta2_ref = theta2_old+theta2_dot*T;
    
    error_x = m1_angle - theta1_ref;
    error_y = m2_angle - theta2_ref;
    
    theta1_old = theta1_ref;
    theta2_old = theta2_ref;
    
    }
    
float PID(float err){
    //P action
    u_p = Kp * fabs(err);
    
    //I action 
    error_integral = error_integral + err * T;
    u_i = Ki * error_integral;
    
/*    
    //D action
    if(q){
        error_prev = err;
        q=false;
        }
        
    float error_derivative = (err - error_prev)/T;
    float filtered_error_derivative = LowPassFilter.step(error_derivative);
    u_d = Kd * filtered_error_derivative;
    error_prev = err;
    
*/
    
    u = u_p + u_i;
    
    return u;
    }
    
void setPWM_controller(void){
    u_1 = PID(error_x);
    u_2 = PID(error_y);
    
    if(u_1 < 0.0f){
        dir1 = 1;
    }else{
        dir1 = 0;
        }
    motor1_pwm.write(fabs(u_1));
    
    if(u_2 < 0.0f){
        dir2 = 1;
    }else{
        dir2 = 0;
        }
    motor2_pwm.write(fabs(u_1));
    
    }

void sample()
{
    switch(currentState)
    {
        case Waiting:
            ledred=0;
            ledgreen=0;
            ledblue=1;
            break;
        case Position_calibration:
            ledred=1;
            ledgreen=1;
            ledblue=0;
            break;
        case EMG_calibration:
            ledred=1;
            ledgreen=0;
            ledblue=0;
            read_emg();
            record_min_max();
            break;
        case Homing:
            ledred=0;
            ledgreen=1;
            ledblue=0;
            break;
        case Operating:
            read_emg();
            set_PWM();
            ledred=1;
            ledgreen=0;
            ledblue=1;
            break;
        case Demo:
            ledred=0;
            ledgreen=0;
            ledblue=0;
            break;
        case Failure:
            ledred=0;
            ledgreen=1;
            ledblue=1;
            motor1_pwm.write(0.0);
            motor2_pwm.write(0.0);
            break;
    }
}

void error_occur()
{
    currentState=Failure;
}

void button_press() 
//Press button to change state
{
    state_time.start();
    switch(currentState)
    {
        case Waiting:
            currentState=Position_calibration;
            wait(1);
            break;
        case Position_calibration:
            currentState=EMG_calibration;
            wait(1);
            break;
        case EMG_calibration:
            currentState=Homing;
            wait(1);
            break;
        case Homing:
            currentState=Operating;
            wait(1);
            break;
        case Operating:
            currentState=Demo;
            wait(1);
            break;
        case Demo:
            currentState=Operating;
            wait(1);
            break;
    }
}

int main()
{
    pc.baud(115200);
    pc.printf("Starting...");
    ledred=0;
    sample_timer.attach(&sample, 0.002);
    err.fall(error_occur);
    button.fall(button_press);
    int frequency_pwm=10000;
    motor1_pwm.period(1.0/frequency_pwm);
    motor2_pwm.period(1.0/frequency_pwm);
    
    while (true) {
        wait(10);
    }

}