Skip to main content

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.

PyPI version Python versions

Features

  • Composes two SO-101 arms (left + right) behind a single Robot protocol
  • 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.

Bimanual SO-101 in the PhysicalAI Studio robot catalog

Connecting to a Bimanual SO-101 in PhysicalAI Studio

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.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for physicalai-bimanual-so101-plugin 0.3.0
File Size Uploaded
physicalai_bimanual_so101_plugin-0.3.0.tar.gz 6.0 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for physicalai-bimanual-so101-plugin 0.3.0
File Interpreter ABI Platform
physicalai_bimanual_so101_plugin-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 11.9 MB

Release files / physicalai_bimanual_so101_plugin-0.3.0.tar.gz

Download URL physicalai_bimanual_so101_plugin-0.3.0.tar.gz
Size 6.0 MB
Tags Source
SHA-256 checksum
How to use checksums
b7b2e211561ec7b5e6ff12e3031f130e38804833dd793025d51e424f208101ca
BLAKE2b-256 checksum
How to use checksums
2ab165a4d5a12e593e87fabc2e61bfd93a897c096a036f34c0e24ef64a824fef
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 4, 2026.

Transparency log

Release files / physicalai_bimanual_so101_plugin-0.3.0-py3-none-any.whl

Download URL physicalai_bimanual_so101_plugin-0.3.0-py3-none-any.whl
Size 6.0 MB
Tags Python 3
SHA-256 checksum
How to use checksums
91e64d8fcafa26e58e7203c86780a5e75988a91a472b6046b63f69cf72ebcd89
BLAKE2b-256 checksum
How to use checksums
cbbdf7ffbe35e3a212e6855a8587a2c9d7d878b4d79ba82ec28ea08736629ade
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 4, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.0

2 release files

0.4.0

2 release files

This release

0.3.0 This release

2 release files

0.2.3

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page