Skip to main content

Programmatic control of an SO-101 follower arm via lerobot (joint waypoints + IK).

Project description

so101-control

A small Python package for programmatic control of an SO-101 robot arm via lerobot.

Move the arm to specific joint-angle configurations, or drive the end-effector to a Cartesian target using inverse kinematics — no teleop or trained policy required. Install it as a library in your own project, or use the bundled so101-control CLI.

What's included

Path Purpose
src/so101_control/ The installable so101_control package (control wrappers + CLI).
src/so101_control/control.py Reusable primitives: move_to_joints, go_to_ee, home, rest.
src/so101_control/cli.py The so101-control command-line entry point.
examples/ Example scripts showing library usage.

Installation

Requires Python ≥3.12 and uv (or any PEP 621-aware installer such as pip).

From source (development)

git clone https://github.com/vi-vi-3482/so101-control
cd so101-control
uv sync

This installs the so101_control package (importable as import so101_control) together with lerobot[kinematics,feetech] (which pulls in placo for IK) and pins cmeel-urdfdom==4.0.1 to work around a shared-library mismatch in the placo wheels.

Platform compatibility

CI runs on macOS and Linux. Windows is not currently supported for direct installation: the placo/pin/coal-library chain depends on cmeel-qhull, which has no prebuilt Windows wheel and fails to build from source on recent CMake (its cmake_minimum_required predates 3.5, which modern CMake no longer accepts). This is an upstream packaging issue, not specific to so101-control.

On Windows, run so101-control inside WSL using a Linux install of lerobot until a newer upstream release ships working Windows wheels.

As a dependency in your own project

Add it to your pyproject.toml dependencies (or let uv do it for you):

# from a local checkout
uv add /path/to/so101-control
# …or from git
uv add "git+https://github.com/vi-vi-3482/so101-control.git"

Then import it from your own code:

from so101_control import (
    HOME_JOINTS,
    go_to_ee,
    home,
    move_to_joints,
    print_status,
    rest,
)

Fetch the SO-101 URDF + meshes (only needed for end-effector / IK mode)

The URDF references STL meshes in an assets/ folder. Download both:

curl -L -o so101_new_calib.urdf \
  https://raw.githubusercontent.com/TheRobotStudio/SO-ARM100/main/Simulation/SO101/so101_new_calib.urdf
mkdir -p assets && cd assets
for f in base_motor_holder_so101_v1 base_so101_v2 motor_holder_so101_base_v1 \
  motor_holder_so101_wrist_v1 moving_jaw_so101_v1 rotation_pitch_so101_v1 \
  sts3215_03a_no_horn_v1 sts3215_03a_v1 under_arm_so101_v1 upper_arm_so101_v1 \
  waveshare_mounting_plate_so101_v2 wrist_roll_follower_so101_v1 \
  wrist_roll_pitch_so101_v2; do
  curl -sL -o "$f.stl" \
    "https://raw.githubusercontent.com/TheRobotStudio/SO-ARM100/main/Simulation/SO101/assets/$f.stl"
done
cd ..

Calibrate the arm (one-time)

If you haven't already calibrated your follower, follow the commands in LeRobot. Calibration is stored under ~/.cache/huggingface/lerobot/calibration/robots/<robot-id>/ and is located automatically by the --robot-id you pass to the CLI.

Quick start

Once installed, the so101-control CLI is available.

Dry-run (no hardware needed — prints the planned trajectory):

so101-control --mode joint \
    --target-joints shoulder_pan=0,shoulder_lift=-20,elbow_flex=60,wrist_flex=30,wrist_roll=0,gripper=50 \
    --dry-run

Live joint move:

so101-control \
    --port /dev/tty.usbmodem585A0076841 \
    --robot-id my_awesome_follower_arm \
    --mode joint \
    --target-joints shoulder_pan=0,shoulder_lift=-20,elbow_flex=60,wrist_flex=30,wrist_roll=0,gripper=50

End-effector move (position-only IK by default):

so101-control \
    --port /dev/tty.usbmodem585A0076841 \
    --robot-id my_awesome_follower_arm \
    --urdf-path /absolute/path/to/so101_new_calib.urdf \
    --mode ee --target-xyz 0.2,0.0,0.15

You can also invoke the module directly without installing the console script:

uv run python -m so101_control.cli --mode home --dry-run

See the Library usage section below for the full API, library usage, safety knobs, and the joint schema.

Library usage

import time

from lerobot.model.kinematics import RobotKinematics
from lerobot.robots.so_follower import SO101Follower, SO101FollowerConfig

from so101_control import (
    HOME_JOINTS,
    go_to_ee,
    home,
    move_to_joints,
    print_status,
    rest,
)

robot = SO101Follower(
    SO101FollowerConfig(
        port="/dev/tty.usbmodem...", id="my_awesome_follower_arm"
    )
)
robot.connect(calibrate=False)

print("Moving to home joint angle")
move_to_joints(robot, HOME_JOINTS, duration_s=2.0)

kin = RobotKinematics(
    urdf_path="/absolute/path/to/so101_new_calib.urdf",  # Must be an absolute path
    target_frame_name="gripper_frame_link",
    joint_names=[
        "shoulder_pan",
        "shoulder_lift",
        "elbow_flex",
        "wrist_flex",
        "wrist_roll",
    ],
)

print_status(kin, robot)

pos = [0.3, 0.0, 0.5]
print(f"Moving to EE position {pos}")
go_to_ee(kin, robot, pos, duration_s=3.0)
time.sleep(0.5)


rest(robot)
time.sleep(0.1)
robot.disconnect()

A ready-to-edit version of the above lives at examples/example_move_ee.py.

Primitives

The package exposes four reusable primitives (all runnable without hardware via dry_run=True):

Primitive Description
move_to_joints Smoothly interpolate to a *.pos -> value joint dict.
go_to_ee Solve IK and route the result through move_to_joints.
home Return the arm to HOME_JOINTS (extended, neutral).
rest Return the arm to REST_JOINTS — the curled-up pose.

Joint schema

SO-101 has 5 arm DOF + 1 gripper. The package exposes the following constants:

Constant Value
ARM_JOINTS shoulder_pan, shoulder_lift, elbow_flex, wrist_flex, wrist_roll
ALL_JOINTS ARM_JOINTS + [gripper]
ACTION_KEYS ARM_JOINTS/ALL_JOINTS suffixed with .pos (the lerobot action keys).
EE_FRAME gripper_frame_link — the IK target frame in the SO-ARM100 URDF.
HOME_JOINTS Neutral extended pose (all arm joints at 0°, gripper at 50).
REST_JOINTS Curled-up pose; safe for storage / power-off.

Safety knobs

  • --max-relative-target (deg, CLI) / max_relative_target (lerobot config): clips per-step joint jumps inside send_action. Strongly recommended on hardware.
  • duration_s / fps: control the interpolation rate. Slower = gentler.
  • orientation_weight (default 0.0): IK orientation weight. 0.0 means position-only; pass --target-rpy to add a soft orientation constraint.
  • position_weight (default 1.0): IK position weight.

Additional Notes

The URDF file path must be an absolute path from the system root, otherwise it fails to find the assets/ folder containing the mesh STLs.

Robot Coordinate system

Orientation facing the robot. The EE is pointed forward in the home position.

Axis Orientation
X Forward
Y Right
Z Up

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

so101_control-0.1.1.tar.gz (26.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

so101_control-0.1.1-py3-none-any.whl (26.5 kB view details)

Uploaded Python 3

File details

Details for the file so101_control-0.1.1.tar.gz.

File metadata

  • Download URL: so101_control-0.1.1.tar.gz
  • Upload date:
  • Size: 26.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for so101_control-0.1.1.tar.gz
Algorithm Hash digest
SHA256 dad3748702974549ca6e394327744e86ceda706c37268967b398bed2b53e6f7e
MD5 2f0dddcde20184b0ed6d236a80717305
BLAKE2b-256 b495f989bb70a9960cdfd576bae210a41c9eccab6fe98a47f55a0d1fd65994ce

See more details on using hashes here.

Provenance

The following attestation bundles were made for so101_control-0.1.1.tar.gz:

Publisher: publish.yml on vi-vi-3482/so101-control

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file so101_control-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: so101_control-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 26.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for so101_control-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d39828ded8b13a808c5a30d7f8b4b477037e6dc310b73d62fc5f238af408665d
MD5 a47d92e804f23a05678143ec3090f0a6
BLAKE2b-256 9c66766d8b97a9ceaf1a034ff46827cdf4759e8215a4dafbda3a1973f780ef9b

See more details on using hashes here.

Provenance

The following attestation bundles were made for so101_control-0.1.1-py3-none-any.whl:

Publisher: publish.yml on vi-vi-3482/so101-control

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page