December 22, 2024

디바이스마트 미디어:

[66호] 원하는 색상으로 제어가 가능한 아두이노 IoT 스마트 무드등 키트 -

2021-06-25

★2021 ICT 융합 프로젝트 공모전 결과 발표! -

2021-05-12

디바이스마트 국내 온라인 유통사 유일 벨로다인 라이다 공급! -

2021-02-16

★총 상금 500만원 /2021 ICT 융합 프로젝트 공모전★ -

2021-01-18

디바이스마트 온라인 매거진 전자책(PDF)이 무료! -

2020-09-29

[61호]음성으로 제어하는 간접등 만들기 -

2020-08-26

디바이스마트 자체제작 코딩키트 ‘코딩 도담도담’ 출시 -

2020-08-10

GGM AC모터 대량등록! -

2020-07-10

[60호]초소형 레이더 MDR, 어떻게 제어하고 활용하나 -

2020-06-30

[60호]NANO 33 IoT보드를 활용한 블루투스 수평계 만들기 -

2020-06-30

라즈베리파이3가 드디어 출시!!! (Now Raspberry Pi 3 is Coming!!) -

2016-02-29

MoonWalker Actuator 판매개시!! -

2015-08-27

디바이스마트 레이저가공, 밀링, 선반, 라우터 등 커스텀서비스 견적요청 방법 설명동영상 입니다. -

2015-06-09

디바이스마트와 인텔®이 함께하는 IoT 경진대회! -

2015-05-19

드디어 adafruit도 디바이스마트에서 쉽고 저렴하게 !! -

2015-03-25

[29호] Intel Edison Review -

2015-03-10

Pololu 공식 Distributor 디바이스마트, Pololu 상품 판매 개시!! -

2015-03-09

[칩센]블루투스 전 제품 10%가격할인!! -

2015-02-02

[Arduino]Uno(R3) 구입시 37종 센서키트 할인이벤트!! -

2015-02-02

[M.A.I]Ahram_ISP_V1.5 60개 한정수량 할인이벤트!! -

2015-02-02

[60호]층간소음 측정 및 상쇄를 통한 이웃사랑 프로젝트

Cap 2020-09-07 14-49-36-584

2019 ICT 융합 프로젝트 공모전 참가상

층간소음 측정 및 상쇄를 통한

이웃사랑 프로젝트

글 | 성균관대학교 김혁균, 고건우, 권동환, 정혜인, 최민수

 

1. 심사평
칩센 사회적 이슈로까지 번지고 있는 층간 소음에 대하여 고민을 한 것으로 보이지만, 제작한 결과물이 어떤 효과를 보이는지에 대한 확인이 어렵습니다. 또한 마이크로 입력된 소음 진동에 대하여 스피커를 통해 상쇄한다는 것도 실효성이 부족해 보입니다. 조금 더 구체적인 가설과 대안을 제시할 필요가 있어 보입니다.
뉴티씨 최근 많은 민원이 층간소음에서 발생하고, 실제로 이웃간의 분쟁도 많이 발생하고 있습니다. 이럴 때 이러한 장비를 고안하여 낸 것은 참 좋은 의미인 것 같습니다. FFT를 통한 주파수 확인 후 주요 주파수를 상쇄시키기 위해 스피커로 소리를 내서 층간소음을 줄이는 것은 제대로 구현된다면 참 도움이 될 것입니다. 다만, 작품의 결과가 어떻게 되었는지, 장치 전과 장치한 후의 결과 비교 또한 FFT주파수로 찾고 나서 반대파를 발생시킨 파형, 그리고 상쇄된 최종 파형 등이 함께 나와있다면 보다 좋은 작품이 되었을 것 같습니다. 20여년전에 전화박스를 외부소음으로부터 막아주기 위해 구현되었던 기능과 매우 유사합니다. 실제 생활에 도움이 되는 이러한 생각들은 평소에도 많이 해서, 삶이 변화되는 계기가 되면 좋겠습니다.
위드로봇 노이즈 캔슬링은 실시간 응답성이 매우 중요한데, 이 부분의 구현이 아쉽습니다,
펌테크 최근 들어 사회적 이슈가 되고 있는 층간 소음 문제를 반영한 아이디어와 실용성이 우수한 작품이라고 생각합니다. 작품에 적용된 기술이 단순히 층간 소음을 측정하여 수집하고 표시하는 것 외에도 층간 소음 발생 시 발생된 소음을 경감시키고자 하는 방안으로 FFT 노이즈 캔슬링 방식을 적용한 회로로 구성한 점은 참신한 기획이라고 생각합니다. 단 제출된 보고서 상에서는 구동되는 자세한 동작 영상을 확인할 수 없어 최종 완성이 되지는 않은 것으로 판단됩니다.

2. 작품 개요
2.1. 제작 배경 및 문제점
대한민국에서 ‘층간소음’의 문제점은 이미 사회적으로 큰 이슈가 되고 있다. 실제로 20대에게 ‘층간소음’ 설문조사를 한 결과 ‘층간소음을 겪어봤다’고 답한 응답자는 91.7%에 달했다. 또 10명 중 5명은 ‘소음 때문에 스트레스를 받고 있다’고 답했다. 국민일보 ‘층간소음, 윗집이 남기고 간 메모 때문에‘ ‘항의해 봤다’는 응답도 51.5%에 달했다. 그래서 층간소음 해결을 위하여 관리실에 민원을 넣거나 문자 메시지, 먹을 것을 주는 등 많은 방법을 사용한다. 하지만 해결이 안 될 경우 민원을 넣어도 현행법상 강제력이 없기 때문에 소음을 내는 집에서 협조하지 않게 되면 별다른 방도가 없다. 그로 인하여 직접 항의하거나 소음을 더 크게 내서 복수하는 행위가 나오게 되고, 이러한 과정에서 다툼이 일어나거나 심각한 경우 살인, 방화, 폭력 등 범죄 행위가 발생하는 상황이다.

Cap 2020-09-07 14-49-45-577

보통 항의를 하는 경우 구체적인 기준을 알아내기 어려운 경우가 많다. ‘시끄럽다‘의 기준은 주관적이기 때문이다. 물론 국토부와 환경부에서 상단 표와 같이 기준을 만들어 놓았지만, 윗집의 소음 정보를 아랫집에 제공하는 건 개인정보 보호법에 의하여 어렵다. 만약, 층간소음의 갈등이 심화 되어 법적인 절차로 넘어갈 경우, 개인적으로 녹음한 소음과 영상 등은 실효성을 입증하기 어렵다. 측정업체에 50~100만원의 비용을 소비해야 증거자료 사용할 수 있다.
또한, 자신이 내는 소음이 어느 정도로 주변에 영향을 미치는지 확실히 알 수 없는 경우가 많다.

2.2. 제작 목적 및 기대 효과
위의 나열한 문제점을 해결하기 위하여 층간소음 센서를 제작하였다. 애매한 ‘시끄럽다’ 기준을 확실히 하기 위하여 <표1>에 나와 있는 야간의 최고 소음도를 기준(실험환경이 미니어처 집이라서 망치로 내려치는 충격과 시끄러운 음악을 트는 것을 최고 소음도를 넘는다는 기준으로 설정하였다.)으로 일정 기준의 소음을 넘으면 LED 등에 ‘X’ 표시가 나오도록 코드를 작성하였다. 개인정보 보호법을 고려하여 소음을 받는 집에서 진동과 소음을 측정하고 기준을 넘으면 소음이 나는 집에 LED가 표시되도록 하였다.
또한, 주거 위치별 층간소음 피해는 위층의 소음으로 인해 아래층에서 불편을 호소하는 경우가 69.4%로 가장 많다는 설문조사를 바탕으로 윗집에서 아랫집으로 소음이 간다는 상황 설정으로 미니어처 집을 제작하였다. 그리고 우퍼(woofer·저음용 스피커) 설치로 보복 소음을 낸다는 것에서 아이디어를 얻어서 ‘음장상쇄 효과’를 적용해서 아랫집에서 소음을 실시간으로 인식하고, 아래층 천장에 부착된 스피커로 반대 위상을 가진 주파수를 내보내어 상쇄시키는 장치를 제작하였다.
이 센서를 사용할 경우 LED등이 소음이 나는 집에 경고를 주어 소음 제공자 스스로 소음을 억제하여 충돌을 방지하는 효과를 기대할 수 있고, 센서의 기록장치에 의해서 법적인 증거자료로 사용할 수 있다. 게다가, 아랫집에서 음장 상쇄 기능을 가진 스피커를 사용하게 되면, 윗집에서 나는 소음이 크더라도 소음이 감쇄된다. 그렇게 되면 층간소음 문제를 어느 정도 해결할 수 있을 것이다.

3. 작품 설명
3.1. 음장 상쇄
3.1.1. FFT(Fast Fourier Transform)
FFT(Fast Fourier Transform)이란 이산 푸리에 변환과 그 역변환을 빠르게 수행하는 효율적인 알고리즘을 의미한다. 보통 디지털 신호 처리 및 편미분 방정식의 풀이 등 여러 분야에서 사용된다.
푸리에 변환이란, 시간에 대한 함수를 주파수에 대한 함수로 변환하는 작업이다. 다시 말해, 푸리에 변환은 시간에 대한 함수를 분해하여 각 주파수 대역에 대한 성분으로 분해한다.

Cap 2020-09-07 14-49-57-793

위 그림에서 빨간 부분은 특정 신호를 시간에 대한 함수로 나타낸 것이다. 이러한 신호는 대개 여러 개의 사인파의 합성으로 이루어져 있는데, 이것을 주파수 대역에 대하여 분해하고 각 주파수 대역에 대한 진폭을 표시한 것이 파란 부분이다. 푸리에 변환을 이용하면 복잡한 신호를 간결하게 정리할 수 있으며, 특정 신호에서 지배적인 주파수 대역이 어떤 성분인지 확인할 수 있다. 이러한 푸리에 변환은 음성 구별, 다양한 소리 재생, 영상 노이즈 제거 등 여러 신호 처리 분야에서 활용되고 있다.

3.1.2. 아두이노를 이용한 FFT와 노이즈 캔슬링 알고리즘
아두이노를 이용한 FFT는 이미 많은 개발자들에 의해 오픈 소스 라이브러리가 제공되고 있다. 본 프로젝트에서는 이를 이용하여 노이즈 캔슬링 알고리즘을 구현한다. 먼저 아두이노의 아날로그 핀을 통해 현실 소리 데이터를 받고 이를 FFT 라이브러리를 이용하여 주파수 영역의 데이터로 전환한다. 그 후, 효율적인 캔슬링을 위해 가장 지배적인 주파수 대역을 선정하여 이에 해당하는 반대 진폭의 음파를 스피커를 통해 출력한다. 그림은 데이터 처리 과정 중, FFT를 이용하여 소리를 시각화한 것과 FFT를 통해 소리를 주파수 대역별로 분리한 후, 그것을 상대적인 진폭으로 나타낸 데이터이다.

Cap 2020-09-07 14-50-06-577 Cap 2020-09-07 14-50-16-711

3.1.3. 상수 및 변수 선언

 

const uint16_t samples = 128;
double signalFrequency = 1000;
double samplingFrequency = 5000;
uint8_t amplitude = 100;

sample의 크기와 FFT 변환에 필요한 신호 주파수를 정의하는 부분이다.

 

double vReal[samples];
double vImag[samples];

현실의 소리 샘플을 FFT 변환을 통해 주파수 대역으로 분리할 때 사용하는 Array를 선언한다. 아날로그 핀을 통해 얻은 소리 신호를 vReal에 저장하고, FFT 변환을 통해 얻은 주파수 영역 소리 신호를 vImag에 저장한다.

 

uint8_t mySensVals[samples];
float freq1;
float freq2;
float freq3;
uint8_t ampl1;
uint8_t ampl2;
uint8_t ampl3;
uint8_t i1;
uint8_t i2;
uint8_t i3;

FFT를 통해 분석한 소리 대역을 저장하는 mySensVals를 정의하고 그 중에서 진폭이 가장 큰 주파수 대역을 freq1, freq2, freq3에 저장한다. 그 후 각 주파수 대역에 대한 진폭을 ampl1, ampl2, ampl3에 저장한다. i1, i2, i3는 mySensVals에서 주파수가 큰 세 개의 인덱스를 의미한다,

3.1.4. 메인 loop 함수

for (uint8_t i = 0; i < samples; i++)

  {

    vReal[i] = analogRead(A0);

    delayMicroseconds(100);

    vImag[i] = 0;

  }

vReal에 마이크 센서를 이용해 아날로그 핀을 통해 실제 소리 데이터를 받는다, 그 후, vImag의 모든 데이터를 0으로 정리한다.

FFT.Windowing(vReal, samples, FFT_WIN_TYP_HAMMING, FFT_FORWARD);

  FFT.Compute(vReal, vImag, samples, FFT_FORWARD);

  FFT.ComplexToMagnitude(vReal, vImag, samples);

  PrintVector(vReal, (samples >> 1), SCL_FREQUENCY);

vReal에 있는 소리 데이터를 FFT를 이용하여 주파수 대역에 대하여 정리한 후, vImag에 저장한다. 해당 부분 코드는 FFT를 아두이노에 맞게 적용할 수 있는 arduinoFFT.h 오픈 소스 라이브러리를 이용하였다.

PrintVector(vImag, (samples >> 1), SCL_FREQUENCY);

vReal을 FFT를 통해 정리하여 주파수 영역의 소리 데이터를 mySensVals array에 저장한다.

i1 = 2;

  for (int k = 2;k < 64; k++){

    if(mySensVals[k]>mySensVals[i1]){

      i1 = k;

    }

  }

 

  i2 = 2;

  for (int k = 2;k < 64; k++){

    if((mySensVals[k]>mySensVals[i2]) && (k!=i1)){

      i2 = k;

    }

  }

 

  i3 = 2;

  for (int k = 2;k < 64; k++){

    if((mySensVals[k]>mySensVals[i3]) && (k!=i1) && (k!=i2)){

      i3 = k;

    }

  }

mySensVals의 데이터 중, 가장 진폭이 큰 데이터 3개를 추출하는 과정이다.

freq1 = scailing(i1);

freq2 = scailing(i2);

freq3 = scailing(i3);

freq 변수에 진폭이 가장 큰 주파수 대역에 인덱스를 주파수로 변환해 저장한다.

tone(speaker_pin1, freq1);

tone(speaker_pin2, freq2);

tone(speaker_pin3, freq3);

선택한 주파수 대역을 3개의 스피커를 통해 재생한다.

3.1.5. 기타 함수 설정

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);

    mySensVals[i] = val_temp;

    /*Serial.print(bufferSize);

    Serial.print(” “);*/

  }

}

선택한 영역의 데이터를 글로벌 변수인 mySensVals에 저장하는 함수이다. 이 과정에서 진폭의 스케일을 0에서 255로 조정한다.

float scailing(int i){

  float ans = 200 + (1000-200)/(50-12)*(i-12);

  return ans;

}

소리 데이터의 인덱스를 주파수로 변환하는 함수이다.

3.2. 소음 및 진동 센서
3.2.1. 라이브러리

#include <FrequencyTimer2.h>

//도트 매트릭스 사용에 필요한 라이브러리를 가져옴.

//진동, 소음 센서의 경우 추가적인 라이브러리 불필요.

3.2.2. 도트매트릭스 설정

#define X  { \

    {1, 0, 0, 0, 0, 0, 0, 1}, \

    {0, 1, 0, 0, 0, 0, 1, 0}, \

    {0, 0, 1, 0, 0, 1, 0, 0}, \

    {0, 0, 0, 1, 1, 0, 0, 0}, \

    {0, 0, 0, 1, 1, 0, 0, 0}, \

    {0, 0, 1, 0, 0, 1, 0, 0}, \

    {0, 1, 0, 0, 0, 0, 1, 0}, \

    {1, 0, 0, 0, 0, 0, 0, 1}  \

 

#define D { \

    {0, 0, 0, 0, 0, 0, 0, 0},\

    {0, 0, 0, 0, 0, 0, 0, 0},\

    {0, 0, 0, 0, 0, 0, 0, 0},\

    {0, 0, 0, 0, 0, 0, 0, 0},\

    {0, 0, 0, 0, 0, 0, 0, 0},\

    {0, 0, 0, 0, 0, 0, 0, 0},\

    {0, 0, 0, 0, 0, 0, 0, 0},\

    {0, 0, 0, 0, 0, 0, 0, 0},\

}

//도트 매트릭스로 비춰줄 X 모양 상태와 불빛이 모두 꺼져있는 상태를 1과 0으로 지정해줌.

3.2.3. 상수 및 변수 선언

int SOUND_SENSOR = A5;

int threshold = 25;

//소음 센서를 A5로 받고, 감도조절 threshold를 25로 설정, Sensor_Value로 소음센서 정의함.

 

int tact = A5;      

//택트 스위치 A5에 연결

byte col = 0;       

//COL을 0으로 초기화

byte leds[8][8];    

//현재 출력해야 할 LED 모양 업로드하는 배열함.

 

//맨 처음 PINS[0]은 사용하지 않기때문에 -1로 설정. 1~16번까지의 핀을 PIN에 연결함.

int pins[17]= {-1, 5, 4, 3, 2, 14, 15, 16, 17, 13, 12, 11, 10, 9, 8, 7, 6};

 

//행 0~7번까지 핀 연결해 주기

int cols[8] = {pins[13], pins[3], pins[4], pins[10], pins[6], pins[11], pins[15], pins[16]};

 

//열 0~ 7번까지 핀 연결해 주기

int rows[8] = {pins[9], pins[14], pins[8], pins[12], pins[1], pins[7], pins[2], pins[5]};

 

const int numPatterns = 2;      

//총 사용할 패턴 수를 지정하기.

byte patterns[numPatterns][8][8] = { X, D };   

//위에서 정의한 led 모양을 patterns에 입력해 주기

int pattern = 0;

pattern의 초기값을 0으로 지정.

int Sensor_value;

 

void clearLeds() { 

  for (int i = 0; i < 8; i++) 

    for (int j = 0; j < 8; j++) 

      leds[i][j] = 0;  }

//led를 다 초기화 시키는 clearLeds 정의.

void display() {

  digitalWrite(cols[col], HIGH);  

//이전 행들 다 꺼지게끔 해주기

  col++;

  if (col == 8) {

    col = 0;

  }

  for (int row = 0; row <= 7; row++) {

    if (leds[col][7 - row] == 1) {     // 위의 배열에서 1일때 해당되는 위치 불빛 켜주기

      digitalWrite(rows[row], HIGH);

    }

    else {                          // 위 배열에서 0일때 해당되는 위치 불빛 꺼줌.

      digitalWrite(rows[row], LOW);

    }

  }

  digitalWrite(cols[col], LOW);     

// 다음 패턴을 위해 led 다 꺼줌.

}

void setPattern(int pattern) {        // LED 배열에 PATTREN 입력하기

  for (int i = 0; i < 8; i++) 

    for (int j = 0; j < 8; j++) 

      leds[i][j] = patterns[pattern][i][j];  

}

 

 

 

 

void setup() {

// 1~16번 까지의 핀을 출력으로 설정

  for (int i = 1; i <= 16; i++) {

    pinMode(pins[i], OUTPUT);

  }

 

// 행 0~7번까지를 high로 

  for (int i = 0; i < 8; i++) {

    digitalWrite(cols[i], HIGH );

  }

// 열 0~7번 까지를 low로

  for (int i = 0; i < 8; i++) {

    digitalWrite(rows[i], HIGH);

  }

 clearLeds();   // led 초기화

 

FrequencyTimer2::setOnOverflow(display);  

//leds를 보여주기 위해서 setOnOverFlow를 사용

 

  pinMode(tact, INPUT); 

//SW 를 설정, 아두이노 풀업저항 사용

  setPattern(pattern); 

 

Serial.begin(9600); 

// 시리얼모니터 출력

  pinMode(SOUND_SENSOR, INPUT);  

 

-메인 loop 함수

void loop() { 

  int val; 

 

 val=analogRead(4);

 

 Serial.print(val,DEC);

 

delay(100);  

//진동 센서 값을 아날로그 4핀으로 받고 val 로 정의한 뒤 delay 100의 지연시간으로 진동 센서 값을 시리얼 모니터에 표시함.

 

Sensor_value = analogRead(A5);   

 

  Serial.println(Sensor_value);   

 

  delay(100);

//소음 센서 값을 아날로그 5핀으로 받고 Sensor_value로 정의한 뒤 delay 100의 지연시간으로 소음 센서 값을 시리얼 모니터에 표시함.

 

int readTact = digitalRead(tact);

  if((Sensor_value>=300) || (val>=300)){

 // 소음센서 또는 진동센서의 값이 300을 넘는 값을 가질 때

    

    if(readTact == LOW){     

// 택트 스위치가 low일때 

      if(pattern == 2) pattern = 0;   

//패턴의 끝까지 다 출력하고 다시 처음을 출력

      else pattern++;                

// 다음 패턴 출력을 위해 pattern ++

     delay(300);                 

// 길게 눌러 패턴이 순식간에 넘어가지 않기 위해서 딜레이를 줌

    }    

       setPattern(pattern);           

// 패턴 출력 

  }

 

 

  else  

 clearLeds();

  Sensor_value = analogRead(A5);

//아닐 경우 다시 처음으로 돌아가 측정을 시작.

  }

3.3. 전체 시스템 구성

Cap 2020-09-07 14-50-31-077
본 시스템은 크게 주요한 두 가지 기능으로 나뉜다. 첫 번째는 두 개의 센서를 이용해 소음을 입력 값으로 받고, LED로 출력하는 과정이다. 이 과정은 층간소음의 진동과 소음을 각각 진동 감지 센서와 소음 감지 센서에서 인식한다. 이때, 진동과 소음의 정도가 일정 값을 넘게 되면 기준치와 비교해 LED에 표시를 해주게 된다.
두 번째는 마이크를 이용해 소음을 입력 받은 후 Fast Fourier Transform 변환 과정을 거쳐 일상의 소음을 사인파로 분석한다. 분석된 여러 주파수 중 가장 큰 주파수를 선택해 그에 반대되는 파장을 내보냄으로써 소음을 상쇄시킬 수 있다.
이때, 진동은 진동 감지 센서에서만 입력되고, 소음은 소음 감지 센서와 마이크 두 곳에서 입력을 받게 된다. 또한, 개인정보보호를 위해, 본 팀은 LED는 위층에 표시하고, 소음을 상쇄시키는 파장은 아래층에 있는 스피커에서 송출하도록 구성하였다. 따라서 위층은 자신의 소음을 인식할 수 있고, 아래층은 소음을 상쇄시킨 조용한 환경에서 지낼 수 있다.

3.4. 개발 환경
본 팀은 프로젝트의 목적하는 바를 구현하기 위해 Arduino Uno와 Arduino Due를 사용하였다. 소음센서, 진동센서의 값을 받아들여 LED와 스피커를 통제하기 위해 상호작용이 가능한 시스템을 만들어 내기 위해 아두이노 보드를 선택하였다. 또한, 임베디드 시스템 중의 하나로 장치의 제어를 쉽게 개발할 수 있다는 장점 때문에 우노 보드를 사용하였다.
아두이노의 IDE에서 소음 및 진동 값의 기준 비교와 음장 상쇄 등 프로젝트의 주된 기능들을 구현하였다. 기본적으로 내장된 라이브러리를 이용해 소음 및 진동센서 제어와 LED 출력을 프로그래밍 하였고, 음장상쇄 과정에서는 FFT관련한 오픈 소스 라이브러리를 사용하여 프로그래밍 하였다. 한편, 빠르게 연산 과정을 수행해야 하는 음장상쇄 기능에 있어서 아두이노 보드는 비교적 느린 사이클을 가지고 있었고, 소음의 발생보다 조금 늦게 반대파가 생성되는 것이 한계점이라 할 수 있다.

4. 단계별 제작 과정
4.1. 진동센서와 소음센서를 이용한 소음인식과 디스플레이 장치

Cap 2020-09-07 14-50-42-178
1. 진동센서. 소음센서와 LED디스플레이를 아두이노와 연결한다.
2. 진동센서의 진동 적정값과 소음센서의 소음 적정값을 구한다.
3. 아두이노가 진동과 소음을 동시에 느낄 때 디스플레이에 X표시가 뜨도록 코딩한다.

4.2. 마이크와 스피커를 이용한 음장상쇄 장치
1. 마이크와 스피커를 아두이노에 연결한다.
2. 마이크로 들어온 소리의 주파수를 분석해 역파장을 스피커로 출력하는 코딩한다.

Cap 2020-09-07 14-50-47-511

4.3. 층간소음 모형 및 실험
1. 아래층과 위층으로 나뉜 공간을 만든다.

Cap 2020-09-07 14-50-54-711

Cap 2020-09-07 14-50-59-310
2. 아래층의 천장 부분에 센서들과 스피커를 붙인다. 진동 및 소음 센서는 아래층의 소음이 감지되지 않고, 위층의 소음과 진동만 감지하도록 방음이 되는 재료로 둘러싸준다.
3. 위층에 충격과 소음을 일으켜 정상적으로 소음과 진동이 인식되어 디스플레이와 스피커가 반응하는지 확인한다.

Cap 2020-09-07 14-51-05-360

4.4. 소스코드

 

#include “arduinoFFT.h”
#define twoPi 6.28318531
#define fourPi 12.56637061
arduinoFFT::arduinoFFT(void)
{
/* Constructor */
}

arduinoFFT::~arduinoFFT(void)
{
/* Destructor */
}

uint8_t arduinoFFT::Revision(void)
{
return(FFT_LIB_REV);
}
void arduinoFFT::Compute(double *vReal, double *vImag, uint16_t samples, uint8_t dir)
{
Compute(vReal, vImag, samples, Exponent(samples), dir);
}

void arduinoFFT::Compute(double *vReal, double *vImag, uint16_t samples, uint8_t power, uint8_t dir)
{
/* Computes in-place complex-to-complex FFT */
/* Reverse bits */
uint16_t j = 0;
for (uint16_t i = 0; i < (samples – 1); i++) {
if (i < j) {
Swap(&vReal[i], &vReal[j]);
Swap(&vImag[i], &vImag[j]);
}
uint16_t k = (samples >> 1);
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
/* Compute the FFT */
double c1 = -1.0;
double c2 = 0.0;
uint8_t l2 = 1;
for (uint8_t l = 0; (l < power); l++) {
uint8_t l1 = l2;
l2 <<= 1;
double u1 = 1.0;
double u2 = 0.0;
for (j = 0; j < l1; j++) {
for (uint16_t i = j; i < samples; i += l2) {
uint16_t i1 = i + l1;
double t1 = u1 * vReal[i1] – u2 * vImag[i1];
double t2 = u1 * vImag[i1] + u2 * vReal[i1];
vReal[i1] = vReal[i] – t1;
vImag[i1] = vImag[i] – t2;
vReal[i] += t1;
vImag[i] += t2;
}
double z = ((u1 * c1) – (u2 * c2));
u2 = ((u1 * c2) + (u2 * c1));
u1 = z;
}
c2 = sqrt((1.0 – c1) / 2.0);
if (dir == FFT_FORWARD) {
c2 = -c2;
}
c1 = sqrt((1.0 + c1) / 2.0);
}
/* Scaling for reverse transform */
if (dir != FFT_FORWARD) {
for (uint16_t i = 0; i < samples; i++) {
vReal[i] /= samples;
vImag[i] /= samples;
}
}
}
void arduinoFFT::ComplexToMagnitude(double *vReal, double *vImag, uint16_t samples)
{
/* vM is half the size of vReal and vImag */
for (uint8_t i = 0; i < samples; i++) {
vReal[i] = sqrt(sq(vReal[i]) + sq(vImag[i]));
}
}

void arduinoFFT::Windowing(double *vData, uint16_t samples, uint8_t windowType, uint8_t dir)
{
/* Weighing factors are computed once before multiple use of FFT */
/* The weighing function is symetric; half the weighs are recorded */
double samplesMinusOne = (double(samples) – 1.0);
for (uint16_t i = 0; i < (samples >> 1); i++) {
double indexMinusOne = double(i);
double ratio = (indexMinusOne / samplesMinusOne);
double weighingFactor = 1.0;
/* Compute and record weighting factor */
switch (windowType) {
case FFT_WIN_TYP_RECTANGLE: /* rectangle (box car) */
weighingFactor = 1.0;
break;
case FFT_WIN_TYP_HAMMING: /* hamming */
weighingFactor = 0.54 – (0.46 * cos(twoPi * ratio));
break;
case FFT_WIN_TYP_HANN: /* hann */
weighingFactor = 0.54 * (1.0 – cos(twoPi * ratio));
break;
case FFT_WIN_TYP_TRIANGLE: /* triangle (Bartlett) */
weighingFactor = 1.0 – ((2.0 * abs(indexMinusOne – (samplesMinusOne / 2.0))) / samplesMinusOne);
break;
case FFT_WIN_TYP_BLACKMAN: /* blackmann */
weighingFactor = 0.42323 – (0.49755 * (cos(twoPi * ratio))) + (0.07922 * (cos(fourPi * ratio)));
break;
case FFT_WIN_TYP_FLT_TOP: /* flat top */
weighingFactor = 0.2810639 – (0.5208972 * cos(twoPi * ratio)) + (0.1980399 * cos(fourPi * ratio));
break;
case FFT_WIN_TYP_WELCH: /* welch */
weighingFactor = 1.0 – sq((indexMinusOne – samplesMinusOne / 2.0) / (samplesMinusOne / 2.0));
break;
}
if (dir == FFT_FORWARD) {
vData[i] *= weighingFactor;
vData[samples - (i + 1)] *= weighingFactor;
}
else {
vData[i] /= weighingFactor;
vData[samples - (i + 1)] /= weighingFactor;
}
}
}

double arduinoFFT::MajorPeak(double *vD, uint16_t samples, double samplingFrequency)
{
double maxY = 0;
uint16_t IndexOfMaxY = 0;
for (uint16_t i = 1; i < ((samples >> 1) – 1); i++) {
if ((vD[i-1] < vD[i]) && (vD[i] > vD[i+1])) {
if (vD[i] > maxY) {
maxY = vD[i];
IndexOfMaxY = i;
}
}
}
double delta = 0.5 * ((vD[IndexOfMaxY-1] – vD[IndexOfMaxY+1]) / (vD[IndexOfMaxY-1] – (2.0 * vD[IndexOfMaxY]) + vD[IndexOfMaxY+1]));
double interpolatedX = ((IndexOfMaxY + delta) * samplingFrequency) / (samples-1);
/* retuned value: interpolated frequency peak apex */
return(interpolatedX);
}

/* Private functions */

void arduinoFFT::Swap(double *x, double *y)
{
double temp = *x;
*x = *y;
*y = temp;
}

uint8_t arduinoFFT::Exponent(uint16_t value)
{
/* Computes the Exponent of a powered 2 value */
uint8_t result = 0;
while (((value >> result) & 1) != 1) result++;
return(result);
}

4.5. 참조
· 국민일보 ‘층간소음, 윗집이 남기고 간 메모 때문에’ : 
· 약한 진동까지 감지할 수 있는Piezo Disk Vibration Sensor(피에조 디스크 진동 센서) :
· [아두이노 강좌] 8 x8 도트 매트릭스 사용하기 :
· 아두이노 FFT 오픈소스 라이브러리 (arduinoFFT) :

 

Leave A Comment

*