Skip to content

Tool mounting - #472

Open
gonzalocasas wants to merge 9 commits into
mainfrom
tool-mounting-convention
Open

Tool mounting#472
gonzalocasas wants to merge 9 commits into
mainfrom
tool-mounting-convention

Conversation

@gonzalocasas

@gonzalocasas gonzalocasas commented Aug 3, 2026

Copy link
Copy Markdown
Member

Following up on compas-dev/compas_robots#33, this PR uses the newly added base_frame for tool models, to correctly mount tools onto robot models. I've also introduced one extra param to the analytical solvers, to allow them to match urdf definition of mount points.

Since the test matrix is large (multiple backends, multiple robot cells, at least two conventions for defining tools either along X+ or along Z+), I added a script and corresponding github action, that will ran them all, and generate GLTF files for the resulting cells with a tool mounted on them. This basically verifies that the change is consistent across: pybullet/analytical solver/ros - tool x+/tool z+ - URs/ABBs robots

Here's a screenshot of the results of this script for the UR5+ROS, with two tool orientations (X+ and Z+), both of them are identically mounted and that's a good thing!

image image

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 7 commits July 31, 2026 16:25
The tools of the ToolLibrary were modelled reaching along their own +X,
following REP 199, while every planning group of the RobotCellLibrary ends
at a link whose +Z points away from the arm — `tool0` on the industrial
robots, `panda_hand_tcp` on the Panda. The two conventions disagreed, so
each cell carried the same rotation in its attachment frame to bridge
them, and a tool authored against one robot did not necessarily fit
another.

Adopt the z+ convention for our tools, so a tool mounts on any robot of
the library with an identity attachment frame. The tools are still
modelled along +X internally and re-framed on the way out through
`ToolModel.reframe_base`, which keeps the change to one line per tool and
leaves the geometry untouched.

The TCF follows the same convention: its Z axis states the direction the
tool works in, so a `TargetMode.TOOL` target aligns the tool along the
target's Z. Re-framing alone would have left the TCF's X axis running
along the tool, which mounts the tool correctly but sends every tool-mode
target off at 90 degrees. The printing tool is the exception, its nozzle
points across the mounting axis, so its TCF is carried along with the
geometry to keep pointing out of the nozzle.

The beams the gripper cells hold are authored in TCF coordinates, so they
were re-authored to match the rotated TCF and stay where they were.

Nothing moves: the attached tools, their TCP positions and the grasped
beams were compared against main and land in exactly the same place, down
to the vertex. What changes is the orientation of the tool's base frame
and of its TCF, which is the point.

Note the `flange` link that some robot models ship is the opposite
convention (REP 199 puts x+ away from the last link). Since only some
models have one, picking it as the end effector link would leave the
library with two mounting conventions depending on the robot, which is
exactly what this commit removes; `get_end_effector_link_name` documents
that.

Requires the `reframe_base` support of the next compas_robots release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`MoveItInverseKinematics` sent the target pose without an `ik_link_name`,
letting MoveIt interpret it at the default tip link of the planning group.
That tip is the end effector link compas_fab computes the Planner
Coordinate Frame at for every robot in the library, but nothing guarantees
it for a group configured with a different tip, and the failure is silent:
the IK result comes back rotated.

Add the field to `PositionIKRequest` and always state the link, defaulting
to the group's end effector link and overridable with the new `"link"`
option, mirroring `plan_cartesian_motion`. Verified against a live ROS 2
MoveIt stack: IK round-trips through model FK at the requested link.

Also use `get_end_effector_link_name` instead of `get_link_names(group)[-1]`
in the PyBullet cell state feature, same result, explicit intent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A tool built from a Rhino mesh had to be modelled in the tool's own base
frame, because that frame was hardcoded to the origin: the mesh
coordinates *were* the base frame. Draw a gripper reaching along X and it
mounts sideways, with nothing to say so.

Add a `base_plane` input to `Tool From Mesh`: the plane, in the
coordinates you modelled in, at which the robot's flange takes hold of the
geometry. Its Z axis points away from the robot, matching the tools of the
ToolLibrary, so a tool drawn reaching along world Z needs no plane at all
and one drawn along another axis is mounted by wiring a plane rather than
redrawing. Backed by `base_frame` of compas_robots' ToolModel, which folds
it into the link origins instead of baking it into the mesh, so it can be
re-wired at any time. It is only passed when wired, so an older
compas_robots keeps working for definitions that don't use it.

The component also remarks 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
the roll of the tool, and a gripper with a lateral TCP offset would come
out subtly tilted rather than obviously wrong — so it is reported rather
than applied.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The UR solvers' DH chain ends at the robot's flange, whose x+ points away
from the last link, while the planning group ends at `tool0`, whose z+
does. The two are a fixed rotation apart, so the analytical planner and
the URDF model disagreed about the orientation of the end effector: a
target handed to the planner was interpreted rotated, and the robot it
drew never reached the frame it had been asked for. Tool and workpiece
modes compounded it, since the tool offset was then applied along the
wrong axis.

`AnalyticalKinematics` already documented `flange_frame` as the way to
state that offset — "should match the end effector of the robot in the
URDF model" — but nothing ever applied it, and no solver set one. Apply it
in the analytical forward and inverse kinematics, and declare it once on
`OffsetWristKinematics`, which covers the whole UR family. The spherical
wrist solvers need no correction, their orientation already matches.

Analytical FK now agrees with the robot model to 1e-10, and matches the
zero-configuration frame the PyBullet tests have always expected, which it
never did before. A TargetMode.TOOL round trip reaches its target exactly.

The three tests that broke had the uncorrected frame baked into them. The
forward kinematics expectation is updated and cross-checked against the
robot model; the two inverse kinematics tests now assert that each
solution actually reaches the target, read back through the model, rather
than pinning joint values that encode a convention.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s, solvers and tool definitions to test tool mounting
@gonzalocasas

Copy link
Copy Markdown
Member Author

You can review all of the generate cell snapshots (in GLB/GLTF format) if you click on the robot-cell-snapshots check (or click this link if you're lazy: https://github.com/compas-dev/compas_fab/actions/runs/30831053111?pr=472)

@gonzalocasas

Copy link
Copy Markdown
Member Author

/cc @jf---

@gonzalocasas

Copy link
Copy Markdown
Member Author

Obivously, the dependency to compas_robots branch will be changed to the released version once the other branch is merged and released ;)

gonzalocasas and others added 2 commits August 4, 2026 15:57
Three changes in this release alter results rather than the API, and one
of them - a leftover compensating attachment_frame now rotating the tool
twice - fails silently. A Migrating section at the top of the release
says what to remove, what to re-author and what to regenerate, and the
docstring of set_tool_attached_to_group points at it from the parameter
that carries the break.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Follows the requirement pinned in the previous commit; both revert when
compas_robots 1.1 is on PyPI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gonzalocasas

Copy link
Copy Markdown
Member Author

@ericgozzi @paulocinco if you guys have time to take a look at and/or test this change (and the related compas_robots PR), I would appreciate!!

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.

1 participant