PhysicalAI Bimanual SO-101 Plugin
Third-party bimanual SO-101 robot arm plugin for PhysicalAI, the Python library and runtime for robot control, transport, and CLI workflows. It registers with Physical AI Studio, the application that discovers catalog plugins and provides robot setup, teleoperation, and workflow experiences. Part of the physicalai-plugins monorepo.
Features
- Composes two SO-101 arms (left + right) behind a single
Robotprotocol - Follower and leader (read-only) roles for teleoperation
- Bundled dual-arm URDF for kinematics and visualization
Hardware
| Class | Robot | Motors | Protocol |
|---|---|---|---|
BimanualSO101 |
Dual 6-DOF STS3215 arms | Feetech STS3215 | POSITION |
Screenshots
Placeholder images — replace them with real screenshots.
Installation
uv add physicalai-bimanual-so101-plugin
feetech-servo-sdk is included as a core dependency.
Calibration examples
Bundled SO-101 calibration files (LeRobot format) live under
examples/calibration/:
| File | Arm | Use |
|---|---|---|
examples/calibration/follower.json |
Follower | Left + right follower arms |
examples/calibration/leader.json |
Leader | Left + right leader arms |
Pass them as the calibration path when constructing a SO101:
from physicalai.robot.so101 import SO101, SO101Calibration
cal = SO101Calibration.from_path("examples/calibration/follower.json")
arm = SO101(port="/dev/ttyACM0", calibration=cal, role="follower")
The bundled runtime config (examples/runtime/teleop.yaml) references these
files already.
Quick start
Calibrated follower
import numpy as np
from physicalai.robot import connect
from physicalai_bimanual_so101_plugin import BimanualSO101
# Build left and right arms manually
from physicalai.robot.so101 import SO101, SO101Calibration
left_cal = SO101Calibration.from_path("left_calibration.json")
right_cal = SO101Calibration.from_path("right_calibration.json")
left = SO101(port="/dev/ttyACM0", calibration=left_cal, role="follower")
right = SO101(port="/dev/ttyACM1", calibration=right_cal, role="follower")
robot = BimanualSO101(left=left, right=right)
with connect(robot) as arm:
obs = arm.get_observation() # shape (12,)
action = obs.joint_positions.copy()
arm.send_action(action)
Uncalibrated bringup mode
from physicalai.robot import connect
from physicalai_bimanual_so101_plugin import BimanualSO101
from physicalai.robot.so101 import SO101
left = SO101.uncalibrated(port="/dev/ttyACM0", role="follower")
right = SO101.uncalibrated(port="/dev/ttyACM1", role="follower")
robot = BimanualSO101(left=left, right=right)
Leader (read-only teleoperation)
from physicalai.robot import connect
from physicalai.robot.so101 import SO101
from physicalai_bimanual_so101_plugin import BimanualSO101
left = SO101.uncalibrated(port="/dev/ttyACM0", role="leader")
right = SO101.uncalibrated(port="/dev/ttyACM1", role="leader")
robot = BimanualSO101(left=left, right=right)
with connect(robot) as arm:
while True:
obs = arm.get_observation()
print(obs.joint_positions)
Teleoperation
Because BimanualSO101 satisfies the Robot protocol, it can be driven by
the built-in physicalai.runtime.TeleopSource with a leader BimanualSO101
(both arms in role="leader"), e.g. via physicalai run --config or directly
in Python:
from physicalai.robot.so101 import SO101
from physicalai.runtime import RobotRuntime, TeleopSource
from physicalai_bimanual_so101_plugin import BimanualSO101
follower = BimanualSO101(
left=SO101.uncalibrated(port="/dev/ttyACM0", role="follower"),
right=SO101.uncalibrated(port="/dev/ttyACM1", role="follower"),
)
leader = BimanualSO101(
left=SO101.uncalibrated(port="/dev/ttyUSB0", role="leader"),
right=SO101.uncalibrated(port="/dev/ttyUSB1", role="leader"),
)
runtime = RobotRuntime(fps=30, robot=follower, action_source=TeleopSource(leader=leader))
with runtime:
runtime.run()
The same teleoperation can be run from a config with the PhysicalAI CLI:
uv run physicalai run --config packages/physicalai-bimanual-so101-plugin/examples/runtime/teleop.yaml
Press Ctrl+C to stop.
URDF Model
from physicalai_bimanual_so101_plugin import get_urdf_path
urdf_path = get_urdf_path()
| URDF | Model | Use |
|---|---|---|
so101_dual/so101_dual.urdf |
Bimanual SO-101 | Kinematics & visualization |
Two arms mounted 0.4 m apart: left at +0.2 m (Y), right at -0.2 m (Y).
Joint Order
Left arm (indices 0-5): left_shoulder_pan, left_shoulder_lift, left_elbow_flex, left_wrist_flex, left_wrist_roll, left_gripper
Right arm (indices 6-11): right_shoulder_pan, right_shoulder_lift, right_elbow_flex, right_wrist_flex, right_wrist_roll, right_gripper
Development
uv sync
uv run pytest packages/physicalai-bimanual-so101-plugin/tests/
Acknowledgments
URDF model generated from Onshape via onshape-to-robot. SO-101 arm by LeRobot.
Metadata
Release files for physicalai-bimanual-so101-plugin 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| physicalai_bimanual_so101_plugin-0.5.0.tar.gz | 6.0 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| physicalai_bimanual_so101_plugin-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 11.9 MB
Release files / physicalai_bimanual_so101_plugin-0.5.0.tar.gz
| Download URL | physicalai_bimanual_so101_plugin-0.5.0.tar.gz |
|---|---|
| Size | 6.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7bab0bd096e4757ec010fdc838a085d4567d3e000e4122e6720282f7850e3e22
|
|
BLAKE2b-256 checksum How to use checksums |
dfc6d442769f968b161a2029cfa3e1998679155487a843cc7635b9defa53c477
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logRelease files / physicalai_bimanual_so101_plugin-0.5.0-py3-none-any.whl
| Download URL | physicalai_bimanual_so101_plugin-0.5.0-py3-none-any.whl |
|---|---|
| Size | 6.0 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f5f004fee41fc7bb3fed1190c4cc5609c577af9842b85c2975b07ff7b843194a
|
|
BLAKE2b-256 checksum How to use checksums |
1def68693bfc7c85ead56c089d0d5d7852e33f9b576ea66ca9c3be074fb3997e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency log