이번엔 아두이노를 이용한 간단한 신호처리에 대해 알아보겠습니다.


보통 아두이노를 사용하게 되면, 


Analog Input

Digital Input / Output 


이 기능들을 주로 사용하게 되고,

보통 아두이노를 사용하시는 분들을 보면


위 사진처럼


아두이노에서 받아온 데이터(Raw Data)는 통신(시리얼통신 or SPI 통신) 을 통해 PC로 전송하게 되죠.


전송된 데이터는 PC상에서 연산이 되어서 다시 아두이노로 데이터(Processed Data)를 전송하게되고,

아두이노는 이에 따라 반응하게 됩니다.


하지만 이런 구조의 주된 문제점은 아무래도,

시스템의 구조가 커질 수 밖에 없다는 것이죠


아두이노와 센서만 있는다고 되는게 아니라, pc까지 있어야 하니 말이죠


이런 문제를 해결해 주는 몇몇 보드들이 바로 라즈베리파이, 비글본 블랙, 아두이노 윤, 등등

리눅스(Linux) OS 가 설치되어있는 보드들이랍니다.


이들은 보드 하나에 아두이노와 리눅스가 붙어있어서 상당히 간편하게 사용할 수 있지만, 

다루기가 쉽지않아서 처음 이용하시는 분들에게는 쉽지 않습니다.


그래서 이번시간엔 아두이노만 이용해서

데이터 입출력 및 프로세싱을 해보도록 하겠습니다~


#1 푸리에 변환 (Fourier Transform)


먼저 신호분석에서 가장 흔하게 사용하는것이

푸리에 변환 (Fourier Transform) 일겁니다.

푸리에변환은 시간영역의 신호를 주파수 영역의 신호로 변환 시켜주는 함수에요.


좀 더 구체적으로 설명하기위해 '소리'에 대해 봅시다.

소리신호를 녹음해서 보면 빨간 박스에서 보이는것 처럼 지글지글한 신호로 보이죠?


근데 저 신호만 봐서는 이 소리신호의 음 높이가 '도' 인지, '파' 인지 알수가 없죠.

근데 자세히 보면 빨간 박스의 그래프는 x축이 시간(time)이랍니다.

즉, 시간에 따라 신호가 어떻게 흘러가는지만 볼 수 있을 뿐,

음의 높낮이 정보인 주파수(Hz)는 알 수가 없습니다.


이를 알 수 있게 해주는 것이 바로 푸리에 변환.


빨간 박스의 정보를 푸리에 변환을 하게되면

파란 박스에 있는 정보처럼 이상한 형태의 신호가 나옵니다.

하지만 파란 박스를 자세히 보면 x축이 시간이 아닌 주파수(Frequency)가 됩니다.


즉, 주파수 정보인 음의 높낮이를 파악할 수 있다는 거죠.


그럼 다른 사람이 같은 음을 내더라도 목소리가 다른건 어떻게 알 수 있을까요?

목소리는 '음색'이라 표현하기도 하며, 푸리에 변환을 하게되면 주파수 영역에서 다른 형태를 보입니다.


위 사진은 4옥타브 도 (C4, 261.6Hz)를 각 악기별로 연주할 때 나타나는 신호입니다.

[피아노(a), 트럼펫(b), 바이올린(c), 플룻(d)]


보시다싶이 주파수별로 나타나는 형태가 조금씩 다르죠?

저런 특성들을 이용해 음색을 분별 할 수도 있답니다.


아무튼 푸리에 변환은 기존 신호에 숨어있던 정보들을 뽑아낼 수 있는 기능을 가졌지만,

연산량이 상당하기때문에 일반 컴퓨터로도 큰 데이터의 푸리에변환은 쉽지않답니다.



#2 아두이노를 이용한 푸리에 변환



자 이제 이러한 푸리에변환을 빠르게는 못하지만,

아두이노를 이용해 신호처리의 흉내를 내보도록 하겠습니다.


사실 컴퓨터로 푸리에변환을 할때에는 수식적으로 적분을 계산하는 것이 아니라,

다른 방법을 통해서 간접적으로 빠르게 구하는 Fast Fourier Transform(FFT) 를 주로 사용한답니다.


아두이노에서는 공식적으로 FFT함수가 따로 없기때문에, 사용자가 만든 함수를 사용합니다.


제가 알려드릴 함수는 크게 두가지 입니다.


1. C언어로 구성된 라이브러리 (쉬움, 하지만 느림)

arduinoFFT-master.zip



  1. /* Arduino Code */

  2. #include "arduinoFFT.h"

    arduinoFFT FFT = arduinoFFT(); /* Create FFT object */
    /*
    These values can be changed in order to evaluate the functions
    */
    const uint16_t samples = 128; //This value MUST ALWAYS be a power of 2
    double signalFrequency = 1000;
    double samplingFrequency = 5000;
    uint8_t amplitude = 100;
    /*
    These are the input and output vectors
    Input vectors receive computed results from FFT
    */
    double vReal[samples];
    double vImag[samples];

    #define SCL_INDEX 0x00
    #define SCL_TIME 0x01
    #define SCL_FREQUENCY 0x02

    #define Theta 6.2831 //2*Pi

    void setup()
    {
      Serial.begin(115200);
    //  Serial.println("Ready");
    }

    void loop()
    {
      for (uint8_t i = 0; i < samples; i++)
      {
        vReal[i] = analogRead(A0);
        delayMicroseconds(100);
        vImag[i] = 0;
      }
      FFT.Windowing(vReal, samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD); /* Weigh data */
      FFT.Compute(vReal, vImag, samples, FFT_FORWARD); /* Compute FFT */
      FFT.ComplexToMagnitude(vReal, vImag, samples); /* Compute magnitudes */
      PrintVector(vReal, (samples >> 1), SCL_FREQUENCY);
    }

    void PrintVector(double *vData, uint8_t bufferSize, uint8_t scaleType)
    {
      for (uint16_t i = 2; i < bufferSize; i++)
      {
        uint8_t val_temp = map(vData[i],0,1000,0,255);
        Serial.write(val_temp);
      }
      Serial.write('\n');
    }


  1. /* Processing code */

    import processing.serial.*;

    Serial myPort;        // The serial port

    int[] val = new int[64];

    void setup () {
      // set the window size:
      size(1024, 1024);

      String portName = Serial.list()[2];  // Set your port correctly
      myPort = new Serial(this, portName, 115200);
      
      // don't generate a serialEvent() unless you get a newline character:
      myPort.bufferUntil('\n');

      // set inital background:
      background(255);
      rect(0, 0, 1024, 1024);

      // set stroke line color to red.
      stroke(255,0,0);
      
      //delay for arduino serial reboot.
      delay(2000);
    }

    void draw () {
        //Clear background for re-drawing.
        background(255);

        //Draw stroke for each frequency 
        for (int i =0; i<64; i++){
          line(i*16 +1,height,i*16 +1,height-val[i]*4);
        }
    }


    void serialEvent (Serial myPort) {
      try{
        
          //Create buffer for data saving
          byte[] inByte = new byte[200];
          
          //Put data into buffer
          myPort.readBytesUntil('\n',inByte);
          
          // convert to an int and map to the screen height:
          for(int i = 0; i<64; i++){
           val[i] = int(inByte[i]);      
          }

      }catch(Exception e){
        println("Err");
      }
    }




2. AVR로 구성된 라이브러리(어려움, 하지만 빠름)


그리고 아두이노 우노에서만 동작합니다!!


아무래도 레지스터를 건드리다보니, 물리적으로 칩이 우노에 사용되는 ATMEGA328P이 아니면

동작이 안된답니다...!


ArduinoFFT3.zip



  1. /* Code for Arduino */
  2. /*
    fft_adc_serial.pde
    guest openmusiclabs.com 7.7.14
    example sketch for testing the fft library.
    it takes in data on ADC0 (Analog0) and processes them
    with the fft. the data is sent out over the serial
    port at 115.2kb.
    */

    #define LOG_OUT 1 // use the log output function
    #define FFT_N 256 // set to 256 point fft

    #include <FFT.h> // include the library
    #include <avr/io.h>
    #include <avr/interrupt.h>

    void setup() {
      Serial.begin(115200); // use the serial port
      TIMSK0 = 0; // turn off timer0 for lower jitter
      ADCSRA = 0xe5; // set the adc to free running mode
      ADMUX = 0x40; // use adc0
      DIDR0 = 0x01; // turn off the digital input for adc0
    }

    void loop() {
      while(1) { // reduces jitter
        cli();  // UDRE interrupt slows this way down on arduino1.0
        for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
          while(!(ADCSRA & 0x10)); // wait for adc to be ready
          ADCSRA = 0xf5; // restart adc
          byte m = ADCL; // fetch adc data
          byte j = ADCH;
          int k = (j << 8) | m; // form into an int
          k -= 0x0200; // form into a signed int
          k <<= 6; // form into a 16b signed int
          fft_input[i] = k; // put real data into even bins
          fft_input[i+1] = 0; // set odd bins to 0
        }
        fft_window(); // window the data for better frequency response
        fft_reorder(); // reorder the data before doing the fft
        fft_run(); // process the data in the fft
        fft_mag_log(); // take the output of the fft
        sei();
        Serial.write(fft_log_out, FFT_N/2); // send out the data
        Serial.write("\n");
      }
    }



  1. /* Processing code */
    import processing.serial.*;

    Serial myPort;        // The serial port

    int[] val = new int[128];

    void setup () {
      // set the window size:
      size(1024, 1024);

      String portName = Serial.list()[2];
      myPort = new Serial(this, portName, 115200);
      
      // don't generate a serialEvent() unless you get a newline character:
      myPort.bufferUntil('\n');

      // set inital background:
      background(255);
      rect(0, 0, 1024, 1024);
      stroke(255,0,0);
      noLoop();
      delay(2000);
    }

    void draw () {
        background(255);

        for (int i =0; i<128; i++){
          line(i*8 +1,height,i*8 +1,height-val[i]*4);
        }
    }


    void serialEvent (Serial myPort) {
      try{
          byte[] inByte = new byte[200];
          
          myPort.readBytesUntil('\n',inByte);
          
            // convert to an int and map to the screen height:
            for(int i = 0; i<128; i++){
             val[i] = int(inByte[i]);      
            }
            redraw();

      }catch(Exception e){
        println("Err");
      }
    }


각 구성을 보시고 본인 쓰기에 편하신 라이브러리로 쓰시면 됩니다!


아래 이미지는 프로세싱을 통해 모니터링한 결과입니다.


각각 코드를 돌려보면,


1번 결과



2번 결과



데이터의 갯수와 샘플링 속도차이가 조금 있긴하지만, 첫번째 코드는 깔끔한 반면 느리고 신호가 불안정합니다.


하지만 두번째 경우, 신호가 깔끔하고 안정적이지만, 코드가 복잡합니다.


어떤걸 쓰게 될지는 센서의 성능에 따라 결정하시는게 좋을듯 합니다ㅎㅎ



도움이 되셨다면 공감 한번만 꾹! 눌러주세요^^


이번 시간에는 Advancer Technology 사에서 만든 근전도 신호를 측정하는 모듈에 대해 알아보겠습니다.


먼저, 근전도 신호(Electromyography, EMG)란 근육에서 발생하는 신경신호를 의미하고, 

이는 피부 표면에서도 측정할 수 있답니다.


신기하죠? 몸 안쪽에 있는 신경세포의 신호가 감지된다니..!


이러한 근육신호는 세 개의 전극으로 측정이 된답니다.

하나는 기준 전극(Reference),

또 하나는 + 전극, 다른 하나는 - 전극이겠지요~


보통 이러한 생체신호 센싱 모듈의 경우 전압으로 신호가 발생되는데요,

전압은 흔히 상대적인 전위 차이라고 말하죠?


때문에 생체 신호가 얼마만큼의 전위를 가지고 있는지 알기 위해서는

Reference전극, 말 그대로 신호의 기준 전위를 잡아줘야 하는 것입니다.


자 그럼 +와 -전극은 어떤 역할을 할까요?


여러 이유가 있겠지만,

+ 신호와 -신호의 차이를 구함으로써 노이즈를 제거하기 위한 목적이 크답니다.

자 이제 한번 만들어 봅시다.


센서를 꺼내보면 두개의 전극(+, -) 과 까만 전선에 달린 전극(Reference)이 있습니다.



우선 양 옆단에 핀헤더를 3개씩 납땜을 해줍니다!

왜 양쪽인지는 뒤에서 알려드릴게요~


양쪽에서 나오는 신호가 조금 다르기 때문이에요.



자 이제 납땜을 하셨다면 몸에 부착을 해야하는데,

그냥 떨렁 쇠로 된 전극을 몸에 붙이면 고정이 안되기 때문에

EMG전용 전극이 필요하답니다~


위 사진처럼 연결을 해주시면 사용 준비는 끝!


이제 이걸 어떻게 사용하는가가 관건일텐데,

그냥 막 붙히면....

막 신호가 안나와요ㅠㅠ


붙일때도 방법이 있습니다.



위 사진은 Advancer Technology 사에서 제공해주는 설명서에 있는것인데요,

센서를 부착할 때는


1. 근육의 결방향과 같은 방향으로 센서를 부착


2. 근육의 끝부분(인대 부분)이 아닌 근육의 중심부에 부착


이 두가지를 맞추지 않으면 올바른 신호가 측정이 안됩니다!!



이런 식으로 결방향으로 붙혀주시고,

기준전극은 같은 근육이 아닌 다른 부분이 붙혀주시면 되요~



그리고 힘을 주면 신호가 나온답니다ㅋㅋㅋ

이 모듈의 경우 힘을 주면 LED가 밝게 빛나도록 설정도 되어있네요!


힘을주면 올라가는 신호가 보이시죠?ㅎㅎ



자 그럼 이번엔 다른쪽에서 나오는 신호를 한번 측정해볼게요



아까 신호와는 조금 다른걸 느낄수 있나요?


조금더 신호가 지직지직(?) 하게 보이죠?

사실 이 신호가 원래 EMG신호입니다.


아무런 데이터 수정이 안된 날것의 데이터 (Raw Data) 지요!


그럼 왜 두개가 있을까요?


바로 EMG신호의 분석시에 사용하는 방법이 여러가지가 있기 때문입니다.


근전도(EMG) 신호를 분석할때는 크게


  1. 단순히 크기를 이용한 분석


2. Raw Data의 주파수 특성 분석
(Fourier Transform을 이용한 분석)


이 있답니다. (확실하진 않아요...!)


복잡한 경우가 아니라면 크기를 이용한 분석 만으로도 많은 것을 할 수 있습니다.


하지만 문제는 Raw Data를 크기 성분의 신호로 변환하는데는 상당히 많은 절차가 들어가기때문에,

아두이노와 같은 계산속도가 늦는 MCU의 경우에는  Raw Data를 분석하기가 버겁답니다.


때문에 근전도(EMG) 센서 모듈 내에서 Raw Data를 크기 신호로 변경하여 보내준다면

아두이노에서 훨씬 수월하게 작업을 할 수 있겠지요?


데이터 프로세싱은 또다른 학문분야이기 때문에 여기서 다루기에는 너무 어려울듯 합니다.




혹시나 궁금한점이나 잘못된 부분이 있다면 알려주세요~^^


이번시간에는 서피스 프로4 사용시에 자주 발생하는 CPU 문제에 대해 해결해보겠습니다.


종종 서피스를 껐다가 켜면 CPU점유율이 몇분동안 100%가 될 때가 있습니다.

처음에는 서피스 자체가 문제인건가.. 싶었는데 

작업 관리자(ctl+shift+ESC)을 켰더니 'Microsoft compatibility telemetry' 라는 놈이 가장 큰 비율을 차지하고 있더군요ㅋㅋㅋ



Microsoft라 적혀있으니 뭔가 나쁜놈은 아닌것 같지만 그래도 너무 많은 리소스를 잡아먹어서 확인을 해봤더니

현재 사용중인 윈도우에 대한 정보들을 Microsoft로 보내는 프로세스라고 하더라구요~


하지만 전 별로 정보를 주고싶지도 않고, 발열이 너무 심해져서 프로세스를 껐더니

좀비같이 또 살아납니다ㅋㅋㅋㅋㅋㅋㅋㅋ



그래서 방법을 찾다보니 두가지 방법이 있습니다.



# 1 윈도우 설정에서 변경하기


원래는 시작(윈도우 모양) -> 설정 -> 개인정보 -> 피드백 및 진단 으로 가세요.








그 다음에 Windows에서 내 피드백 요청 / Microsoft에 장치 데이터 보내기를 '안 함' 과 '기본' 으로 설정해주시면 됩니다.


하지만 이 세팅은 전송을 막는것이 아니라 전송량과 빈도를 줄이는것 뿐이랍니다~!



# 2 레지스트리값 변경하기


레지스트리를 변경하려면 먼저 레지스트리 편집기를 열어야 합니다.



실행 -> Regedit -> HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DataCollection



여기서 변수 하나를 생성해서 DWORD(32비트) 값 으로 설정해 주시고,



이름을 AllowTelemetry 라고 변경해주시고, 값은 0 으로 주시면 됩니다.



그 뒤에 Ctrl + Shift + ESC를 눌러서 작업관리자 창을 켜시고,

서비스 탭을 클릭하고 왼쪽 아래에 있는 서비스 열기를 눌러주세요~!


그리고난 뒤 서비스에서 


 'Diagnostics Tracking Service'

또는

' Connected User Experiences and Telemetry'


'dmwappushsvc'


요 두가지를 중지해주시면 됩니다!


이 모든걸 세팅하셨다면 재부팅 해주시면 깔끔하게 마무리가 됩니다^^

+ Recent posts