Luis Hernán CubillosVicente OpasoHannes Hase
Published © GPL3+

Prótesis Mioeléctrica de mano

Prótesis que se controla con los músculos de la persona amputada para manejarla intuitivamente y devolverle en cierta forma la sensibilidad.

AdvancedFull instructions provided20 hours623
Prótesis Mioeléctrica de mano

Things used in this project

Hardware components

Arduino Mega 2560 & Genuino Mega 2560
Arduino Mega 2560 & Genuino Mega 2560
×1
DFRobot Gravity: Analog EMG Sensor by OYMotion
×2
PQ12-S Linear Actuator with Limit Switches
×5
RFID ID-12LA
×1
Sensor de Fuerza Resistivo FSR402
×2
2 Baterías 18650
×2

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Placas

Todas las placas usadas en el proyecto.

Manual General

Manual que contiene todas las instrucciones para replicar el proyecto.

Code

High Five

Arduino
Control general de la mano, teniendo en cuenta todos los componentes.
/**********************************************************************
 *** Control software for High Five Controller Board / Arduino Mega ***
 *** High Five, 2018.                                               ***
 **********************************************************************/



/************
 *** Pins ***
 ************/

// Calibration pin
const int calibPin1 = A10;

// Myoelectric sensors
const int muscle1 = A0;
const int muscle2 = A1;

// Thumb actuator
const int feedbackPosMotor1 = A7;
const int pwm1Motor1 = 3;
const int pwm2Motor1 = 2;

// Index actuator
const int feedbackPosMotor2 = A6;
const int pwm1Motor2 = 5;
const int pwm2Motor2 = 4;

// Middle actuator
const int feedbackPosMotor3 = A5;
const int pwm1Motor3 = 7;
const int pwm2Motor3 = 6;

// Ring and Pinky actuators
const int feedbackPosMotor4 = A4;
const int feedbackPosMotor5 = A3;
const int pwm1Motor4 = 8;
const int pwm2Motor4 = 9;

// Force sensors
const int forceSensorPin1 = A8;
const int forceSensorPin2 = A9;

// Force feedback motors
const int vibrador1 = 12;
const int vibrador2 = 13;

/*****************
 *** RFID Tags ***
 *****************/

String tags[2] = {"19001AXXXX", "17002CXXXX"}; // RFID tags
const int numTags = 2; // Number of RFID saved tags

/*****************
 *** Variables ***
 *****************/

// Raw myoelectric signal value
int valueMuscle1;
int valueMuscle2;

// Processed myoelectric signal value
float muscleActivity1 = 0;
float muscleActivity2 = 0;

// Myoelectric signal thresholds
float threshold1 = 20;
float threshold2 = 50;
float thresholdMargin = 1.5;

// Actuator position values
float actualPos1;
float actualPos2;
float actualPos3;
float actualPos4;
float actualPos5;

// Actuator target position
float posTarget[5] = {1.0, 1.0, 1.0, 1.0, 1.0}; // from 0.0 to 1.0
// Actuator brake
int braked = 0;

// Force sensor values
int valueForce1;
int valueForce2;

// State machine variables:
// 0: regular operation (myoelectric control)
// 1+: rfid posture
int state = 0;
int calib = 0;
int calibLast = 0;

/*************
 *** Setup ***
 *************/

void setup() {

  pinMode(LED_BUILTIN, OUTPUT);

  pinMode(calibPin1, OUTPUT);

  pinMode(pwm1Motor1, OUTPUT);
  pinMode(pwm2Motor1, OUTPUT);
  pinMode(pwm1Motor2, OUTPUT);
  pinMode(pwm2Motor2, OUTPUT);
  pinMode(pwm1Motor3, OUTPUT);
  pinMode(pwm2Motor3, OUTPUT);
  pinMode(pwm1Motor4, OUTPUT);
  pinMode(pwm2Motor4, OUTPUT);

  pinMode(vibrador1, OUTPUT);
  pinMode(vibrador2, OUTPUT);

  Serial.begin(115200); // Optional: serial monitor
  Serial1.begin(9600); // RFID
}

/*****************
 *** Main loop ***
 *****************/

void loop() {

  // Process myoelectric signals
  processEMG();

  // Plot myoelectric signals
  /*
    Serial.print(muscleActivity1);
    Serial.print(" , ");
    Serial.print(muscleActivity2);
    Serial.print(" , ");
    Serial.print(threshold1);
    Serial.print(" , ");
    Serial.print(threshold2);
    Serial.print("\n");
  */

  // Read actuator positions
  actualPos1 = mapfloat(analogRead(feedbackPosMotor1), 1023, 0, 0.0, 1.0);
  actualPos2 = mapfloat(analogRead(feedbackPosMotor2), 1023, 0, 0.0, 1.0);
  actualPos3 = mapfloat(analogRead(feedbackPosMotor3), 1023, 0, 0.0, 1.0);
  actualPos4 = mapfloat(analogRead(feedbackPosMotor4), 1023, 0, 0.0, 1.0);
  actualPos5 = mapfloat(analogRead(feedbackPosMotor5), 1023, 0, 0.0, 1.0);

  // Check calibration state
  calib = digitalRead(calibPin1);
  if (calibLast != calib && calib > 0)
  {
    threshold1 = 0;
    threshold2 = 0;
  }
  calibLast = calib;

  // State machine
  if (calib)
  {
    // Measure signal threshold
    if (muscleActivity1 > threshold1)
    {
      threshold1 = muscleActivity1 * thresholdMargin;
    }
    if (muscleActivity2 > threshold2)
    {
      threshold2 = muscleActivity2 * thresholdMargin;
    }
  }
  else
  {
    // Read RFID
    if (Serial1.available() > 0)
    {
      readRFID();
    }

    // Myoelectric control
    if (state == 0)
    {
      float control1 = muscleActivity1;
      float control2 = muscleActivity2;

      // Apply threshold to processed signals
      control1 = (control1 > threshold1) ? control1 : 0;
      control2 = (control2 > threshold2) ? control2 : 0;

      if (control1 > 0 && control2 <= 0)
      {
        // Close hand
        grip_pattern_2(255);
      }
      else if (control2 > 0 && control1 <= 0)
      {
        // Open hand
        grip_pattern_1(255);
      }
      else
      {
        // Brake
        grip_pattern_0();
      }
    }
    // RFID postures
    else
    {
      switch (state)
      {
        case 1:
          // Pinch grip
          grip_pattern_3();
          break;
      }
    }
    // Move to target position
    moveToTargets();
  }

  // Force feedback
  valueForce1 = analogRead(forceSensorPin1);
  valueForce1 = map(valueForce1, 0, 800, 0, 150);
  analogWrite(vibrador1, valueForce1);
  valueForce2 = analogRead(forceSensorPin2);
  valueForce2 = map(valueForce2, 0, 800, 0, 150);
  analogWrite(vibrador2, valueForce2);

  // 1ms loop delay
  delay(1);
}

Grip Patterns

Arduino
Código para explorar los distintos agarres de la prótesis.
/*********************
 *** Grip Patterns ***
 *********************/

// Stop all
void grip_pattern_0() {
  braked = 1;
}

// Open hand
void grip_pattern_1(int duty) {
  posTarget[0] = 1.0;
  posTarget[1] = 1.0;
  posTarget[2] = 1.0;
  posTarget[3] = 1.0;
  posTarget[4] = 1.0;
  braked = 0;
}

// Power grip
void grip_pattern_2(int duty) {
  posTarget[0] = 0.0;
  posTarget[1] = 0.0;
  posTarget[2] = 0.0;
  posTarget[3] = 0.0;
  posTarget[4] = 0.0;
  braked = 0;
}

// Pinch grip
void grip_pattern_3() {
  posTarget[0] = 0.1;
  posTarget[1] = 0.5;
  posTarget[2] = 0.3;
  posTarget[3] = 0.0;
  posTarget[4] = 0.0;
  braked = 0;
}

Hand Control

Arduino
Código para experimentar con los motores.
/******************************
 *** Hand Control Functions ***
 ******************************/

void up(int signal1, int signal2, int duty) {
  analogWrite(signal1, duty);
  analogWrite(signal2, 0);
}

void down(int signal1, int signal2, int duty) {
  analogWrite(signal1, 0);
  analogWrite(signal2, duty);
}

void brake(int signal1, int signal2) {
  digitalWrite(signal1, LOW);
  digitalWrite(signal2, LOW);
}

void moveToTargets() {
  // If not braked move all actuators towards target position
  if (!braked)
  {
    if (actualPos1 - posTarget[0] > 0.1) {
      down(pwm1Motor1, pwm2Motor1, 255);
    }
    else if (actualPos1 - posTarget[0] < -0.1) {
      up(pwm1Motor1, pwm2Motor1, 255);
    }
    else {
      brake(pwm1Motor1, pwm2Motor1);
    }

    if (actualPos2 - posTarget[1] > 0.1) {
      down(pwm1Motor2, pwm2Motor2, 255);
    }
    else if (actualPos2 - posTarget[1] < -0.1) {
      up(pwm1Motor2, pwm2Motor2, 255);
    }
    else {
      brake(pwm1Motor2, pwm2Motor2);
    }

    if (actualPos3 - posTarget[2] > 0.1) {
      down(pwm1Motor3, pwm2Motor3, 255);
    }
    else if (actualPos3 - posTarget[2] < -0.1) {
      up(pwm1Motor3, pwm2Motor3, 255);
    }
    else {
      brake(pwm1Motor3, pwm2Motor3);
    }

    if (actualPos4 - posTarget[3] > 0.1) {
      down(pwm1Motor4, pwm2Motor4, 255);
    }
    else if (actualPos4 - posTarget[3] < -0.1) {
      up(pwm1Motor4, pwm2Motor4, 255);
    }
    else {
      brake(pwm1Motor4, pwm2Motor4);
    }
  }
  // If braked stop all actuators
  else
  {
    brake(pwm1Motor1, pwm2Motor1);
    brake(pwm1Motor2, pwm2Motor2);
    brake(pwm1Motor3, pwm2Motor3);
    brake(pwm1Motor4, pwm2Motor4);
  }
}

// Map function for float values
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

RFID

Arduino
Código para experimentar con el lector RFID.
/************
 *** RFID ***
 ************/

// RFID variables
byte val = 0;
byte code[6];
char tagValue[10];
byte checksum = 0;
byte bytesread = 0;
byte tempbyte = 0;

// Read RFID tag from serial port
void readRFID()
{
  if ((val = Serial1.read()) == 2) // check for header
  {
    bytesread = 0;
    while (bytesread < 12) // read 10 digit code + 2 digit checksum
    {
      if ( Serial1.available() > 0)
      {
        val = Serial1.read();
        if (bytesread < 10) {
          tagValue[bytesread] = val;
        }
        if ((val == 0x0D) || (val == 0x0A) || (val == 0x03) || (val == 0x02)) // if header or stop bytes before the 10th digit stop reading
        {
          break;
        }
        // Ascii/Hex conversion:
        if ((val >= '0') && (val <= '9'))
        {
          val = val - '0';
        }
        else if ((val >= 'A') && (val <= 'F'))
        {
          val = 10 + val - 'A';
        }

        // Every two hex-digits, add byte to code:
        if (bytesread & 1 == 1)
        {
          // make some space for this hex-digit by
          // shifting the previous hex-digit with 4 bits to the left:
          code[bytesread >> 1] = (val | (tempbyte << 4));

          if (bytesread >> 1 != 5) // If we're at the checksum byte,
          {
            checksum ^= code[bytesread >> 1];       // Calculate the checksum... (XOR)
          };
        }
        else
        {
          tempbyte = val;                           // Store the first hex digit first...
        };

        bytesread++;                                // ready to read next digit
      }
    }

    if (bytesread == 12) // if 12th digit reading is complete
    {
      tagValue[10] = '\0';
    }
    //Serial.print("VALUE: "); Serial.println(tagValue);

    bytesread = 0;
    int tag = compareTags(tagValue);
    if (tag >= 0)
    {
      //Serial.print("Tag "); Serial.println(tag);
      if (tag == 0)
      {
        // Myoelectric Control
        state = 0;
      }
      else if (tag == 1)
      {
        // Posture 1
        state = 1;
      }
    }
    else
    {
      //Serial.println("Tag not in list.");
    }
  }
}

// Compare val to saved RFID tags (first 6 characters)
int compareTags(String val) {
  for (int i = 0; i < numTags; i++)
  {
    if (val.substring(0, 6) == tags[i].substring(0, 6))
    {
      return i;
    }
  }
  return -1;
}

Myoelectric

Arduino
Código para experimentar con el control mioeléctrico
/************************************
 *** Myoelectric Signal Prcessing ***
 ************************************/

// Run time (ms)
long t = 0;

// High-pass filter parameters and variables
float fc1 = 100; // Cut-off frequency
float tau1 = 1 / (2 * 3.1415 * fc1);
float y1_1 = 0;
float x1_1 = 0;
float y2_1 = 0;
float x2_1 = 0;

// Low-pass filter parameters and variables
float fc2 = 5; // Cut-off frequency
float tau2 = 1 / (2 * 3.1415 * fc2);
float z1_1 = 0;
float z2_1 = 0;

// Buffer variables for computing moving average
int buffLength = 20; // approx. 20*2 ms time window
int i1 = 0;
float buffer1 [20] = {0.0};
float sum1 = 0;
int i2 = 0;
float buffer2 [20] = {0.0};
float sum2 = 0;

void processEMG()
{
  // Measure time step
  float dt = 0.001 * (millis() - t);
  t = millis();

  // Read raw myoelectric signals
  valueMuscle1 = analogRead(muscle1);
  valueMuscle2 = analogRead(muscle2);

  // High-pass filtering (remove signal mean value)
  float value1filt = tau1 / (tau1 + dt) * y1_1 + tau1 / (tau1 + dt) * (valueMuscle1 - x1_1);
  y1_1 = value1filt;
  x1_1 = valueMuscle1;
  float value2filt = tau1 / (tau1 + dt) * y2_1 + tau1 / (tau1 + dt) * (valueMuscle2 - x2_1);
  y2_1 = value2filt;
  x2_1 = valueMuscle2;

  // Signal rectification and squaring
  float value1filtrect = pow(value1filt, 2);
  float value2filtrect = pow(value2filt, 2);

  // Low-pass filtering
  float value1env = tau2 / (tau2 + dt) * z1_1 + dt / (tau2 + dt) * value1filtrect;
  z1_1 = value1env;
  float value2env = tau2 / (tau2 + dt) * z2_1 + dt / (tau2 + dt) * value2filtrect;
  z2_1 = value2env;

  // Moving average (compute signal RMS value)
  sum1 = sum1 - buffer1[i1];
  sum1 = sum1 + value1env;
  buffer1[i1] = value1env;
  i1++;
  if (i1 >= buffLength)
  {
    i1 = 0;
  }
  muscleActivity1 = sum1 / buffLength;
  sum2 = sum2 - buffer2[i2];
  sum2 = sum2 + value2env;
  buffer2[i2] = value2env;
  i2++;
  if (i2 >= buffLength)
  {
    i2 = 0;
  }
  muscleActivity2 = sum2 / buffLength;
}

Credits

Luis Hernán Cubillos

Luis Hernán Cubillos

1 project • 5 followers
Vicente Opaso

Vicente Opaso

1 project • 3 followers
Hannes Hase

Hannes Hase

0 projects • 2 followers

Comments