
Um guia testado para o ajuste fino de NVIDIA GR00T N1.7 em um conjunto de dados LeRobot SO-100: flags reais, modality.json, o requisito v2.1, o custo de uma execução e as armadilhas.
A NVIDIA fornece um exemplo de ajuste fino para exatamente o braço que você provavelmente possui. Dentro do repositório Isaac-GR00T há uma pasta chamada demo_data/cube_to_bowl_5: cinco episódios, 4.148 quadros a 30 fps, já escritos como LeRobot v2.1, com uma configuração de modalidade correspondente em examples/SO100/. Seu meta/info.json relata robot_type: so101_follower, que no LeRobot é a mesma classe de configuração que so100_follower. Isso é genuinamente útil, porque significa que o caminho de referência para GR00T N1.7 em um braço de hobby de seis graus de liberdade é mantido pelas pessoas que escreveram o modelo. Não é uma demonstração humanóide reduzida, é o mesmo braço.
A má notícia é a distância entre I recorded 60 episodes e the arm does the task. Existem cerca de seis pontos onde este pipeline falha silenciosamente em vez de ruidosamente, e quatro deles estão em arquivos que a maioria das pessoas nunca abre: meta/modality.json, a configuração de dados Python, meta/relative_stats.json, e a própria string de versão do conjunto de dados. Este guia percorre o caminho manual de ponta a ponta com os comandos reais, depois mostra o mesmo trabalho como um formulário em AY-Robots. Tudo abaixo foi verificado em relação ao branch principal do Isaac-GR00T em 20 de agosto de 2026 (a linha de lançamento n1.7) e lerobot 0.6.1, publicado no PyPI em 3 de agosto de 2026. O upstream se move rapidamente, e onde uma flag foi renomeada, este artigo o indica.
O que você precisa saber antes de começar
- •O ajuste fino do GR00T N1.7 requer 40 GB ou mais de VRAM. A NVIDIA recomenda nós H100 ou L40. Uma RTX 4090 de 24 GB não fará este trabalho, embora treine SmolVLA e ACT.
- •O conjunto de dados deve ser LeRobot v2 (v2.0 ou v2.1) mais um meta/modality.json específico do GR00T. Um conjunto de dados LeRobot v3.0 não carrega e precisa ser convertido para uma versão anterior.
- •O ponto de entrada é gr00t/experiment/launch_finetune.py, um CLI tyro. Ele não possui a flag --seed, então as execuções não são reproduzíveis bit a bit.
- •Para um braço personalizado, a tag de embodiment é NEW_EMBODIMENT, e essa tag torna --modality-config-path obrigatório.
- •A receita SO-100 fornecida prevê as juntas do braço como deltas RELATIVOS e a garra como um alvo ABSOLUTO. Inverter essa combinação é uma falha silenciosa, não um erro.
- •Na AY-Robots, o mesmo trabalho é um formulário: 20000 passos, batch 32, taxa de aprendizado 1e-4, aproximadamente 4 a 12 USD no nível A100 80 GB ou H100.
O que GR00T N1.7 realmente é
GR00T N1.7 é um modelo de visão-linguagem-ação com o layout de sistema duplo descrito no artigo GR00T N1 original: um módulo de visão-linguagem que lê as câmeras e a instrução, e um transformador de difusão que transforma isso em um bloco de comandos motores contínuos. O N1.7 substituiu a primeira metade. O backbone Eagle do N1.6 foi removido, trocado por nvidia/Cosmos-Reason2-2B em uma arquitetura Qwen3-VL, e o modelo foi pré-treinado em aproximadamente 20.000 horas de vídeo humano egocêntrico, além dos dados do robô. A própria descrição da NVIDIA indica o valor de 20.854 horas e relata que passar de 1k para 20k horas mais do que dobra a conclusão média de tarefas.
A segunda metade também mudou, de maneiras que importam para sua execução. O cabeçote de ação caiu de 32 camadas de difusão para 16, o bloco de ação previsto cresceu de 16 para 40 passos, e a largura máxima de estado e ação passou de 29 para 132. Esses três números vêm do changelog no README do repositório; a própria publicação de lançamento da NVIDIA ainda descreve o Sistema 1 como um DiT de 32 camadas, então onde os dois discordam, confie no repositório que você está prestes a clonar. As ações são expressas por padrão em um espaço relativo de efetor final, deltas da pose atual em vez de alvos absolutos, o que permite que os priors de manipulação aprendidos com vídeo humano sejam transferidos para o controle do robô. O próprio cabeçote é um transformador de difusão de correspondência de fluxo, da mesma família do Pi0.5, mas com um backbone diferente à sua frente. Se você ainda está na geração anterior, N1.7 contra N1.5 aborda se a atualização justifica refazer seu pipeline.
| Propriedade | Valor | Origem |
|---|---|---|
| Parâmetros | 3,000,000,000 | Cartão do modelo Hugging Face |
| Backbone de visão-linguagem | nvidia/Cosmos-Reason2-2B (Qwen3-VL), gated no Hugging Face | README do repositório |
| Cabeçote de ação | Transformador de difusão de correspondência de fluxo, 16 camadas (N1.6 tinha 32) | README do repositório |
| Horizonte de ação previsto | 40 passos para o checkpoint base (N1.6 tinha 16) | getting_started/policy.md e README do repositório |
| Largura máxima de estado e ação | 132 (N1.6 tinha 29) | README do repositório |
| Licença de código | Apache 2.0 | Repositório Isaac-GR00T |
| Licença dos pesos | NVIDIA Open Model License Agreement | Cartão do modelo |
| Latência, H100 80 GB, PyTorch eager, 4 passos de denoising, 1 câmera | 85.8 ms de ponta a ponta, 11.7 Hz | Tabela de tempo do cartão do modelo |
| Mesmo hardware, pipeline completo TensorRT | 27.9 ms de ponta a ponta, 35.9 Hz | Tabela de tempo do cartão do modelo |
| Latência que a AY-Robots cita para seu GR00T N1.7 servido | 152 ms por passo de ação | Catálogo de políticas da AY-Robots |
Essas últimas três linhas explicam a maior parte da decepção relatada pelas pessoas. O valor de destaque de 27.9 ms é um motor TensorRT em um H100 com uma câmera e quatro passos de denoising. O PyTorch puro na mesma placa é de 85.8 ms, e o cartão do modelo indica uma diferença de 3.08x. Nenhum dos números inclui uma camada de serviço, uma segunda câmera ou um salto de rede. Os 152 ms por passo de ação que a AY-Robots cita para seu GR00T N1.7 servido é o valor com o serviço em loop, e uma viagem de ida e volta pela internet pública se soma a isso. Mais sobre isso no final. Para os números ao lado de outros modelos, GR00T N1.7 contra Pi0.5 e GR00T N1.7 contra SmolVLA os apresentam lado a lado.

O que a execução precisa antes de você digitar qualquer coisa
| Requisito | Ajuste Fino | Inferência |
|---|---|---|
| VRAM, orientação NVIDIA | 40 GB ou mais, H100 ou L40 recomendado | 16 GB ou mais, uma RTX 4090 funciona |
| Python e CUDA na dGPU | 3.12 e CUDA 12.8 | 3.12 e CUDA 12.8 |
| Backend de vídeo | torchcodec 0.8.0, FFmpeg 4 a 7 apenas | o mesmo |
| Formato do conjunto de dados | LeRobot v2 mais meta/modality.json | não aplicável |
| Acesso ao Hugging Face | aprovado para nvidia/Cosmos-Reason2-2B | o mesmo |
| Outras ferramentas | git-lfs e uv | uv |
| Nível de GPU AY-Robots para o treinador groot1.7 | A100 80 GB ou H100 80 GB | pod provisionado automaticamente |
Cada checkpoint GR00T, incluindo a base nvidia/GR00T-N1.7-3B, carrega nvidia/Cosmos-Reason2-2B no primeiro uso, e esse repositório é restrito. O README declara a falha exatamente: o carregamento do modelo falha com um GatedRepoError / 401 Client Error. O que ele não menciona é quando isso acontece, que é depois de você ter alugado a placa e a execução ter começado. Solicite acesso na página do modelo e, em seguida, execute uv run huggingface-cli login ou exporte HF_TOKEN antes de alugar qualquer coisa.
Passo 0: os próprios episódios
Tudo abaixo assume que você já gravou episódios. Se não o fez, esse é o verdadeiro primeiro passo e é ele que decide a qualidade do resultado, porque aprendizagem por imitação não consegue recuperar informações que não estão nos dados. Calibre ambos os braços primeiro, depois conduza o seguidor com um braço líder enquanto lerobot-record escreve os arquivos parquet e os fluxos da câmera. Se a calibração estiver desativada, os valores das juntas no seu conjunto de dados descrevem um robô ligeiramente diferente daquele que executará a política mais tarde, e nenhuma quantidade de treinamento corrige isso.
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=trueO próprio conselho da LeRobot é gravar pelo menos 50 episódios com cerca de 10 por localização de objeto, manter as câmeras fixas e manter o comportamento de preensão consistente. Adicione variação mais tarde, não no início. A regra prática a ser lembrada: se você não conseguiu realizar a tarefa sozinho apenas com as imagens da câmera, a política também não consegue. Para a configuração específica do braço, introdução ao SO-100 e a página LeRobot do SO-100 cobrem portas, calibração e índices de câmera. Em AY-Robots, você também pode fazer isso pela internet a partir do navegador usando teleoperação e gravar diretamente da sessão.
Passo 1: o conjunto de dados deve ser LeRobot v2.1
Este é o obstáculo mais comum. A CODEBASE_VERSION atual do LeRobot no `main` é v3.0, então qualquer coisa que você gravar hoje com uma toolchain atual sai como v3.0. O carregador do GR00T espera a v2. O repositório é explícito sobre o porquê: muitos conjuntos de dados upstream, como DROID, LIBERO e Bridge, são publicados na v2, e o suporte nativo para ambos está planejado, mas ainda não foi lançado. Portanto, a conversão é por sua conta, e ela é executada em seu próprio virtualenv por uma razão concreta: scripts/lerobot_conversion possui seu próprio pyproject que exige Python 3.10 ou 3.11 e fixa o lerobot a um commit git, enquanto o próprio Isaac-GR00T requer Python 3.12. Instale o conversor a partir da raiz do repositório e você obterá o pacote gr00t em vez disso, o que é o erro sobre o qual seu README adverte. Se você é novo no formato, a entrada do glossário conjunto de dados LeRobot explica o que realmente há dentro de um.
# 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_lerobotSe o conjunto de dados v3.0 já existe localmente, o script constrói o layout v2.1 ao lado dele e então troca: o original é movido para uma pasta irmã com a versão anexada, <name>_v3.0, e a cópia convertida assume o caminho original. (O docstring do próprio script chama essa pasta de _v30; o código anexa a string da versão, então o que você realmente obtém é _v3.0.) Segunda surpresa: a saída sempre aparece em <root>/<repo-id>, então --root examples/SO100/my_dataset_lerobot lhe dá examples/SO100/my_dataset_lerobot/<your-hf-user>/<your-dataset>, e esse caminho mais longo é o que --dataset-path deseja mais tarde. Quando um trabalho de treinamento rejeita seu conjunto de dados por motivos de versão, a página de conjunto de dados rejeitado como v3 lista os sintomas exatos.
A estrutura que o GR00T espera após a conversão é o layout clássico v2: meta/info.json, meta/episodes.jsonl, meta/tasks.jsonl, arquivos parquet em data/chunk-000/, arquivos MP4 em videos/chunk-000/observation.images.
Passo 2: modality.json, os seis números que decidem tudo
Em um conjunto de dados LeRobot, o estado do robô e a ação são armazenados como arrays float32 planos. Para um SO-100, ambos têm a forma [6]: cinco juntas do braço e uma garra. O conjunto de dados de demonstração os nomeia shoulder_pan.pos, shoulder_lift.pos, elbow_flex.pos, wrist_flex.pos, wrist_roll.pos, gripper.pos, mas esses nomes residem em info.json e nada no arquivo parquet indica qual índice é qual. meta/modality.json fornece esse mapeamento, e o GR00T não treinará sem ele. Aqui está o que o repositório envia para o SO-100, na íntegra.
{
"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"
}
}
}Copie-o para o seu conjunto de dados convertido em meta/modality.json e renomeie as chaves de vídeo para o nome real das suas câmeras. Se você gravou com uma única câmera superior chamada top, então original_key é observation.images.top e o nome amigável é o que sua configuração de dados irá referenciar. Os dois devem concordar, e nenhum deles verifica o outro para você. A anotação de linguagem é pior, porque a mesma chave precisa aparecer em três lugares.
| Camada | Arquivo | Formato SO-100 usado no repositório |
|---|---|---|
| Coluna Parquet | data/chunk-*/episode_*.parquet | annotation.human.task_description |
| Chave modality.json | meta/modality.json, under "annotation", without the annotation. prefix | human.task_description |
| modality_keys na configuração de dados | your so100_config.py | annotation.human.task_description |
Os segmentos após annotation. são escolhidos por quem criou o conjunto de dados. Os dados de demonstração SO-100 usam annotation.human.task_description; LIBERO e SimplerEnv usam annotation.human.action.task_description. Ambos são válidos. Se você copiou uma configuração de um exemplo LIBERO e a apontou para sua própria gravação SO-100, o canal de linguagem não resolve nada e o modelo treina com uma instrução vazia. A perda ainda diminui. A política ainda faz algo. Apenas ignora o que você mandou fazer.
Passo 3: a configuração de dados, braço relativo e garra absoluta
A configuração de modalidade é um arquivo Python em vez de JSON, porque também decide como cada grupo de ação é representado. Esta é a parte do fluxo de trabalho N1.7 que não existia na mesma forma no N1.5, e a parte que vale a pena ler duas vezes. A configuração SO-100 fornecida prevê as cinco juntas do braço como deltas RELATIVOS do estado atual e a garra como uma posição alvo ABSOLUTA, porque um sinal binário de abrir ou fechar se comporta melhor como um alvo do que como um 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)Dois detalhes aqui lhe custarão um dia se você não os souber. Primeiro, action_configs é posicional: a documentação exige o mesmo comprimento e a mesma ordem que modality_keys, e eles são diretos sobre a consequência de errar, que é a aplicação silenciosa da representação errada. Sua garra é treinada como um delta e seu braço como um alvo absoluto, e não há mensagem de erro. Segundo, register_modality_config afirma que a tag ainda não está registrada, então uma segunda configuração NEW_EMBODIMENT no mesmo processo Python falha com Embodiment tag ... already registered. Você não pode importar duas dessas para um único script. Uma terceira regra é imposta posteriormente, na implantação: os delta_indices da ação devem ser o intervalo contíguo começando em zero. Uma janela esparsa como [0, 4, 8] é rejeitada, porque tudo a jusante indexa o bloco previsto linearmente e, de outra forma, executaria as linhas erradas.
As estatísticas de normalização, em particular meta/relative_stats.json, são calculadas para o comprimento do horizonte que você tinha ao gerá-las. Encurtar o horizonte de ação de 16 para 8 sem regenerar e o treinamento falha com IndexError: boolean index did not match indexed array ... dimension is 8 but corresponding boolean dimension is 16. A correção é um comando: python gr00t/data/stats.py --dataset-path <path> --embodiment-tag NEW_EMBODIMENT --modality-config-path examples/SO100/so100_config.py. Execute-o após qualquer alteração em delta_indices.
Passo 4: o ambiente
N1.7 moveu o repositório para e Python 3.12. O antigo caminho conda mais pip install -e . ainda existe em uma seção recolhida do README, mas avisa que as dependências da GPU, incluindo flash-attn e TensorRT, podem precisar de instalação manual. Use uv a menos que tenha uma razão específica para não o fazer. No que diz respeito ao flash-attn, um detalhe evita confusão: você verá Installing flash-attn impresso em cada uv run. Não está a ser reconstruído. uv está a revalidar um wheel fixado por URL que já está em cache, e isso leva dois ou três segundos.
- 1Instalar git-lfs e depois clonar com submódulos
git-lfs é obrigatório, não opcional. Sem ele, os arquivos parquet em demo_data/ são baixados como stubs de ponteiro, e a execução da demonstração falha em um conjunto de dados que parece presente na listagem de arquivos.
bashsudo apt install git-lfs && git lfs install git clone --recurse-submodules https://github.com/NVIDIA/Isaac-GR00T cd Isaac-GR00T - 2Instalar uv e sincronizar o ambiente
A instalação padrão puxa as dependências da GPU, incluindo flash-attn e TensorRT. Numa imagem A100 ou H100 nova, este é o passo único mais longo, por isso faça-o antes de começar a prestar atenção a qualquer outra coisa.
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')" - 3Autenticar-se no Hugging Face
Faça isso antes do primeiro lançamento do treinamento, não depois que ele falhar oito minutos depois.
bashuv run huggingface-cli login # or: export HF_TOKEN=<your_token> - 4Verificação de sanidade nos dados de demonstração SO-100 fornecidos
Antes de tocar na sua própria gravação, execute 2000 passos em demo_data/cube_to_bowl_5. São cinco episódios, termina rápido e prova o ambiente em vez dos seus dados. Se esta execução falhar, nada que faça ao seu conjunto de dados ajudará.
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 suporta apenas FFmpeg 4 a 7, e o Ubuntu 25.10 e posteriores vêm com a versão 8. O erro é Could not load libtorchcodec, o que parece uma instalação quebrada em vez de um conflito de versão. Instale um runtime mais antigo, por exemplo conda install -c conda-forge 'ffmpeg<8', e coloque suas bibliotecas em LD_LIBRARY_PATH. CUDA_HOME não está definido. O fine-tuning falha completamente. Execute bash scripts/deployment/dgpu/install_deps.sh uma vez, ou apenas export CUDA_HOME=/usr/local/cuda.
Passo 5: o comando de fine-tune e quais são os valores padrão de suas flags
Troque o conjunto de dados de demonstração pelo seu e adicione os parâmetros que você realmente deseja. Abaixo está a forma completa que o repositório usa em seu próprio tutorial de nova incorporação, incluindo as flags de aumento e checkpointing que o exemplo curto do README omite. Isso é no sentido restrito: o backbone de linguagem e o codificador visual permanecem congelados, e o que é treinado é o projetor e o cabeçalho de ação de difusão.
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 | Padrão em FinetuneConfig | O que faz |
|---|---|---|
| --global-batch-size | 64 | Batch total em todas as GPUs antes da acumulação de gradiente. Os exemplos fornecidos usam 32. |
| --learning-rate | 1e-4 | O mesmo valor que AY-Robots envia para seu treinador groot1.7. |
| --max-steps | 10000 | Total de passos do otimizador. O wrapper examples/finetune.sh também tem 10000 como padrão. |
| --gradient-accumulation-steps | 1 | Multiplica o batch efetivo. Valores acima de 1 emitem um aviso informando o tamanho acumulado. |
| --save-steps and --save-total-limit | 1000 and 5 | Frequência de checkpoint e quantos são mantidos. Os mais antigos são excluídos. |
| --weight-decay and --warmup-ratio | 1e-5 and 0.05 | Definido explicitamente por examples/finetune.sh também. |
| --state-dropout-prob | 0.2 in the CLI, 0.8 in the model config | Descarta aleatoriamente o estado proprioceptivo durante o treinamento. Diminua-o se sua tarefa depender do estado. |
| --tune-llm and --tune-visual | False and False | O backbone permanece congelado por padrão. |
| --tune-projector and --tune-diffusion-model | True and True | O projetor e o cabeçalho de ação de difusão são o que realmente treinam. |
| --use-percentiles | True | Normaliza com q01 e q99 em vez de min e max brutos. |
| --dataloader-num-workers | 2 | O carregador é baseado em CPU por design. Os exemplos aumentam isso para 4. |
| --seed | does not exist | Não há flag de seed nesta CLI. |
Essa última linha não é um erro de digitação. launch_finetune.py é uma CLI tyro gerada a partir de uma dataclass, e essa dataclass não possui um campo de seed. O README observa separadamente uma variação de 5 a 6 por cento entre as execuções, causada por aumento de imagem não determinístico. Duas execuções com flags idênticas não produzirão checkpoints idênticos, o que é muito importante ao tentar decidir se uma mudança de hiperparâmetro ajudou ou se você teve sorte. Para comparação, o próprio treinador do LeRobot usa seed 1000 por padrão, e a receita LeRobot GR00T passa --seed=42 explicitamente.
O fine-tuning é executado com eval_strategy="no", então não há curva de perda de validação. Você obtém apenas a perda de treinamento e nada mais. O guia de nova incorporação diz para ativá-lo com --eval-strategy steps --eval-steps 500, mas essa flag não existe em launch_finetune.py: a CLI é gerada por tyro a partir da dataclass FinetuneConfig, e eval_strategy, eval_steps e eval_batch_size são campos de TrainingConfig. Seus valores padrão são "no", 500 e 2. Para acessá-los, use o ponto de entrada completo gr00t/experiment/launch_train.py, onde a flag aninhada é --training.eval-strategy. De qualquer forma, uma perda de treinamento em queda por si só diz muito pouco sobre generalização, que é exatamente a situação descrita em a perda cai, mas a política não faz nada.
Quanto custa uma execução de 20000 passos
GR00T N1.7 precisa de uma placa de 80 GB, então a questão do custo tem uma resposta restrita. Na AY-Robots, o treinador groot1.7 é executado na camada A100 80 GB ou H100 80 GB, onde uma execução leva de 3 a 6 horas a 1.20 a 2.00 USD por hora no mercado spot. Isso é aproximadamente 4 a 12 USD para a tarefa padrão de 20000 passos. A mesma tarefa em SmolVLA ou ACT é executada em uma placa de 24 GB a 0.30 a 0.60 USD por hora e 1 a 3 USD por execução. Essa é a verdadeira desvantagem: GR00T custa cerca de quatro vezes mais por tentativa, e você não pode executá-lo na 4090 debaixo da sua mesa.
| Modelo | Nível de GPU | Execução típica | Custo típico | Episódios 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 |
O mínimo de 50 episódio é um piso, não um objetivo. O FAQ da própria NVIDIA é mais exigente: aproximadamente 100 trajetórias para uma simples operação de pegar e colocar em local fixo, 500 ou mais para cenas complexas ou com múltiplos passos, e 100 a 500 para manipulação fina. Se você tem 20 episódios, passe a tarde gravando em vez de passar a noite ajustando. O guia de coleta de dados aborda o que separa um episódio útil de um desperdiçado, registre seu primeiro conjunto de dados é a versão curta, e coleta de dados SO-100 é a versão específica para o braço.

Passo 6: avaliação em malha aberta antes de tocar no braço
Não coloque um novo checkpoint em um braço físico para descobrir se o treinamento funcionou. Execute a avaliação em malha aberta primeiro. Ela reproduz um episódio gravado, solicita ações ao modelo em cada etapa e plota a previsão em relação à verdade fundamental com MSE e MAE. Não custa nada e detecta os erros de mapeamento das etapas 2 e 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 gripperO repositório recusa-se deliberadamente a publicar um MSE alvo para dados personalizados, e essa é a decisão correta: o número depende das suas unidades de ação, da sua tarefa e do tamanho do seu conjunto de dados, então um limiar copiado do braço de outra pessoa não significa nada. O que é significativo é a tendência. Aqui está a execução de referência que o repositório documenta em um único H100 com o conjunto de dados de demonstração de cinco episódios e 2000 etapas.
| Checkpoint | MSE Médio na trajetória 0 | MAE Médio na trajetória 0 |
|---|---|---|
| 500 | 87.5 | 5.63 |
| 1000 | 25.4 | 3.30 |
| 1500 | 13.2 | 2.18 |
| 2000 | 10.0 | 1.76 |
A forma é o sinal, não os valores absolutos. O erro deve diminuir constantemente à medida que passos de treinamento se acumulam. Com uma média de todos os cinco episódios de treinamento, em vez de apenas a trajetória 0, o checkpoint final do repositório obteve aproximadamente 7.5 MSE e 1.5 MAE, então mesmo a execução de referência é lida de forma diferente dependendo de quais episódios você faz a média. Registre sua própria linha de base no comando de demonstração não modificado antes de alterar qualquer coisa em seus próprios dados: se você não conseguir reproduzir uma execução comprovadamente boa, não poderá distinguir um erro de configuração de um problema de dados. O repositório também mapeia os sintomas comuns para as causas, e cada um deles é operacional, e não um bug do modelo.
| Sintoma | Causa provável |
|---|---|
| MSE estável ou aumentando entre checkpoints | Taxa de aprendizado muito baixa, ou os dados não estão sendo carregados. Verifique --dataset-path e os workers do dataloader. |
| Curva de previsão estável ou constante | As chaves de modality.json ou --modality-config-path não correspondem. As chaves de ação não estão mapeadas. |
| MSE enorme, ou perda NaN durante o treinamento | Normalização de ação e estado. Verifique meta/stats e se os intervalos de ação são fisicamente plausíveis. |
| Bom na trajetória 0, ruim em episódios não vistos | Escassez de dados, não um bug. Cinco episódios de demonstração não podem generalizar. |
A outra rota: lerobot-train em vez de Isaac-GR00T
A versão atual do LeRobot, 0.6.1 no PyPI desde 3 de agosto de 2026, oferece uma segunda e bastante diferente maneira de ajustar os mesmos pesos base. O LeRobot expõe o GR00T N1.7 como um tipo de política e o treina através de seu próprio lerobot-train ponto de entrada. Duas coisas importam aqui. O CLI do LeRobot é um conjunto de scripts de console, então qualquer coisa que você leia que diga python lerobot/scripts/train.py está desatualizado e não será executado. E o LeRobot removeu o suporte ao GR00T N1.5 inteiramente, rejeitando checkpoints e configurações N1.5 com uma nota de migração, então se você precisar do N1.5 através do LeRobot, você terá que fixar lerobot==0.5.1, a última versão que o suporta, publicada em 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 |
|---|---|---|
| Versão do Dataset | Apenas LeRobot v2, conversão necessária | Dataset nativo LeRobot, sem downgrade |
| Mapeamento de Modalidade | meta/modality.json mais uma configuração de dados Python | sem modality.json; comportamento definido pelas flags --policy.* na linha de comando |
| Seed | nenhuma flag de seed | --seed, padrão LeRobot 1000 |
| Ações relativas | ActionConfig por chave na configuração de dados | --policy.use_relative_actions mais --policy.relative_exclude_joints |
| Resultados de referência publicados | Tendência MSE de loop aberto SO-100 em dados de demonstração | Suítes LIBERO, média de 96.5 por cento em quatro suítes |
| Caminho de implantação | run_gr00t_server.py mais eval_so100.py via ZMQ | lerobot-rollout, com chunking em tempo real (queue_threshold deve permanecer em ou abaixo de 5) |
- Cada flag é visível e alterável. Você pode descongelar o codificador visual, mover state_dropout_prob ou encurtar o horizonte de ação.
- Os gráficos de loop aberto são arquivos locais. Comparar checkpoint-5000 com checkpoint-20000 é um comando de shell.
- Você não depende de nenhuma plataforma permanecer online, e o checkpoint fica no seu disco em um formato padrão.
- Os exemplos de benchmark do repositório para LIBERO, SimplerEnv e DROID fornecem execuções comprovadamente boas para reproduzir antes de confiar nos seus próprios dados.
- O ambiente é a maior parte do trabalho. Versão do FFmpeg, CUDA_HOME, git-lfs, o backbone restrito, torchcodec: nenhum desses são problemas do modelo e cada um deles interrompe a execução.
- A conversão de v3.0 para v2.1 requer um virtualenv separado com sua própria etapa de instalação, e ele reescreve seu diretório de dataset no local.
- O aluguel de GPU começa a ser cobrado quando você inicia a depuração, não quando o treinamento começa, e nada interrompe a instância quando a execução termina.
- Nenhuma seed significa nenhuma reprodutibilidade bit a bit, além de 5 a 6 por cento de variância entre execuções apenas pela aumentação.
Duas maneiras de obter o mesmo checkpoint
Você aluga a GPU e controla cada etapa. Realisticamente, isso leva uma tarde na primeira vez e vinte minutos nas vezes seguintes.
- Grave episódios com lerobot-record no SO-100. Você obtém um conjunto de dados LeRobot v3.0.
- Converta-o para v2.1 com scripts/lerobot_conversion/convert_v3_to_v2.py em seu próprio ambiente virtual.
- Escreva meta/modality.json e uma configuração de modalidade Python, registrada sob EmbodimentTag.NEW_EMBODIMENT.
- Alugue uma placa de 80 GB, clone com submódulos, sincronize com uv, autentique-se no Hugging Face.
- Execute launch_finetune.py, depois open_loop_eval.py em vários checkpoints, e compare a tendência do MSE antes de tocar no hardware.
- Retire o checkpoint da máquina antes de destruir a instância e, em seguida, construa o caminho de serviço para o braço.
Copie o checkpoint da instância alugada antes de desligá-la. --save-total-limit 5 também significa que checkpoints mais antigos são excluídos à medida que o treinamento avança, então o checkpoint que você queria na etapa 5000 pode não existir mais na etapa 20000.
O mesmo trabalho, mas como um formulário. Você escolhe o modelo e o conjunto de dados, o backend aluga uma GPU no mercado spot pela VRAM necessária, executa o treinador e grava os checkpoints no armazenamento de objetos. O guia GR00T N1.7 no SO-100 é exatamente essa combinação; a matriz de treinamento possui todas as outras combinações de modelo e braço, incluindo GR00T N1.7 no SO-101.
| O que o treinador groot1.7 envia | Valor |
|---|---|
| Tamanho do lote | 32 |
| Taxa de aprendizado | 1e-4 |
| Máximo de etapas | 20000 |
| Acumulação de gradiente | 1, e isso tem efeito para este treinador |
| Parâmetro extra exposto no formulário | saveSteps |
| Checkpoint base | nvidia/GR00T-N1.7-3B |
| Formato de conjunto de dados aceito | LeRobot v2.0 ou v2.1 |
O conjunto de dados pode vir de um ID de repositório do Hugging Face, da sua própria máquina, ou de uma sessão que você gravou com o cliente desktop. A inferência é uma etapa separada: a plataforma provisiona um pod que serve a política, e seu cliente robô local se comunica com esse endpoint. Os pods possuem um watchdog de inatividade e se autodestroem após um período de inatividade, para que uma aba de navegador esquecida não gere cobranças durante a noite. Se você preferir não clicar, as mesmas operações existem na CLI e no servidor MCP.
GR00T N1.7 e Pi0.5 são apenas para nuvem aqui; apenas SmolVLA e ACT também rodam localmente. O requisito v2.1 também não desaparece, porque um conjunto de dados v3.0 ainda precisa ser convertido antes que o carregador GR00T o aceite. E nada escreve sua semântica modality.json para você: se suas chaves de câmera ou sua chave de idioma estiverem erradas, elas estarão erradas em ambas as rotas. Consulte a documentação de treinamento para saber o que o backend faz e não faz em seu nome.

Colocando o checkpoint de volta no braço
Isaac-GR00T usa uma divisão servidor-cliente sobre ZMQ. A política é executada na GPU, e um cliente leve na máquina do robô envia observações e recebe blocos de ação. O exemplo do SO-100 é completo o suficiente para copiar: inicie run_gr00t_server.py com seu checkpoint e --embodiment-tag NEW_EMBODIMENT, então execute eval_so100.py no lado do robô com a porta serial, o ID do robô, os índices da câmera e a instrução de idioma. Os nomes das câmeras nesse comando devem corresponder aos nomes amigáveis do seu modality.json, não aos números de dispositivo do sistema operacional.
# 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"Enquanto você reconecta o braço: o SO-100 utiliza servos de barramento Feetech STS3215 em um trilho de 7.4 V. Alimentá-los com 12 V os destrói, e é um erro fácil se você também possui um LeKiwi, cuja base funciona a 12 V enquanto seu braço não. Verifique a alimentação antes da primeira ligação, não depois da fumaça. Veja a página de hardware do SO-100 e SO-100 contra LeKiwi. Se o braço liga, mas nada se move, servo não respondendo é o lugar para começar.
Agora a parte honesta sobre onde a política é executada, porque o treinamento e o serviço têm histórias de hardware diferentes. O ajuste fino requer 40 GB ou mais. A inferência não: o README a coloca em 16 GB ou mais e nomeia explicitamente a RTX 4090, então uma placa que você já possui pode servir um checkpoint que nunca poderia ter produzido. O que decide se a política parece responsiva não é a VRAM, é onde o servidor está localizado. Nos AY-Robots, o GR00T N1.7 é apenas na nuvem, então o loop de controle paga uma viagem de ida e volta pela internet pública além dos 152 ms por etapa de ação, e apenas SmolVLA e ACT também são executados localmente. Para operações lentas de pegar e colocar, um pod remoto é tolerável. Para qualquer coisa reativa, não é: a política se torna hesitante de uma forma que se parece exatamente com uma falha de treinamento e não é. O ACT a 20 ms por etapa de ação é o modelo que tolera o loop mais apertado, o SmolVLA fica em 245 ms, e nenhuma quantidade de ajuste de latência compensa uma viagem de ida e volta que já foi gasta. Execute sua primeira política detalha o lado do serviço de ponta a ponta.
O que realmente dá errado
- GatedRepoError na primeira execução. Você não recebeu acesso a nvidia/Cosmos-Reason2-2B, ou não se autenticou. Isso acontece depois que o clock da GPU já foi iniciado.
- Dataset rejeitado no carregamento. Quase sempre um dataset v3.0. Converta-o para uma versão anterior. Veja dataset rejeitado como v3.
- IndexError sobre dimensões booleanas incompatíveis. Você alterou delta_indices e não regenerou as estatísticas.
- Sem memória no batch 32. Reduza --global-batch-size e aumente --gradient-accumulation-steps, ou reduza --num-shards-per-epoch, o que a configuração sugere explicitamente quando a VRAM é limitada. Veja sem memória durante o treinamento.
- A perda cai, a política não faz nada. Não há divisão de validação por padrão, então uma curva de treinamento limpa prova muito pouco. Esta página aborda o diagnóstico.
- Funciona na sua configuração e em nenhum outro lugar. Esperado com um pequeno dataset filmado sob uma única condição de iluminação. A NVIDIA recomenda aumento de jitter de cor mais 20 a 50 episódios em diferentes iluminações. Mais aqui.
- Garra nunca fecha corretamente. Verifique se a ação da garra é ABSOLUTE e as juntas do braço RELATIVE, nessa ordem em action_configs. Garra não fecha lista as outras causas.
- Uma câmera falha silenciosamente no meio da gravação. O episódio ainda é salvo e a chave de vídeo ainda existe, por isso este é complicado. Câmera não detectada aborda isso.
O índice completo de modos de falha está em . Se você está escolhendo entre modelos em vez de depurar um, e têm números de benchmark com fontes anexadas, e é a comparação que a maioria das pessoas realmente precisa, porque é a escolha entre um modelo que você pode treinar na placa debaixo da sua mesa e um que você precisa alugar um nó de 80 GB para ajustar. Para entender por que esses modelos se comportam da maneira que o fazem, o e o valem a pena ser lidos primeiro. E se você ainda não possui um braço, transmite um SO-100 físico sem necessidade de inscrição.
Quantos episódios preciso antes que o ajuste fino do GR00T N1.7 valha a pena?▾
A AY-Robots estabelece um mínimo de 50 episódios para o treinador groot1.7. O FAQ da própria NVIDIA é mais exigente: aproximadamente 100 trajetórias para um simples pegar e colocar em um local fixo, 500 ou mais para cenas complexas ou de múltiplos passos, e 100 a 500 para manipulação fina. Abaixo de 50, quase sempre é melhor registrar mais dados do que ajustar hiperparâmetros. Se o sucesso estagnar depois disso, a NVIDIA recomenda HG-DAgger: execute a política, intervenha quando ela falhar e adicione essas correções ao dataset.
Por que meu dataset falha ao carregar e como sei qual versão ele é?▾
Abra meta/info.json e leia codebase_version. O CODEBASE_VERSION atual do LeRobot no main é v3.0, então qualquer coisa gravada com uma toolchain recente é v3.0, e o carregador GR00T espera v2. Converta com scripts/lerobot_conversion/convert_v3_to_v2.py do repositório Isaac-GR00T, que escreve codebase_version: v2.1 no dataset convertido. O script é executado em seu próprio virtualenv porque precisa de uma versão diferente do lerobot daquela que o GR00T fixa.
Posso ajustar o GR00T N1.7 em uma RTX 4090?▾
Não. A NVIDIA recomenda 40 GB ou mais de VRAM para ajuste fino e nomeia nós H100 ou L40; outras placas funcionam, mas levam muito mais tempo. Uma 4090 tem 24 GB. A AY-Robots oferece o GR00T N1.7 apenas nos níveis A100 80 GB e H100 80 GB pelo mesmo motivo. A inferência é outra história: 16 GB são suficientes para servir o modelo, então uma 4090 pode executar uma política que não pode treinar. Se você quiser um VLA que possa treinar em 24 GB, esse é o SmolVLA com cerca de 450 M parâmetros ou o ACT com cerca de 80 M.
Por que duas execuções com flags idênticas geram checkpoints diferentes?▾
Porque launch_finetune.py não tem seed. É um CLI tyro gerado a partir de uma dataclass que não contém campo seed, então nada fixa o RNG. O repositório observa separadamente uma variação de 5 a 6 por cento entre as execuções causada por aumento de imagem não determinístico. Se a reprodutibilidade for importante, use a rota LeRobot: lerobot-train aceita --seed e a receita GR00T publicada passa --seed=42.
Devo usar Isaac-GR00T ou lerobot-train?▾
Use Isaac-GR00T se você quiser a implementação de referência, controle por chave sobre a representação da ação, exportação TensorRT, ou os exemplos de benchmark para reproduzir antes de confiar em seus próprios dados. Use lerobot-train se seu dataset já for LeRobot v3.0 e você preferir não convertê-lo, se quiser uma seed, ou se o resto da sua stack já for LeRobot. Ambos ajustam os mesmos pesos nvidia/GR00T-N1.7-3B. Observe que o LeRobot abandonou totalmente o suporte ao GR00T N1.5: os checkpoints N1.5 são rejeitados com uma nota de migração, e você precisa fixar lerobot==0.5.1 para continuar a usá-los.
Eu realmente preciso de uma câmera de pulso além de uma câmera frontal?▾
A configuração SO-100 fornecida usa ambas, e modality.json mapeia a frente e o pulso como chaves de vídeo separadas. Você pode treinar com uma câmera, e a tabela de latência do cartão do modelo é medida com uma câmera, mas a visão do pulso é o que dá à política informações utilizáveis sobre a garra no momento do contato. Se a garra fechar no momento errado em seus rollouts, uma câmera de pulso ausente ou mal direcionada é uma das primeiras coisas a verificar.
Ajuste fino do GR00T N1.7 no seu SO-100 sem construir o ambiente primeiro
Escolha modelo, dataset e hiperparâmetros em um formulário. O backend aluga um A100 de 80 GB ou H100 no mercado spot, executa o treinador com batch 32, taxa de aprendizado 1e-4 e 20000 passos, e escreve checkpoints para armazenamento de objetos. Aproximadamente 4 a 12 USD por execução.
Abrir o guia de treinamento do 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