How to train ACT on the Koch v1.1

The Koch v1.1 sits on a Dynamixel bus instead of the Feetech bus the rest of this platform is built around. The driver layer is therefore the part that differs; the dataset, the trainer and the ACT defaults are unchanged. This guide is written around that split.

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

Trainer key
act
GPU tier
RTX 4090 or any card with 24 GB
Inference
about 20 ms per action step
Batch size
8, gradient accumulation 1
Learning rate
1e-5
Steps
100,000
Dataset format
LeRobot v2.1
Typical run
2 to 5 hours, about 1 to 3 USD
Servos
Dynamixel XL330 and XL430
Servo voltage
5 V and 12 V rails depending on the servo
Short answer

A Koch v1.1 trains ACT through the same pipeline as the Feetech arms: record 50 or more consistent LeRobot v2.1 episodes, then run ACT at batch 8, learning rate 1e-5, 100 000 steps and chunk size 100 for about 1 to 3 USD. The Koch-specific work is on the hardware side, where the Dynamixel bus and its 5 V and 12 V rails behave nothing like an SO-100, and where your recording frame rate decides how many seconds of motion a chunk covers.

What compatible support means in practice

The Koch v1.1 is listed as compatible rather than fully supported, and the distinction is worth taking literally. The reference arm of this platform is the SO-100, everything is tested there first, and the Koch works because the pipeline downstream of the driver does not care which bus produced the numbers. Recording, upload, the LeRobot v2.1 layout, the act trainer, the defaults and the deployment path are all shared.

What you should expect to verify yourself is the layer where the hardware is actually touched: port detection, servo IDs on the bus, calibration ranges and whether every joint answers. When something goes wrong on a Koch, the prior probability sits with that layer rather than with the trainer. Confirm all six actuators respond before you record fifty episodes, not after.

Two supply rails, and neither of them is 7.4 V

The Koch v1.1 uses Dynamixel XL330 and XL430 servos, which run on 5 V and 12 V rails depending on the servo. The Feetech STS3215 arms on this platform run at 7.4 V. Do not reuse a supply between the two families in either direction, and do not assume a 12 V brick from the Koch build is safe anywhere near an SO-100 bus, where it destroys the servos in seconds. Label the supplies at the plug.

Why the bus never reaches the model

ACT sees joint positions and camera frames, normalized against the statistics stored with the dataset. It has no notion of Dynamixel or Feetech, no notion of servo protocol, and no notion of what a raw encoder count means. This is why a Koch dataset trains without any special handling: by the time the trainer reads it, the bus has been abstracted away into a table of numbers and a set of video files.

The corollary matters more than the fact. Because the numbers were normalized per dataset, they are not comparable across arm families. A joint value recorded on a Koch means nothing on an SO-100 and vice versa. Merging Koch and Feetech episodes into one ACT dataset produces contradictory supervision, and ACT, having no pretraining to fall back on, has no way to notice that the two halves come from different robots. One arm family per dataset, without exceptions.

Frame rate decides how long a chunk lasts

ACT predicts a chunk of future actions in one pass and executes some of them before predicting again. That is what suppresses compounding error: fewer prediction points across an episode means fewer chances for a small deviation to push the arm into a state no demonstration ever covered, and actions drawn from one plan agree with one another instead of correcting each other every frame. The chunk, though, is counted in timesteps and not in seconds, so the wall-clock length of a chunk is set by the rate you recorded at.

This is worth spelling out on a Koch specifically, because a Dynamixel setup that has not been tuned sometimes records at a lower and less steady rate than a Feetech arm on the same machine. The consequence is not a training failure, it is a policy that stays open loop for noticeably longer than you intended.

Recording ratenActionStepsOpen-loop time per prediction
30 fps100 (default)About 3.3 seconds
30 fps25About 0.8 seconds
20 fps100 (default)About 5 seconds, which is a long time to be blind
20 fps25About 1.25 seconds

Two conclusions. Record at a steady rate and know what it is, because it changes the meaning of every chunk setting on this page. And if your Koch records below 30 fps, treat nActionSteps 100 as an aggressive setting rather than a neutral default, and start at 25 to 30 instead.

Prerequisites

  • A Koch v1.1 with all six actuators responding on the Dynamixel bus, and the correct rail for each servo
  • The desktop client installed and paired, so the driver and the recording path are in place
  • Calibration completed on this specific arm, since Dynamixel zero points are per unit like any other servo
  • A fixed scene camera, ideally plus a wrist camera
  • 50 or more reviewed episodes of one task, recorded at a rate you have actually checked
bash
ay-robots --version

# Every actuator must answer before you record anything. A joint that drops
# out mid-session poisons the episodes recorded around it.
ay-robots devices

# Per-unit calibration, exactly as on the Feetech arms
ay-robots calibrate

Recording for a from-scratch model

ACT builds its entire understanding of the task out of your episodes, so consistency beats volume by a wide margin here. It is also the part where a careful operator gains the most, and where the gain is easiest to throw away by recording two hundred episodes across three sessions with three slightly different techniques.

  1. One task per dataset. ACT never reads the task string, so a second task inside the same dataset is just a set of contradictory demonstrations.
  2. One approach, one grasp height, one order of operations, held for the whole dataset.
  3. Object placement varies, technique does not. Placement is what generalizes; technique variation is what averages.
  4. Review and discard failures, including the near-misses you would call good enough.
  5. Keep sessions short. Demonstration quality falls off when you are bored, and ACT copies exactly what you show it, tiredness included.
  6. Check the recorded rate on the first session rather than assuming it matches your camera settings.

Starting the run

  1. 1
    Upload and inspect

    Upload the dataset, wait for processing to finish, then open the preview and step through an episode. Confirm the action dimensions match the six actuators you expect and that both camera streams are present.

  2. 2
    Open Training and choose the dataset

    If your account also holds SO-100 data, check the robot type on the card. A Koch dataset and an SO-100 dataset look alike in a list and train into very different policies.

  3. 3
    Pick ACT

    The form switches to the lerobot parameter set and adds the two chunk fields that only ACT exposes.

  4. 4
    Set nActionSteps to match your recording rate

    Leave it at 100 if you recorded a steady 30 fps. If you recorded slower, drop it to 25 or 30 so a single prediction does not carry five seconds of blind motion.

  5. 5
    Fill in a seed, leave everything else

    Batch 8, learning rate 1e-5, 100 000 steps, gradient accumulation 1, chunk size 100. The seed is what makes your second run a comparison rather than a new experiment.

  6. 6
    Start it and check the first minutes

    The pool rents a 24 GB card by VRAM, not by GPU name, so the exact hardware differs per run. Watch that the loss actually starts falling before you walk away.

bash
# The same run over the hosted MCP server, protocol version 2025-06-18.
# start_training refuses to do anything without confirm_spend, because it
# rents a GPU and bills the account.
curl -X POST https://www.ay-robots.com/api/mcp \
  -H "Authorization: Bearer $AY_ROBOTS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "start_training",
      "arguments": {
        "dataset_id": "ds_4b18aa",
        "policy_name": "koch-block-pick-act",
        "model_id": "act",
        "max_steps": 100000,
        "confirm_spend": true
      }
    }
  }'

Defaults and when to move them

SettingDefaultChange this when
Batch size8Keep it. The 24 GB tier holds batch 8 with two camera streams; drop to 4 only in response to a real out-of-memory error, never preemptively.
Learning rate1e-5Keep it. Training from random initialization is less forgiving of a high rate than fine-tuning is, and a diverged run wastes the whole schedule.
Steps100 000Halve it while you are still shaking out the Dynamixel side of the setup. A 40 000 step run answers "did the bus record clean data" for roughly half the money.
Gradient accumulation1Only after you reduced batch size and want the effective batch back. ACT is not built around accumulation the way the A100-tier models are.
chunkSize100Lower toward 50 for episodes of only a few seconds, so the far end of the chunk is not predicting past the end of the task.
nActionSteps100Lower to 25 or 30 if you record below 30 fps, or if anything in the scene can move while the arm is executing. Changing it needs no retraining.
SeedoptionalSet it on any run you intend to compare. On a compatible-tier arm you will be running comparisons more often than on an SO-100.
Log frequencyform defaultTighten it for the first run on a new arm, where the interesting question is whether the loss moves at all in the first few hundred steps.

Cost and duration

ACT trains on the 24 GB tier, which the pool rents from a spot market by VRAM. Budget 2 to 5 hours for the full 100 000 step schedule on 50 episodes, at 0.30 to 0.60 USD per hour, so roughly 1 to 3 USD for the run. The long step count is what makes ACT look expensive on paper and it is not: the same money buys an hour or two on the A100 tier that GR00T N1.7 and Pi0.5 require, where the rate is 1.20 to 2.00 USD per hour.

A cheap run is the right way to test an unfamiliar arm

On a compatible-tier arm the first real question is whether your recordings are clean, not whether the architecture is optimal. A shortened ACT run costs a couple of dollars and answers that question in an afternoon. Only once the data is proven is it worth putting a 3 billion parameter model on an 80 GB card in front of it.

Deployment and evaluation

Inference runs locally in the client, at about 20 ms per action step. That is far inside the budget of a 30 fps control loop and it means the bottleneck on a Koch is the bus and the servo response, not the model. Set the scene back to the recorded configuration before the first attempt: cameras where they were, the same lighting, the same reset.

  1. 1
    Power the arm on the correct rails

    Check the supply against the servo before the checkpoint gets anywhere near the hardware. This is the step where an unfamiliar arm costs people servos.

  2. 2
    Watch the first chunk with a hand on the switch

    A chunked policy commits to seconds of motion at a time. If the opening move is wrong, cut power rather than let it finish the plan.

  3. 3
    Twenty attempts with varied placement

    Write down every outcome. Twenty attempts is the smallest sample that distinguishes a real 40 percent from a lucky three in a row.

  4. 4
    Compare two checkpoints and two nActionSteps values

    Four short evaluations on the same twenty positions. Checkpoint choice and replanning frequency are both worth real percentage points and neither costs GPU time.

  5. 5
    Target the failure cluster

    Record fifteen to twenty new episodes of exactly the situation that fails, then retrain. Targeted data beats more data at every scale.

Troubleshooting

SymptomLikely causeWhat to do
A joint stops responding partway through a recording sessionBus or connector, and it is a Dynamixel-side problem rather than a platform oneDiscard every episode from that session. They contain a frozen joint and ACT will faithfully learn to freeze it
The policy overshoots the object and continuesThe chunk was planned before the arm arrived, and nothing in it can react to arriving earlyLower nActionSteps, and check the recording rate: at 20 fps the default chunk holds about five seconds of motion
Regular twitch at a fixed interval during motionChunk boundaries. Each new prediction starts from a slightly different planRaise nActionSteps for fewer joins, or fix the cause: joins are worst where the dataset holds two techniques for one moment
The policy only succeeds from the exact pose it was recorded fromFrom-scratch training with a single start pose in every episode leaves everything else out of distributionVary the start pose slightly in future sessions and add episodes from the poses that currently fail
Excellent loss curve, no successes on the armThe model averaged incompatible demonstrations into one trajectory that satisfies neitherReview your episodes rather than the hyperparameters. On ACT this symptom is almost always the dataset
Motion is jerky in a way the demonstrations were notThe recorded rate was unsteady, so the timing the policy learned does not match the timing it plays backFix the recording rate first, then re-record. Smoothing the output afterward hides the problem rather than solving it

When to choose something other than ACT

ACT is the right pick on a Koch when the task is fixed, the scene is controlled, and you want the arm to move quickly and continuously. It is the wrong pick when the object can be anywhere, when you want to command the arm in language, or when you need behavior on objects that appear in no demonstration, because nothing in the model was trained before your dataset existed. The full fact sheet is at /policies/act, the head-to-head against the other 24 GB option at /compare/act-vs-smolvla, and the recording method that all of this depends on is at /learn/record-your-first-dataset.

Does the Dynamixel bus need any special configuration for training?

No. The bus is handled entirely by the client while recording, and the trainer only ever sees normalized joint values and video. Everything Koch-specific happens before the dataset exists, which is also why the Koch-specific failures all look like hardware failures rather than training failures.

Can I combine Koch episodes with SO-100 episodes to reach more data?

No. The values are normalized per dataset and the two arms are mechanically and electrically different, so pooling them gives ACT two conflicting definitions of the same task. Since it has no pretraining to reconcile them with, it will average the two and satisfy neither.

What if my Koch records at 20 fps rather than 30?

Training is unaffected, deployment is not. A chunk is counted in timesteps, so 100 executed steps at 20 fps is about five seconds of open-loop motion instead of about 3.3. Lower nActionSteps to 25 or 30 to compensate, which you can do without retraining.

Is 100 000 steps too many for a Koch dataset of 50 episodes?

It is the default and it is fine, since the whole run is 1 to 3 USD. The thing to watch on a small dataset is memorization: if loss collapses within a few thousand steps, your episodes are too similar to each other and more steps will not fix that.

Which power supply do I actually use?

Whichever the servo requires, XL330 and XL430 sit on different rails, 5 V and 12 V. The only universal rule is that nothing from a Koch build belongs on a Feetech arm: STS3215 servos are 7.4 V parts and 12 V destroys them within seconds.