Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Detecção de palavra-chave TinyML para controle de luzes RGB

Componentes e suprimentos

Arduino Nano 33 BLE Sense
× 1

Aplicativos e serviços online

Edge Impulse Studio

Sobre este projeto





A premissa


O aprendizado de máquina no limite é extremamente útil para criar dispositivos que podem realizar tarefas "inteligentes" com muito menos programação e fluxogramas lógicos em comparação com o código tradicional. É por isso que eu queria incorporar a detecção de palavras-chave no limite, que pode reconhecer certas palavras e, em seguida, executar uma tarefa com base no que foi dito.





Hardware


Este projeto tem apenas um componente:um Arduino Nano 33 BLE Sense. A mágica real acontece no modelo de aprendizado de máquina. O Arduino Nano 33 BLE Sense está cheio de sensores, incluindo um microfone, IMU de 9 eixos, sensor ambiental e um sensor de gesto / proximidade / cor / luz ambiente (APDS-9960). O microcontrolador é um nRF52840 que funciona a 64 MHz e contém 1 MB de memória flash e 256 KB de RAM. Este projeto também usa seu LED RGB integrado para exibir a cor atual.






Configurando o Edge Impulse


Comecei criando um novo projeto no Edge Impulse e depois instalei a ferramenta Edge Impulse CLI. Para obter mais instruções sobre como fazer isso, visite a página de instruções de instalação. Isso permite que o Arduino Nano se comunique com o serviço de nuvem para receber comandos e enviar dados do sensor automaticamente. Eu baixei o firmware mais recente do Edge Impulse e o atualizei para a placa clicando duas vezes no botão reset para fazê-lo entrar no modo bootloader. Então executei flash_windows.bat para transferi-lo.

No prompt de comando, executei o edge-Impulse-daemon e seguiu o assistente para configurá-lo. Agora o Nano aparece na lista de dispositivos do projeto, o que permite que as amostras sejam coletadas e carregadas como parte do conjunto de dados de treinamento / teste.






Coletando Amostras


Treinar um modelo de aprendizado de máquina requer dados, e muitos deles. Eu queria ter os seguintes modos para a faixa de LED RGB:
  • Ligado
  • Desligado
  • vermelho
  • Verde
  • azul

Eu obtive cerca de 1 minuto de som para cada modo em que disse repetidamente a palavra em intervalos de 1 a 2 segundos e dividi-os.



Mas apenas ter essas amostras não é suficiente, já que o ruído de fundo e outras palavras darão uma leitura falsa. Felizmente, o Edge Impulse já fornece um conjunto de dados pré-construído para ruído e palavras 'desconhecidas', então usei a ferramenta "Upload de dados existentes" para fazer upload desses arquivos de áudio nos dados de treinamento.

Por fim, rebalanceei o conjunto de dados para ter a divisão 80-20 recomendada para dados de treinamento e teste, respectivamente.






Treinando o modelo


Agora, munido de uma hora de dados de treinamento e muitos rótulos, era hora de treinar um modelo. O impulso que projetei leva o áudio como dados de série temporal com um tamanho de janela de 1 segundo e um aumento de janela de 500 ms. Em seguida, ele passa por um bloco MFCC em um bloco de rede neural Keras.



O bloco MFCC permite configurar como o áudio será processado, junto com um espectograma que mostra as frequências de forma visual.



Deixei as configurações da rede neural como padrão, mas também fiz algumas modificações. Primeiro, alterei o limite mínimo de confiança de 0,80 para 0,70 e adicionei um pouco de aumento de dados na forma de ruído adicional e faixas de tempo de mascaramento. Isso ajuda o NN a evitar um ajuste excessivo do modelo, pois ele possui dados mais diversos para trabalhar.






Implantando no Arduino Nano 33 BLE Sense


O Arduino Nano 33 BLE Sense atua como um microfone sempre ligado que faz a amostragem contínua do áudio e detecta se uma das palavras-chave foi falada. Assim que uma for encontrada, a palavra-chave é convertida em um índice que é usado para decodificar a cor desejada. Para a palavra-chave on ou off, o LED é definido como preto ou cinza claro.

Eu baixei o modelo como uma biblioteca e o adicionei ao IDE do Arduino, então compilei e atualizei o código para o Nano.


Código

  • RGBLEDKeywordDetection
RGBLEDKeywordDetection C / C ++
 / * Exemplos do Edge Impulse Arduino * Copyright (c) 2020 EdgeImpulse Inc. * * A permissão é concedida, gratuitamente, a qualquer pessoa que obtenha uma cópia * deste software e arquivos de documentação associados (o "Software"), negociar * no Software sem restrições, incluindo, sem limitação, os direitos * de usar, copiar, modificar, mesclar, publicar, distribuir, sublicenciar e / ou vender * cópias do Software e permitir que pessoas a quem o Software seja * fornecido para tanto, sujeito às seguintes condições:* * O aviso de copyright acima e este aviso de permissão devem ser incluídos em * todas as cópias ou partes substanciais do Software. * * O SOFTWARE É FORNECIDO "COMO ESTÁ", SEM QUALQUER TIPO DE GARANTIA, EXPRESSA OU * IMPLÍCITA, INCLUINDO, MAS NÃO SE LIMITANDO ÀS GARANTIAS DE COMERCIALIZAÇÃO, * ADEQUAÇÃO A UM DETERMINADO FIM E NÃO VIOLAÇÃO. EM NENHUMA HIPÓTESE OS * AUTORES OU TITULARES DOS DIREITOS AUTORAIS SERÃO RESPONSÁVEIS POR QUALQUER RECLAMAÇÃO, DANOS OU OUTRA * RESPONSABILIDADE, SEJA EM UMA AÇÃO DE CONTRATO, DELITO OU DE OUTRA FORMA, DECORRENTE DE, * FORA DE OU EM CONEXÃO COM O SOFTWARE OU USO OU OUTRO NEGOCIAÇÕES NO * SOFTWARE. * /// Se o seu alvo é limitado na memória, remova esta macro para economizar 10K RAM # define EIDSP_QUANTIZE_FILTERBANK 0 / ** * Defina o número de fatias por janela do modelo. Por exemplo. uma janela de modelo de 1000 ms * com fatias por janela de modelo definida como 4. Resulta em um tamanho de fatia de 250 ms. * Para mais informações:https://docs.edgeimpulse.com/docs/continuous-audio-sampling * / # define EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW 3 / * Inclui ------------------- --------------------------------------------- * / # inclui  #include  #include 
 #define CONFIDENCE_THRESHOLD 0.7static const uint32_t colors [] ={0x00ff0000, 0x0000ff00, 0x000000ff}; // vermelho, verde, azulenum Actions {LED_ON =1, LED_OFF =0, LED_RED =2, LED_GREEN =3, LED_BLUE =4, NONE =5}; funções enum {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  actionToFunctionMap ={{LED_ON, CHANGE_LED_ON_OFF}, {LED_OFF, CHANGE_LED_ON_OFF}, {LED_RED, CHANGE_LED_COLOR}, {LED_GREENCOLANGED_ }, {LED_BLUE, CHANGE_LED_COLOR}, {NONE, CHANGE_LED_NONE}}; const std ::map  labelToActionMap ={{"on", LED_ON}, {"off", LED_OFF}, {"red" , LED_RED}, {"green", LED_GREEN}, {"blue", LED_BLUE}, {"unknown", NONE}, {"noise", NONE}}; / ** Audio buffers, ponteiros e seletores * / estrutura de typedef {buffers curtos assinados * [2]; unsigned char buf_select; unsigned char buf_ready; unsigned int buf_count; sem sinal int n_samples;} inference_t; inference_t inference; bool estático record_ready =false; short assinado estático * sampleBuffer; bool estático debug_nn =false; // Defina como verdadeiro para ver, por exemplo recursos gerados a partir do signalstatic bruto int print_results =- (EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW); / ** * @brief Arduino setup function * / void setup () {// coloque seu código de configuração aqui, para executar uma vez:Serial.begin (115200); Serial.println ("Edge Impulse Inferencing"); setPixelColor (0xaeae00); // resumo das configurações de inferência (de model_metadata.h) ei_printf ("Configurações de inferência:\ n"); ei_printf ("\ tInterval:% .2f ms. \ n", (float) EI_CLASSIFIER_INTERVAL_MS); ei_printf ("\ ttamanho do frame:% d \ n", EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE); ei_printf ("\ tComprimento da amostra:% d ms. \ n", EI_CLASSIFIER_RAW_SAMPLE_COUNT / 16); ei_printf ("\ tNão. de 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:Falha ao configurar amostragem de áudio \ r \ n"); Retorna; }} / ** * @brief função principal do Arduino. Executa o loop de inferência. * / void loop () {bool m =microphone_inference_record (); if (! m) {ei_printf ("ERR:Falha ao gravar áudio ... \ n"); Retorna; } sinal_tinal; signal.total_length =EI_CLASSIFIER_SLICE_SIZE; signal.get_data =µphone_audio_signal_get_data; resultado ei_impulse_result_t ={0}; EI_IMPULSE_ERROR r =run_classifier_continuous (&signal, &result, debug_nn); if (r! =EI_IMPULSE_OK) {ei_printf ("ERR:Falha ao executar o classificador (% d) \ n", r); Retorna; } if (++ print_results> =(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW)) {// imprimir as previsões ei_printf ("Predictions"); ei_printf ("(DSP:% d ms., Classificação:% d ms., Anomalia:% d ms.)", result.timing.dsp, result.timing.classification, result.timing.anomaly); ei_printf (":\ n"); para (size_t ix =0; ix  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 ("pontuação de anomalia:% .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 * classificaçãoLabel) {auto itr =labelToActionMap.find (classificaçãoLabel); if (itr ==labelToActionMap.end ()) return; auto itr2 =actionToFunctionMap.find (itr-> segundo); if (itr2 ==actionToFunctionMap.end ()) return; switch (itr2-> segundo) {case CHANGE_LED_ON_OFF:setPixelColor ((itr-> segundo)? 0x5c5c5c:0x00); pausa; case CHANGE_LED_COLOR:{uint32_t pixelColor =cores [itr-> segundo - 2]; setPixelColor (pixelColor); } pausa; case CHANGE_LED_NONE:break; }} / ** * @brief Printf função usa vsnprintf e saída usando 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, formato); int r =vsnprintf (print_buf, sizeof (print_buf), formato, args); va_end (args); if (r> 0) {Serial.write (print_buf); }} / ** * @brief PDM buffer full callback * Obtenha dados e chame o callback de thread de áudio * / static void pdm_data_ready_inference_callback (void) {int bytesAvailable =PDM.available (); // lê no buffer de amostra int bytesRead =PDM.read ((char *) &sampleBuffer [0], bytesAvailable); if (record_ready ==true) {for (int i =0; i > 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; }}}} / ** * @brief Init inferencing struct e setup / start PDM * * @param [in] n_samples As n amostras * * @return {description_of_the_return_value} * / static bool microphone_inference_start (uint32_t n_samples) {inference.buffers [ 0] =(curto com sinal *) malloc (n_samples * sizeof (curto com sinal)); if (inference.buffers [0] ==NULL) {return false; } inference.buffers [1] =(short assinado *) malloc (n_samples * sizeof (short assinado)); if (inference.buffers [0] ==NULL) {free (inference.buffers [0]); retorna falso; } sampleBuffer =(short assinado *) malloc ((n_samples>> 1) * sizeof (short assinado)); if (sampleBuffer ==NULL) {free (inference.buffers [0]); livre (inferência.buffers [1]); retorna falso; } inference.buf_select =0; inference.buf_count =0; inference.n_samples =n_samples; inference.buf_ready =0; // configurar o retorno de chamada de recebimento de dados PDM.onReceive (&pdm_data_ready_inference_callback); // defina opcionalmente o ganho, o padrão é 20 PDM.setGain (80); PDM.setBufferSize ((n_samples>> 1) * sizeof (int16_t)); // inicializar o PDM com:// - um canal (modo mono) // - uma taxa de amostragem de 16 kHz if (! PDM.begin (1, EI_CLASSIFIER_FREQUENCY)) {ei_printf ("Falha ao iniciar o PDM!"); } record_ready =true; return true;} / ** * @brief Aguardar novos dados * * @return True quando terminar * / static bool microphone_inference_record (void) {bool ret =true; if (inference.buf_ready ==1) {ei_printf ("Erro na saturação do buffer de amostra. Diminua o número de fatias por janela do modelo" "(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW) \ n"); ret =falso; } enquanto (inference.buf_ready ==0) {atraso (1); } inference.buf_ready =0; return ret;} / ** * Obter dados brutos de sinal de áudio * / 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 , comprimento); return 0;} / ** * @brief Parar PDM e liberar buffers * / static void microphone_inference_end (void) {PDM.end (); livre (inferência.buffers [0]); livre (inferência.buffers [1]); free (sampleBuffer);} # if! defined (EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR! =EI_CLASSIFIER_SENSOR_MICROPHONE # erro "Modelo inválido para sensor atual." # Endif 

Processo de manufatura

  1. Dicas de segurança IIoT e tendências para 2020
  2. Computação em nuvem e borda para IoT:um breve histórico
  3. Por que a computação de borda para IoT?
  4. Kontrons KBox A-150-WKL para aplicativos IoT Edge com uso intensivo de dados
  5. Benefícios do Edge Computing para cristalização de IA
  6. Quatro etapas para garantir o sucesso em Edge Computing
  7. Microsoft lança Azure Edge Zones para aplicativos 5G
  8. A necessidade de código aberto na borda (eBook)
  9. Sistema de detecção química para baixos níveis de concentração
  10. 3 razões para a manutenção de dispositivos de detecção de gás