This is a simple EMG Controller for a bionic hand prosthesis

Dependencies:   mbed-dsp mbed NOKIA_5110

Galileo Bionic Hand - ST Nucleo Example

/media/uploads/julioefajardo/small.png

This is an example of a simple hybrid sEMG(surface Electromyography) activated controller for the Galileo Bionic Hand Prosthesis implemented with the Galileo EMG Shield. The user has to select the desired posture by sending a special character through serial port (115200 baud rate) and then perform it through sEMG activation by detecting contraction on flexor muscles of the forearm. Contractions on forearm extensor muscles releases the posture and allows the return to the default or rest posture.

Special characters:

  • '1', for "Power Grip" selection
  • '2', for "Point" selection
  • '3', for "Pinch Grip" selection
  • '4', for "Lateral Grip" selection
  • '5' for "Tripod Grip" selection

/media/uploads/julioefajardo/serialinterface.png

Galileo EMG Shield - 3 Channels for surface EMG

The electrodes for sEMG are placed on the skin surface and are connected to the input of a precision instrumentation differential amplifier based on Texas Instruments (TI) INA122, then its output is passed through an active low pass filter (LPF) based on TI OPA335 in order to sense the action bio-potentials of the muscular fibers with an output signal span in the range of 0 to 3.3V and a bandwidth between 0 to 500 Hz. The circuit is built-in on a custom PCB with 3 sEMG channels and a bias voltage reference output (1.25 V); furthermore, it is pin compatible with Arduino pin compatible ARM Cortex M4 boards and mbed platform ecosystem (ST Nucleo F411RE, Freescale FRDM K64F, NXP LPCXpresso 4337, ST Discovery F746NG, Renesas GR-Peach, etc), which is ideal for the single ended input of a microcontroller in addition to contributing to low cost development kits. /media/uploads/julioefajardo/shieldbrd.png /media/uploads/julioefajardo/image1.jpg

Electrodes Placement and Connection

Standard surface mounted Ag/AgCl electrodes with wet conductive gels are placed on palmaris longus and extensor digitorum muscles (third channel could be placed on Flexor carpi Ulnaris), focusing only on below elbow disarticulation. These electrodes have been well-characterized and most of its properties are well understood, except for some properties as drifting and low-frequency noise. Nevertheless, with proper preparation of the skin, the sEMG signal is excellent.

Disposable electrodes and snap leads information:

Proper placement of disposable electrodes for two channels of surface EMG is shown below:

/media/uploads/julioefajardo/electrodes.png /media/uploads/julioefajardo/mucles.png

Customizable Postures

You can customize the actions by modifying PWM values (microseconds) on FingerPosition function (values depends on the way that the hand was built it). The prosthesis has five fingers and a thumb rotation mechanism and five actuators in order to perform multiple types of grasping. Wrist rotation will be implement later.

The servo motors have to be connected as shown below:

/media/uploads/julioefajardo/servos.png

Function Declaration and Usage Examples

include the mbed library with this snippet

void FingerPosition(float32_t thumb_us, float32_t index_us, float32_t middle_us, float32_t pinky_us, float32_t thumbrot_us);

FingerPosition(2400, 600, 600,2400,2400);   //Close
FingerPosition(2400,2400, 600,2400,2400);   //Point
FingerPosition(2400, 600,2400, 600,2400);   //Pinch
FingerPosition(2400, 600, 600,2400, 600);   //Lateral
FingerPosition(2400, 600, 600, 600,2400);   //Tripod
FingerPosition(1000,2400,2400, 600, 600);   //Open

Serial Oscilloscope Feature

This feature easily allows to watch and log to file the data using serial oscilloscope software (115200 baud rate).

  • Serial_Osciloscope(TRUE,RAW) to watch raw signals, FALSE deactivate this feature
  • Serial_Osciloscope(TRUE,RECTIFIED) to watch rectified signals, FALSE deactivate this feature
  • Serial_Osciloscope(TRUE,SMOOTH) to watch smooth signals, FALSE deactivate this feature

/media/uploads/julioefajardo/smooth_signal.png

Universal Real-Time Software Oscilloscope GUI information: http://www.oscilloscope-lib.com/

Nolia 5110 LCD

Nokia 5110 display implementation for visual feedback will be add later, we have to modify libraries and fonts in order to improve the functionality. The main idea is to change of action by pressing a push button and change thresholds using a potentiometer.

/media/uploads/julioefajardo/lcd.png

Information

Videos, bill of materials and tutorials to build the Galileo EMG Shield and the Galileo Bionic Hand will be posted soon, more information on:

Committer:
julioefajardo
Date:
Sun Oct 11 03:42:30 2015 +0000
Revision:
5:49c5553b6e2c
Parent:
4:d8fd3c4484cc
Child:
6:78494092a326
Version 1.3; EMG Galieo Hand Prosthesis Controller ; Refactorization

Who changed what in which revision?

UserRevisionLine numberNew contents of line
julioefajardo 0:f2b89c6a8a16 1 #include "mbed.h"
julioefajardo 0:f2b89c6a8a16 2 #include "arm_math.h"
julioefajardo 0:f2b89c6a8a16 3
julioefajardo 5:49c5553b6e2c 4 #define TRUE 1
julioefajardo 5:49c5553b6e2c 5 #define FALSE 0
julioefajardo 5:49c5553b6e2c 6 #define RAW 0
julioefajardo 5:49c5553b6e2c 7 #define RECTIFIED 1
julioefajardo 5:49c5553b6e2c 8 #define SMOOTH 2
julioefajardo 5:49c5553b6e2c 9 #define THRESHOLDF 0.25f
julioefajardo 5:49c5553b6e2c 10 #define THRESHOLDE 0.25f
julioefajardo 5:49c5553b6e2c 11 #define FLEXION2 0.0f
julioefajardo 4:d8fd3c4484cc 12
julioefajardo 0:f2b89c6a8a16 13 Ticker EMG_Sampler;
julioefajardo 0:f2b89c6a8a16 14 Serial pc(SERIAL_TX, SERIAL_RX);
julioefajardo 0:f2b89c6a8a16 15 DigitalOut myled(LED1);
julioefajardo 0:f2b89c6a8a16 16 AnalogIn Ref(A0);
julioefajardo 0:f2b89c6a8a16 17 AnalogIn E1(A1);
julioefajardo 0:f2b89c6a8a16 18 AnalogIn E2(A2);
julioefajardo 0:f2b89c6a8a16 19 AnalogIn E3(A3);
julioefajardo 3:f784301a5166 20 PwmOut Thumb(D11);
julioefajardo 3:f784301a5166 21 PwmOut Index(D10);
julioefajardo 3:f784301a5166 22 PwmOut Middle(D9);
julioefajardo 3:f784301a5166 23 PwmOut Pinky(D6);
julioefajardo 3:f784301a5166 24 PwmOut ThumbRot(D5);
julioefajardo 0:f2b89c6a8a16 25
julioefajardo 5:49c5553b6e2c 26 //EMG samples and DSP variables
julioefajardo 0:f2b89c6a8a16 27 float32_t EMG1, EMG2, EMG3;
julioefajardo 2:12f979d695db 28 float32_t samples[25];
julioefajardo 2:12f979d695db 29 float32_t samples2[25];
julioefajardo 2:12f979d695db 30 float32_t samples3[25];
julioefajardo 2:12f979d695db 31 float32_t abs_output[25];
julioefajardo 2:12f979d695db 32 float32_t abs_output2[25];
julioefajardo 2:12f979d695db 33 float32_t abs_output3[25];
julioefajardo 5:49c5553b6e2c 34 float32_t mean = 0.0f;
julioefajardo 5:49c5553b6e2c 35 float32_t mean2 = 0.0f;
julioefajardo 5:49c5553b6e2c 36 float32_t mean3 = 0.0f;
julioefajardo 5:49c5553b6e2c 37
julioefajardo 5:49c5553b6e2c 38 //variables for state machines
julioefajardo 4:d8fd3c4484cc 39 uint8_t state = 0;
julioefajardo 5:49c5553b6e2c 40 uint8_t action = 0;
julioefajardo 5:49c5553b6e2c 41
julioefajardo 5:49c5553b6e2c 42 //conversion and data collection complete flag
julioefajardo 0:f2b89c6a8a16 43 uint8_t COCO = 0;
julioefajardo 0:f2b89c6a8a16 44
julioefajardo 5:49c5553b6e2c 45 //functions declaration
julioefajardo 5:49c5553b6e2c 46 void PWM_Init(void);
julioefajardo 4:d8fd3c4484cc 47 void ADC_Sampler(void);
julioefajardo 5:49c5553b6e2c 48 void RX_Interrupt(void);
julioefajardo 5:49c5553b6e2c 49 void Serial_Oscilloscope(uint8_t activation, uint8_t type);
julioefajardo 5:49c5553b6e2c 50 void FingerPosition(float32_t thumb_us, float32_t index_us, float32_t middle_us, float32_t pinky_us, float32_t thumbrot_us);
julioefajardo 0:f2b89c6a8a16 51
julioefajardo 0:f2b89c6a8a16 52 int main() {
julioefajardo 0:f2b89c6a8a16 53 pc.baud(115200); //Serial com at 115200 bauds
julioefajardo 5:49c5553b6e2c 54 pc.attach(&RX_Interrupt); //Serial RX interrupt attachment
julioefajardo 4:d8fd3c4484cc 55 EMG_Sampler.attach(&ADC_Sampler, 0.001); //1 ms ticker for ADC Sampler
julioefajardo 5:49c5553b6e2c 56 PWM_Init(); //Servo initialization
julioefajardo 5:49c5553b6e2c 57 myled = 1;
julioefajardo 3:f784301a5166 58
julioefajardo 0:f2b89c6a8a16 59 while(1) {
julioefajardo 0:f2b89c6a8a16 60 if(COCO){
julioefajardo 5:49c5553b6e2c 61 arm_abs_f32(samples, abs_output, 25); //rectifier EMG1
julioefajardo 5:49c5553b6e2c 62 arm_abs_f32(samples2, abs_output2, 25); //rectifier EMG2
julioefajardo 5:49c5553b6e2c 63 arm_abs_f32(samples3, abs_output3, 25); //rectifier EMG3
julioefajardo 5:49c5553b6e2c 64 arm_mean_f32(abs_output, 25, &mean); //mean EMG1
julioefajardo 5:49c5553b6e2c 65 arm_mean_f32(abs_output2, 25, &mean2); //mean EMG2
julioefajardo 5:49c5553b6e2c 66 arm_mean_f32(abs_output3, 25, &mean3); //mean EMG3
julioefajardo 5:49c5553b6e2c 67 Serial_Oscilloscope(FALSE,SMOOTH);
julioefajardo 4:d8fd3c4484cc 68 switch(state){
julioefajardo 4:d8fd3c4484cc 69 case 0: {
julioefajardo 5:49c5553b6e2c 70 if (mean>THRESHOLDF){
julioefajardo 4:d8fd3c4484cc 71 myled = 0;
julioefajardo 4:d8fd3c4484cc 72 state = 1;
julioefajardo 5:49c5553b6e2c 73 switch(action){
julioefajardo 5:49c5553b6e2c 74 case '1': FingerPosition(2400, 600, 600,2400,2400); break; //Close
julioefajardo 5:49c5553b6e2c 75 case '2': FingerPosition(2400,2400, 600,2400,2400); break; //Point
julioefajardo 5:49c5553b6e2c 76 case '3': FingerPosition(2400, 600,2400, 600,2400); break; //Pinch
julioefajardo 5:49c5553b6e2c 77 case '4': FingerPosition(2400, 600, 600,2400, 600); break; //Lateral
julioefajardo 5:49c5553b6e2c 78 case '5': FingerPosition(2400, 600, 600, 600,2400); break; //Tripod
julioefajardo 5:49c5553b6e2c 79 default: FingerPosition(2400, 600, 600,2400,2400); //Close
julioefajardo 5:49c5553b6e2c 80 }
julioefajardo 4:d8fd3c4484cc 81 }
julioefajardo 4:d8fd3c4484cc 82 } break;
julioefajardo 4:d8fd3c4484cc 83 case 1: {
julioefajardo 5:49c5553b6e2c 84 if (mean2>THRESHOLDE){
julioefajardo 4:d8fd3c4484cc 85 myled = 1;
julioefajardo 4:d8fd3c4484cc 86 state = 0;
julioefajardo 5:49c5553b6e2c 87 FingerPosition(1000,2400,2400, 600, 600); //Open
julioefajardo 4:d8fd3c4484cc 88 }
julioefajardo 4:d8fd3c4484cc 89 }
julioefajardo 0:f2b89c6a8a16 90 }
julioefajardo 0:f2b89c6a8a16 91 COCO = 0;
julioefajardo 0:f2b89c6a8a16 92 }
julioefajardo 0:f2b89c6a8a16 93 }
julioefajardo 0:f2b89c6a8a16 94 }
julioefajardo 4:d8fd3c4484cc 95
julioefajardo 5:49c5553b6e2c 96 //EMG sampler Ts = 1ms
julioefajardo 4:d8fd3c4484cc 97 void ADC_Sampler() {
julioefajardo 4:d8fd3c4484cc 98 EMG1 = (E1.read()-Ref.read())*3.3f;
julioefajardo 4:d8fd3c4484cc 99 EMG2 = (E2.read()-Ref.read())*3.3f;
julioefajardo 4:d8fd3c4484cc 100 EMG3 = (E3.read()-Ref.read())*3.3f;
julioefajardo 5:49c5553b6e2c 101 Serial_Oscilloscope(TRUE,RAW);
julioefajardo 4:d8fd3c4484cc 102 uint32_t m = __get_PRIMASK();
julioefajardo 4:d8fd3c4484cc 103 __disable_irq();
julioefajardo 4:d8fd3c4484cc 104 for(int j=24;j>0;j--) {
julioefajardo 4:d8fd3c4484cc 105 samples[j]=samples[j-1]; //Fill Array
julioefajardo 4:d8fd3c4484cc 106 samples2[j]=samples2[j-1]; //Fill Array
julioefajardo 4:d8fd3c4484cc 107 samples3[j]=samples3[j-1]; //Fill Array
julioefajardo 4:d8fd3c4484cc 108 }
julioefajardo 4:d8fd3c4484cc 109 samples[0]=EMG1;
julioefajardo 4:d8fd3c4484cc 110 samples2[0]=EMG2;
julioefajardo 4:d8fd3c4484cc 111 samples3[0]=EMG3;
julioefajardo 4:d8fd3c4484cc 112 __set_PRIMASK(m);
julioefajardo 4:d8fd3c4484cc 113 COCO = 1;
julioefajardo 4:d8fd3c4484cc 114 }
julioefajardo 4:d8fd3c4484cc 115
julioefajardo 5:49c5553b6e2c 116 //action selection trough serial console
julioefajardo 5:49c5553b6e2c 117 void RX_Interrupt(void){
julioefajardo 5:49c5553b6e2c 118 action = pc.getc();
julioefajardo 5:49c5553b6e2c 119 /*switch(action){
julioefajardo 5:49c5553b6e2c 120 case '1': pc.printf("Power Grip\n"); break;
julioefajardo 5:49c5553b6e2c 121 case '2': pc.printf("Point\n"); break;
julioefajardo 5:49c5553b6e2c 122 case '3': pc.printf("Pinch\n"); break;
julioefajardo 5:49c5553b6e2c 123 case '4': pc.printf("Lateral\n"); break;
julioefajardo 5:49c5553b6e2c 124 case '5': pc.printf("Tripod\n"); break;
julioefajardo 5:49c5553b6e2c 125 default: pc.printf("Power Grip\n");
julioefajardo 5:49c5553b6e2c 126 }*/
julioefajardo 4:d8fd3c4484cc 127 }
julioefajardo 4:d8fd3c4484cc 128
julioefajardo 5:49c5553b6e2c 129 //PWM initialization for servos
julioefajardo 5:49c5553b6e2c 130 void PWM_Init(void){
julioefajardo 5:49c5553b6e2c 131 //Open -> 0.6ms - Close 2.4ms
julioefajardo 5:49c5553b6e2c 132 Thumb.period(0.02f/2.0f);
julioefajardo 4:d8fd3c4484cc 133 Thumb.pulsewidth(0.0010f/2.0f);
julioefajardo 5:49c5553b6e2c 134 //Open -> 2.4ms - Close 0.6ms
julioefajardo 5:49c5553b6e2c 135 Index.period(0.02f*2.0f);
julioefajardo 4:d8fd3c4484cc 136 Index.pulsewidth(0.0024f*2.0f);
julioefajardo 5:49c5553b6e2c 137 //Open -> 2.4ms - Close 0.6ms
julioefajardo 5:49c5553b6e2c 138 Middle.period(0.02f*2.0f);
julioefajardo 4:d8fd3c4484cc 139 Middle.pulsewidth(0.0024f*2.0f);
julioefajardo 5:49c5553b6e2c 140 //Open -> 0.6ms - Close 2.4ms
julioefajardo 5:49c5553b6e2c 141 Pinky.period(0.02f*2.0f);
julioefajardo 4:d8fd3c4484cc 142 Pinky.pulsewidth(0.0006f*2.0f);
julioefajardo 5:49c5553b6e2c 143 //Open -> 0.6ms - Close 2.4ms
julioefajardo 5:49c5553b6e2c 144 ThumbRot.pulsewidth(0.0006f*2.0f);
julioefajardo 5:49c5553b6e2c 145 }
julioefajardo 5:49c5553b6e2c 146
julioefajardo 5:49c5553b6e2c 147 //send data through serail port for oscilloscope visualization
julioefajardo 5:49c5553b6e2c 148 void Serial_Oscilloscope(uint8_t activation, uint8_t type){
julioefajardo 5:49c5553b6e2c 149 if (activation){
julioefajardo 5:49c5553b6e2c 150 switch(type){
julioefajardo 5:49c5553b6e2c 151 case RAW: pc.printf("%.10f,%.10f,%.10f\n\r",EMG1,EMG2,EMG3); break;
julioefajardo 5:49c5553b6e2c 152 case RECTIFIED: pc.printf("%.10f,%.10f,%.10f\n\r",abs_output[0],abs_output2[0],abs_output3[0]);break;
julioefajardo 5:49c5553b6e2c 153 case SMOOTH: pc.printf("%.10f,%.10f\n\r",mean,mean2,mean3); break;
julioefajardo 5:49c5553b6e2c 154 default: pc.printf("%.10f,%.10f,%.10f\n\r",EMG1,EMG2,EMG3);
julioefajardo 5:49c5553b6e2c 155 }
julioefajardo 5:49c5553b6e2c 156 }
julioefajardo 5:49c5553b6e2c 157 }
julioefajardo 5:49c5553b6e2c 158
julioefajardo 5:49c5553b6e2c 159 //finger position through servos - values on microseconds (600 us - 2400 us)
julioefajardo 5:49c5553b6e2c 160 void FingerPosition(float32_t thumb_us, float32_t index_us, float32_t middle_us, float32_t pinky_us, float32_t thumbrot_us){
julioefajardo 5:49c5553b6e2c 161 Thumb.pulsewidth(thumb_us/1000000.0f/2.0f);
julioefajardo 5:49c5553b6e2c 162 Index.pulsewidth(index_us/1000000.0f*2.0f);
julioefajardo 5:49c5553b6e2c 163 Middle.pulsewidth(middle_us/1000000.0f*2.0f);
julioefajardo 5:49c5553b6e2c 164 Pinky.pulsewidth(pinky_us/1000000.0f*2.0f);
julioefajardo 5:49c5553b6e2c 165 ThumbRot.pulsewidth(thumbrot_us/1000000.0f*2.0f);
julioefajardo 5:49c5553b6e2c 166 }