control for robotic arm that can play chess using a granular gripper

Dependencies:   Encoder mbed HIDScope Servo MODSERIAL

Fork of chessRobot by a steenbeek

Files at this revision

API Documentation at this revision

Comitter:
bjornnijhuis
Date:
Mon Oct 19 12:34:11 2015 +0000
Parent:
70:20e1a73ab134
Child:
72:507d6e76988a
Commit message:
Included EMG processing

Changed in this revision

EMG.cpp Show annotated file Show diff for this revision Revisions of this file
EMG_check.cpp Show diff for this revision Revisions of this file
EMG_check.h Show diff for this revision Revisions of this file
EMG_filter.cpp Show diff for this revision Revisions of this file
EMG_filter.h Show diff for this revision Revisions of this file
--- a/EMG.cpp	Mon Oct 19 12:16:35 2015 +0000
+++ b/EMG.cpp	Mon Oct 19 12:34:11 2015 +0000
@@ -1,7 +1,374 @@
-#include "EMG.h"
 #include "mbed.h"
-// functions for reading and writing EMG data
+#include "HIDScope.h"
+#include "MODSERIAL.h"
+
+// Define objects
+AnalogIn    emg1(A0);                   // Analog input 1
+AnalogIn    emg2(A1);                   // Analog input 2
+DigitalOut  ledred(LED_RED);            // Red led
+DigitalOut  ledgreen(LED_GREEN);        // Green led
+DigitalOut  ledblue(LED_BLUE);          // Blue led
+Ticker      sample_tick;                // Ticker for sampling
+Ticker      output;                     // Ticker for PC output
+HIDScope    scope(6);                   // Number of scopes
+Timer       normalizing_timer;          // Timer for normalizing
+Timer       EMG_timer;                  // Timer for switch statement
+MODSERIAL   pc(USBTX, USBRX);           // Ports for communication to pc
+
+// Define program constants
+const int     on = 0;                   // On-constant for LEDs for program readability
+const int     off = 1;                  // Off-constant for LEDs for program readability
+const int     sample = 0;               // Constant for mode switching for program readability
+const int     normalize = 1;            // Constant for mode switching for program readability
+volatile bool emg_go = false;
+const int baudrate = 115200;            // Baudrate for serial connection
+
+
+//**********************************************************************************************
+bool mode               = normalize;         // Set program mode
+//**********************************************************************************************
+
+// Define sampling constants
+const double sample_frequency = 200;         // Sample frequency [Hz]
+double emg_val1 = 0, emg_val2 = 0, emg_filt_val1 = 0, emg_filt_val2 = 0;
+
+// Define normalizing parameters
+const int     normalize_time    = 2000;      // Normalizing time [ms]
+const int     normalize_wait    = 4000;      // Normalizing wait time [ms]
+double        max_vol_cont1     = 0;         // Maximum voluntary contraction for scaling EMG1
+double        max_vol_cont2     = 0;         // Maximum voluntary contraction for scaling EMG2
+int           channel           = 1;         // Channel for normalizing (EMG1 or EMG2)
+
+// Define movement parameters
+int         DOF = 1;                         // Switch variable for controlled DOF: 1=x 2=y 3=z
+bool        pump = false;                    // Pump switch
+bool        thr_pass1 = false;               // Processing threshold passed for signal 1?
+bool        thr_pass2 = false;               // Processing threshold passed for signal 2?
+double      velocity = 0;                    // Forward velocity
+double      x_velocity = 0;                  // x component for velocity
+double      y_velocity = 0;                  // y component for velocity
+double      z_velocity = 0;                  // z component for velocity
+
+// Define thresholds
+const double UPT = 0.35;                    // Upper Treshold Value     (0-1)
+const double LPT = 0.2;                     // Lower Treshold Vvalue     (0-1)
+const double LST = 100;                     // Lower Switch Time        [ms]
+const double UST = 500;                     // Upper Switch Time        [ms]
+
+// Define filter constants
+double f11_v1=0, f11_v2=0, f12_v1=0, f12_v2=0, f13_v1=0, f13_v2=0;
+double f11_v1b=0, f11_v2b=0, f12_v1b=0, f12_v2b=0, f13_v1b=0, f13_v2b=0;
+const double f11_gain = 0.907616, f11_a1 = 0.00000000267,  f11_a2 = 0.77693864001, f11_b0 = 1, f11_b1 = 0.00000000301, f11_b2 = 1; // Constants for pre-filter BiQuad: Bandstop 48-52Hz     #1
+const double f12_gain = 0.857718, f12_a1 = -0.20392089403, f12_a2 = 0.88318656585, f12_b0 = 1, f12_b1 = 0.00000000301, f12_b2 = 1; // Constants for pre-filter BiQuad: Bandstop 48-52Hz     #2
+const double f13_gain = 1       , f13_a1 = 0.20392089964,  f13_a2 = 0.88318656589, f13_b0 = 1, f13_b1 = 0.00000000301, f13_b2 = 1; // Constants for pre-filter BiQuad: Bandstop 48-52Hz     #3
+
+double f21_v1=0, f21_v2=0, f22_v1=0, f22_v2=0, f23_v1=0, f23_v2=0;
+double f21_v1b=0, f21_v2b=0, f22_v1b=0, f22_v2b=0, f23_v1b=0, f23_v2b=0;
+const double f21_gain = 0.807131, f21_a1 = -1.58896195896, f21_a2 = 0.63360956141, f21_b0 = 1, f21_b1 = -2, f21_b2 =  1; // Constants for pre-filter BiQuad: Highpass 6 Hz        #1
+const double f22_gain = 0.847233, f22_a1 = -1.67098565006, f22_a2 = 0.71793800319, f22_b0 = 1, f22_b1 = -2, f22_b2 =  1; // Constants for pre-filter BiQuad: Highpass 6 Hz        #2
+const double f23_gain = 0.928702, f23_a1 = -1.83505834569, f23_a2 = 0.88662091145, f23_b0 = 1, f23_b1 = -2, f23_b2 =  1; // Constants for pre-filter BiQuad: Highpass 6 Hz        #3
+
+double f31_v1=0, f31_v2=0, f32_v1=0, f32_v2=0, f33_v1=0, f33_v2=0;
+double f31_v1b=0, f31_v2b=0, f32_v1b=0, f32_v2b=0, f33_v1b=0, f33_v2b=0;
+const double f31_gain = 0.130049, f31_a1 = -0.57789344562, f31_a2 = 0.09809080329, f31_b0 = 1, f31_b1 = 2, f31_b2 = 1; // Constants for pre-filter BiQuad: Lowpass  30 Hz       #1
+const double f32_gain = 0.147923, f32_a1 = -0.65731925030, f32_a2 = 0.24901264940, f32_b0 = 1, f32_b1 = 2, f32_b2 = 1; // Constants for pre-filter BiQuad: Lowpass  30 Hz       #2
+const double f33_gain = 0.194139, f33_a1 = -0.86268420183, f33_a2 = 0.63923919773, f33_b0 = 1, f33_b1 = 2, f33_b2 = 1; // Constants for pre-filter BiQuad: Lowpass  30 Hz       #3
+
+double a11_v1=0, a11_v2=0, a12_v1=0, a12_v2=0, a13_v1=0, a13_v2=0;
+double a11_v1b=0, a11_v2b=0, a12_v1b=0, a12_v2b=0, a13_v1b=0, a13_v2b=0;
+const double a11_gain = 0.002932, a11_a1 = -1.79028908066, a11_a2 = 0.80198751695, a11_b0 = 1, a11_b1 = 2, a11_b2 = 1; // Constants for averaging BiQuad:   Lowpass  4 Hz       #1
+const double a12_gain = 0.003004, a12_a1 = -1.83907730494, a12_a2 = 0.85109454222, a12_b0 = 1, a12_b1 = 2, a12_b2 = 1; // Constants for averaging BiQuad:   Lowpass  4 Hz       #2
+const double a13_gain = 0.003145, a13_a1 = -1.93018419700, a13_a2 = 0.94279676171, a13_b0 = 1, a13_b1 = 2, a13_b2 = 1; // Constants for averaging BiQuad:   Lowpass  4 Hz       #2
+
+// Reusable BiQuad filter
+double biquad(double u, double &v1, double &v2, const double a1, const double a2, const double b0, const double b1, const double b2, const double gain)
+{
+    double v = u - a1*v1-a2*v2;
+    double y = gain*(b0*v+b1*v1+b2*v2);
+    v2 = v1;
+    v1=v;
+    return y;
+}
+
+// Apply filters: applies all necesary filters and averaging methods to input signal value
+double filtera(double emg_val)
+{
+    // Filtering signal
+    double emg_filt_val;
+    emg_filt_val = biquad(emg_val,f11_v1,f11_v2,f11_a1,f11_a2,f11_b0,f11_b1,f11_b2,f11_gain);      // Apply bandstop
+    emg_filt_val = biquad(emg_filt_val,f12_v1,f12_v2,f12_a1,f12_a2,f12_b0,f12_b1,f12_b2,f12_gain);
+    emg_filt_val = biquad(emg_filt_val,f13_v1,f13_v2,f13_a1,f13_a2,f13_b0,f13_b1,f13_b2,f13_gain);
+    emg_filt_val = biquad(emg_filt_val,f21_v1,f21_v2,f21_a1,f21_a2,f21_b0,f21_b1,f21_b2,f21_gain); // Apply highpass
+    emg_filt_val = biquad(emg_filt_val,f22_v1,f22_v2,f22_a1,f22_a2,f22_b0,f22_b1,f22_b2,f22_gain);
+    emg_filt_val = biquad(emg_filt_val,f23_v1,f23_v2,f23_a1,f23_a2,f23_b0,f23_b1,f23_b2,f23_gain);
+    emg_filt_val = biquad(emg_filt_val,f31_v1,f31_v2,f31_a1,f31_a2,f31_b0,f31_b1,f31_b2,f31_gain); // Apply lowpass
+    emg_filt_val = biquad(emg_filt_val,f32_v1,f32_v2,f32_a1,f32_a2,f32_b0,f32_b1,f32_b2,f32_gain);
+    emg_filt_val = biquad(emg_filt_val,f33_v1,f33_v2,f33_a1,f33_a2,f33_b0,f33_b1,f33_b2,f33_gain);
+
+    // Rectify signal
+    emg_filt_val = fabs(emg_filt_val);
+
+    // Averaging signal
+    emg_filt_val = biquad(emg_filt_val,a11_v1,a11_v2,a11_a1,a11_a2,a11_b0,a11_b1,a11_b2,a11_gain); // Apply avg. lowpass
+    emg_filt_val = biquad(emg_filt_val,a12_v1,a12_v2,a12_a1,a12_a2,a12_b0,a12_b1,a12_b2,a12_gain);
+    emg_filt_val = biquad(emg_filt_val,a13_v1,a13_v2,a13_a1,a13_a2,a13_b0,a13_b1,a13_b2,a13_gain);
+
+    return emg_filt_val;
+}
+double filterb(double emg_val)
+{
+    // Filtering signal
+    double emg_filt_val;
+    emg_filt_val = biquad(emg_val,f11_v1b,f11_v2b,f11_a1,f11_a2,f11_b0,f11_b1,f11_b2,f11_gain);      // Apply bandstop
+    emg_filt_val = biquad(emg_filt_val,f12_v1b,f12_v2b,f12_a1,f12_a2,f12_b0,f12_b1,f12_b2,f12_gain);
+    emg_filt_val = biquad(emg_filt_val,f13_v1b,f13_v2b,f13_a1,f13_a2,f13_b0,f13_b1,f13_b2,f13_gain);
+    emg_filt_val = biquad(emg_filt_val,f21_v1b,f21_v2b,f21_a1,f21_a2,f21_b0,f21_b1,f21_b2,f21_gain); // Apply highpass
+    emg_filt_val = biquad(emg_filt_val,f22_v1b,f22_v2b,f22_a1,f22_a2,f22_b0,f22_b1,f22_b2,f22_gain);
+    emg_filt_val = biquad(emg_filt_val,f23_v1b,f23_v2b,f23_a1,f23_a2,f23_b0,f23_b1,f23_b2,f23_gain);
+    emg_filt_val = biquad(emg_filt_val,f31_v1b,f31_v2b,f31_a1,f31_a2,f31_b0,f31_b1,f31_b2,f31_gain); // Apply lowpass
+    emg_filt_val = biquad(emg_filt_val,f32_v1b,f32_v2b,f32_a1,f32_a2,f32_b0,f32_b1,f32_b2,f32_gain);
+    emg_filt_val = biquad(emg_filt_val,f33_v1b,f33_v2b,f33_a1,f33_a2,f33_b0,f33_b1,f33_b2,f33_gain);
+
+    // Rectify signal
+    emg_filt_val = fabs(emg_filt_val);
+
+    // Averaging signal
+    emg_filt_val = biquad(emg_filt_val,a11_v1b,a11_v2b,a11_a1,a11_a2,a11_b0,a11_b1,a11_b2,a11_gain); // Apply avg. lowpass
+    emg_filt_val = biquad(emg_filt_val,a12_v1b,a12_v2b,a12_a1,a12_a2,a12_b0,a12_b1,a12_b2,a12_gain);
+    emg_filt_val = biquad(emg_filt_val,a13_v1b,a13_v2b,a13_a1,a13_a2,a13_b0,a13_b1,a13_b2,a13_gain);
+
+    return emg_filt_val;
+}
+
+// Create velocity steps: Converts continious velocity input signal to stepped output
+double velocity_step(double velocity)
+{
+    if (velocity <= 0.33) {
+        velocity=0.33;
+    } else if(velocity <= 0.66) {
+        velocity=0.66;
+    } else if(velocity > 0.66) {
+        velocity=1;
+    }
+    return velocity;
+}
+
+// Output velocity components
+void EMG_velocity(double &x_velocity, double &y_velocity, double &z_velocity, double emg_val, bool thr_pass1, int &DOF, int emg_time)
+{
+    if (emg_time == 0) {
+        EMG_timer.start();
+    }
+
+    if (emg_val > LPT) {
+        if (emg_time > LST) {
+            if(emg_time > UST) {
+                // Output velocities -------------------------------------------------------------------
+                velocity = (emg_val - LPT)*(1/(1-LPT));             
+                
+                if(thr_pass1) {
+                    velocity = velocity_step(velocity);
+                } else {
+                    velocity = - velocity_step(velocity);
+                }
 
-void readEMG(){
-    
-    }
\ No newline at end of file
+                switch(DOF) {
+                    case 1 :
+                        x_velocity = velocity;
+                        y_velocity = 0;
+                        z_velocity = 0;
+                        break;
+                    case 2 :
+                        x_velocity = 0;
+                        y_velocity = velocity;
+                        z_velocity = 0;
+                        break;
+                    case 3 :
+                        x_velocity = 0;
+                        y_velocity = 0;
+                        
+                        // z-velocity is not controlled continiously, but set to fixed value +/- 1
+                        if(velocity > 0) {
+                            z_velocity = 1;
+                        } else if( velocity < 0) {
+                            z_velocity = -1;
+                        } else {
+                            z_velocity = 0;
+                        }
+                        break;
+                }
+            }
+        }
+    } else {
+        if((emg_time > LST)&&(emg_time < UST)) {
+            if(thr_pass1) {
+                // Switch channel  -----------------------------------------------------------------------
+                DOF = DOF + 1;
+                if (DOF == 4) {
+                    DOF = 1;
+                }
+            } else {
+                // Switch pump ---------------------------------------------------------------------------
+                pump = !pump;
+            }
+        }
+        // No input: set all value to zero ---------------------------------------------------------------
+        x_velocity = 0;
+        y_velocity = 0;
+        z_velocity = 0;
+        thr_pass1 = false;
+        thr_pass2 = false;
+        EMG_timer.stop();
+        EMG_timer.reset();
+    }
+}
+
+void EMG_check(bool &thr_pass1, bool &thr_pass2, double &x_velocity, double &y_velocity, double &z_velocity, double emg_filt_val1, double emg_filt_val2, int emg_time)
+{
+    if (emg_filt_val1 > UPT) {
+        thr_pass1 = true;
+    }
+    if (emg_filt_val2 > UPT) {
+        thr_pass2 = true;
+    }
+
+    if ((thr_pass1 == true) && (thr_pass2 == true)) {
+        // If both true terminate
+        thr_pass1 = false;
+        thr_pass2 = false;
+        EMG_timer.stop();
+        EMG_timer.reset();
+        x_velocity = 0;
+        y_velocity = 0;
+        z_velocity = 0;
+        
+    } else {
+        if(thr_pass1) {
+            EMG_velocity(x_velocity, y_velocity,  z_velocity, emg_filt_val1, thr_pass1, DOF, emg_time);
+        } else if(thr_pass2) {
+            EMG_velocity(x_velocity, y_velocity,  z_velocity, emg_filt_val2, thr_pass1, DOF, emg_time);
+        }
+    }
+}
+
+
+// Go_flag for EMG_sampling
+void emg_activate()
+{
+    emg_go=true;
+}
+
+int main()
+{
+    ledgreen.write(off);
+    ledblue.write(off);
+    ledred.write(on);
+    wait_ms(5000);
+    sample_tick.attach(&emg_activate, 1/sample_frequency);      // Call sample function with ticker
+    pc.baud(baudrate);                                          // Connect to pc for debugging output
+
+    if(mode==normalize) {                                       // Start normalizing timer
+        normalizing_timer.reset();
+        normalizing_timer.start();
+    }
+
+    while(1) {
+
+        if(emg_go && mode ==normalize) {
+            emg_go = false;
+
+            emg_val1 = emg1.read();               // Sample EMG value 1 from AnalogIn
+            emg_val2 = emg2.read();               // Sample EMG value 2 from AnalogIn
+
+            emg_filt_val1 = filtera(emg_val1);    // Filter and average signal
+            emg_filt_val2 = filterb(emg_val2);    // Filter and average signal
+
+
+            /* Determining normalizing constants
+            - Read value from EMG and average as above
+            - If mode "normalizing" and normalizing timer below ending time:
+                * store averaged EMG value 1 in max_vol_cont1 while overwriting previous value when current value > previous value
+            - If normalizing time exceeded, wait for given time
+            - Switch channels: redo above procedure for EMG value 2
+            - Switch to sampling mode
+            */
+
+            // First normalizing step: channel 1
+            if (normalizing_timer.read_ms() <= normalize_time && channel == 1) {
+                ledred.write(off);
+                ledgreen.write(on);
+                if (emg_filt_val1 > max_vol_cont1) {
+                    max_vol_cont1 = emg_filt_val1;
+                }
+                // Second normalizing step: wait time, switch channel
+            } else if (normalizing_timer.read_ms() > normalize_time && channel == 1) {
+                channel = 2;
+                ledgreen.write(off);
+                ledred.write(on);
+                // Third normalizing step: channel 2
+            } else if (normalizing_timer.read_ms() >= (normalize_time + normalize_wait) && normalizing_timer.read_ms() <= (2*normalize_time + normalize_wait) && channel == 2) {
+                ledred.write(off);
+                ledgreen.write(on);
+                if (emg_filt_val2 > max_vol_cont2) {
+                    max_vol_cont2 = emg_filt_val2;
+                }
+                // Final normalizing step: stop normalizing process, start outputting velocities
+            } else if (normalizing_timer.read_ms() > (2*normalize_time + normalize_wait)) {
+                normalizing_timer.stop();
+                normalizing_timer.reset();
+                ledgreen.write(off);
+                ledred.write(off);
+                ledblue.write(on);
+                mode = sample;
+            }
+        }
+
+
+        if(emg_go && mode ==sample) {
+            emg_go = false;
+
+            emg_val1 = emg1.read();                             // Sample EMG value 1 from AnalogIn
+            emg_val2 = emg2.read();                             // Sample EMG value 2 from AnalogIn
+
+            // Normalize EMG values using pre-determined coefficients
+            if(max_vol_cont1 != 0 && max_vol_cont2 != 0) {      // Safety check: normalizing coefficients have to be set first
+                emg_val1 = emg_val1 / max_vol_cont1;            // Normalize EMG-value 1
+                emg_val2 = emg_val2 / max_vol_cont2;            // Normalize EMG-value 2
+
+                emg_filt_val1 = filtera(emg_val1);              // Filter and average signal 1
+                emg_filt_val2 = filterb(emg_val2);              // Filter and average signal 2
+
+                if (emg_filt_val1 > 1) {                        // Safety-function: set max. output to 1, even if muscle contraction exceeds max. voluntary contraction
+                    emg_filt_val1 = 1;
+                }
+                if (emg_filt_val2 > 1) {                        // Safety-function: set max. output to 1, even if muscle contraction exceeds max. voluntary contraction
+                    emg_filt_val2 = 1;
+                }
+
+                /* Checking EMG input:
+                -   Make sure only one DOF is actuated at a time
+                -   Distinct between switching function and motion from one EMG-signal
+                */
+                
+                EMG_check(thr_pass1, thr_pass2, x_velocity, y_velocity, z_velocity, emg_filt_val1, emg_filt_val2, EMG_timer.read_ms());
+
+            } else {
+                ledgreen.write(off);
+                ledred.write(on);
+                ledblue.write(off);
+            }
+        }
+
+        // Graphical output to HIDScope for debugging/ program check
+        scope.set(0,emg_filt_val2);
+        scope.set(1,x_velocity);
+        scope.set(2,y_velocity);
+        scope.set(3,pump);
+        scope.set(4,DOF);
+        scope.set(5,DOF);
+        scope.send();
+        //pc.printf("EMG timer: %i, EMG1 %.4f, EMG2 %.4f, DOF: %i, pump: %i \n", EMG_timer.read_ms(),emg_filt_val1,emg_filt_val2,DOF,pump);
+        //pc.printf("Normalizing timer: %i, max_vol_cont1 = %.4f,max_vol_cont2 = %.4f \n",normalizing_timer.read_ms(),max_vol_cont1,max_vol_cont2);
+    }
+}
+