share-rl is a LeRobot extension for structured manipulation workflows.
The repository adds four main things on top of LeRobot:
- new robot and teleoperator integrations, packaged as sibling installable Python projects
- task-frame manipulation primitives and manipulation-primitive nets (MP-Nets) for graph-structured behavior
- recording, training, and offline evaluation helpers around those MP-Nets
- a local-first workspace/runtime layer for editing and reasoning about MP-Nets programmatically
This README is written for someone who wants to use the repo, understand what is installable, and see the intended MP-Net usage pattern.
From the root share-rl package itself you get:
share.envs.manipulation_primitive: a single manipulation primitive environment with task-frame constraints, processor pipelines, and intervention logicshare.envs.manipulation_primitive_net: a graph of primitives plus transitions, start/reset semantics, and MP-Net validationshare.workspace.mpnet: helpers to create, save, load, validate, summarize, and edit MP-Net configsshare.scripts.record: record datasets from MP-Nets with per-primitive datasets and optional per-primitive policiesshare.scripts.train: thin adapter for training policies on recorded datasetsshare.scripts.eval_on_dataset: lightweight offline evaluation/summary adapter for recorded primitive datasets
From the sibling standalone packages in this repo you can also get:
lerobot_robot_ur: UR e-series integrationlerobot_robot_viperx: Trossen ViperX integrationlerobot_teleoperator_delta_keyboard: keyboard delta-velocity teleoperationlerobot_teleoperator_spacemouse: SpaceMouse teleoperationlerobot_teleoperator_widowx: WidowX leader-arm style teleoperation
Those sibling packages are installed through the root extras when you install from this repo checkout.
What is already useful:
- MP-Net config objects, transitions, serialization, and validation
- manipulation-primitive processor stack
- UR, ViperX, keyboard, SpaceMouse, and WidowX integration code
- dataset recording, training adapter, and dataset-summary evaluation adapter
What is still more experimental:
- some policy code under
src/share/policies - the broader workspace/agent tooling under
src/share/workspace - packaging polish for publishing the sibling subprojects independently outside this monorepo checkout
The repository currently targets Python 3.10+ and LeRobot >=0.5.
A typical local setup is:
conda create -n share python=3.12
conda activate share
pip install -e .These extras are designed for use from this repository checkout.
pip install -e .[ur]
pip install -e .[spacemouse]
pip install -e .[aloha]
pip install -e .[all]What each extra does:
.[ur]: installs the rootsharepackage plus the siblinglerobot_robot_urproject fromsrc/share/robots/ur.[spacemouse]: installs the rootsharepackage plus the siblinglerobot_teleoperator_spacemouseproject fromsrc/share/teleoperators/spacemouse.[aloha]: installs the rootsharepackage plus the siblinglerobot_robot_viperxandlerobot_teleoperator_widowxprojects.[all]: installs the three hardware extras above plus test dependencies
If you want one sibling package directly, you can also install it by path:
pip install -e src/share/robots/ur
pip install -e src/share/teleoperators/spacemouseThe root package currently exposes:
share-recordshare-design-constraintsshare-trainshare-eval
The root pyproject.toml also declares share-workspace, but this checkout currently does not contain the share.scripts.robot_workspace wrapper module. The workspace functionality is present as Python modules under share.workspace, but that CLI wrapper is currently in flux.
A manipulation primitive is one locally coherent behavior with:
- one or more task frames
- processor settings
- an optional policy
- optional notes and task description
- an
is_terminalflag
A TaskFrame defines the control contract for one robot inside a primitive:
- target pose or joint target
- command space:
TASKorJOINT - per-axis control mode:
POS,VEL, orWRENCH - per-axis policy mode:
ABSOLUTE,RELATIVE, orNone - gains and min/max bounds
Only axes whose policy_mode is not None are learnable by a policy.
A manipulation-primitive net is a directed graph with:
start_primitivereset_primitive- a dictionary of named primitives
- a list of transitions between primitives
The config validates several invariants for you, including:
- transition sources and targets must exist
- non-terminal primitives must have outgoing edges
- terminal primitives must be reachable from
start_primitive
This is a small, faithful example of the intended usage pattern: define primitives, define transitions, then save the config as JSON.
from pathlib import Path
from share.envs.manipulation_primitive.config_manipulation_primitive import ManipulationPrimitiveConfig
from share.envs.manipulation_primitive.task_frame import ControlMode, PolicyMode, TaskFrame
from share.envs.manipulation_primitive_net.config_manipulation_primitive_net import ManipulationPrimitiveNetConfig
from share.envs.manipulation_primitive_net.transitions import OnSuccess
from share.utils.constants import DEFAULT_ROBOT_NAME
from share.workspace.mpnet import save_mpnet_config, summarize_mpnet
approach = ManipulationPrimitiveConfig(
task_frame={
DEFAULT_ROBOT_NAME: TaskFrame(
target=[0.0, 0.0, 0.15, 0.0, 0.0, 0.0],
control_mode=[ControlMode.POS] * 6,
policy_mode=[PolicyMode.RELATIVE, PolicyMode.RELATIVE, PolicyMode.RELATIVE, None, None, None],
min_pose=[-0.10, -0.10, 0.05, -0.5, -0.5, -0.5],
max_pose=[0.10, 0.10, 0.25, 0.5, 0.5, 0.5],
)
},
notes="Approach the object from above.",
task_description="approach object",
is_terminal=False,
)
close_gripper = ManipulationPrimitiveConfig(
task_frame={
DEFAULT_ROBOT_NAME: TaskFrame(
target=[0.0, 0.0, 0.10, 0.0, 0.0, 0.0],
control_mode=[ControlMode.POS] * 6,
policy_mode=[None] * 6,
)
},
notes="Close and settle.",
task_description="close gripper",
is_terminal=True,
)
mpnet = ManipulationPrimitiveNetConfig(
start_primitive="approach",
reset_primitive="approach",
fps=10,
robot=None,
teleop=None,
primitives={
"approach": approach,
"close_gripper": close_gripper,
},
transitions=[
OnSuccess(source="approach", target="close_gripper", additional_reward=1.0),
],
)
summary = summarize_mpnet(mpnet)
print(summary["primitives"])
save_mpnet_config(mpnet, Path("mpnets/pick.json"))The key pattern is:
- define a
TaskFramefor each robot involved in a primitive - wrap those task frames in a
ManipulationPrimitiveConfig - connect primitives with explicit transition objects
- build a
ManipulationPrimitiveNetConfig - save it as JSON with
save_mpnet_config
If you want a one-node starter template instead, use:
from share.workspace.mpnet import create_template_mpnet, save_mpnet_config
config = create_template_mpnet("main")
save_mpnet_config(config, "mpnets/template.json")share-record runs an MP-Net environment, routes teleop/policy actions through the processor stack, and writes one dataset per adaptive primitive.
share-design-constraints runs an MP-Net as a live calibration routine and lets you edit adaptive primitive task frames in place. The intended workflow is to teleoperate an adaptive primitive, press o to set the current pose as the task-frame origin, move through the desired workspace, and let the tool accumulate min_pose / max_pose in that frame before saving the updated MP-Net JSON.
At a high level it:
- builds a
ManipulationPrimitiveNet - creates one dataset per adaptive primitive
- optionally loads one policy per adaptive primitive
- steps the active primitive
- stores transitions in the corresponding primitive dataset
share-train is a thin adapter around LeRobot training. It builds a train config from a policy checkpoint and a dataset location, then delegates to LeRobot’s training entrypoint.
share-eval is a lightweight dataset-summary pass. It reads recorded primitive datasets and emits a compact summary JSON with counts and available metadata.
Single-robot usage usually touches:
from share.envs.manipulation_primitive.task_frame import TaskFrame, ControlMode, PolicyMode
from share.envs.manipulation_primitive.config_manipulation_primitive import ManipulationPrimitiveConfig
from share.envs.manipulation_primitive_net.config_manipulation_primitive_net import ManipulationPrimitiveNetConfigIf you are using local hardware integrations from this repo checkout, common imports are:
from share.robots.ur import URConfig, UR
from share.teleoperators.delta_keyboard import KeyboardVelocityTeleopConfig, KeyboardVelocityTeleopAnd if you want the standalone LeRobot-style sibling package imports after installing the corresponding extras:
from lerobot_robot_ur import URConfig, UR
from lerobot_teleoperator_spacemouse import SpacemouseConfig, SpaceMousesrc/share/envs: manipulation primitives, MP-Nets, task frames, transitionssrc/share/processor: observation/action/info processing steps used by the env stacksrc/share/workspace: MP-Net persistence, summaries, structured editing helpers, and agent/runtime building blockssrc/share/scripts: recording, training, and evaluation entrypointssrc/share/robots: sibling installable robot packages plussharenamespace facadessrc/share/teleoperators: sibling installable teleoperator packages plussharenamespace facadestests: release-readiness, env, policy, and workspace tests
- The extras in the root package currently use local sibling-package references. They are ideal for working from this monorepo checkout, but they are not yet the final pattern you would want for publishing all pieces independently on PyPI.
- Some of the sibling standalone packages still import
share.*internals. That is fine when you install from this repository, but it means the subprojects are not fully decoupled yet. - The workspace/runtime code is present and useful as a library, but the CLI packaging around it is still being stabilized.