COMPAS FAB 2.0 release#458
Open
gonzalocasas wants to merge 457 commits into
Open
Conversation
…ollision handling
…ultiple solutions and trajectory replay
…ions, and updating API references
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>
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>
4 tasks
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>
|
witnessing you pushing HARD Mr @gonzalocasas |
ensure_value_list wired the new GH_ValueList to the input during the current solve, but the input's value collection had already happened, so the dropdown's selection didn't reach the component until the user manually re-triggered the solver. Calling doc.ScheduleSolution(5) immediately after AddSource queues a fresh solve so the value flows on the very first drop with no user intervention. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds compas_fab.ghpython.ensure_joint_sliders, a helper that on first solve (or whenever the wired robot's joint signature changes) registers one Param_Number input per configurable joint and auto-creates a GH_NumberSlider feeding each input, with min/max set to the joint's URDF limits (±2π fallback for continuous joints, ±1m for prismatic with no declared limit). Joint signature is tracked in sticky so swapping robot_cell rebuilds the bank rather than appending to it; obsolete sliders are removed from the document on rebuild. doc.ScheduleSolution(5) is used so values flow on the first drop. The new Cf_RobotConfiguration component (subcategory Targets) takes one robot_cell input, returns a Configuration with joint_names and joint_types populated from the cell — the ergonomic counterpart to the existing Cf_Configuration, which stays as the manual-list escape hatch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The CPython userobjects build (invoke build-cpython-ghuser-components, run in CI by the build-cpython-components job) requires icon.png in every component folder; without it the build fails. Used Cf_Configuration's icon as a placeholder since the two components are siblings in subcategory Targets — can be swapped for a dedicated icon later. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
integration.yml brings up two docker compose stacks (ROS 1 on 9090, ROS 2
on 9091) with `up -d --build` and immediately runs pytest. On cold-cache
runs the rosbridge containers aren't accepting websocket connections yet
when `--doctest-modules` reaches `RosClient`'s `>>> with RosClient()`
example at client.py:162, and roslibpy's 10s connection timeout fires
with `RosTimeoutError('Failed to connect to ROS')`. The dedicated
tests/backends/ros/test_ros_client.py cases pass because they run later
in the session when the bridge is warm.
Added a `Wait for rosbridge endpoints` step that TCP-probes ports 9090
and 9091 on localhost for up to 120s before pytest. 120s is well above
the worst observed cold-start; hitting it means the container itself is
unhealthy, in which case `docker ps -a` output is dumped before exit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This reverts commit 344084c.
…ation Temporarily point requirements.txt at the lifecycle-fixes branch on gramaziokohler/roslibpy to exercise the 2.1 candidates (singleton twisted log observer, Ros.close() blocking until clientConnectionLost) against the integration workflow. If the RosClient doctest at client.py:162 stops flaking under --doctest-modules, the upstream roslibpy fixes are doing their job and we revert this pin back to roslibpy >= 2.0, < 3 once 2.1 is released. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The two >>> with RosClient() examples in compas_fab.backends.ros.client
(RosClient class docstring at line 162; RosClient.load_robot_cell at
line 257) keep flaking under pytest --doctest-modules in the integration
workflow with `RosTimeoutError('Failed to connect to ROS')` — always on
the FIRST one, with the second succeeding milliseconds later. Marking
both # doctest: +SKIP eliminates the flake; the actual code path is
exercised by tests/backends/ros/test_ros_client.py (9 cases under the
module-scoped ros1_client fixture, never observed to flake), so the
doctests are only losing their role as a self-verifying illustration.
Also reverts the temporary `roslibpy @ git+...#lifecycle-fixes` pin
back to `roslibpy >= 2.0, < 3`. The pin was added to validate two
candidate roslibpy 2.1 fixes (singleton log observer, close() blocking
until clientConnectionLost) against this exact symptom — the
integration run with that branch failed identically, falsifying the
hypothesis that this flake was caused by either of those bugs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…yncio Pin roslibpy to the websockets-asyncio branch (new asyncio/websockets transport) and opt the integration job into it via ROSLIBPY_TRANSPORT. Drop the two `# doctest: +SKIP` markers on the RosClient examples so we actually exercise the live path again. Validation only — not for merge as-is. Reverts the temporary +SKIP from 2cc6602 and the lifecycle-fixes pin from b64d020. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previous design registered one dynamic Param_Number per joint and tried to read slider values via `param.VolatileData`. Dynamic input params on a `GH_ScriptInstance` don't get `CollectData()` called by the parent component's solve, so the values never flowed through and the resulting Configuration was always the per-joint defaults regardless of slider position. Redesigned so the component declares a single static `joints` list-access input; `ensure_joint_sliders` creates one `GH_NumberSlider` per configurable joint and wires them all to that input in canonical order. GH collects the values via the standard solve path and the script reads `joints` as a plain Python list. No more VolatileData or sticky-tracked GUID lookups. Also retires Cf_Configuration — once Cf_RobotConfiguration works from a RobotCell with auto-sliders and auto names/types, hand-typing joint names + integer joint-type enums in a separate component is just busywork. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`joints` is a list-access input, so its arg must be annotated as `System.Collections.Generic.List[float]` for GH to collect it correctly (same pattern as Cf_PointAxisWaypoints). Also dropped the redundant `joints[: len(metadata)]` slice — the upstream wiring already guarantees the lengths match, and the fallback branch above handles the only case where they wouldn't. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ally work Four Rhino 8 CPython quirks were causing the auto-slider helper to either no-op silently or accumulate dozens of duplicate sliders per robot switch. Each one is its own .NET/Python interop gotcha: 1. `isinstance(obj, Grasshopper.Kernel.Special.GH_NumberSlider)` returns False for actual slider instances in Rhino 8 CPython (the .NET interop surfaces `IGH_DocumentObject` as the Python type). Replaced with `_is_number_slider(obj)` using `obj.GetType().Name == "GH_NumberSlider"`. Without this, neither `_strip_slider_sources` nor the `has_any_slider_source` check ever recognised a slider, so every solve re-entered the rebuild branch and added 8 more sliders. 2. `IGH_Param.RemoveSource(IGH_Param)` is a silent no-op for our case (probably marshalling); `RemoveSource(Guid)` works. Strip now uses the Guid overload — verified going from 72 sources to 0 in one pass. 3. `component.ExpireSolution(False)` called inside our own SolveInstance is silently reset when the solve unwinds, so the deferred `ScheduleSolution(5)` had nothing to re-solve. Switched to the `ScheduleSolution(delay, callback)` overload where the callback fires right before the deferred solve and expires the component then. Without this, the first solve created the sliders but their values only flowed after the user manually pressed Play. 4. `doc.AddObject` and `Params.OnParametersChanged` during the rebuild trigger nested solves of this same component. Each nested call saw the still-stale sticky signature and re-entered the rebuild branch, adding N sliders per joint per nesting level (6 × 9 = 54 sliders after one robot switch). Added a `__busy` sticky flag as a re-entrance guard. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR superceedes #456 and contains the entire Project Theseus + ROS 2 / MoveIt 2 support.
What type of change is this?
Checklist
Put an
xin 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.CHANGELOG.mdfile in theUnreleasedsection under the most fitting heading (e.g.Added,Changed,Removed).invoke test).invoke lint).compas_fab.robots.CollisionMesh.