Lets Make Tech

EMG Motor Control Using An Arduino.

To get an idea of what this does I made a short video. In this video I am using a PIC18F4520, but an arduino or whatever you have laying around works fine too.

 

 

If you are wondering what the signal looks like I printed out the values onto a LCD display and have an oscilloscope reading as well in this video:

 

 

First we will need to build our EMG sensor.

First gather up everything that you need.

BOM (For EMG Sensor)                    

1X RR-ST-MTO-DI         
1X ULN2003                 
2X OPA2604A               
1X INA114AP                
1X PIC18F4520             
1X Shielded cable          
2X Alligator Clips (Two double sided wires or just three of the actual clips)           
1x IM414                     
3x T716 Electrode (Bag of disposable electrodes or three reusable ones)         
3x 9 volt battery clip      
1x 10 k resistor             
2x 220 resistor              
2x 1K resistor               
1x 1M resistor               
1x 100 resistor              
1x 47k resistor              
2x 4.7K resistor                        
2x .01 uf capacitor        
1x 4.7 uf capacitor        
1x 10 uf capacitor         
1x 1N5817 diode           
1x 1N5231B diode         

 

Here is an image of the schematic for it taken from the schematic used in the PIC project.

To prevent confusion I removed the PIC portion of the schematic, hence why it is jagged. 

One of the capacitors on this schematic is not labeled, it is 4.7uf. 

The white circles are electrodes.

The square around two of the electrode wires means that the cable should be shielded. 

I write -9 and +9 however your voltage will be lower. Just consider these positive and negative power supplies. Also not that negative in this case is not referring to ground. 

 

To make your power source take two 9V battery clips. Solder together positive on one clip with ground on the other clip. This will be ground. The positive wire left will be your positive source and the negative wire left will be your negative source. This will drop the voltage to lower than 9V, don't worry about it it is expected. Please check to make sure that your power source gives you positive, ground, and negative with your multimeter before attaching it to anything.   

undefined

 

Most EMG circuits give a very noisy signal, this circuit uses filters to give a very clean and smooth circuit that works great for this project. Other projects you may not want to filter and just get the raw signal. To do this remove the 10uf capacitor and everything after it. You may want to adjust the gain using the the resistors on pins 1 and 8 of your instrumentation amp.

 

Alright the hard part is over and you have a hopefully working EMG lets test it.

1. Take the wire that ends right after the 4.7 kohm resistor and hook it up to an oscilloscope. Also hook ground up to the oscilloscope.

2. Take electrode that connected to your op amp instead of the instrumentation amp and put it on the bony part of the back of your forearm just below your elbow.

3. Place one of the other electrodes on the top/middle of your bicep.

4. Place the other electrode on the bottom of your bicep near where your forearm is about to begin.

5. Look at the signal when flexing your arm, if the signal isn't there check your connections and that you have batteries hooked up. Make sure that when flexing as hard as you can your signal does not exceed 5V. The circuit above should prevent this, but better safe than sorry.

6.If it is safe and working how you want proceed, if not feel free to make any alterations you want. 

 

Get your stepper motor set up.

 I recommend getting a stepper motor that is 5V so that you can run it from the Arduino and also getting one that comes with a driver although you can use whatever you want.

I used the RioRand Stepper Motor 5V DC 4-Phase 5-Wire with ULN2003 Driver Board. It will only set you back about $8.00 USD

It needs 4 pins to go from the Arduino to the driver, then hook it up to 5V and ground. Simple. 

 

Code

Stepper Control 

 

void forward()
{
digitalWrite(A, HIGH);
delay(2);
digitalWrite(B, HIGH);
delay(2);
digitalWrite(A, LOW);
delay(2);
digitalWrite(C, HIGH);
delay(2);
digitalWrite(B, LOW);
delay(2);
digitalWrite(D, HIGH);
delay(2);
digitalWrite(C, LOW);
delay(2);
digitalWrite(A, HIGH);
delay(2);
digitalWrite(D, LOW);
delay(2);
digitalWrite(A, LOW);
delay(2);
steps++;
}

void reverse()
{
digitalWrite(A, HIGH);
delay(2);
digitalWrite(D, HIGH);
delay(2);
digitalWrite(A, LOW);
delay(2);
digitalWrite(C, HIGH);
delay(2);
digitalWrite(D, LOW);
delay(2);
digitalWrite(B, HIGH);
delay(2);
digitalWrite(C, LOW);
delay(2);
digitalWrite(A, HIGH);
delay(2);
digitalWrite(B, LOW);
delay(2);
digitalWrite(A, LOW);
delay(2);
steps--;
}

 

Read the signal from the EMG pin, convert it into the new step you want your motor to be in, keep track of your current step, go in the direction of the new step.

 

 Use the serial monitor see what kind of range you get from an analog read of the EMG in Pin. Very relaxed it was 0-13. Fully flexed it was about 600-800. Relaxed but moving my arm straight to bent it would go from 0 to 200.

This number is arbitrary and may be different for you. I use it to make my new step. I keep track of my current step and if it doesn't match the new step i move in the direction it needs to go.

 

So, here is the complete code. It is pretty basic but gives you a reference point on how to get started. I recommend making a multi-channel EMG and comparing the bicep to the triceps to get a more accurate idea of your arm's position. 

  

 

const int A = 12;
const int B = 11;
const int C = 10;
const int D = 9;
const int EMG = A0;

int steps = 0;
int newstep = 0;
float signal;
void forward();
void reverse();
void setup()   /*----( SETUP: RUNS ONCE )----*/
{
  pinMode(A, OUTPUT);
  pinMode(B, OUTPUT);
  pinMode(C, OUTPUT);
  pinMode(D, OUTPUT);  
  pinMode(EMG, INPUT);
  Serial.begin(9600);
}

void loop()   /*----( LOOP: RUNS CONSTANTLY )----*/
{
    signal=analogRead(EMG);
    Serial.print(" Newstep: ");
    Serial.print(newstep);
    Serial.print(" Step:");
    Serial.print( steps);
    newstep=signal;
    if (newstep<steps)
    {
      reverse();
    }
    if (newstep>steps)
    {
      forward();
    }

}
void forward()
{
    digitalWrite(A, HIGH); 
    delay(2);   
    digitalWrite(B, HIGH);
    delay(2);
    digitalWrite(A, LOW);
    delay(2);
    digitalWrite(C, HIGH);
    delay(2);
    digitalWrite(B, LOW);
    delay(2);
    digitalWrite(D, HIGH);
    delay(2);
    digitalWrite(C, LOW); 
    delay(2);
    digitalWrite(A, HIGH);
    delay(2);
    digitalWrite(D, LOW);
    delay(2);
    digitalWrite(A, LOW);
    delay(2);
    steps++;
}

void reverse()
{
    digitalWrite(A, HIGH);
    delay(2);
    digitalWrite(D, HIGH);
    delay(2);
    digitalWrite(A, LOW);
    delay(2);
    digitalWrite(C, HIGH);  
    delay(2);     
    digitalWrite(D, LOW); 
    delay(2);
    digitalWrite(B, HIGH);
    delay(2);
    digitalWrite(C, LOW);  
    delay(2);
    digitalWrite(A, HIGH);
    delay(2);
    digitalWrite(B, LOW);
    delay(2);  
    digitalWrite(A, LOW);
    delay(2);
    steps--;
}

 

Have fun, if you make something cool be sure to share in the comments.

Signal Generator

I threw this together real quick to test out a signal generator and it is a bit sloppy and written around someone else's code (their code isn't sloppy mine is). This code should just be used as a reference or for testing and credit given to David Mills for the part of the code that I borrowed from him.

This tutorial shows how to make a signal generator using the AD9850 DDS signal generator, an Arduino, and a potentiometer. I used code written by David Mills http://webshed.org/wiki/AD9850_Arduino to interface with the signal generator and added to it to allow you to set the frequency using a potentiometer and change the mode between Hz, kHz, and MHz by pressing a button. As David Mills requested, don't claim the code as your own or try to prevent anyone else using it other than that do with it what you will. 

If you are interested in making music with this check out this previous post.

BOM

-1 Arduino

-1 LCD display (optional)

-1 AD9850 DDS signal generator

-1 Touch button

Connections

Note the connections between the arduino and the AD9850 DDS signal generator. Feel free to change them if you need.

#define CLOCK 2 //pin connections for DDS
#define LOAD 3
#define DATA 7
#define RESET 8

For the LCD I used the following connections LiquidCrystal lcd(5, 6, 9, 10, 11, 12); For additional help connecting the LCD see this tutorial

Note that in this tutorial they use different pins and it uses a potentiometer being used on pin 3, I just used a resistor to ground. The value will vary between screens and it is just used to set contrast. If your screen is all black or white you may need to use a different resistor or adjust the pot. 

 

For the pot and button I used the following pins

#define POTPIN A0
#define MODE 13

 

Use the button to change the mode, then depending on what mode it is, change how you interpret the pot.

Interpreting the pot, convert the reading from the pot to a value between 0 and 1,000 for mode 1, 1,000-1,000,000 for mode 2, and 1,000,000-30,000,000 for mode 3. You will want to limit it to 30,000,000 because past that the hardware doesn't work. You may need to edit the code I used for the potentiometer that you use.

Send this to the AD9850 DDS signal generator using the code that David Mills wrote.

Pro tip: Display the frequency and mode on the LCD so you know what you are doing. 

Alternative inputs: The potentiometer I used wasn't very accurate and the value changed a lot when set all the way up or down. Use buttons or a keypad to set the frequency to get an exact amount and easily set it how you want.   

 

The complete code:

#include <LiquidCrystal.h>

LiquidCrystal lcd(5, 6, 9, 10, 11, 12);


#define DDS_CLOCK 125000000

#define  CLOCK  2  //pin connections for DDS
#define  LOAD 3 
#define  DATA  7
#define  RESET 8
#define  POTPIN A0
#define  MODE 13
float hrz;
float pot;
float temp;
int mode=0;
int moderead;
void setup()
{
  pinMode(DATA, OUTPUT); 
  pinMode(CLOCK, OUTPUT); 
  pinMode(LOAD, OUTPUT); 
  pinMode(RESET, OUTPUT); 
  pinMode(POTPIN, INPUT);
  pinMode(MODE, INPUT);
  AD9850_init();
  AD9850_reset();
  lcd.begin(16, 2);
  Serial.begin(9600);
  
  //SetFrequency(hrz);

}

void loop()
{ 
 moderead=digitalRead(MODE); 
 pot=analogRead(POTPIN);  //14-1000
 hrz=((pot-6)*10000000); 
 if (moderead==LOW)
 {
   mode++;
   delay(500);
   if (mode>2)
   {
     mode=0;
   }
 }
 
 if (mode==0)
 {
   hrz=(hrz/10000000);
    lcd.setCursor(0, 1);
    lcd.print("MODE: Hz");

 }
 
 if (mode==1)
 {
   hrz=(hrz/10000);
    lcd.setCursor(0, 1);
    lcd.print("MODE: KHz");
 }
 
 if (mode==2)
 {
    hrz=(hrz/100);
    lcd.setCursor(0, 1);
    lcd.print("MODE: MHz");
 }
if (mode!=3)
{
 if (hrz<0)
 {
   hrz=0;
   
   if (temp>1000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz,1);
   lcd.print("HZ   ");
 }
 
 if (hrz>0 && hrz<1000)
 {
   if (temp>1000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz,1);
   lcd.print("HZ   ");
 }
 if (hrz>1000 && hrz<1000000)
 {
   if (temp<1000|| temp>1000000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz/1000,1);
   lcd.print("KHZ   ");
 }
  if (hrz>1000000 && hrz<30000000)
 {
   if (temp<1000000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz/1000000,1);
   lcd.print("MHZ   ");
 }
 if (hrz>30000000)
 {
   hrz=30000000;
   if (temp<1000000)
   {
     lcd.clear();
   }
   lcd.setCursor(0, 0);
   lcd.print(hrz/1000000,1);
   lcd.print("MHZ   ");
 }
 
 
 SetFrequency(hrz);
 temp=hrz;
} 

}
void SetFrequency(unsigned long frequency)
{
  unsigned long tuning_word = (frequency * pow(2, 32)) / DDS_CLOCK;
  digitalWrite (LOAD, LOW); 

  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 8);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 16);
  shiftOut(DATA, CLOCK, LSBFIRST, tuning_word >> 24);
  shiftOut(DATA, CLOCK, LSBFIRST, 0x0);
  digitalWrite (LOAD, HIGH); 
}

void AD9850_init()
{

  digitalWrite(RESET, LOW);
  digitalWrite(CLOCK, LOW);
  digitalWrite(LOAD, LOW);
  digitalWrite(DATA, LOW);
}

void AD9850_reset()
{
  //reset sequence is:
  // CLOCK & LOAD = LOW
  //  Pulse RESET high for a few uS (use 5 uS here)
  //  Pulse CLOCK high for a few uS (use 5 uS here)
  //  Set DATA to ZERO and pulse LOAD for a few uS (use 5 uS here)

  // data sheet diagrams show only RESET and CLOCK being used to reset the device, but I see no output unless I also
  // toggle the LOAD line here.

  digitalWrite(CLOCK, LOW);
  digitalWrite(LOAD, LOW);

  digitalWrite(RESET, LOW);
  delay(5);
  digitalWrite(RESET, HIGH);  //pulse RESET
  delay(5);
  digitalWrite(RESET, LOW);
  delay(5);

  digitalWrite(CLOCK, LOW);
  delay(5);
  digitalWrite(CLOCK, HIGH);  //pulse CLOCK
  delay(5);
  digitalWrite(CLOCK, LOW);
  delay(5);
  digitalWrite(DATA, LOW);    //make sure DATA pin is LOW

    digitalWrite(LOAD, LOW);
  delay(5);
  digitalWrite(LOAD, HIGH);  //pulse LOAD
  delay(5);
  digitalWrite(LOAD, LOW);
  // Chip is RESET now
}






 

 

 

 

How To Make A Simple Robot

If you ever wanted to make a robot but didn't know where to start or have limited electrical/code knowledge here is a tutorial for you. 

 

undefined

 

First gather up these supplies:

BOM

-Arduino mega 

-Arduino motor shield

-Premade robot chassis of your choice I recommend one with two motorized wheels and one swivel wheels or two tank treads. I used this. Or if you want you can make your own chassis. 

-Jumper wires

-Soldering supplies & shrink tube. 

-Sensors of your choice. I used six proximity sensors. If you want you could use just one and have it rotate on a stepper motor. 

-Power Supply: 9v battery and clip, 9V worth of AA, what ever you have available just be reasonable. 

Connections

Just plug the motor shield into the arduino pins and hook the motor pins up to the terminal. Add a 9v battery to the power terminal if that's how you want to power it. 

For the proximity sensors solder together some wires so that it goes from 5v on the arduino to the + pin on each of the proximity sensors. Do the same for ground. 

The motor shield uses some of the pins by default so you will need to use different pins for the proximity sensors. So use whatever pins you want but keep pins A0, A1, 8, 9, 3, 11, 12, and 13 open for the motor shield. 

Pin Functions

In the setup set the pin mode to be either input or output for the pins you use

Pins A0 and A1 are for current sensing, I didn't use these but you can if you want. (Good for preventing stalls) If you use these pins set them as an input.

Pins 8 and 9 are the brakes, make sure you disengage these in the setup by setting it as an output and to LOW. After that using these is optional. I make sure they are disengaged by including it in the motor control function 

Pins 3 and 11 are for setting the speed 0-255. They use PWM set these pins as an output. 

Pins 12 and 13 set the direction, these pins should also be set as an output.

For the proximity sensor the trigger pin is used to activate the sensor and should be set as an output and the echo pin to gather data and should be set as an input. 

 

Code

Declare Variables


//SPEEDS
const int FAST = 200;
const int SLOW = 100;
//declare sensor pins
//sensor 1

const int pingPin1 = 22;
const int echoPin1 = 23;
//sensor2
const int pingPin2 = 24;
const int echoPin2 = 25;
//sensor 3
const int pingPin3 = 26;
const int echoPin3 = 27;
//sensor 4
const int pingPin4 = 28;
const int echoPin4 = 29;
//sensor 5
const int pingPin5 = 30;
const int echoPin5 = 31;
//sensor 6
const int pingPin6 = 32;
const int echoPin6 = 33;
//store distance read
long cm1, cm2, cm3, cm4, cm5, cm6;
//The direction of both motors and speed of each motor
long DIRECTION, speeda, speedb;

 

Setup: Set the pin mode of each pin. 

void setup() {
//Setup Motor A
pinMode(12, OUTPUT); 
pinMode(9, OUTPUT); 
//Setup Motor B
pinMode(13, OUTPUT); 
pinMode(8, OUTPUT); 
//Setup ping pins
pinMode(pingPin1, OUTPUT);
pinMode(echoPin1, INPUT);
pinMode(pingPin2, OUTPUT);
pinMode(echoPin2, INPUT);
pinMode(pingPin3, OUTPUT);
pinMode(echoPin3, INPUT);
pinMode(pingPin4, OUTPUT);
pinMode(echoPin4, INPUT);
pinMode(pingPin5, OUTPUT);
pinMode(echoPin5, INPUT);
pinMode(pingPin6, OUTPUT);
pinMode(echoPin6, INPUT);
}

 

 Now for the main loop

void loop()
{
//read sensors
ping();
//interpret data from sensors
logic();
//use data from sensors
motorcontrol();
}

 

Now we write the functions that we have called in the main loop. 

Ping: Turn the trigger pin off, on, off. Delay 2 microseconds after the first off, then 5 microseconds after turning it on. Read the signal with pulseIn() and convert to cm using the conversion function we will write later. 

long ping()
{
//variables
long duration;
//quick burst of electricity to the proximity sensor to activate it
digitalWrite(pingPin1, LOW);
delayMicroseconds(2);
digitalWrite(pingPin1, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin1, LOW);
//reads signal that bounces back
duration= pulseIn(echoPin1,HIGH);
//converts duration to cm using function below
cm1 = microsecondsTocm(duration);
digitalWrite(pingPin2, LOW);
delayMicroseconds(2);
digitalWrite(pingPin2, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin2, LOW);
//reads signal that bounces back
duration= pulseIn(echoPin2,HIGH);
//converts duration to cm using function below
cm2 = microsecondsTocm(duration);
Serial.print(cm2);
digitalWrite(pingPin3, LOW);
delayMicroseconds(2);
digitalWrite(pingPin3, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin3, LOW);
//reads signal that bounces back
duration= pulseIn(echoPin3,HIGH);
//converts duration to cm using function below
cm3 = microsecondsTocm(duration);
digitalWrite(pingPin4, LOW);
delayMicroseconds(2);
digitalWrite(pingPin4, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin4, LOW);
//reads signal that bounces back
duration= pulseIn(echoPin4,HIGH);
//converts duration to cm using function below
cm4 = microsecondsTocm(duration);
digitalWrite(pingPin5, LOW);
delayMicroseconds(2);
digitalWrite(pingPin5, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin5, LOW);
//reads signal that bounces back
duration= pulseIn(echoPin5,HIGH);
//converts duration to cm using function below
cm5 = microsecondsTocm(duration);
digitalWrite(pingPin6, LOW);
delayMicroseconds(2);
digitalWrite(pingPin6, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin6, LOW);
//reads signal that bounces back
duration= pulseIn(echoPin6,HIGH);
//converts duration to cm using function below
cm6 = microsecondsTocm(duration);
}

 

Convert to cm

//converts ping duration to cm
long microsecondsTocm(long microseconds)
{
return microseconds / 29 / 2;
}

 

Logic: This is what determines what the robot does based on the sensor input. This assumes we have 6 sensors: three in the back, three in the front. The middle sensors will be straight while the side sensors will be diagonal. When something triggers the center sensors it will reverse direction, if something is closer on one side sensor than the other, it will increase speed on one side and decrease speed on the other resulting in it turning away from the object it sensed. Please note that the placement of the sensors matters.  

 

void logic()
{
/*
Direction Shifting:
The difference in distance between switching from forward to reverse (30) and
switching from reverse to forward (50) results in the robot having a tenancy
to go forward.
*/

//if it is going forward and gets close to something in front of it, switch to reverse
if (DIRECTION==LOW && cm1 > cm4 && cm4<30)
{
DIRECTION=HIGH;
}
//if it is in reverse and something is moderatly close switch to forward
if (DIRECTION==HIGH && cm4 > cm1 && cm1<30)
{
DIRECTION=LOW;
}
/*
Speed Control:
The speeds between the two wheels will always be different resulting in the robot having
a wavy motion as it moves thus "looking around". The difference in speeds also results
in the robot having a tenancy to move towards whichever side has the greatest amount of distance
thus moving away from walls and other objects. When in reverse the wheel speed difference is also
reversed.
*/
if (DIRECTION ==LOW)
{
if (cm5 > cm6)
{
speeda=FAST;
speedb=SLOW;
}
if (cm6 > cm5)
{
speeda=SLOW;
speedb=FAST;
}
}
if (DIRECTION ==HIGH)
{
if (cm2 > cm3)
{
speeda=FAST;
speedb=SLOW;
}
if (cm3 > cm2)
{
speeda=SLOW;
speedb=FAST;
}
}
}

 

Motor Control: Alright so now the robot knows what it want it wants to do and needs to tell the motor shield what to do.

//motor control
void motorcontrol()
{
digitalWrite(12, DIRECTION); 
digitalWrite(9, LOW); 
analogWrite(3, speeda); 
digitalWrite(13, DIRECTION); 
digitalWrite(8, LOW); 
analogWrite(11, speedb); 
}
Home