- 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
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
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 family | Koch v1.1 | |
|---|---|---|
| Servos | Feetech STS3215 throughout | Dynamixel XL330 and XL430 mixed in one arm |
| Protocol | Feetech bus servo protocol | Dynamixel protocol over half duplex TTL |
| Interface | A plain USB to serial adapter | A Dynamixel USB interface, typically a U2D2 |
| Supply | 7.4 V for every servo | 5 V and 12 V rails depending on which servo |
| Parts cost | roughly 110 to 150 euro per arm | roughly 250 to 350 euro per arm |
| What the trainer receives | 6 action values per frame | 6 action values per frame, indistinguishable |
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
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 sceneCalibrating 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.
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.
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.
{
"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
- 1Check 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.
- 2Select 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.
- 3Choose 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.
- 4Review 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.
- 5Start 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.
- 6Watch 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.
- 7Choose 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
| Field | Default | Change this when |
|---|---|---|
| Batch size | 32 | The job reports an out of memory failure. Halve it, then raise gradient accumulation so the effective batch survives. |
| Learning rate | 1e-4 | The 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 steps | 20 000 | You have grown well past a hundred episodes and the curve is still descending when the run ends. |
| Gradient accumulation | 1 | Only in combination with a smaller batch size. Increasing it alone buys nothing and lengthens the run. |
| Save steps | GR00T-only field in the training form | You want more checkpoints to compare, which is worth doing on an arm the platform lists as compatible rather than fully tested. |
| Seed | not offered by this trainer | Not 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
| Symptom | Likely cause | Fix |
|---|---|---|
| Loss plateaus and the policy is mediocre | The dataset lacks the variation you want, or a fraction of episodes contain gravity sag | Review 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-motion | Either an observation outside the training data, or a Dynamixel overload shutdown | Check 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 only | Single illumination across the whole dataset | Record 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 closes | The XL330 gripper joint never reached full closure during calibration, or the grasp moment is under-sampled | Recalibrate and watch the gripper close completely. Only then look at the episodes. |
| Training job fails within two minutes | LeRobot v3.0 dataset | Read codebase_version, convert to v2.1, upload the converted copy. Re-uploading the original changes nothing. |
| One dataset column never changes value | Duplicated servo ID, so two joints answered as one during recording | Fix 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.
The full tutorial chain lives under /learn.