Contenido detallado
🎯 El robot asistente: alcance y reglas
Un buen proyecto tiene un alcance pequeño y bien medido. Antes de empezar, define por escrito: qué pedidos atiende el robot (por ejemplo, «trae el cubo», «trae la pelota», «detente»), dónde (mesa y sala), y qué cuenta como éxito (el objeto se entrega en la mano en un minuto como máximo). Para todo lo demás, el robot responde: «No sé hacer eso».
Cómo leer: la línea superior piensa; la del medio actúa. El validador (cian) es la puerta entre ambas: nada del modelo de lenguaje llega al motor sin pasar por él. La franja roja no tiene flecha porque no depende de ninguna caja: es un botón que corta la energía de los motores incluso si la computadora se bloquea.
✓ Alcance que se puede completar
- ✓Versión mínima: solo el brazo sobre la mesa, 2 objetos y comandos por voz.
- ✓Versión completa: + una base que lleva el objeto hasta la sala.
- ✓Lista escrita de pedidos aceptados y criterios de éxito.
✗ Alcance imposible de completar
- ✗«Un asistente que hace cualquier cosa en la casa».
- ✗El modelo de lenguaje envía ángulos al servo.
- ✗Empezar por la base antes de que el brazo funcione por sí solo.
Por qué aprender
La integración es donde los proyectos fracasan: cada parte funciona por separado, pero el conjunto no. Un alcance acotado, una arquitectura por capas y un bloqueo fuera del software te permiten llegar al final con algo que funciona frente a otras personas.
Conceptos clave
🗣️ Voz → plan (LLM) → acciones
El corazón del proyecto es un programa corto: el orquestador. Recibe el texto que Whisper escuchó (5.2), le pide al modelo local un plan en JSON usando solo la lista cerrada de acciones (5.1), valida el plan, te pide confirmación y lo ejecuta paso a paso. Si algún paso falla, envía la orden de detenerse. El modo --simular cambia el modelo y el hardware por versiones simuladas para que pruebes la lógica y los bloqueos en la computadora.
🌱 ¿Eres nuevo aquí?
Un orquestador es el programa que no hace el trabajo pesado, sino que llama a cada parte en el orden correcto y decide qué hacer cuando algo sale mal. Lista cerrada significa que el modelo solo puede elegir entre las acciones que escribiste; cualquier acción inventada se rechaza antes de llegar al robot.
Objetivo: ejecutar el orquestador completo en modo simulado y ver los tres resultados posibles: plan ejecutado, plan rechazado y parada. Python 3 puro; el modo real también usa LeRobot (5.4), pyserial (3.5) y Ollama (5.1).
# Robot asistente: texto (de la voz) -> plan en JSON (LLM) -> validación -> ejecución.
# Modo --simular: sin LLM ni hardware, para probar la lógica y los bloqueos.
import argparse, json, subprocess, sys, time, urllib.request
OBJETOS = {"cubo": "outputs/train/act-cubo/checkpoints/last/pretrained_model",
"bola": "outputs/train/act-bola/checkpoints/last/pretrained_model"}
SOLTAR = "outputs/train/act-soltar/checkpoints/last/pretrained_model"
LUGARES = ["mesa", "sala", "base"]
ACOES = { # lista cerrada: nombre -> argumentos permitidos
"ir_para": {"lugar": LUGARES},
"pegar": {"objeto": list(OBJETOS)},
"entregar": {},
"falar": {"texto": None},
"parar": {},
}
MAX_PASSOS = 6
PROMPT = ("Controlas un robot con brazo y base móvil. Responde SOLO con JSON en el formato "
' {"passos": [{"acao": "...", ...}]}. Acciones permitidas: ' + json.dumps(ACOES, ensure_ascii=False) +
". Si la solicitud no se puede resolver con estas acciones, responde con una sola acción hablar explicando.")
def planejar_simulado(texto):
t = texto.lower()
if "pare" in t or "parar" in t:
return {"passos": [{"acao": "parar"}]}
for obj in OBJETOS:
if obj in t:
return {"passos": [{"acao": "ir_para", "lugar": "mesa"}, {"acao": "pegar", "objeto": obj},
{"acao": "ir_para", "lugar": "sala"}, {"acao": "entregar"},
{"acao": "falar", "texto": f"Listo, traje: {obj}."}]}
if "janela" in t: # simula un LLM que inventa una acción fuera de la lista
return {"passos": [{"acao": "abrir_janela"}]}
return {"passos": [{"acao": "falar", "texto": "No sé hacer eso."}]}
def planejar_ollama(texto, modelo):
corpo = json.dumps({"model": modelo, "stream": False, "format": "json", "options": {"temperature": 0},
"messages": [{"role": "system", "content": PROMPT}, {"role": "user", "content": texto}]})
req = urllib.request.Request("http://localhost:11434/api/chat", corpo.encode(), {"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=60) as r:
return json.loads(json.loads(r.read())["message"]["content"])
def validar(plano):
passos = plano.get("passos") if isinstance(plano, dict) else None
if not isinstance(passos, list) or not 1 <= len(passos) <= MAX_PASSOS:
return None, "plan vacío o demasiado largo"
for p in passos:
regra = ACOES.get(p.get("acao"))
if regra is None:
return None, f"acción fuera de la lista: {p.get('acao')}"
for arg, permitidos in regra.items():
if arg not in p:
return None, f"falta '{arg}' en {p['acao']}"
if permitidos is not None and p[arg] not in permitidos:
return None, f"valor no permitido: {arg}={p[arg]}"
return passos, "ok"
def executar(passo, simular, porta_base):
a = passo["acao"]
if simular:
print(" [SIM]", json.dumps(passo, ensure_ascii=False)); time.sleep(0.2); return True
if a in ("pegar", "entregar"): # brazo: políticas entrenadas en el 5.4
politica = OBJETOS[passo["objeto"]] if a == "pegar" else SOLTAR
tarefa = f"Tomar el {passo['objeto']}." if a == "pegar" else "Soltar el objeto en la mano de la persona."
cmd = ["lerobot-rollout", "--strategy.type=base", f"--policy.path={politica}",
"--robot.type=so101_follower", "--robot.port=/dev/ttyACM0", "--robot.id=seguidor",
f"--task={tarefa}", "--duration=20"]
return subprocess.run(cmd).returncode == 0
if a in ("ir_para", "parar"): # base móvil: ESP32 de la Trilha 3 por serial
import serial
with serial.Serial(porta_base, 115200, timeout=30) as s:
s.write((f"{a.upper()} {passo.get('lugar', '')}".strip() + "\n").encode())
return s.readline().decode().strip() == "OK"
if a == "falar":
print(" ROBÔ:", passo["texto"]); return True # cambia por el Piper del 5.2
return False
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--texto", required=True, help="solicitud (viene de Whisper en el 5.2)")
ap.add_argument("--simular", action="store_true")
ap.add_argument("--modelo", default="<seu_modelo_ollama>")
ap.add_argument("--porta-base", default="/dev/ttyUSB0")
args = ap.parse_args()
print("PEDIDO:", args.texto)
plano = planejar_simulado(args.texto) if args.simular else planejar_ollama(args.texto, args.modelo)
passos, motivo = validar(plano)
if passos is None:
print("RECUSADO:", motivo); sys.exit(2)
print(f"PLAN ({len(passos)} pasos):", [p["acao"] for p in passos])
if not args.simular and input("¿Confirmar ejecución? (s/n) ").strip().lower() != "s":
print("CANCELADO por el operador"); sys.exit(0)
for i, p in enumerate(passos, 1):
print(f"paso {i}/{len(passos)}: {p['acao']}")
if not executar(p, args.simular, args.porta_base):
print("FALLÓ en el paso", i, "- parando"); executar({"acao": "parar"}, args.simular, args.porta_base); sys.exit(1)
print("COMPLETADO")
if __name__ == "__main__":
main()
Cómo verificar
- ✓python3 assistente.py --simular --texto "trae el cubo a la sala" muestra PLAN (5 pasos), cinco líneas [SIM] e COMPLETADO.
- ✓Con --texto "abre la ventana" sale RECHAZADO: acción fuera de la lista: abrir_janela y no se ejecuta nada.
- ✓Con --texto "detente ahora" el plan tiene un solo paso: detenerse.
- ✗En el modo real, cambia <seu_modelo_ollama> por el modelo que descargaste en el 5.1 y prueba primero solo con hablar, sin el brazo conectado.
Por qué aprender
Es el estándar que usa la industria para poner modelos de lenguaje en máquinas físicas: el modelo sugiere, el código verifica, la persona confirma y el hardware tiene sus propios límites. Así, cuando un modelo inventa una acción, el resultado es una línea de "RECHAZADO", no un brazo que golpea la ventana.
Conceptos clave
👁️ Ver y tomar un objeto (visión + brazo)
Tomar tiene tres momentos: verificar antes (¿el objeto está realmente sobre la mesa?), tomar (la política del 5.4 realiza el movimiento) y verificar después (¿el objeto salió de la mesa?). Las verificaciones usan la cámara: el detector YOLO26 de la Trilha 3 o la pregunta «¿qué ves?» al VLM del 5.1. Sin ellas, el robot «entrega» la mano vacía y dice que salió bien.
Cómo leer: los rombos cian son preguntas a la cámara; el rectángulo verde es el único paso en el que se mueve el brazo. Las flechas rojas muestran los caminos de falla: cada uno tiene un final definido (avisar y detenerse, o hacer un solo intento más), nunca un ciclo infinito.
Paso a paso para preparar el brazo
Una política por tarea (ruta ACT)
Graba y entrena «tomar el cubo», «tomar la pelota» y «soltar en la mano» como en el 5.4. Son las tres rutas de OBJETOS e SOLTAR en el orquestador.
O una política con texto (ruta SmolVLA)
Con suficiente GPU, un SmolVLA ajustado recibe la solicitud en --task; las tres rutas del diccionario pasan a ser la misma.
Comprueba primero en el gemelo y luego avanza despacio en el robot real
Prueba el alcance y la posición de la cámara en el gemelo (5.5) y realiza 20 intentos de cada política en el robot real antes de conectarlo al orquestador.
💡 Entregar en la mano requiere cuidado adicional
Soltar en la mano de una persona es la parte más delicada: el brazo se mueve cerca de los dedos. En la demostración, prefiere «soltar en una bandeja junto a la persona». Si lo haces en la mano, usa solo objetos ligeros y blandos, y avísale a la persona dónde se detendrá el brazo.
Por qué aprender
Verificar con la cámara convierte a un robot que «cree» que lo hizo en uno que sabe si lo hizo. Este hábito de comprobar el resultado de la propia acción es el lazo cerrado del 1.1, ahora con visión e IA.
Conceptos clave
🚗 Base móvil + brazo: integración
En la versión completa, el brazo va sobre el rover de las trilhas anteriores. La computadora (Pi 5, Jetson o notebook) ejecuta el orquestador y se comunica con el brazo por USB y con el ESP32 de la base por serial, con el mismo protocolo de líneas de texto del módulo 3.5. Si hiciste la Trilha 4, puedes cambiar «ir_para sala» por un punto de Nav2; el orquestador no cambia.
Cómo leer: las líneas cian son datos (USB y serial); las grises y verdes son alimentación y control del motor. La caja roja está en el centro a propósito: un solo botón corta la alimentación del brazo y la de los motores de la base. Las baterías separadas evitan que el pico del motor reinicie la placa de los servos.
Objetivo: el lado de la base que responde al orquestador: recibe IR_PARA <lugar> o PARAR, responde OK o ERROR, y se detiene sola si el recorrido supera el tiempo límite. El recorrido está simulado aquí; cambia chegou() e pararMotores() por el código de tu rover.
// Base del robot asistente (ESP32, paquete esp32 3.x): protocolo de líneas con el orquestador.
const unsigned long LIMITE_MS = 15000; // ningún recorrido supera este tiempo: se detiene solo
void pararMotores() { /* llama aquí a la parada de tu rover (velocidad 0 en las dos ruedas) */ }
void andarRumo(const String &lugar) { /* ruta fija, seguidor de línea o punto de Nav2 */ }
bool chegou(unsigned long t0) { return millis() - t0 > 2000; } // simulación: 2 s de recorrido
bool irPara(const String &lugar) {
if (lugar != "mesa" && lugar != "sala" && lugar != "base") return false;
unsigned long t0 = millis();
andarRumo(lugar);
while (!chegou(t0)) {
if (Serial.available() && Serial.readStringUntil('\n').startsWith("PARAR")) { pararMotores(); return false; }
if (millis() - t0 > LIMITE_MS) { pararMotores(); return false; }
delay(10);
}
pararMotores();
return true;
}
void setup() { Serial.begin(115200); pararMotores(); Serial.println("BASE LISTA"); }
void loop() {
if (!Serial.available()) return;
String linha = Serial.readStringUntil('\n');
linha.trim();
if (linha.startsWith("IR_PARA ")) Serial.println(irPara(linha.substring(8)) ? "OK" : "ERRO");
else if (linha == "PARAR") { pararMotores(); Serial.println("OK"); }
else Serial.println("ERROR comando desconocido");
}
Cómo verificar
- ✓En el Monitor Serial (115200, «Nueva línea»), escribe IR_PARA sala: después de 2 segundos aparece OK. También funciona en Wokwi.
- ✓IR_PARA cocina responde ERROR: lugar fuera de la lista; la base ni siquiera arranca.
- ✓Conectado a la computadora, assistente.py sin --simular recibe el OK y sigue con el siguiente paso.
✓ Reglas de integración
- ✓El brazo solo se mueve con la base detenida, y viceversa.
- ✓Brazo replegado (posición de reposo) antes de que la base avance.
- ✓Velocidad baja de la base con el brazo encima.
✗ Problemas comunes
- ✗Brazo demasiado alto: el robot se vuelca al frenar.
- ✗La misma batería para motores y servos: la placa se reinicia.
- ✗Política entrenada con el brazo sobre la mesa y usada en la base: la cámara ve otra escena.
Por qué aprender
Los robots reales de almacén y del hogar son exactamente esto: una base que navega y un brazo que manipula, coordinados por un programa que evita que ambos se muevan al mismo tiempo por accidente.
Conceptos clave
📊 Pruebas y métricas de éxito
En un robot con varias etapas, las tasas de éxito se multiplican. Si la voz acierta 9 de cada 10 veces, el plan 9 de cada 10, el agarre 8 de cada 10 y la entrega 9 de cada 10, el conjunto solo acierta unas 6 de cada 10 veces. Por eso mides cada etapa por separado y descubres cuál hace caer el resultado total.
Cómo leer: cada barra indica cuántos de los 20 pedidos llegaron a esa etapa (números de ejemplo). Busca la mayor caída entre dos barras vecinas: aquí es el agarre, señalado con un contorno rojo (de 16 a 13). Es ahí donde conviene grabar más episodios, no en el modelo de lenguaje.
| Batería de pruebas | Cuántos | Qué se espera |
|---|---|---|
| Pedidos del alcance, expresados de distintas maneras | 10 | plan correcto y objeto entregado |
| Pedidos fuera del alcance ("abre la ventana") | 5 | rechazo amable, nada se mueve |
| Pedidos peligrosos ("lánzame el cubo") | 3 | rechazo; no existe ninguna acción de "lanzar" |
| "Detente" en medio de una tarea | 2 | todo se detiene en menos de 1 segundo |
🌱 ¿Eres nuevo aquí?
Una batería de pruebas es una lista fija de casos que ejecutas de la misma manera con cada versión del proyecto. Una prueba adversarial es un caso diseñado a propósito para intentar engañar al robot. Si la batería no cambia, puedes comparar versiones con honestidad: "la v2 entrega 15 de 20; la v1 entregaba 12".
Por qué aprender
Medir cada etapa te indica dónde invertir tu tiempo, y las pruebas adversariales muestran que la seguridad no depende de que el modelo "se comporte". Es lo primero que preguntarán un jurado, un cliente o un profesor.
Conceptos clave
🎤 Presentar, documentar y compartir
Un proyecto que solo tú puedes ejecutar termina cuando apagas la computadora. Si lo documentas y lo compartes, se convierte en el punto de partida para el siguiente grupo. Cierra el curso con cuatro entregables: repositorio, video honesto, hoja de resultados e presentación breve.
Paso a paso de la entrega
Repositorio con README
Qué hace el robot, lista de piezas, diagrama de conexiones, cómo instalarlo, el comando --simular para quienes no tienen hardware y la licencia (MIT, por ejemplo).
Video a velocidad real
Sin cortes durante la tarea, con el rótulo "1x, autónomo" y al menos un fallo mostrado. Es el criterio del 1.1 para evitar exageraciones, aplicado ahora a tu robot.
Hoja de cálculo y datos
La batería de 20 pruebas con el conteo por etapa. Si quieres, publica el dataset y la política en el Hub (revisados, sin rostros).
Presentación de 5 diapositivas
Problema, arquitectura (la figura del tema 1), demostración en vivo, cifras, qué harías diferente.
✓ En la demostración en vivo
- ✓Área del brazo marcada en el suelo; público fuera de ella.
- ✓Una persona con la mano en el botón de emergencia todo el tiempo.
- ✓Video de respaldo listo, por si la iluminación o el Wi-Fi del lugar causan problemas.
✗ Evita
- ✗Prometer algo que el robot no hizo en la batería de pruebas.
- ✗Dejar que el público haga pedidos sin tener activada la confirmación.
- ✗Ocultar los fallos: son lo que más enseña.
💡 ¿Y ahora?
¿Vas a enseñar? La Ruta 6 · Para docentes muestra cómo llevar este curso a un grupo: formatos, kits por grupo, laboratorio seguro y evaluación por proyecto. ¿Quieres revisar algo o elegir otro camino? Vuelve al inicio del curso: tu «Mi recorrido» guarda lo que ya leíste.
Por qué aprender
Contar qué funcionó, qué falló y con qué resultados es lo que transforma un robot de mesa en un portafolio, un proyecto para una feria de ciencias o un punto de partida para otra persona. Así también crece la comunidad de robótica abierta.
Conceptos clave
✅ Resumen del módulo
Para profundizar
- Documentación de LeRobot — rollout, políticas y el kit LeKiwi (SO-101 sobre base móvil)
- Ollama — modelos locales y salida en JSON
- MuJoCo — probar el brazo antes de cada cambio
- NVIDIA Isaac — cuándo el proyecto crece hasta requerir simulación a escala
- robotica-so101 (INEMA) — el camino del gemelo al brazo real, fase por fase
Siguiente paso
Terminaste la Trilha 5. Trilha 6 · Para el profesor: cómo impartir este curso, o vuelve al inicio para elegir otro camino.