Conteúdo detalhado
📍 Por que IA dentro do microcontrolador
Um modelo de IA pode rodar em três lugares: num servidor na internet, num computador dentro do robô (o Pi 5 do módulo 3.3) ou no próprio microcontrolador que já lê os sensores. Quanto mais perto do sensor, menor o atraso e o consumo, e nenhum dado sai do robô. O preço é o tamanho: no ESP32-S3 cabem modelos de dezenas a centenas de kilobytes, que respondem perguntas simples, como "este movimento é um aceno?" ou "tem uma pessoa na frente?".
🌱 Novo aqui?
Modelo é o arquivo com o "conhecimento" aprendido no treino: uma rede de números chamados pesos. Treinar é ajustar esses pesos com muitos exemplos, e isso acontece no PC ou na nuvem. Inferência é usar o modelo pronto para responder sobre um dado novo, e é só isso que o microcontrolador faz.
Como ler: da esquerda para a direita o modelo fica menor e mais perto do sensor. A caixa em destaque é o assunto deste módulo. Os tempos são ordens de grandeza, não medidas da sua placa: meça sempre no seu caso.
✓ Bom para TinyML
- ✓Classificar movimento pela IMU (gesto, queda, vibração de motor).
- ✓Uma ou poucas palavras de comando de voz.
- ✓"Tem pessoa ou não" em imagem pequena, em tons de cinza.
- ✓Robô a bateria que precisa reagir rápido e sem Wi-Fi.
✗ Deixe para o Pi ou o PC
- ✗Detectar dezenas de classes com caixas, como o YOLO26.
- ✗Entender frases livres ou conversar (isso é a Trilha 5).
- ✗Treinar o modelo: o treino nunca roda no microcontrolador.
- ✗Imagem colorida em alta resolução, quadro a quadro.
Por que aprender
Saber onde cada modelo deve rodar evita dois erros caros: pendurar um Pi 5 num projeto que um ESP32-S3 resolveria a bateria, ou tentar enfiar no microcontrolador uma tarefa que só cabe no computador. No robô completo (módulos 3.5 e 3.6) os dois convivem: o Pi vê o mundo e o ESP32 reage.
Conceitos-chave
🗜️ TinyML: modelo pequeno, quantizado em int8
TinyML é o nome dado a aprendizado de máquina que roda em microcontroladores. O ESP32-S3 tem cerca de 512 KB de RAM interna e alguns megabytes de flash; muitas placas somam uma PSRAM externa de 2 a 8 MB. Um modelo treinado no PC guarda cada peso como float32 (4 bytes). A quantização converte cada peso para int8 (1 byte, de −128 a 127): o modelo fica 4 vezes menor e as contas com inteiros ficam bem mais rápidas, com perda pequena de acerto quando é bem feita.
Como ler: cada peso deixa de ocupar quatro caixinhas (bytes) e passa a ocupar uma. A escala e o ponto zero ficam guardados no modelo para converter de volta quando precisar. Os números do exemplo são ilustrativos.
🌱 Novo aqui?
Flash é a memória que guarda o programa e o modelo mesmo sem energia. RAM é a memória de trabalho, apagada ao desligar; é nela que ficam as contas intermediárias da inferência (a "arena"). PSRAM é uma RAM extra, mais lenta, soldada ao lado do chip em algumas placas, como a XIAO ESP32-S3 Sense.
| Tarefa | Tamanho típico do modelo (int8) | Cabe no S3? |
|---|---|---|
| Gesto com IMU | poucos a dezenas de KB | Sim, com folga |
| Palavra-chave de voz | dezenas de KB | Sim |
| Pessoa sim/não, 96×96 cinza | algumas centenas de KB | Sim, melhor com PSRAM |
| YOLO26n, 640 px | alguns MB | Não: use o Pi 5 |
Por que aprender
Quando um projeto de TinyML falha, quase sempre é por memória: o modelo não cabe na flash ou a arena não cabe na RAM. Entender que int8 é o formato padrão e conhecer a ordem de grandeza de cada tarefa deixa você escolher a placa e o modelo antes de gastar uma tarde compilando.
Conceitos-chave
🧪 Edge Impulse: do dado ao sketch Arduino
O Edge Impulse é uma plataforma web com plano gratuito para projetos pessoais. Você manda os dados do sensor, rotula, escolhe os blocos de processamento e de rede neural com cliques, treina na nuvem e baixa o resultado como uma biblioteca Arduino em .zip, já quantizada. Você não escreve a rede; escreve só o sketch que lê o sensor e chama o classificador.
Como ler: as cinco caixas são as abas do projeto no Edge Impulse, na ordem em que você passa por elas. A volta ciano é o que acontece na prática: o primeiro modelo quase sempre confunde duas classes, e a solução é coletar mais dados, não mexer na rede.
Passo a passo
Crie o projeto e instale a ferramenta
Crie uma conta em edgeimpulse.com e um projeto novo. No PC, instale o Node.js e depois a CLI oficial com npm install -g edge-impulse-cli (confira a instrução atual na documentação).
Mande os dados pela serial
Grave no ESP32-S3 um sketch que imprime as leituras separadas por vírgula (o do tópico 5). Rode edge-impulse-data-forwarder, faça login e dê nome aos eixos. A placa aparece na aba de coleta.
Desenhe o impulso e treine
Escolha o tamanho da janela, um bloco de processamento (para IMU, "Spectral Analysis") e um classificador. Treine, olhe a matriz de confusão e rode o teste com os dados separados para isso.
Exporte e instale a biblioteca
Em "Deployment", escolha "Arduino library" com otimização int8 e baixe o .zip. Na Arduino IDE 2.x: Sketch → Incluir biblioteca → Adicionar biblioteca .ZIP. O header gerado tem o nome do seu projeto: <seu_projeto>_inferencing.h.
💡 Primeira compilação demora
A biblioteca do Edge Impulse traz o motor de inferência inteiro em código-fonte, então a primeira compilação no ESP32 leva vários minutos. É normal. As seguintes aproveitam o cache da IDE e ficam bem mais rápidas.
Por que aprender
É o caminho mais curto entre "tenho um sensor" e "tenho um classificador rodando na placa". Para sala de aula, os alunos coletam os próprios dados e veem na hora o efeito de dado ruim ou de classe desbalanceada, que é a lição mais importante de qualquer projeto de IA.
Conceitos-chave
⚙️ TensorFlow Lite Micro / LiteRT no ESP32-S3
Por baixo de muitos projetos de TinyML está o TensorFlow Lite Micro (TFLM), o motor de inferência do Google para microcontroladores. Em 2024 o TensorFlow Lite passou a se chamar LiteRT; você vai ver os dois nomes. A Espressif mantém o componente esp-tflite-micro para o ESP-IDF, com exemplos prontos (hello_world, micro_speech, person_detection) e as otimizações ESP-NN, que usam as instruções vetoriais do S3 para acelerar as camadas da rede.
Como ler: à esquerda está o que você prepara uma vez (o modelo e a lista de camadas). No centro, o interpretador faz as contas numa área fixa da RAM. À direita, o ciclo de cada inferência: escrever a entrada, chamar Invoke(), ler a saída. O tracejado embaixo indica a aceleração do ESP-NN, que entra sozinha no S3.
Os cinco passos de todo programa com TFLM
tflite::GetModel(g_model) carrega o modelo que foi convertido em vetor C (com xxd -i).
MicroMutableOpResolver registra só as camadas usadas (convolução, totalmente conectada, softmax), economizando flash.
MicroInterpreter recebe modelo, resolvedor e a arena; AllocateTensors() avisa se a arena for pequena.
Você preenche interpreter.input(0) com os dados já convertidos para int8 e chama Invoke().
Lê interpreter.output(0): uma nota por classe. A maior é a resposta.
Objetivo: compilar e gravar o exemplo oficial hello_world (uma rede que aprende a curva seno) no ESP32-S3. Exige o ESP-IDF 5.x instalado e o terminal do ESP-IDF aberto.
# 1. baixar o componente com os exemplos
git clone https://github.com/espressif/esp-tflite-micro.git
cd esp-tflite-micro/examples/hello_world
# 2. escolher o chip e compilar
idf.py set-target esp32s3
idf.py build
# 3. gravar e abrir o monitor (troque a porta pela sua)
idf.py -p /dev/ttyACM0 flash monitor
# no Windows a porta é algo como COM5; para sair do monitor: Ctrl+]
Como verificar
- ✓O monitor mostra linhas com um valor x e o y previsto, subindo e descendo como uma onda seno.
- ✓Funcionou? Repita com examples/person_detection; ele precisa de uma placa com câmera.
- ✗"idf.py: command not found": o terminal não carregou o ambiente do ESP-IDF. Abra o "ESP-IDF Terminal" ou rode o script export do ESP-IDF.
Por que aprender
O Edge Impulse resolve a maioria dos casos, mas quando você treina a sua própria rede no TensorFlow ou quer controlar cada byte de memória, o TFLM é a ferramenta. E saber como o interpretador funciona explica as mensagens de erro de qualquer biblioteca de TinyML, inclusive a do Edge Impulse.
Conceitos-chave
👋 Exemplo: reconhecer um gesto com a IMU
Use a MPU-6050 do módulo 2.2 presa ao ESP32-S3 e ensine três classes: parado, chacoalhar e círculo. O modelo não olha uma leitura isolada, e sim uma janela: por exemplo 2 segundos de aceleração nos três eixos, lidos 100 vezes por segundo. São dois sketches: um para coletar, outro para classificar.
Como ler: as três linhas são os eixos do acelerômetro. O retângulo é a janela que o modelo enxerga de uma vez. À direita, a saída: uma nota de 0 a 1 por classe, somando 1. Só aceite a resposta se a maior nota passar de um limite (0,8 no sketch).
Objetivo (coleta): imprimir a aceleração em m/s², 100 vezes por segundo, no formato que o edge-impulse-data-forwarder entende (valores separados por vírgula, uma linha por leitura).
// Coleta da MPU-6050 para o Edge Impulse (ESP32-S3, pacote esp32 3.x)
// Saída: "ax,ay,az" em m/s², 100 linhas por segundo, 115200 baud.
#include <Wire.h>
const int PINO_SDA = 8; // troque pelos pinos I2C da sua placa
const int PINO_SCL = 9;
const uint8_t MPU = 0x68; // endereço da MPU-6050 (AD0 no GND)
const unsigned long PERIODO_US = 10000; // 10 ms = 100 Hz
unsigned long proxima = 0;
void escreveRegistro(uint8_t reg, uint8_t valor) {
Wire.beginTransmission(MPU); Wire.write(reg); Wire.write(valor); Wire.endTransmission();
}
void setup() {
Serial.begin(115200);
Wire.begin(PINO_SDA, PINO_SCL);
Wire.setClock(400000);
escreveRegistro(0x6B, 0x00); // acorda o sensor
escreveRegistro(0x1C, 0x00); // faixa de ±2 g: 16384 por g
proxima = micros();
}
void loop() {
if ((long)(micros() - proxima) < 0) return; // espera o próximo tique de 10 ms
proxima += PERIODO_US;
Wire.beginTransmission(MPU); Wire.write(0x3B); Wire.endTransmission(false);
Wire.requestFrom(MPU, (uint8_t)6);
if (Wire.available() < 6) return;
int16_t bruto[3];
for (int i = 0; i < 3; i++) { uint8_t alto = Wire.read(), baixo = Wire.read(); bruto[i] = (int16_t)((alto << 8) | baixo); }
const float G = 9.80665f / 16384.0f; // contagens → m/s²
Serial.printf("%.3f,%.3f,%.3f\n", bruto[0] * G, bruto[1] * G, bruto[2] * G);
}
Como verificar
- ✓Parado sobre a mesa, um eixo mostra cerca de 9,8 e os outros perto de 0.
- ✓Feche o Serial Monitor e rode edge-impulse-data-forwarder: ele detecta cerca de 100 Hz e 3 eixos. Grave pelo menos 3 minutos por classe.
- ✗Tudo zero? Confira SDA/SCL e se o sensor responde no endereço 0x68.
Objetivo (classificação): com a biblioteca exportada instalada, encher a janela com leituras no mesmo ritmo do treino, chamar o classificador e acender o LED quando o gesto "chacoalhar" passar de 0,8. Troque o nome do #include pelo header do seu projeto e os rótulos pelos que você criou.
// Classifica gestos com a biblioteca exportada pelo Edge Impulse (ESP32-S3)
#include <Wire.h>
#include <seu_projeto_inferencing.h> // troque pelo header gerado: <seu_projeto>_inferencing.h
const int PINO_SDA = 8, PINO_SCL = 9, PINO_LED = 2;
const uint8_t MPU = 0x68;
const float LIMITE = 0.80f;
static float janela[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE]; // 200 leituras × 3 eixos
void escreveRegistro(uint8_t reg, uint8_t valor) {
Wire.beginTransmission(MPU); Wire.write(reg); Wire.write(valor); Wire.endTransmission();
}
void leAceleracao(float *a) {
Wire.beginTransmission(MPU); Wire.write(0x3B); Wire.endTransmission(false);
Wire.requestFrom(MPU, (uint8_t)6);
const float G = 9.80665f / 16384.0f;
for (int i = 0; i < 3; i++) { uint8_t alto = Wire.read(), baixo = Wire.read(); a[i] = (int16_t)((alto << 8) | baixo) * G; }
}
void setup() {
Serial.begin(115200);
pinMode(PINO_LED, OUTPUT);
Wire.begin(PINO_SDA, PINO_SCL); Wire.setClock(400000);
escreveRegistro(0x6B, 0x00); escreveRegistro(0x1C, 0x00);
Serial.printf("Modelo pronto: %d classes, janela de %d valores\n", EI_CLASSIFIER_LABEL_COUNT, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
}
void loop() {
// 1) enche a janela no mesmo ritmo usado no treino
for (size_t i = 0; i < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; i += EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME) {
unsigned long t0 = micros();
leAceleracao(&janela[i]);
while (micros() - t0 < (unsigned long)(EI_CLASSIFIER_INTERVAL_MS * 1000)) {}
}
// 2) roda o classificador
signal_t sinal;
numpy::signal_from_buffer(janela, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &sinal);
ei_impulse_result_t resultado = { 0 };
if (run_classifier(&sinal, &resultado, false) != EI_IMPULSE_OK) { Serial.println("erro no classificador"); return; }
// 3) mostra as notas e decide
bool chacoalhou = false;
for (size_t i = 0; i < EI_CLASSIFIER_LABEL_COUNT; i++) {
Serial.printf("%s: %.2f ", resultado.classification[i].label, resultado.classification[i].value);
if (strcmp(resultado.classification[i].label, "chacoalhar") == 0 && resultado.classification[i].value > LIMITE) chacoalhou = true;
}
Serial.printf("| %d ms\n", resultado.timing.dsp + resultado.timing.classification);
digitalWrite(PINO_LED, chacoalhou ? HIGH : LOW);
}
Como verificar
- ✓A cada 2 segundos aparece uma linha com as três notas e o tempo da inferência em milissegundos.
- ✓Chacoalhe a placa: a nota de "chacoalhar" passa de 0,8 e o LED acende.
- ✗Erro de compilação no #include? O nome do header não bate com o da biblioteca instalada.
Por que aprender
O mesmo esquema serve para detectar que o robô capotou, que uma roda está batendo ou que alguém pegou o robô no colo. São reações que precisam ser locais e rápidas, sem esperar o Pi.
Conceitos-chave
🎙️ Exemplo: palavra-chave por voz ou pessoa na câmera
Placas como a XIAO ESP32-S3 Sense trazem microfone e câmera. Com elas você roda os dois exemplos clássicos do esp-tflite-micro: o micro_speech, que reconhece as palavras em inglês "yes" e "no", e o person_detection, que dá uma nota de "tem pessoa" para uma imagem de 96×96 pixels em tons de cinza. Para palavras em português, treine o seu modelo de palavra-chave no Edge Impulse com gravações da sua turma.
Como ler: nos dois fluxos o microcontrolador primeiro encolhe o dado (o som vira um mapa de frequências; a imagem vira 96×96 em cinza) e só depois chama o modelo. Esse pré-processamento é metade do trabalho e precisa ser igual ao usado no treino.
✓ Expectativa certa
- ✓Poucas palavras curtas e bem diferentes entre si ("frente", "para").
- ✓"Tem pessoa?" a um ou dois metros, com boa luz.
- ✓Gravar vozes de várias pessoas e com o ruído do motor ligado.
✗ Expectativa errada
- ✗Transcrever frases: isso é Whisper, no Pi ou no PC (Trilha 5).
- ✗Dizer onde está a pessoa e quem é: o modelo só responde sim ou não.
- ✗Treinar só com a sua voz e esperar que funcione com a turma.
Objetivo: gravar o detector de pessoa oficial numa placa ESP32-S3 com câmera e PSRAM. Os pinos da câmera de cada placa ficam no arquivo de configuração do exemplo; confira no README dele se a sua placa já está listada.
cd esp-tflite-micro/examples/person_detection
idf.py set-target esp32s3
idf.py menuconfig # confira a placa/câmera e se a PSRAM está habilitada
idf.py build
idf.py -p /dev/ttyACM0 flash monitor
Como verificar
- ✓O monitor imprime as notas de "person" e "no person" a cada imagem; aponte a câmera para você e a primeira sobe.
- ✗Erro ao iniciar a câmera? Os pinos configurados não são os da sua placa, ou a PSRAM está desligada.
🌱 Novo aqui?
Um espectrograma é uma "foto" do som: o eixo horizontal é o tempo, o vertical é a frequência e a cor diz a intensidade. Os modelos de palavra-chave tratam esse mapa como se fosse uma imagem pequena. Palavra-chave (keyword spotting) é reconhecer uma palavra de uma lista curta, não entender a fala.
Por que aprender
Um "para" falado que o ESP32 entende sozinho é uma trava de segurança que não depende do Wi-Fi nem do Pi. E um detector de pessoa barato pode acordar o Pi só quando há alguém na frente, economizando bateria. No módulo 3.5 você vai ligar os dois mundos por um fio serial.
Conceitos-chave
✅ Resumo do módulo
Para ir além
- Documentação do Edge Impulse — data forwarder, deployment Arduino
- esp-tflite-micro (Espressif) — exemplos hello_world, micro_speech, person_detection
- LiteRT para microcontroladores (Google) — o TensorFlow Lite Micro
- ESP-IDF: primeiros passos no ESP32-S3 — instalar o ambiente
- XIAO ESP32-S3 Sense (Seeed Studio) — pinos de câmera e microfone
Próximo módulo
3.5 · Raspberry Pi conversa com o ESP32: o Pi decide, o ESP32 executa, ligados por um fio serial com protocolo e parada segura.