+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

[ngg src=”galleries” ids=”170″ display=”basic_thumbnail” thumbnail_width=”200″ thumbnail_height=”200″ thumbnail_crop=”0″ number_of_columns=”4″]

심각한 토양 오염 – 중국 산시성 [링크] 오염된 지하수를 5년 동안 마신 주민들이 건설 회사를 상대로 손해 배상 소송을 제기했습니다. [링크] 법률 사무소 시티 앤 휴먼 환경 센터 [링크]

 

미국 전역에서의 파이프 연령과 누출률은 인프라 개선을 요구합니다   약 2조 갤런의 물이 매년 누출로 손실되며, 이는 미국에서 처리된 총 음용수의 약 15%에 해당합니다.  >> link
모두를 위한 물 관리: OECD 국가의 잘 운영되는 수도 시설에서의 누수는 일반적으로 수돗물 생산량의 10%-30% 범위에 있습니다. 그러나 개발도상국에서는 자주 40%를 초과하며, 때로는 70%에 달하기도 합니다. 그 결과, 소비자에게 최종적으로 도달하는 물보다 훨씬 더 많은 물이 생산되고 운송되어야 합니다. >>link
파이프 누출은 어떻게 되나요?   파이프 누출은 물이 새는 수도꼭지보다는 덜 짜증나거나 명백하지 않지만, 훨씬 더 심각하고 비싼 문제입니다. 평균적으로 연필 끝 크기의 파이프 누출은 심지어 낮은 수압에서 하루에 약 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/