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

vany5921 发表于 2020-5-29 09:22

简易声控计时器


原文地址 https://www.hackster.io/yutakau/ ... und-detector-fa442d
我创造了一个计时器来测量洗手的时间。最主要的问题是如何开始计时。按动开关是最容易的,但最好在洗手前不要碰它们。我们可以使用红外线或超声波传感器,但由于需要外部元件,因此很难广泛使用。这个项目的独特之处在于,自来水的声音是通过麦克风来启动计时器的。这里使用的M5StickC内置麦克风,因此不需要任何外部组件。因此,任何人都可以通过将代码编程成M5StickC来实现一个简单的洗手计时器。

只检测水声有很多可能的方法。例如,使用高于某个值的音量是一种简单的方法,但它可能存在很多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
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'))


HandwashingTimer.ino
// Hand washing timer
//
// for M5Stick-C
//

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


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

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

uint8_t BUFFER = {0};
uint16_t *adcBuffer = NULL;
const uint16_t FFTsamples = 512;
double vReal;
double vImag;
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 ;
//    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, 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;
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);
    }
}

}


WaterSoundDet.ino
// 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 * w;
    }
    sum += bias;
    return(sum * -1.0);
}


topdog 发表于 2020-5-30 20:55

microML
页: [1]
查看完整版本: 简易声控计时器