Skip to content

Add per-point Yoshikawa manipulability to workspace analysis - #614

Open
Yuan-Xinyi wants to merge 4 commits into
perf/workspace-batchingfrom
xinyi/workspace01
Open

Add per-point Yoshikawa manipulability to workspace analysis#614
Yuan-Xinyi wants to merge 4 commits into
perf/workspace-batchingfrom
xinyi/workspace01

Conversation

@Yuan-Xinyi

Copy link
Copy Markdown
Collaborator

Stack

Description

This PR wires true per-point Yoshikawa manipulability into workspace analysis and documents a measured, configuration-only speedup of Cartesian/plane reachability analysis via the seed-selection sampler from #599.

1. Per-point manipulability (new)

The metrics/ subpackage was never invoked by the analyzer (_compute_metrics carried a TODO), and ManipulabilityMetric without Jacobians fell back to a centroid-distance placeholder. We measured that placeholder against ground truth on Franka (470 reachable points):

placeholder true Yoshikawa
mean 0.4196 0.0644
correlation with truth −0.366 1.0

The placeholder is not merely inaccurate — it is anti-correlated with true manipulability, so any consumer ranking by it preferred worse configurations. This PR:

  • computes w = sqrt(det(J J^T)) from the active solver's Jacobian over the stored joint_configurations after analysis (all three modes), row-aligned with the configurations and, in Cartesian/plane modes, with the reachable points;
  • stores manipulability_scores in the results dict and results.npz, restores it on cache hits, and reports aggregates under metrics["manipulability"] (closing the _compute_metrics TODO via the metrics module);
  • gates computation on MetricConfig.enabled_metrics (default ALL — on);
  • removes the placeholder: ManipulabilityMetric without Jacobians or precomputed scores now warns and returns no statistics — fabricated numbers are worse than none;
  • cost is negligible: batched Jacobian + determinant is ~10.5 ms for 470 configurations (GPU), well under 0.5% of the analysis.

Downstream, the aligned scores enable score-weighted runtime sampling (RobotWorkspace already supports weights) and manipulability-aware seed re-ranking as follow-ups; both are out of scope here.

2. Seed-selection speedup for Cartesian/plane analysis (documentation + measurement)

Cartesian/plane reachability runs through the solver's multi-start get_ik, where each analyzer seed occupies slot 0 of the solver-internal multi-start — so PytorchSolverCfg.enable_seed_selection (#599) applies with zero analyzer changes. Measured on Franka, 4000 identical sampled points (fixed RNG), warm timings (compile excluded), 1 seed/point:

solver config reachable detected wall time speedup
num_samples=30, random (default) 470 (11.8%) 3.3 s
enable_seed_selection, num_samples=30 475 (11.9%) 1.0 s 3.3×
enable_seed_selection, num_samples=8 466 (11.7%) 0.8 s 4.1×
enable_seed_selection, num_samples=4 460 (11.5%) 0.5 s 6.6×

At unchanged num_samples=30 the seeded configuration is strictly better: more reachable points detected and 3.3× faster (good seeds converge in fewer DLS iterations under early_stopping_any_converged). The recipe and numbers are recorded in the robot-workspace context docs; analytic solvers (OPW/SRS/UR) are unaffected.

Dependencies: none beyond #606.

Type of change

  • Enhancement (non-breaking change which improves an existing functionality)

Screenshots

N/A

Checklist

  • I have run the black . command to format the code base.
  • I have made corresponding changes to the documentation (agent_context robot-workspace topic: manipulability contract, seed-selection recipe; MAP keywords)
  • Public API changes are reflected in the API docs (python docs/scripts/check_api_docs.py: 1853/1853)
  • I have added tests that prove my feature works (tests/sim/motion/workspace/test_manipulability.py: exact Yoshikawa on synthetic Jacobians, precomputed-score precedence, no-fabricated-statistics guard, end-to-end alignment on CobotMagic, metric gating, cache serialization round-trip)
  • Dependencies have been updated, if applicable (none)

Validation

pytest tests/sim/motion/workspace/test_manipulability.py  -> 7 passed
pytest tests/sim/motion/workspace/                        -> full suite (regression)
python docs/scripts/check_api_docs.py                     -> 1853/1853
black . / context.py check / agent-context map tests      -> clean

Note: one behavioural change is intentional — ManipulabilityMetric.compute() without Jacobians/scores now returns {} with a warning instead of placeholder statistics. Given the measured anti-correlation, silent consumers of the old numbers were being misled; failing loudly is the safer contract.

Wire the metrics module into WorkspaceAnalyzer (closing the long-standing
_compute_metrics TODO) and compute true per-configuration Yoshikawa
manipulability w = sqrt(det(J J^T)) from the active solver's Jacobian after
every analysis mode. Scores are row-aligned with joint_configurations (and
with reachable points in Cartesian/plane modes), stored in results.npz,
restored on cache hits, and aggregated under metrics["manipulability"].
Computation is gated on MetricConfig.enabled_metrics and costs ~10 ms per
470 configurations on GPU.

Remove ManipulabilityMetric's centroid-distance placeholder: measured on
Franka it is negatively correlated with true manipulability (corr = -0.37),
so consumers ranking by it preferred worse configurations. Without
Jacobians or precomputed scores the metric now warns and returns no
statistics instead of fabricating them.

The batching test's mock robot now returns None from get_solver, faithful
to Robot.get_solver with no solvers attached.

Also documented in the robot-workspace context: enabling the #599
seed-selection sampler speeds Cartesian reachability analysis 3.3x at
unchanged num_samples=30 while detecting slightly more reachable points
(measured on Franka, 4000 identical targets), with no analyzer changes.

Covered by tests/sim/motion/workspace/test_manipulability.py; the full
workspace suite passes (68 passed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Yuan-Xinyi Yuan-Xinyi added enhancement New feature or request robot Module related to robot labels Sep 11, 2026
@greptile-apps

greptile-apps Bot commented Sep 11, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the previous cache and isotropy issues are fully addressed and no new actionable failures remain.

Summary

  • Computes manipulability and optional Jacobian condition numbers in chunks.
  • Stores and restores per-configuration scores through results.npz.
  • Repairs or strips cached metric fields according to the active metric configuration.
  • Adds focused metric, analyzer, configuration-gating, cache-repair, and serialization tests.
  • Documents the manipulability contract and seed-selection measurements.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[Workspace analysis] --> B[Aligned joint configurations]
    B --> C[Chunked solver Jacobians]
    C --> D[Yoshikawa scores]
    C --> E[Optional condition numbers]
    D --> F[Current-config aggregates]
    E --> F
    D --> G[results.npz]
    F --> H[Result metrics]
    G --> I[Cache hit]
    I --> J{Metric enabled?}
    J -->|Yes, solver available| C
    J -->|Yes, no solver| K[Reuse scores and rebuild aggregates]
    J -->|No| L[Strip manipulability fields]
Loading

Reviews (4) · Last reviewed commit: "fix(workspace): recompute cached aggrega..."

Comment thread embodichain/lab/sim/motion/workspace/analyzer.py Outdated
Comment thread embodichain/lab/sim/motion/workspace/analyzer.py
Address two review findings on the manipulability integration:

1. Metric settings are deliberately not part of the results-cache key (a
   metric toggle must not invalidate the expensive sampling/IK work), so a
   cache entry written under a different metric configuration — or before
   manipulability existed — could be returned without scores. The cache-hit
   path now runs the same _apply_manipulability step as fresh analysis,
   recomputing scores and aggregates from the cached joint configurations
   in milliseconds and repairing such entries transparently.

2. The analyzer reduced Jacobians to Yoshikawa scalars and discarded them,
   so the default compute_isotropy=True could never produce its documented
   condition statistics. The chunked Jacobian sweep now also collects
   condition numbers (max/min singular value) when isotropy is enabled, and
   ManipulabilityMetric accepts them precomputed.

Tests cover the repair path (an entry written with manipulability disabled
is repaired by a later default-enabled hit on the same key), isotropy
presence via the analyzer, and precomputed condition-number passthrough.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread embodichain/lab/sim/motion/workspace/analyzer.py
The repair-on-load path handled a disabled-producer entry hit by an
enabled run, but not the symmetric direction: an enabled-producer entry
hit by a disabled run leaked stale manipulability_scores and
metrics["manipulability"] into the returned results, diverging from the
fresh-analysis contract. _apply_manipulability now strips both fields
when the metric is disabled, and keeps cached scores when the metric is
enabled but locally not computable (they remain valid for the same joint
configurations).

Covered by test_cache_hit_strips_fields_when_metric_disabled, which
writes a score-bearing entry first so the strip path is genuinely
exercised on the hit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread embodichain/lab/sim/motion/workspace/analyzer.py Outdated
…config

On a cache hit where the current robot has no solver, the previous branch
kept both the cached scores and the producer's aggregate metrics, so the
returned means/counts could reflect a different jacobian_threshold and
condition statistics could be present with isotropy disabled (or stale
when enabled). Cached scores are pure kinematics and stay valid, but
aggregates now always go through ManipulabilityMetric under the CURRENT
configuration; per-point condition numbers are not cached, so condition
statistics are correctly absent on this path instead of leaking through.

Covered by test_no_solver_hit_recomputes_aggregates_under_current_config:
a mock no-solver robot hits an entry carrying producer aggregates from a
different configuration, and the returned aggregates honour the current
threshold while the stale mean_condition is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@yuecideng
yuecideng self-requested a review September 11, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request robot Module related to robot

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant