+82) 10-8453-0224

House Beaver

EN  / 日本語 / 中文한국어

A water leak detector with machine learning Invented in Dyson lab Dyson library, Imperial College Rd, South Kensington, London SW7 9EG / Device, 2021 l 9,000 x w 3,200 x h 4,000 mm (l 9.8 x w 3.5 x h 4.4 yd) House Beaver: Nano 33 BLE(Arduino), NeoPixel, Sound Level Meter [SEN0232], Dc-Dc adapter, Lithium battery Beaver Dam: Nano 33 BLE(Arduino), directional microphones, 360-degree continuous servo motor, Camera, LED

House Beaver是一款工具,通过基于机器学习技术积累的信息,直观地向用户展示漏水和非漏水的声音,从而无需专家帮助,任何人都可以在家中轻松预防和检测漏水。家中的漏水会浪费稀缺的必需资源,污染土地和水源,造成个人财产损失,并引发邻里纠纷。与普遍的看法相反,这不仅仅发生在上层住户到下层住户之间。在多户住宅中,水可能通过墙缝从意想不到的地方漏出。然而,普通人发现这一问题的唯一方法是依赖于拥有昂贵设备的专家。专家们依靠个人经验来判断声音是漏水声还是管道中的正常水声。House Beaver用AI漏水探测器替代了这种专家经验。

 

 

Features

 

Why should this have been developed?

 
 

Development process

Step 1. Gathering the water leak sounds ->

 

Step 2. Creating libraries for machine learning >>link

 

Step3-1. Making physical prototype / Circuit diagram – Portable type (house Beaver)

 

Step 3-2. Making physical prototype / Circuit diagram – Installation type (Beaver Dam)

.

.

Step4. Coding

1. 安装Arduino nano 33ble开发环境。 [Arduino Nano 33 BLE 开发环境](https://www.arduino.cc/en/Guide/NANO33BLE)

 

2. 添加用于数据化检测声音的库。 – Sketch > Include Library > Add.ZIP Library > ei-changsublee-project-1-arduino-1.0.12.zip 添加 – Tool > Library Manager > 搜索并添加Adafruit_NeoPixel

 

3. 检查程序顶部的注释,设置板子,然后进行编译。

 

4. 程序说明 检测器板是否发出与预填充声音相似的声音,并在每次检查时一起检查周围声音的大小。 将检测的存在与否和声音的大小传递给LED板。 当声音的大小被传递并被检测到时,LED板输出LED。

 

Step 4-1. Coding – Portable type (house Beaver)

#define EIDSP_QUANTIZE_FILTERBANK 0 #include <PDM.h> #include <ChangSubLee-project-1_inferencing.h> #define DATA 2 #define DECIBAL A0 /** Audio buffers, pointers and selectors */ typedef struct { int16_t *buffer; uint8_t buf_ready; uint32_t buf_count; uint32_t n_samples; } inference_t; static inference_t inference; static bool record_ready = false; static signed short sampleBuffer[2048]; static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal int mydB; void setup() { // put your setup code here, to run once: Serial.begin(115200); Serial.println(“Edge Impulse Inferencing Demo”); pinMode(DATA, OUTPUT); // summary of inferencing settings (from model_metadata.h) // ei_printf(“Inferencing settings:\n”); // ei_printf(“\tInterval: %.2f ms.\n”, (float)EI_CLASSIFIER_INTERVAL_MS); // ei_printf(“\tFrame size: %d\n”, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE); // ei_printf(“\tSample length: %d ms.\n”, EI_CLASSIFIER_RAW_SAMPLE_COUNT / 16); // ei_printf(“\tNo. of classes: %d\n”, sizeof(ei_classifier_inferencing_categories) / sizeof(ei_classifier_inferencing_categories[0])); if (microphone_inference_start(EI_CLASSIFIER_RAW_SAMPLE_COUNT) == false) { ei_printf(“ERR: Failed to setup audio sampling\r\n”); return; } } void loop() { //ei_printf(“Starting inferencing in 2 seconds…\n”); digitalWrite(DATA, LOW); delay(2000); ei_printf(“Recording…\n”); bool m = microphone_inference_record(); if (!m) { ei_printf(“ERR: Failed to record audio…\n”); return; } ei_printf(“Recording done\n”); signal_t signal; signal.total_length = EI_CLASSIFIER_RAW_SAMPLE_COUNT; signal.get_data = &microphone_audio_signal_get_data; ei_impulse_result_t result = { 0 }; EI_IMPULSE_ERROR r = run_classifier(&signal, &result, debug_nn); if (r != EI_IMPULSE_OK) { ei_printf(“ERR: Failed to run classifier (%d)\n”, r); return; } // print the predictions //ei_printf(“Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.): \n”, // result.timing.dsp, result.timing.classification, result.timing.anomaly); for (size_t ix = 1; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) { Serial.print( result.classification[ix].value); float Data = result.classification[ix].value; //int OUT = map(mydB, 150, 900, 0, 255); Serial.print(“\nOUT: “); if (Data < 0.50) { digitalWrite(DATA, HIGH); Serial.print(“Cough Detected”); } } //for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) { // ei_printf(” %s: %.5f\n”, result.classification[ix].label, result.classification[ix].value); // } #if EI_CLASSIFIER_HAS_ANOMALY == 1 ei_printf(” anomaly score: %.3f\n”, result.anomaly); #endif } void ei_printf(const char *format, …) { static char print_buf[1024] = { 0 }; va_list args; va_start(args, format); int r = vsnprintf(print_buf, sizeof(print_buf), format, args); va_end(args); if (r > 0) { Serial.write(print_buf); } } static void pdm_data_ready_inference_callback(void) { int bytesAvailable = PDM.available(); // read into the sample buffer int bytesRead = PDM.read((char *)&sampleBuffer[0], bytesAvailable); if (record_ready == true || inference.buf_ready == 1) { for(int i = 0; i < bytesRead>>1; i++) { inference.buffer[inference.buf_count++] = sampleBuffer[i]; if(inference.buf_count >= inference.n_samples) { inference.buf_count = 0; inference.buf_ready = 1; } } } } static bool microphone_inference_start(uint32_t n_samples) { inference.buffer = (int16_t *)malloc(n_samples * sizeof(int16_t)); if(inference.buffer == NULL) { return false; } inference.buf_count = 0; inference.n_samples = n_samples; inference.buf_ready = 0; // configure the data receive callback PDM.onReceive(&pdm_data_ready_inference_callback); // optionally set the gain, defaults to 20 PDM.setGain(80); //ei_printf(“Sector size: %d nblocks: %d\r\n”, ei_nano_fs_get_block_size(), n_sample_blocks); PDM.setBufferSize(4096); // initialize PDM with: // – one channel (mono mode) // – a 16 kHz sample rate if (!PDM.begin(1, EI_CLASSIFIER_FREQUENCY)) { ei_printf(“Failed to start PDM!”); } record_ready = true; return true; } static bool microphone_inference_record(void) { inference.buf_ready = 0; inference.buf_count = 0; mydB = 0; analogWrite(DATA, 125); while(inference.buf_ready == 0) { delay(10); } analogWrite(DATA, 0); return true; } static int microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr) { arm_q15_to_float(&inference.buffer[offset], out_ptr, length); return 0; } static void microphone_inference_end(void) { PDM.end(); free(inference.buffer); }  

.

Step 4-2-1. Coding – LED for Installation type (Beaver Dam)

#include <Adafruit_NeoPixel.h> #define PIN 6 #define NUMLED 12 //Create an object to use a Neofixel. //The first factor is the number of LEDs in the neofixel. //The second factor is Arduino’s pin number connected to the Neofixel. //The third factor is the flag that changes depending on the type of Neofixel. Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMLED, PIN, NEO_GRB + NEO_KHZ800); uint32_t myColor[24] = {strip.Color(255, 0, 0), strip.Color(0, 255, 0), strip.Color(0, 0, 255), strip.Color(255, 0, 0), strip.Color(0, 255, 0), strip.Color(0, 0, 255), strip.Color(255, 0, 0), strip.Color(0, 255, 0), strip.Color(0, 0, 255), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 0, 0)}; int myData = 0; int flag = 1; void setup() { strip.begin(); //네오픽셀을 초기화하기 위해 모든LED를 off시킨다 strip.show(); pinMode(3,INPUT); pinMode(4,INPUT); Serial.begin(115200); Serial.println(“Edge Impulse Inferencing Demo”); } void loop() { //Repeat NeoPixel in the order below. while(digitalRead(3)) { int a = analogRead(A1); myData = myData>a ? myData : a; // 0~ 1023 flag = 1; } //Serial.println(myData); if(digitalRead(4) && flag) { Serial.print(“detect”); Serial.println(myData); flag = 0; int Lv = 0; if(myData < 200) //0~49 0step { Lv = 0; } else if(myData < 250) //50~89 1step { Lv = 1; } else if(myData < 300) //90~119 2step { Lv = 2; } else if(myData < 350) //120~140 3step { Lv = 3; } else if(myData < 400) //140~154 4step { Lv = 4; } else if(myData < 430) //155~167 5step { Lv = 5; } else if(myData < 460) //170~179 6step { Lv = 6; } else if(myData < 480) //180~189 7step { Lv = 7; } else if(myData < 500) //190~199 8step { Lv = 8; } else if(myData < 550) //190~199 9step { Lv = 9; } else if(myData < 600) //190~199 10step { Lv = 10; } else if(myData < 650) //190~199 11step { Lv = 11; } else if(myData < 700) //190~199 12step { Lv = 12; } myData = 0; for(uint16_t i=0; i<Lv; i++) { strip.setPixelColor(i, strip.Color(255, 255, 0)); strip.show(); } for(uint16_t i=Lv; i<strip.numPixels(); i++) { strip.setPixelColor(i, strip.Color(0, 0, 255)); strip.show(); } // for(uint16_t i=0; i<Lv; i++) { // strip.setPixelColor(i, myColor[i]); // strip.show(); // } // for(uint16_t i=Lv; i<strip.numPixels(); i++) { // strip.setPixelColor(i, myColor[i]); // strip.show(); // } Serial.print(“DETECT”); Serial.println(Lv); delay(1000); for(uint16_t i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, strip.Color(0, 0, 255)); strip.show(); } } }  

.

Step 4-2. Coding – Installation type (Beaver Dam)

  #include <Adafruit_NeoPixel.h> #include <Servo.h> #define PIN 6 #define NUMLED 24 #define RSIG 4 #define DSIG 5 int dataA[110]; int dataB[110]; int dataC[110]; int dataD[110]; byte itemSize = sizeof(dataA[0]); byte itemCount = sizeof(dataA) / itemSize; int pos1 = 0; int pos2 = 0; //Create an object to use a Neofixel. //The first factor is the number of LEDs in the neofixel. //The second factor is Arduino’s pin number connected to the Neofixel. //The third factor is the flag that changes depending on the type of Neofixel. Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMLED, PIN, NEO_GRB + NEO_KHZ800); Servo myservo1; Servo myservo2; uint32_t myColor[8] = {strip.Color(0, 0, 0), strip.Color(255, 0, 0), strip.Color(255, 140, 0), strip.Color(255, 255, 0), strip.Color(0, 255, 0), strip.Color(0, 0, 255), strip.Color(75, 0, 130), strip.Color(128, 0, 128)}; int myData = 0; int flag = 0; int myData1; int myData2; int myData3; int myData4; void setup() { strip.begin(); //네오픽셀을 초기화하기 위해 모든LED를 off시킨다 strip.show(); pinMode(RSIG,INPUT); pinMode(DSIG,INPUT); myservo1.attach(9); myservo2.attach(10); Serial.begin(115200); Serial.println(“Edge Impulse Inferencing Demo”); for(uint16_t i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, myColor[0]); strip.show(); } myservo1.write(0); myservo2.write(180); } void loop() { //아래의 순서대로 NeoPixel을 반복한다. memset(dataA, 0, sizeof(dataA)); memset(dataB, 0, sizeof(dataB)); memset(dataC, 0, sizeof(dataC)); memset(dataD, 0, sizeof(dataD)); int i = 0; while(digitalRead(RSIG)) { dataA[i] = analogRead(A0); dataB[i] = analogRead(A1); dataC[i] = analogRead(A2); dataD[i++] = analogRead(A3); delay(10); flag = 1; } if(flag == 1) { qsort(dataA, itemCount, itemSize, sort_asc); qsort(dataB, itemCount, itemSize, sort_asc); qsort(dataC, itemCount, itemSize, sort_asc); qsort(dataD, itemCount, itemSize, sort_asc); myData1 = (dataA[2]+dataA[3]+dataA[4]+dataA[5]+dataA[6])/5; myData2 = (dataB[2]+dataB[3]+dataB[4]+dataB[5]+dataB[6])/5; myData3 = (dataC[2]+dataC[3]+dataC[4]+dataC[5]+dataC[6])/5; myData4 = (dataD[2]+dataD[3]+dataD[4]+dataD[5]+dataD[6])/5; flag=2; } //Serial.println(myData); if(digitalRead(DSIG) && flag==2) { Serial.println(“detect”); Serial.print(myData1); Serial.print(“, “); Serial.print(myData2); Serial.print(“, “); Serial.print(myData3); Serial.print(“, “); Serial.println(myData4); flag = 0; int Lv = 0; int a = myData1 > myData2 ? myData1 : myData2; int b = myData3 > myData4 ? myData3 : myData4; myData = a > b ? a : b; int dir = 0; if(myData == myData1) dir = 3; else if(myData == myData2) dir = 4; else if(myData == myData3) dir = 1; else if(myData == myData4) dir = 2; if(myData < 100) //0~49 0step { Lv = 0; } else if(myData < 150) //50~89 1step { Lv = 1; } else if(myData < 200) //90~119 2step { Lv = 2; } else if(myData < 250) //120~140 3step { Lv = 3; } else if(myData < 300) //140~154 4step { Lv = 4; } else if(myData < 350) //155~167 5step { Lv = 5; } else if(myData < 400) //170~179 6step { Lv = 6; } else if(myData < 450) //180~189 7step { Lv = 7; } myData = 0; Serial.print(“LV: “); Serial.print(Lv); Serial.print(” dir: “); Serial.print(dir); if(dir==1) { myservo1.write(180); myservo2.write(140); for(uint16_t i=0; i<5; i++) { strip.setPixelColor(i, myColor[Lv]); strip.show(); } } else if(dir==2) { myservo1.write(105); myservo2.write(180); delay(10); for(uint16_t i=7; i<12; i++) { strip.setPixelColor(i, myColor[Lv]); strip.show(); } } else if(dir==3) { myservo1.write(0); myservo2.write(180); for(uint16_t i=13; i<18; i++) { strip.setPixelColor(i, myColor[Lv]); strip.show(); } } else if(dir==4) { myservo1.write(180); myservo2.write(60); for(uint16_t i=19; i<24; i++) { strip.setPixelColor(i, myColor[Lv]); strip.show(); } } delay(1000); for(uint16_t i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, strip.Color(0, 0, 0)); strip.show(); } } } int sort_asc(const void* item1, const void* item2) { //compare int and assume it and cast it as int int a = *((int*)item1); int b = *((int*)item2); return b – a; }  
 

‘Step 5-1. User test – Portable type (house Beaver)

 

Step 5-2. User test – Installation type (House Dam)

 
 

Previous Research

中国山西省严重的土壤污染  >>link

饮用污染地下水五年的居民起诉建筑公司索赔  >>link 律师事务所城市与人类环境中心  >>link

美国各地的管道老化和漏水率呼吁基础设施升级   每年约有近2万亿加仑的水因泄漏而流失,占美国处理的总饮用水量的约15%。 >> link
为所有人管理水资源:经合组织国家管理良好的供水设施中的漏水率通常在10%-30%之间。然而,在发展中国家,这一比例经常超过40%,有时甚至达到70%。结果,需要生产和运输的水远远多于最终到达消费者的水 >>link
管道泄漏怎么办?   管道泄漏虽然不像漏水的水龙头那么让人烦恼或者明显,但比漏水的水龙头更严重更昂贵。平均而言,铅笔尖大小的管道泄漏在24小时内甚至在低水压下也会浪费约970加仑的水(这个计算是使用40psi的水压进行的;对于夏洛特地区的住宅来说,这个水压水平较低)。>>link

 

The keywords of the survey with 80 respondents and 10 stakeholders

.

  • Qurrel or dispute with neighbors
  • Financial damage
  • No insurance
  • floor heating system
  • Apartment
  • Willing to buy the water leak detector
  • Be reasonable over £64.
  • Should be made mandatory
  • Causes electrical problems
  • Contaminates soil and water

 

Expected Solution

.

 

 

 

 

 

 

 

 

SUBART ™ by Bear Lee.  2006.

105-87-21524

Bear Lee

T.  +82) 10-4216-3225

E.  subart28@gmail.com

W. https://subart.co.kr/