
Servo ids, horn angle, cable routing and the 7.4 V rule: the SO-100 build errors nobody catches on the bench, and how each one turns into a dataset that trains a bad policy.
An SO-100 built wrong usually still works. That is the problem. The arm powers up, teleoperation feels smooth, the cameras appear in the recorder, and you spend a Saturday collecting fifty episodes. The build error surfaces four days later, when the policy you fine-tuned closes on air two centimetres left of the cube.
Four decisions on a SO-100 are invisible on the bench and expensive in the dataset: servo ids, horn angle, cable routing, and matching the supply to the servo variant. Checked against LeRobot main (release v0.6.1, 3 August 2026) and the SO-ARM100 repository as it stands today.
What you need to know
- •Every STS3215 ships with id 1. Daisy-chain before lerobot-setup-motors and nothing on the bus can be addressed.
- •lerobot gives the sts3215 4096 counts per turn and Waveshare's ST3215 page lists mechanical limited angle: no. The horn angle decides where the 0/4095 seam falls inside the joint's travel.
- •A seam inside the travel writes a silently wrong calibration file, and it does not raise. Under the default use_degrees=True every sample of that joint is offset. Only the gripper is clamped. See lerobot issues 3586 and 3587.
- •The SO-100 is a 7.4 V arm. The 12 V STS3215 is a different part with its own supply. Do not cross them.
- •The cheap fix is a 30 minute acceptance pass before recording.
What actually breaks, and when you find out
Ranked by cost, not likelihood. The second is the weekend-eater: it gives no signal until a training run has already finished.
| Build mistake | First visible symptom | When you notice | Cost to undo |
|---|---|---|---|
| Ids never set, or set after daisy-chaining | setup-motors reports the motor was not found | Before the first teleop session | 30 minutes, the cheapest failure available |
| Horn fitted at an arbitrary angle | None. Teleoperation feels correct. | After training, as a constant offset | Teardown of that joint plus a re-record |
| Wrong supply for the servo variant | Motors do not answer a ping, or drop out | Immediately, or as a flaky fault later | New servos |
| Cable crossing a rotation axis | Intermittent 'no status packet' while recording | Mid-recording, as frozen joints | One joint apart, plus the lost episodes |
| No recalibration after a repair | Joint stops early, or the arm snaps at connect | First teleop after the repair | Delete the calibration file, redo it |
Mistake 1: servo ids, and the order they have to be set in
The six servos share one half-duplex serial bus, addressed by id, and every STS3215 leaves the factory as id 1. Until each has a distinct id, six devices answer to one address. The SO-100 assembly guide configures motors before assembly, because once the arm is together the connectors are unreachable.
# install lerobot with the Feetech SDK
pip install -e ".[feetech]"
# find which serial port belongs to which arm
lerobot-find-port
# Finding all available ports for the MotorBus.
# ['/dev/tty.usbmodem575E0032081', '/dev/tty.usbmodem575E0031751']
# Remove the USB cable from your MotorsBus and press Enter when done.The id map is fixed in the source, not chosen by you. so_follower.py declares shoulder_pan on 1 through gripper on 6, and the leader uses the same names. The setup script walks the list in reverse, gripper first, shoulder_pan last.
| Joint | Motor id | How its range is set during calibration | Value the policy sees |
|---|---|---|---|
| shoulder_pan | 1 | Swept by hand | Degrees (use_degrees defaults to True) |
| shoulder_lift | 2 | Swept by hand | Degrees |
| elbow_flex | 3 | Swept by hand | Degrees |
| wrist_flex | 4 | Swept by hand | Degrees |
| wrist_roll | 5 | Not swept. Forced to the full 0 to 4095 turn. | Degrees |
| gripper | 6 | Swept open and closed | 0 to 100 |
- 1Connect exactly one motor
Power the board, plug in USB, and run one 3-pin cable to the gripper motor only. It must not be chained to another motor at the far end.
bashlerobot-setup-motors \ --robot.type=so100_follower \ --robot.port=/dev/tty.usbmodem585A0076841 - 2Answer the prompt
Press Enter and the script writes the id and 1 Mbps baud rate into that motor's EEPROM, then moves on.
textConnect the controller board to the 'gripper' motor only and press enter. 'gripper' motor id set to 6 Connect the controller board to the 'wrist_roll' motor only and press enter. - 3Repeat down to shoulder_pan
Move the board cable to the next motor each time. Finding a motor sweeps the STS/SMS baud table, 1000000 down to 19200, so a servo repurposed from another robot is still found.
- 4Do the leader with the teleop flags
The leader takes
--teleop.typeand--teleop.portinstead of--robot.*. Same procedure, same id order.bashlerobot-setup-motors \ --teleop.type=so100_leader \ --teleop.port=/dev/tty.usbmodem575E0031751 - 5Only now chain them
Plug each motor into the next, and shoulder_pan (id 1) into the board. The ids live in EEPROM, so this never repeats.
If a second motor is still chained beyond the one you are programming, both answer as id 1 and the write lands where you did not intend. The error is RuntimeError: Motor 'gripper' (model 'sts3215') was not found. On a Waveshare board, check both jumpers sit on the B channel and the power barrel has not fallen out. See servo not responding.

Mistake 2: the horn angle, and why it only shows up after training
This is the expensive one. Waveshare's wiki page for the ST3215, the 12 V sibling of the SO-100's servo, lists a 360 degree magnetic encoder at 360/4096 resolution and, under mechanical limited angle, says: no. lerobot's model table gives the sts3215 the same 4096 counts. What stops your joint is the printed link bolted to the horn hitting another printed part, so the horn angle when you tighten the M3 screw decides where the 0/4095 seam falls inside that travel.
The LeRobot SO-100 page hints at it at step 6: install both horns, and try not to move the motor position while attaching them, especially on the leader arms where the gears were removed. That reads like a tidiness note. Here is what calibration does with the position you leave behind.
# lerobot/motors/motors_bus.py - set_half_turn_homings docstring
# "The function computes and writes a homing offset such that the present
# position becomes exactly one half-turn (e.g. 2047 on a 12-bit encoder)."
# lerobot/motors/feetech/feetech.py - _get_half_turn_homings does the arithmetic
max_res = self.model_resolution_table[model] - 1 # 4096 - 1 = 4095 for sts3215
half_turn_homings[motor] = pos - int(max_res / 2) # int(4095 / 2) = 2047
# lerobot/robots/so_follower/so_follower.py - calibrate()
input(f"Move {self} to the middle of its range of motion and press ENTER....")
homing_offsets = self.bus.set_half_turn_homings()
full_turn_motor = "wrist_roll"
unknown_range_motors = [m for m in self.bus.motors if m != full_turn_motor]
range_mins, range_maxes = self.bus.record_ranges_of_motion(unknown_range_motors)
range_mins[full_turn_motor] = 0
range_maxes[full_turn_motor] = 4095Read that order carefully. The homing offset is chosen before anything is known about the joint's real travel. If the pose you held is not the mechanical midpoint, the seam lands inside the travel, and the sweep that follows records the range with a plain min and max over a value that wraps modulo 4096.
# record_ranges_of_motion - the two lines that cannot see a wrap
mins = {motor: min(positions[motor], min_) for motor, min_ in mins.items()}
maxes = {motor: max(positions[motor], max_) for motor, max_ in maxes.items()}
# _normalize - what the dataset and the policy actually get
bounded_val = min(max_, max(min_, val))
if norm_mode is MotorNormMode.RANGE_M100_100: # body joints, use_degrees=False
norm = (((bounded_val - min_) / (max_ - min_)) * 200) - 100
elif norm_mode is MotorNormMode.RANGE_0_100: # the gripper, always
norm = ((bounded_val - min_) / (max_ - min_)) * 100
elif norm_mode is MotorNormMode.DEGREES: # body joints, the default
mid = (min_ + max_) / 2
max_res = self.model_resolution_table[self._id_to_model(id_)] - 1
normalized_values[id_] = (val - mid) * 360 / max_res # val, not bounded_valA bad range does not raise. Under the default use_degrees=True the five body joints take the DEGREES branch, where each sample is measured from the midpoint of the recorded range: a midpoint 200 counts off shifts that joint by about 18 degrees for the whole dataset, and a pose on the far side of the seam arrives as a jump of nearly a full turn. The gripper is the one column always clamped.
lerobot issue 3586 documents an SO-101 follower whose shoulder_lift recorded 2045 to 3919 while the seam sat at raw count 3374, inside the joint's real travel of roughly 1314 to 3657. Its rest pose at raw 3601 then reads as Present 228: outside the recorded band, physically reachable. The JSON looked normal, no warning. Companion issue 3587 proposes sweeping first and choosing the offset second. Both are open, so today this is your responsibility.
Issue 3585 is the dramatic version: when a present position sits outside the calibrated band and something writes a goal, the firmware clamps to the nearest limit, sets torque enable on its next PID tick even though the host disabled torque, and drives there. That is the arm that snaps when you start teleop. Start at arm twitches then sags.
Before tightening a horn screw, put that joint where the printed parts allow roughly equal travel both ways, and hold the servo body still. Then hold that same mid-travel pose on every joint at the first lerobot-calibrate prompt. wrist_roll is exempt: lerobot forces it to the full turn and never sweeps it.
The SO-101 adds a gear-ratio mistake the SO-100 does not have
On the SO-100 the bill of materials is one line item, twelve identical STS3215 servos, and the leader is made movable by removing the gears from its six. On the SO-101 nothing is removed: the follower stays on six 1/345 motors while the leader mixes three ratios in fixed positions, so a servo in the wrong socket is a new way to build the arm wrong.
| SO-101 leader axis | Motor id | Gear ratio | Part code |
|---|---|---|---|
| Base / shoulder_pan | 1 | 1/191 | C044 |
| shoulder_lift | 2 | 1/345 | C001 |
| elbow_flex | 3 | 1/191 | C044 |
| wrist_flex | 4 | 1/147 | C046 |
| wrist_roll | 5 | 1/147 | C046 |
| gripper | 6 | 1/147 | C046 |
Gear removal on the SO-100 is its own ordering trap: SO-ARM100 issue 31 is somebody who assembled the whole leader before finding the instruction. The maintainer's answer: not strictly necessary, but without it the leader cannot be driven one-handed for long. SO-100 against SO-101 has the rest.
Mistake 3: the 7.4 V rule
The platform lists the SO-100 and SO-101 as 7.4 V arms on Feetech STS3215 servos, at roughly 110 to 150 EUR and 130 to 170 EUR of parts. That voltage names the servo variant, and it matters because an identical-looking 12 V STS3215 is sold in the same shops.
| Part | Stall torque | Supply it is paired with | Source of the number |
|---|---|---|---|
| STS3215 7.4 V (C001, C044, C046) | 16.5 kg.cm at 6 V, less at 5 V | 5 V, per the bill of materials | SO-ARM100 README |
| STS3215 12 V | 30 kg.cm | 12 V at 5 A or more | SO-ARM100 README |
| ST3215 (Waveshare listing) | up to 30 kg.cm at 12 V | 6 to 12.6 V input | Waveshare ST3215 wiki |
Feeding 12 V into 7.4 V STS3215 servos destroys them, and the SO-ARM100 README is explicit that the 12 V motors need a 12 V 5 A+ supply instead of the 5 V one. The mismatch runs both ways: a commenter on lerobot issue 1252 ran a kit follower built on 12 V servos off a 5 V supply, and only got a clean bus after switching. Over-voltage counts too: issue 3394 opens with six STS3215 that answer no ping at any baud rate, and a commenter with the same symptom measured a nominally 12 V adapter delivering about 16 V, which had tripped protection mode. A multimeter on the barrel jack is a two minute check.
The other supported arms have their own rails: Koch v1.1 runs Dynamixel XL330 and XL430 on 5 V and 12 V rails, and LeKiwi is a 7.4 V arm on a 12 V base. On a LeKiwi both supplies sit on one robot, the easiest place to plug the wrong barrel into the wrong board.
Mistake 4: cable routing, which becomes dropped frames
The LeRobot assembly notes say it in an aside: inserting cables beforehand is much easier than afterward. What they do not spell out is the later cost. A 3-pin cable crossing a rotation axis under tension is tugged every time that joint sweeps. It rarely disconnects outright. It intermittently corrupts a packet on a 1 Mbps bus.
Failed to sync read 'Present_Position' on ids=[2,3,4,6] after 1 tries.
[TxRxResult] There is no status packet!Current lerobot ships num_read_retries: int = 2 on SOFollowerConfig, with a source comment that Feetech buses occasionally return a corrupted status packet when several joints move at once, which otherwise aborts the control loop. Retries are immediate and only happen on failure. It sits on the same config object as --robot.port. Raising it lets recording survive a marginal bus, but a run that only completes at a high retry count is telling you a cable is being pulled.
- Insert both 3-pin cables before the motor goes into its printed holder. Retrofitting means taking the joint apart.
- Loop the cable with slack around the rotation axis instead of stretching it across. The guide says which wire goes behind the shoulder holder and which routes upward.
- Use the printed cable holders. The motor 3 to motor 4 cable gets its own screw for a reason.
- With power off, sweep every joint to both limits and watch the cable. If it goes taut, it fails during recording, not now.
- Rule the host out too: on issue 1252 an SO-101 owner found the error got much worse with two arms and two cameras on one cheap USB hub.
- A dropped read is not a harmless retry. It is a repeated or missing frame in an episode, and it trains as a moment where the arm did not move.
Mistake 5: not recalibrating after you touch the hardware
Calibration is a file, and the file outlives the hardware it describes. lerobot writes one JSON per arm id under your Hugging Face cache and reads it at connect time. Swap a servo, re-seat a horn or reprint a link, and it describes an arm that no longer exists.
# where the file lives (override with HF_LEROBOT_CALIBRATION)
ls ~/.cache/huggingface/lerobot/calibration/robots/so_follower/
# my_awesome_follower_arm.json
# per joint: id, drive_mode, homing_offset, range_min, range_max
cat ~/.cache/huggingface/lerobot/calibration/robots/so_follower/my_awesome_follower_arm.json
# after any mechanical change, delete it and calibrate from scratch
rm ~/.cache/huggingface/lerobot/calibration/robots/so_follower/my_awesome_follower_arm.json
lerobot-calibrate \
--robot.type=so100_follower \
--robot.port=/dev/tty.usbmodem58760431551 \
--robot.id=my_awesome_follower_armThat reuse prompt is the trap: the default keeps the old numbers and writes them back to the motors, exactly wrong after a repair. Far enough out you get a hard error instead of silence, which is the good case. Issue 1296 is the reported version, Magnitude 2114 exceeds 2047 (max for sign_bit_index=11), raised because Homing_Offset uses sign bit index 11.
How a build error becomes bad training data
A LeRobot dataset stores joint states and actions as numeric columns. A mechanically wrong arm produces no error there, just plausible numbers describing something other than what happened, and imitation learning fits them faithfully.
| Build error | What the dataset shows | Where to start |
|---|---|---|
| Two joints given the same id | One column missing or duplicated across joints | servo not responding |
| Encoder seam inside a joint's travel | A jump of nearly 360 degrees when the pose crosses the seam, plus a constant offset | joint stops early |
| Leader and follower on different mid-poses | action and observation.state differ by a constant offset | policy only works in one setup |
| Cable tug on the bus | Repeated frames and gaps in the timestamp column | policy freezes mid-motion |
| Gripper hitting its torque limit | Gripper column saturates short of closed | gripper does not close |
| Camera re-enumerated between sessions | One camera key showing two viewpoints | camera not detected |
At connect time the follower writes Max_Torque_Limit = 500, Protection_Current = 250 and Overload_Torque = 25 to the gripper only, with a source comment saying this is 50 percent of max torque to avoid burnout. A gripper that will not crush an object works as designed. One that will not close at all is a calibration range or horn angle problem.
- Catches the two silent failures, wrong calibration range and bus dropouts, while the fix is still a screwdriver.
- Costs 30 minutes against the 3 to 6 hours and 4 to 12 USD of a run on the A100 tier.
- Leaves a known-good calibration file to diff against after every future repair.
- Uses only upstream lerobot commands, so nothing goes stale when you change tooling.
- It says nothing about whether the task is well posed or the demonstrations consistent.
- A clean bench test can hide a cable that only goes taut in a sweep you skipped.
- No software check exists for a horn fitted 40 degrees off. You have to be honest about mid-travel.
- Recalibrating writes new numbers into the motors, so datasets before and after use different conventions.
A 30 minute acceptance pass
Run this on a freshly built arm, and again after any repair, before going near recording a dataset.
- 1Recalibrate both arms from scratch
Type
cat the reuse prompt. Hold every joint at mid-travel for the first Enter, then sweep all but wrist_roll to both limits.bashlerobot-calibrate --robot.type=so100_follower \ --robot.port=/dev/ttyACM0 --robot.id=follower_a lerobot-calibrate --teleop.type=so100_leader \ --teleop.port=/dev/ttyACM1 --teleop.id=leader_a - 2Read the calibration file, do not assume it succeeded
A span near the full 4096 counts, or suspiciously narrow, is the signature of the wrap problem. wrist_roll is the only joint legitimately at 0 to 4095.
pythonimport json, pathlib f = pathlib.Path.home() / ".cache/huggingface/lerobot/calibration" \ / "robots/so_follower/follower_a.json" cal = json.loads(f.read_text()) for joint, c in cal.items(): span = c["range_max"] - c["range_min"] flag = "" if joint != "wrist_roll" and span > 3600: flag = " <-- suspiciously wide, check for a wrap" if span < 400: flag = " <-- suspiciously narrow, joint may not have been swept" print(f"{joint:<14} ho={c['homing_offset']:>6} " f"[{c['range_min']:>4}, {c['range_max']:>4}] span={span:>4}{flag}") - 3Teleoperate for five minutes with the data view on
Drive every joint to both limits, gripper included. Watch for status packet errors, and for a channel that jumps or flattens while the joint moves smoothly.
bashlerobot-teleoperate \ --robot.type=so100_follower --robot.port=/dev/ttyACM0 --robot.id=follower_a \ --teleop.type=so100_leader --teleop.port=/dev/ttyACM1 --teleop.id=leader_a \ --display_data=true - 4Add the cameras and repeat
Cameras change control loop timing. A bus that was fine without them can start dropping reads once frames share the loop.
bashlerobot-teleoperate \ --robot.type=so100_follower --robot.port=/dev/ttyACM0 --robot.id=follower_a \ --robot.cameras="{front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \ --teleop.type=so100_leader --teleop.port=/dev/ttyACM1 --teleop.id=leader_a \ --display_data=true - 5Record two throwaway episodes and read the numbers
Two, not fifty. Open the parquet and check every joint column. A jump of nearly a full turn, a gripper column that saturates, or a flat channel while the arm moved: that is the calibration fault.
Six calibration spans that differ from each other and sit well inside 0 to 4096, five minutes of teleoperation with cameras and no status packet errors, and two throwaway episodes in which all six joint columns move smoothly and none jumps a full turn. With that, the hardware is not why your policy is bad.

Do it yourself, or do it on AY-Robots
Everything above runs locally on upstream lerobot. You own the arm, the calibration files and the serial port, which is the right trade while debugging hardware: every layer between you and the bus can hide the fault.
git clone https://github.com/huggingface/lerobot
cd lerobot
pip install -e ".[feetech]"
lerobot-find-port
lerobot-setup-motors --robot.type=so100_follower --robot.port=/dev/ttyACM0
lerobot-calibrate --robot.type=so100_follower --robot.port=/dev/ttyACM0 --robot.id=follower_a
lerobot-teleoperate --robot.type=so100_follower --robot.port=/dev/ttyACM0 --robot.id=follower_a \
--teleop.type=so100_leader --teleop.port=/dev/ttyACM1 --teleop.id=leader_a- You need the arm, a Feetech-capable serial adapter, and a supply matching the servo variant.
- For servo-level debugging outside lerobot, the vendor tool is Feetech's Windows software; FT_SCServo_Debug_Qt is the Qt rebuild the SO-ARM100 README points Ubuntu users to.
- Training and inference are then yours: 24 GB for ACT or SmolVLA, 80 GB for GR00T or Pi0.5.
The platform cannot assemble your arm or see your horn angle. It removes what comes after: the desktop client records LeRobot-format datasets from a teleop session, training rents a GPU on a spot market by required VRAM and writes checkpoints to object storage, and inference auto-provisions a pod that serves the policy back to the local client.
- No arm yet? /live has a physical SO-100 streaming with no signup, queue-based.
- The dataset directory lists public datasets, so you can train on known-good data and rule your recording out.
- Runs cost 1 to 3 USD on the RTX 4090 tier for ACT or SmolVLA, 4 to 12 USD on the A100 or H100 tier. Pods carry an idle watchdog and destroy themselves.
- The CLI and the MCP server expose the same operations to a terminal and to AI agents.
| Step | Yourself | On AY-Robots |
|---|---|---|
| Set ids and calibrate | lerobot-setup-motors, lerobot-calibrate | Same commands. Yours either way. |
| Record episodes | lerobot-record locally | Desktop client records from the teleop session |
| Rent a GPU and train | Provision it yourself | Form picks model, dataset, hyperparameters |
| Serve the policy | Run the server next to the arm | Pod auto-provisioned, local client talks to it |
Symptom first, cause second
Failure-mode pages for what actually goes wrong on an SO-100: arm not detected, servo not responding, joint stops early, gripper does not close. Each starts from what you observed.
Open the failure indexWhat none of this fixes
A perfect SO-100 does not give you a working vision-language-action model. It removes one class of explanation, which is worth a lot while debugging, and nothing else.
- It does not fix too little data. ACT, GR00T N1.7, GR00T N1.5 and Pi0.5 each want at least 50 episodes here; only SmolVLA starts at 30.
- It does not fix camera placement, lighting, or a task ambiguous from the frames. See collecting good VLA training data.
- It does not fix inference latency: 20 ms per action step for ACT, 485 ms for Pi0.5, and internet round trips on top turn a working policy hesitant.
- It does not detect a horn at the wrong angle, and no hosted pipeline can, from the dataset alone.
- It does not make a v3.0 dataset loadable by GR00T. That is a conversion down to v2.1, with its own page.
With no hardware yet, the no-hardware starting points let you drive a real arm, compare models on the arena leaderboard across 85 VLA models and 332 benchmark results, or rent a GPU. If you would rather be paid to drive arms than build them, there is operator work.

Where to go next
After the acceptance pass: SO-100 getting started, data collection, your first training run, running the policy. The complete SO-100 guide covers the same ground end to end, and the VLA overview the models on the other side of the dataset.
Do I really have to set the servo ids before assembling the SO-100?▾
On the SO-100, yes. The LeRobot guide says the motor connectors are not easily accessible once the arm is assembled, which is why configuration comes first. The SO-101 improved the wiring, but ids still have to be unique before the motors share a bus, because every STS3215 ships as id 1.
How do I tell whether my calibration is wrong or my policy is just bad?▾
Read the calibration JSON under ~/.cache/huggingface/lerobot/calibration/robots/. Every joint except wrist_roll should have a span clearly under 4096 counts and over a few hundred. Then record two throwaway episodes. Under the default use_degrees=True a body joint is not clamped, so the tell is a jump of nearly a full turn where the pose crossed the seam, or a constant offset; a flat line is the gripper, which is clamped.
What happens if I put a 12 V supply on 7.4 V STS3215 servos?▾
You destroy them. The SO-ARM100 bill of materials pairs the 7.4 V variant with a 5 V supply and says the 12 V variant needs a 12 V 5 A or larger one. Over-voltage counts too: one reported case had a nominally 12 V adapter measuring around 16 V, which tripped protection mode so none answered a ping.
Can I fix a badly fitted horn in software instead of taking the joint apart?▾
Partly, and it is not worth it. You can pick a calibration pose that pushes the seam out of the joint's travel, and issue 3587 proposes automating that by sweeping first. But today the offset is chosen before the sweep, so you are hand-tuning around a known bug. Refitting the horn at mid-travel is faster.
Does AY-Robots check my hardware for me?▾
No. It records datasets, rents GPUs, trains policies and serves them back, but it cannot see your arm's mechanics. What it gives you is a comparison: train on a public dataset from the directory, and if that policy behaves while yours does not, the difference is in your recording rather than the model.
Sources
- TheRobotStudio SO-ARM100: bill of materials, servo variants and gear ratios
- LeRobot: SO-100 motor configuration, assembly and calibration
- LeRobot: SO-101 assembly, the leader-arm gear ratio table and calibration
- LeRobot: imitation learning on real-world robots, lerobot-teleoperate flags
- lerobot MotorsBus: set_half_turn_homings, record_ranges_of_motion, _normalize
- lerobot SOFollower: motor id map, calibrate(), gripper torque limits
- lerobot SOFollowerConfig: use_degrees, PID gains, num_read_retries
- lerobot FeetechMotorsBus: default baud rate, baud-rate search, _get_half_turn_homings
- lerobot issue 3586: calibration overflow protection, wrap-blind range recording
- lerobot issue 3587: homing offset should be chosen after the sweep
- lerobot issue 3585: goal positions outside min/max, firmware clamp and torque auto-enable
- lerobot issue 3394: no STS3215 answers a ping; a same-symptom commenter traced it to a 16 V adapter
- lerobot issue 1252: sync read 'There is no status packet', first on a Koch arm, then on SO-101 followers
- SO-ARM100 issue 31: leader gears removed only after assembly
- Waveshare ST3215 servo (the 12 V part): 360/4096 resolution, mechanical limited angle none, 1 Mbps, 6 to 12.6 V
Sources
- TheRobotStudio SO-ARM100: bill of materials, servo variants and gear ratios
- LeRobot: SO-100 motor configuration, assembly and calibration
- LeRobot: SO-101 assembly, the leader-arm gear ratio table and calibration
- LeRobot: imitation learning on real-world robots, lerobot-teleoperate flags
- lerobot MotorsBus: set_half_turn_homings, record_ranges_of_motion, _normalize
- lerobot SOFollower: motor id map, calibrate(), gripper torque limits
- lerobot SOFollowerConfig: use_degrees, PID gains, num_read_retries
- lerobot FeetechMotorsBus: default baud rate, baud-rate search, _get_half_turn_homings
- lerobot issue 3586: calibration overflow protection, wrap-blind range recording
- lerobot issue 3587: homing offset should be chosen after the sweep
- lerobot issue 3585: goal positions outside min/max, firmware clamp and torque auto-enable
- lerobot issue 3394: no STS3215 answers a ping; a same-symptom commenter traced it to a 16 V adapter
- lerobot issue 1252: sync read 'There is no status packet', first on a Koch arm, then on SO-101 followers
- SO-ARM100 issue 31: leader gears removed only after assembly
- Waveshare ST3215 servo (the 12 V part): 360/4096 resolution, mechanical limited angle none, 1 Mbps, 6 to 12.6 V
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started