blog-eletrogate-logo-desktop blog-eletrogate-logo-mobile
  • Categorias
    • Voltar
    • INICIANTES
    • INTERMEDIÁRIOS
    • AVANÇADOS
    • divide
    • Automação Residencial
    • Componentes Eletrônicos
    • Impressão 3D
    • IoT
    • Modelagem 3D
    • Módulos Wifi
    • Por trás da tecnologia
    • Projetos
    • Raspberry Pi
    • Robótica
    • Sensores
    • Shields
    • Sistemas Operacionais
    • Tipos de Arduino
    • Tutoriais
  • Apostilas
  • Quem Somos
  • Seja um redator
  • Trabalhe Conosco
    • Categorias
      • Voltar
      • INICIANTES
      • INTERMEDIÁRIOS
      • AVANÇADOS
      • divide
      • Automação Residencial
      • Componentes Eletrônicos
      • Impressão 3D
      • IoT
      • Modelagem 3D
      • Módulos Wifi
      • Por trás da tecnologia
      • Projetos
      • Raspberry Pi
      • Robótica
      • Sensores
      • Shields
      • Sistemas Operacionais
      • Tipos de Arduino
      • Tutoriais
    • Apostilas
    • Quem Somos
    • Seja um redator
    • Trabalhe Conosco
Loja Eletrogate
voltar
  • Introdução
  • Materiais Necessários para o Projeto com TensorFlow Lite no ESP32
  • Inteligência Artificial das Coisas
  • Redes Neurais
  • TensorFlow Lite
  • Construindo uma Rede Neural
  • Deploy
  • Conclusão
  • Referências
  • Sobre o Autor
Tutoriais

TensorFlow Lite no ESP32

Eletrogate 15 de agosto de 2023

Introdução

A combinação entre aprendizado de máquina e microcontroladores abre um mundo de possibilidades para a criação de sistemas inteligentes e autônomos. O TensorFlow Lite, uma versão leve do TensorFlow, permite que os desenvolvedores implantem modelos de aprendizado de máquina em dispositivos com recursos limitados, como o ESP32. 

Além disso, as redes neurais ganharam imensa popularidade no campo da inteligência artificial e do aprendizado de máquina devido à sua capacidade de aprender padrões complexos e fazer previsões precisas. Neste artigo, vamos explorar o processo de uso do TensorFlow Lite em uma placa ESP32 e demonstrar como integrá-lo aos seus projetos. Para isso, também abordaremos alguns conceitos fundamentais da AIoT através da implementação de um modelo de rede neural simples, capaz de prever o seno de um número que varia de 0 a π.


Materiais Necessários para o Projeto com TensorFlow Lite no ESP32

Para a execução desse projeto, precisaremos dos seguintes materiais:

  • Módulo WiFi ESP32 Bluetooth 30 pinos
  • Cabo Micro USB para Arduino Leonardo, Micro, DUE e Raspberry Pi – Azul 50cm

Inteligência Artificial das Coisas

A Inteligência Artificial das Coisas, ou AIoT, é uma poderosa combinação dos recursos da Inteligência Artificial (IA) com a interconectividade dos dispositivos de Internet das Coisas (IoT). Os dispositivos IoT são dispositivos inteligentes interconectados e incorporados com sensores, atuadores e conectividade de rede que permitem coletar e transmitir dados. Assim, a AIoT aproveita esses dados para tomar decisões inteligentes e automatizar processos. Ao combinar o poder da IA e da IoT, os sistemas AIoT podem aumentar a eficiência, otimizar a utilização de recursos e permitir diversas análises preditivas.

Dessa forma, a AIoT representa uma mudança de paradigma em como as máquinas interagem e tomam decisões. Isso, pois, ao integrar algoritmos e técnicas de IA em dispositivos e redes de IoT, permite que esses dispositivos não apenas coletem e transmitam dados, mas também analisem, aprendam e tomem decisões autônomas com base nas inferências realizadas em tempo real. Assim, traz um novo nível de inteligência cognitiva para a vasta rede de dispositivos conectados, capacitando-os a operar de forma mais eficiente, precisa e adaptável.

Como exemplo de maior eficiência, podemos citar a forma com que os sistemas tradicionais de IA baseados em nuvem enfrentam desafios em termos de latência, restrições de largura de banda e questões de privacidade. Dessa forma, a chamada computação de borda aproxima os recursos de IA dos próprios dispositivos, permitindo processamento e análise de dados em tempo real, em proximidade ao local de geração. Essa arquitetura distribuída minimiza a necessidade de comunicação constante com a nuvem, melhora a capacidade de resposta e reduz o congestionamento da rede.


Redes Neurais

As redes neurais são uma classe de algoritmos inspirados na estrutura neural do cérebro humano, sendo, portanto um importante componente dos sistemas AIoT. São compostas por nós interconectados, chamados neurônios, que trabalham juntos para processar e transformar dados. Além disso, uma rede neural é constituída por camadas: uma camada de entrada, uma ou mais camadas ocultas e uma camada de saída. Cada neurônio recebe uma entrada, realiza cálculos e produz uma saída.

Dessa forma, um neurônio em uma rede neural imita o comportamento de um neurônio biológico. Ao receber sinais de entrada, aplica pesos (como se fossem notas) a eles, os soma e então aplica uma função de ativação, que irá finalmente produzir uma saída. Assim, a função de ativação introduz a não-linearidade na rede, permitindo modelar relacionamentos complexos entre dados.

Esse processo de alimentação de entradas e saídas através da rede é conhecido como propagação feedforward, ou retropropagação. Treinar uma rede neural também envolve expô-la a um grande conjunto de dados com entradas e saídas conhecidas e, nesse processo, a rede ajusta seus pesos e vieses com base no feedback de erro obtido por meio da retropropagação.

Assim, a retropropagação é um processo crucial que permite que as redes neurais aprendam com seus erros e melhorem seu desempenho, pois envolve comparar a saída prevista da rede com a saída real, calcular o erro e ajustar os pesos e desvios de acordo. Esse processo iterativo é guiado por algoritmos de otimização. Finalmente, depois de treinada, a rede pode ser testada em dados não vistos antes, para que seja possível avaliar seu desempenho. 

Existem diferentes algoritmos de redes neurais que podem ser aplicados para diversos tipos de dados. No entanto, nem sempre é necessário escrever um algoritmo do zero, pois existem frameworks poderosos disponíveis para uso, tornando esse processo muito mais simples. Assim, ao invés de precisar desenvolver uma rede neural sozinho, é possível usar modelos prontos e otimizar suas arquiteturas.


TensorFlow Lite

Um desses frameworks é o TensorFlow, uma biblioteca de código aberto desenvolvida pelo Google e projetada para permitir computações numéricas e machine learning em grande escala. Com uma ampla gama de modelos e algoritmos de machine learning e deep learning, o TensorFlow simplifica o uso dessas técnicas, abstraindo grande parte da complexidade.

Uma das principais características do TensorFlow é a capacidade de criar grafos de fluxo de dados. Essas estruturas descrevem como os dados se movem através de uma série de nós de processamento. Em um grafo TensorFlow, cada nó representa uma operação matemática, enquanto as conexões ou arestas entre os nós representam arrays de dados multidimensionais. 

Blog-Eletrogate-TensorFlow

Arquitetura simplificada do TensorFlow.

Na imagem acima, cada nódulo representa uma computação realizada, que fluem de um nódulo para outro de forma a transformar os dados para gerar os resultados. Enquanto, no TensorFlow, essas computações são chamados de operators, as arestas que ligam uma operação a outra recebem o nome de tensor, unidade fundamental desse framework e que representa arrays multi-dimensionais de dados.

O TensorFlow Lite, por sua vez, é responsável por converter um modelo pré-treinado no TensorFlow para um formato otimizado para menos armazenamento e processamento, pois é projetado para dispositivos com recursos limitados, como microcontroladores, telefones celulares e dispositivos IoT, em geral. Assim, fornece um tempo de execução simplificado que permite o uso eficiente de modelos de IA em dispositivos com memória mínima e requisitos computacionais baixos. Para isso, o TensorFlow Lite emprega várias técnicas de otimização, como a quantização, para otimizar seu desempenho nesses dispositivos.

Blog-Eletrogate-FrameworksTF

Diagrama das etapas do modelo e seus respectivos frameworks.

Na imagem acima podemos visualizar um esquema que demonstra qual framework será responsável por cada etapa do projeto. Note que as inferências são realizadas pelo modelo do TensorFlow Lite no ESP 32.


Construindo uma Rede Neural

Agora chegou a hora de aplicarmos os conhecimentos adquiridos para desenvolver um algoritmo usando o TensorFlow e realizarmos inferências pelo ESP32. Para demonstrar a aplicação prática de AIoT e redes neurais, criaremos uma rede neural simples que prevê o seno de um número que varia de 0 a π. A escolha dessa aplicação decorre da simplicidade de gerar dados para o treino da rede e dos diversos conceitos abordados na criação do algoritmo que facilitarão o entendimento sobre o funcionamento desse tipo de IA.

Como usaremos o Tensor Flow (e, portanto, o Python), talvez seja necessário possuir ambos instalados em sua máquina. No entanto, os códigos podem ser rodados em serviços de nuvem, como o Google Colab, sem a necessidade de instalar esses softwares na sua máquina. Por isso, esse artigo não abordará a instalação dos pacotes, mas o notebook Python com os códigos compilados estará disponível logo abaixo.

O primeiro passo é importar as bibliotecas:

import math
import numpy as np
# as bibliotecas math e numpy serão usadas para gerar os dados de treino e teste, isto é, usadas para gerar os valores de seno
import tensorflow as tf
from tensorflow.keras import layers
from everywhereml.code_generators.tensorflow import tf_porter

Agora, precisamos gerar um conjunto de dados, que serão usados como pares de entrada e saída na nossa rede neural, onde a entrada é um número que varia de 0 a π e a saída é o valor do seno correspondente. Você pode usar um script para criar esse conjunto de dados, ou criar manualmente um arquivo CSV com os pares de entrada-saída.

# a variável SAMPLES recebe a quantidade de dados que vamos gerar para x e y, ou seja, um valor entre 0 e pi e o respectivo seno desse valor
SAMPLES = 1000
# aqui, usaremos uma função do numpy para gerar uma quantidade números aleatórios entre um intervalo, definido entre 0 e 2*pi 
x_values = np.random.uniform(low=0, high=2*math.pi, size=SAMPLES)
# e então, geramos os dados de seno correspondentes, através da função sin do numpy
y_values = np.sin(x_values)

Agora que já possuímos os dados necessários, chegou a hora de criarmos nosso modelo. Usando o TensorFlow, criaremos uma rede neural com duas camadas e 16 neurônios. Para facilitar, gerarei os dados de treino e teste na mesma função que treinarei o modelo. 

def get_model():
    # a variável SAMPLES recebe a quantidade de dados que vamos gerar para x e y, ou seja, um valor entre 0 e pi e o respectivo seno desse valor
    SAMPLES = 1000
    # a função seed, do numpy, define uma "semente" para gerar números aleatórios, garantindo que a sequência permaneça a mesma em várias execuções, permitindo reprodutibilidade
    np.random.seed(1337)
    # aqui, usaremos uma função do numpy para gerar uma quantidade números aleatórios entre um intervalo, definido entre 0 e 2*pi 
    x_values = np.random.uniform(low=0, high=2*math.pi, size=SAMPLES)
    # agora, usaremos outra função do numpy para embaralhar os dados gerados
    np.random.shuffle(x_values)
    # e então, geramos os dados de seno correspondentes, através da função sin do numpy
    y_values = np.sin(x_values)
    # o próximo passo é adicionar algum tipo de ruído aos dados. A função randn do numpy gera dados aleatórios pertencentes à distribuição normal, a partir de um formato especificado
    y_values += 0.1 * np.random.randn(*y_values.shape)

    # agora, com os dados em mãos, podemos separá-los entre treino e teste.
    TRAIN_SPLIT =  int(0.6 * SAMPLES)
    TEST_SPLIT = int(0.2 * SAMPLES + TRAIN_SPLIT)
    x_train, x_test, x_validate = np.split(x_values, [TRAIN_SPLIT, TEST_SPLIT])
    y_train, y_test, y_validate = np.split(y_values, [TRAIN_SPLIT, TEST_SPLIT])

    # aqui, criaremos uma rede neural com 2 camadas de 16 neurônios
    model = tf.keras.Sequential()
    model.add(layers.Dense(16, activation='relu', input_shape=(1,)))
    model.add(layers.Dense(16, activation='relu'))
    model.add(layers.Dense(1))
    # e então definimos as métricas e o otimizador, que é algoritmo usado para minimizar uma função de perda em relação aos parâmetros treináveis do modelo
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    # finalmente, treinamos o modelo
    model.fit(x_train, y_train, epochs=200, batch_size=16,
                        validation_data=(x_validate, y_validate))
    # precisamos que, além do próprio modelo, a função retorne os dados de treino para que possam ser usados pela biblioteca everywhereml
    return model, x_train, y_train

Finalmente, com a arquitetura pronta e os dados em mãos, podemos treinar o nosso algoritmo. 

# chamamos a função para treinar o modelo
tf_model, x_train, y_train = get_model()
# e então usamos a função do TensorFlow para converter o modelo para o TensorFlow Lite
converter = tf.lite.TFLiteConverter.from_keras_model(tf_model)
tflite_model = converter.convert()

with open('model.tflite', 'wb') as f:
  f.write(tflite_model)

Deploy

Com o modelo em mãos, o próximo passo é carregá-lo para o nosso microcontrolador. Como citado, o treinamento do modelo foi realizado usando o Python, linguagem utilizada pelo próprio Tensor Flow. No entanto, dispositivos como os ESP compilam códigos em C/C++. Portanto, para que seja possível carregar o nosso modelo para a placa, iremos converte-lo para C++. Para isso, utilizaremos uma biblioteca chamada everywhereml.

# agora usamos a biblioteca everywhereml para converter o modelo do TF Lite em um código c++
porter = tf_porter(tflite_model, x_train, y_train)
cpp_code = porter.to_cpp(instance_name='modelo_seno', arena_size=4096)

print(cpp_code)
# salve o código gerado em um arquivo c++

Os códigos em Python usados até agora também estão disponíveis para visualização no seguinte notebook Colab. Abaixo, o código resultante.

#ifndef UUID140516234207264
#define UUID140516234207264

#include <EloquentTinyML.h>
#include <eloquent_tinyml/tensorflow.h>

#ifdef __has_attribute
#define HAVE_ATTRIBUTE(x) __has_attribute(x)
#else
#define HAVE_ATTRIBUTE(x) 0
#endif
#if HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__))
#define DATA_ALIGN_ATTRIBUTE __attribute__((aligned(4)))
#else
#define DATA_ALIGN_ATTRIBUTE
#endif

#ifndef ARENA_SIZE
#define ARENA_SIZE 4096
#endif

/** model size = 3164 bytes **/
const unsigned char modelData[] DATA_ALIGN_ATTRIBUTE = { 0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x14, 0x00, 0x20, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, 0x07, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x98, 0xff, 0xff, 0xff, 0x09, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x32, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xca, 0xf9, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x14, 0x06, 0x00, 0x00, 0x0c, 0x06, 0x00, 0x00, 0xbc, 0x05, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x76, 0xfa, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x32, 0x2e, 0x30, 0x00, 0x00, 0xd6, 0xfa, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf5, 0xff, 0xff, 0xc4, 0xf5, 0xff, 0xff, 0xc8, 0xf5, 0xff, 0xff, 0xfe, 0xfa, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x74, 0x2d, 0x17, 0x3e, 0x9d, 0xc8, 0xbc, 0x3f, 0x70, 0xf7, 0x0a, 0x3e, 0xc4, 0x1c, 0x07, 0x3e, 0x29, 0x93, 0x63, 0x3f, 0x3c, 0x8e, 0x74, 0x3e, 0x0c, 0x53, 0x02, 0xbe, 0x1d, 0xb5, 0xbf, 0x3e, 0x63, 0x7f, 0xae, 0xbf, 0x50, 0x2a, 0x69, 0x3e, 0x7c, 0x69, 0xba, 0x3e, 0x6b, 0xff, 0xde, 0xbe, 0xbd, 0xa9, 0xb3, 0x3d, 0xf6, 0x5b, 0xb0, 0x3e, 0xf5, 0x29, 0xf7, 0xbe, 0xe0, 0xb9, 0xb6, 0x3f, 0x4a, 0xfb, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x27, 0x7d, 0x50, 0xbc, 0x58, 0xb3, 0x5b, 0xbe, 0x50, 0x7e, 0xad, 0x3c, 0x08, 0xfe, 0x08, 0xbd, 0x89, 0x37, 0x11, 0xbe, 0x73, 0x55, 0xd7, 0x3e, 0x02, 0xcf, 0xdc, 0xbe, 0x63, 0xa9, 0x96, 0x3e, 0xed, 0x6a, 0xd9, 0x3d, 0xb4, 0xe2, 0xa5, 0xbe, 0x4d, 0x11, 0xcf, 0xbe, 0xfb, 0x76, 0xba, 0x3e, 0x5c, 0xe4, 0x13, 0xbe, 0xa2, 0x48, 0x55, 0x3e, 0x75, 0x32, 0x9d, 0x3e, 0x38, 0x30, 0x1d, 0xbe, 0x13, 0x71, 0x57, 0x3e, 0x52, 0xe8, 0xdb, 0xbe, 0x8c, 0xa9, 0x61, 0xbf, 0x41, 0x78, 0x2c, 0xbe, 0x53, 0xbe, 0x00, 0xbe, 0x17, 0xaa, 0x9b, 0xbe, 0xaf, 0xc2, 0x80, 0x3e, 0x53, 0xe1, 0x9b, 0x3e, 0xca, 0x7c, 0xd1, 0xbc, 0x7c, 0x62, 0xcc, 0x3d, 0x7e, 0x18, 0xe2, 0x3d, 0xd8, 0xa9, 0xb9, 0xbe, 0x38, 0xed, 0x3b, 0x3d, 0x9c, 0x65, 0xc5, 0x3d, 0x48, 0xad, 0x42, 0xbf, 0xb9, 0x06, 0xf3, 0x3e, 0xf0, 0x1f, 0x86, 0x3c, 0xa0, 0x60, 0x9b, 0xbe, 0xe0, 0x78, 0x94, 0xbd, 0xdd, 0x9b, 0x80, 0xbe, 0xfb, 0x02, 0x9c, 0x3e, 0xb2, 0x1d, 0xd8, 0xbe, 0x5b, 0x62, 0xb7, 0x3e, 0x56, 0x38, 0x06, 0x3e, 0xfb, 0x44, 0x86, 0xbe, 0x70, 0x5d, 0xee, 0xbc, 0xc0, 0x02, 0x0f, 0x3c, 0x30, 0xd0, 0x04, 0xbe, 0xf7, 0x4f, 0x80, 0xbe, 0xf4, 0xf4, 0xda, 0xbd, 0x42, 0x83, 0x06, 0x3e, 0x54, 0xe7, 0xad, 0xbd, 0x5b, 0xdb, 0xbc, 0x3e, 0x30, 0xbc, 0x2a, 0xbd, 0xde, 0xf0, 0xa9, 0xbe, 0x2e, 0xc5, 0x03, 0x3e, 0xe1, 0xcd, 0xd6, 0xbe, 0x2d, 0xee, 0xc3, 0xbe, 0x67, 0x7a, 0xc9, 0x3e, 0x20, 0xa2, 0x6b, 0x3c, 0x78, 0x89, 0x69, 0xbe, 0xa5, 0xf7, 0x7c, 0xbe, 0x7f, 0x9a, 0xb1, 0x3e, 0x22, 0x0c, 0x5a, 0x3e, 0x6c, 0x49, 0xc9, 0x3d, 0x96, 0x38, 0x7c, 0x3e, 0x3b, 0xd1, 0x8c, 0x3e, 0x2f, 0x4f, 0xad, 0xbe, 0xa6, 0xf6, 0xba, 0x3e, 0xbb, 0x60, 0xc9, 0x3e, 0x68, 0x68, 0x1b, 0xbf, 0xd8, 0xfc, 0x3d, 0xbf, 0x5f, 0x02, 0x9c, 0xbe, 0xc8, 0xd4, 0xe7, 0xbd, 0x00, 0x51, 0xf4, 0x3c, 0x30, 0x77, 0x26, 0xbe, 0x47, 0x99, 0x0b, 0x3f, 0x5d, 0x4f, 0x97, 0x3e, 0x53, 0xa2, 0xb6, 0xbe, 0x16, 0x67, 0x6a, 0x3e, 0x52, 0x4c, 0x37, 0x3e, 0x53, 0x38, 0xd8, 0xbe, 0xb7, 0x4f, 0x8a, 0xbe, 0x56, 0x3c, 0x92, 0x3d, 0xea, 0xca, 0x03, 0xbe, 0xa0, 0x8d, 0xdc, 0x3c, 0x14, 0xa3, 0x99, 0x3d, 0xa7, 0xb6, 0xdc, 0x3e, 0xa7, 0xd3, 0xa4, 0x3e, 0x60, 0x32, 0x81, 0x3d, 0xf5, 0x03, 0xa7, 0x3e, 0x7e, 0x40, 0x34, 0x3e, 0xf0, 0xac, 0xb0, 0xbd, 0x9e, 0xad, 0x71, 0x3e, 0xb3, 0x00, 0xb2, 0xbe, 0x20, 0xce, 0x22, 0xbd, 0x32, 0xca, 0x7b, 0x3e, 0xe9, 0x4d, 0x90, 0x3e, 0x64, 0xf8, 0xa4, 0xbe, 0x98, 0x1d, 0xaf, 0xbd, 0x9f, 0xde, 0xca, 0xbe, 0xb6, 0x55, 0x57, 0xbe, 0x96, 0xb3, 0x44, 0xbe, 0x70, 0x32, 0xbb, 0xbd, 0x34, 0x4e, 0xb6, 0x3d, 0xef, 0xc5, 0x4e, 0xbe, 0xf4, 0x9f, 0xc4, 0xbe, 0x1c, 0x14, 0xa3, 0xbe, 0x1c, 0x5f, 0xea, 0xbd, 0xac, 0x3d, 0x91, 0x3d, 0x6c, 0xb2, 0xb1, 0xbd, 0xe0, 0x8d, 0xb2, 0xbc, 0x00, 0x70, 0xcc, 0xba, 0xa1, 0xcd, 0x64, 0xbe, 0x5d, 0x92, 0x6a, 0xbe, 0xb0, 0x42, 0xf3, 0xbd, 0x3d, 0xe6, 0x87, 0xbe, 0x56, 0x31, 0x1c, 0x3e, 0x44, 0x26, 0xa1, 0x3e, 0x31, 0xbf, 0xf1, 0x3d, 0x51, 0x47, 0x9a, 0xbe, 0x32, 0x60, 0x2c, 0x3e, 0xde, 0xf3, 0x44, 0x3e, 0x2d, 0x3a, 0x82, 0xbe, 0x29, 0x9d, 0xf4, 0x3e, 0x4a, 0x8f, 0x28, 0x3e, 0x27, 0x93, 0xc8, 0xbe, 0xec, 0x7e, 0xe7, 0xbd, 0xa4, 0x5d, 0xe7, 0x3d, 0x1c, 0x7d, 0xfc, 0x3d, 0x2d, 0x25, 0x87, 0xbe, 0x95, 0x63, 0xa2, 0xbe, 0xd1, 0x2b, 0x9b, 0xbe, 0x26, 0x29, 0x1b, 0xbe, 0xf0, 0x55, 0x83, 0xbd, 0x22, 0x7f, 0x5f, 0x3d, 0x09, 0xb0, 0x18, 0x3e, 0x19, 0x4a, 0x9b, 0xbe, 0x89, 0xc8, 0x93, 0xbe, 0x49, 0x22, 0x89, 0x3e, 0x8f, 0x89, 0x01, 0xbd, 0x19, 0xc8, 0xa7, 0xbe, 0x2e, 0x59, 0x7e, 0x3e, 0x22, 0xfe, 0xd1, 0xbe, 0xc2, 0x88, 0x2c, 0xbe, 0x0d, 0x6f, 0xb8, 0x3e, 0x82, 0x12, 0x4a, 0xbf, 0xd3, 0x41, 0x43, 0xbe, 0xaa, 0x95, 0x3a, 0x3e, 0xc2, 0xeb, 0x08, 0x3e, 0x8a, 0x22, 0x39, 0x3e, 0x10, 0x9b, 0xfb, 0x3c, 0xbb, 0x5c, 0xa0, 0x3e, 0xbc, 0x41, 0xcb, 0xbd, 0xf0, 0xa9, 0x83, 0x3c, 0x34, 0x10, 0x3b, 0xbe, 0x91, 0x8b, 0x7e, 0xbe, 0xa3, 0xfb, 0xd5, 0xbe, 0x76, 0xaf, 0x59, 0xbe, 0x00, 0xba, 0x93, 0x3c, 0xde, 0x19, 0x88, 0xbe, 0x09, 0x6a, 0x40, 0xbe, 0xa6, 0x5b, 0x9b, 0xbe, 0x9a, 0x94, 0x5e, 0xbe, 0x73, 0x3f, 0xa1, 0x3e, 0x9a, 0xac, 0x14, 0xbe, 0x03, 0x0a, 0x61, 0xbe, 0xfe, 0x7c, 0x04, 0x3e, 0x38, 0xa6, 0xc9, 0xbe, 0x64, 0x2c, 0xab, 0xbd, 0x8c, 0x47, 0xc6, 0xbe, 0x56, 0xb8, 0xa7, 0xbe, 0xaf, 0x36, 0x48, 0xbe, 0x1d, 0x71, 0xb6, 0x3e, 0xae, 0x1d, 0x48, 0x3e, 0x4c, 0x7a, 0x80, 0x3d, 0x44, 0x40, 0x8a, 0xbe, 0x6e, 0xb7, 0x13, 0x3e, 0x96, 0x4a, 0x46, 0x3e, 0xf2, 0x44, 0xc5, 0xbe, 0xc0, 0x16, 0x8c, 0x3c, 0x6b, 0x85, 0x8d, 0x3e, 0x5f, 0xc7, 0xc9, 0xbe, 0xdd, 0xfd, 0xd1, 0xbe, 0xa8, 0x79, 0x8b, 0xbe, 0x91, 0xfb, 0x91, 0xbe, 0x0f, 0x1d, 0xd3, 0x3e, 0x00, 0x74, 0x36, 0x3d, 0x57, 0x84, 0x4d, 0xbe, 0xdb, 0xef, 0x68, 0xbe, 0x0a, 0x70, 0x2d, 0xbe, 0x47, 0xf0, 0x90, 0x3e, 0x68, 0x2f, 0xb6, 0xbe, 0x88, 0xa8, 0xb2, 0xbd, 0xfa, 0xae, 0x3c, 0x3e, 0x54, 0xb9, 0x90, 0xbd, 0x7a, 0xa7, 0x9b, 0x3e, 0xb0, 0x99, 0x3c, 0x3d, 0xb1, 0xa1, 0x96, 0x3e, 0x81, 0x94, 0x2b, 0xbe, 0xfa, 0xa5, 0x61, 0xbe, 0xc0, 0x32, 0x76, 0xbc, 0xfc, 0xcc, 0x70, 0xbe, 0x03, 0x8c, 0xa5, 0x3e, 0x38, 0x46, 0x75, 0xba, 0xcc, 0x73, 0x9d, 0xbe, 0x64, 0x62, 0xc6, 0xbe, 0xa4, 0xab, 0xc1, 0xbe, 0x50, 0x40, 0xd5, 0xbc, 0x6e, 0x28, 0x36, 0x3e, 0xfc, 0x99, 0xa7, 0xbd, 0x41, 0x34, 0x83, 0xbd, 0xd4, 0x9c, 0x95, 0xbe, 0x74, 0xd9, 0xd4, 0xbe, 0x60, 0x47, 0xcc, 0xbd, 0x80, 0xeb, 0x2a, 0xbc, 0xd2, 0x0e, 0x0e, 0x3e, 0x16, 0x10, 0x73, 0xbe, 0xcf, 0x86, 0x80, 0xbe, 0x8d, 0x0a, 0xc4, 0x3e, 0x8e, 0xd9, 0x75, 0xbe, 0x4c, 0x46, 0x1e, 0xbe, 0x75, 0x01, 0x8e, 0x3e, 0x6b, 0x1e, 0x93, 0x3e, 0x6b, 0x2b, 0x8b, 0x3e, 0xf0, 0xa3, 0x87, 0x3c, 0xfe, 0x32, 0x23, 0x3e, 0xa5, 0x13, 0xd9, 0xbe, 0x6d, 0xee, 0xb5, 0xbe, 0x12, 0x9c, 0x00, 0x3e, 0x5e, 0x8f, 0x84, 0xbe, 0x8d, 0xc7, 0xd4, 0x3e, 0x44, 0x8e, 0x97, 0xbd, 0x34, 0x78, 0xc1, 0xbd, 0x87, 0xe2, 0x95, 0xbe, 0x04, 0xd7, 0x13, 0xbe, 0x56, 0x9c, 0x21, 0xbe, 0xc0, 0x7f, 0x38, 0x3c, 0x20, 0x8d, 0x9a, 0xbd, 0x6e, 0x10, 0x7a, 0x3e, 0x98, 0x35, 0x56, 0xbe, 0x0d, 0x91, 0xb1, 0xbe, 0x20, 0x4a, 0x24, 0xbc, 0x3a, 0x75, 0x18, 0x3e, 0x5e, 0xb8, 0xfe, 0x3e, 0x30, 0x7d, 0x82, 0x3c, 0x55, 0x7b, 0x2e, 0xbf, 0x6f, 0xf9, 0x04, 0xbf, 0x37, 0x30, 0x0d, 0xbe, 0x65, 0x8e, 0x8f, 0x3e, 0x3b, 0xa9, 0xdd, 0xbe, 0x16, 0x82, 0x30, 0x3e, 0xe3, 0xc4, 0xc9, 0x3e, 0x40, 0xfe, 0x3c, 0xbd, 0xf8, 0x8c, 0xb8, 0xbd, 0x63, 0x73, 0xb8, 0x3e, 0x80, 0x32, 0x19, 0xbd, 0xed, 0xeb, 0x9b, 0x3e, 0x5e, 0xdb, 0x5f, 0xbf, 0xca, 0x7b, 0xbf, 0xbd, 0x56, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xd6, 0x71, 0x5f, 0x3e, 0x50, 0x48, 0x87, 0xbd, 0x70, 0x88, 0x8b, 0x3e, 0xe6, 0xc2, 0x3e, 0x3e, 0xc7, 0x8e, 0xdf, 0x3e, 0x40, 0xec, 0x6a, 0xbc, 0xe6, 0xb2, 0xe7, 0xbe, 0xf0, 0x9b, 0x22, 0xbd, 0xf9, 0x13, 0x1c, 0x3f, 0x0c, 0xb8, 0xbb, 0xbe, 0xd4, 0xdc, 0x03, 0x3f, 0xe4, 0xaa, 0xd3, 0xbd, 0xc2, 0xc5, 0x89, 0xbe, 0xf2, 0x4a, 0xf8, 0xbe, 0x11, 0x2e, 0xa6, 0x3e, 0x11, 0xf3, 0xa7, 0x3e, 0xa2, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xb4, 0x52, 0xb1, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x18, 0xe8, 0xbe, 0x56, 0x43, 0x54, 0xbe, 0x36, 0x0d, 0x8a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x9d, 0x33, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xe1, 0xa9, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0xb9, 0x4c, 0xbf, 0xbb, 0x63, 0x1c, 0xbd, 0xee, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc6, 0x4f, 0xe0, 0xbe, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7f, 0x35, 0xd9, 0x3d, 0xd6, 0x1a, 0x9d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x9f, 0x97, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xbd, 0x88, 0x3e, 0xff, 0x0e, 0x44, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x62, 0x77, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x3b, 0x8f, 0x3e, 0x20, 0xfb, 0xff, 0xff, 0x24, 0xfb, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x4d, 0x4c, 0x49, 0x52, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9a, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9c, 0xfb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xca, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xba, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x8c, 0x03, 0x00, 0x00, 0x1c, 0x03, 0x00, 0x00, 0xac, 0x02, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb2, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x9c, 0xfc, 0xff, 0xff, 0x19, 0x00, 0x00, 0x00, 0x53, 0x74, 0x61, 0x74, 0x65, 0x66, 0x75, 0x6c, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0xf4, 0xfc, 0xff, 0xff, 0x4c, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x31, 0x2f, 0x4d, 0x61, 0x74, 0x4d, 0x75, 0x6c, 0x3b, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x31, 0x2f, 0x52, 0x65, 0x6c, 0x75, 0x3b, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x31, 0x2f, 0x42, 0x69, 0x61, 0x73, 0x41, 0x64, 0x64, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x96, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x80, 0xfd, 0xff, 0xff, 0x46, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x2f, 0x4d, 0x61, 0x74, 0x4d, 0x75, 0x6c, 0x3b, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x2f, 0x52, 0x65, 0x6c, 0x75, 0x3b, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x2f, 0x42, 0x69, 0x61, 0x73, 0x41, 0x64, 0x64, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x86, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf4, 0xfd, 0xff, 0xff, 0x19, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x32, 0x2f, 0x4d, 0x61, 0x74, 0x4d, 0x75, 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xce, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3c, 0xfe, 0xff, 0xff, 0x19, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x31, 0x2f, 0x4d, 0x61, 0x74, 0x4d, 0x75, 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x16, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x84, 0xfe, 0xff, 0xff, 0x17, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x2f, 0x4d, 0x61, 0x74, 0x4d, 0x75, 0x6c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5a, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xc8, 0xfe, 0xff, 0xff, 0x27, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x2f, 0x42, 0x69, 0x61, 0x73, 0x41, 0x64, 0x64, 0x2f, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x70, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xaa, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0xff, 0xff, 0xff, 0x29, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x32, 0x2f, 0x42, 0x69, 0x61, 0x73, 0x41, 0x64, 0x64, 0x2f, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x70, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x18, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x84, 0xff, 0xff, 0xff, 0x29, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x31, 0x2f, 0x42, 0x69, 0x61, 0x73, 0x41, 0x64, 0x64, 0x2f, 0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x70, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 };

/**
 * Wrapper around the EloquentTinyML library
 */
template<uint32_t arenaSize>
class TensorFlowPorter {
    public:
        Eloquent::TinyML::TensorFlow::AllOpsTensorFlow<1, 1, arenaSize> tf;

        /**
         * Init model
         */
        bool begin() {
            return tf.begin(modelData);
        }

        /**
         * Proxy
         */
        uint8_t predict(uint8_t *input, uint8_t *output = NULL) {
            return tf.predict(input, output);
        }

        /**
         * Proxy
         */
        int8_t predict(int8_t *input, int8_t *output = NULL) {
            return tf.predict(input, output);
        }

        /**
         * Proxy
         */
        float predict(float *input, float *output = NULL) {
            return tf.predict(input, output);
        }

        /**
         * Proxy
         */
        template<typename T>
        uint8_t predictClass(T *input) {
            return tf.predictClass(input);
        }

        /**
         * Proxy
         */
        float getScoreAt(uint8_t index) {
            return tf.getScoreAt(index);
        }

        /**
         * Proxy
         */
        String getErrorMessage() {
            return tf.getErrorMessage();
        }
};



TensorFlowPorter<ARENA_SIZE> modelo_seno;


#endif

Finalmente, com o script em C++ em mãos, podemos carregá-lo para o Arduino IDE e, então, para a placa. Os passos para uso do ESP32 através do IDE do Arduino já foram abordados de forma mais extensiva em diversos posts aqui do blog, como este aqui.

Iremos usar a biblioteca EloquentTinyML para que não seja necessário baixar a estrutura completa do TensorFlow Lite e compilá-la em sua própria máquina. Para baixá-la, abra o Arduino IDE e vá para Sketch > Incluir Biblioteca > Gerenciar Bibliotecas e procure por EloquentTinyML. Selecione a versão a partir da 2.4.4 e então clique em instalar.

Blog-Eletrogate-EloquentTinyML

Finalmente, podemos carregar nosso código principal para o ESP32.

// importe o arquivo c++ gerado na etapa anterior
#include "modelo_seno.h"

void setup() {
    Serial.begin(115200);

    while (!modelo_seno.begin()) {
        Serial.print("Erro ao carregar modelo_seno: ");
        Serial.println(modelo_seno.getErrorMessage());
    }
}

void loop() {
    for (int i = 0; i < 20; i++) {
        // escolha aleatória entre 0 e pi
        float x = 3.14f * i / 20.0f;
        // criando array
        float input[1] = { x };

        // gerar valor real do seno do número
        float y_true = sin(x);
        // gerando valor do seno a partir do modelo
        float y_pred = modelo_seno.predict(input);

        // printar o valor real do seno e o valor gerador pelo modelo para comparação
        Serial.print("seno de ");
        Serial.print(x);
        Serial.print(" = ");
        Serial.print(y_true);
        Serial.print("\t seno previsto: ");
        Serial.println(y_pred);
        delay(1000);
    }
}

É possível que, na hora de compilar o código, a biblioteca EloquentTinyML retorne um erro do tipo “No such file or directory”. Nesse caso, será necessário trocar a versão do Arduino IDE para a 1.8.18, 1.8.9 ou anterior, de forma a não gerar mais incompatibilidades com a biblioteca.

Finalmente, é só compilar o código e carregá-lo para a placa! No vídeo abaixo, é possível ver o projeto em execução através do simulador Wokwi, cujo uso também já foi explicado em outro post do blog. A simulação é conferida neste link.

https://blog.eletrogate.com/wp-content/uploads/2023/07/tf-esp32.mp4

Conclusão

Ao combinar o poder dos algoritmos de IA com a interconectividade de dispositivos IoT, os sistemas AIoT podem desbloquear níveis sem precedentes de automação, tomada de decisão e análise de dados. Neste artigo, exploramos os conceitos essenciais de AIoT e redes neurais e demonstramos como construir uma rede neural simples usando o TensorFlow Lite para prever o seno de um número que varia de 0 a π.

Embora o exemplo fornecido seja direto, ele mostra o potencial dos aplicativos AIoT. Ao aproveitar o TensorFlow Lite, você pode implantar modelos de aprendizado de máquina em dispositivos com recursos limitados, abrindo um mundo de possibilidades para soluções AIoT e permitindo uma tomada de decisão mais inteligente e eficiente para seus projetos futuros! 

Caso tenha alguma dúvida ou sugestão, deixe um comentário abaixo! E, caso execute o projeto, não deixe de nos mostrar e marcar no Instagram @eletrogate.


Referências

  • Predicting Sine Wave Output and Visualizing the Deep Learning Network
  • Intro to TinyML Part 1: Training a Model for Arduino in TensorFlow
  • Tensorflow Lite on ESP32 in Arduino IDE

Sobre o Autor


Barbara Leidens

Graduanda em Estatística pela UFSM, analista de dados freelancer e mentora do grupo de robótica ASTERIA.


Eletrogate

15 de agosto de 2023

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!

Projetos

Jogo da Velha na TV!

Eletrogate19 de setembro de 2023

Este é um jogo de quebra-cabeça para dois jogadores, identificados como “X” e “O”, que se revezam marcando os espaços em uma área 3 × 3.
Alguma vez na vida você já deve ter jogado este clássico jogo conhecido como Jogo-da-Velha ou Tic-Tac-Toe.

Neste post, você vai aprender a montar e programar uma versão digital deste jogo, com saída de imagem para TV, utilizando apenas alguns componentes eletrônicos básicos, uma placa Arduino UNO e um Teclado Matricial de 16 teclas.

Projetos

Jogo da Velha na TV!

Eletrogate19 de setembro de 2023

Este é um jogo de quebra-cabeça para dois jogadores, identificados como “X” e “O”, que se revezam marcando os espaços em uma área 3 × 3.
Alguma vez na vida você já deve ter jogado este clássico jogo conhecido como Jogo-da-Velha ou Tic-Tac-Toe.

Neste post, você vai aprender a montar e programar uma versão digital deste jogo, com saída de imagem para TV, utilizando apenas alguns componentes eletrônicos básicos, uma placa Arduino UNO e um Teclado Matricial de 16 teclas.

IoT

LittleFS: Alto Desempenho para RP Pico, ESP32 e ESP8266

Eletrogate12 de setembro de 2023

Aprenda neste post, a usar o sistema de arquivos LittleFS, que possui mais desempenho do que o sistema SPIFFS, nas placas Raspberry Pi Pico, ESP32 e ESP8266.

IoT

LittleFS: Alto Desempenho para RP Pico, ESP32 e ESP8266

Eletrogate12 de setembro de 2023

Aprenda neste post, a usar o sistema de arquivos LittleFS, que possui mais desempenho do que o sistema SPIFFS, nas placas Raspberry Pi Pico, ESP32 e ESP8266.

Sensores

Como Utilizar o Módulo Sensor de Cor RGB TCS34725

Eletrogate5 de setembro de 2023

Você já precisou de algum sensor para detectar as cores de objetos? Venha conferir o post de hoje! Nós vamos utilizar o sensor RGB TCS34725.

Sensores

Como Utilizar o Módulo Sensor de Cor RGB TCS34725

Eletrogate5 de setembro de 2023

Você já precisou de algum sensor para detectar as cores de objetos? Venha conferir o post de hoje! Nós vamos utilizar o sensor RGB TCS34725.

Projetos

Controlando o Braço Robótico em MDF via Bluetooth

Eletrogate29 de agosto de 2023 Atualizado em: 01 set 2023

Que tal montar um braço robótico e controlá-lo pelo celular? Entre para dicas de montagem e aprender a controlar servos e conectar sua placa.

Projetos

Controlando o Braço Robótico em MDF via Bluetooth

Eletrogate29 de agosto de 2023 Atualizado em: 01 set 2023

Que tal montar um braço robótico e controlá-lo pelo celular? Entre para dicas de montagem e aprender a controlar servos e conectar sua placa.

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!

blog-eletrogate-logo-footer

Rua Rio de Janeiro, 441 - Sala 1301
Centro - Belo Horizonte/MG
CEP 30160-041
*Não temos atendimento físico

ANWAR SLEIMAN HACHOUCHE - ME
CNPJ: 18.917.521/0001-73

Atendimento

(31) 3142-3800

[email protected]


Seg a Sex - das 8h às 17h

Institucional

  • Apostilas
  • Quem Somos
  • Privacidade
  • Seja um Redator
  • Trabalhe Conosco

Nos acompanhe

Facebook Instagram Youtube

© ELETROGATE 2023 - Todos os direitos reservados. Termos de uso e Política de privacidade.