Hello All,
I have been playing around with an EKG/EMG Shield to detect a muscle movement to control the InMoov Hand.
The progress is good, but needs a lot of work still.
This is the first video I have made: http://www.youtube.com/watch?v=ZUIqR2JodlY
I will continue the development and will post the updates and Tutorials.
I have implemented new features, now I can make multiples movements controlled by the muscle with combination of specific modes. The modes can be configured by a smartphone and wifi.
Here is a small tutorial:
What you need:
InMoov Arm
Arduino uno R3
EMS Shild - https://www.olimex.com/Products/Duino/Shields/SHIELD-EKG-EMG/ (
Wifi Shield (UDP compatible)- http://www.ebay.com/itm/UART-WiFi-Server-Client-Module-Kits-Arduino-Compatible-with-shield-for-Arduino-/130766449058?pt=LH_DefaultDomain_0&hash=item1e7249a9a2
Software TouchOSC (for smartphone and Workstation) - http://hexler.net/software/touchosc
The Wifi Module should be configured as UDP Server with fixed IP (ip and network from phone hotspot) and 57600 bps
This is the TouchOSC Layout:
https://dl.dropboxusercontent.com/u/14231908/EMG.touchosc
This is the code for Arduino:
#include <Servo.h>
#include <compat/deprecated.h>
#include <FlexiTimer2.h>
#define SAMPFREQ 256 // ADC sampling rate 256
#define TIMER2VAL (1024/(SAMPFREQ)) // Set 256Hz sampling frequency
volatile unsigned char CurrentCh=0; //Current channel being sampled.
volatile unsigned int ADC_Value = 0; //ADC current value
volatile unsigned int ADC_Value1 = 0; //ADC current value
Servo myServo1;
Servo myServo2;
Servo myServo3;
Servo myServo4;
Servo myServo5;
Servo myServo6;
int pos = 0; // variable to store the servo position
int modo = 0;
int oscMsg[12]; // buffer for incoming OSc packet
int inByte = 0; // incomming serial byte
int inbyteIndex=0; // incomming bytes counter
float msgVal=0; // resulting message value
void setup()
{
Serial.begin(57600); // debug port
myServo1.attach(13); // pulso
myServo2.attach(8); //dedo mindinho
myServo3.attach(9); //dedo2
myServo4.attach(10); //dedo3
myServo5.attach(11, 576, 2384); //indicador
myServo6.attach(12); //poegar
}
void loop(){
int value99 = 0; //contador de numero de vezes
int value98 = 0; //contador de ups
while (value99 < 500) { //Here finds the muscle pattern.
if (Serial.available()) { // if we have new byte from LAN
if (getOscMsg()==1){ // add it to message while is is not ready
if (byte(oscMsg[1]) != 32){ //para evitar o envio de mensagem vazia
Serial.print(oscMsg[1]); // or print the OSC message name
Serial.print(" / ");
Serial.println(msgVal); // and vaule
int value = msgVal;
switch(oscMsg[1]){
case'a':
if(value > 0 && value <= 180){
modo = 1; //variavel para determinar o movimento
Serial.print("MODO ");
Serial.println(modo);
value = 0;
break;
}
case'b':
if(value > 0 && value <= 180){
modo = 2; //variavel para determinar o movimento
Serial.print("MODO ");
Serial.println(modo);
value = 0;
break;
}
case'c':
if(value > 0 && value <= 180){
modo = 3; //variavel para determinar o movimento
Serial.print("MODO ");
Serial.println(modo);
value = 0;
break;
}
case'd':
if(value > 0 && value <= 180){
modo = 4; //variavel para determinar o movimento
Serial.print("MODO ");
Serial.println(modo);
value = 0;
break;
}
case'e':
if(value > 0 && value <= 180){
modo = 5; //variavel para determinar o movimento
Serial.print("MODO ");
Serial.println(modo);
value = 0;
break;
}
case'f':
if(value > 0 && value <= 180){
modo = 6; //variavel para determinar o movimento
Serial.print("MODO ");
Serial.println(modo);
value = 0;
break;
}
}
}
}
}
delay(1);
ADC_Value = analogRead(CurrentCh);
value99 = value99+1;
if(ADC_Value > 400){
value98 = value98 + 1; //caso tenha o valor alto, adiciona um no contador
}
if(ADC_Value < 250){
value98 = value98 + 1; //caso tenha o valor alto, adiciona um no contador
}
}
if (value98 < 25){ //off
Serial.print("off"); //this is packet header for my application
Serial.println(ADC_Value);
myServo1.write(90);
myServo2.write(10);
myServo3.write(10);
myServo4.write(10);
myServo5.write(10);
myServo6.write(10);
delay(1);
}
else if (value98 >= 45){ //on
Serial.print("on"); //this is packet header for my application
Serial.println(ADC_Value);
Serial.println(value98);
if(modo <= 1){
Serial.print("MODO ");
Serial.println(modo);
myServo1.write(90);
myServo2.write(150); // fechado
myServo3.write(150);
myServo4.write(150);
myServo5.write(150);
myServo6.write(150);
delay(1);
}
if(modo ==2){
Serial.print("MODO ");
Serial.println(modo);
myServo1.write(90);
myServo2.write(10); // pinca
myServo3.write(10);
myServo4.write(10);
myServo5.write(140);
myServo6.write(150);
delay(1);
}
if(modo ==3){
Serial.print("MODO ");
Serial.println(modo);
myServo1.write(90);
myServo2.write(150); // aponta
myServo3.write(150);
myServo4.write(150);
myServo5.write(15);
myServo6.write(20);
delay(1);
}
if(modo ==4){
Serial.print("MODO ");
Serial.println(modo);
myServo1.write(90);
myServo2.write(15); // Spider
myServo3.write(150);
myServo4.write(150);
myServo5.write(15);
myServo6.write(20);
delay(1);
}
if(modo ==5){
Serial.print("MODO ");
Serial.println(modo);
myServo1.write(90);
myServo2.write(150); // Joia
myServo3.write(150);
myServo4.write(150);
myServo5.write(150);
myServo6.write(20);
delay(1);
}
if(modo ==6){
Serial.print("MODO ");
Serial.println(modo);
myServo1.write(90);
myServo2.write(15); // hang
myServo3.write(150);
myServo4.write(150);
myServo5.write(150);
myServo6.write(20);
delay(1);
}
}
}
// function to process simple OSC message sent by TouchOSC for iphone / ipad
float getOscMsg(){
inByte=Serial.read(); // read next serial byte
if (inByte == 47){ // if byte = slash it's message start
inbyteIndex=0; // and we set array pointer to 0
}
if ((inbyteIndex <= 11) && (inbyteIndex >= 0)){ // is it time to finish or can we start?
oscMsg[inbyteIndex]=inByte; // we add the byte to the array
inbyteIndex++; // increase array counter
}
if (inbyteIndex == 11){ // end of the message
inbyteIndex=-1; // set the pointer to -1 so we stop processing
union u_tag { // this is array to float conversation routine
byte bytes[4]; // I copied from Arduino.cc forum
float buffer;
}
u;
u.bytes[0]=oscMsg[11]; // to decode we have to supply bytes inr everse order
u.bytes[1]=oscMsg[10];
u.bytes[2]=oscMsg[9];
u.bytes[3]=oscMsg[8];
msgVal = u.buffer; // byte array to float
return 1; // signal we are ready to display value
}
return 0; // in this case the message is not ready yet
Just had a look at your video and one of the comments We would like to put the motors outside of the arm (for disabled people) and Gael langevin comes to meet us in Rennes at the end of June with Nicolas Huchet to explore solutions
--
You received this message because you are subscribed to the Google Groups "InMoov" group.
To unsubscribe from this group and stop receiving emails from it, send an email to inmoov+un...@googlegroups.com.
To post to this group, send email to inm...@googlegroups.com.
Visit this group at http://groups.google.com/group/inmoov.
For more options, visit https://groups.google.com/groups/opt_out.
Hello Jackie,
As myoelectric is something relative new, I could not find material about it. My tip is start on the big muscles and attached to the computer to find your patterns and than take down to lower platform like arduino. I have ordered a myoelectric sensor with more channels so soon I will be able to decode more complex movements. For now I am combining the myoelectric stimulation with different modes chosen on a smartphone to be able to do practically any movements.
Have you seen this: http://pranoysaji.wordpress.com/2013/05/30/myo-the-band-that-control-the-computer-by-listening-to-our-muscle-movement/
I think might be of help.
--
--
You received this message because you are subscribed to a topic in the Google Groups "InMoov" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/inmoov/vI1sG6WvnQc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to inmoov+un...@googlegroups.com.
@ Jackie,
I see now what you mean! That is a good project.
Since the muscle might have atrophy, the fine-tuning can be hard, but can be done.
Everything I use is Arduino based. A Shield from Olimex to get the signal( OLIMEX-EKG/EM) and I a PC Software to see the patterns. The software is ElecGuru. (https://www.olimex.com/Products/Duino/Shields/SHIELD-EKG-EMG/resources/ShieldEkgEmgDemo.zip)
I started with try and error to manage the first movements, but since you are going to have weaker signals, I recommend you reading this article:
http://www.stanford.edu/~shenoy/GroupPublications/RiveraAlvidrezEtAlIEEEEMBC2010.pdf
@ Gael,
Yes, that ought be very fragile, thus, not practical!
Have you seen this Linear Servos?
I am developing an Open Source Myoelectric Prosthetic Arm and the size and power consumption of the SparkCore will be decisive.
Take a look:
--
You received this message because you are subscribed to the Google Groups "InMoov" group.
To unsubscribe from this group and stop receiving emails from it, send an email to inmoov+un...@googlegroups.com.
--
You received this message because you are subscribed to a topic in the Google Groups "InMoov" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/inmoov/vI1sG6WvnQc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to inmoov+un...@googlegroups.com.
To post to this group, send email to inm...@googlegroups.com.
Visit this group at http://groups.google.com/group/inmoov.
For more options, visit https://groups.google.com/groups/opt_out.
<photo1.JPG>
<photo2.JPG>
I read about Bebionic hand and that guys use servos in the hand. I found some information how they build there fingers. It seems very easy(see point (C)): http://www.rehab.research.va.gov/jour/2013/505/images/belter505f02th.jpg
Full description about hands you can find by this link: http://www.rehab.research.va.gov/jour/2013/505/belter505.html
--
You received this message because you are subscribed to the Google Groups "InMoov" group.
To unsubscribe from this group and stop receiving emails from it, send an email to inmoov+un...@googlegroups.com.