DFROBOT SEN0240 肌电传感器

简介

这是由DFRobot与OYMotion合作推出的一款肌电传感器模块。本传感器模块通过检测人体的表面肌电信号(sEMG),进而反应出人体肌肉和神经的活动情况。 本传感器模块集成了滤波、放大电路,将范围在±1.5mV内的微弱人体表面肌电信号进行1000倍放大,并通过差分输入、模拟滤波电路的方式对噪音(特别是工频干扰)进行有效抑制。输出信号为模拟量形式,以1.5V为基准电压,0~3.0V量程的输出。输出信号的大小取决于选定肌肉的活动量,输出信号的波形可显著指示被观察位置皮下肌肉的情况,方便做肌电信号的分析与研究,如使用Arduino作为控制器检测肌肉活动情况,如肌肉是否紧绷,强度如何,是否疲劳等。 本产品是一种主动感应传感器,能提供高质量的信号搜集,且易于使用。不论是被用到静态还是动态的应用领域,仅需要一些极为简单的准备工作即可。本产品使用干电极,无须导电凝胶也可得到良好的信号质量,因此具有寿命长、使用简单方便等特点,更适合普通用户。而采用凝胶探头的医用电极通常为一次性,使用起来较为麻烦。 本产品的测量具有非侵入性、无创伤、操作简单等优点,可用于人机交互等相关应用。虽然测量肌肉活动历来被用于医学研究,然而随着不断缩小但功能更强大的微控制器和集成电路的完善,肌电图电路和传感器也逐渐被应用于各种控制系统。

warning_yellow.png

技术规格

引脚说明



肌电传感器信号处理板管脚定义

标号 名称 功能描述
1 A 模拟信号输出端(0~3.0V)
2 + 电源输入正极(3.3~5.5V)
3 - 电源输入负极
4 PJ-342 电极连线接口

肌电传感器信号处理板管脚定义

使用教程

本教程将演示如何使用这款肌电传感器,通过Arduino IDE的Serial Plotter打印出肌电波形。

准备

接线图



样例代码

本样例代码需要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
    }

校准

warning_yellow.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;
       }
    }

warning_yellow.png 推荐每次使用时都校准一次,因为即使是同一个人,不同位置的肌电信号也是不同的。

常见问题

Q1. 手臂上什么地方可以放置干电极?有什么要求吗?

更多问题及有趣的应用,可以 访问论坛 进行查阅或发帖。

更多资料

DFshopping_car1.png DFRobot商城购买链接