The AY-Robots SO-100 hub page, the entry point for setup, teleoperation, data collection and training guides for SO-100 class arms
SO-100SO-101Robot HardwareLeRobotTeleoperation

SO-100 vs SO-101: Which Arm Should You Build?

AY-Robots ResearchAugust 23, 202618 min read

SO-100 against SO-101: the leader gear ratios, the joint 3 wiring fix, the real bill of materials in four currencies, and why LeRobot treats both arms as one class.

SO-100 against SO-101 in one screen

  • Same arm. Six Feetech STS3215 joints, motor IDs 1 to 6, the same joint names. The SO-101 is a revision, not a new design.
  • The leader is the real change. On the SO-100 you pull the gearbox out of all six leader motors by hand; the SO-101 ships three gear ratios instead (1/345, 1/191, 1/147).
  • The wiring was rerouted. On the SO-100 the joint 3 cable worked loose and limited that joint's travel; the SO-101 routing fixes it.
  • In LeRobot 0.6.2 the two arms are one Python class: SO100Follower = SOFollower and SO101Follower = SOFollower, two lines apart in one file.
  • Parts cost is a rounding error apart: 229.88 USD for the two-arm SO-101 bill of materials against 232 USD for the SO-100.
  • Buy the SO-101 unless you already own SO-100 parts. The SO-100 build is deprecated upstream and everything past the USB port is identical.

What you are actually choosing between

The SO-100 debuted at around 100 USD in October 2024, the SO-101 on 28 April 2025, both from The Robot Studio with Hugging Face; TechCrunch covered the launch. They share a repository, a servo family and, as of LeRobot 0.6.2, one driver class. Everything below is read from the SO-ARM100 repository main branch and the LeRobot docs on 23 August 2026.

The upstream README states the change in one sentence: the SO-101 "has improved wiring, is easier to assemble (no gear removal) and uses updated motors for the leader arm". No new degree of freedom, no new gripper geometry, no different bus. If you have driven an SO-100 through teleoperation you already know how an SO-101 behaves.

PropertySO-100SO-101
AnnouncedOctober 202428 April 2025
Upstream doc statusSO100.md, deprecated in the READMEPrimary: README plus LeRobot guide
Leader servos6x STS3215, gears removed by hand1x C001 (1/345), 2x C044 (1/191), 3x C046 (1/147)
Leader gear removalRequired for all six motorsNot required
Motor config before assemblyRequired, connectors are not reachable afterwardsCan be done with the arm assembled
WiringJoint 3 cable works loose and limits travelRerouted, no longer limits joint range
LeRobot driver classSOFollower / SOLeaderSOFollower / SOLeader (identical)

The leader arm is where the money went

The follower barely changed. The leader-follower pair is where the SO-101 earns its number. On the SO-100 the leader uses the same six 1/345 servos as the follower, and the documented build step is to open every one and pull the gear train out. The SO-100 guide says why: "you will only use the position encoding of the motor and reduce friction to more easily operate the leader arm".

That works, and thousands of arms were built that way, but it is fiddly disassembly on brand new parts and it is irreversible. The SO-101 replaces it with three gear ratios chosen per joint, so the leader is light enough to drag by hand while still holding its own weight. This is the mapping the LeRobot SO-101 assembly guide publishes:

Leader jointMotor IDSO-101 gear ratioFeetech part codeSO-100 equivalent
shoulder_pan11 / 191C0441/345, gears removed
shoulder_lift21 / 345C0011/345, gears removed
elbow_flex31 / 191C0441/345, gears removed
wrist_flex41 / 147C0461/345, gears removed
wrist_roll51 / 147C0461/345, gears removed
gripper61 / 147C0461/345, gears removed
Why shoulder_lift keeps the 1/345 motor

shoulder_lift carries the whole arm against gravity, so it is the one leader joint that keeps the tall reduction and the C001. The three wrist-side joints drop to 1/147 because they only carry the handle and trigger. The two-arm bill of materials matches exactly: 7x C001 (six follower plus one leader), 2x C044, 3x C046.

There is a second consequence. The SO-101 leader keeps a working drivetrain, so its motors can be driven, not only read. The DAgger strategy requires a teleoperator with active motors that can toggle torque and move to target positions, so the leader parks on the robot pose before you take over; the docs list the shared so_leader class as covering both SO100 and SO101. On an SO-100 leader built to spec there is no gear train left between motor and joint, so nothing for that torque to act through. That last step is mechanical inference, not a documented limitation: same software interface, different physics.

The wrist revision, part by part

"Wrist revision" is doing a lot of work in community summaries, so here is what is in the repository. The SO-101 ships new wrist geometry as Wrist_Roll_Pitch_SO101.stl and Motor_holder_SO101_Wrist.stl, and keeps separate leader and follower wrist roll parts as the SO-100 already did: Wrist_Roll_SO101.stl and Wrist_Roll_Follower_SO101.stl replace the older pair. The cabling runs differently too, the change the Seeed wiki credits with removing the joint 3 disconnection.

You can check what did not change by diffing the two URDF files the repo ships for simulation, so100.urdf and so101_new_calib.urdf. The two long links come out identical to the published digit; the wrist and gripper origins do not line up, but that is an export artefact rather than a redesign:

text
# Simulation/SO100/so100.urdf   vs   Simulation/SO101/so101_new_calib.urdf
# joint origin xyz in metres, copied as written in each file

joint          SO-100 xyz                SO-101 xyz
shoulder_pan   0 -0.0452 0.0165          0.0388353 -8.97657e-09 0.0624
shoulder_lift  0 0.1025 0.0306           -0.0303992 -0.0182778 -0.0542
elbow_flex     0 0.11257 0.028           -0.11257 -0.028 1.73763e-16   <- same magnitudes
wrist_flex     0 0.0052 0.1349           -0.1349 0.0052 3.62355e-17    <- same magnitudes
wrist_roll     0 -0.0601 0               5.55112e-17 -0.0611 0.0181
gripper        -0.0202 -0.0244 0         0.0202 0.0188 -0.0234

# The SO-101 file is exported in a different frame: axis order and signs are permuted,
# and it carries an extra gripper_frame_joint the SO-100 file does not have. Only
# elbow_flex (112.57 mm) and wrist_flex (5.2 / 134.9 mm) map term for term, and those
# two are exactly equal. The wrist_roll and gripper rows are not in the same frame,
# so subtracting them tells you nothing about link length.

# joint limits are not comparable either:
# so100.urdf            shoulder_lift  lower=0        upper=3.5      -> span 3.5
# so101_new_calib.urdf  shoulder_lift  lower=-1.74533 upper=1.74533  -> span 3.49066
# near-identical span, different zero pose. Other joints differ by more than that:
# wrist_flex spans 3.7 rad in so100.urdf and 3.31612 rad in so101_new_calib.urdf.
Joint origins and limits from the two URDF files in the SO-ARM100 repo, main branch, read 23 August 2026.
Do not difference the two URDF files

The two files use different zero poses, axis orders and sign conventions, and their effort and velocity fields are export defaults rather than measurements. Comparing lower and upper across them will make you believe joints gained or lost travel that they did not. Compare spans where the frames allow it, or better, the ranges your own calibration run records on the real hardware.

The honest summary: the wrist parts were redesigned, the cable no longer fights the joint, and the leader wrist joints got much lighter gearing. Nothing in either URDF shows the follower reaching further or moving less, and nobody would notice a millimetre of link length in a policy. Everybody notices the cable that stops falling out.

What each one costs

Two numbers get mixed up: what the upstream bill of materials totals if you source everything yourself, and what a kit costs. These are the repository totals, excluding printing and cameras.

ConfigurationUSEUCNJP
SO-101, follower plus leader229.88 USD226.30 EUR1343.16 RMB44,530 JPY
SO-101, single follower arm121.94 USD124.30 EUR682.23 RMB24,414 JPY
SO-100, follower plus leader232 USD244 EUR1343.16 RMBnot listed
SO-100, single arm123 USD128 EUR682.23 RMBnot listed

The SO-101 is the same price or slightly cheaper despite the mixed servo set, because the unit price fell: 13.89 USD per servo against a flat 15 USD in the SO-100 table. On this platform the SO-100 is quoted at roughly 110 to 150 EUR in parts and the SO-101 at 130 to 170 EUR, the same ballpark once you add filament, a camera and shipping. Either way the arm is the cheap part: a single fine-tuning run costs 1 to 3 USD on a 24 GB card and 4 to 12 USD on an A100 or H100, and you will do many.

7.4 V servos, 12 V supply, dead arm

The standard build for both arms uses the 7.4 V STS3215, rated 16.5 kg.cm stall at 6 V in the README. Feetech also sells a 12 V STS3215 at 30 kg.cm, which needs a 12 V 5 A+ supply instead of the 5 V one. Putting 12 V into a 7.4 V servo destroys it. The README is explicit that the SO-101 leader is always 7.4 V. Check the barrel jack before first power-up, and see servo not responding if a joint has already gone quiet.

What the shared servo actually does

Both arms hang on the same STS3215, so its limits are the arm's limits. Robonine bench-tested a 1:345 STS3215 and measured about 0.87 degrees of angular backlash at the joint against 0.5 degrees in the datasheet, and repeatability near 0.17 degrees, some two counts on the 12-bit encoder. Their unit was the 12 V C018, so its torque numbers do not carry to the 7.4 V C001, but the gear train and encoder are the same part. That backlash sets the floor on how repeatable any policy on either revision can be.

The AY-Robots cost table listing which GPU card each policy needs, the typical run time and price per training run, and how many episodes it takes before a policy is useful
The /try cost table. The arm is a one-off purchase; the recurring cost is GPU time, and none of these numbers depend on which revision recorded the episodes.

What stays identical in software

This is the part that decides the buying question. In LeRobot 0.6.2 the SO-100 and SO-101 are not two similar classes; they are one class with two names.

python
# src/lerobot/robots/so_follower/config_so_follower.py
@RobotConfig.register_subclass("so101_follower")
@RobotConfig.register_subclass("so100_follower")
@dataclass
class SOFollowerRobotConfig(RobotConfig, SOFollowerConfig):
    pass

SO100FollowerConfig = SOFollowerRobotConfig
SO101FollowerConfig = SOFollowerRobotConfig

# src/lerobot/robots/so_follower/so_follower.py
class SOFollower(Robot):
    name = "so_follower"
    ...
    "shoulder_pan":  Motor(1, "sts3215", norm_mode_body),
    "shoulder_lift": Motor(2, "sts3215", norm_mode_body),
    "elbow_flex":    Motor(3, "sts3215", norm_mode_body),
    "wrist_flex":    Motor(4, "sts3215", norm_mode_body),
    "wrist_roll":    Motor(5, "sts3215", norm_mode_body),
    "gripper":       Motor(6, "sts3215", MotorNormMode.RANGE_0_100),

SO100Follower = SOFollower
SO101Follower = SOFollower
LeRobot 0.6.2, main branch. The leader side (so_leader) has exactly the same shape.
LayerWhat it seesSO-100SO-101
Servo busEncoder resolution and baudrate4096 counts, 1,000,000 baud4096 counts, 1,000,000 baud
DriverPython classSOFollowerSOFollower
DriverPosition PID written on connectP=16, I=0, D=32P=16, I=0, D=32
TeleoperatorPython classSOLeader (so_leader)SOLeader (so_leader)
Datasetrobot_type written by lerobot-recordso_followerso_follower
PolicyAction and state dimension66
TrainerDataset format it must be inv2.0 or v2.1 for GR00T, v3.0 for the restv2.0 or v2.1 for GR00T, v3.0 for the rest
Your dataset does not know which arm recorded it

lerobot-record writes robot_type=robot.name into the dataset metadata, and SOFollower.name is the string so_follower for both arms. A LeRobot dataset recorded on an SO-100 and one recorded on an SO-101 are indistinguishable at the metadata level. Both feed the same trainers, which is convenient, and a reason to put the physical arm in the dataset name yourself.

The consequence: an ACT or SmolVLA checkpoint trained on SO-100 episodes loads and runs on an SO-101 with no config change. Whether it does the task is another question. Calibration offsets, gripper wear and camera mounting differ from build to build, so cross-arm transfer degrades the way it does between two calibrations of the same model. Re-record if the policy came from someone else's arm, and read policy only works in one setup before you blame the model.

The build and first run, command by command

This is the SO-101 path. For an SO-100, swap so101_follower for so100_follower and so101_leader for so100_leader, and configure the motors before assembling the arm, because the connectors are unreachable afterwards. Nothing else differs.

  1. 1
    Print the parts

    PLA+, 0.4 mm nozzle at 0.2 mm layer or 0.6 mm at 0.4 mm, 15 percent infill, supports everywhere but ignoring slopes over 45 degrees and none in horizontal screw holes. The SO-101 has 14 distinct parts, 9 shared, so print those twice for a leader plus follower pair. Print the gauge first against a real STS3215.

  2. 2
    Install LeRobot with the Feetech SDK

    LeRobot 0.6.2 needs Python 3.12 or newer. The Feetech extra is not installed by default, and nothing finds your servos without it.

    bash
    git clone https://github.com/huggingface/lerobot.git
    cd lerobot
    pip install -e ".[feetech]"
  3. 3
    Find the serial port for each controller board

    Run it with both boards connected and unplug the one you are identifying when prompted. On Linux you usually have to open the device permissions first. If nothing shows up, check that a Waveshare board has both jumpers on the B (USB) channel.

    bash
    lerobot-find-port
    # -> The port of this MotorsBus is /dev/tty.usbmodem575E0032081
    
    # Linux only
    sudo chmod 666 /dev/ttyACM0
    sudo chmod 666 /dev/ttyACM1
  4. 4
    Set motor IDs and baudrate, one motor at a time

    The script walks backwards from the gripper. Connect exactly one motor, press Enter, and it writes the ID and baudrate into that motor's EEPROM. Six times per arm, once for the life of the motor.

    bash
    lerobot-setup-motors \
        --robot.type=so101_follower \
        --robot.port=/dev/tty.usbmodem585A0076841
    
    # 'gripper' motor id set to 6
    # Connect the controller board to the 'wrist_roll' motor only and press enter.
    
    lerobot-setup-motors \
        --teleop.type=so101_leader \
        --teleop.port=/dev/tty.usbmodem575E0031751
  5. 5
    Calibrate both arms

    Move every joint to mid-range, press Enter, then sweep each joint through its full travel. The id you pass is the key the calibration file is stored under; reuse it when teleoperating, recording and rolling out. Calibrate the leader too.

    bash
    lerobot-calibrate \
        --robot.type=so101_follower \
        --robot.port=/dev/tty.usbmodem58760431551 \
        --robot.id=my_awesome_follower_arm
    
    lerobot-calibrate \
        --teleop.type=so101_leader \
        --teleop.port=/dev/tty.usbmodem575E0031751 \
        --teleop.id=my_awesome_leader_arm
  6. 6
    Teleoperate, with the cameras on

    Add the cameras before you record anything. What the camera sees while recording is what the policy gets at inference time; a camera added later means a dataset you throw away.

    bash
    lerobot-teleoperate \
        --robot.type=so101_follower \
        --robot.port=/dev/tty.usbmodem5AB90687491 \
        --robot.id=my_awesome_follower_arm \
        --robot.cameras="{front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
        --teleop.type=so101_leader \
        --teleop.port=/dev/tty.usbmodem5AB90689011 \
        --teleop.id=my_awesome_leader_arm \
        --display_data=true
  7. 7
    Record a dataset

    Defaults are 50 episodes, 60 s per episode, 60 s reset. Upstream suggests at least 50 episodes for a first task, about 10 per object position. Right arrow or n ends an episode early, left arrow or r re-records, Escape or q stops.

    bash
    lerobot-record \
        --robot.type=so101_follower \
        --robot.port=/dev/tty.usbmodem585A0076841 \
        --robot.id=my_awesome_follower_arm \
        --robot.cameras="{front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
        --teleop.type=so101_leader \
        --teleop.port=/dev/tty.usbmodem58760431551 \
        --teleop.id=my_awesome_leader_arm \
        --display_data=true \
        --dataset.repo_id=${HF_USER}/so101-cube \
        --dataset.num_episodes=50 \
        --dataset.single_task="Grab the black cube"
  8. 8
    Run the trained policy back on the arm

    lerobot-rollout takes a strategy: base for a plain autonomous run, sentry for continuous recording with upload, highlight for a ring buffer saved on a keystroke, dagger for human takeover, episodic for reset phases. All of them take --inference.type=sync or rtc; the docs point at rtc for slow VLA models such as Pi0, Pi0.5 and SmolVLA.

    bash
    lerobot-rollout \
      --strategy.type=base \
      --policy.path=${HF_USER}/my_policy \
      --robot.type=so101_follower \
      --robot.port=/dev/ttyACM1 \
      --robot.cameras="{front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
      --task="Grab the black cube" \
      --duration=60

Two ways to get from parts to a working policy

You buy the bill of materials, print 14 parts, assemble two arms, install LeRobot with the Feetech extra, configure twelve motors one at a time, calibrate, record, then find a GPU.

  1. Source the BOM from the SO-ARM100 repo, or buy a kit from one of the listed vendors.
  2. pip install -e ".[feetech]", then find-port, setup-motors, calibrate.
  3. lerobot-record 50 episodes with at least one fixed camera.
  4. Rent a GPU, run lerobot-train, then lerobot-rollout the checkpoint back onto the arm.
The real cost is time, not parts

Upstream puts SO-100 assembly at a bit over an hour for the first arm and under an hour for the second. The SO-101 drops the gear-removal step, but motor configuration is still twelve plug-press-unplug cycles.

Which one to buy today

Choosing the SO-101 over the SO-100
Advantages
  • No gear removal. Six motors you never have to open.
  • Motor configuration works with the arm assembled, so a dead servo later is a swap, not a teardown.
  • The leader keeps a working drivetrain, so the torque-enabled DAgger takeover has something to act through.
  • It is the maintained build: SO100.md is marked deprecated upstream.
  • Same or lower parts cost: 229.88 USD against 232 USD for the two-arm bill of materials.
Trade-offs
  • Three servo part numbers instead of one. Ordering the wrong ratio for the wrong joint is a failure mode the SO-100 did not have, and the upstream BOM carries no China link for the C044 and C046 at all.
  • Nothing you own transfers. Different STLs, so an SO-100 frame with SO-101 servos is not a supported build.
  • It buys you no accuracy, no payload and no reach. The follower is the same arm with the same 7.4 V servos.
  • A trained policy will not improve because you switched. The policy never sees the leader.
Your situationBuyWhy
No hardware at allSO-101Actively maintained build, no gear removal, same price
You already own 12 STS3215 1/345 servosSO-100Reprinting is cheap, rebuying six mixed-ratio servos is not
You own an SO-100 that worksKeep itThe software cannot tell the difference
You want human-in-the-loop takeoverSO-101The DAgger flow parks the leader on the robot pose; a gear-stripped leader has no drivetrain to park with
You want more torque or reachNeitherLook at Koch v1.1, LeKiwi, or a 12 V STS3215 build

If you are deciding between arm families rather than revisions, the SO-100 against SO-101 comparison sits next to SO-100 against Koch v1.1 and SO-100 against LeKiwi. Those are real differences: the Koch v1.1 runs Dynamixel servos on 5 V and 12 V rails at 250 to 350 EUR, and the LeKiwi adds a mobile base at 400 to 500 EUR.

The AY-Robots teleoperator page showing an SO-100 arm on a desk with the heading about becoming a robot operator from anywhere
The arm in the /teleoperator photo is an SO-100. Operators drive it remotely, and nothing in that workflow changes on an SO-101.

Where the arm choice stops mattering

Once the USB cable is in, the revision is invisible. The five trainable policies here all take the same six-dimensional state and action vector, the same camera streams and the same episodes. What constrains you is the minimum episode count and the inference latency of the model you pick:

PolicyParamsLatency per action stepGPU tierMin episodesDataset format
GR00T N1.7~3 B, ~40 M trained152 msA100 80 GB or H100 80 GB50LeRobot v2.0 or v2.1
GR00T N1.5~3 B165 msA100 80 GB or H100 80 GB50LeRobot v2.0 or v2.1
Pi0.5~3 B485 msA100 80 GB or H100 80 GB50LeRobot v3.0
SmolVLA~450 M245 msRTX 4090 or any 24 GB card30LeRobot v3.0
ACT~80 M20 msRTX 4090 or any 24 GB card50LeRobot v3.0

That 20 ms to 485 ms spread decides whether your setup works, not the arm revision. Inference has to sit next to the servos for anything fast: adding public-internet round trips to a 485 ms control step turns a working policy into a hesitant one. Remote inference is viable for slow pick-and-place, not for reactive motion. For benchmark comparisons, the arena lists 85 VLA models with 332 benchmark results, each linked to its paper or model card.

No arm yet? Drive a real one first

A physical SO-100 streams in the browser on a queue, no signup, so you can find out what teleoperating one of these actually feels like before you order twelve servos.

Drive a real arm

The traps that eat a day

Resuming a dataset recorded under an older robot_type

lerobot-record --resume=true runs sanity_check_dataset_robot_compatibility, which compares the dataset's stored robot_type, fps and feature dict against the current run and raises ValueError: Dataset metadata compatibility check failed with mismatches on any difference. Current LeRobot writes so_follower for both arms; releases up to 0.3.3 wrote so100_follower and so101_follower, so a dataset from back then refuses to resume today even though the hardware is fine. Start a new dataset and merge later.

  • Mixed servo ratios on the wrong joints. On an SO-101 leader the C001 belongs on shoulder_lift only. Put it on wrist_flex and the wrist fights you, and you will hunt for the fault in software.
  • Calibration id drift. --robot.id is the filename the calibration lives under. Record with one id, roll out with another, and the policy reaches for the wrong place. See joint stops early.
  • Adding a camera after recording. The trainer builds its input shape from the dataset, so a camera added later means a new dataset.
  • A LeRobot v3.0 dataset fed to GR00T. The GR00T loader crashes on v3.0 and needs converting down to v2.1. See dataset rejected v3.
  • Blaming the revision for a bad policy. Check camera framing and episode count before you buy hardware. Neither is a hardware problem.
The AY-Robots fix index listing failure modes such as arm not detected, servo not responding, gripper does not close and policy freezes mid-motion
The /fix index. Every failure mode on it is shared between the SO-100 and the SO-101, which is another way of saying the two arms fail identically.

Starting from zero, the complete SO-100 setup guide covers the whole chain end to end, SO-100 getting started is the short version, and collecting high quality VLA training data is the one to read before recording. Finally, the VLA overview explains what a vision-language-action model does with those episodes.

Can I run a policy trained on SO-100 data on an SO-101?

Yes, with no configuration change. LeRobot 0.6.2 aliases SO100Follower and SO101Follower to the same SOFollower class: same six joints, same motor IDs, same 6-dimensional action vector, and lerobot-record writes the same robot_type for both. Whether it performs depends on calibration, camera placement and gripper wear, which vary between two builds of one revision as much as across revisions.

Is the SO-101 more accurate or stronger than the SO-100?

No. The follower is the same design with the same 7.4 V Feetech STS3215 servos at 1/345 gearing, and the link offsets that can be compared term for term across the two URDF files are exactly equal. The SO-101 changes leader gearing, wiring and assembly. For more torque the path is the 12 V STS3215 at 30 kg.cm with a 12 V 5 A+ supply, not the revision number.

Do I have to remove the gears from the leader motors?

On an SO-100, yes, from all six leader motors, so they act only as position encoders and the arm is light enough to move by hand. On an SO-101, no. That is the biggest build-time difference, and it is why the SO-101 uses three leader gear ratios instead of one.

Can I upgrade an SO-100 to an SO-101?

Not as a parts swap. The STLs differ, so the frame is a full reprint, and the leader needs new servos with the three mixed ratios. A follower-only upgrade is just a reprint, since the follower servos are identical 1/345 units.

How many episodes do I need before training is worth starting?

SmolVLA needs at least 30 episodes on this platform; GR00T N1.7, GR00T N1.5, Pi0.5 and ACT each need at least 50. Upstream LeRobot suggests about 50 episodes for a first task, roughly 10 per object position, cameras fixed. The arm revision has no bearing on those numbers.

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started