Skip to content

COMPAS FAB 2.0 release#458

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

COMPAS FAB 2.0 release#458
gonzalocasas wants to merge 446 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)

@jf---
Copy link
Copy Markdown

jf--- commented May 19, 2026

holy mackerel! congrats @yck011522 & @gonzalocasas.
epic!

gonzalocasas and others added 5 commits May 25, 2026 14:50
…o CI

Migration to Rhino 8 CPython-only Grasshopper components. The IronPython
components folder is removed entirely, the bumpversion glob and ruff exclusion
follow the cpython folder, and the rhino/ghpython install hooks (no longer
needed under the Rhino 8 yak deployment model) are removed.

build.yml gains a CPython component build job that uploads the resulting
.ghuser files as an artifact for testing without needing a local build.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…g (Phase 1)

The old components targeted the pre-stateless Robot.plan_motion(...) API and
no longer match the current data model (RobotCell + RobotCellState +
Planner.plan_motion(target, start_state)). This commit lands the foundation
needed for an offline analytical-IK demo path that requires no ROS.

New components:
  - Cf_LoadRobotCellFromLibrary  (Robot Cell)
  - Cf_DefaultCellState          (Cell State)
  - Cf_AnalyticalKinematicsPlanner (Backends)
  - Cf_FrameTarget               (Targets, replaces old Cf_FrameTargetFromPlane)
  - Cf_PointAxisTarget           (Targets, ported from IronPython)
  - Cf_ConfigurationTarget       (Targets, ported from IronPython)
  - Cf_InverseKinematics         (Planning, rewritten against the stateless API)
  - Cf_ForwardKinematics         (Planning, rewritten with target_mode + Plane output)
  - Cf_VisualizeRobotCell        (Display, uses cached RobotCellObject)

Removed (depended on Robot.* or pre-RobotCell APIs):
  Cf_AttachTool, Cf_AttachedCollisionMesh, Cf_CollisionMesh, Cf_ConfigMerge,
  Cf_ConfigZero, Cf_ConstraintsFromConfiguration, Cf_ConstraintsFromPlane,
  Cf_PlanCartesianMotion, Cf_PlanMotion, Cf_PlanningScene, Cf_RosRobot,
  Cf_VisualizeRobot, Cf_VisualizeTrajectory.

Placeholder icons are recycled from removed components; proper artwork to
follow. Subsequent phases will add the ROS path, cell-construction builders,
PyBullet planners, and trajectory playback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the ROS path so users can plan against a live MoveIt backend from
Grasshopper. The RosClient handles both ROS 1 and ROS 2 transparently.

New components:
  - Cf_RosClient            (Backends, replaces Cf_RosConnect; exposes ros_distro)
  - Cf_MoveItPlanner        (Backends)
  - Cf_LoadRobotCellFromRos (Robot Cell)
  - Cf_PlanMotion           (Planning, planner-agnostic)
  - Cf_PlanCartesianMotion  (Planning, planner-agnostic)

Cf_RosConnect is removed in favor of Cf_RosClient; behavior is the same
plus the auto-detected ROS distro on output.

Planning components cache results in sticky and gate execution on a `compute`
toggle so the canvas does not re-plan on every refresh.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gonzalocasas and others added 6 commits May 26, 2026 22:43
The CPython Grasshopper componentizer (componentize_cpy.py) imports clr
directly, which requires pythonnet. The Phase 1 build job was failing
with ModuleNotFoundError.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add Cf_SetRobotConfiguration to bridge IK output back into a RobotCellState
  for visualization. Closes the IK loop in the offline demo path; was a
  missing piece from Phase 1.
- Drop the `compute` gate on Cf_InverseKinematics. IK is fast enough that
  always-on evaluation is fine, and the gate added wiring noise.
- Drop `typeHintID: "bool"` from `load_geometry` on the Library and ROS
  loaders. CPython GH coerced unwired bool inputs to False, defeating the
  in-code `None -> True` default; without the hint the input stays None
  when unwired and the default kicks in.
- Cf_LoadRobotCellFromRos: surface the detected ROS distro on a new output
  and print a targeted hint when the load fails with HTTP 404 (typical
  symptom of ROS 1 falling back to the ROS 2 HTTP loader when rosapi is
  missing).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove the deep RobotCellState copy from PyBullet set_robot_cell_state and apply the same by-reference behavior in the analytical planner for consistency.

Why: Data.copy() performs a deep serialize/deserialize copy and was a hot-path cost during collision checking (check_collision -> set_robot_cell_state). set_robot_cell_state only reads state fields and does not mutate the passed object; the copy mainly prevented aliasing with client._robot_cell_state.

Side effect: client._robot_cell_state now stores the caller-provided object by reference. Added explicit docstring notes in PyBullet set_robot_cell_state and check_collision, and analytical set_robot_cell_state, advising callers to pass state.copy() when post-call mutation is intended.

Validation performed: - Benchmarked 2000 collision checks with random configurations in PyBullet: ur5 1.449s -> 1.022s (29.5 percent faster); ur5_cone_tool 3.009s -> 1.844s (38.7 percent faster); abb_irb4600_40_255_gripper_one_beam 3.419s -> 1.932s (43.5 percent faster); collision counts unchanged between variants. - Regression tests: 105 passed (tests with --ignore=tests/backends/ros).
The old default sat on top of the rosbridge port used when remapping a
ROS 2 stack to coexist with a ROS 1 stack (rosbridge 9090 + ROS 2 rosbridge
9091). A user pointing the loader at their local ROS 2 stack got HTTP 404s
because the loader was hitting the bridge, not the file server.

9190 stays mnemonic ("near rosbridge") while sitting clear of the 909x
cluster where ROS-adjacent stuff tends to land. Both reference docker
stacks (integration tests and the ros2-ur10e-demo) default the host AND
container-internal port to 9190 so the stack speaks one number end to end.

The ROS 2 rosbridge port (9091) is unchanged — only the file server moved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Unreleased section had grown four ### Changed, two ### Fixed, two
### Added, and two ### Removed blocks as separate prep-release patches
were merged. Collapsed each Keep-a-Changelog category into a single
heading and dropped the custom \"Fixed (examples)\" heading by folding
example fixes into the regular ### Fixed list. No entries added or
removed; pure reorganization.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The official ros:jazzy image is smaller (no desktop metapackage we don't
use — moveit/ur/rosbridge/zenoh are installed explicitly anyway) and
publishes multi-arch images including arm64, which lets Apple Silicon
hosts run the demo natively.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@gonzalocasas gonzalocasas mentioned this pull request May 29, 2026
10 tasks
Credit for contributions in #449
(not merged as-is, but the author is recognized in the project's author list
and CITATION metadata).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gonzalocasas and others added 12 commits May 29, 2026 11:09
Atomic builders that let users construct a RobotCell and RobotCellState
from scratch on the canvas, plus the missing waypoint and Configuration
helpers needed to drive Cartesian motion planning.

New components (Robot Cell):
  - Cf_LoadRobotCellFromUrdfSrdf  (offline URDF/SRDF loader)
  - Cf_RigidBodyFromMesh          (Rhino mesh -> RigidBody)
  - Cf_RigidBodyFromLibrary       (RigidBodyLibrary entries)
  - Cf_ToolFromLibrary            (ToolLibrary entries)
  - Cf_AddToolToCell              (passthrough builder)
  - Cf_AddRigidBodyToCell         (passthrough builder)

New components (Cell State):
  - Cf_AttachToolToRobot          (set_tool_attached_to_group)
  - Cf_AttachRigidBodyToTool      (set_rigid_body_attached_to_tool)
  - Cf_AttachRigidBodyToLink      (set_rigid_body_attached_to_link)
  - Cf_SetRigidBodyFrame          (place static body at WCF frame)
  - Cf_SetTouchLinks              (allowed-collision links)

New components (Targets):
  - Cf_FrameWaypoints             (planes -> FrameWaypoints)
  - Cf_PointAxisWaypoints         (points + axes -> PointAxisWaypoints)
  - Cf_Configuration              (joint values + names + types -> Configuration)

All cell-state builders use deepcopy to keep the input state immutable —
chaining Attach*/Set* components composes cleanly without surprise.

Placeholder icons recycled as before.

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

The Sphinx -> MkDocs Material migration left RST role markup in the
docstrings. This sweep converts the entire compas_fab.robots module
(and its reachability_map subpackage) to mkdocstrings syntax:

  :class:`compas_fab.robots.Foo`    -> [\`Foo\`][compas_fab.robots.Foo]
  :class:`~compas_fab.robots.Foo`   -> [\`Foo\`][compas_fab.robots.Foo]
  :meth:`Foo.bar()`                  -> \`Foo.bar()\` (unqualified - no cross-ref)
  :meth:`compas_fab.robots.Foo.bar` -> [\`Foo.bar\`][compas_fab.robots.Foo.bar]
  :attr:`compas_fab.robots.Foo.bar` -> [\`Foo.bar\`][compas_fab.robots.Foo.bar]
  :obj:`bool/float/str/...`          -> \`bool\`/\`float\`/\`str\`/...
  :exc:`ValueError`                  -> \`ValueError\`
  :ref:`targets`                     -> \`targets\`

Also fixes RST hyperlinks (\`Label <url>\`_) to markdown ([Label](url)),
the singular typo \`compas_fab.robot.plan_motion\` -> \`compas_fab.robots...\`,
and a handful of stale cross-references to the removed \`Robot\` class
(updated to point at \`RobotCell\`).

509 roles converted across 14 files. Net diff is roughly neutral on
line count (-492/+478). All files still parse.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
\`uv run invoke docs\` was emitting 6 warnings from the doc build:

- trajectory.py: \`compas_robots.Joint.TYPE\` / \`.REVOLUTE\` cross-refs.
  Joint lives at \`compas_robots.model.Joint\`, but class-level attributes
  aren't in compas_robots' Sphinx inventory. Switched to plain backticks
  since cross-linking external class attributes isn't supported.
- semantics.py: stale refs to \`PyBulletClient.load_semantics\` and
  \`RosClient.load_robot\` (both methods removed/renamed during the API
  redesign). Replaced with a single working ref to \`RosClient.load_robot_cell\`.
- targets.py: refs to \`compas_fab.robots.plan_motion\` /
  \`plan_cartesian_motion\`. These were never top-level functions; the
  methods live on planner backends. Reworded to "the planner's plan_motion
  method".
- robot_library.py: ref to instance attribute \`RobotCellState.tool_states\`.
  mkdocstrings can't resolve instance attributes set in __init__; kept the
  class link and made \`.tool_states\` a plain trailing access.

Docs build now warning-free.

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

Attaching a tool with no `group` wired in was a silent no-op — confusing for
the common case where the user has a single-group robot (UR, ABB, Panda)
and shouldn't have to look up the group name.

Added an optional `robot_cell` input. When `group` is empty:
  - if `robot_cell` is wired, fall back to `robot_cell.main_group_name`
  - otherwise, raise with a message that points at the fix.

Reported during Phase 3 testing.

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

Swallowing the exception with `print(...) + return None` hid the failure —
on the canvas you got an empty output and a console line that's easy to
miss. Removed the try/except so the exception bubbles up; Grasshopper
will then turn the component red and show the message in its tooltip.

Reported during Phase 3 testing.

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

Replaces silent exceptions and console prints with Grasshopper-native
runtime messages (component balloons), so failures are visible without
needing the user to wire an `error` output or watch the console.

Components updated to use error() instead of raise / print:
  Cf_AddRigidBodyToCell, Cf_AddToolToCell, Cf_AnalyticalKinematicsPlanner,
  Cf_AttachToolToRobot, Cf_Configuration, Cf_InverseKinematics,
  Cf_LoadRobotCellFromLibrary, Cf_LoadRobotCellFromRos,
  Cf_PointAxisWaypoints, Cf_RigidBodyFromLibrary, Cf_ToolFromLibrary.

Components updated to use warning() (partial success) + error() (no result):
  Cf_PlanMotion, Cf_PlanCartesianMotion.

Cf_RosClient: warning() when the previous client fails to close cleanly,
remark() when ROS distro detection fails (informational).

Also adds a `visual_meshes` output to Cf_ToolFromLibrary so users can preview
the tool's geometry (Rhino meshes, drawn in the tool's own base frame) before
attaching it to a robot.

Reported during Phase 3 testing.

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

Adds compas_fab.ghpython.ensure_value_list, a reusable helper that
auto-creates a Grasshopper Value List on an unconnected component input.
Cf_LoadRobotCellFromLibrary now uses it to expose every RobotCellLibrary
entry as a dropdown (default "ur5") instead of a free-text panel.
Loader calls are wrapped in try/except and surfaced via compas_ghpython.error
with the sticky cache cleared on failure, so retries actually retry.

Also fixes ToolLibrary.cone(): it now calls add_link("cone_link", ...) like
ToolLibrary.printing_tool() does. Without the link, the cone's underlying
ToolModel.root tree was malformed and iter_joints() crashed with
"'NoneType' object has no attribute 'joints'". This is what made
RobotCellLibrary.ur5_cone_tool(load_geometry=False) always crash and
manifested in the GH component when toggling load_geometry off while
clicking through robot names.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@jf---
Copy link
Copy Markdown

jf--- commented May 29, 2026

witnessing you pushing HARD Mr @gonzalocasas
one EPIC PR... hang in there ;)

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