PTENES
MÓDULO 3.4 · FUNDAMENTO

🧠 IA dentro del microcontrolador

En los módulos anteriores, la IA se ejecutó en Raspberry Pi 5. Aquí baja a un chip que cuesta unos pocos reales y consume unos pocos milivatios: el ESP32-S3. Entenderás qué es TinyML, por qué el modelo debe cuantizarse en int8 y dos formas de hacerlo: Edge Impulse (sin escribir la red) y TensorFlow Lite Micro (control total).

6
Temas
Intermedio
Nivel
Fundamentos
Tipo
ESP32-S3
Placa sugerida
0%
0 de 0 temas leídos · Sección 1 de 6

Contenido detallado

1

📍 Por qué IA dentro del microcontrolador

Un modelo de IA puede ejecutarse en tres lugares: en un servidor en internet, en una computadora dentro del robot (el Pi 5 del módulo 3.3) o en el propio microcontrolador que ya lee los sensores. Cuanto más cerca del sensor, menor es el retraso y el consumo, y ningún dato sale del robot. El costo es el tamaño: en el ESP32-S3 caben modelos de decenas a cientos de kilobytes, que responden preguntas sencillas, como «¿este movimiento es un saludo?» o «¿hay una persona enfrente?».

🌱 ¿Eres nuevo aquí?

Modelo es el archivo con el «conocimiento» aprendido durante el entrenamiento: una red de números llamados pesos. Entrenar es ajustar esos pesos con muchos ejemplos, y eso ocurre en la PC o en la nube. Inferencia es usar el modelo listo para responder sobre un dato nuevo, y eso es todo lo que hace el microcontrolador.

NUBEmodelo enormenecesita internet100–1000 ms PI 5YOLO26, OpenCVunos pocos wattsdecenas de ms ESP32-S3modelo de KBfracción de wattunos pocos ms lejos del sensor junto al sensor menos retraso →

Cómo leer: de izquierda a derecha, el modelo se hace más pequeño y está más cerca del sensor. El recuadro destacado corresponde al tema de este módulo. Los tiempos son órdenes de magnitud, no mediciones de tu placa: mide siempre en tu caso.

✓ Ideal para TinyML

  • ✓Clasificar movimientos con la IMU (gestos, caídas, vibraciones del motor).
  • ✓Una o unas pocas palabras de comando de voz.
  • ✓«Hay una persona o no» en una imagen pequeña en escala de grises.
  • ✓Robot a batería que necesita reaccionar rápido y sin Wi-Fi.

✗ Déjalo para el Pi o la PC

  • ✗Detectar decenas de clases con cuadros, como YOLO26.
  • ✗Entender frases libres o conversar (eso es la Ruta 5).
  • ✗Entrenar el modelo: el entrenamiento nunca se ejecuta en el microcontrolador.
  • ✗Imagen a color en alta resolución, cuadro por cuadro.

Por qué aprender

Saber dónde debe ejecutarse cada modelo evita dos errores costosos: poner un Pi 5 en un proyecto que un ESP32-S3 resolvería con batería, o intentar meter en el microcontrolador una tarea que solo cabe en la computadora. En el robot completo (módulos 3.5 y 3.6), ambos conviven: el Pi ve el mundo y el ESP32 reacciona.

Conceptos clave

Inferencia
usar el modelo listo
Entrenamiento
en la PC o en la nube
Latencia
tiempo hasta la respuesta
Edge AI
IA junto al sensor
2

🗜️ TinyML: modelo pequeño, cuantizado en int8

TinyML es el nombre que se da al aprendizaje automático que se ejecuta en microcontroladores. El ESP32-S3 tiene cerca de 512 KB de RAM interna y algunos megabytes de flash; muchas placas suman una PSRAM externa de 2 a 8 MB. Un modelo entrenado en la PC guarda cada peso como float32 (4 bytes). La cuantización convierte cada peso a int8 (1 byte, de −128 a 127): el modelo queda 4 veces más pequeño y los cálculos con enteros son mucho más rápidos, con una pequeña pérdida de precisión cuando se hace bien.

float32 · 4 bytes 0,4172 cuantizar int8 · 1 byte 53 entero = redondear(valor ÷ escala) + punto cero ej.: escala 0,0079 y punto cero 0 → 0,4172 ÷ 0,0079 ≈ 53 modelo de 400 KB en float32 → cerca de 100 KB en int8

Cómo leer: cada peso deja de ocupar cuatro cajitas (bytes) y pasa a ocupar una. La escala y el punto cero quedan guardados en el modelo para convertirlo de vuelta cuando haga falta. Los números del ejemplo son ilustrativos.

🌱 ¿Eres nuevo aquí?

Flash es la memoria que guarda el programa y el modelo incluso sin energía. RAM es la memoria de trabajo, que se borra al apagar; allí quedan los cálculos intermedios de la inferencia (la «arena»). PSRAM es una RAM extra, más lenta, soldada junto al chip en algunas placas, como la XIAO ESP32-S3 Sense.

TareaTamaño típico del modelo (int8)¿Cabe en el S3?
Gesto con IMUde unos pocos a decenas de KBSí, con holgura
Palabra clave de vozdecenas de KBSí
Persona sí/no, 96×96 en escala de grisesalgunos cientos de KBSí, mejor con PSRAM
YOLO26n, 640 pxalgunos MBNo: usa el Pi 5

Por qué aprender

Cuando falla un proyecto de TinyML, casi siempre es por la memoria: el modelo no cabe en la flash o la arena no cabe en la RAM. Entender que int8 es el formato estándar y conocer el orden de magnitud de cada tarea te permite elegir la placa y el modelo antes de pasar una tarde compilando.

Conceptos clave

TinyML
IA en microcontrolador
Cuantización
float32 → int8
Arena
RAM para los cálculos
PSRAM
RAM extra de la placa
3

🧪 Edge Impulse: del dato al sketch Arduino

O Edge Impulse es una plataforma web con un plan gratuito para proyectos personales. Envías los datos del sensor, los etiquetas, eliges los bloques de procesamiento y de red neuronal con unos clics, entrenas en la nube y descargas el resultado como una biblioteca Arduino en .zip, ya cuantizada. No escribes la red; solo escribes el sketch que lee el sensor y llama al clasificador.

1 recopilarsensor → PC 2 etiquetarnombre de la clase 3 impulsoventana + bloques 4 entrenary probar 5 exportarbiblioteca .zip ¿se equivocó en la prueba? vuelve y recopila más ejemplos de la clase que confundió

Cómo leer: las cinco cajas son las pestañas del proyecto en Edge Impulse, en el orden en que vas pasando por ellas. El ciclo cian es lo que ocurre en la práctica: el primer modelo casi siempre confunde dos clases, y la solución es recopilar más datos, no modificar la red.

Paso a paso

1

Crea el proyecto e instala la herramienta

Crea una cuenta en edgeimpulse.com y un proyecto nuevo. En la PC, instala Node.js y luego la CLI oficial con npm install -g edge-impulse-cli (consulta la instrucción actual en la documentación).

2

Envía los datos por el puerto serial

Carga en el ESP32-S3 un sketch que imprima las lecturas separadas por comas (el del tema 5). Ejecuta edge-impulse-data-forwarder, inicia sesión y ponles nombre a los ejes. La placa aparece en la pestaña de recopilación.

3

Diseña el impulso y entrena

Elige el tamaño de la ventana, un bloque de procesamiento (para IMU, «Spectral Analysis») y un clasificador. Entrena, revisa la matriz de confusión y ejecuta la prueba con los datos separados para eso.

4

Exporta e instala la biblioteca

En «Deployment», elige «Arduino library» con optimización int8 y descarga el .zip. En Arduino IDE 2.x: Sketch → Incluir biblioteca → Añadir biblioteca .ZIP. El header generado lleva el nombre de tu proyecto: <seu_projeto>_inferencing.h.

💡 La primera compilación tarda

La biblioteca de Edge Impulse incluye el motor de inferencia completo en código fuente, así que la primera compilación en el ESP32 tarda varios minutos. Es normal. Las siguientes aprovechan la caché del IDE y son mucho más rápidas.

Por qué aprender

Es el camino más corto entre «tengo un sensor» y «tengo un clasificador ejecutándose en la placa». En el aula, los alumnos recopilan sus propios datos y ven de inmediato el efecto de los datos deficientes o de una clase desequilibrada, que es la lección más importante de cualquier proyecto de IA.

Conceptos clave

Impulso
ventana + bloques
Etiqueta
la respuesta correcta
Data forwarder
serial → Edge Impulse
Matriz de confusión
quién se confunde con quién
4

⚙️ TensorFlow Lite Micro / LiteRT en el ESP32-S3

Detrás de muchos proyectos de TinyML está TensorFlow Lite Micro (TFLM), el motor de inferencia de Google para microcontroladores. En 2024, TensorFlow Lite pasó a llamarse LiteRT; vas a ver ambos nombres. Espressif mantiene el componente esp-tflite-micro para ESP-IDF, con ejemplos listos (hello_world, micro_speech, person_detection) y las optimizaciones ESP-NN, que usan las instrucciones vectoriales del S3 para acelerar las capas de la red.

modelo .tflitevector en C en la flash op resolvercapas usadas intérpreteInvoke()área en la RAM entrada int8 salida: puntuaciones ESP-NN (S3)

Cómo leer: a la izquierda está lo que preparas una vez (el modelo y la lista de capas). En el centro, el intérprete hace los cálculos en un área fija de la RAM. A la derecha, el ciclo de cada inferencia: escribir la entrada, llamar Invoke(), leer la salida. La línea discontinua de abajo indica la aceleración de ESP-NN, que se activa automáticamente en el S3.

Los cinco pasos de todo programa con TFLM

1

tflite::GetModel(g_model) carga el modelo que se convirtió en un vector C (con xxd -i).

2

MicroMutableOpResolver registra solo las capas utilizadas (convolución, totalmente conectada, softmax), lo que ahorra memoria flash.

3

MicroInterpreter recibe el modelo, el resolvedor y el área; AllocateTensors() avisa si el área es demasiado pequeña.

4

Llenas interpreter.input(0) con los datos ya convertidos a int8 y llamas a Invoke().

5

Lee interpreter.output(0): una puntuación por clase. La más alta es la respuesta.

Objetivo: compilar y cargar el ejemplo oficial hello_world (una red que aprende la curva seno) en el ESP32-S3. Requiere tener ESP-IDF 5.x instalado y abrir la terminal de ESP-IDF.

terminal (ESP-IDF)
# 1. descargar el componente con los ejemplos
git clone https://github.com/espressif/esp-tflite-micro.git
cd esp-tflite-micro/examples/hello_world

# 2. elegir el chip y compilar
idf.py set-target esp32s3
idf.py build

# 3. cargar y abrir el monitor (cambia el puerto por el tuyo)
idf.py -p /dev/ttyACM0 flash monitor
# en Windows el puerto es algo como COM5; para salir del monitor: Ctrl+]

Cómo verificar

  • ✓El monitor muestra líneas con un valor x y el valor y previsto, que suben y bajan como una onda seno.
  • ✓¿Funcionó? Repite el proceso con examples/person_detection; necesita una placa con cámara.
  • ✗"idf.py: command not found": la terminal no cargó el entorno de ESP-IDF. Abre "ESP-IDF Terminal" o ejecuta el script export de ESP-IDF.

Por qué aprender

Edge Impulse resuelve la mayoría de los casos, pero cuando entrenas tu propia red en TensorFlow o quieres controlar cada byte de memoria, TFLM es la herramienta indicada. Y saber cómo funciona el intérprete te ayuda a entender los mensajes de error de cualquier biblioteca de TinyML, incluida la de Edge Impulse.

Conceptos clave

LiteRT
nuevo nombre de TF Lite
esp-tflite-micro
componente de ESP-IDF
Invoke()
ejecuta una inferencia
ESP-NN
aceleración en el S3
5

👋 Ejemplo: reconocer un gesto con la IMU

Usa la MPU-6050 del módulo 2.2 conectada al ESP32-S3 y enséñale tres clases: detenido, sacudir e círculo. El modelo no observa una lectura aislada, sino una ventana: por ejemplo, 2 segundos de aceleración en los tres ejes, leídos 100 veces por segundo. Son dos sketches: uno para recopilar datos y otro para clasificar.

axayaz ventana: 2 s × 100 Hz = 200 × 3 puntuaciones quieto 0,03 sacudir 0,91 círculo 0,06 la ventana avanza en el tiempo y el clasificador vuelve a ejecutarse

Cómo leer: las tres líneas son los ejes del acelerómetro. El rectángulo es la ventana que el modelo observa de una vez. A la derecha está la salida: una puntuación de 0 a 1 por clase, cuya suma es 1. Acepta la respuesta solo si la puntuación más alta supera un límite (0,8 en el sketch).

Objetivo (recopilación de datos): imprimir la aceleración en m/s², 100 veces por segundo, en el formato que entiende edge-impulse-data-forwarder (valores separados por comas, una línea por lectura).

coleta-imu.ino
// Recopilación de datos de la MPU-6050 para Edge Impulse (ESP32-S3, paquete esp32 3.x)
// Salida: "ax,ay,az" en m/s², 100 líneas por segundo, 115200 baud.
#include <Wire.h>

const int PINO_SDA = 8;           // cambia por los pines I2C de tu placa
const int PINO_SCL = 9;
const uint8_t MPU = 0x68;        // dirección de la MPU-6050 (AD0 en 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);      // activa el sensor
  escreveRegistro(0x1C, 0x00);      // rango de ±2 g: 16384 por g
  proxima = micros();
}

void loop() {
  if ((long)(micros() - proxima) < 0) return;  // espera el siguiente intervalo 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;  // cuentas → m/s²
  Serial.printf("%.3f,%.3f,%.3f\n", bruto[0] * G, bruto[1] * G, bruto[2] * G);
}

Cómo verificar

  • ✓En reposo sobre la mesa, un eje muestra cerca de 9,8 y los otros, cerca de 0.
  • ✓Cierra el Monitor Serial y ejecuta edge-impulse-data-forwarder: detecta cerca de 100 Hz y 3 ejes. Graba al menos 3 minutos por clase.
  • ✗¿Todo en cero? Revisa SDA/SCL y comprueba si el sensor responde en la dirección 0x68.

Objetivo (clasificación): con la biblioteca exportada instalada, llenar la ventana con lecturas al mismo ritmo que durante el entrenamiento, llamar al clasificador y encender el LED cuando el gesto "sacudir" supere 0,8. Cambia el nombre de #include por el header de tu proyecto y las etiquetas por las que creaste.

gesto-imu.ino
// Clasifica gestos con la biblioteca exportada por Edge Impulse (ESP32-S3)
#include <Wire.h>
#include <seu_projeto_inferencing.h>  // cambia por el header generado: <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 lecturas × 3 ejes

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 listo: %d clases, ventana de %d valores\n", EI_CLASSIFIER_LABEL_COUNT, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
}

void loop() {
  // 1) llena la ventana al mismo ritmo que se usó en el entrenamiento
  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) ejecuta el clasificador
  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("error en el clasificador"); return; }
  // 3) muestra las puntuaciones y 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);
}

Cómo verificar

  • ✓Cada 2 segundos aparece una línea con las tres puntuaciones y el tiempo de inferencia en milisegundos.
  • ✓Sacude la placa: la puntuación de "sacudir" supera 0,8 y el LED se enciende.
  • ✗¿Error de compilación en #include? El nombre del header no coincide con el de la biblioteca instalada.

Por qué aprender

El mismo esquema sirve para detectar si el robot se volcó, si una rueda está golpeando o si alguien levantó el robot. Son reacciones que deben ser locales y rápidas, sin esperar al Pi.

Conceptos clave

Ventana
fragmento de tiempo analizado
Frecuencia fija
igual durante el entrenamiento y en la placa
run_classifier
llama al modelo
Límite
solo acepta una puntuación alta
6

🎙️ Ejemplo: palabra clave por voz o persona en la cámara

Placas como la XIAO ESP32-S3 Sense incluyen micrófono y cámara. Con ellas puedes ejecutar los dos ejemplos clásicos de esp-tflite-micro: el micro_speech, que reconoce las palabras en inglés "yes" y "no", y el person_detectionque asigna una puntuación de «hay una persona» a una imagen de 96×96 píxeles en escala de grises. Para palabras en portugués, entrena tu modelo de palabra clave en Edge Impulse con grabaciones de tu grupo.

VOZ sí · nosilencio · otra 1 s de audioespectrograma CÁMARA cuadro QVGA 96×96 en gris persona 0,87sin persona 0,13

Cómo leer: en ambos flujos, el microcontrolador primero reduce el dato (el sonido se convierte en un mapa de frecuencias; la imagen se convierte en una imagen de 96×96 en gris) y solo después llama al modelo. Este preprocesamiento es la mitad del trabajo y debe ser igual al que se usó durante el entrenamiento.

✓ Expectativa correcta

  • ✓Pocas palabras cortas y bien diferenciadas entre sí («frente», «para»).
  • ✓«¿Hay una persona?» a uno o dos metros, con buena luz.
  • ✓Graba voces de varias personas y con el ruido del motor encendido.

✗ Expectativa incorrecta

  • ✗Transcribir frases: eso es Whisper, en la Pi o en la PC (Ruta 5).
  • ✗Decir dónde está la persona y quién es: el modelo solo responde sí o no.
  • ✗Entrenar solo con tu voz y esperar que funcione con el grupo.

Objetivo: graba el detector de personas oficial en una placa ESP32-S3 con cámara y PSRAM. Los pines de la cámara de cada placa están en el archivo de configuración del ejemplo; revisa su README para comprobar si tu placa ya aparece en la lista.

terminal (ESP-IDF)
cd esp-tflite-micro/examples/person_detection
idf.py set-target esp32s3
idf.py menuconfig      # comprueba la placa/cámara y si la PSRAM está habilitada
idf.py build
idf.py -p /dev/ttyACM0 flash monitor

Cómo verificar

  • ✓El monitor imprime las puntuaciones de «person» y «no person» con cada imagen; apunta la cámara hacia ti y la primera sube.
  • ✗¿Error al iniciar la cámara? Los pines configurados no son los de tu placa o la PSRAM está desactivada.

🌱 ¿Eres nuevo aquí?

Un espectrograma es una «foto» del sonido: el eje horizontal representa el tiempo, el vertical, la frecuencia, y el color indica la intensidad. Los modelos de palabra clave tratan este mapa como si fuera una imagen pequeña. Palabra clave (keyword spotting) consiste en reconocer una palabra de una lista corta, no en entender el habla.

Por qué aprender

Un «para» que el ESP32 entiende por sí solo es un bloqueo de seguridad que no depende del Wi-Fi ni de la Pi. Y un detector de personas económico puede activar la Pi solo cuando hay alguien enfrente, para ahorrar batería. En el módulo 3.5 conectarás ambos mundos con un cable serial.

Conceptos clave

Palabra clave
lista corta de palabras
Espectrograma
sonido como imagen
person_detection
persona sí o no
Preprocesamiento
igual al del entrenamiento

✅ Resumen del módulo

✓
IA junto al sensor: menos retraso, menos energía y nada sale del robot; el costo es un modelo pequeño.
✓
int8 es el estándar: cuantizar hace que el modelo sea 4 veces más pequeño y más rápido.
✓
Edge Impulse: recopilar, etiquetar, entrenar y exportar como biblioteca Arduino.
✓
TFLM / LiteRT: modelo, resolvedor, intérprete, arena e Invoke(); esp-tflite-micro en el S3.
✓
Expectativa correcta: los gestos, unas pocas palabras y «¿hay una persona?» son posibles; la detección completa se hace en la Pi.

Para profundizar

Siguiente módulo

3.5 · La Raspberry Pi se comunica con el ESP32: la Pi decide, el ESP32 ejecuta; se conectan por un cable serial con protocolo y parada segura.