How to train GR00T N1.7 on a Koch v1.1

The Koch v1.1 runs Dynamixel servos on two voltage rails, which changes the driver, the wiring and how the arm behaves in your hands. It does not change a single thing about the dataset or the fine-tune.

90 min · advanced · Last updated 2026-08-09

Trainer key
groot1.7
GPU tier
A100 80 GB or H100 80 GB
Inference
about 152 ms per action step
Batch size
32, gradient accumulation 1
Learning rate
1e-4
Steps
20,000
Dataset format
LeRobot v2.1
Typical run
3 to 6 hours, about 4 to 12 USD
Servos
Dynamixel XL330 and XL430
Servo voltage
5 V and 12 V rails depending on the servo
Short answer

A Koch v1.1 records the same six action columns as any other supported arm, so GR00T N1.7 fine-tunes on its LeRobot v2.1 dataset with the standard defaults on an A100 80 GB. The differences are all on the hardware side: a Dynamixel bus instead of Feetech, 5 V and 12 V rails instead of a single 7.4 V one, and torque enable behavior that changes how you record. Platform support for this arm is compatible rather than fully tested.

Support status, stated plainly

Compatible, not fully tested

The arm catalog marks the Koch v1.1 as compatible rather than fully supported, and that distinction is deliberate. The dataset format, the trainer, the run page and the checkpoint flow are arm agnostic and behave identically here. The driver path, device detection and the calibration flow get their first test on an SO-100 every release. If something misbehaves on a Koch, that lower layer is where to look before you suspect the training stack.

In practical terms: expect the training half of this guide to be boring and reliable, and expect to spend your troubleshooting budget on the bus, the rails and the servo IDs. That is also why this page spends more words on the hardware than the SO-100 page does. The interesting problems on a Koch happen before the first upload.

What actually differs: the bus, the rails, the torque

SO-100 familyKoch v1.1
ServosFeetech STS3215 throughoutDynamixel XL330 and XL430 mixed in one arm
ProtocolFeetech bus servo protocolDynamixel protocol over half duplex TTL
InterfaceA plain USB to serial adapterA Dynamixel USB interface, typically a U2D2
Supply7.4 V for every servo5 V and 12 V rails depending on which servo
Parts costroughly 110 to 150 euro per armroughly 250 to 350 euro per arm
What the trainer receives6 action values per frame6 action values per frame, indistinguishable
Two rails on one arm, and a third hazard on the bench

A Koch v1.1 mixes XL330 units on a 5 V rail with XL430 units on 12 V. Swapping those kills servos in both directions. Separately: if an SO-100, an SO-101 or a LeKiwi shares your workspace, keep the 12 V Koch supply physically away from them. Feetech STS3215 servos are 7.4 V parts and 12 V destroys all of them in seconds. Label every brick with the arm it belongs to.

Prerequisites

  • A built Koch v1.1 follower, with a leader arm if you have the pair
  • A Dynamixel USB interface and the correct supply for each rail, checked twice
  • Unique servo IDs across the chain and one agreed baud rate
  • At least one fixed camera, and a wrist camera if the build has a mount for it
  • The client installed and paired. It ships the Dynamixel driver, so there is no Python environment to build
  • A LeRobot v2.1 dataset of 50 or more reviewed episodes
bash
ay-robots devices

# SERIAL PORTS
#   /dev/cu.usbserial-FT89ILSF   Koch v1.1  (6 servos responding)
#     id 1  XL430-W250   12 V rail
#     id 2  XL430-W250   12 V rail
#     id 3  XL330-M288    5 V rail
#     id 4  XL330-M288    5 V rail
#     id 5  XL330-M288    5 V rail
#     id 6  XL330-M288    5 V rail   gripper
# CAMERAS
#   0  USB 2.0 Camera   1280x720   scene
Six servos answering, and the rail each one belongs to. Five answering means a duplicated ID more often than a dead servo.

Calibrating a Dynamixel arm

Dynamixel servos store a homing offset in their own EEPROM, and a Koch built from the community instructions may already carry offsets written by the LeRobot setup scripts. Those are not the reference frame the client uses. Run the calibration through the client regardless of what the servos already believe, so that what your dataset records and what the policy later commands agree with each other.

bash
ay-robots calibrate

# Koch v1.1 on /dev/cu.usbserial-FT89ILSF
#  torque disabled on all 6 servos
#  move the arm to the home pose, then confirm
#  sweeping joint limits ...
# calibration stored for robot "koch-bench"

Two failure modes are specific to this bus and both are quiet. A duplicated servo ID does not raise an error, it simply reports one servo where two exist, and the joint that vanished shows up much later as a dataset column that never changes. A baud rate mismatch mid-chain makes the servos past that point invisible, which looks identical to a broken cable. Confirm six responses before you calibrate, not after you have recorded.

Torque, and why hand guided recording is different here

This is the difference you will feel in your hands. Dynamixel servos hold position through a torque enable register. With torque on, the arm resists being moved. With torque off, it back-drives freely, and the XL430 joints at the shoulder will sag under the weight of everything above them the moment they release.

The consequence for your data is worth being precise about. A joint that sags while you reposition between episodes is harmless, because nothing is recording. A joint that sags during an episode is written into the dataset as an action you intended, and the policy learns to drop the shoulder at that point in the trajectory. It will reproduce it faithfully, and it will look like a model defect.

Use the leader follower pair if you have it

The Koch design assumes a leader arm, and it is the better recording method here. The follower stays torqued and tracks the leader, so recorded actions are commanded rather than gravity assisted, and the episodes come out visibly cleaner than anything hand guiding a torque-disabled arm produces. If you only have the follower, support its weight with your other hand through every episode.

The dataset the trainer sees

Once the episodes are recorded, none of the above is visible any more. A Koch episode is six floats of state, six floats of action, one or two video streams and a task string, arranged exactly as an SO-100 episode is. GR00T N1.7 does not branch on robot_type and has no concept of which servo brand produced the numbers. This is the reassuring half of the guide: the arm is unusual, the training job is not.

json
{
  "codebase_version": "v2.1",
  "robot_type": "koch",
  "fps": 30,
  "total_episodes": 58,
  "features": {
    "observation.state":         { "dtype": "float32", "shape": [6] },
    "action":                    { "dtype": "float32", "shape": [6] },
    "observation.images.scene":  { "dtype": "video",   "shape": [480, 640, 3] }
  }
}

The fifty episode floor applies for the same reason it applies on every other arm: fine-tuning adapts a small set of parameters on top of a frozen backbone, and with fewer examples than that it fits your specific start positions instead of the task. On a Koch there is one extra reason to respect it. Torque related noise costs you episodes at review time, so plan to record sixty or seventy to keep fifty.

LeRobot v2.1, and the rejection you will see if you skip this

GR00T loads LeRobot v2.1 and refuses v3.0. Koch owners hit this more often than most, because this arm has been around the LeRobot community long enough that people have older recordings and mixed tooling on disk. A dataset assembled from a directory you last touched a year ago is exactly the case that fails.

The visible symptom is a job that burns two minutes of A100 time and returns FAILED with a metadata error, before any chart exists on the run page. It reads like an infrastructure problem, which is why the usual reaction is to re-upload and start again. That reproduces it exactly. Read codebase_version in meta/info.json instead, convert if it says v3.0, and upload the converted copy.

Running the fine-tune

  1. 1
    Check the dataset preview before you select it

    Open the dataset and confirm it has six action columns and that none of them is flat across every episode. A flat column is the joint that a duplicated ID hid from you at recording time, and training on it wastes the whole run.

  2. 2
    Select the dataset in the Training page

    Only datasets that finished processing are listed. The card shows episode count and robot type, which is your last chance to notice you picked last week’s version.

  3. 3
    Choose GR00T N1.7

    It requests the A100 80 GB tier, and it is the current model of the two GR00T entries. N1.5 exists to reproduce runs from before N1.7 shipped and is not a reasonable default for a new Koch project.

  4. 4
    Review the hyperparameters and leave them alone

    Batch size 32, learning rate 1e-4, 20 000 max steps, gradient accumulation 1, plus save steps. There is no seed field for GR00T. Nothing about a Dynamixel arm justifies a different starting point than a Feetech one, because the numbers reaching the model are the same shape.

  5. 5
    Start the run

    The pool selects an 80 GB card on the spot market by VRAM rather than by name. Waiting in QUEUED costs nothing, so a busy market is an inconvenience and not an expense.

  6. 6
    Watch loss and telemetry for the first few hundred steps

    A visible drop early means the data loaded. Flat from step one means it did not, and the run is worth cancelling immediately rather than at hour four. GPU utilization and power on the same page confirm the card is actually working.

  7. 7
    Choose checkpoints for the hardware test

    Take two or three from the second half of the run. The last checkpoint is frequently not the best one, and the only way to establish which is best is on the arm.

Defaults for GR00T N1.7

FieldDefaultChange this when
Batch size32The job reports an out of memory failure. Halve it, then raise gradient accumulation so the effective batch survives.
Learning rate1e-4The loss spikes and stays high. Halve it. If your loss is stable and the policy is poor, the problem is in the episodes, most likely torque sag.
Max steps20 000You have grown well past a hundred episodes and the curve is still descending when the run ends.
Gradient accumulation1Only in combination with a smaller batch size. Increasing it alone buys nothing and lengthens the run.
Save stepsGR00T-only field in the training formYou want more checkpoints to compare, which is worth doing on an arm the platform lists as compatible rather than fully tested.
Seednot offered by this trainerNot available. If a run must be reproducible for a writeup, use Pi0.5 or a lerobot-family model, which do expose a seed.

Cost and duration

The A100 80 GB tier rents at 1.20 to 2.00 USD per hour, and a default 20 000 step run on fifty to a hundred fifty episodes takes 3 to 6 hours. That puts a complete Koch fine-tune at about 4 to 12 USD. The price is set by the spot market on the day, not by your arm or your dataset size.

Relative to a Koch v1.1, which costs roughly 250 to 350 euro in parts, the training runs are the cheap part of the project. The expensive resource is your recording time, which is a reason to fix the torque and ID problems before you record rather than to save two dollars on a shorter run.

Deploying the policy on the Koch

Select the checkpoint for the paired robot and start inference from the client. Restore the recording conditions exactly: camera positions, lighting, reset procedure, task sentence. At roughly 152 ms per action step the arm receives a new command six or seven times a second, which on Dynamixel servos looks like a series of short deliberate moves rather than one flowing motion.

One hardware precaution is specific to this arm. Dynamixel servos raise a hardware error flag and shut the joint down when they overload, and an early policy that drives the gripper into the table will trigger exactly that. It looks like the policy freezing mid-motion, which is also a data symptom, so check the servo state before you conclude you have an observation gap. Conservative current limits on the shoulder joints make the first evaluation session much less eventful.

Then evaluate properly: twenty attempts, the object somewhere different each time, results recorded as they happen. Physical success rate is the only number that means anything. A loss curve cannot tell you that a policy learned to compensate for a sagging shoulder that no longer sags now that torque is on during inference, and that is a genuinely Koch-shaped way to fail.

Troubleshooting

SymptomLikely causeFix
Loss plateaus and the policy is mediocreThe dataset lacks the variation you want, or a fraction of episodes contain gravity sagReview a random ten episodes for joints drifting when you were not moving them. Re-record those, then extend the dataset where the policy fails.
Policy freezes mid-motionEither an observation outside the training data, or a Dynamixel overload shutdownCheck the servo error state first, because that distinction decides everything. Torque flags mean hardware, a clean bus means a hole in the data.
Works under one lighting condition onlySingle illumination across the whole datasetRecord a second block under the other lighting and retrain on both. This is not arm specific and there is no shortcut for it.
Gripper approaches and never closesThe XL330 gripper joint never reached full closure during calibration, or the grasp moment is under-sampledRecalibrate and watch the gripper close completely. Only then look at the episodes.
Training job fails within two minutesLeRobot v3.0 datasetRead codebase_version, convert to v2.1, upload the converted copy. Re-uploading the original changes nothing.
One dataset column never changes valueDuplicated servo ID, so two joints answered as one during recordingFix the IDs, recalibrate, and re-record. The affected episodes cannot be repaired after the fact.

Frequently asked questions

Does the trainer need to know that this is a Dynamixel arm?

No. By the time the data reaches the trainer it is six normalized floats per frame plus video, and the servo brand has been abstracted away by the driver. Set robot_type honestly for your own records, but nothing in the training path reads it.

Can I run a policy trained on SO-100 data on my Koch?

The column count matches, so it will load and move, but the joint ranges and the geometry do not match, so treat the result as unrelated to the original policy. Retraining on Koch episodes is the realistic answer.

Why is the Koch listed as compatible rather than fully supported?

Because every feature is tested on an SO-100 first, and the Dynamixel driver path gets less exposure by a wide margin. The training stack is identical for both. It is a statement about how much testing the hardware layer has had, not about whether fine-tuning works.

Do I need to install the Dynamixel SDK separately?

No. The desktop client and the CLI ship the driver, so there is no Python environment to create and no dependency resolution to do. You do need the physical USB interface the Dynamixel bus expects.

Can I power the whole arm from the 12 V rail to simplify wiring?

No. The XL330 servos are 5 V parts and 12 V destroys them. The two rails exist because the arm genuinely needs both, and this particular shortcut is expensive and immediate.