Machine Learning na Franzininho WiFi:  Pulseira para crises de epilepsia  – Parte 2

Este post faz parte da série Machine Learning na Franzininho WiFi

Introdução

Na primeira parte desta prova de conceito de uma pulseira capaz de detectar crises convulsivas epilépticas, coletamos os dados e treinamos o modelo de machine learning. Nesta segunda etapa, vamos implementar esse modelo, utilizando-o para identificar crises e, em seguida, enviar uma mensagem de emergência via WhatsApp para um contato de emergência. Isso é interessante, pois crises epilépticas podem ser perigosas, frequentemente resultando em quedas ou outros acidentes que colocam a saúde do paciente em risco.

Material necessário

Utilize os mesmos componentes e circuito que foi mostrado na Parte 1. 

Versão 1: Envio de Mensagem Serial ao Detectar Crise Epiléptica 

  1. Na Etapa 1, realizamos o deployment do modelo e baixamos um pacote .zip. Para importar esse pacote na sua IDE do Arduino, siga os passos: vá até Sketch > Include Library > Add .ZIP Library e selecione o arquivo .zip correspondente ao modelo.
  1. Inclua o nome da sua biblioteca em  seu código. Por exemplo:
#include <epilepsia_inferencing.h>
  1. Inclua também as seguintes bibliotecas necessárias e defina os parâmetros:
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>


static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal


#define LIS3DH_CLK 36
#define LIS3DH_MISO 37
#define LIS3DH_MOSI 35
#define LIS3DH_CS 34


// software SPI
Adafruit_LIS3DH lis = Adafruit_LIS3DH(LIS3DH_CS, LIS3DH_MOSI, LIS3DH_MISO, LIS3DH_CLK);
  1. No bloco void setup(), inicie a comunicação serial, verifique se o acelerômetro foi inicializado e configure suas definições iniciais, incluindo o alcance, modo e taxa de amostragem. Além disso, confirme se o número de dados de classificação é igual a 6.
void setup(void) {
  Serial.begin(115200);


  if (!lis.begin(0x18)) {  
    Serial.println("Couldn't start");
    while (1) yield();
  }
  lis.setRange(LIS3DH_RANGE_2_G);   // 2, 4, 8 or 16 G
  lis.setPerformanceMode(LIS3DH_MODE_NORMAL); //LIS3DH_MODE_LOW_POWER, LIS3DH_MODE_NORMAL, LIS3DH_MODE_HIGH_RESOLUTION
  lis.setDataRate(LIS3DH_DATARATE_50_HZ); //1Hz (1 leitura por segundo), 10Hz, 25Hz, 50Hz, 100Hz, 200Hz, 400Hz, POWERDOWN, LOWPOWER_5KHZ, LOWPOWER_1K6HZ
 
  if (EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME != 6) {
    ei_printf("ERR: EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME should be equal to 6 (the 6 sensor axes)\n");
    return;
  }


}
  1. No bloco void loop(), implemente a coleta das leituras do acelerômetro, armazenando os valores em um buffer. Para cada amostra, capture as acelerações nos eixos X, Y e Z, e calcule os ângulos de rotação (roll, pitch e yaw). As leituras, juntamente com os ângulos calculados, devem ser armazenadas no buffer para posterior análise. Em seguida, converta o buffer em um sinal que será utilizado pelo classificador. Se a conversão for bem-sucedida, execute o classificador e aguarde os resultados. Imprima no console os resultados da classificação e baseando-se nas previsões do classificador, verifique se a probabilidade de não haver epilepsia é superior a 0,8. Se essa condição for satisfeita, exiba “Sem Epilepsia” no console. Caso contrário, se a probabilidade de epilepsia for superior a 0,8, exiba “Epilepsia identificada”. Se nenhuma condição for satisfeita exiba “Nao eh possivel determinar”.
void loop() {


  ei_printf("\nClassificacao inicia em 10 segundos...\n");
  delay(10000);


  ei_printf("Sampling...\n");


  // buffer para leitura acelerometro
  float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 };
  for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 6) {
    // proximo next tick
    uint64_t next_tick = micros() + (EI_CLASSIFIER_INTERVAL_MS * 1000);
    // captura accel X, Y e Z em m/s^2
    sensors_event_t event;
    lis.getEvent(&event);
    // Calcular o ângulo em relação aos eixos X (roll), Y (pitch) e Z (yaw)
    float roll = (atan2(event.acceleration.y, event.acceleration.z) * 180 / M_PI)*0.1;
    float pitch = (atan2(-event.acceleration.x, sqrt(event.acceleration.y * event.acceleration.y + event.acceleration.z * event.acceleration.z)) * 180 / M_PI)*0.1;
    // Aproximação do ângulo yaw (rotação em torno do eixo Z) usando atan2 com X e Y
    float yaw = (atan2(event.acceleration.y, event.acceleration.x) * 180 / M_PI)*0.1;
    // coloca leituras no buffer
    buffer[ix + 0] = roll;
    buffer[ix + 1] = pitch;
    buffer[ix + 2] = yaw;
    buffer[ix + 3] = event.acceleration.x;
    buffer[ix + 4] = event.acceleration.y;
    buffer[ix + 5] = event.acceleration.z;
    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;
  }


  // roda classificador
  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;
  }


  // predicoes
  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);
  }


  // Controle dos LEDs com base nas predições
  if (result.classification[0].value > 0.8) {  // epilepsia
     Serial.println("Sem Epilepsia");
  } else if (result.classification[1].value > 0.8) {  // sem epilepsia"
     Serial.println("Epilepsia identificada");
  } else {
    // Se nenhuma predição for suficientemente
    Serial.println("Nao eh possivel determinar");
  }
}
  1. O resultado será:

Versão 2: Integração com API para Envio de Mensagem pelo WhatsApp 

Nosso objetivo vai além de indicar a ocorrência de uma crise epiléptica via comunicação serial. Queremos também enviar uma mensagem de alerta pelo WhatsApp para um contato de emergência. Para isso, utilizaremos a API da CallMeBot (https://www.callmebot.com).

  1. Acesse a API da CallMeBot (https://www.callmebot.com) para ler a documentação.
  • Adicione o número +34 611 04 87 48 da API nos contatos de quem será o seu contato de emergência
  • Envie a seguinte mensagem pelo whatsapp para o número da callmebot: I allow callmebot to send me messages
  • Aguarde receber api key
  1. Instale biblioteca UrlEncode
  1. Inclua as seguintes bibliotecas: 
#include <WiFi.h>
#include <HTTPClient.h>
#include <UrlEncode.h>
  1. Crise as constantes:
const char* ssid = "SUA REDE WIFI";
const char* password = "SENHA DO SEU WIFI";
String phoneNumber = "+55SEUNUMEROEMERGIAAQUI";
String apiKey = "SUA APIKEY";
  1. Crie a função que será responsável por enviar a mensagem no whatsapp seguindo a url indicada pela API:
void sendMessage(String message) {
  // Data to send with HTTP POST
  String url = "https://api.callmebot.com/whatsapp.php?phone=" + phoneNumber + "&apikey=" + apiKey + "&text=" + urlEncode(message);
  HTTPClient http;
  http.begin(url);
  // Specify content-type header
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  // Send HTTP POST request
  int httpResponseCode = http.POST(url);
  if (httpResponseCode == 200) {
    Serial.print("Mensagem enviada com sucesso");
  } else {
    Serial.println("Erro no envio da mensagem");
    Serial.print("HTTP response code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();
}
  1. Modifique o seu bloco void setup() para que além das configuraçoes feitasd na versão 1 também conete na rede wifi:
void setup(void) {
  Serial.begin(115200);


  // configuracao acelerometro
  if (!lis.begin(0x18)) {  
    Serial.println("Couldn't start");
    while (1) yield();
  }
  lis.setRange(LIS3DH_RANGE_2_G);   // 2, 4, 8 or 16 G
  lis.setPerformanceMode(LIS3DH_MODE_NORMAL); //LIS3DH_MODE_LOW_POWER, LIS3DH_MODE_NORMAL, LIS3DH_MODE_HIGH_RESOLUTION
  lis.setDataRate(LIS3DH_DATARATE_50_HZ); //1Hz (1 leitura por segundo), 10Hz, 25Hz, 50Hz, 100Hz, 200Hz, 400Hz, POWERDOWN, LOWPOWER_5KHZ, LOWPOWER_1K6HZ
 
  // verificacao numero samples
  if (EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME != 6) {
    ei_printf("ERR: EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME should be equal to 6 (the 6 sensor axes)\n");
    return;
  }


  // configuracao conexao wifi
  WiFi.begin(ssid, password);
  Serial.println("Conectando");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Conectado ao WiFi neste IP ");
  Serial.println(WiFi.localIP());

}

Nessa versão, o setup é conectado na rede wifi. 

  1. Modifique o seu bloco void setup() para que, além das configurações realizadas na versão 1, também inclua a conexão à rede Wi-Fi:
void loop() {


  ei_printf("\nClassificacao inicia em 10 segundos...\n");
  delay(60000);


  ei_printf("Sampling...\n");


  // buffer para leitura acelerometro
  float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 };
  for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 6) {
    // proximo next tick
    uint64_t next_tick = micros() + (EI_CLASSIFIER_INTERVAL_MS * 1000);
    // captura accel X, Y e Z em m/s^2
    sensors_event_t event;
    lis.getEvent(&event);
    // Calcular o ângulo em relação aos eixos X (roll), Y (pitch) e Z (yaw)
    float roll = (atan2(event.acceleration.y, event.acceleration.z) * 180 / M_PI)*0.1;
    float pitch = (atan2(-event.acceleration.x, sqrt(event.acceleration.y * event.acceleration.y + event.acceleration.z * event.acceleration.z)) * 180 / M_PI)*0.1;
    // Aproximação do ângulo yaw (rotação em torno do eixo Z) usando atan2 com X e Y
    float yaw = (atan2(event.acceleration.y, event.acceleration.x) * 180 / M_PI)*0.1;
    // coloca leituras no buffer
    buffer[ix + 0] = roll;
    buffer[ix + 1] = pitch;
    buffer[ix + 2] = yaw;
    buffer[ix + 3] = event.acceleration.x;
    buffer[ix + 4] = event.acceleration.y;
    buffer[ix + 5] = event.acceleration.z;
    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;
  }


  // roda classificador
  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;
  }


  // predicoes
  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);
  }


  // Controle dos LEDs com base nas predições
  if (result.classification[0].value > 0.8) {  // sem epilepsia
     Serial.println("Sem Epilepsia");
  } else if (result.classification[1].value > 0.8) {  // com epilepsia"
     Serial.println("Epilepsia identificada");
     sendMessage("Olá, você está no contato de emergência de Graziele.Ela está passando por uma crise epiléptica e precisa de ajuda urgente. Por favor, procure-a imediatamente!");
  } else {
    // Se nenhuma predição for suficientemente
    Serial.println("Nao eh possivel determinar");
  }


}

Nesta versão, ao detectar uma crise epiléptica, a função é acionada e a mensagem destinada ao contato de emergência é encaminhada. O código para enviar a mensagem é o seguinte: sendMessage(“Olá, você está no contato de emergência de Graziele. Ela está passando por uma crise epiléptica e precisa de ajuda urgente. Por favor, procure-a imediatamente!”);

  1. Funcionamento

Ao simular uma crise epiléptica com o braço (vídeo simulação), o evento é detectado e uma mensagem é enviada ao meu contato de emergência.

  1. Código completo:
void loop() {


  ei_printf("\nClassificacao inicia em 10 segundos...\n");
  delay(60000);


  ei_printf("Sampling...\n");


  // buffer para leitura acelerometro
  float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 };
  for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 6) {
    // proximo next tick
    uint64_t next_tick = micros() + (EI_CLASSIFIER_INTERVAL_MS * 1000);
    // captura accel X, Y e Z em m/s^2
    sensors_event_t event;
    lis.getEvent(&event);
    // Calcular o ângulo em relação aos eixos X (roll), Y (pitch) e Z (yaw)
    float roll = (atan2(event.acceleration.y, event.acceleration.z) * 180 / M_PI)*0.1;
    float pitch = (atan2(-event.acceleration.x, sqrt(event.acceleration.y * event.acceleration.y + event.acceleration.z * event.acceleration.z)) * 180 / M_PI)*0.1;
    // Aproximação do ângulo yaw (rotação em torno do eixo Z) usando atan2 com X e Y
    float yaw = (atan2(event.acceleration.y, event.acceleration.x) * 180 / M_PI)*0.1;
    // coloca leituras no buffer
    buffer[ix + 0] = roll;
    buffer[ix + 1] = pitch;
    buffer[ix + 2] = yaw;
    buffer[ix + 3] = event.acceleration.x;
    buffer[ix + 4] = event.acceleration.y;
    buffer[ix + 5] = event.acceleration.z;
    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;
  }


  // roda classificador
  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;
  }


  // predicoes
  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);
  }


  // Controle dos LEDs com base nas predições
  if (result.classification[0].value > 0.8) {  // sem epilepsia
     Serial.println("Sem Epilepsia");
  } else if (result.classification[1].value > 0.8) {  // com epilepsia"
     Serial.println("Epilepsia identificada");
     sendMessage("Olá, você está no contato de emergência de Graziele.Ela está passando por uma crise epiléptica e precisa de ajuda urgente. Por favor, procure-a imediatamente!");
  } else {
    // Se nenhuma predição for suficientemente
    Serial.println("Nao eh possivel determinar");
  }


}

https://github.com/Graziele-Rodrigues/TinyML_FranzininhoWifi/tree/main/epilepsia

Conclusão

Este projeto serviu como uma prova de conceito, demonstrando a viabilidade de criar uma pulseira capaz de identificar crises epilépticas. Os dados coletados foram simulados, mas, em uma aplicação real, seriam obtidos em diversas situações com pessoas que vivenciam essas crises. Quanto mais variados forem os dados, melhor será o modelo na identificação de uma crise epiléptica.

Além disso, este projeto abre oportunidades para diversas melhorias. No exemplo apresentado, foi enviada uma mensagem simples pelo WhatsApp a um contato fixo no código. Contudo, há potencial para desenvolver um aplicativo que permita a mudança desse contato e que salve o histórico de crise. Seria interessante, também, enviar a localização junto com a mensagem, para que o contato de emergência saiba com mais precisão onde está a pessoa. Outra aplicação interessante seria não apenas identificar quando a crise está ocorrendo, mas também prever sua ocorrência antes que aconteça.

Machine Learning na Franzininho WiFi

Machine Learning na Franzininho WiFi:  Pulseira para crises de epilepsia – Parte 1 Machine Learning na Franzininho WiFi: Está chovendo? (Parte 1)
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
0 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Home » Software » Inteligência Artificial » Machine Learning na Franzininho WiFi:  Pulseira para crises de epilepsia  – Parte 2

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: