Dependencies:   mbed QEI HIDScope biquadFilter MODSERIAL FastPWM

Files at this revision

API Documentation at this revision

Comitter:
PatrickZieverink
Date:
Wed Oct 30 15:27:06 2019 +0000
Commit message:
Werkend model zonder boundaries 16:27;

Changed in this revision

FastPWM.lib Show annotated file Show diff for this revision Revisions of this file
HIDScope.lib Show annotated file Show diff for this revision Revisions of this file
MODSERIAL.lib Show annotated file Show diff for this revision Revisions of this file
QEI.lib Show annotated file Show diff for this revision Revisions of this file
biquadFilter.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FastPWM.lib	Wed Oct 30 15:27:06 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/Sissors/code/FastPWM/#c0b2265cff9c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HIDScope.lib	Wed Oct 30 15:27:06 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/tomlankhorst/code/HIDScope/#d23c6edecc49
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL.lib	Wed Oct 30 15:27:06 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/Sissors/code/MODSERIAL/#d2a5e26fd658
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QEI.lib	Wed Oct 30 15:27:06 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/aberk/code/QEI/#5c2ad81551aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/biquadFilter.lib	Wed Oct 30 15:27:06 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/tomlankhorst/code/biquadFilter/#26861979d305
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Oct 30 15:27:06 2019 +0000
@@ -0,0 +1,586 @@
+/*
+To-do:
+    calibration with reverse kinematics
+    EMG calibration upper bound
+
+    Homing
+    Turning the magnet on/off (reading magnet status?)
+    Gravity compensation (if necessary)
+    PID values (too large)
+    Boundaries (after verification of the PID values)
+*/
+#include "mbed.h"
+#include "MODSERIAL.h"
+#include "FastPWM.h"
+#include "QEI.h"
+#include "HIDScope.h"
+#include "BiQuad.h"
+#define PI 3.14159265
+
+Serial pc(USBTX, USBRX);            //connect to pc
+HIDScope scope(5);                  //HIDScope instance
+DigitalOut motor0_direction(D7);    //rotation motor 1 on shield (always D6)
+FastPWM motor0_pwm(D6);             //pwm 1 on shield (always D7)
+DigitalOut motor1_direction(D4);    //rotation motor 2 on shield (always D4)
+FastPWM motor1_pwm(D5);             //pwm 2 on shield (always D5)
+Ticker loop_ticker;                 //used in main()
+Ticker scope_ticker;
+InterruptIn but1(SW2);              //button on mbed board
+InterruptIn but2(D9);               //debounced button on biorobotics shield
+InterruptIn but3(D8);               //button 1 on bio shield
+AnalogIn EMG0_sig(A0);
+AnalogIn EMG0_ref(A1);
+AnalogIn EMG1_sig(A2);
+AnalogIn EMG1_ref(A3);
+
+//Joystick test
+
+float direction_switch;
+
+void check_failure();
+int schmitt_trigger(float i);
+
+QEI enc1 (D11, D12, NC, 8400, QEI::X4_ENCODING); //encoder 1 gebruiken
+QEI enc2 (D1,  D2,  NC, 8400, QEI::X4_ENCODING); //encoder 2 gebruiken
+
+BiQuad bq1_1 (0.881889334678067,    -1.76377866935613,    0.8818893346780671,   -1.77069673005903, 0.797707797506027);
+BiQuad bq1_2 (8.05339325492925e-06, 1.61067865098585e-05, 8.05339325492925e-06, -1.99254216118629, 0.992574747774901);
+BiQuad bq2_1 (0.881889334678067,    -1.76377866935613,    0.8818893346780671,   -1.77069673005903, 0.797707797506027);
+BiQuad bq2_2 (8.05339325492925e-06, 1.61067865098585e-05, 8.05339325492925e-06, -1.99254216118629, 0.992574747774901);
+
+//variables
+enum States {s_idle, s_cali_EMG_low, s_cali_EMG_high, s_cali_enc, s_moving_magnet_off, s_moving_magnet_on, s_homing, s_failure};
+States state;                       //using the States enum
+struct actuator_state {
+    float duty_cycle[2];            //pwm of 1st motor
+    bool direction[2];               //direction of 1st motor
+    int default_direction[2];
+    bool magnet;                    //state of the magnet
+} actuator;
+
+struct EMG_params {
+    float max[2];  //params of the emg, tbd during calibration
+    float min[2];
+} EMG;
+
+struct PID_struct {
+    float P[2];
+    float I[2];
+    float D[2];
+    float I_counter[2];
+} PID;
+
+float dt = 0.001;
+float theta[2]; //theta0 = rot, theta1 = lin
+int enc_zero[2] = {0, 0};//the zero position of the encoders, to be determined from the encoder calibration
+float EMG_raw[2][2];
+float EMG_filtered[2];
+int enc_value[2];
+float current_error[2] = {0.0, 0.0};
+float errors[2];
+float last_error[2] = {0.0, 0.0};
+float action[2];
+bool state_changed = false; //used to see if the state is "starting"
+volatile bool button1_pressed = false;
+volatile bool button2_pressed = false;
+volatile bool button3_pressed = false;
+volatile bool failure_occurred = false;
+bool EMG_low_has_been_calibrated;
+bool EMG_high_has_been_calibrated;
+float filter_value[2];
+int speed[2];
+int past_speed[2] = {0, 0};
+float velocity_desired[2];
+float theta_desired[2];
+const int EMG_cali_amount = 1000;
+float past_EMG_values[2][EMG_cali_amount];
+int past_EMG_count = 0;
+
+void do_nothing()
+/*
+    Idle state. Used in the beginning, before the calibration states.
+*/
+{
+    if (button1_pressed) {
+        state_changed = true;
+        state = s_cali_EMG_low;
+        button1_pressed = false;
+    }
+    errors[0] = 0.0;
+    errors[1] = 0.0;
+}
+
+void failure()
+
+//    Failure mode. This should execute when button 2 is pressed during operation.
+
+{
+    if (state_changed) {
+        pc.printf("Something went wrong!\r\n");
+        state_changed = false;
+    }
+    errors[0] = 0.0;
+    errors[1] = 0.0;
+    PID.I_counter[0] = 0.0;
+    PID.I_counter[1] = 0.0;
+}
+void cali_EMG_low()
+/*
+    Calibration of the EMG. Values determined during calibration should be
+    added to the EMG_params instance.
+*/
+{
+    if (state_changed) {
+        pc.printf("Started low value EMG calibration\r\nTime is %i\r\n", us_ticker_read());
+        state_changed = false;
+    }
+    if (past_EMG_count < EMG_cali_amount) {
+        past_EMG_values[0][past_EMG_count] = EMG_filtered[0];
+        past_EMG_values[1][past_EMG_count] = EMG_filtered[1];
+        past_EMG_count++;
+    } else if (!EMG_low_has_been_calibrated) { //calibration has concluded
+        pc.printf("Low value calibration concluded.\r\nTime is %i\r\n", us_ticker_read());
+        float sum[2] = {0.0, 0.0};
+        float mean[2] = {0.0, 0.0};
+        for(int c = 0; c<2; c++) {
+            for(int i = 0; i<EMG_cali_amount; i++) {
+                sum[c] += past_EMG_values[c][i];
+            }
+            mean[c] = sum[c]/(float)EMG_cali_amount;
+            EMG.min[c] = mean[c];
+        }
+
+        //calibration done
+        pc.printf("Low EMG values: %f %f\r\n", EMG.min[0], EMG.min[1]);
+        EMG_low_has_been_calibrated = true;
+    }
+    errors[0] = 0.0;
+    errors[1] = 0.0;
+    PID.I_counter[0] = 0.0;
+    PID.I_counter[1] = 0.0;
+    if (button1_pressed && EMG_low_has_been_calibrated) {// move to high value calibration
+        button1_pressed = false;
+        state = s_cali_EMG_high;
+        state_changed = true;
+    }
+}
+
+void cali_EMG_high()
+/*
+    Calibration of the EMG. Values determined during calibration should be
+    added to the EMG_params instance.
+*/
+{
+    if (state_changed) {
+        pc.printf("Started high value EMG calibration\r\nTime is %i\r\n", us_ticker_read());
+        state_changed = false;
+        past_EMG_count = 0;
+    }
+    if (past_EMG_count < EMG_cali_amount) {
+        past_EMG_values[0][past_EMG_count] = EMG_filtered[0];
+        past_EMG_values[1][past_EMG_count] = EMG_filtered[1];
+        past_EMG_count++;
+    } else { //calibration has concluded
+        pc.printf("High value calibration concluded.\r\nTime is %i\r\n", us_ticker_read());
+        float max[2] = {0.0, 0.0};
+        for(int c = 0; c<2; c++) {
+            for(int i = 0; i<EMG_cali_amount; i++) {
+                if (past_EMG_values[c][i] > max[c]) {
+                    max[c] = past_EMG_values[c][i];
+                }
+            }
+            EMG.max[c] = max[c];
+        }
+
+        //calibration done, moving to cali_enc
+        pc.printf("High EMG values: %f %f\r\n", EMG.max[0], EMG.max[1]);
+        EMG_high_has_been_calibrated = true;
+        state_changed = true;
+        state = s_cali_enc;
+    }
+    errors[0] = 0.0;
+    errors[1] = 0.0;
+    PID.I_counter[0] = 0.0;
+    PID.I_counter[1] = 0.0;
+}
+void cali_enc()
+/*
+    Calibration of the encoder. The encoder should be moved to the lowest
+    position for the linear stage and the horizontal postition for the
+    rotating stage.
+*/
+{
+    if (state_changed) {
+        pc.printf("Started encoder calibration\r\n");
+        state_changed = false;
+    }
+    if (button1_pressed) {
+        pc.printf("Encoder has been calibrated\r\n");
+        enc_zero[0] = enc_value[0];
+        enc_zero[1] = enc_value[1];//+130990; //the magic number!
+        button1_pressed = false;
+        state = s_moving_magnet_off;
+        state_changed = true;
+    }
+    errors[0] = 1.0e-5*speed[0];
+    errors[1] = 1.0e-5*speed[1];
+}
+
+void moving_magnet_off()
+/*
+    Moving with the magnet disabled. This is the part from the home position
+    towards the storage of chips.
+*/
+{
+    if (state_changed) {
+        pc.printf("Moving without magnet\r\n");
+        state_changed = false;
+    }
+    theta_desired[0] = theta_desired[0] + dt*(velocity_desired[1]*cos(theta[0])/theta[1] - velocity_desired[0]*sin(theta[0])/theta[1]);
+    theta_desired[1] = theta_desired[1] + dt*(velocity_desired[0]*cos(theta[0]) + velocity_desired[1]*sin(theta[0]));
+    errors[0] = theta_desired[0] - theta[0];
+    errors[1] = theta_desired[1] - theta[1];
+    if (button2_pressed) {
+        pc.printf("positions: (rad, m): %f %f\r\n"
+                  "Errors:              %f %f\r\n"
+                  "Previous actions:    %f %f\r\n"
+                  "Vx, Vy:              %f %f\r\n", theta[0], theta[1], errors[0], errors[1], actuator.duty_cycle[0], actuator.duty_cycle[1], velocity_desired[0],velocity_desired[1]);
+        button2_pressed = false;
+    }
+}
+
+void moving_magnet_on()
+
+//    Moving with the magnet enabled. This is the part of the movement from the
+//    chip holder to the top of the playing board.
+
+{
+    if (state_changed) {
+        pc.printf("Moving with magnet\r\n");
+        state_changed = false;
+    }
+    theta_desired[0] = theta_desired[0] + dt*(velocity_desired[1]*cos(theta[0])/theta[1] - velocity_desired[0]*sin(theta[0])/theta[1]);
+    theta_desired[1] = theta_desired[1] + dt*(velocity_desired[0]*cos(theta[0]) + velocity_desired[1]*sin(theta[0]));
+    errors[0] = theta_desired[0] - theta[0];
+    errors[1] = theta_desired[1] - theta[0];
+}
+void homing()
+
+//    Dropping the chip and moving towards the rest position.
+
+{
+    if (state_changed) {
+        pc.printf("Started homing\r\n");
+        state_changed = false;
+    }
+    theta_desired[0] = theta_desired[0] + dt*(velocity_desired[1]*cos(theta[0])/theta[1] - velocity_desired[0]*sin(theta[0])/theta[1]);
+    theta_desired[1] = theta_desired[1] + dt*(velocity_desired[0]*cos(theta[0]) + velocity_desired[1]*sin(theta[0]));
+    errors[0] = theta_desired[0] - theta[0];
+    errors[1] = theta_desired[1] - theta[0];
+}
+
+void measure_signals()
+{
+    //only one emg input, reference and plus
+    EMG_raw[0][0] = EMG0_sig.read();
+    EMG_raw[0][1] = EMG0_ref.read();
+    EMG_raw[1][0] = EMG1_sig.read();
+    EMG_raw[1][1] = EMG1_ref.read();
+    filter_value[0] = fabs(bq1_2.step(fabs(bq1_1.step(EMG_raw[0][0] - EMG_raw[0][1]))));
+    filter_value[1] = fabs(bq2_2.step(fabs(bq2_1.step(EMG_raw[1][0] - EMG_raw[1][1]))));
+
+    enc_value[0] = enc1.getPulses();
+    enc_value[1] = enc2.getPulses();
+
+    for(int c = 0; c < 2; c++) {
+        if (EMG_high_has_been_calibrated && EMG_low_has_been_calibrated) {
+            EMG_filtered[c] = (filter_value[c]-EMG.min[c])/(EMG.max[c]-EMG.min[c]);
+        } else {
+            EMG_filtered[c] = filter_value[c];
+        }
+        enc_value[c] -= enc_zero[c];
+        theta[c] = (float)(enc_value[c])/(float)(8400)*2*PI;    //Revoluties        Theta 0 en 1 zijn de gemeten waardes hier!
+    }
+    
+    if (EMG_high_has_been_calibrated && EMG_low_has_been_calibrated){
+        if (EMG_filtered[0] > EMG_filtered[1]){
+            EMG_filtered[1] = 0;
+        }
+        else {
+            EMG_filtered[0] = 0;
+        }  
+    }
+    
+    if(button3_pressed) {
+        direction_switch = -1.0*direction_switch;
+        button3_pressed = false;
+    } 
+    
+    
+    theta[1] = theta[1]*(5.05*0.008/2.0/PI)+0.63;
+    theta[0] = theta[0]*-1.0;
+    
+    float y = theta[1]*sin(theta[0]);         // quick y calculation
+
+    for(int c = 0; c<2; c++) {
+        
+        speed[c] = schmitt_trigger(EMG_filtered[c]);
+        if (speed[c] == -1) {
+            speed[c] = past_speed[c];
+        }
+        past_speed[c] = speed[c];
+        if (speed[c] == 0) {
+            velocity_desired[c] = 0.00f*direction_switch;
+        }
+        if (speed[c] == 1) {
+            velocity_desired[c] = 0.01f*direction_switch;
+        }
+        if (speed[c] == 2) {
+            velocity_desired[c] = 0.02f*direction_switch;
+        }
+         
+        // Joystick beweging, comment dan de EMG zooi!
+        /*
+        if ( c == 0){
+            velocity_desired[c] = (vrx.read()*100-50)/50*0.02;
+            if (velocity_desired[c] < 0.002 && velocity_desired[c] > -0.002){
+                velocity_desired[c] = 0;
+            }
+        }
+        if ( c == 1){
+            velocity_desired[c] = (vry.read()*100-50)/50*0.02;
+            if (velocity_desired[c] < 0.002 && velocity_desired[c] > -0.002){
+                velocity_desired[c] = 0;
+            }
+        }
+        */
+    }
+}
+
+
+void motor_controller()   //s_idle, s_cali_EMG, s_cali_enc, s_moving_magnet_off, s_moving_magnet_on, s_homing, s_failure
+{
+    float P_action[2];
+    float I_action[2];
+    float D_action[2];
+    float mg;
+    float duty_mg;
+    float velocity_estimate[2];
+    for (int c = 0; c<2; c++) {
+
+        //P action
+        P_action[c] = PID.P[c] * errors[c];
+
+        //I part
+        PID.I_counter[c] += errors[c]*dt;
+        if (PID.I_counter[c] > 0.05) {
+            PID.I_counter[c] = 0.05;
+        }
+        if (PID.I_counter[c] < -0.05) {
+            PID.I_counter[c] = -0.05;
+        }
+        I_action[c] = PID.I_counter[c] * PID.I[c];
+
+        //D part
+        velocity_estimate[c] = (errors[c]-last_error[c])/dt; //estimate of the time derivative of the error
+        D_action[c] = velocity_estimate[c] * PID.D[c];
+
+        action[c] = (P_action[c] + I_action[c] + D_action[c])/dt;
+        last_error[c] = errors[c];
+    }
+    
+    if (theta[0]> 50){
+        mg =  0;
+    } else { mg = (theta[1]-0.125)*9.81*0.200*cos(theta[0]*1.75); }        // zwaartekracht compensatie
+    duty_mg = mg/10.0;
+    action[0] += duty_mg;
+
+    for (int c = 0; c<2; c++) {
+        actuator.direction[c] = (action[c] > 0); //1 if action is positive, 0 otherwise
+        actuator.duty_cycle[c] = fabs(action[c]);
+        if (actuator.duty_cycle[c] > 1.0f) {
+            actuator.duty_cycle[c] = 1.0f;
+        }
+        if (actuator.duty_cycle[c] < 0.0f) {
+            actuator.duty_cycle[c] = 0.0f;
+        }
+    }
+}
+
+void output()
+{
+    for (int c = 0; c <2; c++) {
+        if (actuator.default_direction[c] == false) {
+            if (actuator.direction[c] == 1) {
+                actuator.direction[c] = 0;
+            } else {
+                actuator.direction[c] = 1;
+            }
+        } else {
+            if (actuator.direction[c] == 1) {
+                actuator.direction[c] = 1;
+            } else {
+                actuator.direction[c] = 0;
+            }
+        }
+    }
+
+    motor0_direction = actuator.direction[0];
+    motor1_direction = actuator.direction[1];
+    motor0_pwm.write(actuator.duty_cycle[0]);
+    motor1_pwm.write(actuator.duty_cycle[1]);
+
+    scope.set(0, EMG_filtered[0]);
+    scope.set(1, EMG_filtered[1]);
+    scope.set(2, actuator.duty_cycle[0]);
+    scope.set(3, actuator.duty_cycle[1]);
+    scope.set(4, speed[0]);
+    scope.set(5, speed[1]);
+    /*
+    scope.set(0, actuator.duty_cycle[1]);
+    scope.set(1, theta[1]);
+    scope.set(2, theta_desired[1]);
+    scope.set(3, actuator.duty_cycle[0]);
+    scope.set(4, theta[0]);
+    scope.set(5, errors[0]);
+    */
+}
+
+void state_machine()
+{
+    check_failure(); //check for an error in the last loop before state machine
+    //run current state
+    switch (state) {
+        case s_idle:
+            do_nothing();
+            break;
+        case s_failure:
+            failure();
+            break;
+        case s_cali_EMG_low:
+            cali_EMG_low();
+            break;
+        case s_cali_EMG_high:
+            cali_EMG_high();
+            break;
+        case s_cali_enc:
+            cali_enc();
+            break;
+        case s_moving_magnet_on:
+            moving_magnet_on();
+            break;
+        case s_moving_magnet_off:
+            moving_magnet_off();
+            break;
+        case s_homing:
+            homing();
+            break;
+    }
+}
+
+void main_loop()
+{
+    measure_signals();
+    state_machine();
+    motor_controller();
+    output();
+
+}
+
+//Helper functions, not directly called by the main_loop functions or
+//state machines
+void check_failure()
+{
+    if (failure_occurred) {
+        state = s_failure;
+        state_changed = true;
+    }
+}
+
+void but1_interrupt()
+{
+    if(!but2.read() || !but3.read()) {//both buttons are pressed
+        failure_occurred = true;
+    }
+    button1_pressed = true;
+    pc.printf("Button 1 pressed!\n\r");
+}
+
+void but2_interrupt()
+{
+    if(!but1.read() || !but3.read()) {//both buttons are pressed
+        failure_occurred = true;
+    }
+    button2_pressed = true;
+    pc.printf("Button 2 pressed!\n\r");
+}
+
+void but3_interrupt()
+{
+    if(!but1.read() || !but2.read()) {
+        failure_occurred = true;
+    }
+    button3_pressed = true;
+    
+    pc.printf("Button 3 pressed!\n\r");
+}
+
+int schmitt_trigger(float i)
+{
+    int speed;
+    speed = -1; //default value, this means the state should not change
+    if (i >  0.000f && i < 0.125f) {
+        speed = 0;
+    }
+    if (i >  0.250f && i < 0.375f) {
+        speed = 1;
+    }
+    if (i >  0.500f && i < 1.000f) {
+        speed = 2;
+    }
+    return speed;
+}
+
+int main()
+{
+    pc.baud(115200);
+    pc.printf("Executing main()... \r\n");
+    state = s_idle;                         // Hij slaat nu dus de calibratie over!
+
+    motor0_pwm.period(1.0f/160000.0f);      // 1/frequency van waarop hij draait
+    motor1_pwm.period(1.0f/160000.0f);      // 1/frequency van waarop hij draait
+
+    actuator.direction[0] = 0;
+    actuator.direction[1] = 0;
+
+    actuator.default_direction[0] = true;
+    actuator.default_direction[1] = false;          // dit is gedubbelcheckt!
+
+    direction_switch = 1.0;
+
+    PID.P[0] = 1.75;
+    PID.P[1] = 0.5;
+    PID.I[0] = 0.0;
+    PID.I[1] = 0.0;
+    PID.D[0] = 0.005;
+    PID.D[1] = 0.0;
+    PID.I_counter[0] = 0.0;
+    PID.I_counter[1] = 0.0;
+
+    theta_desired[0] = 0.0;
+    theta_desired[1] = 0.63;
+
+    actuator.magnet = false;
+    EMG.max[0] = 0.01;
+    EMG.max[1] = 0.01;
+
+    but1.fall(&but1_interrupt);
+    but2.fall(&but2_interrupt);
+    but3.fall(&but3_interrupt);
+    scope_ticker.attach(&scope, &HIDScope::send, 0.05);
+    loop_ticker.attach(&main_loop, dt); //main loop at 1kHz
+    pc.printf("Main_loop is running\n\r");
+    while (true) {
+        wait(0.1f);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Oct 30 15:27:06 2019 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/mbed_official/code/mbed/builds/d1b4690b3f8b
\ No newline at end of file