
Una guía probada para el ajuste fino de NVIDIA GR00T N1.7 en un conjunto de datos LeRobot SO-100: flags reales, modality.json, el requisito de la v2.1, cuánto cuesta una ejecución y las trampas.
NVIDIA incluye un ejemplo de ajuste fino para el brazo que probablemente ya tienes. Dentro del repositorio Isaac-GR00T hay una carpeta llamada demo_data/cube_to_bowl_5: cinco episodios, 4.148 fotogramas a 30 fps, ya escritos como LeRobot v2.1, con una configuración de modalidad coincidente en examples/SO100/. Su meta/info.json informa robot_type: so101_follower, que en LeRobot es la misma clase de configuración que so100_follower. Esto es realmente útil, porque significa que la ruta de referencia para GR00T N1.7 en un brazo de hobby de seis grados de libertad es mantenida por las personas que escribieron el modelo. No es una demo humanoide escalada, es el mismo brazo.
La mala noticia es la distancia entre I recorded 60 episodes y the arm does the task. Hay aproximadamente seis puntos donde este pipeline falla silenciosamente en lugar de ruidosamente, y cuatro de ellos residen en archivos que la mayoría de la gente nunca abre: meta/modality.json, la configuración de datos de Python, meta/relative_stats.json, y la cadena de versión propia del conjunto de datos. Esta guía recorre la ruta manual de principio a fin con los comandos reales, luego muestra el mismo trabajo como un formulario en AY-Robots. Todo lo que sigue fue verificado con la rama principal de Isaac-GR00T a partir del 20 de agosto de 2026 (la línea de lanzamiento n1.7) y lerobot 0.6.1, publicado en PyPI el 3 de agosto de 2026. El desarrollo avanza rápido, y donde se haya renombrado una bandera, este artículo lo indica.
Lo que necesitas saber antes de empezar
- •El ajuste fino de GR00T N1.7 requiere 40 GB o más de VRAM. NVIDIA recomienda nodos H100 o L40. Una RTX 4090 de 24 GB no realizará este trabajo, aunque sí entrenará SmolVLA y ACT.
- •El conjunto de datos debe ser LeRobot v2 (v2.0 o v2.1) más un meta/modality.json específico de GR00T. Un conjunto de datos de LeRobot v3.0 no se carga y debe ser convertido a una versión anterior.
- •El punto de entrada es gr00t/experiment/launch_finetune.py, una CLI de tyro. No tiene la bandera --seed, por lo que las ejecuciones no son reproducibles bit a bit.
- •Para un brazo personalizado, la etiqueta de encarnación es NEW_EMBODIMENT, y esa etiqueta hace que --modality-config-path sea obligatorio.
- •La receta SO-100 incluida predice las articulaciones del brazo como deltas RELATIVOS y el efector final como un objetivo ABSOLUTO. Invertir ese emparejamiento es un fallo silencioso, no un error.
- •En AY-Robots, el mismo trabajo es un formulario: 20000 pasos, lote 32, tasa de aprendizaje 1e-4, aproximadamente 4 a 12 USD en el nivel A100 de 80 GB o H100.
Qué es realmente GR00T N1.7
GR00T N1.7 es un modelo de visión-lenguaje-acción con el diseño de sistema dual descrito en el artículo original de GR00T N1: un módulo de visión-lenguaje que lee las cámaras y la instrucción, y un transformador de difusión que convierte eso en un fragmento de comandos motores continuos. N1.7 reemplazó la primera mitad. El backbone Eagle de N1.6 ha desaparecido, siendo reemplazado por nvidia/Cosmos-Reason2-2B en una arquitectura Qwen3-VL, y el modelo fue preentrenado con aproximadamente 20,000 horas de video humano egocéntrico además de los datos del robot. El propio informe de NVIDIA sitúa la cifra en 20,854 horas y reporta que pasar de 1k a 20k horas más que duplica la finalización promedio de tareas.
La segunda mitad también cambió, de maneras que importan para su ejecución. El cabezal de acción se redujo de 32 capas de difusión a 16, el fragmento de acción predicho creció de 16 pasos a 40, y el ancho máximo de estado y acción pasó de 29 a 132. Esos tres números provienen del registro de cambios en el README del repositorio; la propia publicación de lanzamiento de NVIDIA todavía describe el Sistema 1 como un DiT de 32 capas, así que donde los dos discrepen, confíe en el repositorio que está a punto de clonar. Las acciones se expresan por defecto en un espacio relativo del efector final, deltas de la pose actual en lugar de objetivos absolutos, lo que permite que los priors de manipulación aprendidos de videos humanos se transfieran al control del robot. El cabezal en sí es un transformador de difusión de coincidencia de flujo, de la misma familia que Pi0.5 pero con un backbone diferente delante. Si todavía está en la generación anterior, N1.7 contra N1.5 cubre si la actualización justifica rehacer su pipeline.
| Property | Value | Where it comes from |
|---|---|---|
| Parámetros | 3,000,000,000 | Hugging Face model card |
| Backbone de visión-lenguaje | nvidia/Cosmos-Reason2-2B (Qwen3-VL), gated on Hugging Face | repo README |
| Cabezal de acción | Transformador de difusión de coincidencia de flujo, 16 capas (N1.6 tenía 32) | repo README |
| Horizonte de acción predicho | 40 pasos para el checkpoint base (N1.6 tenía 16) | getting_started/policy.md and repo README |
| Ancho máximo de estado y acción | 132 (N1.6 tenía 29) | repo README |
| Licencia del código | Apache 2.0 | Isaac-GR00T repository |
| Licencia de los pesos | NVIDIA Open Model License Agreement | model card |
| Latencia, H100 80 GB, PyTorch eager, 4 pasos de denoising, 1 cámara | 85.8 ms de principio a fin, 11.7 Hz | model card timing table |
| Mismo hardware, pipeline completo con TensorRT | 27.9 ms de principio a fin, 35.9 Hz | model card timing table |
| Latencia que AY-Robots cita para su GR00T N1.7 servido | 152 ms por paso de acción | AY-Robots policy catalog |
Esas últimas tres filas explican la mayor parte de la decepción que la gente reporta. El titular de 27.9 ms corresponde a un motor TensorRT en una H100 con una cámara y cuatro pasos de denoising. PyTorch puro en la misma tarjeta es de 85.8 ms, y la tarjeta del modelo sitúa la diferencia en 3.08x. Ninguno de los números incluye una capa de servicio, una segunda cámara o un salto de red. Los 152 ms por paso de acción que AY-Robots cita para su GR00T N1.7 servido es la cifra con el servicio en el bucle, y un viaje de ida y vuelta por internet público se suma a eso. Más sobre esto al final. Para los números junto a otros modelos, GR00T N1.7 contra Pi0.5 y GR00T N1.7 contra SmolVLA los presentan uno al lado del otro.

Lo que la ejecución necesita antes de que escribas nada
| Requisito | Ajuste fino | Inferencia |
|---|---|---|
| VRAM, guía de NVIDIA | 40 GB o más, H100 o L40 recomendado | 16 GB o más, una RTX 4090 funciona |
| Python y CUDA en dGPU | 3.12 y CUDA 12.8 | 3.12 y CUDA 12.8 |
| Backend de vídeo | torchcodec 0.8.0, FFmpeg 4 a 7 solamente | igual |
| Formato del conjunto de datos | LeRobot v2 plus meta/modality.json | no aplicable |
| Acceso a Hugging Face | aprobado para nvidia/Cosmos-Reason2-2B | igual |
| Otras herramientas | git-lfs y uv | uv |
| Nivel de GPU de AY-Robots para el entrenador groot1.7 | A100 80 GB o H100 80 GB | pod aprovisionado automáticamente |
Cada punto de control de GR00T, incluyendo la base nvidia/GR00T-N1.7-3B, carga nvidia/Cosmos-Reason2-2B en el primer uso, y ese repositorio está restringido. El README indica el fallo exactamente: la carga del modelo falla con un GatedRepoError / 401 Client Error. Lo que no menciona es cuándo ocurre eso, que es después de que hayas alquilado la tarjeta y la ejecución haya comenzado. Solicita acceso en la página del modelo, luego ejecuta uv run huggingface-cli login o exporta HF_TOKEN antes de alquilar nada.
Paso 0: los episodios en sí
Todo lo que sigue asume que ya ha grabado episodios. Si no lo ha hecho, ese es el verdadero primer paso y es el que decide la calidad del resultado, porque aprendizaje por imitación no puede recuperar información que no está en los datos. Calibre ambos brazos primero, luego dirija el seguidor con un brazo líder mientras lerobot-record escribe los archivos parquet y las transmisiones de la cámara. Si la calibración está desactivada, los valores de las articulaciones en su conjunto de datos describen un robot ligeramente diferente al que ejecutará la política más tarde, y ninguna cantidad de entrenamiento puede solucionar eso.
lerobot-record \
--robot.type=so100_follower \
--robot.port=/dev/ttyACM0 \
--robot.id=my_follower_arm \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 2, width: 640, height: 480, fps: 30}}" \
--teleop.type=so100_leader \
--teleop.port=/dev/ttyACM1 \
--teleop.id=my_leader_arm \
--dataset.repo_id=${HF_USER}/cube-into-bowl \
--dataset.num_episodes=60 \
--dataset.single_task="put the cube in the yellow bowl" \
--display_data=trueEl propio consejo de LeRobot es grabar al menos 50 episodios con unos 10 por ubicación de objeto, mantener las cámaras fijas y mantener el comportamiento de agarre consistente. Añada variación más tarde, no al principio. La regla general que vale la pena recordar: si usted mismo no pudiera realizar la tarea solo con las imágenes de la cámara, la política tampoco podrá. Para la configuración específica del brazo, introducción al SO-100 y la página de LeRobot del SO-100 cubren los puertos, la calibración y los índices de las cámaras. En AY-Robots también puede hacer esto por internet desde el navegador usando teleoperación y grabar directamente desde la sesión.
Paso 1: el conjunto de datos debe ser LeRobot v2.1
Este es el obstáculo más común. La CODEBASE_VERSION actual de LeRobot en `main` es v3.0, por lo que cualquier cosa que grabe hoy con una cadena de herramientas actual saldrá como v3.0. El cargador de GR00T espera v2. El repositorio es explícito sobre el motivo: muchos conjuntos de datos ascendentes como DROID, LIBERO y Bridge se publican en v2, y el soporte nativo para ambos está planeado pero no se ha lanzado. Así que la conversión corre de su cuenta, y se ejecuta en su propio virtualenv por una razón concreta: scripts/lerobot_conversion lleva su propio pyproject que requiere Python 3.10 o 3.11 y fija lerobot a un commit de git, mientras que Isaac-GR00T en sí mismo requiere Python 3.12. Instale el conversor desde la raíz del repositorio y obtendrá el paquete gr00t en su lugar, que es el error sobre el que advierte su README. Si es nuevo en el formato, la entrada del glosario de conjunto de datos LeRobot explica lo que realmente contiene.
# from the Isaac-GR00T repo root
cd scripts/lerobot_conversion
uv venv
source .venv/bin/activate
uv pip install -e . --verbose
# pulls the dataset from the Hub and writes a v2.1 copy into the default cache
python convert_v3_to_v2.py --repo-id <your-hf-user>/<your-dataset>
# or, back in the repo root, keep it next to the SO-100 example
uv run --project scripts/lerobot_conversion \
python scripts/lerobot_conversion/convert_v3_to_v2.py \
--repo-id <your-hf-user>/<your-dataset> \
--root examples/SO100/my_dataset_lerobotSi el conjunto de datos v3.0 ya existe localmente, el script construye el diseño v2.1 junto a él y luego los intercambia: el original se mueve a una carpeta hermana con la versión adjunta, <name>_v3.0, y la copia convertida toma la ruta original. (El docstring del propio script llama a esa carpeta _v30; el código adjunta la cadena de versión, por lo que lo que realmente se obtiene es _v3.0.) Segunda sorpresa: la salida siempre aterriza bajo <root>/<repo-id>, por lo que --root examples/SO100/my_dataset_lerobot le da examples/SO100/my_dataset_lerobot/<your-hf-user>/<your-dataset>, y esa ruta más larga es la que --dataset-path querrá más tarde. Cuando un trabajo de entrenamiento rechaza su conjunto de datos por razones de versión, la página de conjunto de datos rechazado como v3 enumera los síntomas exactos.
La estructura que GR00T requiere después de la conversión es el diseño clásico v2: meta/info.json, meta/episodes.jsonl, meta/tasks.jsonl, archivos parquet bajo data/chunk-000/, archivos MP4 bajo videos/chunk-000/observation.images.
Paso 2: modality.json, los seis números que lo deciden todo
En un conjunto de datos de LeRobot, el estado del robot y la acción se almacenan como arrays planos de float32. Para un SO-100, ambos tienen la forma [6]: cinco articulaciones del brazo y una pinza. El conjunto de datos de demostración los nombra shoulder_pan.pos, shoulder_lift.pos, elbow_flex.pos, wrist_flex.pos, wrist_roll.pos, gripper.pos, pero esos nombres residen en info.json y nada en el archivo parquet indica qué índice corresponde a cada uno. meta/modality.json proporciona ese mapeo, y GR00T no se entrenará sin él. Aquí está el que el repositorio incluye para el SO-100, textualmente.
{
"state": {
"single_arm": {
"start": 0,
"end": 5
},
"gripper": {
"start": 5,
"end": 6
}
},
"action": {
"single_arm": {
"start": 0,
"end": 5
},
"gripper": {
"start": 5,
"end": 6
}
},
"video": {
"front": {
"original_key": "observation.images.front"
},
"wrist": {
"original_key": "observation.images.wrist"
}
},
"annotation": {
"human.task_description": {
"original_key": "task_index"
}
}
}Cópielo en su conjunto de datos convertido en meta/modality.json y cambie el nombre de las claves de video a como se llamen realmente sus cámaras. Si grabó con una sola cámara cenital llamada top, entonces original_key es observation.images.top y el nombre amigable es el que referenciará su configuración de datos. Los dos deben coincidir, y ninguno de ellos verifica al otro por usted. La anotación de idioma es peor, porque la misma clave debe aparecer en tres lugares.
| Capa | Archivo | Formato SO-100 utilizado en el repositorio |
|---|---|---|
| Columna Parquet | data/chunk-*/episode_*.parquet | annotation.human.task_description |
| Clave de modality.json | meta/modality.json, under "annotation", without the annotation. prefix | human.task_description |
| modality_keys en la configuración de datos | your so100_config.py | annotation.human.task_description |
Los segmentos después de annotation. son elegidos por quien haya creado el conjunto de datos. Los datos de demostración SO-100 usan annotation.human.task_description; LIBERO y SimplerEnv usan annotation.human.action.task_description. Ambos son válidos. Si copió una configuración de un ejemplo de LIBERO y la apuntó a su propia grabación SO-100, el canal de idioma no resuelve nada y el modelo se entrena con una instrucción vacía. La pérdida sigue disminuyendo. La política sigue haciendo algo. Simplemente ignora lo que le dijo que hiciera.
Paso 3: la configuración de datos, brazo relativo y pinza absoluta
La configuración de modalidad es un archivo Python en lugar de JSON, porque también decide cómo se representa cada grupo de acciones. Esta es la parte del flujo de trabajo N1.7 que no existía de la misma forma en N1.5, y la parte que vale la pena leer dos veces. La configuración SO-100 incluida predice las cinco articulaciones del brazo como RELATIVOS deltas desde el estado actual y la pinza como una posición objetivo ABSOLUTA, porque una señal binaria de abierto o cerrado se comporta mejor como objetivo que como delta.
from gr00t.configs.data.embodiment_configs import register_modality_config
from gr00t.data.embodiment_tags import EmbodimentTag
from gr00t.data.types import (
ActionConfig, ActionFormat, ActionRepresentation, ActionType, ModalityConfig,
)
so100_config = {
"video": ModalityConfig(
delta_indices=[0], # current frame only
modality_keys=["front", "wrist"], # must match modality.json
),
"state": ModalityConfig(
delta_indices=[0],
modality_keys=["single_arm", "gripper"],
),
"action": ModalityConfig(
delta_indices=list(range(0, 16)), # predict 16 future steps
modality_keys=["single_arm", "gripper"],
action_configs=[
ActionConfig(rep=ActionRepresentation.RELATIVE, # arm joints
type=ActionType.NON_EEF,
format=ActionFormat.DEFAULT),
ActionConfig(rep=ActionRepresentation.ABSOLUTE, # gripper
type=ActionType.NON_EEF,
format=ActionFormat.DEFAULT),
],
),
"language": ModalityConfig(
delta_indices=[0],
modality_keys=["annotation.human.task_description"],
),
}
register_modality_config(so100_config, embodiment_tag=EmbodimentTag.NEW_EMBODIMENT)Dos detalles aquí te costarán un día si no los conoces. Primero, action_configs es posicional: la documentación requiere la misma longitud y el mismo orden que modality_keys, y son directos sobre la consecuencia de equivocarse, que es que la representación incorrecta se aplica silenciosamente. Tu pinza se entrena como un delta y tu brazo como un objetivo absoluto, y no hay mensaje de error. Segundo, register_modality_config afirma que la etiqueta no está ya registrada, por lo que una segunda configuración NEW_EMBODIMENT en el mismo proceso Python falla con Embodiment tag ... already registered. No puedes importar dos de estas en un mismo script. Una tercera regla se aplica más tarde, en el despliegue: la acción delta_indices debe ser el rango contiguo que comienza en cero. Una ventana dispersa como [0, 4, 8] es rechazada, porque todo lo que está aguas abajo indexa el fragmento predicho linealmente y de lo contrario ejecutaría las filas incorrectas.
Las estadísticas de normalización, en particular meta/relative_stats.json, se calculan para la longitud del horizonte que tenías cuando las generaste. Acorta el horizonte de acción de 16 a 8 sin regenerar y el entrenamiento falla con IndexError: boolean index did not match indexed array ... dimension is 8 but corresponding boolean dimension is 16. La solución es un comando: python gr00t/data/stats.py --dataset-path <path> --embodiment-tag NEW_EMBODIMENT --modality-config-path examples/SO100/so100_config.py. Ejecútalo después de cualquier cambio en delta_indices.
Paso 4: el entorno
N1.7 movió el repositorio a y Python 3.12. La antigua ruta de conda más pip install -e . todavía existe en una sección colapsada del README, pero advierte que las dependencias de GPU, incluyendo flash-attn y TensorRT, pueden requerir instalación manual. Use uv a menos que tenga una razón específica para no hacerlo. En cuanto a flash-attn, un detalle evita confusiones: verá Installing flash-attn impreso en cada uv run. No se está reconstruyendo. uv está revalidando un wheel anclado a una URL que ya está en caché, y esto toma dos o tres segundos.
- 1Instalar git-lfs, luego clonar con submódulos
git-lfs es obligatorio, no opcional. Sin él, los archivos parquet en demo_data/ se descargan como stubs de puntero, y la ejecución de la demostración falla en un conjunto de datos que parece presente en el listado de archivos.
bashsudo apt install git-lfs && git lfs install git clone --recurse-submodules https://github.com/NVIDIA/Isaac-GR00T cd Isaac-GR00T - 2Instalar uv y sincronizar el entorno
La instalación predeterminada descarga las dependencias de GPU, incluyendo flash-attn y TensorRT. En una imagen nueva de A100 o H100, este es el paso individual más largo, así que hágalo antes de prestar atención a cualquier otra cosa.
bashcurl -LsSf https://astral.sh/uv/install.sh | sh sudo apt-get update && sudo apt-get install -y ffmpeg uv sync --python 3.12 uv run python -c "import gr00t; print('GR00T installed successfully')" - 3Autenticarse con Hugging Face
Haga esto antes del primer lanzamiento de entrenamiento, no después de que falle a los ocho minutos.
bashuv run huggingface-cli login # or: export HF_TOKEN=<your_token> - 4Verificación de cordura con los datos de demostración SO-100 incluidos
Antes de tocar su propia grabación, ejecute 2000 pasos en demo_data/cube_to_bowl_5. Son cinco episodios, termina rápido y prueba el entorno en lugar de sus datos. Si esta ejecución falla, nada de lo que haga con su conjunto de datos ayudará.
bashCUDA_VISIBLE_DEVICES=0 uv run python \ gr00t/experiment/launch_finetune.py \ --base-model-path nvidia/GR00T-N1.7-3B \ --dataset-path demo_data/cube_to_bowl_5 \ --embodiment-tag NEW_EMBODIMENT \ --modality-config-path examples/SO100/so100_config.py \ --num-gpus 1 \ --output-dir /tmp/test_finetune \ --max-steps 2000 \ --global-batch-size 32 \ --dataloader-num-workers 4
FFmpeg 8. torchcodec 0.8.0 solo es compatible con FFmpeg 4 a 7, y Ubuntu 25.10 y posteriores incluyen la versión 8. El error es Could not load libtorchcodec, lo que parece una instalación rota en lugar de un conflicto de versiones. Instale un tiempo de ejecución más antiguo, por ejemplo conda install -c conda-forge 'ffmpeg<8', y coloque sus bibliotecas en LD_LIBRARY_PATH. CUDA_HOME no está configurado. El ajuste fino falla por completo. Ejecute bash scripts/deployment/dgpu/install_deps.sh una vez, o simplemente export CUDA_HOME=/usr/local/cuda.
Paso 5: el comando de ajuste fino y cuáles son los valores predeterminados de sus flags
Cambia el conjunto de datos de demostración por el tuyo y añade los parámetros que realmente deseas. A continuación se muestra la forma completa que el repositorio utiliza en su propio tutorial de nueva encarnación, incluyendo los flags de aumento y de puntos de control que el breve ejemplo del README omite. Esto es en el sentido estricto: el backbone del lenguaje y el codificador visual permanecen congelados, y lo que se entrena es el proyector y el cabezal de acción de difusión.
export NUM_GPUS=1
CUDA_VISIBLE_DEVICES=0 uv run python \
gr00t/experiment/launch_finetune.py \
--base-model-path nvidia/GR00T-N1.7-3B \
--dataset-path ./my_dataset_lerobot \
--embodiment-tag NEW_EMBODIMENT \
--modality-config-path examples/SO100/so100_config.py \
--num-gpus $NUM_GPUS \
--output-dir /tmp/so100 \
--save-total-limit 5 \
--save-steps 2000 \
--max-steps 20000 \
--use-wandb \
--global-batch-size 32 \
--color-jitter-params brightness 0.3 contrast 0.4 saturation 0.5 hue 0.08 \
--dataloader-num-workers 4| Flag | Valor predeterminado en FinetuneConfig | Qué hace |
|---|---|---|
| --global-batch-size | 64 | Tamaño total del batch en todas las GPUs antes de la acumulación de gradientes. Los ejemplos proporcionados usan 32. |
| --learning-rate | 1e-4 | El mismo valor que AY-Robots envía para su entrenador groot1.7. |
| --max-steps | 10000 | Pasos totales del optimizador. El wrapper examples/finetune.sh también tiene un valor predeterminado de 10000. |
| --gradient-accumulation-steps | 1 | Multiplica el batch efectivo. Valores superiores a 1 emiten una advertencia indicando el tamaño acumulado. |
| --save-steps and --save-total-limit | 1000 and 5 | Frecuencia de los puntos de control y cuántos se conservan. Los más antiguos se eliminan. |
| --weight-decay and --warmup-ratio | 1e-5 and 0.05 | También establecido explícitamente por examples/finetune.sh. |
| --state-dropout-prob | 0.2 in the CLI, 0.8 in the model config | Descarta aleatoriamente el estado propioceptivo durante el entrenamiento. Redúcelo si tu tarea depende del estado. |
| --tune-llm and --tune-visual | False and False | El backbone permanece congelado por defecto. |
| --tune-projector and --tune-diffusion-model | True and True | El proyector y el cabezal de acción de difusión son lo que realmente se entrena. |
| --use-percentiles | True | Normaliza con q01 y q99 en lugar de los valores mínimos y máximos brutos. |
| --dataloader-num-workers | 2 | El cargador está basado en CPU por diseño. Los ejemplos lo aumentan a 4. |
| --seed | does not exist | No existe un flag de semilla en esta CLI. |
Esa última fila no es un error tipográfico. launch_finetune.py es una CLI de tyro generada a partir de una dataclass, y esa dataclass no tiene un campo de semilla. El README señala por separado una varianza del 5 al 6 por ciento entre ejecuciones causada por la aumentación de imágenes no determinista. Dos ejecuciones con flags idénticos no producirán checkpoints idénticos, lo cual es muy importante cuando se intenta decidir si un cambio de hiperparámetro ayudó o si se tuvo suerte. En comparación, el propio entrenador de lerobot por defecto usa la semilla 1000, y la receta LeRobot GR00T pasa --seed=42 explícitamente.
El ajuste fino se ejecuta con eval_strategy="no", por lo que no hay ninguna curva de pérdida de validación. Se obtiene la pérdida de entrenamiento y nada más. La guía de nueva encarnación indica que se active con --eval-strategy steps --eval-steps 500, pero ese flag no existe en launch_finetune.py: la CLI es generada por tyro a partir de la dataclass FinetuneConfig, y eval_strategy, eval_steps y eval_batch_size son campos de TrainingConfig en su lugar. Sus valores predeterminados son "no", 500 y 2. Para acceder a ellos, utilice el punto de entrada más completo gr00t/experiment/launch_train.py, donde el flag anidado es --training.eval-strategy. De cualquier manera, una pérdida de entrenamiento decreciente por sí sola dice muy poco sobre la generalización, que es exactamente la situación descrita en la pérdida disminuye pero la política no hace nada.
Cuánto cuesta una ejecución de 20000 pasos
GR00T N1.7 necesita una tarjeta de 80 GB, por lo que la pregunta del costo tiene una respuesta limitada. En AY-Robots, el entrenador groot1.7 se ejecuta en el nivel A100 de 80 GB o H100 de 80 GB, donde una ejecución tarda de 3 a 6 horas a 1.20 a 2.00 USD por hora en el mercado spot. Eso es aproximadamente de 4 a 12 USD para el trabajo predeterminado de 20000 pasos. La misma tarea en SmolVLA o ACT se ejecuta en una tarjeta de 24 GB a 0.30 a 0.60 USD por hora y de 1 a 3 USD por ejecución. Esa es la verdadera compensación: GR00T cuesta aproximadamente cuatro veces más por intento, y no se puede ejecutar en la 4090 debajo de su escritorio.
| Modelo | Nivel de GPU | Ejecución típica | Costo típico | Episodios mínimos |
|---|---|---|---|---|
| GR00T N1.7 | A100 80 GB or H100 80 GB | 3 to 6 hours | 4 to 12 USD | 50 |
| GR00T N1.5 | A100 80 GB or H100 80 GB | 3 to 6 hours | 4 to 12 USD | 50 |
| Pi0.5 | A100 80 GB or H100 80 GB | 3 to 6 hours | 4 to 12 USD | 50 |
| SmolVLA | RTX 4090 or any 24 GB card | 2 to 5 hours | 1 to 3 USD | 30 |
| ACT | RTX 4090 or any 24 GB card | 2 to 5 hours | 1 to 3 USD | 50 |
El mínimo de 50-episodio es un mínimo, no un objetivo. Las propias preguntas frecuentes de NVIDIA son más exigentes: aproximadamente 100 trayectorias para una simple operación de recogida y colocación en una ubicación fija, 500 o más para escenas complejas o de varios pasos, y de 100 a 500 para manipulación fina. Si tienes 20 episodios, dedica la tarde a grabar en lugar de la noche a ajustar. La guía de recolección de datos cubre lo que separa un episodio útil de uno desperdiciado, graba tu primer conjunto de datos es la versión corta, y recolección de datos SO-100 es la específica para el brazo.
Paso 6: evaluación en bucle abierto antes de tocar el brazo
No coloque un nuevo punto de control en un brazo físico para averiguar si el entrenamiento funcionó. Ejecute primero la evaluación en bucle abierto. Reproduce un episodio grabado, solicita acciones al modelo en cada paso y grafica la predicción frente a la verdad fundamental con MSE y MAE. No cuesta nada y detecta los errores de mapeo de los pasos 2 y 3.
uv run python gr00t/eval/open_loop_eval.py \
--dataset-path ./my_dataset_lerobot \
--embodiment-tag NEW_EMBODIMENT \
--model-path /tmp/so100/checkpoint-20000 \
--traj-ids 0 \
--execution-horizon 16 \
--steps 400 \
--modality-keys single_arm gripperEl repositorio se niega deliberadamente a publicar un MSE objetivo para datos personalizados, y esa es la decisión correcta: el número depende de sus unidades de acción, su tarea y el tamaño de su conjunto de datos, por lo que un umbral copiado del brazo de otra persona no significa nada. Lo que es significativo es la tendencia. Aquí está la ejecución de referencia que el repositorio documenta en una única H100 con el conjunto de datos de demostración de cinco episodios y 2000 pasos.
| Punto de control | MSE promedio en trayectoria 0 | MAE promedio en trayectoria 0 |
|---|---|---|
| 500 | 87.5 | 5.63 |
| 1000 | 25.4 | 3.30 |
| 1500 | 13.2 | 2.18 |
| 2000 | 10.0 | 1.76 |
La forma es la señal, no los valores absolutos. El error debería disminuir constantemente a medida que los pasos de entrenamiento se acumulan. Promediado sobre los cinco episodios de entrenamiento en lugar de solo la trayectoria 0, el punto de control final del repositorio obtuvo aproximadamente 7.5 MSE y 1.5 MAE, por lo que incluso la ejecución de referencia se lee de manera diferente según los episodios que promedies. Registra tu propia línea de base con el comando de demostración sin modificar antes de cambiar cualquier cosa sobre tus propios datos: si no puedes reproducir una ejecución conocida como buena, no podrás distinguir un error de configuración de un problema de datos. El repositorio también mapea los síntomas comunes a sus causas, y cada uno de ellos es operacional en lugar de un error del modelo.
| Síntoma | Causa probable |
|---|---|
| MSE plano o en aumento a través de los puntos de control | Tasa de aprendizaje demasiado baja, o los datos no se están cargando en absoluto. Verifica --dataset-path y los workers del dataloader. |
| Curva de predicción plana o constante | Las claves de modality.json o --modality-config-path no coinciden. Las claves de acción no están mapeadas. |
| MSE enorme, o pérdida NaN durante el entrenamiento | Normalización de acción y estado. Verifica meta/stats y que los rangos de acción sean físicamente plausibles. |
| Bueno en la trayectoria 0, pobre en episodios no vistos | Escasez de datos, no un error. Cinco episodios de demostración no pueden generalizar. |
La otra ruta: lerobot-train en lugar de Isaac-GR00T
La versión actual de LeRobot, 0.6.1 en PyPI desde el 3 de agosto de 2026, ofrece una segunda y bastante diferente forma de ajustar los mismos pesos base. LeRobot expone GR00T N1.7 como un tipo de política y lo entrena a través de su propio lerobot-train punto de entrada. Dos cosas importan aquí. La CLI de LeRobot es un conjunto de scripts de consola, por lo que cualquier cosa que leas que diga python lerobot/scripts/train.py está obsoleto y no se ejecutará. Y LeRobot eliminó por completo el soporte para GR00T N1.5, rechazando los puntos de control y configuraciones de N1.5 con una nota de migración, por lo que si necesitas N1.5 a través de LeRobot, tienes que fijar lerobot==0.5.1, la última versión que lo soporta, publicada el 7 de abril de 2026.
pip install "lerobot[groot]" "lerobot[training]"
hf auth login
lerobot-train \
--dataset.repo_id=$HF_USER/$DATASET_NAME \
--dataset.image_transforms.enable=true \
--policy.type=groot \
--policy.device=cuda \
--policy.base_model_path=nvidia/GR00T-N1.7-3B \
--policy.embodiment_tag=new_embodiment \
--policy.chunk_size=16 \
--policy.n_action_steps=16 \
--policy.use_relative_actions=true \
--policy.relative_exclude_joints='["gripper"]' \
--policy.use_bf16=true \
--seed=42 \
--batch_size=64 \
--steps=20000 \
--save_freq=5000 \
--output_dir=$OUTPUT_DIR| Aspecto | Isaac-GR00T launch_finetune.py | lerobot-train --policy.type=groot |
|---|---|---|
| Versión del conjunto de datos | Solo LeRobot v2, se requiere conversión | Conjunto de datos nativo de LeRobot, sin degradación |
| Mapeo de modalidad | meta/modality.json más una configuración de datos de Python | sin modality.json; el comportamiento se establece mediante las banderas --policy.* en la línea de comandos |
| Semilla | ninguna bandera de semilla | --seed, LeRobot por defecto 1000 |
| Acciones relativas | ActionConfig por clave en la configuración de datos | --policy.use_relative_actions más --policy.relative_exclude_joints |
| Resultados de referencia publicados | Tendencia MSE de bucle abierto SO-100 en datos de demostración | Suites LIBERO, 96.5 por ciento de promedio en cuatro suites |
| Ruta de despliegue | run_gr00t_server.py más eval_so100.py sobre ZMQ | lerobot-rollout, con fragmentación en tiempo real (queue_threshold debe permanecer en o por debajo de 5) |
- Cada bandera es visible y modificable. Puedes descongelar el codificador visual, mover state_dropout_prob o acortar el horizonte de acción.
- Los gráficos de bucle abierto son archivos locales. Comparar checkpoint-5000 con checkpoint-20000 es un comando de shell.
- No dependes de que ninguna plataforma permanezca en línea, y el punto de control se guarda en tu disco en un formato estándar.
- Los ejemplos de benchmark del repositorio para LIBERO, SimplerEnv y DROID te proporcionan ejecuciones conocidas y buenas para reproducir antes de confiar en tus propios datos.
- El entorno es la mayor parte del trabajo. La versión de FFmpeg, CUDA_HOME, git-lfs, el backbone con puerta, torchcodec: ninguno de estos son problemas del modelo y cada uno de ellos detiene la ejecución.
- La conversión de v3.0 a v2.1 requiere un entorno virtual separado con su propio paso de instalación, y reescribe tu directorio de conjunto de datos en su lugar.
- El alquiler de GPU comienza a facturar cuando empiezas a depurar, no cuando comienza el entrenamiento, y nada detiene la instancia cuando la ejecución termina.
- Ninguna semilla significa que no hay reproducibilidad bit a bit, además de una varianza del 5 al 6 por ciento entre ejecuciones solo por la aumentación.
Dos formas de obtener el mismo punto de control
Usted alquila la GPU y controla cada paso. Realistamente, la primera vez le tomará una tarde y veinte minutos cada vez después.
- Grabe episodios con lerobot-record en el SO-100. Obtendrá un conjunto de datos LeRobot v3.0.
- Conviértalo a v2.1 con scripts/lerobot_conversion/convert_v3_to_v2.py en su propio entorno virtual.
- Escriba meta/modality.json y una configuración de modalidad en Python, registrada bajo EmbodimentTag.NEW_EMBODIMENT.
- Alquile una tarjeta de 80 GB, clone con submódulos, sincronice con uv, autentíquese con Hugging Face.
- Ejecute launch_finetune.py, luego open_loop_eval.py en varios puntos de control, y compare la tendencia del MSE antes de tocar el hardware.
- Extraiga el punto de control de la máquina antes de destruir la instancia, luego construya la ruta de servicio al brazo.
Copie el punto de control de la instancia alquilada antes de apagarla. --save-total-limit 5 también significa que los puntos de control más antiguos se eliminan a medida que avanza el entrenamiento, por lo que el punto de control que deseaba en el paso 5000 podría no existir más en el paso 20000.
El mismo trabajo como un formulario. Usted elige el modelo y el conjunto de datos, el backend alquila una GPU en el mercado spot según la VRAM requerida, ejecuta el entrenador y escribe los puntos de control en el almacenamiento de objetos. La guía de GR00T N1.7 en SO-100 es esta combinación exacta; la matriz de entrenamiento tiene todos los demás emparejamientos de modelos y brazos, incluyendo GR00T N1.7 en el SO-101.
| Lo que envía el entrenador groot1.7 | Valor |
|---|---|
| Tamaño del lote | 32 |
| Tasa de aprendizaje | 1e-4 |
| Pasos máximos | 20000 |
| Acumulación de gradiente | 1, y sí tiene efecto para este entrenador |
| Parámetro adicional expuesto en el formulario | saveSteps |
| Punto de control base | nvidia/GR00T-N1.7-3B |
| Formato de conjunto de datos aceptado | LeRobot v2.0 o v2.1 |
El conjunto de datos puede provenir de un ID de repositorio de Hugging Face, de su propia máquina o de una sesión que grabó con el cliente de escritorio. La inferencia es un paso separado: la plataforma aprovisiona un pod que sirve la política, y su cliente de robot local se comunica con ese endpoint. Los pods tienen un vigilante de inactividad y se autodestruyen después de un período de inactividad, por lo que una pestaña del navegador olvidada no generará cargos durante la noche. Si prefiere no hacer clic, las mismas operaciones existen en la CLI y el servidor MCP.
GR00T N1.7 y Pi0.5 son solo para la nube aquí; solo SmolVLA y ACT también se ejecutan localmente. El requisito de v2.1 tampoco desaparece, porque un conjunto de datos v3.0 aún debe convertirse antes de que el cargador GR00T lo acepte. Y nada escribe la semántica de su modality.json por usted: si las claves de su cámara o su clave de idioma son incorrectas, lo serán en ambas rutas. Consulte la documentación de entrenamiento para saber qué hace y qué no hace el backend en su nombre.

Volviendo a cargar el punto de control en el brazo
Isaac-GR00T utiliza una división cliente-servidor sobre ZMQ. La política se ejecuta en la GPU, y un cliente ligero en la máquina del robot envía observaciones y recibe fragmentos de acción. El ejemplo del SO-100 es lo suficientemente completo como para copiar: inicie run_gr00t_server.py con su punto de control y --embodiment-tag NEW_EMBODIMENT, luego ejecute eval_so100.py en el lado del robot con el puerto serie, el ID del robot, los índices de la cámara y la instrucción de idioma. Los nombres de las cámaras en ese comando deben coincidir con los nombres amigables de su modality.json, no con los números de dispositivo del sistema operativo.
# GPU side
uv run python gr00t/eval/run_gr00t_server.py \
--model-path /tmp/so100/checkpoint-20000 \
--embodiment-tag NEW_EMBODIMENT \
--device cuda:0 \
--host 0.0.0.0 --port 5555
# robot side, from gr00t/eval/real_robot/SO100
uv run --no-sync python eval_so100.py \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM2 \
--robot.id=orange_follower \
--robot.cameras="{ front: {type: opencv, index_or_path: 6, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 2, width: 640, height: 480, fps: 30}}" \
--policy_host=localhost --policy_port=5555 \
--lang_instruction="put the cube in the yellow bowl"Mientras vuelve a cablear el brazo: el SO-100 utiliza servomotores de bus Feetech STS3215 en un riel de 7.4 V. Alimentarlos con 12 V los destruye, y es un error fácil si también posee un LeKiwi, cuya base funciona a 12 V mientras que su brazo no. Verifique la alimentación antes del primer encendido, no después del humo. Consulte la página de hardware del SO-100 y SO-100 contra LeKiwi. Si el brazo se enciende pero nada se mueve, servo no responde es el lugar para empezar.
Ahora la parte honesta sobre dónde se ejecuta la política, porque el entrenamiento y el servicio tienen diferentes historias de hardware. El ajuste fino requiere 40 GB o más. La inferencia no: el README lo sitúa en 16 GB o más y nombra explícitamente la RTX 4090, por lo que una tarjeta que ya posee puede servir un checkpoint que nunca podría haber producido. Lo que decide si la política se siente receptiva no es la VRAM, es dónde reside el servidor. En AY-Robots GR00T N1.7 es solo en la nube, por lo que el bucle de control paga un viaje de ida y vuelta por internet público además de los 152 ms por paso de acción, y solo SmolVLA y ACT también se ejecutan localmente. Para tareas lentas de 'pick and place', un pod remoto es viable. Para cualquier cosa reactiva no lo es: la política se vuelve indecisa de una manera que se parece exactamente a un fallo de entrenamiento y no lo es. ACT a 20 ms por paso de acción es el modelo que tolera el bucle más ajustado, SmolVLA se sitúa en 245 ms, y ninguna cantidad de ajuste de latencia recupera un viaje de ida y vuelta que ya se ha gastado. Ejecute su primera política explica el lado del servicio de principio a fin.
Qué sale mal en realidad
- GatedRepoError en la primera ejecución. No se le ha concedido acceso a nvidia/Cosmos-Reason2-2B, o no se autenticó. Esto ocurre después de que el reloj de la GPU ya haya comenzado.
- Conjunto de datos rechazado al cargar. Casi siempre un conjunto de datos v3.0. Conviértalo a una versión anterior. Consulte conjunto de datos rechazado como v3.
- IndexError sobre dimensiones booleanas no coincidentes. Cambió delta_indices y no regeneró las estadísticas.
- Sin memoria en el lote 32. Reduzca --global-batch-size y aumente --gradient-accumulation-steps, o reduzca --num-shards-per-epoch, lo cual la configuración sugiere explícitamente cuando la VRAM es limitada. Consulte sin memoria durante el entrenamiento.
- La pérdida disminuye, la política no hace nada. No hay división de validación por defecto, por lo que una curva de entrenamiento limpia demuestra muy poco. Esta página cubre el diagnóstico.
- Funciona en su configuración y en ningún otro lugar. Esperado con un conjunto de datos pequeño filmado bajo una única condición de iluminación. NVIDIA recomienda la aumentación de fluctuación de color (colour jitter) más 20 a 50 episodios con diferentes iluminaciones. Más aquí.
- El efector final nunca cierra correctamente. Verifique que la acción del efector final sea ABSOLUTA y las articulaciones del brazo RELATIVAS, en ese orden en action_configs. El efector final no cierra enumera las otras causas.
- Una cámara se desconecta silenciosamente a mitad de la grabación. El episodio aún se guarda y la clave de video aún existe, por eso este es problemático. Cámara no detectada lo cubre.
El índice completo de modos de fallo se encuentra en . Si está eligiendo entre modelos en lugar de depurar uno, y tienen números de referencia con fuentes adjuntas, y es la comparación que la mayoría de la gente realmente necesita, porque es la elección entre un modelo que puede entrenar en la tarjeta debajo de su escritorio y uno para el que tiene que alquilar un nodo de 80 GB para ajustar. Para entender por qué estos modelos se comportan de la manera en que lo hacen, la y la valen la pena leer primero. Y si aún no posee un brazo, transmite un SO-100 físico sin necesidad de registrarse.
¿Cuántos episodios necesito antes de que valga la pena ajustar GR00T N1.7?▾
AY-Robots establece un mínimo de 50 episodios para el entrenador groot1.7. Las propias preguntas frecuentes de NVIDIA son más exigentes: aproximadamente 100 trayectorias para una simple operación de recoger y colocar en una ubicación fija, 500 o más para escenas complejas o de varios pasos, y de 100 a 500 para manipulación fina. Por debajo de 50, casi siempre es mejor grabar más datos que ajustar hiperparámetros. Si el éxito se estanca después de eso, NVIDIA recomienda HG-DAgger: ejecute la política, intervenga cuando falle y añada esas correcciones al conjunto de datos.
¿Por qué mi conjunto de datos no se carga y cómo sé qué versión es?▾
Abra meta/info.json y lea codebase_version. La CODEBASE_VERSION actual de LeRobot en main es v3.0, por lo que cualquier cosa grabada con una cadena de herramientas reciente es v3.0, y el cargador de GR00T espera v2. Convierta con scripts/lerobot_conversion/convert_v3_to_v2.py del repositorio Isaac-GR00T, que escribe codebase_version: v2.1 en el conjunto de datos convertido. El script se ejecuta en su propio entorno virtual porque necesita una versión de lerobot diferente a la que GR00T fija.
¿Puedo ajustar GR00T N1.7 en una RTX 4090?▾
No. NVIDIA recomienda 40 GB o más de VRAM para el ajuste fino y menciona nodos H100 o L40; otras tarjetas funcionan pero tardan mucho más. Una 4090 tiene 24 GB. AY-Robots solo ofrece GR00T N1.7 en el nivel A100 de 80 GB y H100 de 80 GB por la misma razón. La inferencia es otra historia: 16 GB son suficientes para servir el modelo, por lo que una 4090 puede ejecutar una política que no puede entrenar. Si desea un VLA que pueda entrenar con 24 GB, ese es SmolVLA con aproximadamente 450 millones de parámetros o ACT con aproximadamente 80 millones.
¿Por qué dos ejecuciones con flags idénticos dan checkpoints diferentes?▾
Porque launch_finetune.py no tiene semilla. Es una CLI de tyro generada a partir de una dataclass que no contiene un campo de semilla, por lo que nada fija el RNG. El repositorio señala por separado una varianza del 5 al 6 por ciento entre ejecuciones causada por la aumentación de imagen no determinista. Si la reproducibilidad importa, use la ruta de LeRobot en su lugar: lerobot-train acepta --seed y la receta publicada de GR00T pasa --seed=42.
¿Debo usar Isaac-GR00T o lerobot-train?▾
Use Isaac-GR00T si desea la implementación de referencia, control por clave sobre la representación de acciones, exportación a TensorRT o los ejemplos de benchmark para reproducir antes de confiar en sus propios datos. Use lerobot-train si su conjunto de datos ya es LeRobot v3.0 y prefiere no convertirlo, si desea una semilla, o si el resto de su pila ya es LeRobot. Ambos ajustan los mismos pesos nvidia/GR00T-N1.7-3B. Tenga en cuenta que LeRobot eliminó por completo el soporte para GR00T N1.5: los checkpoints de N1.5 son rechazados con una nota de migración, y debe fijar lerobot==0.5.1 para seguir usándolos.
¿Realmente necesito una cámara de muñeca además de una cámara frontal?▾
La configuración del SO-100 suministrada utiliza ambas, y modality.json mapea la frontal y la de muñeca como claves de video separadas. Puede entrenar con una cámara, y la tabla de latencia de la tarjeta del modelo se mide con una cámara, pero la vista de la muñeca es lo que le da a la política información útil sobre el efector final en el momento del contacto. Si el efector final cierra en el momento equivocado en sus rollouts, una cámara de muñeca faltante o mal apuntada es una de las primeras cosas a verificar.
Ajuste GR00T N1.7 en su SO-100 sin construir el entorno primero
Elija el modelo, el conjunto de datos y los hiperparámetros en un formulario. El backend alquila una A100 de 80 GB o H100 en el mercado spot, ejecuta el entrenador con un lote de 32, una tasa de aprendizaje de 1e-4 y 20000 pasos, y escribe los checkpoints en el almacenamiento de objetos. Aproximadamente de 4 a 12 USD por ejecución.
Abrir la guía de entrenamiento de GR00T N1.7Sources
- NVIDIA Isaac-GR00T repository README, N1.7 main branch
- Isaac-GR00T: Fine-tune on Custom Embodiments (NEW_EMBODIMENT)
- Isaac-GR00T: Finetuning Models for the SO100/SO101 Robot
- Isaac-GR00T: Robot Data Preparation Guide, the GR00T LeRobot format
- Isaac-GR00T: modality config and ActionConfig reference
- Isaac-GR00T: Policy API Guide and the embodiment tag list
- Isaac-GR00T: FinetuneConfig dataclass with every CLI default
- Isaac-GR00T: the shared finetune launcher wrapper
- GR00T N1.7 FAQ: data volume, augmentation and deployment
- nvidia/GR00T-N1.7-3B model card, including the inference timing table
- nvidia/Cosmos-Reason2-2B, the gated VLM backbone used by N1.7
- GR00T N1: An Open Foundation Model for Generalist Humanoid Robots
- NVIDIA Isaac GR00T N1.7: Open Reasoning VLA Model for Humanoid Robots
- LeRobot: GR00T Policy, the lerobot-train recipe
- LeRobot: Imitation Learning on Real-World Robots (lerobot-record, lerobot-train)
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started