Metadata-Version: 2.4
Name: srmp
Version: 0.1.5
Summary: Python bindings for Search-Based Robot Motion Planning (SRMP)
Author-Email: The SRMP Team <imishani@gmail.com>
Maintainer-Email: Itamar Mishani <imishani@gmail.com>, Ramkumar Natarajan <nrkumar93@gmail.com>
License-Expression: AGPL-3.0-or-later
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: C++
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Requires-Dist: numpy
Requires-Dist: meshcat
Requires-Dist: viser
Requires-Dist: yourdfpy
Provides-Extra: agent
Requires-Dist: google-genai==1.47.0; python_version < "3.10" and extra == "agent"
Requires-Dist: google-genai==2.17.0; python_version >= "3.10" and extra == "agent"
Description-Content-Type: text/markdown

![](https://raw.githubusercontent.com/imishani/srmp-docs/main/docs/source/_static/assets/srmp_logo.png)

**SRMP** is a motion planning software for robotic manipulation, leveraging state-of-the-art search-based algorithms. It ensures consistent and predictable motions, backed by rigorous theoretical guarantees. Additionally, SRMP can efficiently plan for up to dozens of manipulators while guaranteeing collision-free execution—both between robots and with the environment—while maintaining motion consistency and predictability.

## Why SRMP?
Existing motion planning frameworks often struggle with the demands of high-stakes applications, where predictability and repeatability are critical. 
SRMP addresses these challenges by leveraging search-based planning methods, ensuring motions that are both efficient and reliable. 
Whether you're working on robotic manipulation, industrial automation, or large-scale multi-robot coordination, SRMP provides a powerful solution tailored to your needs.

## Key Features
- **Multi-Robot Motion Planning**: First-of-its-kind support for planning coordinated motions in multi-manipulator systems.
- **Reliable and Consistent Trajectories**: Generates predictable and repeatable motions, making it ideal for high-precision and safety-critical applications.
- **Seamless Integration**: Compatible with major simulators, including MuJoCo, Sapien, Genesis, PyBullet and Isaac.
- **Multi-Lingual**: Available in both Python and C++ for easy integration into research and industrial workflows.
- **MoveIt! Plugin**: Enables deployment on real-world robotic systems with minimal setup.

## Getting Started

To get started, check our [documentation](https://srmp.readthedocs.io/en/latest/). 

- [Saving and Loading](docs/saving-and-loading.md) — scenes, agent chats, and plans

## Build and Develop within the Package

The default editable install builds the VAMP SIMD validity-checking backend
alongside the coal backend. VAMP is vendored as a git submodule at `vamp/`
(pinned to the commit this integration expects), exactly like `search/` and
`third_party/pybind11`, so the default build needs no install step, no
download, and no `vamp_DIR` — it compiles the in-tree VAMP as a CMake source
subproject and links the stock `vamp::vamp` target:

```bash
git submodule update --init --recursive
pip install -e .
```

VAMP requires CMake 3.16+, a C++17 compiler, and Eigen 3.4+. On x86-64 the
result requires AVX2; ARM builds use NEON.

A source build targets the host CPU (`-march=native`). The **PyPI wheels** are
built for a fixed baseline instead: x86-64-v3 (AVX2/FMA/BMI2, Intel Haswell
2013+ and AMD Excavator/Zen+) on Linux, and Apple M1+ on macOS. `import srmp`
checks for those features on Linux x86-64 and raises an `ImportError` naming
what is missing rather than crashing later with an illegal instruction. On an
older CPU, build from source with the coal-only option below, or set
`SRMP_SKIP_CPU_CHECK=1` to bypass the check. To pin a different baseline for
your own wheel, pass
`--config-settings=cmake.define.IMS_MANIP_VAMP_ARCH_FLAGS=-march=<target>`.

### Coal-only build

To build without VAMP (for example on an architecture VAMP's SIMD backend does
not support, or a checkout where the `vamp/` submodule was not initialized),
turn the backend off explicitly:

```bash
python -m pip install -e . \
  --config-settings=cmake.define.IMS_MANIP_WITH_VAMP=OFF
```

This uses coal mesh collision checking only; `get_available_validity_backends()`
then reports `['coal']`.

### VAMP-enabled build details

Restart all running Python and Viser processes after rebuilding, then verify
the loaded extension:

```bash
python -c "import srmp; p=srmp.PlannerInterface(); print(p.get_available_validity_backends())"
# ['coal', 'vamp']
```

VAMP resolution order at configure time, first match wins:

1. An explicitly installed package (`find_package(vamp CONFIG)`), so pointing
   at an install with `-Dvamp_DIR=<prefix>/share/cmake/vamp` still behaves as
   before.
2. The in-tree submodule at `vamp/` (built as a subproject) — the default.
3. A download of the pinned fork into `ext/vamp` when
   `-DIMS_MANIP_FETCH_VAMP=ON` and the submodule is absent (fallback for
   checkouts without submodules).

If none resolve, the configure fails with instructions; the quickest fix is
`git submodule update --init --recursive vamp`.

SRMP uses stock VAMP headers and the stock `vamp::vamp` target. The
`vamp-planner` Python package is not a substitute for the C++ source or CMake
target used here.

Include `.[agent]` instead of `.` if the Gemini-backed CLI/Viser agent should
be installed at the same time.

A VAMP-enabled extension still defaults to coal, so the same installation
supports controlled A/B runs:

```python
from srmp import PlannerInterface

planner = PlannerInterface()
planner.add_robot("panda")
print(planner.get_available_validity_backends())  # ["coal", "vamp"]

print(planner.get_vamp_supported_robots())        # ["fetch", "panda", "ur5"]
print(planner.is_robot_vamp_supported("panda"))   # True

context = {
    "planner_id": "wAstar",
    "heuristic": "bfs",
    "weight": "10",
    "validity_backend": "auto",  # or "coal" / "vamp" explicitly
}
planner.make_planner(["panda"], context)
# "auto" picks VAMP when this build and robot support it, coal otherwise;
# an explicit "vamp" raises instead of falling back.
print(planner.get_active_validity_backend())      # "vamp"
```

### Planners

Single-agent `planner_id` values: `Astar`, `wAstar`, `ARAstar`, `MHAstar`, `wPASE` (parallel), `MGS`,
`vamp_rrtc`; multi-agent (more than one articulation): `ECBS`, `xECBS`. All context values are strings.

`vamp_rrtc` is VAMP's RRT-Connect run on the VAMP backend's SIMD environment (Halton sampling,
deterministic): joint goals only, `validity_backend` `vamp` or `auto`, typically well under a
millisecond on uncluttered scenes. Keys: `rrtc_range` (2.0 rad), `rrtc_max_iterations` (100000),
`rrtc_max_samples` (100000), `rrtc_simplify` (`true`: VAMP's shortcut/B-spline pass). The
simplified path goes through the usual shortcutting and time parameterization; stats report
`rrtc_iterations`, `rrtc_raw_waypoints`, `rrtc_waypoints`, `rrtc_solve_time`, `rrtc_simplify_time`.

`MGS` (multi-graph search) grows a start graph and, for joint goals, a goal graph, and adds
further graphs rooted at IK solutions of the BFS heuristic's workspace attractors when the
search stalls, bridging graphs with straight joint-space edges:

```python
context = {
    "planner_id": "MGS",
    "g_num": "10",                  # maximum number of graphs (start, goal, roots)
    "weight": "100",                # suboptimality bound of the anchor (start) graph search
    "heuristic": "joint_steps",     # root-to-root heuristic (joint distance in discretization steps)
    "anchor_heuristic": "bfs",      # goal-distance heuristic for the start graph; "joint_steps" (aimed at the
                                    # goal root for pose goals) is 10x faster through narrow passages, slower on easy scenes
    "graph_growth": "on_stall",     # or "upfront": all g_num - 2 roots before searching
    "stall_window": "50",           # expansions without progress before a root is added
    "root_selection": "frontier_nearest",  # or "sequential"
    "nonblocking_roots": "false",   # "true": never wait for root IK; inject roots as they become ready
    "goal_roots": "1",              # pose goals: goal graphs rooted at IK solutions of the goal pose
    "bridge_nn": "kd_tree",         # bridge partner lookup: k-d tree per graph, or "heuristic" (linear scan)
    "anchor_switch_on_stall": "true",   # at the first stall switch the start graph's anchor to
    "anchor_fallback_heuristic": "joint_steps",  # this heuristic (BFS in the open, joint-space in depressions)
    "root_clearance": "0.05",       # roots retreat from obstacles until the EE has this clearance (m)
    "short_prims_near_start": "false",  # "true": use the short primitives within short_dist of the start too
    "anchor_search": "true",        # continue after the first connection to bound the cost
    "expansion_threads": "12",      # parallel edge checks per expansion (coal backend)
    "snap_ik_seeds": "1",           # IK seeds per snap attempt on pose goals (>1 runs them on the worker pool)
    "ik_dt": "1.0",                 # CLIK step scale (full step); "ik_max_iter": "50", "ik_eps": "1e-5"
    "snap_ik_backoff": "0",         # opt-in: skip 1, 2, 4.. snap IKs after a failure (see design notes)
    "snap_cached_goals": "3",       # goal configurations kept as snap targets
    "vamp_packed_edges": "true",    # VAMP: validate all primitive edges of an expansion in shared SIMD blocks
    "root_ik_threads": "6",         # background root IK threads
    "anchor_orientation_weight": "0",  # cost/rad of EE orientation error in the anchor heuristic
    "time_limit": "10",
}
```

### Mobile manipulators and primitive-file units

A mobile base modelled as ordinary joints of the URDF (`x`, `y` prismatic and `theta` revolute, as
in `data/ridgeback_ur10e`) is part of the move group of its end effector, and the planners treat
the joints by kind: the articulation reports `get_move_group_joint_types()`, prismatic
coordinates are never wrapped by 2*pi, and the lattice uses `resolution` degrees for revolute
joints and `resolution_prismatic` metres (default `0.05`) for prismatic ones. A primitive file may
declare its units and cost model at the top level:

```yaml
units: steps    # degrees (default) | radians | meters | steps -- steps: multiples of the joint's discretization
cost: time      # file (default): the listed transition costs | time: duration at the joint velocity limits
```

With `units: steps` one file serves any mix of prismatic and revolute joints; with `cost: time`
base, torso and arm motions -- and MGS bridge and snap edges -- are priced in seconds, so the
search compares them on one scale.

A composite file assembles the move group from per-group primitive files, each in its own units
and magnitudes, padded into the full state (`config/ridgeback_ur10e_mprim.yaml`):

```yaml
cost: time
groups:
  - name: base
    joints: [ x, y, theta ]          # move-group joint names (or `dims: [0, 1, 2]`)
    file: omni_base_mprim.yaml       # 0.4 m / 10 deg long steps, 0.1 m / 2 deg short steps, diagonals
  - name: arm
    file: manip_6dof_mprim.yaml      # no joints/dims: the remaining coordinates, in order
```

Group files are looked up next to the composite. The composite's `cost:` applies to every group.
A mobile manipulator's end effector roams far beyond the default 2 m occupancy grid, so construct
the `PlannerInterface` with a `GridConfig` that covers the base's workspace (the BFS heuristic
reports the goal position and the grid bounds otherwise).

On a mobile manipulator the planners' `bfs` heuristic is base-aware: besides the end-effector
wavefront it runs a 2D wavefront over the floor (obstacles up to `base_height`, inflated by
`base_footprint_radius`) from the docking region within `arm_reach` of the goal, and takes the
larger of the two, so a knee-high wall the end effector passes over still sends the base around.
Under `cost: time` the cells are priced in seconds (`ee_speed`, default 1 m/s; `base_speed`,
default the slower base velocity limit) and `joint_steps` heuristics become `joint_time`, the
straight-move duration at the velocity limits:

```python
context = {
    "planner_id": "wAstar", "heuristic": "bfs", "weight": "50",
    "mprim_path": "config/ridgeback_ur10e_mprim.yaml",
    "resolution": "1",                # degrees, revolute joints
    "resolution_prismatic": "0.05",   # metres, prismatic joints (base x/y, torso)
    "base_footprint_radius": "0.5", "base_height": "0.5", "arm_reach": "1.0",
}
```

Roots are cached across plans with the same start, goal pose and scene, and `get_planning_stats()`
reports MGS counters (`bridge_attempts`, `graph_merges`, `num_roots`, `num_graphs`, timings) under
`bonus_stats`. Design notes and measurements: `docs/plans/2026-09-03-mgs-followups.md`,
`docs/plans/2026-09-03-mgs-incremental-graphs-design.md`.

The occupancy grid (the BFS heuristics' world) defaults to 2 x 2 x 2 m at 2 cm around the
origin. Adding a mobile manipulator grows it to the base's x/y limits plus 1.5 m at 5 cm
automatically; `set_grid(GridConfig)` rebuilds it at any time (obstacles are voxelized again,
the planner is rebuilt on the next plan) and `get_grid_config()` reports it. The Viser panel's
**Occupancy grid** folder shows the bounds as an outline in the scene and edits them; the
**Planner context** field takes extra `key=value` pairs for the next Plan.

A mobile manipulator needs no context at all: with no `mprim_path` the factories compose
`omni_base_mprim.yaml` with the arm's file (`config/mobile_6dof_mprim.yaml`,
`mobile_7dof_mprim.yaml`), and `bfs` is the base-aware heuristic. In the Viser **Plan** panel tick
**Goal as EE pose** to send the gizmo pose itself and let the planner place the base (off, the
panel plans to the IK solution shown as the ghost); to tune, fill `viz.plan_context_defaults`
(e.g. `{"weight": "50", "arm_reach": "1.2"}`), which every Plan click merges into its
`make_planner` call, the panel's own planner and backend choices winning.

In the Viser **Plan** panel, select one robot and leave **Collision backend**
on `Auto (VAMP when supported)` (or pick `VAMP (SIMD spheres)` explicitly --
the option is offered only when the selected robot has a Robot_Module),
position the end-effector goal, and click
**Plan to goal**. The status line reports `backend: vamp` when VAMP is actually
attached to the planner. If the VAMP option is absent, the running process
loaded a coal-only extension; repeat the backend verification above and restart
Viser. VAMP selection is intentionally limited to single-agent planners until
Phase 2 multi-robot verification is complete. A coal-only build advertises only
coal and gives a reinstall message if Python requests VAMP directly.

## Agentic mode

The default CLI backend is Gemini and uses Google's native `google-genai` SDK.
For a new editable installation, install the agent extra and configure a Google
AI Studio API key:

```bash
pip install -e ".[agent]"
export GEMINI_API_KEY=...
python -m srmp.agent.cli
```

The default build already includes VAMP, so no extra CMake flags are needed —
just add the `[agent]` extra:

```bash
git submodule update --init --recursive
pip install -e ".[agent]"
```

You can create a free key at <https://aistudio.google.com/app/apikey>. To add
Gemini to an existing installation without rebuilding SRMP, install only the
SDK with Python-compatible pins:

```bash
python -m pip install \
  'google-genai==1.47.0; python_version < "3.10"' \
  'google-genai==2.17.0; python_version >= "3.10"'
```

Other backends have separate dependencies; run
`python -m srmp.agent.cli --help` or select a backend in the GUI for their
setup instructions.

Launch the Viser agent interface with:

```bash
python -m srmp.agent.gui
```
