PTENES
MÓDULO 5.1 · FUNDAMENTO

🧠 El modelo de lenguaje como cerebro

Un modelo de lenguaje entiende pedidos como «lleva la pinza hasta el vaso», pero se equivoca, inventa y no percibe el mundo. En este módulo montas una arquitectura que aprovecha lo que hace bien sin dejarle el control del motor: el modelo elige, el robot ejecuta, dentro de una lista cerrada de acciones con límites comprobados en el código.

6
Temas
Avanzado
Nivel
Fundamentos
Tipo
PC o Pi 5
Hardware
0%
0 de 0 temas leídos · Sección 1 de 6

Contenido detallado

1

🧩 LLM, VLM y VLA: qué hace cada uno en el robot

En todo proyecto de robot con IA aparecen tres siglas, y cada una ocupa un lugar diferente. El LLM (modelo de lenguaje grande) recibe texto y devuelve texto: entiende el pedido y decide qué acción tiene sentido. El VLM (modelo de visión y lenguaje) recibe una imagen junto con el texto y responde sobre ella: «hay un vaso azul a la izquierda». El VLA (visión, lenguaje y acción) va un paso más allá: recibe una imagen y una instrucción, y devuelve el movimiento de las articulaciones, varias veces por segundo.

🌱 ¿Eres nuevo aquí?

Un modelo es un archivo de números (los parámetros, o pesos) aprendidos a partir de muchos ejemplos. Ejecutar el modelo para obtener una respuesta se llama inferencia. El texto entra y sale en fragmentos llamados tokens (partes de palabras). En general, cuantos más parámetros, mejor es la respuesta y más memoria y tiempo requiere la inferencia.

entramodelosale textoLLMtexto oacción elegidaimagen+ texto VLM«vaso azula la izquierda»imagen+ instrucciónVLA ángulos de las articulacionesvarias veces/s de arriba hacia abajo: el modelo se acerca cada vez más al motor

Cómo leer: cada fila es un tipo de modelo. A la izquierda (cian) está lo que recibe; a la derecha, lo que devuelve. El LLM y el VLM se detienen en palabras o en una elección; el VLA, con brillo, devuelve movimiento directo. Este módulo usa los dos primeros como planificadores; el VLA aparece en el módulo 5.4, con SmolVLA.

💬 LLM

Traduce «avanza un poco» a avanzar 20 cm. Sirve para entender pedidos, pero no para medir distancias.

👁️ VLM

Describe la escena de la cámara. Ayuda al LLM a saber qué hay sobre la mesa, pero puede equivocarse con el color, la cantidad y la posición.

🦾 VLA

Entrenado con demostraciones del propio brazo. Controla las articulaciones, por eso necesita datos, pruebas y límites aún más estrictos.

Por qué aprender

Saber qué hace cada modelo evita el error más común de quienes empiezan: pedirle a un modelo de texto que controle un motor en tiempo real. Cada tipo tiene un lugar en la estructura del robot, y elegir el lugar correcto es la mitad de la seguridad.

Conceptos clave

LLM
texto → texto
VLM
imagen + texto → texto
VLA
imagen + texto → movimiento
Inferencia
ejecutar el modelo
2

🛡️ Arquitectura segura: el modelo elige, el robot ejecuta

El modelo de lenguaje se queda en el nivel alto: lee el pedido y elige una acción. Todo lo que viene después es código común, que tú escribiste y probaste. Un validador en la Raspberry Pi o en la PC comprueba si la acción existe y si los números están dentro de los límites. Solo entonces una línea de texto se envía por el puerto serial al ESP32, que tiene sus propios límites y la parada de emergencia. Si el modelo inventa algo, la respuesta se detiene en el validador.

puede equivocarsecódigo probado por tisolicitud modelovalidadorserialESP32 JSONAVANZAR 20límitesde nuevo rechaza y explicaALTOacción fuera de la listao fuera del límite

Cómo leer: el recuadro cian con borde discontinuo es la parte que puede equivocarse (el modelo); el recuadro verde azulado es código determinista, que siempre hace lo mismo. La flecha roja discontinua muestra la ruta del rechazo. El botón ALTO actúa directamente en el ESP32, sin pasar por el modelo ni por la Pi.

✓ Hazlo

  • ✓Deja que el modelo elija entre pocas acciones con nombres claros.
  • ✓Comprueba todo en el código antes de enviarlo por el puerto serial.
  • ✓Repite los límites en el ESP32: es la última barrera.
  • ✓Registra cada pedido, respuesta y decisión en un log.

✗ No lo hagas

  • ✗Dejar que el modelo genere código y lo ejecute sin revisión.
  • ✗Enviar el texto del modelo directamente por el puerto serial.
  • ✗Pedirle al modelo valores de PWM o ángulos de motor sin procesar.
  • ✗Depender del modelo para detener el robot en una emergencia.

Por qué aprender

A veces, los modelos de lenguaje responden con seguridad algo incorrecto (a eso se le llama alucinación). En un chat, eso genera una frase inadecuada; en un robot, puede provocar un choque. Separar «elegir» de «ejecutar» te permite cambiar de modelo, local o en la nube, sin modificar lo que garantiza la seguridad.

Conceptos clave

Alto nivel
qué, no cómo
Validador
código que rechaza
Alucinación
error dicho con seguridad
Última barrera
límites en el ESP32
3

📋 Lista cerrada de acciones en JSON (function calling)

En vez de pedir una respuesta libre, le das al modelo un menú: las acciones que el robot sabe realizar, los parámetros de cada una y el formato exacto de la respuesta. Esta idea se conoce como function calling (llamada a función) o tool use. Ollama acepta un JSON Schema en el campo format y fuerza la respuesta a seguir ese molde; el campo enum enumera los únicos nombres de acción permitidos.

🌱 ¿Eres nuevo aquí?

JSON es un formato de texto para datos, con claves y valores: {"acao": "girar", "parametros": {"graus": 90}}. Un JSON Schema es la regla que indica qué claves existen y de qué tipo es cada valor. El schema ayuda al modelo a acertar el formato, pero no garantiza que el valor sea seguro: 9000 grados sigue siendo un número válido. Por eso, el validador comprueba los límites después.

lista cerradaavanzarde 5 a 100 cm girar−180 a 180°pinzaabrir | cerrardetenersesin parámetrosolo existen estas avanzar · cm: 30✓ envía ANDAR 30girar · grados: 900✗ fuera del límite saltar · altura: 1✗ la acción no existe

Cómo leer: A la izquierda está el menú que escribes en el código. A la derecha, tres posibles respuestas del modelo. Solo la primera se convierte en comando; el validador rechaza las rojas y el robot se queda quieto y explica el motivo.

Objetivo: ver cómo queda una respuesta válida del modelo. Este es el formato que exige el script del tema 4; fíjate en que motivo es solo texto para el registro y nunca se ejecuta.

respuesta-ejemplo.json
{
  "acao": "girar",
  "parametros": { "graus": -90 },
  "motivo": "el usuario pidió girar a la izquierda"
}

Cómo verificar: pega el bloque en un validador de JSON (o ejecuta python3 -m json.tool resposta-exemplo.json); debe aceptarlo sin errores. Cambia -90 por "izquierda" y verás que deja de ser un número: el validador del tema 4 lo rechazaría.

Por qué aprender

La lista cerrada transforma un problema abierto («¿qué responderá el modelo?») en uno cerrado y comprobable («¿cuál de las cuatro acciones eligió y el número está dentro del límite?»). Es el mismo principio que usan los asistentes comerciales para llamar herramientas, aplicado al robot.

Conceptos clave

Function calling
el modelo elige una herramienta
JSON Schema
molde de la respuesta
enum
solo estos nombres
Límite
mínimo y máximo
4

💻 Ejecutar un modelo local (Ollama en el Pi 5 o en la PC)

O Ollama descarga modelos abiertos y los ejecuta en tu máquina mediante una API HTTP en http://localhost:11434. Tu script se comunica con él como lo haría con un servicio en internet, pero nada sale de la computadora. Los modelos en la nube también sirven para esta arquitectura (el validador es el mismo); el curso muestra la opción local porque funciona sin internet, no tiene costo por solicitud y no envía fuera la voz ni las imágenes de tu casa.

tu PC, Pi 5 o Jetson scriptPythonOllamapuerto 11434modelo en la RAMESP32motores HTTPinternet:no hace falta USB

Cómo leer: Todo lo que está dentro del marco teal se ejecuta en tu máquina. Las dos flechas entre el script y Ollama representan la pregunta y la respuesta a través de la red local (localhost). El cable cian discontinuo es la conexión serial con el ESP32, la misma del módulo 3.5.

Dónde se ejecutaTamaño de modelo que cabeEn la práctica
Raspberry Pi 5 (8 GB), solo CPUpequeños, de 1 a 3 mil millones de parámetrosunos segundos por respuesta corta
Pi 5 + AI HAT+ 2LLM y VLM pequeños en el aceleradorconsulta los modelos compatibles en la documentación del Pi
Jetson Orin Nano Superpequeños y medianos, con GPUmás rápido que el Pi, más caro
PC con GPU NVIDIAmedianos (de 7 a 14 mil millones) con holgurarespuesta casi inmediata

Paso a paso

1

Instala Ollama

Sigue el instalador de tu sistema en ollama.com/download (en Linux y en el Pi basta con un comando). Inicia un servicio que escucha en el puerto 11434.

2

Elige y descarga un modelo

En ollama.com/library, filtra los modelos compatibles con «tools» y de un tamaño que quepa en tu memoria. Descárgalo con ollama pull <modelo>. Los nombres y las versiones cambian rápido: consulta la lista actualizada.

3

Pruébalo desde la terminal e instala pyserial

Ejecuta los comandos de abajo. Si curl devuelve un JSON con "message", el servidor está listo para el script.

terminal
ollama pull <modelo>          # reemplaza por el nombre elegido
ollama list                     # el modelo debe aparecer aquí
curl http://localhost:11434/api/chat -d '{"model": "<modelo>", "stream": false,
  "messages": [{"role": "user", "content": "Responda só: ok"}]}'
python3 -m venv ~/robo-ia && source ~/robo-ia/bin/activate
pip install pyserial

Objetivo: el script completo de la arquitectura segura. Envía la solicitud y el schema al modelo local, valida la respuesta (acción incluida en la lista, parámetros dentro de los límites, nada más), pide confirmación para movimientos grandes y solo entonces envía una línea de texto al ESP32. Con --simular, imprime la línea en vez de enviarla, para que puedas probar sin hardware.

cerebro_llm.py
#!/usr/bin/env python3
"""Cérebro com modelo local: o modelo ESCOLHE uma ação da lista fechada,
o código VALIDA e só então manda uma linha de texto ao ESP32 pela serial.
Uso: python3 cerebro_llm.py            (com ESP32)
     python3 cerebro_llm.py --simular  (sem hardware: só imprime)"""
import json, sys, time, urllib.request, urllib.error

OLLAMA_URL = "http://localhost:11434/api/chat"
MODELO = "<modelo>"          # reemplaza por el nombre que aparece en: ollama list
PORTA = "/dev/ttyUSB0"       # USB: /dev/ttyUSB0 o /dev/ttyACM0 · UART de Pi: /dev/serial0 · Windows: COM3
BAUD = 115200
CONFIRMAR_ACIMA_CM = 50        # para avanzar más que esto, hay que presionar "s" en el teclado

# Lista cerrada: acción -> {parámetro: (mínimo, máximo)} o {parámetro: [valores permitidos]}
ACOES = {
    "andar": {"cm": (5, 100)},
    "girar": {"graus": (-180, 180)},
    "garra": {"estado": ["abrir", "fechar"]},
    "parar": {},
}

ESQUEMA = {
    "type": "object",
    "properties": {
        "acao": {"type": "string", "enum": list(ACOES)},
        "parametros": {"type": "object"},
        "motivo": {"type": "string"},
    },
    "required": ["acao", "parametros"],
}

SISTEMA = (
    "Controlas un robot pequeño. Elige UNA acción y responde solo en JSON.\n"
    "Acciones: andar {cm: 5 a 100, no existe el valor negativo}; girar {grados: -180 a 180, "
    "negativo = izquierda}; garra {estado: abrir o cerrar}; parar {}.\n"
    "Si el pedido no corresponde a ninguna acción o es peligroso, usa parar y explícalo en el motivo."
)

def perguntar_modelo(pedido):
    """Envía el pedido a Ollama y devuelve el diccionario de la respuesta (o genera un error)."""
    corpo = {
        "model": MODELO,
        "messages": [{"role": "system", "content": SISTEMA},
                     {"role": "user", "content": pedido}],
        "stream": False,           # una respuesta completa, no fragmentos
        "format": ESQUEMA,          # fuerza el formato JSON
        "options": {"temperature": 0},
    }
    req = urllib.request.Request(OLLAMA_URL, data=json.dumps(corpo).encode(),
                                 headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=120) as r:
        dados = json.loads(r.read())
    return json.loads(dados["message"]["content"])

def validar(resp):
    """Devuelve (ok, línea_o_motivo, necesita_confirmar). Nunca confíes en el modelo."""
    if not isinstance(resp, dict):
        return False, "la respuesta no es un objeto JSON", False
    acao = resp.get("acao")
    if acao not in ACOES:
        return False, f"la acción '{acao}' no está en la lista", False
    params = resp.get("parametros") or {}
    if not isinstance(params, dict):
        return False, "los parámetros no son un objeto", False
    sobra = set(params) - set(ACOES[acao])
    if sobra:
        return False, f"parámetro desconocido: {', '.join(sorted(sobra))}", False
    valores = []
    for nome, regra in ACOES[acao].items():
        if nome not in params:
            return False, f"falta el parámetro '{nome}'", False
        v = params[nome]
        if isinstance(regra, list):                      # valores permitidos
            if v not in regra:
                return False, f"'{v}' no está permitido para {nome}", False
            valores.append(str(v).upper())
        else:                                            # rango numérico
            if isinstance(v, bool) or not isinstance(v, (int, float)):
                return False, f"{nome} debe ser un número", False
            minimo, maximo = regra
            if not (minimo <= v <= maximo):
                return False, f"{nome}={v} fuera del límite ({minimo} a {maximo})", False
            valores.append(str(int(round(v))))
    linha = " ".join([acao.upper()] + valores)         # ej.: ANDAR 30 · GARRA ABRIR · PARAR
    confirmar = acao == "andar" and params["cm"] > CONFIRMAR_ACIMA_CM
    return True, linha, confirmar

def abrir_serial(simular):
    if simular:
        return None
    import serial                                       # pip install pyserial
    s = serial.Serial(PORTA, BAUD, timeout=2)
    time.sleep(2)                                       # abrir el puerto reinicia el ESP32: espera
    s.reset_input_buffer()
    return s

def enviar(s, linha):
    if s is None:
        return f"(simulado) {linha}"
    s.write((linha + "\n").encode())
    return s.readline().decode(errors="replace").strip() or "(sin respuesta del ESP32)"

def main():
    simular = "--simular" in sys.argv
    s = abrir_serial(simular)
    print("Cerebro listo. Escribe un pedido (o 'salir').")
    while True:
        pedido = input("Tú: ").strip()
        if pedido.lower() in ("sair", "exit"):
            break
        if not pedido:
            continue
        try:
            resp = perguntar_modelo(pedido)
        except urllib.error.URLError as e:
            print(f"Ollama no respondió ({e}). ¿Está en ejecución? Prueba: ollama list"); continue
        except (json.JSONDecodeError, KeyError) as e:
            print(f"Rechazado: respuesta ilegible ({e})."); continue
        print("Modelo:", json.dumps(resp, ensure_ascii=False))
        ok, linha, confirmar = validar(resp)
        if not ok:
            print("Recusado:", linha); continue
        if confirmar and input(f"¿Confirmas '{linha}'? (s/n) ").strip().lower() != "s":
            print("Cancelado por ti."); continue
        print("ESP32:", enviar(s, linha))
    if s:
        s.write(b"PARAR\n"); s.close()

if __name__ == "__main__":
    main()

Cómo verificar

  • ✓Con --simular, "avanza treinta centímetros" muestra el JSON del modelo y "ESP32: (simulado) ANDAR 30".
  • ✓"avanza dos metros" se rechaza (cm fuera del límite) o se convierte en detenerse; "avanza 80 cm" pide confirmación antes.
  • ✓"haz un café" termina en rechazo o en PARAR, nunca en una acción inventada.
  • ✗¿"Ollama no respondió"? El servicio está detenido o el nombre del modelo no coincide con ollama list.

💡 Prueba el validador sin modelo

En el intérprete de Python, ejecuta from cerebro_llm import validar y llama a validar({"acao": "girar", "parametros": {"graus": 900}}). Escribe unos diez casos inválidos (acción inexistente, texto en lugar de un número, parámetro sobrante). Si todos se rechazan, tu robot está protegido contra cualquier modelo que conectes después.

Por qué aprender

Con el modelo local puedes probar todo lo que quieras, sin límite de uso ni cuenta, y el robot sigue funcionando sin Wi-Fi. Además, como el validador está en tu código, cambiar Ollama por un modelo en la nube solo requiere cambiar la función perguntar_modelo.

Conceptos clave

Ollama
servidor local de modelos
localhost:11434
la API en tu máquina
temperature 0
respuesta más predecible
--simular
probar sin hardware
5

👁️ VLM: "¿qué ves?" a través de la cámara

Un VLM recibe una foto y una pregunta. En Ollama, la imagen se envía en base64 en el campo images del mensaje. El uso seguro es el mismo de antes: el VLM describe, y esa descripción se incluye como contexto en el pedido al LLM (o en el mismo modelo, si acepta imágenes y herramientas). No controla ningún motor. Para obtener una posición exacta, el detector del módulo 3.3 sigue siendo más confiable; el VLM ayuda cuando la pregunta es abierta: "¿hay algo en el camino?", "¿qué objeto es rojo?".

Objetivo: tomar una foto con la cámara USB (o con la Camera Module 3 a través de OpenCV) y preguntarle al VLM local qué hay en la escena. Necesitas un modelo con visión: en la biblioteca de Ollama, filtra por "vision".

o_que_ve.py
#!/usr/bin/env python3
"""Toma una foto y pregunta al VLM local qué hay en ella. pip install opencv-python"""
import base64, json, sys, urllib.request
import cv2

MODELO_VISAO = "<modelo-de-visao>"   # un modelo con 'vision' en ollama.com/library
PERGUNTA = sys.argv[1] if len(sys.argv) > 1 else "Enumera los objetos sobre la mesa, con su color y posición (izquierda, centro, derecha)."

cam = cv2.VideoCapture(0)                # 0 = primera cámara
for _ in range(5):                       # descarta los cuadros oscuros del inicio
    ok, quadro = cam.read()
cam.release()
if not ok:
    sys.exit("No se pudo abrir la cámara: comprueba el índice (0, 1...) y el cable.")
quadro = cv2.resize(quadro, (640, 480))   # imagen más pequeña = respuesta más rápida
cv2.imwrite("foto.jpg", quadro)
_, jpg = cv2.imencode(".jpg", quadro)

corpo = {
    "model": MODELO_VISAO, "stream": False, "options": {"temperature": 0},
    "messages": [{"role": "user", "content": PERGUNTA + " Responde en español, en hasta 3 frases.",
                  "images": [base64.b64encode(jpg.tobytes()).decode()]}],
}
req = urllib.request.Request("http://localhost:11434/api/chat", data=json.dumps(corpo).encode(),
                             headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=180) as r:
    print(json.loads(r.read())["message"]["content"])

Cómo verificar: el archivo foto.jpg aparece en la carpeta y la terminal muestra una descripción breve de la escena. Pon dos objetos de distintos colores sobre la mesa y cámbialos de lado: la descripción debe reflejarlo. Si se equivoca, acabas de ver por qué el VLM no puede controlar el motor.

✓ Buen uso del VLM

  • ✓Dar contexto al LLM: "en la mesa hay un vaso azul y una pelota".
  • ✓Responder preguntas abiertas del usuario sobre la escena.
  • ✓Comprobar después de la acción: "¿la garra está sujetando algo?".

✗ Mal uso del VLM

  • ✗Pedir coordenadas en milímetros y mover el brazo hasta allí.
  • ✗Usarlo como sensor de seguridad ("¿hay alguien cerca?").
  • ✗Llamarlo en cada cuadro de video: es demasiado lento para eso.

Por qué aprender

Combinar visión y lenguaje permite que el robot responda a pedidos que mencionan el mundo ("agarra el objeto rojo"). Saber en qué destaca el VLM y en qué se equivoca te prepara para el VLA del módulo 5.4, que lleva la misma idea hasta el movimiento.

Conceptos clave

images (base64)
foto dentro del JSON
Contexto
descripción que ayuda al LLM
Detector x VLM
posición x significado
Resolución
más pequeña = más rápida
6

🚨 Bloqueos: límites, confirmación y parada de emergencia

La seguridad de un robot con IA se construye en capas, y cada capa desconfía de la anterior. El validador de la Pi es la primera. El ESP32 vuelve a comprobar los límites y rechaza cualquier línea que no entienda. Un watchdog de comandos detiene los motores si no llega ninguna orden en poco tiempo. Y un botón de parada físico detiene el movimiento sin depender de ningún software.

4 · botón de parada (corta la energía de los motores) 3 · watchdog: si no llega un comando nuevo, se detiene2 · el ESP32 vuelve a comprobar los límites 1 · validador en la Pi / PCmotor

Cómo leer: el motor está en el centro; para que se mueva, ninguna de las cuatro capas que lo rodean debe haber bloqueado el comando. La capa roja, por fuera de todo, es la única que funciona incluso si el programa se bloquea: es física. Si falla una capa, la de afuera aún lo detiene.

Objetivo: el lado ESP32 del protocolo de líneas de texto (el mismo del módulo 3.5), con límites propios, respuesta OK/ERRO, watchdog de 2 segundos y un botón de parada en el GPIO 13. Las funciones del motor solo imprimen; reemplázalas por el código de tus motores (Trilha 1).

esp32_executor.ino
// Ejecutor seguro: recibe "ANDAR 30", "GIRAR -90", "GARRA ABRIR", "PARAR" (ESP32, paquete esp32 3.x)
const int PINO_PARADA = 13;              // botón a GND (usa el pull-up interno)
const unsigned long WATCHDOG_MS = 2000;  // sin comando durante 2 s: se detiene
unsigned long ultimoComando = 0;
bool movendo = false;

void pararMotores() { movendo = false; Serial.println("# motores detenidos"); }  // reemplaza por tu driver
void andar(int cm)    { movendo = true; Serial.printf("# avanzando %d cm\n", cm); }
void girar(int graus) { movendo = true; Serial.printf("# girando %d grados\n", graus); }
void garra(bool abrir) { Serial.println(abrir ? "# abriendo la garra" : "# cerrando la garra"); }

void setup() {
  Serial.begin(115200);
  pinMode(PINO_PARADA, INPUT_PULLUP);
  pararMotores();
  Serial.println("PRONTO");
}

void loop() {
  if (digitalRead(PINO_PARADA) == LOW) { pararMotores(); while (digitalRead(PINO_PARADA) == LOW) delay(10); }
  if (movendo && millis() - ultimoComando > WATCHDOG_MS) pararMotores();
  if (!Serial.available()) return;
  String linha = Serial.readStringUntil('\n'); linha.trim();
  if (linha.length() == 0 || linha.length() > 32) { Serial.println("ERROR línea inválida"); return; }
  ultimoComando = millis();
  int esp = linha.indexOf(' ');
  String cmd = esp < 0 ? linha : linha.substring(0, esp);
  String arg = esp < 0 ? "" : linha.substring(esp + 1);
  if (cmd == "PARAR") { pararMotores(); Serial.println("OK PARAR"); }
  else if (cmd == "ANDAR") { int v = arg.toInt(); if (v < 5 || v > 100) Serial.println("ERROR límite"); else { andar(v); Serial.println("OK ANDAR"); } }
  else if (cmd == "GIRAR") { int v = arg.toInt(); if (v < -180 || v > 180) Serial.println("ERROR límite"); else { girar(v); Serial.println("OK GIRAR"); } }
  else if (cmd == "GARRA" && (arg == "ABRIR" || arg == "FECHAR")) { garra(arg == "ABRIR"); Serial.println("OK GARRA"); }
  else { pararMotores(); Serial.println("ERROR comando desconocido"); }
}

Cómo verificar

  • ✓En el Serial Monitor (115200, fin de línea "Nueva línea"), "ANDAR 30" responde "OK ANDAR" y, 2 s después, "# motores parados".
  • ✓"ANDAR 500" responde "ERRO limite" aunque la Pi no haya logrado bloquearlo.
  • ✓Con el botón del GPIO 13 presionado, cualquier movimiento se detiene de inmediato. Pruébalo también en Wokwi.

💡 La parada real es física

El botón del GPIO depende de que el programa esté en ejecución. En un robot con fuerza (brazo, base pesada), instala también un interruptor en serie con la alimentación de los motores, al alcance de la mano. Antes de cada prueba, presiónalo una vez para asegurarte de que funciona.

Por qué aprender

Los módulos siguientes incorporan un brazo que recoge objetos y una voz que da órdenes. Con las cuatro capas listas, cualquier modelo que pruebes, local o en la nube, grande o pequeño, funciona dentro de un espacio que tú controlas.

Conceptos clave

Capas
ninguna confía en la otra
Watchdog
sin orden, se detiene
Confirmación
una persona aprueba la acción grande
Parada física
corta la energía

✅ Resumen del módulo

✓
LLM, VLM y VLA: texto, imagen con texto y movimiento; cada uno tiene un lugar en la pila.
✓
El modelo elige, el robot ejecuta: el código valida antes de cualquier movimiento.
✓
Lista cerrada en JSON: enum de acciones, límites numéricos, nada más.
✓
Ollama local: API en localhost:11434, sin internet; la nube también sirve con el mismo validador.
✓
Cuatro capas de bloqueo: validador, límites en el ESP32, watchdog y parada física.

Para profundizar

Siguiente módulo

5.2 · Voz local: escucha con Whisper y habla con Piper, conectando la voz con el cerebro de este módulo.