|
原文地址 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
[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]
|
|