First arm: SO-101, real¶
python3 -m pip install "strands-robots[sim,feetech]"
ls /dev/tty.usbmodem* /dev/ttyACM* 2>/dev/null # the arm's serial port
Read before you move:
from strands_robots import Robot
arm = Robot("so101", mode="real", port="/dev/tty.usbmodem5AB01818061")
obs = arm.observe()
print(obs.frame, sorted(obs.joints)) # ticks by default; six joints
print(arm.is_connected()) # asked of the wire
Move one joint:
receipt = arm.act({"shoulder_pan": obs.joints["shoulder_pan"] + 50}) # 50 ticks, clamped per step
print(receipt.clamped)
arm.close() # torque off, port released
From the shell, real motion needs --confirm:
strands-robots observe so101 --mode real --port /dev/tty.usbmodem5AB01818061
strands-robots move so101 '{"shoulder_pan": 2100}' --mode real \
--port /dev/tty.usbmodem5AB01818061 --confirm
| rail | what it does |
|---|---|
| per-step clamp | every write is bounded by the row's max_step in the arm's native frame |
preflight(policy) |
refuses a policy whose action_frame or DOF the arm cannot consume, before motion |
close() |
torque off and port released on every exit path |
| twin | Robot("so101", mode="twin") runs the same Feetech driver against the MuJoCo model: same verbs, same refusals, no bench |
Calibration lives with the driver: an arm without a calibration file gets a synthetic one
that is safe but not metric, and arm.calibration says synthetic rather than preexisting.
No bench today? Robot("so101", mode="twin") runs every line above against the MuJoCo model.