
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.
| Egenskap | Värde | Källa |
|---|---|---|
| Parametrar | 3,000,000,000 | Hugging Face modellkort |
| Syn-språk ryggrad | nvidia/Cosmos-Reason2-2B (Qwen3-VL), gated on Hugging Face | repo README |
| Aktionshuvud | Flow-matching diffusion transformer, 16 layers (N1.6 had 32) | repo README |
| Förutsagd aktionshorisont | 40 steps for the base checkpoint (N1.6 had 16) | getting_started/policy.md och repo README |
| Maximal tillstånds- och aktionsbredd | 132 (N1.6 had 29) | repo README |
| Kodlicens | Apache 2.0 | Isaac-GR00T förråd |
| Viktlicens | NVIDIA Open Model License Agreement | modellkort |
| Latens, H100 80 GB, PyTorch eager, 4 denoising steg, 1 kamera | 85.8 ms end to end, 11.7 Hz | modellkort tidsmätningstabell |
| Samma hårdvara, TensorRT full pipeline | 27.9 ms end to end, 35.9 Hz | modellkort tidsmätningstabell |
| Latens AY-Robots anger för sin serverade GR00T N1.7 | 152 ms per aktionssteg | AY-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.

Vad körningen behöver innan du skriver något
| Krav | Finjustering | Inferens |
|---|---|---|
| VRAM, NVIDIA-vägledning | 40 GB eller mer, H100 eller L40 rekommenderas | 16 GB eller mer, ett RTX 4090 fungerar |
| Python och CUDA på dGPU | 3.12 och CUDA 12.8 | 3.12 och CUDA 12.8 |
| Videobakgrund | torchcodec 0.8.0, endast FFmpeg 4 till 7 | samma |
| Datasetformat | LeRobot v2 plus meta/modality.json | ej tillämpligt |
| Hugging Face-åtkomst | godkänd för nvidia/Cosmos-Reason2-2B | samma |
| Andra verktyg | git-lfs och uv | uv |
| AY-Robots GPU-nivå för groot1.7-tränaren | A100 80 GB eller H100 80 GB | pod tillhandahålls automatiskt |
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.
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=trueLeRobots 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.
# 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_lerobotOm 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.
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.
{
"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"
}
}
}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.
| Lager | Fil | SO-100-form som används i repot |
|---|---|---|
| Parquet-kolumn | data/chunk-*/episode_*.parquet | annotation.human.task_description |
| modality.json-nyckel | meta/modality.json, under "annotation", utan prefixet annotation. | human.task_description |
| modality_keys i datakonfigurationen | your so100_config.py | annotation.human.task_description |
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.
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)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.
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.
- 1Installera 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.
bashsudo apt install git-lfs && git lfs install git clone --recurse-submodules https://github.com/NVIDIA/Isaac-GR00T cd Isaac-GR00T - 2Installera 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.
bashcurl -LsSf https://astral.sh/uv/install.sh | sh sudo apt-get update && sudo apt-get install -y ffmpeg uv sync --python 3.12 uv run python -c "import gr00t; print('GR00T installed successfully')" - 3Autentisera mot Hugging Face
Gör detta före den första träningsstarten, inte efter att den misslyckas åtta minuter in.
bashuv run huggingface-cli login # or: export HF_TOKEN=<your_token> - 4Sanitetskontroll 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.
bashCUDA_VISIBLE_DEVICES=0 uv run python \ gr00t/experiment/launch_finetune.py \ --base-model-path nvidia/GR00T-N1.7-3B \ --dataset-path demo_data/cube_to_bowl_5 \ --embodiment-tag NEW_EMBODIMENT \ --modality-config-path examples/SO100/so100_config.py \ --num-gpus 1 \ --output-dir /tmp/test_finetune \ --max-steps 2000 \ --global-batch-size 32 \ --dataloader-num-workers 4
FFmpeg 8. torchcodec 0.8.0 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.
export NUM_GPUS=1
CUDA_VISIBLE_DEVICES=0 uv run python \
gr00t/experiment/launch_finetune.py \
--base-model-path nvidia/GR00T-N1.7-3B \
--dataset-path ./my_dataset_lerobot \
--embodiment-tag NEW_EMBODIMENT \
--modality-config-path examples/SO100/so100_config.py \
--num-gpus $NUM_GPUS \
--output-dir /tmp/so100 \
--save-total-limit 5 \
--save-steps 2000 \
--max-steps 20000 \
--use-wandb \
--global-batch-size 32 \
--color-jitter-params brightness 0.3 contrast 0.4 saturation 0.5 hue 0.08 \
--dataloader-num-workers 4| Flag | Standardvärde i FinetuneConfig | Vad den gör |
|---|---|---|
| --global-batch-size | 64 | Total batch över alla GPU:er före gradientackumulering. De medföljande exemplen använder 32. |
| --learning-rate | 1e-4 | Samma värde som AY-Robots skickar för sin groot1.7-tränare. |
| --max-steps | 10000 | Totalt antal optimeringssteg. examples/finetune.sh-wrappern har också 10000 som standard. |
| --gradient-accumulation-steps | 1 | Multiplicerar den effektiva batchen. Värden över 1 genererar en varning som anger den ackumulerade storleken. |
| --save-steps and --save-total-limit | 1000 and 5 | Frekvens för kontrollpunkter och hur många som behålls. Äldre raderas. |
| --weight-decay and --warmup-ratio | 1e-5 and 0.05 | Ställs in explicit av examples/finetune.sh också. |
| --state-dropout-prob | 0.2 in the CLI, 0.8 in the model config | Slumpartat släpper proprioceptivt tillstånd under träning. Sänk det om din uppgift förlitar sig på tillståndet. |
| --tune-llm and --tune-visual | False and False | Ryggraden förblir fryst som standard. |
| --tune-projector and --tune-diffusion-model | True and True | Projektorn och diffusionsåtgärdshuvudet är det som faktiskt tränas. |
| --use-percentiles | True | Normalisera med q01 och q99 istället för råa min- och maxvärden. |
| --dataloader-num-workers | 2 | Laddaren är CPU-baserad per design. Exemplen höjer detta till 4. |
| --seed | does not exist | Det 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.
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.
| Modell | GPU-nivå | Typisk körning | Typisk kostnad | Minsta antal episoder |
|---|---|---|---|---|
| GR00T N1.7 | A100 80 GB or H100 80 GB | 3 to 6 hours | 4 to 12 USD | 50 |
| GR00T N1.5 | A100 80 GB or H100 80 GB | 3 to 6 hours | 4 to 12 USD | 50 |
| Pi0.5 | A100 80 GB or H100 80 GB | 3 to 6 hours | 4 to 12 USD | 50 |
| SmolVLA | RTX 4090 or any 24 GB card | 2 to 5 hours | 1 to 3 USD | 30 |
| ACT | RTX 4090 or any 24 GB card | 2 to 5 hours | 1 to 3 USD | 50 |
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.

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.
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 gripperArkivet 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.
| Kontrollpunkt | Genomsnittligt MSE på traj 0 | Genomsnittligt MAE på traj 0 |
|---|---|---|
| 500 | 87.5 | 5.63 |
| 1000 | 25.4 | 3.30 |
| 1500 | 13.2 | 2.18 |
| 2000 | 10.0 | 1.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.
| Symptom | Trolig orsak |
|---|---|
| MSE platt eller stigande över kontrollpunkter | Inlärningshastigheten är för låg, eller så laddas inte datan alls. Kontrollera --dataset-path och dataladdarens arbetare. |
| Prediktionskurvan är platt eller konstant | modality.json-nycklar eller --modality-config-path matchar inte. Aktionsnycklarna är inte mappade. |
| MSE enormt, eller NaN-förlust under träning | Normalisering av aktion och tillstånd. Verifiera meta/stats och att aktionsintervallen är fysiskt rimliga. |
| Bra på traj 0, dåligt på undanhållna avsnitt | Databrist, 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.
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| Aspekt | Isaac-GR00T launch_finetune.py | lerobot-train --policy.type=groot |
|---|---|---|
| Datasetversion | Endast LeRobot v2, konvertering krävs | Inbyggt LeRobot-dataset, ingen nedgradering |
| Modalitetsmappning | meta/modality.json plus en Python-datakonfiguration | ingen modality.json; beteende ställs in av --policy.*-flaggor på kommandoraden |
| Frö | ingen fröflagga alls | --seed, LeRobot standard 1000 |
| Relativa åtgärder | per-nyckel ActionConfig i datakonfigurationen | --policy.use_relative_actions plus --policy.relative_exclude_joints |
| Publicerade referensresultat | SO-100 open-loop MSE-trend på demodata | LIBERO sviter, 96,5 procents genomsnitt över fyra sviter |
| Distributionsväg | run_gr00t_server.py plus eval_so100.py över ZMQ | lerobot-rollout, med realtidschunking (queue_threshold bör ligga på eller under 5) |
- 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.
- 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.
- Spela in episoder med lerobot-record på SO-100. Du får ett LeRobot v3.0-dataset.
- Konvertera det till v2.1 med scripts/lerobot_conversion/convert_v3_to_v2.py i sin egen virtuella miljö.
- Skriv meta/modality.json och en Python-modalitetskonfiguration, registrerad under EmbodimentTag.NEW_EMBODIMENT.
- Hyr ett 80 GB-kort, klona med submoduler, uv sync, autentisera mot Hugging Face.
- 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.
- Hämta checkpointen från maskinen innan du förstör instansen, bygg sedan upp serveringsvägen till armen.
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.
Samma jobb som ett formulär. Du väljer modell och dataset, backend hyr en GPU på spotmarknaden baserat på nödvändig VRAM, kör tränaren och skriver checkpoints till objektlagring. GR00T N1.7 på SO-100-guiden är denna exakta kombination; träningsmatrisen har alla andra modell- och armparningar, inklusive GR00T N1.7 på SO-101.
| Vad groot1.7-tränaren skickar | Värde |
|---|---|
| Batchstorlek | 32 |
| Inlärningshastighet | 1e-4 |
| Max antal steg | 20000 |
| Gradientackumulering | 1, and it does take effect for this trainer |
| Extra inställning exponerad i formuläret | saveSteps |
| Bas-checkpoint | nvidia/GR00T-N1.7-3B |
| Accepterat datasetformat | LeRobot v2.0 or v2.1 |
Datasetet kan komma från ett Hugging Face repo id, från din egen maskin, eller från en session du spelade in med skrivbordsklienten. Inferens är ett separat steg: plattformen tillhandahåller en pod som serverar policyn, och din lokala robotklient kommunicerar med den slutpunkten. Poddar har en inaktiv vakthund och förstör sig själva efter en inaktiv period, så en bortglömd webbläsarflik faktureras inte över natten. Om du hellre inte vill klicka, finns samma operationer på CLI:n och MCP-servern.
GR00T N1.7 och Pi0.5 är endast molnbaserade här; endast SmolVLA och ACT körs även lokalt. Kravet på v2.1 försvinner inte heller, eftersom ett v3.0-dataset fortfarande måste konverteras ner innan GR00T-laddaren accepterar det. Och inget skriver dina modality.json-semantik åt dig: om dina kameranycklar eller din språknyckel är fel, är de fel på båda vägarna. Se träningsdokumentationen för vad backend gör och inte gör å dina vägnar.

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.
# 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"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.7Sources
- NVIDIA Isaac-GR00T repository README, N1.7 main branch
- Isaac-GR00T: Fine-tune on Custom Embodiments (NEW_EMBODIMENT)
- Isaac-GR00T: Finetuning Models for the SO100/SO101 Robot
- Isaac-GR00T: Robot Data Preparation Guide, the GR00T LeRobot format
- Isaac-GR00T: modality config and ActionConfig reference
- Isaac-GR00T: Policy API Guide and the embodiment tag list
- Isaac-GR00T: FinetuneConfig dataclass with every CLI default
- Isaac-GR00T: the shared finetune launcher wrapper
- GR00T N1.7 FAQ: data volume, augmentation and deployment
- nvidia/GR00T-N1.7-3B model card, including the inference timing table
- nvidia/Cosmos-Reason2-2B, the gated VLM backbone used by N1.7
- GR00T N1: An Open Foundation Model for Generalist Humanoid Robots
- NVIDIA Isaac GR00T N1.7: Open Reasoning VLA Model for Humanoid Robots
- LeRobot: GR00T Policy, the lerobot-train recipe
- LeRobot: Imitation Learning on Real-World Robots (lerobot-record, lerobot-train)
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started