Contenido detallado
📍 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.
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
🗜️ 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.
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.
| Tarea | Tamaño típico del modelo (int8) | ¿Cabe en el S3? |
|---|---|---|
| Gesto con IMU | de unos pocos a decenas de KB | Sí, con holgura |
| Palabra clave de voz | decenas de KB | Sí |
| Persona sí/no, 96×96 en escala de grises | algunos cientos de KB | Sí, mejor con PSRAM |
| YOLO26n, 640 px | algunos MB | No: 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
🧪 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.
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
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).
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.
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.
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
⚙️ 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.
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
tflite::GetModel(g_model) carga el modelo que se convirtió en un vector C (con xxd -i).
MicroMutableOpResolver registra solo las capas utilizadas (convolución, totalmente conectada, softmax), lo que ahorra memoria flash.
MicroInterpreter recibe el modelo, el resolvedor y el área; AllocateTensors() avisa si el área es demasiado pequeña.
Llenas interpreter.input(0) con los datos ya convertidos a int8 y llamas a Invoke().
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.
# 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
👋 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.
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).
// 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.
// 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
🎙️ 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.
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.
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
✅ Resumen del módulo
Para profundizar
- Documentación de Edge Impulse — data forwarder, deployment Arduino
- esp-tflite-micro (Espressif) — ejemplos hello_world, micro_speech, person_detection
- LiteRT para microcontroladores (Google) — TensorFlow Lite Micro
- ESP-IDF: primeros pasos en ESP32-S3 — instalar el entorno
- XIAO ESP32-S3 Sense (Seeed Studio) — pines de cámara y micrófono
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.