A página do guia AY-Robots para treinar GR00T N1.7 em um braço SO-100, mostrando o nível de GPU necessário, o formato do conjunto de dados e os padrões do treinador
GR00T N1.7SO-100Ajuste finoLeRobotVLA

Como Treinar GR00T N1.7 no Seu Próprio Conjunto de Dados SO-100

AY-Robots ResearchAugust 23, 202628 min de leitura

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.

PropriedadeValorOrigem
Parâmetros3,000,000,000Cartão do modelo Hugging Face
Backbone de visão-linguagemnvidia/Cosmos-Reason2-2B (Qwen3-VL), gated no Hugging FaceREADME do repositório
Cabeçote de açãoTransformador de difusão de correspondência de fluxo, 16 camadas (N1.6 tinha 32)README do repositório
Horizonte de ação previsto40 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ção132 (N1.6 tinha 29)README do repositório
Licença de códigoApache 2.0Repositório Isaac-GR00T
Licença dos pesosNVIDIA Open Model License AgreementCartão do modelo
Latência, H100 80 GB, PyTorch eager, 4 passos de denoising, 1 câmera85.8 ms de ponta a ponta, 11.7 HzTabela de tempo do cartão do modelo
Mesmo hardware, pipeline completo TensorRT27.9 ms de ponta a ponta, 35.9 HzTabela de tempo do cartão do modelo
Latência que a AY-Robots cita para seu GR00T N1.7 servido152 ms por passo de açãoCatá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.

A página do modelo AY-Robots para GR00T N1.7 mostrando a contagem de parâmetros, o nível da GPU, a latência de inferência e os pontos fortes e limites declarados do modelo
A página /policies/groot-n1-7 contém a mesma faixa de especificações que você montaria manualmente a partir do cartão do modelo e do README do repositório.

O que a execução precisa antes de você digitar qualquer coisa

RequisitoAjuste FinoInferência
VRAM, orientação NVIDIA40 GB ou mais, H100 ou L40 recomendado16 GB ou mais, uma RTX 4090 funciona
Python e CUDA na dGPU3.12 e CUDA 12.83.12 e CUDA 12.8
Backend de vídeotorchcodec 0.8.0, FFmpeg 4 a 7 apenaso mesmo
Formato do conjunto de dadosLeRobot v2 mais meta/modality.jsonnão aplicável
Acesso ao Hugging Faceaprovado para nvidia/Cosmos-Reason2-2Bo mesmo
Outras ferramentasgit-lfs e uvuv
Nível de GPU AY-Robots para o treinador groot1.7A100 80 GB ou H100 80 GBpod provisionado automaticamente
O backbone restrito irá pará-lo na primeira execução

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.

bash
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=true
lerobot-record em um LeRobot atual. O comprimento do episódio padrão é 60 s e o tempo de reinício é 60 s. so100_follower e so101_follower estão ambos registrados na mesma classe de configuração LeRobot, razão pela qual o exemplo Isaac-GR00T SO100 usa os nomes so101; ambos funcionam em um SO-100. Os nomes das câmeras que você escolhe aqui (front, wrist) são os nomes que devem reaparecer em modality.json.

O 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.

bash
# 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_lerobot
O conversor aceita --repo-id, um --root opcional e --force-conversion, que exclui qualquer snapshot local existente e o baixa novamente. Ele escreve codebase_version: v2.1 em meta/info.json.
A conversão sobrescreve no local

Se 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./, e um arquivo extra que o LeRobot padrão não possui. Esse arquivo extra é onde a maioria das falhas restantes reside.

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.

json
{
  "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"
    }
  }
}
examples/SO100/modality.json. Os índices são baseados em zero e seguem o fatiamento do Python, então single_arm é [0:5] e gripper é [5:6].

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.

CamadaArquivoFormato SO-100 usado no repositório
Coluna Parquetdata/chunk-*/episode_*.parquetannotation.human.task_description
Chave modality.jsonmeta/modality.json, under "annotation", without the annotation. prefixhuman.task_description
modality_keys na configuração de dadosyour so100_config.pyannotation.human.task_description
Por que a chave de linguagem confunde as pessoas

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.

python
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)
examples/SO100/so100_config.py, reduzido ao essencial. NON_EEF significa espaço de junta; EEF esperaria um vetor de nove dimensões de x, y, z mais uma rotação 6D.

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.

Altere delta_indices e você deve regenerar as estatísticas

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.

  1. 1
    Instalar 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.

    bash
    sudo apt install git-lfs && git lfs install
    git clone --recurse-submodules https://github.com/NVIDIA/Isaac-GR00T
    cd Isaac-GR00T
  2. 2
    Instalar 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.

    bash
    curl -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')"
  3. 3
    Autenticar-se no Hugging Face

    Faça isso antes do primeiro lançamento do treinamento, não depois que ele falhar oito minutos depois.

    bash
    uv run huggingface-cli login   # or: export HF_TOKEN=<your_token>
  4. 4
    Verificaçã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á.

    bash
    CUDA_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
Duas armadilhas de ambiente que parecem bugs do modelo

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.

bash
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
GPU única. Para oito placas, substitua o lançador por uv run torchrun --nproc_per_node=8 --master_port=29500 e defina --num-gpus 8. Use uv run torchrun, não apenas torchrun, ou você obterá o ambiente errado.
FlagPadrão em FinetuneConfigO que faz
--global-batch-size64Batch total em todas as GPUs antes da acumulação de gradiente. Os exemplos fornecidos usam 32.
--learning-rate1e-4O mesmo valor que AY-Robots envia para seu treinador groot1.7.
--max-steps10000Total de passos do otimizador. O wrapper examples/finetune.sh também tem 10000 como padrão.
--gradient-accumulation-steps1Multiplica o batch efetivo. Valores acima de 1 emitem um aviso informando o tamanho acumulado.
--save-steps and --save-total-limit1000 and 5Frequência de checkpoint e quantos são mantidos. Os mais antigos são excluídos.
--weight-decay and --warmup-ratio1e-5 and 0.05Definido explicitamente por examples/finetune.sh também.
--state-dropout-prob0.2 in the CLI, 0.8 in the model configDescarta aleatoriamente o estado proprioceptivo durante o treinamento. Diminua-o se sua tarefa depender do estado.
--tune-llm and --tune-visualFalse and FalseO backbone permanece congelado por padrão.
--tune-projector and --tune-diffusion-modelTrue and TrueO projetor e o cabeçalho de ação de difusão são o que realmente treinam.
--use-percentilesTrueNormaliza com q01 e q99 em vez de min e max brutos.
--dataloader-num-workers2O carregador é baseado em CPU por design. Os exemplos aumentam isso para 4.
--seeddoes not existNã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.

A validação está desativada por padrão, e a flag documentada não está nesta CLI

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.

ModeloNível de GPUExecução típicaCusto típicoEpisódios mínimos
GR00T N1.7A100 80 GB or H100 80 GB3 to 6 hours4 to 12 USD50
GR00T N1.5A100 80 GB or H100 80 GB3 to 6 hours4 to 12 USD50
Pi0.5A100 80 GB or H100 80 GB3 to 6 hours4 to 12 USD50
SmolVLARTX 4090 or any 24 GB card2 to 5 hours1 to 3 USD30
ACTRTX 4090 or any 24 GB card2 to 5 hours1 to 3 USD50

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.

O guia de treinamento AY-Robots para GR00T N1.7 no SO-100, mostrando a faixa de especificações com o nível de GPU, formato de conjunto de dados necessário e os padrões do treinador
O guia /train/groot-n1-7-on-so-100 apresenta os fatos que você, de outra forma, reconstruiria manualmente: nível de GPU, formato do conjunto de dados e os padrões exatos que o treinador envia.

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.

bash
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 gripper
Os gráficos são salvos em /tmp/open_loop_eval/traj_<id>.jpeg a menos que você passe --save-plot-path. Padrões: --execution-horizon 16, --steps 200, --denoising-steps 4, --traj-ids 0.

O 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.

CheckpointMSE Médio na trajetória 0MAE Médio na trajetória 0
50087.55.63
100025.43.30
150013.22.18
200010.01.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.

SintomaCausa provável
MSE estável ou aumentando entre checkpointsTaxa 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 constanteAs 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 treinamentoNormalizaçã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 vistosEscassez 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.

bash
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
A receita nativa do LeRobot GR00T N1.7. Observe relative_exclude_joints: a garra é excluída de ações relativas, que é a mesma decisão que so100_config.py toma com ActionRepresentation.ABSOLUTE.
AspectoIsaac-GR00T launch_finetune.pylerobot-train --policy.type=groot
Versão do DatasetApenas LeRobot v2, conversão necessáriaDataset nativo LeRobot, sem downgrade
Mapeamento de Modalidademeta/modality.json mais uma configuração de dados Pythonsem modality.json; comportamento definido pelas flags --policy.* na linha de comando
Seednenhuma flag de seed--seed, padrão LeRobot 1000
Ações relativasActionConfig por chave na configuração de dados--policy.use_relative_actions mais --policy.relative_exclude_joints
Resultados de referência publicadosTendência MSE de loop aberto SO-100 em dados de demonstraçãoSuítes LIBERO, média de 96.5 por cento em quatro suítes
Caminho de implantaçãorun_gr00t_server.py mais eval_so100.py via ZMQlerobot-rollout, com chunking em tempo real (queue_threshold deve permanecer em ou abaixo de 5)
Executando o fine-tune você mesmo
Vantagens
  • 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.
Desvantagens
  • 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.

  1. Grave episódios com lerobot-record no SO-100. Você obtém um conjunto de dados LeRobot v3.0.
  2. Converta-o para v2.1 com scripts/lerobot_conversion/convert_v3_to_v2.py em seu próprio ambiente virtual.
  3. Escreva meta/modality.json e uma configuração de modalidade Python, registrada sob EmbodimentTag.NEW_EMBODIMENT.
  4. Alugue uma placa de 80 GB, clone com submódulos, sincronize com uv, autentique-se no Hugging Face.
  5. 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.
  6. 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.
A etapa que todos esquecem

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.

A matriz de treinamento AY-Robots com cinco modelos de política como linhas e quatro braços robóticos como colunas, cada célula ligando a um guia de treinamento específico
A matriz /train: cinco modelos contra quatro braços. A linha GR00T N1.7 também cobre o SO-101, Koch v1.1 e LeKiwi.

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.

bash
# 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"
--execution-horizon controla quantos dos passos previstos são executados antes do replanejamento. Deve ser no máximo o action_horizon da política, e esse número é o comprimento dos delta_indices de ação na sua configuração de modalidade, não o do modelo base. A configuração SO-100 fornecida prevê 16, então 16 é o seu limite; o checkpoint base nvidia/GR00T-N1.7-3B está configurado para 40, e policy.md afirma claramente que os checkpoints ajustados podem diferir. Exceda-o e você receberá um ValueError nomeando ambos os números. 8 é o valor que a documentação sugere para implantação em tempo real. O antigo nome da flag --action-horizon ainda funciona, mas avisa.
7.4 V, não 12 V

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.7

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started