AY-Robots guidesida för träning av GR00T N1.7 på en SO-100-arm, som visar den nödvändiga GPU-nivån, datasetformatet och tränarens standardinställningar
GR00T N1.7SO-100FinjusteringLeRobotVLA

Hur man tränar GR00T N1.7 på ditt eget SO-100 Dataset

AY-Robots ResearchAugust 23, 202628 min lästid

En beprövad genomgång för finjustering av NVIDIA GR00T N1.7 på ett SO-100 LeRobot dataset: riktiga flaggor, modality.json, v2.1-kravet, vad en körning kostar och fallgroparna.

NVIDIA levererar ett finjusteringsexempel för exakt den arm du förmodligen äger. Inuti Isaac-GR00T-förvaret finns en mapp som heter demo_data/cube_to_bowl_5: fem episoder, 4 148 bildrutor vid 30 fps, redan skrivna som LeRobot v2.1, med en matchande modalitetskonfiguration under examples/SO100/. Dess meta/info.json rapporterar robot_type: so101_follower, vilket i LeRobot är samma konfigurationsklass som so100_follower. Det är genuint användbart, eftersom det betyder att referensvägen för GR00T N1.7 på en sex-frihetsgrads-hobbyarm underhålls av personerna som skrev modellen. Det är inte en nedskalad humanoid-demo, det är samma arm.

Den dåliga nyheten är avståndet mellan Jag spelade in 60 episoder och armen utför uppgiften. Det finns ungefär sex ställen där denna pipeline misslyckas tyst snarare än högljutt, och fyra av dem finns i filer som de flesta aldrig öppnar: meta/modality.json, Python-datakonfigurationen, meta/relative_stats.json, och datasetets egen versionssträng. Denna guide går igenom den manuella vägen från början till slut med de verkliga kommandona, och visar sedan samma jobb som ett formulär på AY-Robots. Allt nedan kontrollerades mot Isaac-GR00T:s huvudgren per den 20 augusti 2026 (n1.7-release-linjen) och lerobot 0.6.1, publicerad på PyPI den 3 augusti 2026. Uppströms rör sig snabbt, och där en flagga döptes om anges det i denna artikel.

Vad du behöver veta innan du börjar

  • GR00T N1.7 finjustering kräver 40 GB eller mer VRAM. NVIDIA rekommenderar H100- eller L40-noder. Ett 24 GB RTX 4090 kommer inte att klara detta jobb, även om det kan träna SmolVLA och ACT.
  • Datasetet måste vara LeRobot v2 (v2.0 eller v2.1) plus en GR00T-specifik meta/modality.json. Ett LeRobot v3.0-dataset laddas inte och måste konverteras ned.
  • Startpunkten är gr00t/experiment/launch_finetune.py, en tyro CLI. Den har ingen --seed-flagga, så körningar är inte bit-för-bit reproducerbara.
  • För en anpassad arm är embodiment-taggen NEW_EMBODIMENT, och den taggen gör --modality-config-path obligatorisk.
  • Den medföljande SO-100-receptet förutsäger armleder som RELATIVA deltan och griparen som ett ABSOLUT mål. Att få den parningen baklänges är ett tyst fel, inte ett felmeddelande.
  • På AY-Robots är samma jobb ett formulär: 20000 steg, batch 32, inlärningshastighet 1e-4, ungefär 4 till 12 USD på A100 80 GB eller H100-nivån.

Vad GR00T N1.7 faktiskt är

GR00T N1.7 är en syn-språk-handlingsmodell med den dubbelsystemslayout som beskrivs i den ursprungliga GR00T N1-rapporten: en syn-språkmodul som läser kamerorna och instruktionen, och en diffusionstransformator som omvandlar detta till en sekvens av kontinuerliga motoriska kommandon. N1.7 ersatte den första halvan. Eagle-ryggraden från N1.6 är borta, utbytt mot nvidia/Cosmos-Reason2-2B på en Qwen3-VL-arkitektur, och modellen förtränades på ungefär 20 000 timmar av egocentrisk mänsklig video utöver robotdata. NVIDIAs egen rapport anger siffran till 20 854 timmar och rapporterar att en ökning från 1k till 20k timmar mer än fördubblar den genomsnittliga uppgiftskompletteringen.

Den andra halvan ändrades också, på sätt som är viktiga för din körning. Aktionshuvudet minskade från 32 diffusionslager till 16, den förutsagda aktionssekvensen växte från 16 steg till 40, och den maximala tillstånds- och aktionsbredden gick från 29 till 132. Dessa tre siffror kommer från ändringsloggen i förrådets README; NVIDIAs egen lanseringspost beskriver fortfarande System 1 som en 32-lagers DiT, så där de två inte stämmer överens, lita på det förråd du är på väg att klona. Aktioner uttrycks som standard i ett relativt gripdon utrymme, deltor från den aktuella posen snarare än absoluta mål, vilket är det som överhuvudtaget möjliggör överföring av manipulationsförkunskaper från mänsklig video till robotstyrning. Huvudet i sig är en flödesmatchande diffusionstransformator, samma familj som Pi0.5 men med en annan ryggrad framför sig. Om du fortfarande använder den tidigare generationen, N1.7 mot N1.5 behandlar huruvida uppgraderingen motiverar att du gör om din pipeline.

EgenskapVärdeKälla
Parametrar3,000,000,000Hugging Face modellkort
Syn-språk ryggradnvidia/Cosmos-Reason2-2B (Qwen3-VL), gated on Hugging Facerepo README
AktionshuvudFlow-matching diffusion transformer, 16 layers (N1.6 had 32)repo README
Förutsagd aktionshorisont40 steps for the base checkpoint (N1.6 had 16)getting_started/policy.md och repo README
Maximal tillstånds- och aktionsbredd132 (N1.6 had 29)repo README
KodlicensApache 2.0Isaac-GR00T förråd
ViktlicensNVIDIA Open Model License Agreementmodellkort
Latens, H100 80 GB, PyTorch eager, 4 denoising steg, 1 kamera85.8 ms end to end, 11.7 Hzmodellkort tidsmätningstabell
Samma hårdvara, TensorRT full pipeline27.9 ms end to end, 35.9 Hzmodellkort tidsmätningstabell
Latens AY-Robots anger för sin serverade GR00T N1.7152 ms per aktionsstegAY-Robots policykatalog

Dessa tre sista rader förklarar det mesta av den besvikelse som folk rapporterar. Rubrikens 27.9 ms är en TensorRT-motor på en H100 med en kamera och fyra denoising-steg. Ren PyTorch på samma kort är 85.8 ms, och modellkortet anger skillnaden till 3.08x. Inget av dessa nummer inkluderar ett serving-lager, en andra kamera eller ett nätverkshopp. De 152 ms per aktionssteg som AY-Robots anger för sin serverade GR00T N1.7 är siffran med serving i loopen, och en tur och retur-resa över det publika internet kommer utöver det. Mer om detta i slutet. För siffrorna bredvid andra modeller, GR00T N1.7 mot Pi0.5 och GR00T N1.7 mot SmolVLA lägger ut dem sida vid sida.

AY-Robots modellsida för GR00T N1.7 som visar parameterantal, GPU-nivå, inferenslatens och modellens angivna styrkor och begränsningar
Sidan /policies/groot-n1-7 innehåller samma specifikationsremsa som du annars skulle sätta ihop för hand från modellkortet och repo README.

Vad körningen behöver innan du skriver något

KravFinjusteringInferens
VRAM, NVIDIA-vägledning40 GB eller mer, H100 eller L40 rekommenderas16 GB eller mer, ett RTX 4090 fungerar
Python och CUDA på dGPU3.12 och CUDA 12.83.12 och CUDA 12.8
Videobakgrundtorchcodec 0.8.0, endast FFmpeg 4 till 7samma
DatasetformatLeRobot v2 plus meta/modality.jsonej tillämpligt
Hugging Face-åtkomstgodkänd för nvidia/Cosmos-Reason2-2Bsamma
Andra verktyggit-lfs och uvuv
AY-Robots GPU-nivå för groot1.7-tränarenA100 80 GB eller H100 80 GBpod tillhandahålls automatiskt
Den spärrade ryggraden kommer att stoppa dig vid första körningen

Varje GR00T-checkpoint, inklusive basen nvidia/GR00T-N1.7-3B, laddar nvidia/Cosmos-Reason2-2B vid första användningen, och det arkivet är spärrat. README anger felet exakt: modelladdning misslyckas med en GatedRepoError / 401 Client Error. Vad den inte nämner är när det händer, vilket är efter att du har hyrt kortet och körningen har startat. Begär åtkomst på modellsidan, kör sedan uv run huggingface-cli login eller exportera HF_TOKEN innan du hyr något.

Steg 0: avsnitten själva

Allt nedan förutsätter att du redan har spelat in avsnitt. Om du inte har det, är det det verkliga första steget och det är det som avgör hur bra resultatet kan bli, eftersom imitationsinlärning inte kan återställa information som inte finns i datan. Kalibrera båda armarna först, kör sedan följaren med en ledararm medan lerobot-record skriver parquet-filerna och kameraströmmarna. Om kalibreringen är felaktig, beskriver ledvärdena i din datamängd en något annorlunda robot än den som senare kommer att utföra policyn, och ingen mängd träning kan åtgärda det.

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 på en aktuell LeRobot. Avsnittslängden är som standard 60 s och återställningstiden 60 s. so100_follower och so101_follower är båda registrerade mot samma LeRobot-konfigurationsklass, vilket är anledningen till att Isaac-GR00T SO100-exemplet använder so101-namnen; båda fungerar på en SO-100. Kameranamnen du väljer här (front, wrist) är de namn som måste återkomma i modality.json.

LeRobots eget råd är att spela in minst 50 avsnitt med cirka 10 per objektplats, hålla kamerorna fixerade och hålla gripbeteendet konsekvent. Lägg till variation senare, inte i början. Tumregeln att komma ihåg: om du inte kunde utföra uppgiften själv enbart från kamerabilderna, kan policyn inte heller. För den armspecifika installationen, komma igång med SO-100 och SO-100 LeRobot-sidan täcker portar, kalibrering och kamera-index. På AY-Robots kan du också göra detta över internet från webbläsaren med hjälp av teleoperation och spela in direkt från sessionen.

Steg 1: datasetet måste vara LeRobot v2.1

Detta är det vanligaste hindret. LeRobots nuvarande CODEBASE_VERSION på main är v3.0, så allt du spelar in idag med en aktuell verktygskedja kommer ut som v3.0. GR00T:s laddare förväntar sig v2. Repositoryt är tydligt med varför: många uppströms dataset som DROID, LIBERO och Bridge publiceras i v2, och inbyggt stöd för båda är planerat men inte levererat. Så konverteringen ligger på dig, och den körs i sin egen virtualenv av en konkret anledning: scripts/lerobot_conversion har sitt eget pyproject som kräver Python 3.10 eller 3.11 och låser lerobot till en specifik git-commit, medan Isaac-GR00T själv kräver Python 3.12. Installera konverteraren från repositoryns rot och du får gr00t paketet istället, vilket är misstaget dess README varnar för. Om du är ny med formatet, förklarar LeRobot-datasetet ordlisteposten vad som faktiskt finns inuti ett sådant.

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
Konverteraren tar --repo-id, en valfri --root, och --force-conversion, som raderar befintliga lokala ögonblicksbilder och laddar ner dem igen. Den skriver codebase_version: v2.1 till meta/info.json.
Konverteringen skriver över på plats

Om v3.0-datasetet redan finns lokalt, bygger skriptet v2.1-layouten bredvid det och byter sedan plats: originalet flyttas till en syskonmapp med versionen tillagd, <name>_v3.0, och den konverterade kopian tar den ursprungliga sökvägen. (Skriptets egen docstring kallar den mappen _v30; koden lägger till versionssträngen, så det du faktiskt får är _v3.0.) Andra överraskningen: utdata hamnar alltid under <root>/<repo-id>, så --root examples/SO100/my_dataset_lerobot ger dig examples/SO100/my_dataset_lerobot/<your-hf-user>/<your-dataset>, och den längre sökvägen är den som --dataset-path vill ha senare. När ett träningsjobb avvisar ditt dataset på grund av versionsskäl, listar sidan för dataset avvisat som v3 de exakta symptomen.

Strukturen GR00T vill ha efter konvertering är den klassiska v2-layouten: meta/info.json, meta/episodes.jsonl, meta/tasks.jsonl, parquet-filer under data/chunk-000/, MP4-filer under videos/chunk-000/observation.images./, och en extra fil som standard LeRobot inte har. Den extra filen är där de flesta återstående felen finns.

Steg 2: modality.json, de sex siffrorna som avgör allt

I ett LeRobot-dataset lagras robotens tillstånd och handling som platta float32-arrayer. För en SO-100 har båda formen [6]: fem armleder och en gripklo. Demodatasetet namnger dem shoulder_pan.pos, shoulder_lift.pos, elbow_flex.pos, wrist_flex.pos, wrist_roll.pos, gripper.pos, men dessa namn finns i info.json och inget i parquet-filen anger vilket index som är vilket. meta/modality.json tillhandahåller den mappningen, och GR00T kommer inte att träna utan den. Här är den som lagringsplatsen levererar för SO-100, ordagrant.

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. Index är nollbaserade och följer Python-slicing, så single_arm är [0:5] och gripper är [5:6].

Kopiera den till din konverterade datamängd på meta/modality.json och byt namn på videonycklarna till vad dina kameror faktiskt heter. Om du spelade in med en enda takkamera med namnet top, då är original_key observation.images.top och det vänliga namnet är det som din datakonfiguration kommer att referera till. De två måste överensstämma, och ingen av dem kontrollerar den andra åt dig. Språkanmärkningen är värre, eftersom samma nyckel måste förekomma på tre ställen.

LagerFilSO-100-form som används i repot
Parquet-kolumndata/chunk-*/episode_*.parquetannotation.human.task_description
modality.json-nyckelmeta/modality.json, under "annotation", utan prefixet annotation.human.task_description
modality_keys i datakonfigurationenyour so100_config.pyannotation.human.task_description
Varför språkenyckeln ställer till det

Segmenten efter annotation. väljs av den som skapade datamängden. SO-100 demodata använder annotation.human.task_description; LIBERO och SimplerEnv använder annotation.human.action.task_description. Båda är giltiga. Om du kopierade en konfiguration från ett LIBERO-exempel och pekade den mot din egen SO-100-inspelning, löses språkkanaen till ingenting och modellen tränas på en tom instruktion. Förlusten minskar fortfarande. Policyn gör fortfarande något. Den ignorerar bara vad du bad den att göra.

Steg 3: datakonfigurationen, relativ arm och absolut gripare

Modalitetskonfigurationen är en Python-fil snarare än JSON, eftersom den också bestämmer hur varje åtgärdsgrupp representeras. Detta är den del av N1.7-arbetsflödet som inte fanns i samma form i N1.5, och den del som är värd att läsa två gånger. Den levererade SO-100-konfigurationen förutsäger de fem armlederna som RELATIVE deltan från det aktuella tillståndet och griparen som en ABSOLUTE målposition, eftersom en binär öppen-eller-stängd signal fungerar bättre som ett mål än som ett 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, nedkortad till det väsentliga. NON_EEF betyder ledrum; EEF skulle förvänta sig en niodimensionell vektor av x, y, z plus en 6D-rotation.

Två detaljer här kommer att kosta dig en dag om du inte känner till dem. För det första, action_configs är positionell: dokumentationen kräver samma längd och samma ordning som modality_keys, och de är tydliga med konsekvensen av att göra fel, vilket är att fel representation tillämpas tyst. Din gripare tränas som ett delta och din arm som ett absolut mål, och det finns inget felmeddelande. För det andra, register_modality_config hävdar att taggen inte redan är registrerad, så en andra NEW_EMBODIMENT-konfiguration i samma Python-process dör med Embodiment tag ... already registered. Du kan inte importera två av dessa till ett och samma skript. En tredje regel tillämpas senare, vid driftsättning: åtgärden delta_indices måste vara det sammanhängande intervallet som börjar vid noll. Ett glest fönster som [0, 4, 8] avvisas, eftersom allt nedströms indexerar den förutsagda delen linjärt och annars skulle exekvera fel rader.

Ändra delta_indices och du måste återskapa statistiken

Normaliseringsstatistik, i synnerhet meta/relative_stats.json, beräknas för den horisontlängd du hade när du genererade dem. Korta ner åtgärdshorisonten från 16 till 8 utan att återskapa, och träningen dör med IndexError: boolean index did not match indexed array ... dimension is 8 but corresponding boolean dimension is 16. Lösningen är ett kommando: python gr00t/data/stats.py --dataset-path <path> --embodiment-tag NEW_EMBODIMENT --modality-config-path examples/SO100/so100_config.py. Kör det efter varje ändring av delta_indices.

Steg 4: miljön

N1.7 flyttade repositoryt till uv och Python 3.12. Den gamla conda plus pip install -e . sökvägen finns fortfarande i en hopfälld sektion av README, men den varnar för att GPU-beroenden inklusive flash-attn och TensorRT kan behöva manuell installation. Använd uv om du inte har en specifik anledning att inte göra det. När det gäller flash-attn, en detalj sparar förvirring: du kommer att se Installing flash-attn utskrivet vid varje uv run. Det byggs inte om. uv validerar om ett URL-fäst wheel som redan är cachat, och det tar två eller tre sekunder.

  1. 1
    Installera git-lfs, klona sedan med submoduler

    git-lfs är obligatoriskt, inte valfritt. Utan det laddas parquet-filerna i demo_data/ ner som pekstubbar, och demo-körningen misslyckas på en datamängd som ser ut att finnas i fillistan.

    bash
    sudo apt install git-lfs && git lfs install
    git clone --recurse-submodules https://github.com/NVIDIA/Isaac-GR00T
    cd Isaac-GR00T
  2. 2
    Installera uv och synkronisera miljön

    Standardinstallationen hämtar GPU-beroendena inklusive flash-attn och TensorRT. På en ny A100- eller H100-avbildning är detta det längsta enskilda steget, så gör det innan du börjar ägna uppmärksamhet åt något annat.

    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
    Autentisera mot Hugging Face

    Gör detta före den första träningsstarten, inte efter att den misslyckas åtta minuter in.

    bash
    uv run huggingface-cli login   # or: export HF_TOKEN=<your_token>
  4. 4
    Sanitetskontroll på den medföljande SO-100 demo-datan

    Innan du rör din egen inspelning, kör 2000 steg på demo_data/cube_to_bowl_5. Det är fem episoder, det slutförs snabbt, och det bevisar miljön snarare än dina data. Om denna körning misslyckas, kommer inget du gör med din datamängd att hjälpa.

    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
Två miljöfällor som ser ut som modellbuggar

FFmpeg 8. torchcodec 0.8.0 stöder endast FFmpeg 4 till 7, och Ubuntu 25.10 och senare levereras med version 8. Felet är Could not load libtorchcodec, vilket låter som en trasig installation snarare än en versionskonflikt. Installera en äldre runtime, till exempel conda install -c conda-forge 'ffmpeg<8', och placera dess bibliotek på LD_LIBRARY_PATH. CUDA_HOME är odefinierad. Finjustering misslyckas helt. Kör bash scripts/deployment/dgpu/install_deps.sh en gång, eller bara export CUDA_HOME=/usr/local/cuda.

Steg 5: kommandot för finjustering och vad dess flaggor faktiskt har för standardvärden

Byt ut demodatauppsättningen mot din egen och lägg till de inställningar du faktiskt vill ha. Nedan är den fullständiga formen som förvaret använder i sin egen handledning för ny förkroppsligande, inklusive flaggorna för augmentation och checkpointing som det korta README-exemplet utelämnar. Detta är i snäv bemärkelse: språkryggraden och den visuella kodaren förblir frysta, och det som tränas är projektorn och diffusionsåtgärdshuvudet.

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
Enkel GPU. För åtta kort, ersätt startprogrammet med uv run torchrun --nproc_per_node=8 --master_port=29500 och ställ in --num-gpus 8. Använd uv run torchrun, inte bara torchrun, annars får du fel miljö.
FlagStandardvärde i FinetuneConfigVad den gör
--global-batch-size64Total batch över alla GPU:er före gradientackumulering. De medföljande exemplen använder 32.
--learning-rate1e-4Samma värde som AY-Robots skickar för sin groot1.7-tränare.
--max-steps10000Totalt antal optimeringssteg. examples/finetune.sh-wrappern har också 10000 som standard.
--gradient-accumulation-steps1Multiplicerar den effektiva batchen. Värden över 1 genererar en varning som anger den ackumulerade storleken.
--save-steps and --save-total-limit1000 and 5Frekvens för kontrollpunkter och hur många som behålls. Äldre raderas.
--weight-decay and --warmup-ratio1e-5 and 0.05Ställs in explicit av examples/finetune.sh också.
--state-dropout-prob0.2 in the CLI, 0.8 in the model configSlumpartat släpper proprioceptivt tillstånd under träning. Sänk det om din uppgift förlitar sig på tillståndet.
--tune-llm and --tune-visualFalse and FalseRyggraden förblir fryst som standard.
--tune-projector and --tune-diffusion-modelTrue and TrueProjektorn och diffusionsåtgärdshuvudet är det som faktiskt tränas.
--use-percentilesTrueNormalisera med q01 och q99 istället för råa min- och maxvärden.
--dataloader-num-workers2Laddaren är CPU-baserad per design. Exemplen höjer detta till 4.
--seeddoes not existDet finns ingen seed-flagga på denna CLI.

Den sista raden är inget stavfel. launch_finetune.py är en tyro CLI genererad från en dataklass, och den dataklassen har inget seed-fält. README-filen noterar separat 5 till 6 procents varians mellan körningar orsakad av icke-deterministisk bildförbättring. Två körningar med identiska flaggor kommer inte att producera identiska checkpoints, vilket spelar stor roll när du försöker avgöra om en hyperparameterändring hjälpte eller om du hade tur. Som jämförelse använder lerobots egen tränare seed 1000 som standard, och LeRobot GR00T-receptet skickar --seed=42 explicit.

Validering är avstängd som standard, och den dokumenterade flaggan finns inte på denna CLI

Finjustering körs med eval_strategy="no", så det finns ingen valideringsförlustkurva alls. Du får träningsförlust och inget annat. Guider för nya utföranden säger att du ska slå på det med --eval-strategy steps --eval-steps 500, men den flaggan finns inte på launch_finetune.py: CLI:n genereras av tyro från FinetuneConfig-dataklassen, och eval_strategy, eval_steps och eval_batch_size är istället fält i TrainingConfig. Deras standardvärden där är "no", 500 och 2. För att nå dem, använd den fullständiga startpunkten gr00t/experiment/launch_train.py, där den kapslade flaggan är --training.eval-strategy. Oavsett vilket säger en fallande träningsförlust i sig väldigt lite om generalisering, vilket är exakt den situation som beskrivs på förlusten faller men policyn gör ingenting.

Vad en körning på 20000 steg kostar

GR00T N1.7 kräver ett 80 GB-kort, så kostnadsfrågan har ett snävt svar. På AY-Robots körs groot1.7-tränaren på A100 80 GB- eller H100 80 GB-nivån, där en körning tar 3 till 6 timmar till 1.20 till 2.00 USD per timme på spotmarknaden. Det är ungefär 4 till 12 USD för standardjobbet på 20000 steg. Samma uppgift på SmolVLA eller ACT hamnar på ett 24 GB-kort till 0.30 till 0.60 USD per timme och 1 till 3 USD per körning. Det är den verkliga avvägningen: GR00T kostar ungefär fyra gånger så mycket per försök, och du kan inte köra det på en 4090 under ditt skrivbord.

ModellGPU-nivåTypisk körningTypisk kostnadMinsta antal episoder
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

Det minsta antalet på 50-episod är ett golv, inte ett mål. NVIDIAs egen FAQ är mer krävande: ungefär 100 trajektorier för en enkel plock-och-placera på fast plats, 500 eller fler för komplexa eller flerstegsscener, och 100 till 500 för finmanipulation. Om du har 20 episoder, ägna eftermiddagen åt att spela in snarare än kvällen åt att finjustera. guiden för datainsamling beskriver vad som skiljer en användbar episod från en bortkastad, spela in din första datamängd är den korta versionen, och SO-100 datainsamling är den armspecifika.

AY-Robots träningsguide för GR00T N1.7 på SO-100, som visar specifikationsremsan med GPU-nivå, nödvändigt datasetformat och tränarens standardinställningar
Guiden /train/groot-n1-7-on-so-100 presenterar de fakta du annars skulle behöva rekonstruera manuellt: GPU-nivå, datasetformat och de exakta standardinställningarna som tränaren skickar.

Steg 6: utvärdering i öppen slinga innan du rör armen

Sätt inte en ny kontrollpunkt på en fysisk arm för att ta reda på om träningen fungerade. Kör open-loop-utvärderingen först. Den spelar upp ett inspelat avsnitt, frågar modellen om åtgärder vid varje steg och plottar förutsägelse mot sanning med MSE och MAE. Det kostar ingenting och det fångar upp mappningsfelen från steg 2 och 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
Plottar hamnar i /tmp/open_loop_eval/traj_<id>.jpeg om du inte skickar med --save-plot-path. Standardvärden: --execution-horizon 16, --steps 200, --denoising-steps 4, --traj-ids 0.

Arkivet vägrar medvetet att publicera ett mål-MSE för anpassad data, och det är rätt beslut: siffran beror på dina åtgärdsenheter, din uppgift och din datasetstorlek, så ett tröskelvärde kopierat från någon annans arm betyder ingenting. Vad som är meningsfullt är trenden. Här är referenskörningen som arkivet dokumenterar på en enda H100 med demo-datasetet med fem avsnitt och 2000 steg.

KontrollpunktGenomsnittligt MSE på traj 0Genomsnittligt MAE på traj 0
50087.55.63
100025.43.30
150013.22.18
200010.01.76

Formen är signalen, inte de absoluta värdena. Felet bör minska stadigt när träningssteg ackumuleras. Genomsnittligt över alla fem träningsavsnitt snarare än enbart trajektoria 0, fick repots slutliga kontrollpunkt ungefär 7.5 MSE och 1.5 MAE, så även referenskörningen läses annorlunda beroende på vilka avsnitt du genomsnittar. Registrera din egen baslinje med det omodifierade demotkommandot innan du ändrar något i din egen data: om du inte kan reproducera en känd fungerande körning kan du inte skilja ett installationsfel från ett dataproblem. Lagringsplatsen kartlägger också de vanliga symptomen till orsaker, och var och en av dem är operationell snarare än en modellbugg.

SymptomTrolig orsak
MSE platt eller stigande över kontrollpunkterInlärningshastigheten är för låg, eller så laddas inte datan alls. Kontrollera --dataset-path och dataladdarens arbetare.
Prediktionskurvan är platt eller konstantmodality.json-nycklar eller --modality-config-path matchar inte. Aktionsnycklarna är inte mappade.
MSE enormt, eller NaN-förlust under träningNormalisering av aktion och tillstånd. Verifiera meta/stats och att aktionsintervallen är fysiskt rimliga.
Bra på traj 0, dåligt på undanhållna avsnittDatabrist, inte en bugg. Fem demoavsnitt kan inte generalisera.

Den andra vägen: lerobot-train istället för Isaac-GR00T

Den nuvarande LeRobot-versionen, 0.6.1 på PyPI sedan 3 augusti 2026, erbjuder ett andra och ganska annorlunda sätt att finjustera samma basvikter. LeRobot exponerar GR00T N1.7 som en policytyp och tränar den via sin egen lerobot-train ingångspunkt. Två saker är viktiga här. LeRobot CLI är en uppsättning konsolskript, så allt du läser som säger python lerobot/scripts/train.py är föråldrat och kommer inte att köras. Och LeRobot tog bort stödet för GR00T N1.5 helt, och avvisar N1.5-kontrollpunkter och konfigurationer med en migreringsanteckning, så om du behöver N1.5 via LeRobot måste du låsa till lerobot==0.5.1, den senaste versionen som stöder det, publicerad 7 april 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
Det LeRobot-inbyggda GR00T N1.7-receptet. Observera relative_exclude_joints: gripdonet är exkluderat från relativa åtgärder, vilket är samma beslut som so100_config.py fattar med ActionRepresentation.ABSOLUTE.
AspektIsaac-GR00T launch_finetune.pylerobot-train --policy.type=groot
DatasetversionEndast LeRobot v2, konvertering krävsInbyggt LeRobot-dataset, ingen nedgradering
Modalitetsmappningmeta/modality.json plus en Python-datakonfigurationingen modality.json; beteende ställs in av --policy.*-flaggor på kommandoraden
Fröingen fröflagga alls--seed, LeRobot standard 1000
Relativa åtgärderper-nyckel ActionConfig i datakonfigurationen--policy.use_relative_actions plus --policy.relative_exclude_joints
Publicerade referensresultatSO-100 open-loop MSE-trend på demodataLIBERO sviter, 96,5 procents genomsnitt över fyra sviter
Distributionsvägrun_gr00t_server.py plus eval_so100.py över ZMQlerobot-rollout, med realtidschunking (queue_threshold bör ligga på eller under 5)
Köra finjusteringen själv
Fördelar
  • Varje flagga är synlig och ändringsbar. Du kan avfrysa den visuella kodaren, flytta state_dropout_prob, eller förkorta åtgärdshorisonten.
  • Open-loop-diagrammen är lokala filer. Att diffa checkpoint-5000 mot checkpoint-20000 är ett shell-kommando.
  • Du är inte beroende av att någon plattform förblir online, och checkpointen ligger på din disk i ett standardformat.
  • Repots benchmark-exempel för LIBERO, SimplerEnv och DROID ger dig kända, fungerande körningar att reproducera innan du litar på dina egna data.
Kompromisser
  • Miljön är det mesta av arbetet. FFmpeg-version, CUDA_HOME, git-lfs, den gated backbone, torchcodec: inget av detta är modellproblem och vart och ett av dem stoppar körningen.
  • Konverteringen från v3.0 till v2.1 kräver en separat virtualenv med ett eget installationssteg, och den skriver om din datasetkatalog på plats.
  • GPU-uthyrning börjar faktureras när du börjar felsöka, inte när träningen startar, och inget stoppar instansen när körningen är klar.
  • Inget frö innebär ingen bit-för-bit-reproducerbarhet, utöver 5 till 6 procents variation mellan körningar enbart från augmentation.

Två sätt att få samma checkpoint

Du hyr GPU:n och äger varje steg. Realistiskt sett tar det en eftermiddag första gången och tjugo minuter varje gång därefter.

  1. Spela in episoder med lerobot-record på SO-100. Du får ett LeRobot v3.0-dataset.
  2. Konvertera det till v2.1 med scripts/lerobot_conversion/convert_v3_to_v2.py i sin egen virtuella miljö.
  3. Skriv meta/modality.json och en Python-modalitetskonfiguration, registrerad under EmbodimentTag.NEW_EMBODIMENT.
  4. Hyr ett 80 GB-kort, klona med submoduler, uv sync, autentisera mot Hugging Face.
  5. Kör launch_finetune.py, sedan open_loop_eval.py på flera checkpoints, och jämför MSE-trenden innan du rör hårdvaran.
  6. Hämta checkpointen från maskinen innan du förstör instansen, bygg sedan upp serveringsvägen till armen.
Steget alla glömmer

Kopiera checkpointen från den hyrda instansen innan du stänger av den. --save-total-limit 5 innebär också att äldre checkpoints raderas när träningen fortskrider, så den checkpoint du ville ha vid steg 5000 kanske inte längre finns vid steg 20000.

The AY-Robots training matrix with five policy models as rows and four robot arms as columns, each cell linking to a specific training guide
The /train matrix: five models against four arms. The GR00T N1.7 row also covers the SO-101, Koch v1.1 and LeKiwi.

Få tillbaka kontrollpunkten på armen

Isaac-GR00T använder en server-klient-uppdelning över ZMQ. Policyn körs på GPU:n, och en tunn klient på robotmaskinen skickar observationer och tar emot åtgärdsbitar. SO-100-exemplet är tillräckligt komplett för att kopiera: starta run_gr00t_server.py med din kontrollpunkt och --embodiment-tag NEW_EMBODIMENT, kör sedan eval_so100.py på robotsidan med serieporten, robot-ID:t, kamera-indexen och språkinstruktionen. Kameranamnen i det kommandot måste matcha de vänliga namnen från din modality.json, inte OS-enhetsnumren.

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 styr hur många av de förutsagda stegen som utförs innan omplanering. Det måste vara högst policyens action_horizon, och det numret är längden på action delta_indices i din modalitetskonfiguration, inte basmodellens. Den medföljande SO-100-konfigurationen förutsäger 16, så 16 är ditt tak; bas-checkpointen nvidia/GR00T-N1.7-3B är konfigurerad för 40, och policy.md säger tydligt att finjusterade checkpoints kan skilja sig åt. Överskrider du det får du ett ValueError som nämner båda numren. 8 är det värde som dokumentationen föreslår för realtidsdistribution. Det gamla flaggnamnet --action-horizon fungerar fortfarande men varnar.
7.4 V, inte 12 V

Medan du kopplar ihop armen igen: SO-100 använder Feetech STS3215 busservon på en 7.4 V-skena. Att mata dem med 12 V förstör dem, och det är ett lätt misstag om du också äger en LeKiwi, vars bas körs på 12 V medan dess arm inte gör det. Kontrollera strömförsörjningen före den första uppstarten, inte efter att röken har lagt sig. Se SO-100 hårdvarusida och SO-100 mot LeKiwi. Om armen startar men inget rör sig, är servo svarar inte rätt ställe att börja.

Nu till den ärliga delen om var policyn körs, eftersom träning och drift har olika hårdvarukrav. Finjustering kräver 40 GB eller mer. Inferens gör det inte: README anger 16 GB eller mer och nämner RTX 4090 explicit, så ett kort du redan äger kan köra en checkpoint som det aldrig hade kunnat producera. Vad som avgör om policyn känns responsiv är inte VRAM, det är var servern är placerad. På AY-Robots är GR00T N1.7 endast molnbaserad, så kontrollslingan betalar en tur och returresa över det publika internet utöver de 152 ms per åtgärdssteg, och endast SmolVLA och ACT körs också lokalt. För långsam plockning och placering är en fjärr-pod hanterbar. För något reaktivt är det inte det: policyn blir tveksam på ett sätt som ser ut exakt som ett träningsfel men inte är det. ACT med 20 ms per åtgärdssteg är modellen som tolererar den snävaste slingan, SmolVLA ligger på 245 ms, och ingen mängd av latensjustering köper tillbaka en tur och returresa som redan har förbrukats. Kör din första policy går igenom serversidan från början till slut.

Vad som faktiskt går fel

  • GatedRepoError vid första körningen. Du har inte beviljats åtkomst till nvidia/Cosmos-Reason2-2B, eller så autentiserade du dig inte. Detta inträffar efter att GPU-klockan redan har startat.
  • Dataset avvisades vid laddning. Nästan alltid ett v3.0-dataset. Konvertera det nedåt. Se dataset avvisades som v3.
  • IndexError om felaktiga booleska dimensioner. Du ändrade delta_indices och återskapade inte statistiken.
  • Slut på minne vid batch 32. Minska --global-batch-size och öka --gradient-accumulation-steps, eller minska --num-shards-per-epoch, vilket konfigurationen uttryckligen föreslår när VRAM är begränsat. Se slut på minne under träning.
  • Förlusten sjunker, policyn gör ingenting. Det finns ingen valideringsdelning som standard, så en ren träningskurva bevisar väldigt lite. Denna sida täcker diagnosen.
  • Fungerar i din installation och ingen annanstans. Förväntat med ett litet dataset filmat under en ljusförhållande. NVIDIA rekommenderar färgjitter-augmentation plus 20 till 50 episoder under olika ljusförhållanden. Mer här.
  • Gripdonet stängs aldrig ordentligt. Kontrollera att gripdonets åtgärd är ABSOLUT och armlederna RELATIVA, i den ordningen i action_configs. Gripdonet stängs inte listar de andra orsakerna.
  • En kamera slutar tyst att fungera mitt under inspelningen. Episoden sparas fortfarande och videonyckeln finns kvar, vilket är anledningen till att detta är obehagligt. Kamera upptäcks inte täcker detta.

Hela indexet över fellägen finns på . Om du väljer mellan modeller snarare än att felsöka en, och har benchmark-siffror med källor bifogade, och är den jämförelse de flesta faktiskt behöver, eftersom det är valet mellan en modell du kan träna på kortet under ditt skrivbord och en du måste hyra en 80 GB nod för att finjustera. För bakgrund om varför dessa modeller beter sig som de gör, är VLA-översikten och kompletta SO-100-guiden värda att läsa först. Och om du inte äger en arm ännu, strömmar en fysisk SO-100 utan registrering.

Hur många episoder behöver jag innan det är värt att finjustera GR00T N1.7?

AY-Robots sätter en nedre gräns på 50 episoder för groot1.7-tränaren. NVIDIAs egen FAQ är mer krävande: ungefär 100 trajektorier för en enkel plocka-och-placera på en fast plats, 500 eller fler för komplexa eller flerstegsscener, och 100 till 500 för finmanipulation. Under 50 är det nästan alltid bättre att spela in mer data än att justera hyperparametrar. Om framgången planar ut efter det, rekommenderar NVIDIA HG-DAgger: kör policyn, ingrip när den misslyckas och lägg till dessa korrigeringar i datasetet.

Varför misslyckas mitt dataset att laddas, och hur vet jag vilken version det är?

Öppna meta/info.json och läs codebase_version. LeRobots nuvarande CODEBASE_VERSION på main är v3.0, så allt som spelats in med en ny verktygskedja är v3.0, och GR00T-laddaren förväntar sig v2. Konvertera med scripts/lerobot_conversion/convert_v3_to_v2.py från Isaac-GR00T-repot, vilket skriver codebase_version: v2.1 in i det konverterade datasetet. Skriptet körs i sin egen virtualenv eftersom det behöver en annan lerobot-version än vad GR00T använder.

Kan jag finjustera GR00T N1.7 på ett RTX 4090?

Nej. NVIDIA rekommenderar 40 GB eller mer VRAM för finjustering och nämner H100- eller L40-noder; andra kort fungerar men tar mycket längre tid. Ett 4090 har 24 GB. AY-Robots erbjuder endast GR00T N1.7 på A100 80 GB och H100 80 GB-nivån av samma anledning. Inferens är en annan sak: 16 GB räcker för att köra modellen, så ett 4090 kan köra en policy som det inte kan träna. Om du vill ha en VLA du kan träna på 24 GB, är det SmolVLA med cirka 450 M parametrar eller ACT med cirka 80 M.

Varför ger två körningar med identiska flaggor olika checkpoints?

Eftersom launch_finetune.py inte har någon seed. Det är en tyro CLI genererad från en dataclass som inte innehåller något seed-fält, så inget låser RNG. Repot noterar separat 5 till 6 procents varians mellan körningar orsakade av icke-deterministisk bildaugmentation. Om reproducerbarhet är viktigt, använd LeRobot-vägen istället: lerobot-train tar --seed och det publicerade GR00T-receptet skickar --seed=42.

Ska jag använda Isaac-GR00T eller lerobot-train?

Använd Isaac-GR00T om du vill ha referensimplementeringen, per-nyckelkontroll över åtgärdsrepresentation, TensorRT-export, eller benchmark-exemplen att reproducera innan du litar på din egen data. Använd lerobot-train om ditt dataset redan är LeRobot v3.0 och du hellre inte vill konvertera det, om du vill ha en seed, eller om resten av din stack redan är LeRobot. Båda finjusterar samma nvidia/GR00T-N1.7-3B-vikter. Observera att LeRobot helt har släppt stödet för GR00T N1.5: N1.5-checkpoints avvisas med en migreringsnotis, och du måste låsa lerobot==0.5.1 för att fortsätta använda dem.

Behöver jag verkligen en handledskamera utöver en frontkamera?

Den levererade SO-100-konfigurationen använder båda, och modality.json mappar front och handled som separata videonycklar. Du kan träna med en kamera, och modellkortets latens-tabell är mätt med en kamera, men handledsvyn är det som ger policyn användbar information om gripdonet vid kontaktögonblicket. Om gripdonet stängs vid fel tidpunkt i dina rollouts, är en saknad eller dåligt riktad handledskamera en av de första sakerna att kontrollera.

Finjustera GR00T N1.7 på din SO-100 utan att först bygga miljön

Välj modell, dataset och hyperparametrar i ett formulär. Backend hyr en A100 80 GB eller H100 på spotmarknaden, kör tränaren med batch 32, inlärningshastighet 1e-4 och 20000 steg, och skriver checkpoints till objektlagring. Ungefär 4 till 12 USD per körning.

Öppna träningsguiden för GR00T N1.7

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started