The AY-Robots policy comparison table showing GR00T N1.7 and GR00T N1.5 side by side with parameter counts, GPU tier, inference latency and minimum episode counts
GR00TVLAMigrationNVIDIAFine-tuningLeRobot

GR00T N1.5 to N1.7: What Changed and How to Migrate

AY-Robots ResearchAugust 23, 202620 min read

What actually changed between GR00T N1.5 and N1.7: the Cosmos-Reason2 backbone swap, relative actions, why old checkpoints will not load, dataset compatibility, and when to stay.

NVIDIA shipped GR00T N1.5 on 11 June 2025, then N1.6, and GR00T N1.7 is now the General Availability release on the main branch of the Isaac-GR00T repository. The version numbers suggest a point upgrade. They are misleading. The vision-language backbone was replaced, the action space went from absolute to relative, state and action dimensions grew from 64 and 32 to 132, and the fine-tuning entry point moved to a different file with different flags.

This is a migration guide, not a launch post. It covers what changed, whether your LeRobot dataset survives the move, whether an old checkpoint still loads (it does not), which flags were renamed, and when staying on N1.5 is correct. Every number below was read from a config file, a source file or a README on 2026-08-23, and where upstream contradicts itself that is said plainly.

What you need to know

  • N1.7 is not a drop-in upgrade. Its checkpoint declares model_type Gr00tN1d7, N1.5 declares gr00t_n1_5, and no converter exists.
  • The VLM backbone moved from a vendored Eagle model to nvidia/Cosmos-Reason2-2B, a 2.44 B parameter model built on Qwen3-VL-2B-Instruct and gated on Hugging Face. Every N1.7 checkpoint pulls it on first load.
  • Your recorded data is fine. Both read a GR00T flavour of LeRobot v2 with a meta/modality.json, so you rewrite the config, not the episodes.
  • Relative actions did not start with N1.7. N1.6 already predicted state-relative chunks; N1.7 makes it a relative EEF space shared with human video.
  • Licensing reversed: N1.5 weights are noncommercial, N1.7 weights ship under the NVIDIA Open Model License and repo code is Apache 2.0.
  • On AY-Robots both stay trainable: A100 80 GB or H100 80 GB, 50 episodes minimum, LeRobot v2.0 or v2.1.

Two different models that share a family name

The fastest way to see how far apart they are is to read the config.json files side by side. Each is public, and each is what the loader parses. Here is what they declare as of 2026-08-23, with N1.6 in the middle.

config.json fieldGR00T N1.5GR00T N1.6GR00T N1.7
architecturesGR00T_N1_5Gr00tN1d6Gr00tN1d7
model_typegr00t_n1_5Gr00tN1d6Gr00tN1d7
VLM backboneNVEagle/eagle_er-qwen3_1_7B-Siglip2_400M_stage1_5_128gpu_er_v7_1mlp_nops (vendored)nvidia/Eagle-Block2A-2B-v2nvidia/Cosmos-Reason2-2B
action_horizon165040
max_state_dim64128132
max_action_dim32128132
diffusion_model_cfg.num_layers163232
model_dtypefloat32bfloat16bfloat16
select_layer121616
tune_top_llm_layersnot present40
use_relative_actionnot presenttruenot present as a flag; relative EEF is the default
transformers_version4.51.34.51.34.57.1

The parameter count is unchanged at roughly 3 billion in all three, which is why the headline stayed the same and the internals did not. Note the transformers pin: N1.5 and N1.6 record 4.51.3, N1.7 records 4.57.1. One environment cannot serve both, which matters more day to day than any architectural bullet.

bash
# Read the declarations yourself, no clone and no GPU needed
curl -s https://huggingface.co/nvidia/GR00T-N1.5-3B/raw/main/config.json \
  | python3 -c "import json,sys; c=json.load(sys.stdin); h=c['action_head_cfg']; \
print(c['model_type'], 'horizon', c['action_horizon'], 'state', h['max_state_dim'])"

curl -s https://huggingface.co/nvidia/GR00T-N1.7-3B/raw/main/config.json \
  | python3 -c "import json,sys; c=json.load(sys.stdin); \
print(c['model_type'], 'horizon', c['action_horizon'], 'state', c['max_state_dim'])"
Even the shape of the config differs: in N1.5 the dimensions live under action_head_cfg, in N1.7 they are top level.

The backbone swap has the widest blast radius

N1.5 used a vendored Eagle vision-language model, frozen during both pretraining and fine-tuning. N1.6 switched to nvidia/Eagle-Block2A-2B-v2 and unfroze the top four LLM layers. N1.7 replaces Eagle entirely with nvidia/Cosmos-Reason2-2B, a 2.44 billion parameter VLM whose Hugging Face metadata lists Qwen/Qwen3-VL-2B-Instruct as its base model, and sets tune_top_llm_layers back to 0. The stated gain is flexible resolution: images are encoded in their native aspect ratio without padding.

The trap that eats an afternoon: the backbone is gated

nvidia/Cosmos-Reason2-2B requires you to accept a license on Hugging Face before it downloads, and every GR00T N1.7 checkpoint loads it on first use, including the base nvidia/GR00T-N1.7-3B. You can confirm the gate without an account: a plain curl of the model card returns 401. Without access, loading dies with a GatedRepoError or 401 Client Error naming the backbone rather than GR00T, so the stack trace points somewhere unhelpful. Request access, then huggingface-cli login or export HF_TOKEN. N1.5 had no gated dependency, so this catches every pipeline once. See training job stuck queued.

There is a quieter consequence. A vision-language-action model inherits its language grounding from the backbone, so prompts tuned against Eagle can land differently after the swap, especially for tasks that name objects. If your policy carried an odd prompt that happened to work, re-test it.

The action head, the horizon, and relative actions

N1.7 keeps the flow-matching diffusion transformer head, so the architecture from the GR00T N1 paper still describes it: a vision-language module reads the scene and a diffusion transformer denoises an action chunk. What changed is the chunk, and what the numbers inside it mean.

BehaviourGR00T N1.5GR00T N1.7
Action headFlow-matching DiT, 16 layersFlow-matching DiT, 32 layers in the shipped base config
Predicted chunk length16 steps40 in the base config; the SO-100 example config asks for 16
Action representationAbsolute joint or end-effector targetsRelative deltas for most axes, gripper absolute in the SO-100 example
When relative arrivednot applicableN1.6 introduced state-relative chunks; N1.7 makes it a shared relative EEF space
Normalization defaultmin_max in the shipped so100 data configq01/q99 percentiles (use_percentiles defaults to True)
Human video in pretrainingFLARE latent-alignment objective, which NVIDIA says unlocks learning from human ego video20K hours of EgoScale video, shared relative EEF representation
Rollout flag--action-horizon--execution-horizon

The relative action space is NVIDIA's stated reason for N1.7's cross-embodiment behaviour: a delta means the same thing on a human arm and a robot arm, so priors from human video transfer. It is also the change most likely to break a naive port. Copy an N1.5 modality config forward and you feed absolute targets to a model expecting deltas, which reads as a calibration fault rather than a config fault. The N1.7 SO-100 example sets ActionRepresentation.RELATIVE for the arm joints and ActionRepresentation.ABSOLUTE for the gripper, since a binary open and close works better as a target.

Where the README and the shipped weights disagree

The README's collapsed "Detailed changes from N1.6" list does not match the published config.json files, read on 2026-08-23. It claims the DiT drops 32 to 16 layers (N1.7 config: num_layers 32), select_layer 16 to 12 (config: 16), load_bf16 true to false (config: true), state and action dims 29 to 132 (N1.6 config: 128), action_horizon 16 to 40 (N1.6 config: 50), transformers 4.51.3 to 4.57.3 (N1.7 config: 4.57.1). Only tune_top_llm_layers 4 to 0 matches. Several "before" values are N1.5 numbers, so the list looks written against the wrong baseline. Treat config.json as authoritative for whatever revision you pull.

Do your old N1.5 checkpoints still load? No.

This is usually the first question and the answer is unambiguous. The N1.5 model class lived in a flat module, gr00t/model/gr00t_n1.py, holding the class GR00T_N1_5. That file is gone from main, and so is scripts/gr00t_finetune.py. The packaged layout arrived with N1.6: the n1d6 branch holds a gr00t_n1d6 package, main holds gr00t_n1d7, and there is no gr00t_n1d5 package and never was. LeRobot went further and removed N1.5 support outright rather than letting it fail obscurely.

text
GR00T N1.5 support was removed from LeRobot. To keep using an N1.5
checkpoint, pin the last release that supports it:
`pip install 'lerobot==0.5.1'`. To use the current release, migrate to
GR00T N1.7 (model_version='n1.7', base model nvidia/GR00T-N1.7-3B).
The guidance LeRobot raises when it detects an N1.5 checkpoint, config or processor. It lives in src/lerobot/policies/groot/configuration_groot.py, where the aliases n1.5, n1_5, n1d5, n15 and 1.5 are kept only so they can be recognised and rejected.

The code comment there is unusually honest: the legacy identifier is retained so the N1.7 loader can fail loudly rather than silently treating an N1.5 checkpoint as N1.7. Mirror that in your own tooling: a silent mis-load produces a policy that runs, emits actions, and is wrong.

Keep the old path alive before you delete anything

NVIDIA left the previous releases on branches: n1d5 and n1d6. Nothing forces a deadline. Keep a second virtualenv pinned to n1d5 and lerobot==0.5.1, keep the N1.5 checkpoints in object storage, and decommission only after the N1.7 policy beats them on your arm.

The AY-Robots policies comparison table listing GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT with parameter counts, required GPU, inference latency and minimum episodes
GR00T N1.7 and GR00T N1.5 sit side by side on /policies with the same GPU tier and the same 50-episode minimum, which is why this migration is a software problem, not a hardware one.

Dataset compatibility: episodes survive, config does not

The good news dominates. Both versions consume a GR00T flavour of the LeRobot v2 format: parquet under data/chunk-000/, mp4 under videos/chunk-000/, and a GR00T-specific meta/modality.json mapping the concatenated state and action arrays into named fields. That schema did not change, so a dataset recorded a year ago for an SO-100 is still valid input.

What changed is the layer above. N1.5 selected a registered Python class by name with --data-config so100, and that class carried its transforms inline: crop scale 0.95, resize to 224x224, color jitter, min_max normalization. N1.7 replaced it with --modality-config-path, a Python file you supply that builds ModalityConfig objects and calls register_modality_config. The transforms moved onto the CLI as --color-jitter-params, --shortest-image-edge, --crop-fraction and --use-percentiles.

  1. 1
    Confirm the dataset version first

    GR00T reads LeRobot v2. A v3.0 dataset will not load, and the downstream failure is not obvious.

    bash
    python3 -c "import json;print(json.load(open('my_dataset/meta/info.json'))['codebase_version'])"
    # expect: v2.0 or v2.1
  2. 2
    Convert down from v3 if needed

    The repo ships a helper with its own pyproject.toml, installed separately so its LeRobot pin does not fight the GR00T one.

    bash
    cd scripts/lerobot_conversion
    uv venv && source .venv/bin/activate
    uv pip install -e . --verbose
    python convert_v3_to_v2.py --repo-id <DATASET_REPO_ID>
  3. 3
    Put modality.json back where the loader expects it

    Conversion does not produce the GR00T-specific mapping file. The SO-100 example maps single_arm to indices 0 to 5 and gripper to 5 to 6, for both state and action, with video keys front and wrist.

    bash
    cp examples/SO100/modality.json \
      my_dataset/meta/modality.json
  4. 4
    Rewrite the data config as a modality config

    The real porting work: rebuild the old class as a file registering ModalityConfig objects under EmbodimentTag.NEW_EMBODIMENT, action representation set explicitly per key.

    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],
                                modality_keys=["front", "wrist"]),
        "state": ModalityConfig(delta_indices=[0],
                                modality_keys=["single_arm", "gripper"]),
        "action": ModalityConfig(
            delta_indices=list(range(0, 16)),
            modality_keys=["single_arm", "gripper"],
            action_configs=[
                ActionConfig(rep=ActionRepresentation.RELATIVE,
                             type=ActionType.NON_EEF,
                             format=ActionFormat.DEFAULT),
                ActionConfig(rep=ActionRepresentation.ABSOLUTE,
                             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)
The normalization default flipped and nothing warns you

The N1.5 SO-100 data config normalized state and action with min_max. The N1.7 finetune config defaults use_percentiles to True, which its own docstring defines as q01/q99 percentile statistics instead of full min/max. On clean data the difference is small. With a few outlier frames - a servo at its limit, one bad reading - the two produce visibly different action scaling, and the symptom is a policy that consistently undershoots or overshoots. When comparing versions, set the flag explicitly on both sides. See also joint stops early and dataset rejected as v3.

The CLI is a different program now

Both entry points are tyro CLIs generated from a dataclass, so --help is authoritative. Here is the mapping between the flags you had and the flags you need.

GR00T N1.5GR00T N1.7What to watch
scripts/gr00t_finetune.pygr00t/experiment/launch_finetune.pyThe old path returns 404 on the main branch
--data-config so100--modality-config-path examples/SO100/so100_config.pyA registered name becomes a Python file you write
--batch-size 32 (per device)--global-batch-size 32 (summed over GPUs, pre-accumulation)Same number, different meaning; on 8 GPUs this is an 8x cut
--lora_rank 64 --lora_alpha 128removedgr00t/utils/peft.py is gone; no LoRA path in the N1.7 config
--video-backend torchcodec|decord|torchvision_avremovedtorchcodec only, and it needs FFmpeg 4 to 7, not 8
--embodiment-tag defaults to new_embodiment--embodiment-tag is requiredIt has no default in FinetuneConfig, so the run refuses to start without it
--action-horizon (rollout)--execution-horizonHow many predicted actions execute per policy call
--resume--resume-from-checkpointDefaults to False, so a rerun into an existing output-dir starts fresh
report_to defaults to wandb--use-wandb, defaults to FalseBut examples/finetune.sh sets USE_WANDB=1 unless you override it
not available--ds-weights-alpha, --state-dropout-probDataset mixture weighting and state-input dropout
bash
# GR00T N1.5, the way it was (n1d5 branch)
python scripts/gr00t_finetune.py \
    --dataset-path ./my_dataset \
    --data-config so100 \
    --embodiment-tag new_embodiment \
    --batch-size 32 \
    --max-steps 20000 \
    --num-gpus 1

# GR00T N1.7, the way it is (main branch)
CUDA_VISIBLE_DEVICES=0 uv run python \
    gr00t/experiment/launch_finetune.py \
    --base-model-path nvidia/GR00T-N1.7-3B \
    --dataset-path ./my_dataset \
    --embodiment-tag NEW_EMBODIMENT \
    --modality-config-path examples/SO100/so100_config.py \
    --num-gpus 1 \
    --output-dir /tmp/so100_finetune \
    --max-steps 20000 \
    --global-batch-size 32 \
    --dataloader-num-workers 4
The N1.7 invocation follows the repository's own new-embodiment example. Embodiment tags are case insensitive.
Defaults that bite, straight from finetune_config.py

global_batch_size 64, learning_rate 1e-4, max_steps 10000, gradient_accumulation_steps 1, dataloader_num_workers 2, save_steps 1000, save_total_limit 5, warmup_ratio 0.05, state_dropout_prob 0.2, tune_llm False, tune_visual False, tune_projector True, tune_diffusion_model True. Three matter. save_total_limit: 5 prunes older checkpoints, so a long run will not keep the early ones you wanted for a learning-curve check. state_dropout_prob is 0.2 on the CLI but 0.8 in the model config, and the benchmark scripts override it per suite - lower it if your task leans on proprioceptive state. And neither version lets you set a seed from the CLI: N1.5 hardcodes seed=42 inside gr00t_finetune.py, N1.7's FinetuneConfig has no seed field at all. The README puts run-to-run variance from non-deterministic image augmentation at 5 to 6 percent, so treat smaller single-run deltas with suspicion.

Does the new model actually do better?

The most comparable public evidence is LIBERO, because both branches publish a table for it: examples/Libero/README.md on n1d5 and examples/LIBERO/README.md on main.

LIBERO suiteGR00T N1.5 (n1d5 branch)GR00T N1.7 (main branch)
Spatial46/50 (92%)195/200 (97.65%)
Goal43/50 (86%)195/200 (97.5%)
Object46/50 (92%)197/200 (98.45%)
10 (Long)38/50 (76%), 60K steps189/200 (94.35%), 20K steps
Batch size used128, and 72 with grad accum 4 for Goal640
Episodes per suite50200
This table is not a controlled ablation

The N1.5 README says its numbers came from "minimal hyperparameter tuning" and were "intended primarily for demonstration purposes", and points at published work scoring higher. The runs also differ in batch size (128 versus 640), evaluation episodes (50 versus 200), and training length on the Long suite (60K versus 20K steps). The direction is credible; the gap is not a measurement. LeRobot's N1.7 integration reports a different set again from its own harness - 95, 100, 98 and 93 percent for Spatial, Object, Goal and Long - which shows the harness matters as much as the model.

For the wider picture, the arena tracks 85 VLA models with 332 benchmark results, each linked to its paper or model card, with separate entries for GR00T N1.5, GR00T N1.6 and GR00T N1.7.

The AY-Robots arena leaderboard, a sortable table of 85 vision-language-action models with 332 benchmark results, each linked to its source paper or model card
The arena keeps N1.5, N1.6 and N1.7 as separate rows rather than one GR00T entry, because their published results come from different harnesses.

Licensing: the quiet reason most teams move

This gets less attention than the backbone swap and decides more migrations. The N1.5 card says the model is "ready for non-commercial use" and links the NVIDIA OneWay Noncommercial License. The N1.7 card says "ready for commercial/non-commercial use" and links the NVIDIA Open Model License, with repo code under Apache 2.0. If you built a product on an N1.5 fine-tune, that driver does not care whether N1.7 is better on your task.

Moving from N1.5 to N1.7
What you gain
  • Weights you can use commercially under the NVIDIA Open Model License, with Apache 2.0 code.
  • A maintained branch. N1.5 is frozen and LeRobot has already dropped it.
  • Relative end-effector actions and 20K hours of EgoScale human video in pretraining.
  • Higher published LIBERO success rates on all four suites, per the repo's own tables.
  • Slightly faster on this platform: 152 ms per action step against 165 ms for N1.5.
  • Full ONNX and TensorRT export, plus documented Jetson Thor, Orin and DGX Spark install paths.
What it costs you
  • Every existing N1.5 checkpoint is dead weight. No converter, no shortcut; retrain from the new base.
  • A gated Hugging Face dependency in the critical path of every load, including CI.
  • LoRA is gone, so fine-tuning on a 24 GB card the way N1.5 allowed no longer exists.
  • Guidance moved up: N1.5 documented 4090 fine-tuning with --no-tune_diffusion_model, N1.7 recommends 40 GB or more.
  • A whole new environment: N1.5 was tested on Python 3.10 and CUDA 12.4, N1.7 wants Python 3.12 and CUDA 12.8.
  • torchcodec is the only video backend and needs FFmpeg 4 to 7, so a distro shipping FFmpeg 8 breaks the install.

When staying on N1.5 is correct

Migration is not free, and a working N1.5 policy on a fixed task beats an untested N1.7 one. Stay put if any of these describe you.

  • You have a deployed policy hitting its target on one arm, one cell, one task, and nothing is changing. A frozen branch is a stable branch.
  • Your use is academic or internal, so the noncommercial license was never a constraint.
  • Your hardware tops out at 24 GB and you relied on LoRA. Plan a hardware step, or look at SmolVLA and ACT, which run on any 24 GB card.
  • You cannot accept a gated dependency in an air-gapped or tightly reviewed build. Cosmos-Reason2-2B is not optional for N1.7.
  • You are mid-experiment. Changing the base model invalidates every baseline you recorded; finish, then migrate.
  • Your task is fast and reactive rather than slow pick-and-place. Neither version fixes that, and 13 ms will not either.

One thing that is not a reason to stay: fear that your recorded episodes are wasted. The format is the part that survived intact, and a well-recorded dataset outlives every model generation you train on it. The data-collection guide covers what makes one durable.

Two ways to run the migration

You need 40 GB or more for fine-tuning (the repo recommends H100 or L40 nodes; A6000 works but converges slower), CUDA 12.8, Python 3.12 and FFmpeg between 4 and 7. Budget the first day for the environment, not the model.

  1. 1
    Clone with submodules and build the environment

    git-lfs is required for the demo parquet files, and uv handles GPU dependencies pip does not.

    bash
    sudo apt install git-lfs && git lfs install
    git clone --recurse-submodules https://github.com/NVIDIA/Isaac-GR00T
    cd Isaac-GR00T
    sudo apt-get update && sudo apt-get install -y ffmpeg
    curl -LsSf https://astral.sh/uv/install.sh | sh
    uv sync --python 3.12
    uv run python -c "import gr00t; print('GR00T installed successfully')"
  2. 2
    Unlock the gated backbone before you touch the model

    Accept the license on the Cosmos-Reason2-2B page, then authenticate. Do this before the first launch, not after it fails at 3 a.m.

    bash
    uv run huggingface-cli login
    # or: export HF_TOKEN=<your_token>
  3. 3
    Prove the stack works on the shipped example first

    demo_data/cube_to_bowl_5 is five SO-100 episodes with a matching modality config. If it does not run, your own data will not either.

    bash
    CUDA_VISIBLE_DEVICES=0 NUM_GPUS=1 uv run bash examples/finetune.sh \
      --base-model-path nvidia/GR00T-N1.7-3B \
      --dataset-path demo_data/cube_to_bowl_5 \
      --modality-config-path examples/SO100/so100_config.py \
      --embodiment-tag NEW_EMBODIMENT \
      --output-dir /tmp/so100_finetune
  4. 4
    Establish a baseline you can compare against

    Upstream deliberately publishes no target MSE here, because five episodes will not transfer to your task. The curve shape is the signal. Their reference run on 1x H100 at --max-steps 2000, on traj 0, went 87.5 at step 500, 25.4 at 1000, 13.2 at 1500, 10.0 at 2000.

    bash
    uv run python gr00t/eval/open_loop_eval.py \
      --dataset-path demo_data/cube_to_bowl_5 \
      --embodiment-tag NEW_EMBODIMENT \
      --model-path /tmp/so100_finetune/checkpoint-2000 \
      --traj-ids 0 \
      --execution-horizon 16 \
      --steps 400 \
      --modality-keys single_arm gripper
  5. 5
    Serve the policy and drive the arm

    Server on the GPU box, thin client next to the servos, port 5555 by default.

    bash
    uv run python gr00t/eval/run_gr00t_server.py \
      --model-path /tmp/so100_finetune/checkpoint-2000 \
      --embodiment-tag NEW_EMBODIMENT \
      --device cuda:0 --host 0.0.0.0 --port 5555

Keep the old environment alive in parallel. A second virtualenv on the n1d5 branch with transformers 4.51.3 costs disk and nothing else, and it is the only way to run the A/B that justifies the migration.

Latency and cost after the move

N1.7 is marginally faster per action step than N1.5 here, 152 ms against 165 ms, on the same GPU tier. That is real but small, and it does not change the class of task either model can do. The inference latency constraint is unchanged: the policy has to sit next to the servos for anything fast. Across the five trainable models the control loop runs from 20 ms to 485 ms per step, and public-internet round trips on top of a 152 ms loop turn a working policy into a hesitant one. Remote inference suits slow pick-and-place, not fast reactive motion.

GPU tierModelsTypical runSpot priceCost per run
A100 80 GB or H100 80 GBGR00T N1.7, GR00T N1.5, Pi0.53 to 6 hours1.20 to 2.00 USD per hourabout 4 to 12 USD
RTX 4090 or any 24 GB cardSmolVLA, ACT2 to 5 hours0.30 to 0.60 USD per hourabout 1 to 3 USD

Inference pods carry an idle watchdog and destroy themselves after an idle period, so a forgotten migration test does not keep billing. Full numbers are on the pricing page, and the training docs record what the form sends to the trainer.

The migration checklist

  1. Record the N1.5 baseline first: success rate on your task, over enough trials to mean something. Without it the migration has no verdict.
  2. Check meta/info.json for codebase_version v2.0 or v2.1, and convert down from v3 if needed.
  3. Rewrite the data config as a modality config file: RELATIVE for the arm axes, ABSOLUTE for the gripper.
  4. Request access to nvidia/Cosmos-Reason2-2B and authenticate everywhere, including CI.
  5. Run the shipped demo_data/cube_to_bowl_5 fine-tune end to end before touching your own data.
  6. Retrain from nvidia/GR00T-N1.7-3B on the same dataset and episode count, with --use-percentiles set explicitly on both sides.
  7. Compare on the arm, not on a benchmark, over enough trials to clear the 5 to 6 percent run-to-run variance. Then decommission n1d5.

If the retrained policy converges but the arm does nothing useful, that is a mapped failure rather than a migration problem. Loss falls, policy does nothing and policy only works in one setup are the two you will hit most. The CLI exposes the same operations if you would rather script the A/B than click it.

The AY-Robots head to head comparison page for GR00T N1.7 against Pi0.5, showing parameters, GPU tier, inference latency, dataset format and minimum episodes
If you are rebuilding the training config anyway, it is worth checking whether GR00T is still the right family for your task at all.

GR00T N1.5 to N1.7 is a backbone swap plus an action-space change, both moves the wider VLA field has been making. Pi0's flow-matching design reached a similar action head from another direction, and the wider VLA overview puts the family in order. If this migration is your entry point, start from the SO-100 setup guide.

Five policies, real numbers, no marketing

GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT compared on parameters, GPU tier, inference latency, minimum episodes and dataset format. The same table the trainer reads.

Compare the policies
Can I load a GR00T N1.5 checkpoint with the GR00T N1.7 code?

No. The N1.5 checkpoint declares architectures GR00T_N1_5 and model_type gr00t_n1_5; N1.7 declares Gr00tN1d7. N1.5's model class lived in gr00t/model/gr00t_n1.py, which no longer exists on main, and the packaged model tree (gr00t_n1d6, then gr00t_n1d7) only started with N1.6. LeRobot detects N1.5 checkpoints specifically so it can reject them rather than mis-load them, and tells you to pin lerobot==0.5.1 for the old path. There is no conversion script.

Do I have to re-record my dataset?

No. Both read the same GR00T flavour of LeRobot v2 with a meta/modality.json describing state, action and video keys, and that schema did not change. You rewrite the Python data config: N1.5 selected a registered class with --data-config, N1.7 wants a file passed to --modality-config-path that registers ModalityConfig objects. A v3.0 dataset must be converted down first with scripts/lerobot_conversion/convert_v3_to_v2.py, for either version.

Why does GR00T N1.7 fail with a 401 or GatedRepoError before training starts?

Because the VLM backbone nvidia/Cosmos-Reason2-2B is a gated Hugging Face repository and every GR00T N1.7 checkpoint loads it on first use, including the base nvidia/GR00T-N1.7-3B. Accept the license on the model page, then run huggingface-cli login or export HF_TOKEN. The error names the backbone rather than GR00T, which is why it reads as unrelated. N1.5 had no gated dependency.

Is GR00T N1.7 commercially usable when N1.5 was not?

The N1.5 card states the model is ready for non-commercial use and links the NVIDIA OneWay Noncommercial License. The N1.7 card states it is ready for commercial and non-commercial use and links the NVIDIA Open Model License; the repository puts code under Apache 2.0 and weights under the Open Model License. Read both yourself; this summarises the cards and is not legal advice.

Does GR00T N1.7 need more GPU than N1.5?

The published guidance moved up. The N1.5 README documented fine-tuning on H100, L40, RTX 4090 and A6000 with Python 3.10 and CUDA 12.4, plus a note to pass --no-tune_diffusion_model on a 4090 to avoid running out of memory, and it supported LoRA. The N1.7 README recommends 40 GB or more for fine-tuning and 16 GB or more for inference, on Python 3.12 and CUDA 12.8, with no LoRA path in its finetune config. On AY-Robots both run on the same A100 80 GB or H100 80 GB tier, so cost per run is unchanged at roughly 4 to 12 USD.

Should I stop at GR00T N1.6 instead?

There is little reason to. N1.6 is a frozen branch like N1.5, it still uses an Eagle backbone, and its config records action_horizon 50 with 128-dimensional state and action, so porting to it costs nearly as much as porting to N1.7 without the licensing or maintenance benefit. It is worth knowing for one reason: N1.6, not N1.7, is where state-relative action chunks were introduced. If you are doing the work anyway, land on the branch that still receives fixes.

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started