Skip to main content
Ask the arm to move its gripper somewhere, and three things happen in order:
The first two live in ManipulationModule β€” that is the planning stack. The third lives in the ControlCoordinator and you will rarely touch it. This layer is where your time goes. It is deliberately pluggable: the planner, the IK solver, and the collision world are all protocols you can swap β€” and a learned policy can replace the whole thing.

Your first plan

Start the planner and a mock arm, then drive it from Python:
skip
That plan β†’ preview β†’ execute rhythm is the whole API in miniature. Preview is cheap and execute is explicit, so you always see a motion before the robot performs it.

The API

Everything below is an RPC on ManipulationModule. The building blocks: And the one-shot skills that agents call β€” move_to_pose(x, y, z), move_to_joints("0.1, -0.5, ..."), go_home(), open_gripper() β€” each wrap plan-and-execute into a single step. Poses land in IK, IK lands in the planner, and the planner only ever talks to the world through WorldSpec β€” which is what makes the backends swappable.

Picking backends

Three independent choices, set on ManipulationModuleConfig or with -o overrides:
Two rules, enforced at startup rather than at your first plan: the roboplan planner needs the roboplan world, and drake_optimization IK needs the drake world. Everything else combines freely. If you are not sure, the defaults are right: Drake world, RRT-Connect, Pink IK.

Plugging in your own

The three protocols live in dimos/manipulation/planning/spec/protocols.py, and they are duck-typed β€” no base class, just the methods. A planner is one method:
skip
An IK solver is one method too β€” solve(world, robot_id, target_pose, seed, ...) -> IKResult. Wiring it up is a two-line change in dimos/manipulation/planning/factory.py: add your name to the PlannerName (or KinematicsName) literal, and add a branch in create_planner (or create_kinematics) that constructs your class. There is no plugin registry to learn β€” the factory is the registry. Stay on WorldSpec methods inside your implementation and it will work with every world backend, current and future. RRTConnectPlanner (planning/planners/rrt_planner.py) is a readable reference β€” pure Python, backend-agnostic. One naming trap: PinocchioIK exists in the kinematics folder but is not a planning backend β€” it is the fast in-loop IK used by the teleop and servo control tasks. You cannot select it via kinematics.backend.

Where learned policies fit

A learned policy does not extend the planner β€” it replaces it. Both are just producers of joint targets, and the coordinator accepts either:
LeRobotPolicyModule streams joint_command at the policy’s rate into a coordinator servo task β€” no IK, no collision world, no planner in the loop. Which path to use is a per-task decision: structured, obstacle-aware motion suits planning; contact-rich or hard-to-model skills suit a policy trained from demonstrations. Collecting those demonstrations and training that policy is the learning loop.