简易声控计时器-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 2247|回复: 1

简易声控计时器

[复制链接]
发表于 2020-5-29 09:22 | 显示全部楼层 |阅读模式
_b51OvUNWNw.blob.jpeg
原文地址 https://www.hackster.io/yutakau/ ... und-detector-fa442d
我创造了一个计时器来测量洗手的时间。最主要的问题是如何开始计时。按动开关是最容易的,但最好在洗手前不要碰它们。我们可以使用红外线或超声波传感器,但由于需要外部元件,因此很难广泛使用。这个项目的独特之处在于,自来水的声音是通过麦克风来启动计时器的。这里使用的M5StickC内置麦克风,因此不需要任何外部组件。因此,任何人都可以通过将代码编程成M5StickC来实现一个简单的洗手计时器。
blockdiagram_eS6OAut4SB.png.jpeg
只检测水声有很多可能的方法。例如,使用高于某个值的音量是一种简单的方法,但它可能存在很多bug,在这里,我决定利用具有独特频谱的水声的特性。麦克风的音频输入通过FFT转换到频域。然后使用支持向量机(SVM)对输入音频与水声的接近程度进行评分。该分数以FFT的样本单位表示。下一步,累积这个分数。这是为了检测这个特性已经持续了超过一定时间。
我用Jupyter Notebook来学习SVM。为了达到这个目的,我使用了Arduino的串口log记录有和没有水声的数据。详细情况将在后面描述。
软件设置
由于我们使用的是FFT库ArduinoFFT,因此如果库尚未安装在Arduino IDE中,请安装https://github.com/kosme/arduinoFFT。接下来,在github上编译并编程这两个源文件。
如何创建支持向量机模型
需要以下3个步骤。
1.记录样本数据。
2.基于JupyterNotebook的支持向量机训练
3.根据训练数据生成支持向量机编码
首先,要记录样本数据,请在HandwashingTimer.ino中添加以下代码
#define _SAMPLEMODE
通过此更改,将通过arduino串行端口记录FFT结果。你可以把数据保存到文件中。这些数据记录了几秒钟,每一次流水和关掉的情况。请详细参考源代码。然后,用JupyterNotebook训练SVM。我把训练脚本上传到github站点,/training/trainingSVM.py。最后,将训练结果存储到finalized_model.csv。我把数据转换成WaterSoundDet.ino。由于JupyterNotebook的SVM功能是基于LIBSVM的,所以请参阅LIBSVM文档

trainingSVM.py
[mw_shl_code=python,true]import pandas as pd
from sklearn import model_selection
from sklearn import svm
from sklearn import metrics
df = pd.read_csv('LOGDATA.log', names=["label","band","data"])
print(df)
%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(df["band"],df["data"],c=df["label"])
feature = df[["band","data"]]
target = df["label"]
feature_train, feature_test, target_train, target_test = model_selection.train_test_split(feature, target, test_size=0.2)

#Train SVM
model = svm.SVC(kernel="linear",verbose=1)
model.fit(feature_train, target_train)

#calcurate training model accuracy
pred_train = model.predict(feature_train)
metrics.accuracy_score(target_train, pred_train)

#calcurate test model accuracy
pred_test = model.predict(feature_test)
metrics.accuracy_score(target_test, pred_test)

# Save Model
filename = 'finalized_model.sav'
pickle.dump(model, open(filename, 'wb'))[/mw_shl_code]


HandwashingTimer.ino
[mw_shl_code=arduino,true]// Hand washing timer
//
// for M5Stick-C
//

#include <M5StickC.h>
#include <driver/i2s.h>
#include "arduinoFFT.h"


#define PIN_CLK  0
#define PIN_DATA 34
#define READ_LEN (2 * 1024)
#define SAMPLING_FREQUENCY 44100

#define SCORE_LIMIT 1000000
#define DET_THRES 300000
#define DET_TCNT  5
#define DETCNT_MAX 200
#define DETCNT_MIN 0
#define MAX_SEC 30

uint8_t BUFFER[READ_LEN] = {0};
uint16_t *adcBuffer = NULL;
const uint16_t FFTsamples = 512;
double vReal[FFTsamples];
double vImag[FFTsamples];
arduinoFFT FFT = arduinoFFT(vReal, vImag, FFTsamples, SAMPLING_FREQUENCY);
unsigned int sampling_period_us;
bool ledflag = true;
int nsamples = FFTsamples/2;
int label;
int detcnt, detectflag;
int start, seccnt;
  
void i2sInit(){
   i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
    .sample_rate =  44100,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 12bit, stereo, MSB
    .channel_format = I2S_CHANNEL_FMT_ALL_RIGHT,
    .communication_format = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 2,
    .dma_buf_len = 128,
   };

   i2s_pin_config_t pin_config;
   pin_config.bck_io_num   = I2S_PIN_NO_CHANGE;
   pin_config.ws_io_num    = PIN_CLK;
   pin_config.data_out_num = I2S_PIN_NO_CHANGE;
   pin_config.data_in_num  = PIN_DATA;
  
   i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
   i2s_set_pin(I2S_NUM_0, &pin_config);
   i2s_set_clk(I2S_NUM_0, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
}

void mic_record_task (void* arg){     
  while(1){
    i2s_read_bytes(I2S_NUM_0,(char*)BUFFER,READ_LEN,(100/portTICK_RATE_MS));
    adcBuffer = (uint16_t *)BUFFER;
    fft();
    detect();
    vTaskDelay(100 / portTICK_RATE_MS);
  }
}

void setup() {
  Serial.begin(115200);
  M5.begin();
  M5.Axp.ScreenBreath(9);  
  sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
  M5.Lcd.setRotation(1);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
  
  pinMode(M5_LED, OUTPUT);
  digitalWrite(M5_LED, HIGH);
  pinMode(M5_BUTTON_HOME, INPUT);
  pinMode(M5_BUTTON_RST, INPUT);
  i2sInit();
  xTaskCreatePinnedToCore(mic_record_task,"mic_record_task",2048,NULL,1,NULL,1);
  detcnt = 0;

  M5.Lcd.fillScreen(WHITE);
  delay(500);
  M5.Lcd.fillScreen(BLACK);
}

void fft(){
  for (int i = 0; i < FFTsamples; i++) {
    unsigned long t = micros();
    vReal = adcBuffer;
    vImag = 0;
    while ((micros() - t) < sampling_period_us) ;
  }

  FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.Compute(FFT_FORWARD);
  FFT.ComplexToMagnitude();
  //M5.Lcd.fillScreen(BLACK);  

#ifdef _SAMPLEMODE
  for (int band = 2; band < nsamples; band++) {
    float d = vReal[band] ;
//    Serial.print(band);
//    Serial.print(" : ");
//    Serial.print((band * 1.0 * SAMPLING_FREQUENCY) / FFTsamples / 1000);
//    Serial.print("kHz : ");
//    Serial.println(d);
    label = (ledflag) ? 0:1;   
    Serial.print(label);
    Serial.print(",");   
    Serial.print(band);
    Serial.print(",");
    Serial.println(d);
  }
  
  if(digitalRead(M5_BUTTON_RST) == LOW){
    ledflag = !ledflag;
    while(digitalRead(M5_BUTTON_RST) == LOW);
  }
  digitalWrite(M5_LED, ledflag);
#endif
}

void detect() {
  int res;
  res = predict( &vReal[0], nsamples );
  if (res > SCORE_LIMIT ) res = SCORE_LIMIT;  
  if (res > DET_THRES) {
    detcnt++;
    if (detcnt > DETCNT_MAX) detcnt=DETCNT_MAX;
  } else {
    detcnt--;
    if (detcnt < DETCNT_MIN) detcnt=DETCNT_MIN;
  }

  //Serial.println(detcnt);
  if (detcnt > DET_TCNT) {
    detectflag=1;   
  }else{
    detectflag=0;
  }
}

void finish_logo()
{
  M5.Lcd.setTextColor(0x7B1F);
  M5.Lcd.drawString( "OK!!", 0, 0, 8);
  delay(3000);
}

void loop(){
  char s[8];
  unsigned long t;
  //digitalWrite(M5_LED, !detectflag);  
  
  if ((start == 0) && (detectflag == 1)) {
    start=1;
    seccnt=0;
    Serial.println("start");
  }

  //if ((start == 1) && (detectflag == 0)) {
  //  M5.Lcd.fillScreen(BLACK);
  //  start = 0;
  //}
  if (digitalRead(M5_BUTTON_HOME) == LOW){
    M5.Lcd.fillScreen(BLACK);
    start = 0;
  }

  if (start==1) {
    t = micros();
    sprintf(s, "%2d", seccnt);
   
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);   
    M5.Lcd.drawString( s, 0, 0, 8);
   
    while ((micros() - t) < 1*1000*1000 ) ;  //1s=1000*1000us
    seccnt++;
    if (seccnt > MAX_SEC) {
      finish_logo();
      while (digitalRead(M5_BUTTON_HOME) == LOW) ;
      
      start=0;
      M5.Lcd.fillScreen(BLACK);
    }
  }
  
}
[/mw_shl_code]

WaterSoundDet.ino
[mw_shl_code=arduino,true]// SVM baased water sound detector
//

float w[] = {
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -0.83045869, -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,   -1,   -1,   -1,
  -1,   -1,   -1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    0.66035589,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    1,    1,    1,
   1,    1,    1,    0.17010281,  1,    1,
   1,    1,    1,    1,    1,    1,
   1  };

float bias = -4.85587964;


double predict( double *val, int nsamples )
{
    double sum=0.0;
   
    for (int band = 2; band < nsamples; band++) {
      sum += val[band] * w[band];
    }
    sum += bias;
    return(sum * -1.0);
}
[/mw_shl_code]

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 02:29 , Processed in 0.131464 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表