mplib return_closest: ranking IK candidates and equivalent joint angles

In mplib, return_closest=True selects an accepted inverse-kinematics candidate near the supplied starting joint configuration. The implementation first gathers candidates that pass its solver, joint-limit, collision and pose-error checks. It then considers a particular equivalent-angle adjustment before choosing the candidate with the smallest joint-vector distance to the start.

There are two different comparisons in that process. The pose threshold decides whether an endpoint matches the requested pose; the joint-space distance ranks the accepted endpoints. They operate on different quantities and occur at different stages.

The stable IK tutorial and API reference describe the options. The rendered Planner source exposes the filtering and ranking steps. Both were checked on October 16, 2026; no IK solve, planning run or robot motion was performed for this article.

The starting posture seeds a bounded set of attempts

The current signature uses goal_pose for the target and start_qpos for the starting joint configuration. n_init_qpos defaults to 20. In the inspected implementation, the loop makes that many solver attempts in total: the first uses the supplied starting configuration, and subsequent attempts use sampled configurations.

After each iteration, the code samples another configuration and restores masked joints from start_qpos. Those joints therefore retain their starting values in the sampled seed. The mask is also passed to the underlying IK call.

The tutorial’s explanatory prose uses the names target_pose and init_qpos, while its API block and linked implementation use goal_pose and start_qpos. The implementation also makes the attempt count precise: its loop does not add a separate initial attempt on top of n_init_qpos.

The inverse-kinematics and inverse-dynamics guide explains why one target pose can admit multiple joint configurations. Here, return_closest supplies a specific selection rule for the candidates found by this bounded numerical search.

A solved posture still passes through acceptance checks

Each attempt calls compute_IK_CLIK. A successful solver return is combined with wrap_joint_limit, which tries to express the result within the joint limits. For revolute joints, the wrapper normally maps angles into the interval beginning at the lower limit and spanning one full turn, then checks the upper limit with the code’s numerical tolerance.

The implementation next updates the planning world with the candidate’s move-group joint values and checks collisions. A detected collision removes that candidate from the successful path. For a remaining candidate, forward kinematics computes the target link’s pose so the code can evaluate its distance from goal_pose.

The API describes that pose distance as the position-error norm plus the quaternion-error norm. The default threshold=1e-3 therefore applies to this combined quantity; it is not a standalone positional tolerance of one millimeter. The implementation accepts a candidate only when the computed distance is strictly below the threshold.

It then applies a separate uniqueness test. If the norm of the difference from an already retained candidate’s move-group joint values is less than 0.1, the new candidate is treated as a duplicate. This comparison prevents the retained list from filling with near-identical sampled results; it is separate from both pose accuracy and final closest-candidate selection.

Stage Compared quantity Role
Pose acceptance Position-error norm plus quaternion-error norm Determine whether the target pose is matched within threshold
Candidate uniqueness Difference between move-group joint vectors Avoid retaining nearby duplicates using the source’s 0.1 cutoff
Closest selection Difference between adjusted full candidate and start_qpos Choose one retained endpoint

Equivalent angles can change the nearest candidate

Before the final ranking, return_closest considers an alternative angle for selected revolute joints. The selection mask requires a revolute joint type and a joint-limit range strictly greater than 2π. A range exactly equal to one full turn does not enter this adjustment mask.

For a masked joint, the code compares the retained angle q with q + 2π. It substitutes the latter only when that value is strictly below the upper joint limit and closer to the corresponding start angle in absolute difference. This is the particular adjustment implemented in the checked source: one additional positive turn after the earlier wrapping step.

The distinction is useful because two angle values can describe the same revolute-joint orientation while having very different numerical distances from the start. The adjustment changes the numerical representation used for ranking.

Consider a hypothetical single-joint calculation, with limits from −2π to 2π and a starting angle of 3.0 radians. Use illustrative wrapped candidate values of −3.0 and −3.5 radians, holding other vector dimensions at their starting values to isolate the ranking arithmetic.

Candidate Wrapped angle Distance from 3.0 Angle after adding 2π Adjusted distance from 3.0
A −3.0 6.0 About 3.283 About 0.283
B −3.5 6.5 About 2.783 About 0.217

Both adjusted angles are below the example’s upper limit, and both are closer to the start. Before adjustment, A has the smaller numerical difference. After adjustment, B is nearer. This is why equivalent-angle handling occurs before the final minimum-distance choice.

The code tests q + 2π, rather than enumerating every integer-turn representation. Its joint-range mask and strict upper-limit comparison define exactly which alternatives participate.

The final norm selects one of the retained endpoints

After applying the eligible adjustments, the implementation evaluates np.linalg.norm(q_goals - start_qpos, axis=1) and selects its minimum. This final expression uses the full candidate vectors. The earlier uniqueness test uses the move-group indices, so the two comparisons should not be conflated.

With return_closest=False, successful output contains the retained candidate list. With the flag enabled, successful output contains one NumPy joint vector. When no valid candidate is retained, the function returns a failure status and None.

The selected vector is nearest under the implemented joint-distance calculation among the candidates that survived this search. A different collection of sampled candidates can change that choice. The calculation also ranks endpoint configurations; it does not evaluate the length of a collision-free path from the start to each endpoint.

Reading the process in this order makes the options easier to use: threshold controls endpoint pose acceptance, sampling determines which candidates are found, wrapping and equivalent-angle adjustment determine their joint representations, and return_closest applies the final posture preference. Each step has a specific role in the returned configuration.