В данной статье мы рассмотрим создание устройства на основе платы Arduino Nano 33 BLE Sense, которое будет способно контролировать состояние здоровья пчелиной семьи и обнаруживать в ней событие роения. Вдобавок к этому устройству будет представлено приложение, которое, на наш взгляд, поможет пчеловодам в эффективном производстве мёда и других продуктов пчеловодства.
Разработчики этого приложения ставили перед собой цель помочь пчеловодам максимально эффективно управлять пчелами. Идея работы приложения основана на контроле температуры и влажности, также оно способно как бы проникать внутрь пчелиной семьи в определенном улье и обнаруживать такое событие как роение (swarming). Это событие заключается в том, что пчелиная семья разделяется на 2 части. Одна часть пчел при этом остается в улье, а другая покидает улей и находит себе новый дом. Первая часть остается в улье и ждет когда у них появится новая матка, а вторая часть покидает улей со старой маткой. И было бы неплохо если бы пчеловод как можно скорее узнал об этом событии чтобы предпринять своевременные действия. В этом ему и будет помогать наше приложение, которое будет распознавать/обнаруживать событие роения пчел на основе анализа звуков жужжания пчел.
Необходимые компоненты
Аппаратное обеспечение
- Плата Arduino Nano 33 BLE Sense.
- Кабель USB-A to Micro-USB.
- Смартфон с операционной системой Android.
- Батарейка (аккумулятор).
Программное обеспечение
- Arduino IDE.
- Edge Impulse Studio.
- Android Studio.
Основные принципы работы проекта
Поскольку у каждого пчеловода обычно достаточно большое количество ульев, то их "ручной" контроль отнимает много времени. С помощью нашего приложения пчеловод сможет подключаться к каждому улью с помощью смартфона и Bluetooth соединения и на экране приложения он сможет наблюдать за состояние здоровья пчелиной семьи. В дополнение к этому приложение будет предупреждать пчеловода о событии роения в ульев чтобы он смог предпринять своевременные действия по предотвращению улета пчел в дикую природу, что, естественно, приведет к снижению производства мёда.
Основой проекта является плата Arduino Nano BLE 33 Sense, которая содержит в своем составе микрофон. С помощью него микроконтроллер в составе платы будет "прислушиваться" к жужжанию пчел и с помощью специальной обучающей модели обнаруживать различия в жужжании пчел во время присутствия матки в улье и во время ее отсутствия. Также плата Arduino Nano BLE 33 Sense содержит в своем составе датчик температуры и влажности – с помощью его показаний мы можем судить об условиях содержания и состоянии здоровья пчелиной семьи внутри улья. Поскольку при разработке данного проекта ключевое внимание уделялось уменьшению потребления энергии, то система измеряет условия внутри улья всего несколько раз в день или во временные периоды между 10 a.m. (до полудня) и 1 p.m (после полудня), когда вероятность события роения максимальна. В остальное время система находится в холостом режиме и не потребляет энергию.
Модель машинного обучения
Для построения модели машинного обучения нам необходимо будет выполнить следующую последовательность шагов.
1. Захват данных с помощью микрофона. На этом этапе мы будем собирать звуки жужжания пчел чтобы сформировать основу (набор данных) для обучающей модели.
2. Спектральный анализ звуков. На его основе мы будем строить спектрограмму.
3. Построение модели с использованием нейронной сети. Спектрограмма будет являться входными данными для нейронной сети, которые будут использоваться для обучения модели. После проведения расчетов мы получим результаты в форме матрицы, которая будет показывать эффективность распознавания нашей модели.
Граф на следующем рисунке дает наглядное представление этого процесса.
4. Создание библиотеки и загрузка ее в плату Arduino.
Авторы проекта все это уже проделали и вы можете воспользоваться готовыми результатами их труда – в этом случае вам не нужно будет записывать звуки жужжания пчел и обучать модель машинного обучения.
Взаимодействие между составными частями проекта
Для передачи данных от платы Arduino на смартфон с Android мы будем использовать технологию Bluetooth. Плата Arduino Nano BLE 33 Sense содержит в своем составе встроенный Bluetooth модуль, что позволяет без привлечения дополнительных устройств организовать связь с помощью данной технологии. Технология Bluetooth позволит нам получать данные на смартфон из улья, находясь на безопасном расстоянии от него и, таким образом, не опасаясь укусов пчел.
Приложение для Android
Также авторы данного проекта разработали приложение для Android (ссылка на его скачивание в конце статьи), которое будет подключаться по Bluetooth к плате Arduino Nano BLE 33 Sense, получать от нее данные и показывать на своем экране сообщения о состоянии здоровья пчелиной семьи.
Соединение с устройством в улье.
Главный экран приложения с предупреждения, которые плата Arduino передает приложению для Android.
На следующем рисунке показана работа приложения.
Тестирование работы проекта
Для тестирования работы контролера пчел выполните следующую последовательность шагов.
1. Загрузите программу проекта (приведена в конце статьи) в плату Arduino.
2. Скачайте приложение на свой смартфон (или другое устройство) с Android (ссылка на скачивание .apk файла приведена в конце статьи).
3. Установите плату Arduino в улей.
4. Установите Bluetooth соединение между платой Arduino и приложением на Android.
Возможные улучшения проекта
Вы можете внести в проект следующие улучшения:
- улучшить модель машинного обучения увеличив выборку звуков жужжания пчел;
- добавить функционал приложению на Android;
- добавить в проект сеть на основе LoraWan (основана на технологии LoRa), с помощью которой можно будет передавать данные от разных ульев на единый сервер, информацию на котором можно будет посмотреть в любое время из любой точки, где есть подключение к сети интернет.
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
/* Edge Impulse Arduino examples Copyright (c) 2021 EdgeImpulse Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // If your target is limited in memory remove this macro to save 10K RAM (если в вашей плате Arduino проблемы со свободной памятью, то можете удалить вышеописанную преамбулу перед загрузкой кода программы в память – этим вы сэкономите 10 Кбайт памяти) #define EIDSP_QUANTIZE_FILTERBANK 0 /* Includes ---------------------------------------------------------------- */ #include <PDM.h> #include <smartbees_inference.h> #include <ArduinoBLE.h> #include <ArduinoJson.h> #include <Arduino_HTS221.h> #define BLE_UUID_STRING "1A3AC131-31EF-758B-BC51-54A61958EF82" #define BLE_UUID_TEST_SERVICE "9A48ECBA-2E92-082F-C079-9E75AAE428B1" /** 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 signed short sampleBuffer[2048]; static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal BLEDevice central; BLEService service(BLE_UUID_TEST_SERVICE); BLEStringCharacteristic serviceOutput(BLE_UUID_STRING, BLERead | BLENotify, 200); /** @brief Arduino setup function */ void setup() { // put your setup code here, to run once: Serial.begin(115200); while (!Serial); pinMode(LED_BUILTIN, OUTPUT); if (!BLE.begin()) { Serial.println("starting BLE failed!"); while (1); } BLE.setLocalName("BeeHive"); BLE.setAdvertisedService(service); service.addCharacteristic(serviceOutput); BLE.addService(service); BLE.advertise(); Serial.println("Bluetooth device active, waiting for connections..."); if (!HTS.begin()) { Serial.println("Failed to initialize humidity temperature sensor!"); while (1); } if (microphone_inference_start(EI_CLASSIFIER_RAW_SAMPLE_COUNT) == false) { ei_printf("ERR: Failed to setup audio sampling\r\n"); return; } } /** @brief Arduino main function. Runs the inferencing loop. */ void loop() { central = BLE.central(); if (central) { Serial.print("Connected to central: "); Serial.println(central.address()); digitalWrite(LED_BUILTIN, HIGH); while (central.connected()) { ei_printf("Starting inferencing in 2 seconds...\n"); delay(2000); ei_printf("Recording...\n"); bool m = microphone_inference_record(); if (!m) { ei_printf("ERR: Failed to record audio...\n"); return; } float temperature = HTS.readTemperature(); float humidity = HTS.readHumidity(); StaticJsonDocument<600> doc; doc["temperature"] = (round(temperature*10)/10.0); doc["humidity"] = (round(humidity*10)/10.0); doc["event"] = ""; ei_printf("Recording done\n"); signal_t signal; signal.total_length = EI_CLASSIFIER_RAW_SAMPLE_COUNT; signal.get_data = µphone_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 "); ei_printf("(DSP: %d ms., Classification: %d ms., Anomaly: %d ms.)", result.timing.dsp, result.timing.classification, result.timing.anomaly); ei_printf(": \n"); float predictions[3]; for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) { predictions[ix] = (round(result.classification[ix].value * 10) / 10.0); //data.add((round(result.classification[ix].value*10)/10.0)); } int maximal = 0, index = 0; for (int i = 0; i < 3; i++) { if (maximal < predictions[i]) { maximal = predictions[i]; index = i; } } switch (index) { case (0): doc["event"] = "MATICA"; break; case (1): doc["event"] = "NIMATICE"; break; case (2): doc["event"] = "ROJENJE"; break; default: doc["event"] = "ERROR"; break; } String json; serializeJson(doc, json); if (central.connected()) serviceOutput.writeValue(json); #if EI_CLASSIFIER_HAS_ANOMALY == 1 ei_printf(" anomaly score: %.3f\n", result.anomaly); #endif Serial.print("Temperature = "); Serial.print(temperature); Serial.println(" °C"); Serial.print("Humidity = "); Serial.print(humidity); Serial.println(" %"); } digitalWrite(LED_BUILTIN, LOW); Serial.print("Disconnected from central: "); Serial.println(central.address()); } } /** @brief Printf function uses vsnprintf and output using Arduino Serial @param[in] format Variable argument list */ 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); } } /** @brief PDM buffer full callback Get data and call audio thread callback */ 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 (inference.buf_ready == 0) { 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; break; } } } } /** @brief Init inferencing struct and setup/start PDM @param[in] n_samples The n samples @return { description_of_the_return_value } */ 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); 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!"); microphone_inference_end(); return false; } return true; } /** @brief Wait on new data @return True when finished */ static bool microphone_inference_record(void) { inference.buf_ready = 0; inference.buf_count = 0; while (inference.buf_ready == 0) { //delay(10); delay(2000); } return true; } /** Get raw audio signal data */ static int microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr) { numpy::int16_to_float(&inference.buffer[offset], out_ptr, length); return 0; } /** @brief Stop PDM and release buffers */ static void microphone_inference_end(void) { PDM.end(); free(inference.buffer); } #if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_MICROPHONE #error "Invalid model for current sensor." #endif |
Apk файла Android приложения для данного проекта можно скачать по следующей ссылке.
979 просмотров