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

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.2.3
File Size Uploaded
physicalai_bimanual_so101_plugin-0.2.3.tar.gz 6.0 MB Details

Built distribution (wheel)

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

Total release size: 11.9 MB

Release files / physicalai_bimanual_so101_plugin-0.2.3.tar.gz

Download URL physicalai_bimanual_so101_plugin-0.2.3.tar.gz
Size 6.0 MB
Tags Source
SHA-256 checksum
How to use checksums
f3adc92b52e6b48b73d19bf27a45b961d50ad73ed4297cb44f2849e8f7a90a32
BLAKE2b-256 checksum
How to use checksums
6f44ed9babccc73c1b47c405446d27346fc1207fe13b9eb98fc6622623c0a4c1
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 Aug 29, 2026.

Transparency log

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

Download URL physicalai_bimanual_so101_plugin-0.2.3-py3-none-any.whl
Size 6.0 MB
Tags Python 3
SHA-256 checksum
How to use checksums
69ada2a1e7fb7901cae6bd9afcd0e94861efdc4b8aeaf4026bf261824a5a7a23
BLAKE2b-256 checksum
How to use checksums
2d12216837350c8437609e66fd3757a693a8980b48e9e9deab02136d9c9f6f6a
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 Aug 29, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

This release

0.2.3 This release

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