-
Notifications
You must be signed in to change notification settings - Fork 24
fix ur solver allocate memory #604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,10 +25,13 @@ | |
| from embodichain.compute.kinematics._warp.ur import ( | ||
| URParam, | ||
| ur_ik_kernel, | ||
| ur_ik_nearest_kernel, | ||
| ) | ||
| import math | ||
| from embodichain.utils.device_utils import standardize_device_string | ||
|
|
||
| __all__ = ["URSolverCfg", "URSolver"] | ||
|
|
||
|
|
||
| @configclass | ||
| class URSolverCfg(SolverCfg): | ||
|
|
@@ -140,19 +143,24 @@ def get_ik( | |
| qpos_seed: torch.Tensor | None = None, | ||
| return_all_solutions: bool = False, | ||
| **kwargs, | ||
| ): | ||
| """Compute target joint positions using OPW inverse kinematics. | ||
| ) -> tuple[torch.Tensor, torch.Tensor]: | ||
| """Compute target joint positions using UR inverse kinematics. | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The updated public Context Used: CLAUDE.md (source) Prompt To Fix With AIThis is a comment left during a code review.
Path: embodichain/lab/sim/motion/solvers/ur_solver.py
Line: 148
Comment:
**Annotate variadic keyword values**
The updated public `get_ik` signature leaves `**kwargs` untyped, preventing type checkers and generated API information from determining the accepted keyword-value type.
**Context Used:** CLAUDE.md ([source](https://github.com/dexforce/embodichain/blob/main/CLAUDE.md))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||
| Args: | ||
| target_xpos (torch.Tensor): Current end-effector pose, shape (n_sample, 4, 4). | ||
| qpos_seed (torch.Tensor): Current joint positions, shape (n_sample, num_joints). | ||
| return_all_solutions (bool, optional): Whether to return all IK solutions or just the best one. Defaults to False. | ||
| qpos_seed (torch.Tensor): Current joint positions, shape (n_sample, 6) | ||
| or (1, 6). Defaults to the joint-limit midpoint. | ||
| return_all_solutions (bool, optional): Whether to return all 512 | ||
| candidates. False selects the nearest valid candidate inside | ||
| the Warp kernel without allocating the full candidate tensor. | ||
| **kwargs: Additional keyword arguments for future extensions. | ||
|
|
||
| Returns: | ||
| Tuple[torch.Tensor, torch.Tensor]: | ||
| - target_joints (torch.Tensor): Computed target joint positions, shape (n_sample, n_solution, num_joints). | ||
| - success (torch.Tensor): Boolean tensor indicating IK solution validity for each environment, shape (n_sample,). | ||
| - success (torch.Tensor): Boolean validity, shape (n_sample,) | ||
| or (n_sample, 512) when all solutions are requested. | ||
| - target_joints (torch.Tensor): Joint positions, shape | ||
| (n_sample, 6) or (n_sample, 512, 6), respectively. | ||
| """ | ||
| N_SOL = 512 | ||
| DOF = 6 | ||
|
|
@@ -164,7 +172,7 @@ def get_ik( | |
| target_xpos_batch = target_xpos_batch @ tcp_inv[None, :, :] | ||
| n_sample = target_xpos_batch.shape[0] | ||
|
|
||
| if qpos_seed is None: | ||
| if qpos_seed is None and not return_all_solutions: | ||
| # A missing seed previously crashed at the nearest-solution step; | ||
| # default to the feasibility-safe joint-range midpoint. | ||
| qpos_seed = ( | ||
|
|
@@ -178,10 +186,44 @@ def get_ik( | |
| wp_device = standardize_device_string(self.device) | ||
| # Flatten target poses to a 1-D float array for the Warp kernel. | ||
| xpos_wp = wp.from_torch(target_xpos_batch.reshape(-1)) | ||
| all_qpos_wp = wp.zeros(n_sample * N_SOL * DOF, dtype=float, device=wp_device) | ||
| all_ik_valid_wp = wp.zeros(n_sample * N_SOL, dtype=int, device=wp_device) | ||
| lower_qpos_limits_wp = wp.from_torch(self.lower_qpos_limits) | ||
| upper_qpos_limits_wp = wp.from_torch(self.upper_qpos_limits) | ||
|
|
||
| if not return_all_solutions: | ||
| # Match PyTorch's promotion in weight * (float32 candidates - seed), | ||
| # including double-precision seeds and broadcast/noncontiguous seeds. | ||
| selection_dtype = torch.promote_types(torch.float32, qpos_seed.dtype) | ||
| selection_dtype = torch.promote_types( | ||
| selection_dtype, self.ik_nearest_weight.dtype | ||
| ) | ||
| seed = ( | ||
| qpos_seed.to(device=device, dtype=selection_dtype) | ||
| .expand(n_sample, DOF) | ||
| .contiguous() | ||
| ) | ||
| weights = self.ik_nearest_weight.to( | ||
| device=device, dtype=selection_dtype | ||
| ).contiguous() | ||
| best_qpos_wp = wp.empty((n_sample, DOF), dtype=float, device=wp_device) | ||
| best_valid_wp = wp.empty(n_sample, dtype=int, device=wp_device) | ||
| wp.launch( | ||
| kernel=ur_ik_nearest_kernel, | ||
| dim=n_sample, | ||
| inputs=[ | ||
| xpos_wp, | ||
| self._ur_params, | ||
| lower_qpos_limits_wp, | ||
| upper_qpos_limits_wp, | ||
| wp.from_torch(seed), | ||
| wp.from_torch(weights), | ||
| ], | ||
| outputs=[best_qpos_wp, best_valid_wp], | ||
| device=wp_device, | ||
| ) | ||
| return wp.to_torch(best_valid_wp).bool(), wp.to_torch(best_qpos_wp) | ||
|
|
||
| all_qpos_wp = wp.zeros(n_sample * N_SOL * DOF, dtype=float, device=wp_device) | ||
| all_ik_valid_wp = wp.zeros(n_sample * N_SOL, dtype=int, device=wp_device) | ||
| wp.launch( | ||
| kernel=ur_ik_kernel, | ||
| dim=(n_sample,), | ||
|
|
@@ -207,19 +249,7 @@ def get_ik( | |
| .to(device=device) | ||
| ) | ||
|
|
||
| if return_all_solutions: | ||
| return all_solutions_validity, all_solutions | ||
| # Select ik qpos based on the closest distance to the seed qpos | ||
| qpos_seed_expanded = qpos_seed.unsqueeze(1).expand(-1, N_SOL, -1) | ||
| distances = torch.norm( | ||
| self.ik_nearest_weight * (all_solutions - qpos_seed_expanded), dim=-1 | ||
| ) | ||
| # fill invalid solutions with inf distance | ||
| distances[~all_solutions_validity] = float("inf") | ||
| closest_indices = torch.argmin(distances, dim=1) | ||
| ik_qpos = all_solutions[torch.arange(n_sample), closest_indices] | ||
| ik_validity = all_solutions_validity[torch.arange(n_sample), closest_indices] | ||
| return ik_validity, ik_qpos | ||
| return all_solutions_validity, all_solutions | ||
|
|
||
| @staticmethod | ||
| def dh_matrix(theta_i, d_i, a_i, alpha_i): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2] Preserve the legacy selection order for equal and near-equal distances
The scalar sum of squares followed by
wp.sqrtdoes not reproduce the rounding of the previoustorch.normreduction. Consequently, the strict<comparison below does not preserve the promised first-candidate tie behavior: valid seeds near branch bisectors can select a different analytical branch, with joint differences of several radians.On CPU with PyTorch 2.7.0 and Warp 1.15.0, uniform weights, limits of ±2π, and 3,584 seeds constructed as midpoints between analytical branches, I reproduced 81 selections differing from the legacy path. In 41 cases, the legacy norms were exactly equal but the new kernel selected a later candidate. I also verified that the base commit and the refactored all-solutions kernel produced exactly identical candidate tensors and validity flags, isolating the difference to this selection calculation.
For a concrete float32 example, using the target and seed below selects candidate 256 with the legacy reduction and candidate 384 with the new kernel. Both legacy distances are
2.708728551864624, and the maximum joint difference is approximately3.5985radians. Both solutions are valid; the regression is in selection compatibility.Please align the distance reduction/rounding with the legacy selection semantics and add regression coverage for nonzero equal and near-equal distances. The existing zero-weight tie test cannot detect this difference; all 15 added CPU cases passed in this environment.