Мы видели платформу разработки Edge Impulse для машинного обучения на периферийных устройствах, используемую несколькими платами, но пока у нас не было возможности опробовать ее. Поэтому, когда представители компании Seeed Studio спросили нас, интересно ли нам протестировать плату XIAO BLE Sense на базе nRF52840 , мы подумали, что было бы неплохо протестировать ее с помощью Edge Impulse, поскольку мы видели демонстрацию распознавания движения/жестов на плате.
Это было не просто, так как нам потребовалось четыре месяца, чтобы завершить обзор с того момента, как Seeed Studio впервые связалась с нами, в основном из-за плохой работы DHL, из-за которой первые платы застряли на таможни, а затем тратить время на не самые лучшие инструкции, которые нам приходилось видеть (теперь исправлены), и другие моменты. Но нам удалось заставить его работать (вроде), так что давайте посмотрим.
Распаковка XIAO BLE (Sense) и OLED-дисплея
Поскольку в демонстрации распознавания жестов использовался OLED-дисплей, мы также попросили его и получили плату XIAO BLE (без датчика), плату XIAO BLE Sense и OLED-дисплей Grove 0,66″ .
Обе платы очень маленькие и абсолютно одинаковые, за исключением того, что в XIAO BLE отсутствует встроенный 6-осевой IMU LSM6DS3TR (внизу слева).
Необходимость пайки…
Перед загрузкой прошивки на плату пришлось припаять дисплей к плате. Мы просто перерезали кабель Grove и припаяли черный и красный провода к питанию, а белый и желтый к I2C.
У нас нет 3D-принтера (так же застрял на таможне), поэтому вместо этого мы использовали несколько слоев двустороннего скотча, чтобы соединить две платы вместе, но это необязательно.
Эскизы Arduino для OLED-дисплея и акселерометра XIAO BLE Sense
Нам потребовалось некоторое время, чтобы найти инструкции для демонстрации распознавания жестов, так как они не были указаны в описании видео, и нам не удалось ничего найти об этом в вики Seeed Studio. В конце концов нам дали ссылку на инструкции, и компания изменила веб-сайт, чтобы его было легче найти.
Прежде чем испробовать Edge Impulse, мы запустим два скетча Arduino, чтобы проверить, как работает OLED-дисплей и акселерометр.
Первым шагом является добавление URL-адреса диспетчера плат Seeed Studio: https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json.
Теперь мы можем установить пакет для поддержки плат Seeed nRF52…
… и как только это будет сделано, давайте подключим плату кабелем USB-C к нашему компьютеру и выберем плату Seeed XIAO BLE Sense — nRF52840 с настройками по умолчанию.
Давайте попробуем программу «Hello World», чтобы убедиться, что наша плата работает и соединение с OLED-дисплеем в порядке.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <Arduino.h> #include <U8g2lib.h> #ifdef U8X8_HAVE_HW_SPI #include <SPI.h> #endif #ifdef U8X8_HAVE_HW_I2C #include <Wire.h> #endif U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ PIN_WIRE_SCL, /* data=*/ PIN_WIRE_SDA, /* reset=*/ U8X8_PIN_NONE); void setup(void) { u8g2.begin(); } void loop(void) { u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font u8g2.drawStr(32,30,"Hello"); // write something to the internal memory u8g2.drawStr(32,45,"Seeed!"); u8g2.sendBuffer(); // transfer internal memory to the display delay(1000); } |
Но устройство работало не так как ожидалось, и во время компиляции мы получили ошибку:
1 2 |
exec: "adafruit-nrfutil": executable file not found in $PATH Error compiling for board Seeed XIAO BLE Sense - nRF52840. |
Ответ на сайте Adafruit, где мы узнаем две важные детали:
- Для nRF52 требуется Arduino 1.8.15 или выше, поэтому вам, возможно, придется обновиться до последней версии.
- Linux требует установки adafruit-nrfutil
Поскольку мы используем Ubuntu 20.04, нам пришлось запустить:
1 2 |
pip3 install --user adafruit-nrfutil export PATH=:$PATH:$HOME/.local/bin |
Обратите внимание, что это НЕ требуется, если вы используете Arduino IDE в Windows или MacOS. Утилита была установлена в $HOME/.local/bin, поэтому вам нужно добавить ее в свой путь и перезапустить Arduino IDE. Это можно временно сделать в командной строке:
1 2 |
export PATH=:$PATH:$HOME/.local/bin |
Или вы можете изменить файл /etc/environment или ~/.bashrc, чтобы навсегда добавить папку в свой PATH. На удалось создать скетч и двоичный файл был записан на плату без проблем.
Но на дисплее ничего не отображалось. Мы добавили отладочное сообщение serial.println в основной цикл, чтобы убедиться, что он действительно работает, дважды проверили соединения с помощью мультиметра и не смогли найти очевидного решения. Представители Seeed Studio посоветовали нам перейти на версию 1.0.0 пакета плат Seeed nRF52.
И это действительно помогло!
Обратите внимание, что вам больше не нужно переходить на версию 1.0.0 в остальной части обзора, и рекомендуется использовать 2.6.1 и выше. Новый образец «Hello World» выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <Arduino.h> #include <U8x8lib.h> U8X8_SSD1306_64X48_ER_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); void setup(void) { u8x8.begin(); } void loop(void) { u8x8.setFont(u8x8_font_amstrad_cpc_extended_r); u8x8.drawString(0,0,"idle"); u8x8.drawString(0,1,"left"); u8x8.drawString(0,2,"right"); u8x8.drawString(0,3,"up&down"); } |
Пора переходить к тестированию демо акселерометра. Во-первых, нам нужно установить библиотеку LSM6DS3 Arduino от Seeed Studio.
Обратите внимание, что существует также официальный Arduino_LSM6DS3, который вам может потребоваться удалить, чтобы избежать конфликтов. Вот код:
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 |
#include "LSM6DS3.h" #include "Wire.h" //Create a instance of class LSM6DS3 LSM6DS3 myIMU(I2C_MODE, 0x6A); //I2C device address 0x6A #define CONVERT_G_TO_MS2 9.80665f #define FREQUENCY_HZ 50 #define INTERVAL_MS (1000 / (FREQUENCY_HZ + 1)) static unsigned long last_interval_ms = 0; void setup() { // put your setup code here, to run once: Serial.begin(115200); while (!Serial); //Call .begin() to configure the IMUs if (myIMU.begin() != 0) { Serial.println("Device error"); } else { Serial.println("Device OK!"); } } void loop() { if (millis() > last_interval_ms + INTERVAL_MS) { last_interval_ms = millis(); Serial.print(myIMU.readFloatGyroX() * CONVERT_G_TO_MS2,4); Serial.print('\t'); Serial.print(myIMU.readFloatGyroY() * CONVERT_G_TO_MS2,4); Serial.print('\t'); Serial.println(myIMU.readFloatGyroZ() * CONVERT_G_TO_MS2,4); } } |
Обратите внимание, что когда мы использовали платы Seeed nRF52 v1.0.0 с этим образцом, мы получали сообщения «ошибка декодирования», которые исчезли с версией 2.6.1. Нам нужно открыть последовательный монитор, чтобы проверить, отображаются ли значения X, Y, Z.
Важно: для следующего шага нам все равно потребуется запустить демо-версию ускорителя. Поэтому, если вы сначала играете с другими примерами, убедитесь, что демо-версия accelerator запущена, прежде чем переключаться на Edge Impulse.
Edge Impulse на XIAO BLE Sense
Теперь, когда мы знаем, что наше оборудование работает должным образом, давайте перейдем к Edge Impulse Studio, чтобы начать работу. Давайте зарегистрируемся и создадим наш первый проект.
Мы выберем данные акселерометра.
Нам также потребуется установить Edge Impulse CLI в Linux (Ubuntu 20.04 для этого обзора). Для этого сначала требуется установить NodeJS 14.x:
1 2 |
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - sudo apt install -y nodejs |
Каталог по умолчанию будет находиться в /usr, доступном для root, давайте изменим его на каталог пользователя, который мы также добавим в наш путь:
1 2 3 |
mkdir ~/.npm-global npm config set prefix '~/.npm-global' echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc |
Теперь мы можем установить Edge Impulse CLI:
1 |
npm install -g edge-impulse-cli |
Возможно, вам придется выйти из терминала и перезапустить его, чтобы применить новый PATH. Теперь мы можем запустить edge-impulse-data-forwarder, используемый для плат, официально не поддерживаемых Edge Impulse (например, XIAO BLE Sense):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$ edge-impulse-data-forwarder Edge Impulse data forwarder v1.14.12 Endpoints: Websocket: wss://remote-mgmt.edgeimpulse.com API: https://studio.edgeimpulse.com/v1 Ingestion: https://ingestion.edgeimpulse.com [SER] Connecting to /dev/ttyACM0 [SER] Serial is connected (5B:5B:83:F3:C6:0A:0C:DD) [WS ] Connecting to wss://remote-mgmt.edgeimpulse.com [WS ] Connected to wss://remote-mgmt.edgeimpulse.com [SER] Detecting data frequency... [SER] Detected data frequency: 50Hz ? 3 sensor axes detected (example values: [10.9834,-15.7887,-4.8053]). What do y ou want to call them? Separate the names with ',': Ax, Ay, Az ? What name do you want to give this device? XIAO BLE SENSE [WS ] Device "XIAO BLE SENSE" is now connected to project "XIAO BLE Sense motion detection" [WS ] Go to https://studio.edgeimpulse.com/studio/91558/acquisition/training to build your machine learning model! |
При первом запуске команды вам потребуется ввести имя пользователя и пароль (не показано выше). Утилита сканирует последовательные устройства, подключается к граничному импульсу, пытается обнаружить данные из последовательного порта и после этого просит нас назвать поля данных (Ax, Ay, Az), устройство (XIAO BLE SENSE) и автоматически подключит его в проект, который мы только что создали. Если в Edge Impulse несколько проектов, вам будет предложено сначала выбрать проект. Это означает, что он в основном не зависит от аппаратного обеспечения, и пока ваша плата выводит данные акселерометра на последовательный интерфейс, он должен работать.
Вернёмся к Edge Impulse, нажмём на Сбор данных, и мы увидим наше устройство вместе с параметрами датчика и настройками частоты данных.
Давайте установим размер выборки на 20 000 мс, определим метку, нажмем «Начать выборку» и переместим плату вверх и вниз примерно на 1 секунду в течение 20 секунд, чтобы получить данные.
Затем нам нужно разделить данные, щелкнув три точки в необработанном разделе и выбрав «Разделить выборку». Нажмите «+Добавить сегмент», чтобы добавить дополнительный раздел. Мы должны повторять это, пока не получим около 20 сегментов, представляющих движение вверх и вниз. Если вы двигались медленнее или быстрее, чем 1 с, отрегулируйте время в «Установить длину сегмента (мс)».
Мы используем Firefox, и у нас была ошибка, из-за которой мы могли добавить сегмент, но когда мы выбирали его для перемещения, он смещался вправо, иногда за пределы экрана. Но если бы мы продолжали нажимать кнопку мыши и двигать ее влево, это могло бы вернуть ее в поле зрения. Это было не совсем удобно, и мы не можем слишком сильно увеличивать масштаб, иначе коробка слишком сильно выйдет за пределы дисплея. Использование Chrome или Microsoft Edge с Edge Impulse может быть лучше.
Как только мы нажмем «Разделить», мы увидим выбранную нами 1-секундную выборку данных.
Мы можем повторить сбор и разделение данных для других жестов, таких как левый и правый, круг по часовой стрелке и круг против часовой стрелки. Но мы бы порекомендовали сначала сделать это простым, как мы увидим ниже.
На этом этапе вы можете увидеть проблему:
Одна или несколько меток в вашем наборе данных имеют плохое разделение обучения/тестирования.
Чтобы исправить это, вы можете либо собирать тестовые данные в течение более короткого периода (например, 2 секунды), либо повторно сбалансировать набор данных, щелкнув «Панель инструментов» в левом меню и прокрутив вниз, чтобы найти кнопку «Выполнить обучение/тестовое разделение».
Теперь мы готовы создать импульс. Нажмите «Создать импульс» -> «Добавить блок обработки» -> «Спектральный анализ» -> «Добавить блок обучения» -> «Выбрать классификацию (Keras)» -> «Сохранить импульс».
Щелкните Спектральные объекты в Спектральном анализе , затем Сохранить параметры и Создать объекты .
Вы ожидаете, что данные будут четко разделены, но явно есть некоторое совпадение, поэтому обученные данные не идеальны. Мы все же постараемся продолжить.
Нажмите «Классификатор NN», затем «Начать обучение», что займет около одной минуты. Затем мы можем выбрать Неоптимизированный (float32)
Точность действительно низкая, и модель практически непригодна для использования, поскольку правильно определяется только образец «против часовой стрелки».
Давайте попробуем еще раз, но только вверх и вниз, влево и вправо и по кругу (по часовой стрелке), стараясь уложить каждое движение в одну секунду.
Диаграмма обозревателя функций выглядит намного лучше с синими, оранжевыми и зелеными точками в своих областях. Также можно удалить некоторые образцы, которые могут вызвать проблемы. Результаты не идеальны, но мы все же можем попробовать.
Пройдемся по «Тестированию модели» в левом меню.
Это разочаровывает, так как хорошо работает только круг по часовой стрелке, в то время как «лево и право» можно обнаружить примерно в половине случаев, а «вверх и вниз» ошибочно определяют как лево и право. Таким образом, помимо обучения данных, оператор также должен быть обучен.
Важной частью является метод создания импульса, и со временем мы сможем создавать более качественные данные. Давайте создадим библиотеку Arduino, щелкнув Deployment в левом меню, затем Arduino Library, Build и, наконец, загрузим файл .ZIP.
Вернемся к среде разработки Arduino. Загрузите образец Arduino, предоставленный Seeed Studio, который претерпел множество изменений за последние несколько месяцев. Измените заголовок файла Edge Impulse (строка 24 в примере ниже), чтобы он соответствовал вашему собственному, и нам также пришлось закомментировать строку с библиотекой U8X8lib.h. Мы также немного модифицировали код, так как не тренировался на «холостом ходу», как в их демо:
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 |
/* 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. */ /* Includes ---------------------------------------------------------------- */ #include <XIAO_BLE_Sense_motion_detection_inferencing.h> #include <LSM6DS3.h> #include <U8g2lib.h> // #include <U8X8lib.h> #include <Wire.h> /* Constant defines -------------------------------------------------------- */ #define CONVERT_G_TO_MS2 9.80665f #define MAX_ACCEPTED_RANGE 2.0f // starting 03/2022, models are generated setting range to +-2, but this example use Arudino library which set range to +-4g. If you are using an older model, ignore this value and use 4.0f instead /* ** NOTE: If you run into TFLite arena allocation issue. ** ** This may be due to may dynamic memory fragmentation. ** Try defining "-DEI_CLASSIFIER_ALLOCATION_STATIC" in boards.local.txt (create ** if it doesn't exist) and copy this file to ** `<ARDUINO_CORE_INSTALL_PATH>/arduino/hardware/<mbed_core>/<core_version>/`. ** ** See ** (https://support.arduino.cc/hc/en-us/articles/360012076960-Where-are-the-installed-cores-located-) ** to find where Arduino installs cores on your machine. ** ** If the problem persists then there's not enough memory for this model and application. */ U8X8_SSD1306_64X48_ER_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); /* Private variables ------------------------------------------------------- */ static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal LSM6DS3 myIMU(I2C_MODE, 0x6A); /** * @brief Arduino setup function */ const int RED_ledPin = 11; const int BLUE_ledPin = 12; const int GREEN_ledPin = 13; void setup() { // put your setup code here, to run once: Serial.begin(115200); //u8g2.begin(); u8x8.begin(); Serial.println("Edge Impulse Inferencing Demo"); //if (!IMU.begin()) { if (!myIMU.begin()) { ei_printf("Failed to initialize IMU!\r\n"); } else { ei_printf("IMU initialized\r\n"); } if (EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME != 3) { ei_printf("ERR: EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME should be equal to 3 (the 3 sensor axes)\n"); return; } } /** * @brief Return the sign of the number * * @param number * @return int 1 if positive (or 0) -1 if negative */ float ei_get_sign(float number) { return (number >= 0.0) ? 1.0 : -1.0; } /** * @brief Get data and run inferencing * * @param[in] debug Get debug info if true */ void loop() { u8x8.clear(); u8x8.setFont(u8g2_font_ncenB08_tr); ei_printf("\nStarting inferencing in 2 seconds...\n"); delay(2000); ei_printf("Sampling...\n"); // Allocate a buffer here for the values we'll read from the IMU float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 }; for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 3) { // Determine the next tick (and then sleep later) uint64_t next_tick = micros() + (EI_CLASSIFIER_INTERVAL_MS * 1000); buffer[ix] = myIMU.readFloatAccelX(); buffer[ix+1] = myIMU.readFloatAccelY(); buffer[ix+2] = myIMU.readFloatAccelZ(); for (int i = 0; i < 3; i++) { if (fabs(buffer[ix + i]) > MAX_ACCEPTED_RANGE) { buffer[ix + i] = ei_get_sign(buffer[ix + i]) * MAX_ACCEPTED_RANGE; } } buffer[ix + 0] *= CONVERT_G_TO_MS2; buffer[ix + 1] *= CONVERT_G_TO_MS2; buffer[ix + 2] *= CONVERT_G_TO_MS2; delayMicroseconds(next_tick - micros()); } // Turn the raw buffer in a signal which we can the classify signal_t signal; int err = numpy::signal_from_buffer(buffer, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal); if (err != 0) { ei_printf("Failed to create signal from buffer (%d)\n", err); return; } // Run the classifier ei_impulse_result_t result = { 0 }; err = run_classifier(&signal, &result, debug_nn); if (err != EI_IMPULSE_OK) { ei_printf("ERR: Failed to run classifier (%d)\n", err); 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"); 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 if (result.classification[0].value > 0.5) { digitalWrite(RED_ledPin, LOW); digitalWrite(BLUE_ledPin, HIGH); // circle red digitalWrite(GREEN_ledPin, HIGH); u8x8.setFont(u8x8_font_amstrad_cpc_extended_r); u8x8.drawString(1,2,"Circle"); u8x8.refreshDisplay(); delay(2000); } else if (result.classification[1].value > 0.5) { digitalWrite(RED_ledPin, HIGH); //left&right blue digitalWrite(BLUE_ledPin, LOW); digitalWrite(GREEN_ledPin, HIGH); u8x8.setFont(u8x8_font_amstrad_cpc_extended_r); u8x8.drawString(2,3,"left"); u8x8.drawString(2,4,"right"); u8x8.refreshDisplay(); delay(2000); } else if (result.classification[2].value > 0.5) { digitalWrite(RED_ledPin, HIGH); digitalWrite(BLUE_ledPin, HIGH); digitalWrite(GREEN_ledPin, LOW); //up&down green u8x8.setFont(u8x8_font_amstrad_cpc_extended_r); u8x8.drawString(2,3,"up"); u8x8.drawString(2,4,"down"); u8x8.refreshDisplay(); delay(2000); } else { digitalWrite(RED_ledPin, LOW); digitalWrite(BLUE_ledPin, LOW); digitalWrite(GREEN_ledPin, LOW); //idle off LEDs off u8x8.setFont(u8x8_font_amstrad_cpc_extended_r); u8x8.drawString(2,3,"idle"); u8x8.refreshDisplay(); delay(2000); } } |
Нам нужно добавить библиотеку ZIP, которую мы загрузили из Edge Impulse, в библиотеку Arduino, и теперь мы можем собрать и записать код на плату. Это займет около 5 минут в первый раз и около 2 минут для последующих сборок.
Вот вывод из последовательной консоли.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Starting inferencing in 2 seconds... Sampling... Predictions (DSP: 10 ms., Classification: 0 ms., Anomaly: 0 ms.): circle clockwise: 0.59337 left and right: 0.14814 up and down: 0.25850 Starting inferencing in 2 seconds... Sampling... Predictions (DSP: 10 ms., Classification: 0 ms., Anomaly: 0 ms.): circle clockwise: 0.35315 left and right: 0.30938 up and down: 0.33747 Starting inferencing in 2 seconds... Sampling... Predictions (DSP: 10 ms., Classification: 0 ms., Anomaly: 0 ms.): circle clockwise: 0.67348 left and right: 0.09551 up and down: 0.23102 |
Таким образом, каждый раз, когда значение превышает 50, будет отображаться соответствующий текст (например, «Круг»), и если ни один из результатов не превышает 50, программа просто отображает «Idle».
Вот как это выглядит на видео.
Круг распознается (даже если мы идем не в ту сторону, как на видео ниже), но мы только несколько раз попадали «влево и вправо» и никогда не «вверх и вниз». Таким образом, должно пройти некоторое время, чтобы получить надлежащую демонстрацию, важной частью которой является сбор данных и точное разделение, чтобы убедиться, что все образцы для определенного жеста выглядят примерно одинаково.
Нам хотелось бы поблагодарить Seeed Studio за отправку плат XIAO BLE (Sense) и OLED-дисплея Grove для тестирования Edge Impulse. Нам просто хотелось бы, чтобы документация была правильной с первого раза. Если вы заинтересованы в воспроизведении приведенной выше демонстрации, плата XIAO BLE Sense продается за 15,99 долларов США, а OLED-дисплей — за 5,50 долларов США, и это необязательно, мы можем увидеть результаты в последовательном терминале.
Выражаем свою благодарность источнику из которого взята и переведена статья, сайту cnx-software.com.
Оригинал статьи вы можете прочитать здесь.