Saltar al contenido principal

Creación y publicación de apps

Reachy Mini tiene un ecosistema de apps impulsado por Hugging Face Spaces. Puedes crear apps en Python, publicarlas y cualquier propietario de Reachy Mini puede instalarlas con un solo clic desde el panel.

Para un tutorial paso a paso con capturas de pantalla, consulta la entrada del blog: Make and Publish Your Reachy Mini Apps.


Uso de agentes de IA

Si utilizas un agente de programación con IA (Claude Code, Cursor, Copilot, etc.), puede crear apps por ti. Indícale el archivo AGENTS.md del proyecto:

Me gustaría crear una app para Reachy Mini. Empieza leyendo https://github.com/pollen-robotics/reachy_mini/blob/main/AGENTS.md

El repositorio incluye un directorio skills/ con guías detalladas que los agentes de IA usan para crear apps correctamente:

SkillQué cubre
create-app.mdFlujo de trabajo y plantillas para crear apps
ai-integration.mdCreación de apps impulsadas por LLM
control-loops.mdApps reactivas en tiempo real (seguimiento, juegos)
motion-philosophy.mdElección entre goto_target y set_target
interaction-patterns.mdAntenas como botones, cabeza como controlador
symbolic-motion.mdDefinir el movimiento matemáticamente (bailes, ritmos)

Cómo funcionan las apps

El demonio de Reachy Mini gestiona todo el ciclo de vida de tu app:

  1. Envías una petición de "start app" (desde el panel o la REST API).
  2. El demonio lanza tu app como un subproceso de Python (python -u -m your_app.main).
  3. Tu app recibe una instancia conectada de ReachyMini y un stop_event.
  4. Al detenerse, el demonio envía SIGINT a tu proceso, lo que dispara un apagado limpio.
  5. Después de que la app termina, el demonio devuelve el robot a su posición por defecto.

Restricciones clave:

  • Solo puede ejecutarse una app a la vez.
  • Tu app se ejecuta dentro del subproceso del demonio: no gestiona sus propias conexiones de hardware.
  • En el robot Wireless, tu app se ejecuta en un entorno virtual compartido en /venvs/apps_venv/.

Creación de una app

Utiliza siempre la herramienta de CLI para crear apps: genera la estructura correcta, los metadatos y los puntos de entrada:

# Install reachy-mini if not already done
uv pip install reachy-mini

# Create and publish in one step (recommended)
reachy-mini-app-assistant create my_app_name /path/to/destination --publish

# Or create locally first
reachy-mini-app-assistant create my_app_name /path/to/destination

Nunca crees carpetas de apps manualmente. El asistente se encarga del código plantilla, las etiquetas de Hugging Face, los puntos de entrada y la estructura de paquetes correcta. La creación manual provoca problemas sutiles difíciles de depurar.

Elegir una plantilla

PlantillaComandoÚsala cuando
Defaultreachy-mini-app-assistant create my_app .La mayoría de las apps. Estructura mínima funcional.
Conversationreachy-mini-app-assistant create --template conversation my_app .Integración con LLM, voz, hacer que el robot hable. Incluye canal de audio, herramientas LLM, fusión de movimiento y toda la fontanería.

Estructura generada

my_app/
├── index.html # Hugging Face Space landing page
├── style.css # Landing page styles
├── pyproject.toml # Package config with entry points
├── README.md # Must contain reachy_mini_python_app tag
└── my_app/
├── __init__.py
├── main.py # Your app logic
└── static/ # Optional web UI
├── index.html
├── style.css
└── main.js

El contrato ReachyMiniApp

Tu app es una clase que extiende ReachyMiniApp e implementa un método run(). Esta es la estructura mínima:

import threading
import time

import numpy as np

from reachy_mini import ReachyMini, ReachyMiniApp
from reachy_mini.utils import create_head_pose


class MyApp(ReachyMiniApp):
def run(self, reachy_mini: ReachyMini, stop_event: threading.Event):
t0 = time.time()

while not stop_event.is_set():
t = time.time() - t0

# Move the head
yaw = 30.0 * np.sin(2.0 * np.pi * 0.2 * t)
head_pose = create_head_pose(yaw=yaw, degrees=True)

# Move the antennas
a = np.deg2rad(25.0 * np.sin(2.0 * np.pi * 0.5 * t))
antennas = np.array([a, -a])

reachy_mini.set_target(head=head_pose, antennas=antennas)
time.sleep(0.02)


if __name__ == "__main__":
app = MyApp()
try:
app.wrapped_run()
except KeyboardInterrupt:
app.stop()

Puntos clave

  • run(reachy_mini, stop_event): El único método que debes implementar. La instancia de ReachyMini ya está conectada y lista. Consulta stop_event en tu bucle principal para salir limpiamente.
  • wrapped_run(): Se llama desde el bloque __main__. Se encarga de conectar con el robot, iniciar servicios opcionales y llamar a tu método run().
  • stop(): Establece el stop_event. El demonio lo llama mediante SIGINT al detener tu app.
  • Bloque __main__: Obligatorio. El demonio ejecuta tu app como un módulo (python -m my_app.main), por lo que este bloque es el punto de entrada real.

El punto de entrada en pyproject.toml

El demonio descubre tu app mediante un punto de entrada estándar de Python. El asistente lo genera por ti:

[project.entry-points."reachy_mini_apps"]
my_app = "my_app.main:MyApp"

El nombre del grupo es reachy_mini_apps (con guiones bajos). El valor apunta a tu clase (module.main:ClassName).

Cualquier dependencia adicional de tu proyecto debe añadirse en este archivo.


Opcional: interfaz web para tu app

Si quieres una página de ajustes o cualquier interfaz web para tu app, define custom_app_url en tu clase:

class MyApp(ReachyMiniApp):
custom_app_url: str | None = "http://0.0.0.0:8042"

def run(self, reachy_mini: ReachyMini, stop_event: threading.Event):
# Define FastAPI routes on self.settings_app
@self.settings_app.post("/my_endpoint")
def my_endpoint():
return {"status": "ok"}

# Your main loop...

Cuando custom_app_url está definido, la app inicia automáticamente un servidor web FastAPI que sirve archivos desde el directorio static/ dentro de tu paquete. El panel muestra un icono de ajustes para abrir esta interfaz. La página se puede acceder desde http://localhost:8042 con una lite o http://reachy-mini.local:8042 con una wireless.

Establece custom_app_url = None si tu app no necesita una interfaz web.

Para un ejemplo funcional con un interruptor y reproducción de sonido, consulta la template app.


Pruebas de tu app

1. Validar la estructura

reachy-mini-app-assistant check /path/to/my_app

Esto comprueba que tu app tenga la estructura correcta, los puntos de entrada y los metadatos.

2. Ejecutar directamente

Puedes ejecutar directamente el main.py de tu app para iterar rápidamente (asegúrate de que el demonio esté en ejecución):

python -m my_app.main

3. Probar desde el panel

Instala tu app localmente y pruébala desde el panel, como lo harían los usuarios:

# Install in development mode
uv pip install -e /path/to/my_app

# Start the daemon
reachy-mini-daemon # Lite
reachy-mini-daemon --sim # Simulation

Luego abre http://127.0.0.1:8000/ — tu app aparecerá en la lista de instaladas.


Publicación en Hugging Face

1. Iniciar sesión en Hugging Face

uv pip install --upgrade huggingface_hub
hf auth login

Utiliza un token con permisos de Write.

2. Publicar

Si usaste --publish al crear la app, ya es un Space de Hugging Face con un remoto Git. Solo tienes que hacer push:

git add . && git commit -m "my changes" && git push

Si la creaste sin --publish, puedes publicarla más tarde:

reachy-mini-app-assistant publish /path/to/my_app

3. Descubribilidad

Para que tu app aparezca en la tienda de apps de Reachy Mini, su README.md debe contener la etiqueta reachy_mini_python_app en el frontmatter YAML:

---
tags:
- reachy_mini_python_app
---

El asistente la añade automáticamente. Si creas el README manualmente, no lo olvides.


Instalación de apps

Desde el panel

Abre el panel de Reachy Mini y haz clic en Install en cualquier app de la comunidad. Es la forma más sencilla.

A través de la REST API

# Install from Hugging Face
curl -X POST http://localhost:8000/api/apps/install \
-H "Content-Type: application/json" \
-d '{"url": "https://huggingface.co/spaces/<user>/<app_name>"}'

# Start an app
curl -X POST http://localhost:8000/api/apps/start-app/<app_name>

# Stop the current app
curl -X POST http://localhost:8000/api/apps/stop-current-app

# List installed apps
curl http://localhost:8000/api/apps/list

Sustituye localhost por reachy-mini.local o por la dirección IP del robot para la versión Wireless.

Despliegue offline / manual para una unidad Wireless

Si no tienes acceso a internet en el robot (por ejemplo, en una conferencia), puedes instalar tu app directamente:

# Copy and install your app on the robot
scp -r /path/to/my_app [email protected]:/tmp/my_app
ssh [email protected] "/venvs/apps_venv/bin/pip install /tmp/my_app"

Después de actualizar el código manualmente, reinicia el demonio o la app para que los cambios surtan efecto.


Depuración de apps

Visualización de logs

Lite / Simulation

Si ejecutas el demonio en una terminal, los logs de la app (stdout/stderr) aparecen directamente allí.

reachy-mini-daemon          # Lite
reachy-mini-daemon --sim # Simulation
# App logs will print here
Wireless

La salida de la app es capturada por el daemon y está disponible vía journalctl:

ssh [email protected]

# Live logs
sudo journalctl -u reachy-mini-daemon -f

# Recent logs, filtered (daemon logs are noisy with HTTP access logs)
sudo journalctl -u reachy-mini-daemon --since '5 min ago' | grep -v "uvicorn\|GET \|POST "

Problemas comunes

ProblemaSolución
"An app is already running"Detén primero la app actual: curl -X POST http://localhost:8000/api/apps/stop-current-app
Daemon en mal estadoReinícialo: sudo systemctl restart reachy-mini-daemon (espera ~30s antes de iniciar una app)
La app no detecta cambios en el códigoReinicia la app. Si la desplegaste manualmente, borra también el bytecode: rm -rf __pycache__

Consejo: registra todo al inicio

Una práctica útil de depuración es registrar tu configuración cuando la app se inicia:

import logging
import sys

logger = logging.getLogger(__name__)

def run(self, reachy_mini, stop_event):
logger.info("=" * 50)
logger.info("MY APP STARTING")
logger.info(f" Python: {sys.version}")
logger.info("=" * 50)
# ...

Configuración de la app

El subproceso de la app hereda las variables de entorno del daemon. No hay ningún mecanismo especial de inyección: tu app ve lo mismo que ve el proceso del daemon.

Si tu app necesita configuración en tiempo de ejecución (claves de API, URLs de servidores, etc.), el enfoque recomendado es usar la interfaz web de la app. Define custom_app_url en tu clase y añade una página de ajustes donde los usuarios puedan introducir valores (claves de API, direcciones de servidor, etc.) directamente desde su navegador. Esta es la opción más fácil de usar y funciona en todas las plataformas. Consulta Optional: Web UI for Your App más arriba para ver cómo configurarla.

Otros enfoques:

  • Archivo de configuración: Lee desde una ruta conocida (por ejemplo, .env, consulta ejemplo aquí).
  • Valores predeterminados hardcodeados: Sencillos y fáciles de depurar durante el desarrollo.

Uso de audio en tu app

La grabación de audio, la reproducción y la detección de la dirección de llegada funcionan de la misma manera dentro de una app que en un script independiente: usa directamente los métodos del SDK (start_recording(), get_audio_sample(), push_audio_sample(), play_sound()).

Consulta los ejemplos oficiales para ver código funcional:

Para más detalles sobre cómo difieren los flujos de audio entre Wireless y Lite, consulta Media Architecture.


Lecturas adicionales

AppPatrones claveEnlace
Conversation AppHerramientas LLM, canalización de audio, bucles de controlGitHub
MarionetteGrabación de movimiento, par seguro, datasets de HFHF Space
RadioPatrón de interacción con antenaHF Space
SimonPatrón sin GUI (antena para iniciar)HF Space
Hand TrackerBucle de control en tiempo real basado en cámaraHF Space
Spaceship GameCabeza como joystick, botones de antenaHF Space
Loading Comments...