I am new to coding, and am undergoing a school project where I need to control a linear actuator using EMG signals from the forearm (through an Arduino Nano).
How it Works
Essentially the EMG pads detect a signal, send them to A0 input on Arduino Nano which processes them and if they're higher than a threshold (when clenching fist), the Arduino Nano sends a signal to motor controller to extract actuator arm.
Hardware
Arduino Nano
EMG Sensor (AD8232 - I know it is a heart rate monitor, it also works for EMG)
TB6612FNG
Breadboard & Jumper Wires
Problems
My first problem is that the graph produced from the EMG signals is not always starting at the same value, it deviates from a constant of 235-240. This means my values aren't correct for when I flex and the graph spikes, as shown. Is there a way to make the constant always the same, so the spikes can be similar.
My second problem is the code in general, it has no errors, but I'm unsure if I have done it correctly?
/*#################__add includes here__#############################################################################*/
#include <SparkFun_TB6612.h> //include sparkfun TB6612 library
/*#################__add instance's here__#############################################################*/
//Threshold for actuator control with muscle sensor.
//You can set a threshold according to the maximum and minimum values of the muscle sensor.
#define THRESHOLD 240 //Need to change according to graph, check by Serial Plotter (Ctrl+Shift+L)
//Pin number where the sensor is connected. (Analog 0)
#define EMG_PIN 0
//Define Linear Actuator
#define AIN1 8 //AI1 of control board connected to Arduino pin 12
#define AIN2 7 //AI2 of control board connected to Arduino pin 11
#define PWMA 6 //PWMA of control board connected to Arduino pin 10
#define STBY 4 //STBY of control board connected to Arduino pin 13
const int offsetA = 1; //needed by sparkfun library to set up the motor instance(actuator) below, (value can be 1 or -1)
Motor actuator = Motor(AIN1, AIN2, PWMA, offsetA, STBY); //set up an instance of Motor called actuator
/*-------------------------------- void setup ------------------------------------------------*/
void setup() {
// initialize the serial communication:
Serial.begin(115200);
pinMode(11, INPUT); // Setup for leads off detection LO +
pinMode(10, INPUT); // Setup for leads off detection LO -
}
/*-------------------------------- void loop ------------------------------------------------*/
void loop() {
if ((digitalRead(11) == 1) || (digitalRead(10) == 1)) {
Serial.println('!');
}
else {
// send the value of analog input 0:
Serial.println(analogRead(A0));
}
//Wait for a bit to keep serial data from saturating
delay(2);
//The "Value" variable reads the value from the analog pin to which the sensor is connected.
int value = analogRead(EMG_PIN);
//If the sensor value is GREATER than the THRESHOLD, the actuator will extract.
if (value > THRESHOLD) {
void mode_0(void); //wait here for user command
void mode_1(void); //extract horizontally
void mode_4(void); //retract mode 1
//extract horizontally
void mode_1(void);
actuator.drive(255, 1700); //extract horizontally, 255 connects power forward polarity, 1700 is delay in ms it takes to fully extract
actuator.brake(); //Cuts power to actuator
//If the sensor is LESS than the THRESHOLD, the servo motor will turn to 10 degrees.
} else
//retract horizontally
{
void mode_4(void);
actuator.drive(-255, 1700); //retract horizontally, -255 connects power reverse polarity, 1700 is delay in ms it takes to fully retract
actuator.brake(); //cuts actuator power
}
}
I would try processing the signal by monitoring changes in the signal, instead of the absolute value of the signal, and applying some smoothing to remove the noise in the signal.
To monitor the changes, use an extra variable to save the previous value of analogRead() and subtract that from the new value to find the difference (+ or -).
To smooth those differences, use a third variable which you update each time to 80% of its current value plus 20% of the difference calculated above. You can try adjusting the 80/20 values to adjust how heavy or light the smoothing effect is. For example 98% and 2% would be much heavier smoothing.
Plot the smoothed value on serial plotter and see if you can achieve something more reliable for the sketch to base decisions on.
Thank you very much for the reply. I fixed the code, just in regards to monitoring the changes on the graph, I understand what you're saying, I'm just unsure how I would write this in code? Sorry to take your time, I appreciate the help.
I also noticed that the values you are seeing have only a very small range between around 237 and 243, a range of around 6 values versus the full range of 1024 values that the Arduino can measure. Are these the lowest and highest values you ever see with this sensor?
If so, it might work better if you amplify the signal. This may help reduce the noise. Does the sensor have any way to do that?
Yes that is the full range I have always seen. And the sensor doesn't seem to have any amplification devices. I'll give the code a go with what you said, sorry if it takes some time, I'll try to finish it asap for some feedback.
I watched a tutorial and here is the new code. The averaging part is at the end.
/*#################__add includes here__#############################################################################*/
#include <SparkFun_TB6612.h> //include sparkfun TB6612 library
/*#################__add instance's here__#############################################################*/
//Threshold for actuator control with muscle sensor.
//You can set a threshold according to the maximum and minimum values of the muscle sensor.
#define THRESHOLD 240 //Need to change according to graph, check by Serial Plotter (Ctrl+Shift+L)
//Pin number where the sensor is connected. (Analog 0)
#define EMG_PIN A0
//Define Linear Actuator
#define AIN1 8 //AI1 of control board connected to Arduino pin 12
#define AIN2 7 //AI2 of control board connected to Arduino pin 11
#define PWMA 6 //PWMA of control board connected to Arduino pin 10
#define STBY 4 //STBY of control board connected to Arduino pin 13
const int offsetA = 1; //needed by sparkfun library to set up the motor instance(actuator) below, (value can be 1 or -1)
Motor actuator = Motor(AIN1, AIN2, PWMA, offsetA, STBY); //set up an instance of Motor called actuator
/*-------------------------------- void setup ------------------------------------------------*/
void setup() {
// initialize the serial communication:
Serial.begin(115200);
pinMode(11, INPUT); // Setup for leads off detection LO +
pinMode(10, INPUT); // Setup for leads off detection LO -
}
/*-------------------------------- void loop ------------------------------------------------*/
void loop() {
if ((digitalRead(11) == 1) || (digitalRead(10) == 1)) {
Serial.println('!');
}
else {
// send the value of analog input 0:
Serial.println(analogRead(A0));
}
//Wait for a bit to keep serial data from saturating
delay(2);
//The "Value" variable reads the value from the analog pin to which the sensor is connected.
int value = analogRead(EMG_PIN);
//If the sensor value is GREATER than the THRESHOLD, the actuator will extract.
if (value > THRESHOLD) {
void mode_0(void); //wait here for user command
void mode_1(void); //extract horizontally
void mode_4(void); //retract mode 1
//extract horizontally
void mode_1(void);
actuator.drive(255, 1700); //extract horizontally, 255 connects power forward polarity, 1700 is delay in ms it takes to fully extract
actuator.brake(); //Cuts power to actuator
//If the sensor is LESS than the THRESHOLD, the servo motor will turn to 10 degrees.
} else
//retract horizontally
{
void mode_4(void);
actuator.drive(-255, 1700); //retract horizontally, -255 connects power reverse polarity, 1700 is delay in ms it takes to fully retract
actuator.brake(); //cuts actuator power
}
int numReads = 10; // number of samples
int senseSum = 0; // sum of sensor readings
for(int k = 0; k < numReads; k++) {
senseSum += analogRead(A0);
delay(1);
}
int senseAve = senseSum / numReads; // average sensor readings
Serial.println(senseAve);
}