Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ jobs:
with:
python: ${{ matrix.python }}
invoke_lint: true
# The ordinary suite includes PyBullet backend and analytical +
# PyBullet tests. PyBullet is an explicit package extra rather than
# an implicit development dependency.
extras: dev,pybullet

build-cpython-components:
runs-on: windows-latest
Expand Down
13 changes: 8 additions & 5 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ on:

jobs:
build:
name: ubuntu-py39-integration
name: ubuntu-py312-integration
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python 3.9
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.9'
python-version: '3.12'

- name: Bring up both ROS stacks
# Defaults: ROS 1 rosbridge on 9090, ROS 2 rosbridge on 9091, ROS 2
Expand All @@ -33,10 +33,13 @@ jobs:
# but NOT the package itself nor its runtime deps. The top-level
# `conftest.py` then crashes on `from twisted.internet import ...`
# because twisted only arrives transitively via `roslibpy` (a runtime
# dep). Installing the package with the `[dev]` extra pulls both.
# dep). Both optional backends are included explicitly so their tests
# cannot be silently skipped by optional-dependency guards.
run: |
python -m pip install --upgrade pip wheel
python -m pip install --no-cache-dir -e ".[dev]"
python -m pip install --no-cache-dir -e ".[dev,pybullet,pyroki]"
python -c "import pybullet"
python -c "import pyroki"

- name: Run tests + doctests
# `COMPAS_FAB_RUN_ROS_INTEGRATION_TESTS=1` opts the `ros1_client` /
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/robot-cell-snapshots.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up Python 3.11
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.11"
python-version: "3.12"
cache: pip
cache-dependency-path: |
requirements.txt
requirements-dev.txt
requirements-pybullet.txt

- name: Bring up ROS 1 and ROS 2 integration stacks
run: |
Expand All @@ -44,7 +45,7 @@ jobs:
- name: Install project and snapshot dependencies
run: |
python -m pip install --upgrade pip wheel
python -m pip install --no-cache-dir -e ".[dev]"
python -m pip install --no-cache-dir -e ".[dev,pybullet]"

- name: Generate local robot-cell snapshots
run: |
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ Requires `compas_robots >= 1.1`.

### Added

* Added the optional `PyRokiPlanner` backend for differentiable `FrameTarget` and `PointAxisTarget` inverse kinematics. It converts COMPAS robot models directly, supports planning groups and target modes, enforces joint limits, checks and avoids capsule/box-approximated collisions by default, and caches analyzed JAX problems for inexpensive warm-start solves. Install it with `compas_fab[pyroki]`; until PyRoKI publishes a release, the extra is pinned to a tested Git commit.
* Added a Python 3.12 PyRoKI integration job, a dedicated backend guide and API reference, runnable IK/collision/servo examples, and integration coverage for workpiece targets, planning groups, external axes, continuous joints, unreachable targets, and actual self-collision reporting.
* The `Tool From Mesh` Grasshopper component gained a `base_plane` input: where the robot's flange takes hold of the geometry, expressed in the coordinates the mesh was modelled in. Its Z axis points away from the robot, so a tool drawn reaching along world Z needs none, and a tool drawn along another axis is mounted by wiring a plane instead of redrawing the geometry. Backed by the new `base_frame` argument of `compas_robots.ToolModel`; nothing is baked into the mesh, so the plane can be re-wired at any time. The component also surfaces a remark when the TCP does not sit roughly on the tool's +Z, since that means the tool will point sideways once attached — the direction from the mount to the TCP is only a hint (it says nothing about roll), so it is reported rather than applied.
* `RobotCellLibrary` now includes UR3, UR3e, UR5e, UR10, UR16e, and Stäubli TX2-60L robot-only cells, completing the locally bundled models that have analytical kinematics solvers.
* Added `UR16eKinematics` and the correctly named `Staubli_TX2_60LKinematics` analytical solver classes. The previous Stäubli class and solver key remain as compatibility aliases.

### Changed

* Migrated the contributor guide from the leftover Sphinx-era `CONTRIBUTING.rst` to a canonical Markdown guide that is also rendered in the MkDocs developer section. The setup now documents the current development commands and both ROS integration stacks.
* Migrated the contributor guide from the leftover Sphinx-era `CONTRIBUTING.rst` to a canonical Markdown guide that is also rendered in the MkDocs developer section. The setup now installs optional test backends explicitly and documents both ROS integration stacks.
* Refreshed the bundled UR5 and UR10e descriptions and meshes from the official Universal Robots ROS 2 description package. All seven supported UR cells now share one `ur_description` mesh package instead of duplicating model assets per cell.
* Bundled UR and Panda visual meshes now use their original Collada files instead of derived OBJ copies, preserving source materials and eliminating redundant converted assets. The ROS package extraction script likewise keeps downloaded DAE files unchanged.
* The tools in `ToolLibrary` now mount along the +Z axis of their base frame instead of +X. Every planning group in `RobotCellLibrary` ends at a link whose +Z points away from the arm (`tool0` for the industrial robots, `panda_hand_tcp` for the Panda), so with this the same tool attaches to any of them with an identity attachment frame — previously each cell carried a rotation to bridge the two conventions, and a tool authored for one robot did not necessarily fit another. Their TCF states the tool's working direction with its own Z axis too, so a `TargetMode.TOOL` target aligns the tool along the target's Z — previously the TCF's X axis ran along the tool, which put every tool-mode target 90 degrees out. The tools are still modelled along +X internally and re-framed on the way out via `ToolModel.reframe_base`. The beams held by the gripper cells are authored in TCF coordinates and were re-authored to match, so they stay put. Poses are unchanged: the attached tools and workpieces of every cell land exactly where they did, only the tool's base frame is now the end effector link's frame rather than a rotated version of it. Requires the `reframe_base` support of `compas_robots >= 1.1`.
Expand Down
8 changes: 6 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ Contributions are welcome and greatly appreciated.
2. Create and activate a virtual environment using `venv`, `uv`, `conda`, or
another environment manager.
3. From the repository root, install the package in editable mode together
with its development tools:
with its development tools and optional test backends:

```bash
python -m pip install -e ".[dev]"
python -m pip install -e ".[dev,pybullet,pyroki]"
```

PyRoKI requires Python 3.10 or newer. Python 3.12 is the version used by the
integration workflow. If your change does not touch an optional backend,
`python -m pip install -e ".[dev]"` is sufficient for the core suite.

4. Run the ordinary test suite:

```bash
Expand Down
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ include CONTRIBUTING.md
include LICENSE
include README.md
include requirements.txt
include requirements-pybullet.txt
include requirements-pyroki.txt

exclude requirements-dev.txt
exclude .deepsource.toml
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ It can also be installed using `pip`:

pip install compas_fab

Optional in-process backends are available as extras:

pip install "compas_fab[pybullet]"
pip install "compas_fab[pyroki]"

The PyRoKI extra provides fast differentiable and collision-aware numerical IK
on Python 3.10 or newer. Until PyRoKI publishes its first package release, the
extra installs a tested, pinned Git commit. See the
[backend guide](https://compas.dev/compas_fab/latest/backends/pyroki/).


> On Windows, you may need to install [Microsoft Visual C++ 14.0](https://www.scivision.co/python-windows-visual-c++-14-required/).

Expand Down
1 change: 1 addition & 0 deletions docs/api/compas_fab.backends.pyroki.backend_features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# ::: compas_fab.backends.pyroki.backend_features
1 change: 1 addition & 0 deletions docs/api/compas_fab.backends.pyroki.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# ::: compas_fab.backends.pyroki
2 changes: 1 addition & 1 deletion docs/api/core/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ imported and form the foundation that the integrations build on top of.
- **[compas_fab.robots](../compas_fab.robots.md)**: robot cells, tools,
rigid bodies, targets, waypoints, and the robot library.
- **[compas_fab.backends](../compas_fab.backends.md)**: backend client
and planner classes (analytical, PyBullet, ROS/MoveIt). Each planner
and planner classes (analytical, PyRoKI, PyBullet, ROS/MoveIt). Each planner
exposes its full set of FK/IK/motion-planning methods directly; the
building blocks behind them (interfaces and backend features) are covered
by [Backend architecture](../../developer/architecture.md) in the developer
Expand Down
4 changes: 2 additions & 2 deletions docs/backends/analytical_pybullet.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ paired with [AnalyticalPyBulletClient][compas_fab.backends.AnalyticalPyBulletCli
## Setup

```bash
uv pip install pybullet
uv pip install "compas_fab[pybullet]"
```

On macOS, PyBullet may need an extra build flag:

```bash
CFLAGS="-fno-define-target-os-macros" uv pip install pybullet
CFLAGS="-fno-define-target-os-macros" uv pip install "compas_fab[pybullet]"
```

The analytical solvers ship with `compas_fab` itself, nothing else to install.
Expand Down
32 changes: 19 additions & 13 deletions docs/backends/index.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,42 @@
# Choosing a backend

**COMPAS FAB** is one library that drives five different planning back-ends.
**COMPAS FAB** is one library that drives six different planning back-ends.
Pick the one that matches what you want to do.

## By intent

| I want to… | Use | Why |
|---|---|---|
| Just compute IK on a robot (e.g., UR, Staubli, ABB, etc) | [Analytical IK](analytical.md) | Closed-form, microsecond IK, no Docker, no PyBullet |
| Continuously servo a target with numerical IK | [PyRoKI](pyroki.md) | Differentiable IK with inexpensive warm solves and optional task-axis freedom |
| Compute IK *and* check collisions | [Analytical IK + PyBullet](analytical_pybullet.md) | Analytical-fast IK filtered against a real collision scene |
| Plan motion (collisions + trajectory smoothing) without ROS | [PyBullet](pybullet.md) | In-process motion planning; runs anywhere Python runs |
| Compute collision-aware IK for a redundant or custom robot | [PyRoKI](pyroki.md) | Joint-limit and differentiable approximate-collision constraints in one solve |
| Plan Cartesian motion without ROS | [PyBullet](pybullet.md) | In-process waypoint interpolation and collision checking |
| Plan motion via ROS 2 + MoveIt 2 | [ROS 2 + MoveIt 2](ros2.md) | Current ROS LTS; the recommended starting point for new ROS work |
| Drive an existing ROS 1 + MoveIt 1 setup | [ROS 1 + MoveIt 1](ros.md) | Legacy stack; only use if you must |
| Just model / visualize a robot cell in a CAD environment | *no backend* | The core data model works without any planner. See [Concepts](../concepts.md) |

## By capability

| Capability | Analytical | Analytical + PyBullet | PyBullet | ROS 1 + MoveIt 1 | ROS 2 + MoveIt 2 |
|---|:-:|:-:|:-:|:-:|:-:|
| Forward kinematics | ✓ | ✓ | ✓ | ✓ | ✓ |
| Inverse kinematics | ✓ (closed-form) | ✓ (closed-form) | ✓ (numerical) | ✓ | ✓ |
| Collision checking | — | ✓ | ✓ | ✓ | ✓ |
| Point-to-point motion planning | — | — | ✓ | ✓ | ✓ |
| Cartesian motion planning | — | ✓ (partial) | ✓ | ✓ | ✓ |
| Visualisation | — | PyBullet GUI | PyBullet GUI | RViz | RViz |
| Setup cost | none | `pip install pybullet` | `pip install pybullet` | Docker | Docker |
| Usable from inside Rhino 7/8 | ✓ | — | — | ✓ (over WebSocket) | ✓ (over WebSocket) |
| Capability | Analytical | Analytical + PyBullet | PyRoKI | PyBullet | ROS 1 + MoveIt 1 | ROS 2 + MoveIt 2 |
|---|:-:|:-:|:-:|:-:|:-:|:-:|
| Forward kinematics | ✓ | ✓ | internal | ✓ | ✓ | ✓ |
| Inverse kinematics | ✓ (closed-form) | ✓ (closed-form) | ✓ (differentiable) | ✓ (numerical) | ✓ | ✓ |
| Collision checking | — | ✓ (mesh-based) | ✓ (approximate) | ✓ (mesh-based) | ✓ | ✓ |
| Point-axis targets | — | — | ✓ | ✓ | ✓ | ✓ |
| Point-to-point motion planning | — | — | — | — | ✓ | ✓ |
| Cartesian motion planning | — | ✓ (partial) | — | ✓ | ✓ | ✓ |
| Visualisation | — | PyBullet GUI | — | PyBullet GUI | RViz | RViz |
| Setup cost | none | `pip install "compas_fab[pybullet]"` | `pip install "compas_fab[pyroki]"` | `pip install "compas_fab[pybullet]"` | Docker | Docker |
| Usable from inside Rhino 8 | ✓ | — | — | — | ✓ (over WebSocket) | ✓ (over WebSocket) |

## Setup cost in plain words

- **Analytical IK**: nothing to install beyond `compas_fab` itself.
- **PyBullet / Analytical + PyBullet**: one `pip install pybullet` (with a
- **PyRoKI**: install `compas_fab[pyroki]` in Python 3.10 or newer. The
dependency is currently pinned to a tested Git commit because it has not
published a package-index release.
- **PyBullet / Analytical + PyBullet**: one `pip install "compas_fab[pybullet]"` (with a
small workaround on macOS, see the per-backend pages).
- **ROS 1 & ROS 2**: Docker Desktop + the per-robot compose stack in
[`docs/installation/docker_files/`](https://github.com/compas-dev/compas_fab/tree/main/docs/installation/docker_files).
Expand Down
4 changes: 2 additions & 2 deletions docs/backends/pybullet.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ PyBullet is an optional dependency of `compas_fab`. It is not installed by
default because upstream does not ship wheels for the newest Python versions.

```bash
uv pip install pybullet
uv pip install "compas_fab[pybullet]"
```

On macOS, PyBullet sometimes needs an extra build flag:

```bash
CFLAGS="-fno-define-target-os-macros" uv pip install pybullet
CFLAGS="-fno-define-target-os-macros" uv pip install "compas_fab[pybullet]"
```

`PyBulletClient` supports three connection types:
Expand Down
101 changes: 101 additions & 0 deletions docs/backends/pyroki.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# PyRoKI

[PyRoKI](https://github.com/chungmin99/pyroki) provides differentiable robot
kinematics on JAX. The COMPAS FAB backend converts a `RobotModel` directly into
PyRoKI's kinematic representation and exposes it through the usual
`PyRokiPlanner.inverse_kinematics()` API.

## When to use

- You need fast warm-start numerical IK for servoing or interactive design.
- Your robot has redundant joints, external axes, or no analytical solver.
- You need `FrameTarget` or `PointAxisTarget` constraints.
- You want collision-aware IK without running ROS or a separate service.

## Trade-offs

| What you get | What you give up |
|---|---|
| In-process differentiable IK with inexpensive warm solves | The first solve compiles a JAX problem and is comparatively slow |
| Arbitrary COMPAS robot models and planning groups | One solution is currently produced per starting configuration |
| Collision checking and avoidance enabled by default | Collision meshes are approximated, not checked exactly |
| Joint limits, tools, workpieces, rigid bodies, and target modes | No point-to-point or Cartesian trajectory planning yet |

## Setup

PyRoKI is an optional dependency and requires Python 3.10 or newer. Python 3.12
is the version exercised by COMPAS FAB's PyRoKI CI job.

```bash
uv pip install "compas_fab[pyroki]"
```

PyRoKI has not published a package-index release. The extra therefore installs
the exact Git commit recorded in `requirements-pyroki.txt`. Updating that pin
should be accompanied by the complete PyRoKI test suite because the upstream
interfaces can still change before its first release.

## First example

Solve a frame target and verify it independently with COMPAS Robots forward
kinematics:

```python
--8<-- "docs/backends/pyroki/files/01_inverse_kinematics.py"
```

This example loads no meshes, so it explicitly sets `check_collision=False`.
Collision checking is enabled by default for normal geometry-loaded cells.

## Collision model

The backend preserves FAB's disabled-collision pairs, hidden objects,
attachments, `touch_links`, and `touch_bodies`. Each moving collision mesh is
approximated by a fitted capsule, while each stationary world mesh is
approximated by an axis-aligned bounding box. This makes distances
differentiable and solves fast, but it can produce false positive or false
negative results compared with a mesh-based checker. Validate safety-critical
results with a higher-fidelity backend such as PyBullet or MoveIt.

`collision_margin` adds clearance around the approximations. Passing
`options={"check_collision": False}` opts out of collision constraints and
allows a cell loaded without collision geometry.

## Warm starts and caching

The configuration in `RobotCellState` is the numerical seed. The first query
for a robot cell and solver structure includes JAX compilation and collision
geometry setup. The planner caches that analyzed problem, so later targets and
active-joint seed values with the same structure are inexpensive. Changes to
the cell, attachments, stationary geometry, inactive joints, planning group,
target kind, or structural solver options select a new cache entry.

For continuous servoing, keep one planner instance, merge each returned
configuration into the state, and use that state as the next query's seed.

## Options

| Option | Default | Meaning |
|---|---:|---|
| `check_collision` | `True` | Include collision constraints and verify the result |
| `collision_margin` | `0.0` | Additional clearance in metres |
| `collision_weight` | `10.0` | Weight of collision residuals |
| `max_iterations` | `100` | Maximum nonlinear solver iterations |
| `lambda_initial` | `1.0` | Initial trust-region damping |
| `position_weight` | `50.0` | Target-position residual weight |
| `orientation_weight` | `10.0` | Target-orientation or target-axis residual weight |
| `position_tolerance` | `1e-4` | Accepted position error in metres, unless set on the target |
| `orientation_tolerance` | `1e-3` | Accepted angular error in radians, unless set on the target |
| `return_full_configuration` | `False` | Return all configurable joints instead of only the planning group |
| `verbose` | `False` | Enable PyRoKI/JAXLS solver output |

## More examples

- [`02_collision_aware_inverse_kinematics.py`](pyroki/files/02_collision_aware_inverse_kinematics.py){: download="02_collision_aware_inverse_kinematics.py" } — move a redundant Panda posture away from an obstacle while preserving its end-effector pose
- [`03_point_axis_target.py`](pyroki/files/03_point_axis_target.py){: download="03_point_axis_target.py" } — constrain position and tool axis while leaving rotation about that axis free
- [`04_warm_start_servo.py`](pyroki/files/04_warm_start_servo.py){: download="04_warm_start_servo.py" } — repeatedly update a target and feed each solution into the next warm start

## API reference

- [compas_fab.backends.PyRokiPlanner][]
- [compas_fab.backends.pyroki.backend_features][]
Loading
Loading