Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

RGB-verlichting besturen met TinyML trefwoordenherkenning

Onderdelen en benodigdheden

Arduino Nano 33 BLE Sense 1

Apps en online diensten

  • Edge Impulse Studio

Over dit project

De uitgangspunten

Machine learning aan de rand (edge) is extreem nuttig voor het creëren van apparaten die "intelligente" taken kunnen uitvoeren met veel minder programmeren en logische flowchart dan traditionele code. Daarom wilde ik trefwoordenherkenning aan de rand integreren die bepaalde woorden kan herkennen en vervolgens een taak uitvoert op basis van wat er gezegd wordt.

Hardware

Dit project heeft maar één component: een Arduino Nano 33 BLE Sense. De echte magie zit in het machine learning-model. De Arduino Nano 33 BLE Sense zit vol met sensoren, waaronder een microfoon, 9-as IMU, omgevingsensor en een gestuur/proximiteit/kleur/omgevingslichtsensor (APDS-9960). De microcontroller is een nRF52840 die op 64MHz draait en 1MB flash-geheugen en 256KB RAM bevat. Dit project gebruikt ook de onboard RGB-LED om de huidige kleur weer te geven.

RGB-verlichting besturen met TinyML trefwoordenherkenning

Edge Impulse instellen

Ik begon met het aanmaken van een nieuw project op Edge Impulse en installeerde de Edge Impulse CLI-tool. Voor meer instructies, bezoek de installatie-instructiepagina. Hierdoor kan de Arduino Nano communiceren met de cloud-service om opdrachten te ontvangen en sensordata automatisch te verzenden. Ik downloadde de meest recente Edge Impulse firmware en flashtte deze naar het bord door dubbel op de reset-knop te drukken om de bootloader-modus te activeren. Vervolgens voerde ik flash_windows.bat uit om het te flashen.

RGB-verlichting besturen met TinyML trefwoordenherkenning

In de opdrachtprompt draaide ik edge-impulse-daemon en volgde de wizard om het in te stellen. Nu verschijnt de Nano in de apparaatlijst van het project, waarmee samples genomen en geüpload kunnen worden als onderdeel van de training/testdataset.

RGB-verlichting besturen met TinyML trefwoordenherkenning

Samples verzamelen

Het trainen van een machine learning-model vereist data, en best veel. Ik wilde de volgende modi voor de RGB-LED-strip:

  • Aan
  • Uit
  • Rood
  • Groen
  • Blauw

Ik verzamelde ongeveer 1 minuut geluid per modus waarbij ik het woord herhaaldelijk zei met 1-2 seconden intervallen en splitste ze.

RGB-verlichting besturen met TinyML trefwoordenherkenning
RGB-verlichting besturen met TinyML trefwoordenherkenning

Maar alleen deze samples is niet genoeg, omdat achtergrondruis en andere woorden een valse reading geven. Gelukkig biedt Edge Impulse al een kant-en-klaar dataset voor ruis en 'onbekende' woorden, dus ik gebruikte hun "Upload Existing Data"-tool om deze audiobestanden in de trainingdata te uploaden.

RGB-verlichting besturen met TinyML trefwoordenherkenning

Ten slotte herbalanceerde ik de dataset naar de aanbevolen 80-20 splitsing voor training- en testdata.

RGB-verlichting besturen met TinyML trefwoordenherkenning

Het model trainen

Nu, gewapend met een uur trainingdata en voldoende labels, was het tijd om een model te trainen. De impulse die ik ontwierp neemt audio als time-series data met een venstergrootte van 1 seconde en een vensterverhoging van 500ms. Vervolgens gaat het door een MFCC-blok naar een Keras neural network-blok.

RGB-verlichting besturen met TinyML trefwoordenherkenning

Het MFCC-blok stelt je in staat om te configureren hoe het audio verwerkt wordt, samen met een spectrogram dat de frequenties visueel toont.

RGB-verlichting besturen met TinyML trefwoordenherkenning

Ik liet de neurale netwerkinstellingen grotendeels standaard, maar maakte een paar aanpassingen. Allereerst veranderde ik de minimale betrouwbaarheidsdrempel van 0,80 naar 0,70, en voegde wat data-augmentatie toe in de vorm van extra ruis en tijdmaskering. Dit helpt het neurale netwerk om overfitting te voorkomen, omdat het meer diverse data heeft om mee te werken.

RGB-verlichting besturen met TinyML trefwoordenherkenning

Deployen op de Arduino Nano 33 BLE Sense

De Arduino Nano 33 BLE Sense fungeert als een always-on microfoon die continu audio bemonstert en detecteert of één van de trefwoorden is gezegd. Zodra er een trefwoord gevonden wordt, wordt dit omgezet in een index die gebruikt wordt om de gewenste kleur te decoderen. Voor het trefwoord "aan" of "uit" wordt de LED ingesteld op respectievelijk zwart of lichtgrijs.

Ik downloadde het model als een library en voegde het toe aan de Arduino IDE, compileerde en flashtte de code naar de Nano.

RGB-verlichting besturen met TinyML trefwoordenherkenning

Code

RGBLEDKeywordDetection (C/C++)

/* Edge Impulse Arduino examples
 * Copyright (c) 2020 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.
 */

#define EIDSP_QUANTIZE_FILTERBANK 0
#define EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW 3

#include <PDM.h>
#include <keyword_detection_inference.h>
#include <map>

#define CONFIDENCE_THRESHOLD 0.7

static const uint32_t colors[] = {0x00ff0000, 0x0000ff00, 0x000000ff}; // red, green, blue
enum Actions { LED_ON = 1, LED_OFF = 0, LED_RED = 2, LED_GREEN = 3, LED_BLUE = 4, NONE = 5 };
enum Functions { CHANGE_LED_ON_OFF = 0, CHANGE_LED_COLOR = 1, CHANGE_LED_NONE = 2 };

static const uint8_t redPin = 22, greenPin = 23, bluePin = 24;

const std::map<Actions, Functions> actionToFunctionMap = {
  {LED_ON, CHANGE_LED_ON_OFF}, {LED_OFF, CHANGE_LED_ON_OFF},
  {LED_RED, CHANGE_LED_COLOR}, {LED_GREEN, CHANGE_LED_COLOR},
  {LED_BLUE, CHANGE_LED_COLOR}, {NONE, CHANGE_LED_NONE}
};

const std::map<const char*, Actions> labelToActionMap = {
  {"on", LED_ON}, {"off", LED_OFF}, {"red", LED_RED},
  {"green", LED_GREEN}, {"blue", LED_BLUE}, {"unknown", NONE}, {"noise", NONE}
};

typedef struct {
  signed short *buffers[2];
  unsigned char buf_select;
  unsigned char buf_ready;
  unsigned int buf_count;
  unsigned int n_samples;
} inference_t;

static inference_t inference;
static bool record_ready = false;
static signed short *sampleBuffer;
static bool debug_nn = false;
static int print_results = -(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW);

void setup() {
  Serial.begin(115200);
  Serial.println("Edge Impulse Inferencing");
  setPixelColor(0xaeae00);
  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]));
  run_classifier_init();
  if (microphone_inference_start(EI_CLASSIFIER_SLICE_SIZE) == false) {
    ei_printf("ERR: Failed to setup audio sampling\r\n");
    return;
  }
}

void loop() {
  bool m = microphone_inference_record();
  if (!m) { ei_printf("ERR: Failed to record audio...\n"); return; }
  signal_t signal;
  signal.total_length = EI_CLASSIFIER_SLICE_SIZE;
  signal.get_data = µphone_audio_signal_get_data;
  ei_impulse_result_t result = {0};
  EI_IMPULSE_ERROR r = run_classifier_continuous(&signal, &result, debug_nn);
  if (r != EI_IMPULSE_OK) {
    ei_printf("ERR: Failed to run classifier (%d)\n", r); return;
  }
  if (++print_results >= (EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW)) {
    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++) {
      if(result.classification[ix].value > CONFIDENCE_THRESHOLD) {
        performAction(result.classification[ix].label);
      }
      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
    print_results = 0;
  }
}

void setPixelColor(uint32_t c) {
  analogWrite(redPin, 255 - (c >> 16));
  analogWrite(greenPin, 255 - ((c >> 8) & 0xff));
  analogWrite(bluePin, 255 - (c & 0xff));
}

void performAction(const char* classificationLabel) {
  auto itr = labelToActionMap.find(classificationLabel);
  if(itr == labelToActionMap.end()) return;
  auto itr2 = actionToFunctionMap.find(itr->second);
  if(itr2 == actionToFunctionMap.end()) return;
  switch(itr2->second) {
    case CHANGE_LED_ON_OFF:
      setPixelColor((itr->second) ? 0x5c5c5c : 0x00);
      break;
    case CHANGE_LED_COLOR: {
      uint32_t pixelColor = colors[itr->second - 2];
      setPixelColor(pixelColor);
    } break;
    case CHANGE_LED_NONE: break;
  }
}

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();
  int bytesRead = PDM.read((char *)&sampleBuffer[0], bytesAvailable);
  if (record_ready == true) {
    for (int i = 0; i < bytesRead >> 1; i++) {
      inference.buffers[inference.buf_select][inference.buf_count++] = sampleBuffer[i];
      if (inference.buf_count >= inference.n_samples) {
        inference.buf_select ^= 1;
        inference.buf_count = 0;
        inference.buf_ready = 1;
      }
    }
  }
}

static bool microphone_inference_start(uint32_t n_samples) {
  inference.buffers[0] = (signed short *)malloc(n_samples * sizeof(signed short));
  if (inference.buffers[0] == NULL) return false;
  inference.buffers[1] = (signed short *)malloc(n_samples * sizeof(signed short));
  if (inference.buffers[0] == NULL) { free(inference.buffers[0]); return false; }
  sampleBuffer = (signed short *)malloc((n_samples >> 1) * sizeof(signed short));
  if (sampleBuffer == NULL) { free(inference.buffers[0]); free(inference.buffers[1]); return false; }
  inference.buf_select = 0; inference.buf_count = 0; inference.n_samples = n_samples; inference.buf_ready = 0;
  PDM.onReceive(&pdm_data_ready_inference_callback);
  PDM.setGain(80);
  PDM.setBufferSize((n_samples >> 1) * sizeof(int16_t));
  if (!PDM.begin(1, EI_CLASSIFIER_FREQUENCY)) { ei_printf("Failed to start PDM!"); }
  record_ready = true;
  return true;
}

static bool microphone_inference_record(void) {
  bool ret = true;
  if (inference.buf_ready == 1) {
    ei_printf("Error sample buffer overrun. Decrease the number of slices per model window (EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW)\n");
    ret = false;
  }
  while (inference.buf_ready == 0) { delay(1); }
  inference.buf_ready = 0;
  return ret;
}

static int microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr) {
  numpy::int16_to_float(&inference.buffers[inference.buf_select ^ 1][offset], out_ptr, length);
  return 0;
}

static void microphone_inference_end(void) {
  PDM.end();
  free(inference.buffers[0]);
  free(inference.buffers[1]);
  free(sampleBuffer);
}

#if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_MICROPHONE
#error "Invalid model for current sensor."
#endif

Productieproces

  1. Vier bewijzen stappen voor edge computing-succes in slimme fabrieken
  2. Waarom je gasdetectieapparaten in topconditie moet houden: 3 essentiële redenen
  3. Arm introduceert Cortex-M55 & Ethos-U55: Nieuwe IP-kernen voor TinyML op laagstroom-endpoints
  4. Ultrgevoelig chemisch wapendetectiesysteem voor laag-niveau dreigingen
  5. Industriële IoT Beveiliging: Belangrijkste Trends en Best Practices voor 2020
  6. Edge & Cloud Computing in IoT: Een Concis Evolutionair Overzicht
  7. Waarom Edge Computing Essentieel is voor IoT-succes
  8. Kontron KBox A‑150‑WKL: hoog‑prestatie, laag‑vermogen IoT Edge PC voor industriële automatisering
  9. Edge Computing: AI-implementatie versnellen aan de netwerkrand
  10. Microsoft Lanceert Azure Edge Zones voor 5G-ingeschakelde IoT & AI-workloads
  11. Waarom Open Source Innovatie Aan De Edge Drijft – Essentiële eBook