Skip to content
personalroboticsPublic

About

A small simulator-neutral robot interface for planning, policies, and real-world execution.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ssrobot

A small simulator-neutral robot interface for planning, policies, and real-world execution.

What works today

ssrobot loads and inspects portable robot packages, with no runtime dependencies and no simulator or ROS. From a clone, with uv:

uv sync
uv run ssrobot inspect examples/packages/bimanual_lift   # readable summary
uv run ssrobot inspect examples/packages/bimanual_lift --json report.json
import ssrobot

package = ssrobot.load_package("examples/packages/bimanual_lift")
robot = package.description                 # immutable, validated RobotDescription
print(robot.name, robot.fingerprint()[:12])
for arm in robot.manipulators:
    print(arm.name, robot.group(arm.group).joints, arm.base_frame, "->", arm.tool_frame)

ssrobot.load_installed_package("geodude_assets") loads an installed package the same way, without running its code. ssrobot init writes a package's manifest from an MJCF or URDF model, and ssrobot doctor checks the package and, given --wheel, its built wheel. A RobotContext over the reference ReplayRuntime exercises the observation, command, ownership, and trace contracts.

Simulation in MuJoCo

With the mujoco extra (uv sync installs it for development), a package runs in MuJoCo behind the same RobotContext. This moves the example arm along a trajectory in simulated time:

import ssrobot
from ssrobot.mujoco import MujocoRuntime

package = ssrobot.load_package("examples/packages/mujoco_arm")
arm_q = ssrobot.ObservationRequest(channels=("arm_q",))
with ssrobot.RobotContext(package.description, MujocoRuntime(package)) as ctx:  # at home
    start = ctx.observe(arm_q).readings[0].value
    move = ssrobot.JointTrajectory(
        group="arm",
        joints=("shoulder", "elbow", "wrist"),
        time_from_start_ns=(0, 1_000_000_000),
        positions=(start, (1.0, -0.5, -0.5)),
    )
    status = ctx.run_until(ctx.submit(move), max_ticks=2_000)  # steps simulated time
    print(status.state.value, ctx.observe(arm_q).readings[0].value)

It also observes joint state and effort, frame poses, wrenches, and camera images. A GuardedJointTrajectory runs a joint trajectory and stops it within one tick when a force-torque guard triggers, as for placing or skewering; the status says whether it triggered or completed. MujocoRuntime(package, scene="tabletop.xml") adds task objects from an MJCF scene. ctx.attach(object, end_effector) then declares a grasp for planners and the trace without changing physics: friction alone holds the object, and a slip is reported. ctx.snapshot() freezes the world, and ctx.planning_scene(snapshot, "arm") answers a planner's collision and kinematics queries on that copy, built on sscbirrt's native MuJoCo checker. ctx.submit(command, snapshot=snapshot) refuses a command planned on a snapshot whose scene has since changed. docs/mujoco.md describes what it binds and how commands run.

Planning

With the planning extra as well, sscbirrt plans in a snapshot and times the motion under the arm's velocity and acceleration limits. ctx.execute compiles the plan for the runtime's declared trajectory contract, has the planner's own validator check the command, and submits it only if every check passes:

Goal Public call
One joint configuration, or any of several alternatives plan_to_configuration, plan_to_configurations
A selected tool or other frame at a pose plan_to_end_effector_pose
A straight Cartesian approach or retreat inside a constrained corridor plan_to_end_effector_offset
Any pose in one or more task-space regions plan_to_tsr
Any of the above while a frame remains in task-space regions along the path the call's path_constraint option

Pose, offset, and TSR goals, and path constraints, need kinematics for the arm: CBiRRT.from_package(package, "right_arm") builds the planner with ssik kinematics for that manipulator, or fails with kinematics_unavailable. CBiRRT(description) offers configuration goals only: any other goal is refused at negotiation (unsupported_goal), and a path constraint with unsupported_constraint.

The request may select the controlled frame, scale velocity and acceleration, and set a timeout and random seed. Declared attachments move with the robot during collision checking, so the same calls plan while carrying an object and apply its contact allowances.

import ssrobot
from ssrobot.mujoco import MujocoRuntime
from ssrobot.planning import CBiRRT

package = ssrobot.load_package("examples/packages/mujoco_arm")
runtime = MujocoRuntime(package, scene="examples/scenes/pedestal.xml")
with ssrobot.RobotContext(package.description, runtime) as ctx:
    arm = ctx.manipulator()                                  # the only manipulator
    planner = CBiRRT(package.description)                    # configuration goals
    plan = arm.plan_to_configuration(                        # in the world as it is now
        (-0.6, -0.4, 0.0), planner=planner, velocity_scaling=0.5
    )                                                        # carries its request
    execution = ctx.execute(plan, planner=planner)           # compile, validate, submit
    status = ctx.run_until(execution, max_ticks=5_000)
    print(status.state.value, plan.backend, round(plan.trajectory.duration, 2), "s")

Without snapshot=, a planning call captures exactly one snapshot as it starts and plans in it; pass snapshot=ctx.snapshot() to plan several requests in exactly the same world. What stays explicit is deliberate:

  • the runtime, because nothing chooses a simulator or a robot for you;
  • the manipulator, when the robot has several (Geodude never guesses);
  • the planner, with its kinematics and validation policy, because there is no registry;
  • planning, then execute, because planning never moves the robot, and the plan can be inspected first;
  • planner= again at execute, because a plan is data, and its validator comes from the planner you name;
  • max_ticks, because the wait is bounded and blocking. That is for a manual clock, as in simulation; on an external clock, as on hardware, run_until takes timeout_s.

On Geodude and ADA in simulation, a straight move of the tool is three setup lines and three motion lines:

planner = CBiRRT.from_package(package, "right_arm")
with ssrobot.RobotContext(package.description, runtime) as ctx:
    arm = ctx.manipulator("right_arm")
    plan = arm.plan_to_end_effector_offset((0.0, 0.0, 1.0), 0.05, planner=planner)
    execution = ctx.execute(plan, planner=planner)
    status = ctx.run_until(execution, max_ticks=20_000)

A plan made before the scene changed (an object attached or detached) is refused with stale_snapshot, and the trace links each execution to the compiled command and validation evidence it ran. ssrobot.plan_portfolio runs several planner, seed, and processor attempts at one request in isolated scenes of one snapshot, and selects by declared priority and duration, never by which finished first. The validation guarantee is discrete: CBiRRTConfig.edge_resolution is the user-selected largest joint-space distance between checked states, and nothing is claimed between them. docs/planning.md describes the adapter, validation policy, and checks.

The two reference robots, Geodude and ADA (Kinova JACO), run one planning scenario from their installed packages in CI: the same function plans to a task space region and to a named configuration, executes both, and is byte-identical across two runs, with no robot-specific code. So does the concise journey above. See Reference robots in docs/packages.md.

Target v0.1

Core arm planning works in simulation. M3's remaining work is isolated and parallel planner execution (#23), jointed tools (#121), and force-guarded trajectory execution (#124). The neutral contracts include base-pose requests and allow other planner adapters, but end-to-end base planning, coupled bimanual planning, and an adapter other than CBiRRT are not implemented. Object state channels (the rest of M2) and policies (M4) are not implemented yet. The policy-only and hybrid examples in docs/architecture.md show the target v0.1 behavior.

Documentation

See docs/architecture.md for the charter: scope, ownership, non-goals, status, and the architecture decisions that bind v0.1, and docs/contracts.md for the implemented public types, conventions, and wire schemas. docs/packages.md describes portable robot packages, and docs/authoring.md shows how to create one from a model file with ssrobot init. docs/mujoco.md covers MujocoRuntime, and docs/planning.md the planning adapter and executing plans.

About

A small simulator-neutral robot interface for planning, policies, and real-world execution.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages