Skip to content

COMPAS FAB 2.0 release#458

Open
gonzalocasas wants to merge 505 commits into
mainfrom
prep-release
Open

COMPAS FAB 2.0 release#458
gonzalocasas wants to merge 505 commits into
mainfrom
prep-release

Conversation

@gonzalocasas
Copy link
Copy Markdown
Member

@gonzalocasas gonzalocasas commented May 19, 2026

image image

This PR superceedes #456 and contains the entire Project Theseus + ROS 2 / MoveIt 2 support.

What type of change is this?

  • Bug fix in a backwards-compatible manner.
  • New feature in a backwards-compatible manner.
  • Breaking change: bug fix or new feature that involve incompatible API changes.
  • Other (e.g. doc update, configuration, etc)

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • I added a line to the CHANGELOG.md file in the Unreleased section under the most fitting heading (e.g. Added, Changed, Removed).
  • I ran all tests on my computer and it's all green (i.e. invoke test).
  • I ran lint on my computer and there are no errors (i.e. invoke lint).
  • I added new functions/classes and made them available on a second-level import, e.g. compas_fab.robots.CollisionMesh.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added necessary documentation (if appropriate)

gonzalocasas and others added 30 commits June 2, 2026 13:43
Adds compas_fab.robots.MotionPlan and PlanStep — a backend-agnostic
container for assembling multi-stage motions (the canonical example is
pick-and-place: approach → contact → state change → retract → transfer
→ contact → state change → retract). The planner return types are
unchanged; MotionPlan is an additive composition layer the user (or a
GH canvas) builds on top.

JointTrajectory gains an optional start_state attribute that carries
the full RobotCellState the planner was given. Backends (MoveIt,
PyBullet, analytical Cartesian) populate it. start_configuration
becomes a property: explicit setter still works, otherwise it derives
from start_state.robot_configuration. This is a breaking ctor change
— start_configuration is no longer a constructor parameter (drop the
kwarg and assign via the property instead). New ctor signature:
(trajectory_points, joint_names, start_state, fraction, attributes).

MotionPlan key features:
* Linear chain of named PlanSteps; trajectory steps derive their
  post-state automatically (last point applied), state-change steps
  carry an explicit post-state.
* Fluent chaining: each append_* returns self.
* Name-based lookup (step_by_name); no index API by design, so a future
  tree extension stays non-breaking.
* Optional robot_cell at construction stores a SHA-256 signature
  (robot name + sorted tool/body ids); verify_cell() fails loudly on a
  mismatched cell when loading.
* Serialisation strips trajectory.start_state inside the plan (the
  chain owns it) and omits the derived post_state of trajectory steps
  — only the plan's start_state and explicit state-change post-states
  hit disk.

Bundled fixes that flow from the same work:
* MoveItPlanMotion / MoveItPlanCartesianMotion: unwrap RosValidation-
  Error so typed planner errors (MPNoPlanFoundError, etc.) reach the
  caller as documented, instead of being masked by the validation
  wrapper. Also fixed a local-variable shadowing bug in the cartesian
  branch (start_state was overwritten with a ROS RobotState message
  for the request payload, then captured at the wrong type by the
  response handler).
* convert_trajectory_points (MoveIt) and analytical Cartesian planner
  now attach the parent trajectory's joint_names to each
  JointTrajectoryPoint at construction — the ROS message spec only
  carries names on the parent, so per-point name access was silently
  empty.
* PyBullet plan_cartesian_motion drops the redundant
  start_configuration kwarg now that start_state covers it.

Grasshopper components (CPython userobjects, COMPAS FAB / Planning):
* Cf_TrajectoryStep, Cf_StateChangeStep — wrap a JointTrajectory or a
  RobotCellState into a named PlanStep next to the data it labels.
* Cf_MotionPlan — assembles a MotionPlan from an ordered list of
  PlanSteps; emits the plan plus composite EE planes (DataTree, one
  branch per trajectory step) and per-step polylines (list).
* Cf_DeconstructTrajectory now derives cell_states from
  trajectory.start_state (the start_state input was dropped), and adds
  velocities / accelerations / efforts DataTree outputs for plotting
  the motion profile.
* Cf_PlanMotion / Cf_PlanCartesianMotion drop the redundant `error`
  output (failures flag the component red via gh_error instead) and
  emit `planes` + `polyline` outputs computed via the new local-FK
  helper.
* trajectory_to_planes_and_polyline (compas_fab.ghpython) takes
  robot_cell directly instead of digging through planner.client; uses
  RobotCell.forward_kinematics_target_frame in-process so a 30-point
  trajectory resolves in low-single-digit ms instead of a per-point
  rosbridge round trip. Polyline is built in compas and converted via
  polyline_to_rhino.

Docs:
* New "Assembling multi-stage motions" section in docs/concepts.md.
* New docs/developer/motion_plan.md — records what was deferred
  (tree / branching), the three forward-compat hooks the current API
  provides (iteration via methods, name-based lookup, alternatives
  sibling key on serialization), and open questions for vNext.
* Runnable end-to-end examples for both PyBullet and ROS at
  docs/backends/{pybullet,ros}/files/08_motion_plan_pick_and_place.py
  with signposts from the respective backend pages.

Tests: 12 new motion-plan tests + 2 new trajectory tests
(test_start_configuration_derived_from_start_state,
test_start_state_roundtrips_through_serialization). 85/85 robots
tests pass, ruff clean, mkdocs --strict clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… scrubbing

Adds MotionPlan.iter_cell_states(), a generator that yields one
RobotCellState per trajectory point + one per state-change step across
the entire realized path. Snapshots are independent copies (mutating
one does not affect the plan or other snapshots).

Cf_MotionPlan now exposes a `cell_states` output (flat list, slotted
between `polyline` and `duration`). The intended wiring is:

    cell_states -> List Item <- index slider -> VisualizeRobotCell

so a single slider scrubs the whole pick-and-place end-to-end, not
just one trajectory. State-change steps contribute one frame each,
representing the instant of the change (gripper closed, tool
attached, etc.).

Tests: 2 new (covers every point + state-change is included, empty
plan yields nothing). 87/87 robots tests pass, ruff clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…lient to ROS subcategory

* Collision-check option key unified to `check_collision` (the name
  the PyBullet and analytical backends already use). MoveIt's IK
  feature now reads `check_collision` (default True = collision-free
  solutions only) and accepts the legacy `allow_collision` key as a
  backward-compat alias when `check_collision` is unset. No call
  sites broke. The existing `04_ik_allow_collision.py` example now
  demonstrates the canonical name (`check_collision: False` to
  bypass) while the integration test keeps validating the alias.

* Cf_InverseKinematics gains a `check_collision` boolean input that
  passes straight to the backend with no UI-layer translation —
  since all backends now agree on the name, a single toggle works
  against MoveIt, PyBullet, analytical, and analytical+PyBullet.

* New Cf_ToolFromMesh component (Robot Cell subcategory) mirroring
  Cf_RigidBodyFromMesh: takes a visual mesh + TCP plane (Rhino or
  COMPAS Frame), an optional collision mesh, and an optional name;
  emits a `compas_robots.ToolModel` ready for AddToolToCell /
  AddAndAttachTool. Closes the gap noted while reviewing components
  for compactable flows.

* Moved Cf_RosClient from the Backends subcategory to ROS — it's a
  client, not a backend on its own.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both Cf_AddAndAttachTool and Cf_AttachToolToRobot had local copies
of a `_default_touch_links` helper that returned `[ee_link]` plus,
when the EE was geometry-less, its single parent link. On UR robots
(`tool0` → `flange` → `wrist_3_link`) the parent was *also*
geometry-less, so the result was `[tool0, flange]` — two frame-only
links that MoveIt has no collision geometry for. Listing them as
touch links is meaningless.

* New `RobotCell.default_touch_links(group)` method: walks the
  parent chain from the end-effector link, skips geometry-less
  mounting links, and returns the first link with visual or
  collision geometry — the one the tool actually sits flush
  against. Geometry-less links are no longer added to the
  result. Returns `[]` in the degenerate case where no ancestor
  has geometry.

* Both GH components drop their private `_default_touch_links`
  copies and call `robot_cell.default_touch_links(group)` directly.

* New test `test_default_touch_links_returns_first_geometry_bearing_link`
  pins UR5/UR10e to `['wrist_3_link']`, ABB IRB4600 to `['link_6']`,
  Panda to `['panda_hand']`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`MoveItPlanner.robot_cell` previously raised AttributeError because
only `AnalyticalKinematicsPlanner` defined the property locally (as
a passthrough to its client). Callers like the `Cf_InverseKinematics`
fallback path that derive a zero-config start_state from the planner's
cell were therefore broken against the MoveIt backend.

Lifted the two properties to `PlannerInterface` itself, where they
delegate to `self._client.robot_cell` / `self._client.robot_cell_state`.
Safe when `_client` is None (returns None). Every concrete backend
planner (MoveIt, PyBullet, AnalyticalKinematics, AnalyticalPyBullet)
inherits the same accessor for free; the local override in
`AnalyticalKinematicsPlanner` becomes redundant and is removed.

Also folds in a test rename that finishes the earlier check_collision
migration: `test_04_inverse_kinematics_allow_collision_example` →
`test_04_inverse_kinematics_check_collision_example`, with the option
flipped to `check_collision: False` so the ROS integration test now
exercises the canonical key instead of the legacy alias.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Collapse the planned/unplanned PlanStep split into a single Action class
wrapping start_state + optional trajectory + optional explicit post_state.
A trajectory makes it planned (post-state derived); no trajectory makes it
an unplanned/state-change action (post-state explicit). Single-class model
means backtracking just clears the trajectory instead of swapping types.

Action carries a free-form attributes bag with tags as the default,
first-class key to drive differentiated downstream execution.

MotionPlan -> ActionChain: same state-threading and serialization
optimization (strip the duplicated start_state, re-thread on load), now
mirroring Action.start_state onto the contained trajectory.start_state.
Surface renamed: actions / iter_actions / action_by_name / append_action.

MotionPlan/PlanStep were unreleased, so this is a clean rename with no
back-compat aliases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cf_TrajectoryStep -> Cf_TrajectoryAction, Cf_StateChangeStep ->
Cf_StateChangeAction (both now emit an Action and accept an optional
comma-separated `tags` input that lands on the action's attributes),
Cf_MotionPlan -> Cf_ActionChain (takes `actions`, emits `chain`).

Component code.py/metadata.json updated to the Action/ActionChain API;
icons carried over unchanged. Userobject regeneration is a separate step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename developer/motion_plan.md -> developer/action.md and rewrite it;
update concepts.md, developer/index.md, backends/{ros,pybullet}.md, the
two 08_*pick_and_place example scripts, the icon-system catalog labels,
and mkdocs nav. Fold the rename + new tags feature into the existing
Unreleased CHANGELOG bullets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR #459 targets the prep-release integration branch, not main, so the
build workflow (and its build-cpython-components artifact job) was not
running. Add prep-release to the push/pull_request branch filters so the
Grasshopper userobjects are built and uploaded as an artifact for this PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finish the Action rename in the icon catalog (glyph keys trajectoryStep
-> trajectoryAction, stateChangeStep -> stateChangeAction, motionPlan ->
actionChain). Redesign the "from library" glyphs around a stack-of-books
motif (cell/tool/body), add a dedicated toolFromMesh glyph, and re-render
the affected component icon.png files. Bump the catalog count to 37.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants