Add RTP (Robot with Tool Positioner) Inverse Kinematics Plugin - #1297
Add RTP (Robot with Tool Positioner) Inverse Kinematics Plugin#1297rjoomen wants to merge 6 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1297 +/- ##
==========================================
+ Coverage 94.36% 94.44% +0.08%
==========================================
Files 278 282 +4
Lines 21532 21879 +347
==========================================
+ Hits 20318 20663 +345
- Misses 1214 1216 +2
🚀 New features to boost your workflow:
|
|
hi @rjoomen super interesting... FWIW the Levenberg-Marquardt / ChainIkSolverPos_LMA I think is (quite a bit more) performant over the |
|
From some preliminary benchmarks this is on par with the KDL solvers or Trac-IK for 7 joints (6 axis industrial plus gripper), but the difference is that this returns a lot of solutions. OPW can return up to 8 solutions per point, multiplied by the number of successful 7th joint samples. |
|
This currently does simple linearly spaced sampling for the 7th joint, like ROP does. I'm working on a somewhat smarter sampling solution, so less underlying solver (OPW, in my case) calls are necessary and speed increases. |
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws, factory size and joint-name mismatches, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws, factory size and joint-name mismatches, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an RTP (Robot with Tool Positioner) inverse-kinematics plugin that composes a manipulator IK solver with a tool-side FK chain by sampling the tool joints and solving the manipulator IK at each sample. This fits into the kinematics plugin ecosystem alongside ROP/REP, extending support for manipulators with additional tool joints at the tip.
Changes:
- Introduces
RTPInvKin(core implementation) andRTPInvKinFactory(YAML/plugin factory) plus shared factory parsing helpers. - Adds new kinematics utility helpers (
computeChainReachUpperBound,gatherJointLimits,buildSampleGrid) and corresponding unit tests. - Adds RTP-focused unit tests and an optional benchmark suite comparing RTP vs KDL-NR-JL.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| kinematics/test/rtp_kinematics_unit.cpp | New unit tests covering RTP IK behavior, validation, cloning, factory loading, and edge cases. |
| kinematics/test/kinematics_test_utils.h | Adds ABB scene-graph fixtures with tool positioners and a small helper for building chains in tests. |
| kinematics/test/kinematics_core_unit.cpp | Adds coverage for new reach upper-bound and sampling helper behaviors, plus ctor rejection regression tests. |
| kinematics/test/CMakeLists.txt | Registers the new RTP unit test target. |
| kinematics/test/benchmarks/rtp_vs_kdl_nr_jl_benchmarks.cpp | Adds a benchmark comparing RTP (OPW + sampled tool joint) vs KDL NR-JL across randomized reachable targets. |
| kinematics/test/benchmarks/README.md | Documents how to run/interpret the RTP vs KDL benchmark. |
| kinematics/test/benchmarks/CMakeLists.txt | Adds benchmark build rules for kinematics benchmarks. |
| kinematics/core/src/utils.cpp | Implements new kinematics utility functions used by RTP and tests. |
| kinematics/core/src/rtp_inv_kin.cpp | Implements the RTP inverse kinematics solver. |
| kinematics/core/src/rtp_factory.cpp | Implements the RTP IK plugin factory and YAML parsing/creation logic. |
| kinematics/core/src/factory_utils.cpp | Adds shared YAML parsing helpers for factories (plugin blocks + sample resolution maps). |
| kinematics/core/include/tesseract/kinematics/utils.h | Declares new utility APIs. |
| kinematics/core/include/tesseract/kinematics/rtp_inv_kin.h | Declares the RTP IK solver class and public constructors. |
| kinematics/core/include/tesseract/kinematics/rtp_factory.h | Declares the RTP IK factory. |
| kinematics/core/include/tesseract/kinematics/fwd.h | Adds forward declarations for RTP types. |
| kinematics/core/include/tesseract/kinematics/factory_utils.h | Declares shared factory YAML parsing helpers. |
| kinematics/core/CMakeLists.txt | Wires new sources into the core kinematics libraries and factories target. |
| kinematics/CMakeLists.txt | Adds conditional inclusion of the new kinematics benchmark subdirectory. |
| .github/workflows/clang_format.yml | Fixes quoting for workflow echo output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@rjoomen I'm curious how if the IK of the rotary table and manipulator is solved in unison, kinetically is it considered a single chain? |
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9351a8f to
f10ee41
Compare
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
f10ee41 to
0c1c4bf
Compare
|
@rjoomen wow, should this be added to 0.36? |
Adds unit tests for previously uncovered error paths flagged by codecov on PR tesseract-robotics#1297: RTPInvKin operator=, ctor / init validation throws (including the tool-base "not in scene graph" and "disconnected from manip tip" checks), factory size and joint-name mismatches, calcInvKin early-return when the inner manipulator IK yields no solutions, computeChainReachUpperBound prismatic limit cases, and parseSampleResolutionMap missing-limits. Also drops two redundant null checks in RTPInvKin::init() that every public ctor already pre-validates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9ec6fb5 to
6db6b50
Compare
6db6b50 to
399639c
Compare
3cf77d1 to
ebd81e0
Compare
|
Rebased onto master following the id migration. The string-based version is preserved at rjoomen/tesseract@feat/rtp-inv-kin-strings. No behavioural change — the first four commits are the same work retyped onto LinkId/JointId. |
e051eef to
525082d
Compare
525082d to
eee94e9
Compare
Three new utilities used by RTP/REP/ROP:
- computeChainReachUpperBound: triangle-inequality upper bound on
base-to-tip Cartesian distance, for sizing reach filters in compound
IK solvers.
- gatherJointLimits: lookup joint position limits as a (N,2) matrix.
- buildSampleGrid: uniformly subdivide each row of a (N,2) range using
a per-joint resolution vector.
And test scaffolding:
- addRevoluteChild / getSceneGraphABBWithToolPositioner helpers in
kinematics_test_utils.h.
- 15 new tests covering the three utilities and the addRevoluteChild
helper's defaults, including computeChainReachUpperBound's rejection
of prismatic joints with absent or non-finite limits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
YAML-parsing helpers used by the ROP/REP/RTP factory plugins: parseSampleResolutionMap and parsePluginInfo, with consistent error messages on missing keys / wrong types. Adds tesseract::kinematics_factories as an in-tree ALIAS target. The library was already exported under that name by configure_component, so downstream consumers could link it, but no in-tree target could — which is why these helpers had no direct test coverage. factory_utils_unit.cpp exercises both helpers against a two-link scene graph: joint-limit defaults, min/max overrides, overrides outside the joint limits, inverted ranges, missing name/value/class keys, unknown joint names, and joints whose limits member is null.
RTPInvKin is a robot-tool-positioner compound IK solver: it wraps an
inner manipulator (e.g. KDLInvKinChainLMA) and a tool positioner
(e.g. KDLFwdKinChain), discretises the tool positioner over a sample
grid, and dispatches inner IK at every grid sample.
Public API:
- Four ctor variants: explicit-reach × {sample range from joint
limits, explicit sample range} and the same pair with auto-derived
reach via computeChainReachUpperBound.
- YAML factory plugin (RTPInvKinFactory) with schema validation,
optional manipulator_reach, and ctor-exception catching.
The ctors validate up front:
- manipulator and tool positioner must be non-null,
- manipulator must have exactly one tip,
- tool positioner base must lie on the path between the manipulator
base and tip,
- sample range rows must be lower < upper,
- tool sample resolution sized to match the positioner DOF.
init() deliberately does not re-check the manipulator and tool positioner
for null: every public ctor pre-validates both before delegating, so a
second check there would be unreachable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Unit tests:
- rtp_kinematics_unit.cpp: RTPInvKin metadata, ctor validation matrix,
auto-reach equivalence with explicit reach, single- and multi-sample
roundtrips, clone/KinematicGroup integration, factory YAML round-trip
+ auto-reach + failure-path matrix + bad-reach rejection, multi-tip
rejection, parentage check, multi-joint tool FK correctness, and the
error paths flagged by codecov (operator=, init() validation throws,
factory size and joint-name mismatches, calcInvKin early-return when
the inner manipulator IK yields no solutions).
- kinematics_core_unit.cpp: pin runtime_error type for all four RTP
ctor variants when given a null manipulator (regression guard).
Benchmark (benchmarks/rtp_vs_kdl_benchmarks.cpp + README): throughput
against all three KDLInvKinChain variants (NR_JL, NR, LMA) in WARM and
COLD seeding, across reachable / unreachable workloads, with documented
trade-off analysis.
- Adds an in_limits_success_rate counter so unenforced-limits solvers
(KDLInvKinChainNR, KDLInvKinChainLMA) are scored on planner-usable
successes, not raw convergence. Reuses tesseract::common::satisfiesLimits
via a shared anyInLimits helper.
- Refactors per-benchmark counter emission into CallStats + emitCounters,
keyed by named string constants kept in sync with the printed legend.
- Centralises the joint-limit slack as kJointLimitSlack and applies it
in both anyInLimits and isValidSolution.
- Reports time_per_call / time_per_sol / time_per_valid_success with
rounded rates and a printed legend that maps each counter to a planner
use-case (single-shot, batch sampling, realistic per-IK cost).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
RTPInvKinRejectsToolBaseNotInSceneGraph and RTPInvKinRejectsToolBaseDisconnectedFromManipTip passed without ever reaching the checks in their names: the tool base is required in the scene state before it is looked up in the scene graph, and neither test seeded the state, so both were rejected by that earlier guard. Bare EXPECT_ANY_THROW hid it. Seed the state and pin the message so the tests fail if a different guard fires. Also cover the combined tool-grid cap, which had no test. It is a product across joints, not a per-joint bound, so the new case sizes two rows well inside buildSampleGrid's per-joint limit and lets only their product exceed the cap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015wGQZpw6tNHXUj2WypqdzH
The throw matrix for this function was otherwise complete: missing base link, missing tip link, floating joint, mimic prismatic, prismatic without limits, non-finite limits, and the base == tip short-circuit all have cases. A tip link present in the graph but with no path from the base did not. Both links exist here, so the presence checks pass and the empty joint path is what is rejected; the message assertion is what keeps this distinct from the missing-link case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015wGQZpw6tNHXUj2WypqdzH
eee94e9 to
4ee6b43
Compare
| Eigen::MatrixX2d gatherJointLimits(const tesseract::scene_graph::SceneGraph& scene_graph, | ||
| const std::vector<tesseract::common::JointId>& joint_ids); |
There was a problem hiding this comment.
Do you know if this is duplicated anywhere? I know where have several utilities like this but maybe not exactly or should we move this to the SceneGraph class?
| tesseract::common::PluginInfo parsePluginInfo(const YAML::Node& sub_node, | ||
| const std::string& error_prefix, | ||
| const std::string& block_label) | ||
| { | ||
| try | ||
| { | ||
| return sub_node.as<tesseract::common::PluginInfo>(); | ||
| } | ||
| catch (const std::exception& e) | ||
| { | ||
| // The converter does not know which block of the factory config it was handed, and a factory | ||
| // config typically has several. Name it, otherwise the message cannot be acted on. | ||
| throw std::runtime_error(error_prefix + ", '" + block_label + "': " + e.what()); | ||
| } | ||
| } |
There was a problem hiding this comment.
I think these will be better handled when we update to using the property tree / yaml schema.
Adds
RTPInvKin, a compound inverse-kinematics solver for a robot with a tool positioner —extra actuated joint(s) at the end of the manipulator chain, between the robot flange and the
TCP. It is the mirror image of
ROPInvKin, which places the positioner joints at the base of thechain rather than the tip.
The motivating case is a standard industrial robot with an actuated gripper or tool axis: pair
OPWInvKinfor the 6-DOF arm with a 1-DOF tool joint and get analytic IK over the full 7-DOFchain, instead of falling back to a numerical whole-chain solver like Trac-IK. Besides possible
improved speed, main advantage is that RTP+OPW will return a lot of possible solutions, instead
of only one like Trac-IK and the KDL solvers do.
How it works
RTPInvKinwraps an inner manipulator IK solver and discretises the tool chain onto aLinSpacedsample grid. Construction consumes a tool-side forward-kinematics chain to precompute, per grid
sample, the joint values and the transform taking a tool-tip target to the corresponding
manipulator-tip target.
calcInvKin()is then one transform product per sample, followed by aninner IK dispatch only for those samples whose manipulator-tip target falls within
manipulator_reach— the rest are rejected by a squared-norm test before any IK runs. The FK chainis not retained. All branches from all surviving samples are concatenated.
getTipLinkIds()reports the tool chain tip — the frame the target pose is expressed for.There are four public constructor variants: explicit vs. auto-derived
manipulator_reach, crossedwith sample range taken from the tool joint limits vs. given explicitly. Auto-reach derives the
early-exit filter radius from the manipulator's base→tip chain via
computeChainReachUpperBound.All four are one-line delegations to a private
init()that validates every input and populatesevery member, so there is a single validation path rather than four. It throws
std::runtime_erroron: a null manipulator or tool positioner, an empty solver name, an invalid scene-graph root,
either chain having other than exactly one tip link, a link missing from the scene state, a
non-positive reach, a resolution vector or sample-range row count that does not match the tool
positioner's DOF, and a malformed sample range or resolution (non-finite or inverted bounds, or a
resolution that is not finite and positive).
Two of the checks are worth calling out, because failing them is silent rather than loud:
init()rejects abase that is missing from the scene graph, disconnected from the manipulator tip, or separated
from it by an active joint. That last case is the dangerous one — it is perfectly reachable, so
nothing else would catch it, but sampling the tool chain would no longer produce a deterministic
wrist target and the returned solutions would be quietly wrong.
computeChainReachUpperBoundover the manipulator's base→tip chain, so they additionally throwon anything that makes the bound underivable: a FLOATING or PLANAR joint on the path, or a
prismatic joint that is a mimic or has missing or non-finite limits.
A combined sample-count cap is enforced too.
buildSampleGrid()bounds each tool jointindividually;
init()additionally bounds their product, since that sets both the size of theprecomputed tables and the number of inner IK solves per
calcInvKin()call. It turns amisconfigured discretisation into a diagnosable exception instead of a multi-gigabyte allocation.
Plugin configuration
Build change
Adds a
tesseract::kinematics_factoriesin-tree ALIAS.configure_componentalready exports thelibrary under that name, so this only lets in-tree targets link it by the same name downstream
consumers use, instead of by the bare
kinematics_factoriestarget name.Testing
rtp_kinematics_unit.cpp— 19 tests: metadata, the constructor validation matrix, independencefrom arm configuration, auto-reach equivalence with an explicit reach, single- and multi-sample
roundtrips,
clone()/operator=/KinematicGroupintegration, factory YAML round-tripincluding the auto-reach and failure paths, multi-tip rejection, the tool-base parentage checks
(active joint in between, base absent from the scene graph, base disconnected from the manipulator
tip), multi-joint tool FK correctness and solution counting, and
calcInvKin's early return whenthe inner IK yields nothing.
factory_utils_unit.cpp— 12 tests over all three helpers against a two-link scene graph:parsePluginInfoclass/config handling and its missing-classthrow;parseSampleResolutionMapjoint-limit defaults, min/max overrides, overrides outside the joint limits, missing name/value
keys, unknown joint names, and joints whose
limitsmember is null; andtoSampleGridConfigjoint-count mismatch and output ordering.
kinematics_core_unit.cpp— 18 tests:computeChainReachUpperBoundacrossrevolute/prismatic/continuous/branched chains and its rejection of floating, mimic, and unlimited or
non-finite prismatic joints;
gatherJointLimits;buildSampleGridincluding malformed input andthe sample-count cap; the
runtime_errortype for all four RTP constructor variants given a nullmanipulator; and a regression guard on the shared test helper's default joint limits.
Benchmark
tesseract_kinematics_rtp_vs_kdl_benchmarkcomparesRTPInvKin(OPW 6-DOF manipulator + 1-DOFrevolute tool positioner) against all three
KDLInvKinChainsolvers on the same 7-DOF ABB IRB2400in a
WARMand aCOLDseeding variant.The solvers produce fundamentally different solution sets — RTP enumerates every manipulator branch
at every tool sample, the KDL solvers return at most one seed-dependent solution — so no single
number decides it. Reachability dominates the comparison, which is why every solver runs over
two workloads,
REACHABLEandUNREACHABLE, and no row can be mistaken for a solver's generalcost.
Representative
time_per_call, 256 targets, 3 repetitions, RelWithDebInfo on one machine — treat asindicative, not as a spec:
KDLInvKinChainNR_JLWARMKDLInvKinChainNR_JLCOLDKDLInvKinChainNRWARMKDLInvKinChainNRCOLDKDLInvKinChainLMAWARMKDLInvKinChainLMACOLDOn reachable targets the ordering depends on the tool resolution: at 200 mrad RTP is faster per call
than every KDL row, at 100 mrad it is level with the warm-seeded ones, and only at 50 mrad do warm
NR / NR_JL overtake it — while still returning a single solution against RTP's ~950. Per solution
RTP is ~70 ns at every resolution, against 35 µs to 1 ms for KDL.
Against cold seeds RTP is ahead everywhere, and on unreachable targets the gap is three to four
orders of magnitude: RTP rejects a sample with a squared-norm test, while the KDL solvers iterate to
their cap before reporting failure. A planner that queries near or beyond the workspace boundary
should weight the
UNREACHABLErows accordingly.KDLInvKinChainNRandKDLInvKinChainLMAadditionally do not enforce joint limits and can convergeto configurations a planner has to discard, so raw and in-limits convergence are scored separately
via an
in_limits_success_ratecounter. The gap is not academic: cold-seededKDLInvKinChainNRconverges on 97% of reachable targets but only 18% of those results are within limits.
RTP is seeded with ground truth deliberately — its inner OPW solver is closed-form and ignores the
seed — and targets are generated deterministically by FK roundtrip from a seeded RNG, so reruns are
reproducible.
The binary prints a legend after the results table explaining every counter and how to pick a
solver from them. That legend sits next to the code emitting the counters and is the authoritative
description; the README deliberately does not duplicate the counter names.
Follow-up
The
utilsandfactory_utilshelpers added here are deliberately generic: they are written toserve all three compound IK solvers, not RTP alone.
REPInvKinandROPInvKincurrently carrytheir own near-identical copies of the same joint-limit lookup, sample-grid construction and YAML
parsing.
Six helpers are shared:
computeChainReachUpperBound,gatherJointLimitsandbuildSampleGridfrom
utils.h, andparsePluginInfo,parseSampleResolutionMapandtoSampleGridConfigfromfactory_utils.h.Migrating REP and ROP onto them is a follow-up PR, kept separate so this one stays reviewable and
the deduplication can be judged on its own merits. That work is already written and will follow once
this lands.