One joint travels to a certain angle and refuses to go further. Nothing is touching it, it moves freely by hand with the power off, and the other joints reach their full range without complaint.
- SO-100
- SO-101
- LeKiwi
A joint that stops at the same angle every time with nothing in its way is a calibration artifact, not a limit switch, because these arms have none. The servo reports position on a linear scale whose two ends are the two mechanical extremes of its output shaft, so a stored zero near one end pushes part of the joint’s working range off the scale, where every further command is clipped to the last value the scale can express. That clipped value sits on the mechanical stop, so the joint does not only lose reach, it presses into the stop under torque, and the fix is to remount the horn so home sits near the middle of the scale, then recalibrate.
A soft stop and a hard stop feel different
Before you touch anything, establish which of the two you have. A software clamp is silent, perfectly repeatable and lands on the same number every time. A mechanical obstruction is noisy, slightly different on each attempt, and the servo audibly works against it. Confusing them sends you to opposite ends of the workshop.
| Observation | Reading |
|---|---|
| Stops at the same commanded value every run, silently | Software clamp, so calibration |
| Stops a little differently each time, with a strained sound | Mechanical, something is in the way or binding |
| Free by hand with the power off, blocked under power | Software clamp |
| Resists by hand with the power off too | Mechanical |
| Blocked in one direction, full travel in the other | Zero point sits near one end of the scale |
| Blocked symmetrically in both directions | The fixed joint limits, which calibration does not change |
Where the position scale ends
A bus servo reports its position as a count on a linear scale. One end of that scale is one mechanical extreme of the output shaft, the other end is the other extreme, and the electrical middle sits between them. The scale does not continue past either end and it does not start over, so there is nothing on the far side of it except the servo’s own hard stop.
Calibration stores where your neutral pose sits on that scale. Every angle you command afterward is that stored zero plus an offset, converted back into a count. If the zero landed near the middle, offsets in both directions stay on the scale and everything behaves. If the zero landed near one end, the offsets you need in that direction fall past the end of the scale and the conversion saturates: the joint receives the same count no matter how much further you ask for. That is the stop you are feeling. It is arithmetic running out of scale, not a protective feature, and the count it saturates at is the hard stop itself.
This is also why the problem is arm-specific rather than model-specific. The horn can be seated on the output shaft at many different rotations, and where it goes decides where mechanical neutral falls on the electrical scale. Two arms built from the same kit by two people can differ by enough that one owner never encounters this and the other loses an afternoon to it.
Separate from all of this, every joint also carries a fixed software limit that is applied to each commanded position before it ever reaches the servo. That one is symmetric around the zero, it is the same for everybody with the same arm, and calibration neither widens nor narrows it. If a joint stops at the same distance in both directions, that is what you have met, and the remedy is to rebuild the task around the band you can reach rather than to recalibrate.
Fix it
- 1Rule out mechanics first
With the servo supply off, move the joint by hand through the whole range you want. If it goes there freely, nothing physical is stopping it and you are chasing a stored number, not a screw. One exception is worth knowing: a joint that also refuses to go further by hand in one direction only, while the opposite direction is free, is still this problem rather than an obstruction, because the saturated count sits on the servo’s own end of travel.
- 2Write down where it stops, in both directions
Note the commanded value at the stop going each way. A block in one direction only is the signature of a zero point near one end of the scale, and recalibration is the fix. A block at the same distance either way is the fixed software limit for that joint, which is identical before and after calibration, so recalibrating will not widen it. Recalibration can move where that band sits physically, by moving the zero, but not how wide it is.
- 3Remount the horn so neutral sits mid-scale
Bring the joint to the pose you consider home mechanically, loosen the horn, and reseat it so the servo sits near the middle of its travel in that pose. This is the step that actually solves the problem. Everything after it is bookkeeping.
- 4Recalibrate the arm
The command waits for you: guide every joint to the neutral pose, then press Enter in the terminal, and it captures the reference and saves it from there. The same walkthrough exists as a wizard on the Hardware page for your robot in the dashboard. Those two are the only ways in, because the desktop client has no calibration screen. Calibration is a whole-arm operation, so the other five joints get a new zero at the same time whether they needed one or not.
bashay-robots calibrate - 5Drive the joint to both limits and watch
Walk the joint slowly to each end of the range you need. Stop immediately if anything sounds strained. What you are confirming is that the travel you actually use now falls inside the joint limits on both sides of the new zero, instead of running out on one side.
- 6Check that no joint landed near an edge
Do this after every recalibration, not only after this one. A home value sitting close to either end of the scale is the same bomb waiting for a different joint, and it costs one look to notice.
- 7Home the arm and drive it
Return to the home pose, then walk the repaired joint through its full intended travel with the joint sliders on the Control page in the dashboard. The sliders send a position straight to the arm and need no running teleoperation, which is what makes them the right instrument for checking one joint. If it goes where you send it, you are finished.
bashay-robots home
# Confirm a port is there before recalibrating
ay-robots devices
# SERIAL PORTS
# robot_1 unknown
# Capture the zero point: guide each joint to neutral, then press Enter
ay-robots calibrate
# Return to home, then drive the joint from the Control page in the dashboard
ay-robots homeLook at where each joint’s home value landed on its scale. Anything close to either end is a joint whose usable range is about to be clipped, even if it behaves today. The save step warns you about this on its own, and that warning is worth reading rather than skipping past. Fixing it now is a horn remount; fixing it later is a horn remount plus the datasets you recorded in between.
While the command sits past the end of the scale, the servo is still being told to hold a position at its mechanical stop, so it keeps applying torque against something that cannot move. That is a stalled servo, and a stalled servo gets hot. Do not leave a joint parked there while you investigate, and remount the horn before you record or run a policy again. A running policy is the worst case, because it keeps requesting that direction for as long as it runs.
Recorded joint values are relative to the zero point that was in force when you recorded them. After you move the zero, older episodes describe different physical poses than they did before, so do not mix recordings from either side of a recalibration into one dataset. If you already have data worth keeping, finish that dataset first. Recording practice is covered on /so-100/data-collection.
Why this often appears right after a repair
Replacing a servo, removing a horn to get at a screw, or rebuilding a joint after a collision all change where neutral sits on the electrical scale. The arm worked yesterday, you fixed something unrelated, and now one joint stops short. The two events are connected through the horn, not through whatever you were originally repairing, which is why the timing tends to send people looking in the wrong place. Full first-time assembly and calibration is on /learn/so-100-getting-started if you want the reference procedure.
What it is not
- A limit switch. These arms have none, so there is no switch to bypass or replace.
- A firmware restriction you can unlock. The clamp is arithmetic: the commanded angle converts to a position outside the scale, and the conversion saturates at the last value on it.
- A policy problem. A trained policy commands positions like anything else and is clamped the same way.
- Something a longer training run improves. If the joint cannot reach, more steps teach the policy to stop there more confidently.
- A sign the servo is failing. A servo that answers and tracks inside its range is healthy, but a joint left holding against its mechanical stop will heat up whether the servo is healthy or not.
The cost of leaving it alone
A clipped joint quietly shapes your data. During teleoperation your hand keeps moving while the joint stops, so the recorded action column flattens against the limit for the rest of that segment. The policy learns that the correct action at that point is to stop, and it reproduces that faithfully at inference, which then looks like a model that will not reach. You can spend a long time adjusting hyperparameters for a problem that lives in a horn. If you have already trained on such a dataset, the fix is the horn, then a fresh recording, then a new run: the model side of that loop is described on /train/act-on-so-100.
Frequently asked questions
Can I just work inside the smaller range?▾
You can, and your usable workspace shrinks accordingly. The bigger cost is invisible: every demonstration you record inherits the limit, so the policy inherits it too and will not reach past it even on an arm that could.
Which joints does this usually affect?▾
Any of them. The ones people notice are the joints whose useful travel is widest, because a zero point off center eats into a range they use every session. Which joint it hits is decided by how the horn was seated, not by the joint itself.
Do I have to recalibrate all six joints to fix one?▾
Yes. Calibration captures the whole arm in a single pass, so the other five receive a new zero at the same time. That is also why you should treat a recalibration as a boundary in your dataset history.
Is there a command that just shifts the zero for one joint?▾
No. The CLI exposes calibrate for the whole arm and home to return to the neutral pose. Fixing the underlying geometry with the horn and recalibrating is both the supported path and the one that survives the next rebuild.
The joint reaches further when I push it by hand than when I drive it. Why?▾
Because by hand you are moving the mechanism and by command you are moving through the calibration. That difference is the clearest confirmation you will get that the stop is stored rather than physical.
Was this page helpful?
A symptom in one part of the chain is regularly produced by the part before it. These pages cover the neighboring areas.