_SKU_SEN0240_肌电传感器_Analog_EMG_Sensor_by_OYMotion - jimaobian/DFRobotWikiCn GitHub Wiki
这是由DFRobot与OYMotion合作推出的一款肌电传感器模块。本传感器模块通过检测人体的表面肌电信号(sEMG),进而反应出人体肌肉和神经的活动情况。 本传感器模块集成了滤波、放大电路,将范围在±1.5mV内的微弱人体表面肌电信号进行1000倍放大,并通过差分输入、模拟滤波电路的方式对噪音(特别是工频干扰)进行有效抑制。输出信号为模拟量形式,以1.5V为基准电压,0~3.0V量程的输出。输出信号的大小取决于选定肌肉的活动量,输出信号的波形可显著指示被观察位置皮下肌肉的情况,方便做肌电信号的分析与研究,如使用Arduino作为控制器检测肌肉活动情况,如肌肉是否紧绷,强度如何,是否疲劳等。 本产品是一种主动感应传感器,能提供高质量的信号搜集,且易于使用。不论是被用到静态还是动态的应用领域,仅需要一些极为简单的准备工作即可。本产品使用干电极,无须导电凝胶也可得到良好的信号质量,因此具有寿命长、使用简单方便等特点,更适合普通用户。而采用凝胶探头的医用电极通常为一次性,使用起来较为麻烦。 本产品的测量具有非侵入性、无创伤、操作简单等优点,可用于人机交互等相关应用。虽然测量肌肉活动历来被用于医学研究,然而随着不断缩小但功能更强大的微控制器和集成电路的完善,肌电图电路和传感器也逐渐被应用于各种控制系统。
供电电压在3.3~5.5V之间,供电电流不小于20mA,纹波与其他噪音要小。推荐使用经过稳压的直流电压。
肌电信号的有效频谱范围为20Hz~500Hz,推荐采用分辨率不低于8bit、有效采样频率不低于1KHz的模数转换器(ADC)进行采样与数字化,以保留尽量多的原始信息。
采用配套的金属干电极板,需将电极板保持和肌肉方向一致。
本品并非专业医疗仪器,不能作为辅助配件参与诊断和治疗。 |
-
信号处理板
- 供电电压:+3.3V~5.5V
- 工作电压:+3.0V
- 检测范围:+/-1.5mV
- 电极接口:PJ-342
- 模块接口:PH2.0-3P
- 输出范围:0~3.0V
- 工作温度:0~50℃
- 板子尺寸:22 * 35 mm
-
干电极板
- 电极接口:PJ-342
- 电极线长:50cm
- 板子尺寸:22 * 35 mm
- 重量:36g
:[[File:Emg_board_function2.png|肌电传感器信号处理板 ]] |
标号 | 名称 | 功能描述 |
1 | A | 模拟信号输出端(0~3.0V) |
2 | + | 电源输入正极(3.3~5.5V) |
3 | - | 电源输入负极 |
4 | PJ-342 | 电极连线接口 |
肌电传感器信号处理板管脚定义
本教程将演示如何使用这款肌电传感器,通过Arduino IDE的Serial Plotter打印出肌电波形。
-
硬件
- 1 x Arduino UNO控制板(或类似)
- 1 x 肌电传感器信号处理板
- 1 x 肌电传感器干电极
- 1 x 干电极连接线
- 1 x 3P模拟信号线
- 若干 杜邦线
-
软件
- Arduino IDE(推荐1.8.2及以上), 点击下载Arduino IDE
本样例代码需要EMGFilters库文件,请先下载库文件后并安装。 如何安装库?
|
/*
* Copyright 2017, OYMotion Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "EMGFilters.h"
#define TIMING_DEBUG 1
#define SensorInputPin A0 // input pin number
EMGFilters myFilter;
// discrete filters must works with fixed sample frequence
// our emg filter only support "SAMPLE_FREQ_500HZ" or "SAMPLE_FREQ_1000HZ"
// other sampleRate inputs will bypass all the EMG_FILTER
int sampleRate = SAMPLE_FREQ_1000HZ;
// For countries where power transmission is at 50 Hz
// For countries where power transmission is at 60 Hz, need to change to
// "NOTCH_FREQ_60HZ"
// our emg filter only support 50Hz and 60Hz input
// other inputs will bypass all the EMG_FILTER
int humFreq = NOTCH_FREQ_50HZ;
// Calibration:
// put on the sensors, and release your muscles;
// wait a few seconds, and select the max value as the threshold;
// any value under threshold will be set to zero
static int Threshold = 0;
unsigned long timeStamp;
unsigned long timeBudget;
void setup() {
/* add setup code here */
myFilter.init(sampleRate, humFreq, true, true, true);
// open serial
Serial.begin(115200);
// setup for time cost measure
// using micros()
timeBudget = 1e6 / sampleRate;
// micros will overflow and auto return to zero every 70 minutes
}
void loop() {
/* add main program code here */
// In order to make sure the ADC sample frequence on arduino,
// the time cost should be measured each loop
/*------------start here-------------------*/
timeStamp = micros();
int Value = analogRead(SensorInputPin);
// filter processing
int DataAfterFilter = myFilter.update(Value);
int envlope = sq(DataAfterFilter);
// any value under threshold will be set to zero
envlope = (envlope > Threshold) ? envlope : 0;
timeStamp = micros() - timeStamp;
if (TIMING_DEBUG) {
// Serial.print("Read Data: "); Serial.println(Value);
// Serial.print("Filtered Data: ");Serial.println(DataAfterFilter);
Serial.print("Squared Data: ");
Serial.println(envlope);
// Serial.print("Filters cost time: "); Serial.println(timeStamp);
// the filter cost average around 520 us
}
/*------------end here---------------------*/
// if less than timeBudget, then you still have (timeBudget - timeStamp) to
// do your work
delayMicroseconds(500);
// if more than timeBudget, the sample rate need to reduce to
// SAMPLE_FREQ_500HZ
}
|}
推荐每次使用时都校准一次,因为即使是同一个人,不同位置的肌电信号也是不同的。 |
-
1. 将样例代码中的Threshold变量改成0,即:static int Threshold = 0;
-
2. 上传样例代码至arduino控制板中,然后打开arduino IDE的串口监视器,观察打印的数值。
-
3. 放松手臂上的肌肉,观察串口打印的数值。身心平静,让肌肉放松一会,观察串口监视器打印的最大数值,并记录之。如果数值太大,比如1000以上,可尝试微调干电极的放置位置。
-
4. 将样例代码中的Threshold变量改成刚才记录的最大数值,重新上传样例代码至arduino主控板。
-
5. 打开arduino IDE的Serial Plotter,即可看到肌电波形。
-
一般情况下,肌肉放松时,是一条值始终为0的直线,如下图所示。如偶尔有几个尖峰波形出现,也属正常现象。
![肌电信号_放松.png](http://imgtest.dfrobot.com.cn/DFRobotCnWikiImage/肌电信号_放松.png "肌电信号_放松.png")
-
用力握拳的过程中,可看到明显的肌电波形,如下图所示。
![肌电信号_握拳.png](http://imgtest.dfrobot.com.cn/DFRobotCnWikiImage/肌电信号_握拳.png "肌电信号_握拳.png")
本应用通过对握拳次数进行计数的演示,来说明如何识别是否有肌电信号产生。识别是否有肌电信号,是对肌肉活动进行计数,可用于俯卧撑计数、哑铃计数等场合,也可用于人机交互,如用握拳产生的肌电信号玩Flappy Bird游戏等。 本应用使用的硬件连接、传感器位置放置可参考本维库中第四章的“4.1 准备”与“4.2 接线图”。 本样例代码需要EMGFilters库文件,如还没有下载,请先下载库文件后并安装。 如何安装库?
|
/*
Copyright 2017, OYMotion Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "EMGFilters.h"
#define SensorInputPin A0 //sensor input pin number
/*
Define the `threshold` variable as 0 to calibrate the baseline value of input sEMG signals first.
After wiring the sEMG sensors to the Arduino board, wear the sEMG sensors. Relax your muscles for a few seconds,
you will be able to see a series of squared sEMG signals values get printed on your serial terminal.
Choose the maximal one as the baseline by setting the `threshold` variable. Then rebuild this project.
The `envelope`, which is the squared sEMG signal data, will be printed to the serial line.
The developer can plot it using the Arduino SerialPlotter.
Note:
After calibration, Any squared value of sEMG sigal below the baseline will be treated as zero.
It is recommended that you do calibration every time you wear the sEMG sensor.
*/
unsigned long threshold = 0; // threshold: Relaxed baseline values.(threshold=0:in the calibration process)
unsigned long EMG_num = 0; // EMG_num: The number of statistical signals
EMGFilters myFilter;
/*
Set the input frequency.
The filters work only with fixed sample frequency of
`SAMPLE_FREQ_500HZ` or `SAMPLE_FREQ_1000HZ`.
Inputs at other sample rates will bypass
*/
SAMPLE_FREQUENCY sampleRate = SAMPLE_FREQ_500HZ;
/*
Set the frequency of power line hum to filter out.
For countries with 60Hz power line, change to "NOTCH_FREQ_60HZ"
*/
NOTCH_FREQUENCY humFreq = NOTCH_FREQ_50HZ;
void setup()
{
myFilter.init(sampleRate, humFreq, true, true, true);
Serial.begin(115200);
}
void loop()
{
int data = analogRead(SensorInputPin);
int dataAfterFilter = myFilter.update(data); // filter processing
int envelope = sq(dataAfterFilter); //Get envelope by squaring the input
envelope = (envelope > threshold) ? envelope : 0; // The data set below the base value is set to 0, indicating that it is in a relaxed state
/* if threshold=0,explain the status it is in the calibration process,the code bollow not run.
if get EMG singal,number++ and print
*/
if (threshold > 0)
{
if (getEMGCount(envelope))
{
EMG_num++;
Serial.print("EMG_num: ");
Serial.println(EMG_num);
}
}
else {
Serial.println(envelope);
}
delayMicroseconds(500);
}
/*
if get EMG signal,return 1;
*/
int getEMGCount(int gforce_envelope)
{
static long integralData = 0;
static long integralDataEve = 0;
static bool remainFlag = false;
static unsigned long timeMillis = 0;
static unsigned long timeBeginzero = 0;
static long fistNum = 0;
static int TimeStandard = 200;
/*
The integral is processed to continuously add the signal value
and compare the integral value of the previous sampling to determine whether the signal is continuous
*/
integralDataEve = integralData;
integralData += gforce_envelope;
/*
If the integral is constant, and it doesn't equal 0, then the time is recorded;
If the value of the integral starts to change again, the remainflag is true, and the time record will be re-entered next time
*/
if ((integralDataEve == integralData) && (integralDataEve != 0))
{
timeMillis = millis();
if (remainFlag)
{
timeBeginzero = timeMillis;
remainFlag = false;
return 0;
}
/* If the integral value exceeds 200 ms, the integral value is clear 0,return that get EMG signal */
if ((timeMillis - timeBeginzero) > TimeStandard)
{
integralDataEve = integralData = 0;
return 1;
}
return 0;
}
else {
remainFlag = true;
return 0;
}
}
|}
推荐每次使用时都校准一次,因为即使是同一个人,不同位置的肌电信号也是不同的。 |
-
1. 本代码中,threshold变量的值默认为0,表示处于校准模式。改成非零值,则退出校准模式。因此,如要重新进行校准,将代码中的threshold变量改成0即可,即:unsigned long threshold = 0;
-
2. 上传代码至arduino控制板中,然后打开arduino IDE的Serial Plotter,观察y轴数值的最大值。
-
3. 轻轻的握紧拳头,再轻轻的松开拳头,如此循环。观察Serial Plotter中y轴出现的最大值并记录之。将如下图,最大值为400左右。
-
4. 将代码中的threshold变量改成刚才记录的最大数值。推荐改成:最大值+100,提升抗干扰能力。重新上传样例代码至arduino主控板。
-
5. 打开arduino IDE的Serial Monitor,用力握拳并松开一次,即可看到打印出的计数。轻轻的握拳是不会计数的。用力紧握拳不放,计数也不会累加,必须握紧后并松开才能计数。通过这种方式,就实现了对握拳进行精确计数。
-
如发现松拳时,计数多次累加,则是阀值设置的较低,引起误判。可将代码中的threshold变量值再加100,重新上传样例代码至arduino主控板,再次查看效果,直到把threshold变量加到满意效果为止。
![握拳计数.png](http://imgtest.dfrobot.com.cn/DFRobotCnWikiImage/握拳计数.png "握拳计数.png")
将传感器放置在需要检测肌肉活动的位置,再进行校准,就可对肌肉活动进行精确计数,如用于俯卧撑计数、哑铃计数等健身场合,也可用于人机交互,如用握拳产生的肌电信号玩Flappy Bird游戏等。 除了这个基础功能,该传感器也能通过判断每次信号的强度来检测肌肉活动强度。更多应用,请参考本维库下方的“更多资料”。
| '''Q1. '''手臂上什么地方可以放置干电极?有什么要求吗? |
-
'''A. '''一般来说,采用配套的三金属干电极板,无需关注参考电平,只需将电极板保持和肌肉方向一致即可。放置位置可参考下图:
| 更多问题及有趣的应用,可以 访问论坛 进行查阅或发帖。 |