+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?

Subart Cafe cafe for the art community

 

 
 
 

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を出力します。

  1.   
  2. .
  3. Step 4-1. Coding – Portable type (house Beaver)
  4. #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); }  
  5. .
  6. Step 4-2-1. Coding – LED for Installation type (Beaver Dam)
  7. #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(); } } }  
  8. .
  9. Step 4-2. Coding – Installation type (Beaver Dam)
  10.   #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

汚染された地下水を5年間飲んだ住民が建設会社を相手取って損害賠償を求めて提訴 >>link

法律事務所シティ・アンド・ヒューマン・エンバイロンメントセンター >>link

全米のパイプの経年劣化と漏水率はインフラの更新を求める   年間約2兆ガロンの水が漏れによって失われており、これは米国で処理される総飲料水の約15%に相当します。 >> link
すべての人のための水管理:OECD諸国のよく管理された水道施設における漏水率は通常、水生産量の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/