-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Newton Raycast Sensor #5919
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: develop
Are you sure you want to change the base?
Newton Raycast Sensor #5919
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 |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| # Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | ||
| # All rights reserved. | ||
| # | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| # pyright: reportInvalidTypeForm=none, reportPrivateUsage=none | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| import newton | ||
| import numpy as np | ||
| import warp as wp | ||
|
|
||
| from isaaclab.sensors.ray_caster.base_ray_caster import BaseRayCaster | ||
| from isaaclab.sensors.ray_caster.kernels import apply_z_drift_kernel | ||
|
|
||
| from isaaclab_newton.physics import NewtonManager | ||
|
|
||
| from .ray_caster import _NewtonRayCasterMixin | ||
|
|
||
| if TYPE_CHECKING: | ||
| from .newton_ray_caster_cfg import NewtonRayCasterCfg | ||
|
|
||
|
|
||
| @wp.kernel(enable_backward=False) | ||
| def _resolve_ray_hits_kernel( | ||
| # input | ||
| env_mask: wp.array(dtype=wp.bool), | ||
| ray_starts_w: wp.array2d(dtype=wp.vec3f), | ||
| ray_directions_w: wp.array2d(dtype=wp.vec3f), | ||
| ray_dist: wp.array(dtype=wp.float32), | ||
| num_rays: int, | ||
| max_dist: wp.float32, | ||
| # output | ||
| ray_hits_w: wp.array2d(dtype=wp.vec3f), | ||
| ): | ||
| """Convert per-ray hit distances into world-frame hit positions. | ||
|
|
||
| Args: | ||
| env_mask: Mask of environments to update. Shape is (num_envs,). | ||
| ray_starts_w: World-frame ray starts [m]. Shape is (num_envs, num_rays). | ||
| ray_directions_w: World-frame ray directions (unit). Shape is (num_envs, num_rays). | ||
| ray_dist: Flat hit distances [m]. Shape is (num_envs * num_rays,). | ||
| num_rays: Rays per sensor, used to flatten the (env, ray) index. | ||
| max_dist: Maximum hit distance [m]; beyond this counts as a miss. | ||
| ray_hits_w: Output hit positions [m]; ``inf`` on miss. Shape is (num_envs, num_rays). | ||
| """ | ||
| env, ray = wp.tid() | ||
| if not env_mask[env]: | ||
| return | ||
| dist = ray_dist[env * num_rays + ray] | ||
| if dist < 0.0 or dist > max_dist: | ||
| ray_hits_w[env, ray] = wp.vec3f(wp.inf, wp.inf, wp.inf) | ||
| else: | ||
| ray_hits_w[env, ray] = ray_starts_w[env, ray] + dist * ray_directions_w[env, ray] | ||
|
|
||
|
|
||
| class NewtonRayCaster(_NewtonRayCasterMixin, BaseRayCaster): | ||
| """Newton ray-caster that casts against the live collision model. | ||
|
|
||
| Rays are placed in the world frame via the mesh ray-caster's site-based pose tracking, then | ||
| resolved with :func:`newton.intersect_ray` against each environment's collision shapes (and, | ||
| optionally, the global world) through the Newton model's BVH. This handles dynamic geometry | ||
| without parsing or tracking individual meshes. | ||
| """ | ||
|
|
||
| cfg: NewtonRayCasterCfg | ||
| """The configuration parameters.""" | ||
|
|
||
| def _initialize_warp_meshes(self): | ||
| """Skip USD mesh parsing; the Newton collision model is queried directly.""" | ||
| pass | ||
|
|
||
| def _initialize_rays_impl(self): | ||
| super()._initialize_rays_impl() | ||
| num_envs, num_rays = self._view_count, self.num_rays | ||
| # Flat (zero-copy) ray views for newton.intersect_ray. | ||
| self._ray_starts_w_flat = self._ray_starts_w.flatten() | ||
| self._ray_directions_w_flat = self._ray_directions_w.flatten() | ||
| self._ray_dist = wp.empty(num_envs * num_rays, dtype=wp.float32, device=self._device) | ||
| # Each environment maps to its own Newton world (env index == world index). | ||
| ray_worlds = np.repeat(np.arange(num_envs, dtype=np.int32), num_rays) | ||
| self._ray_worlds = wp.array(ray_worlds, dtype=wp.int32, device=self._device) | ||
|
|
||
| def _update_buffers_impl(self, env_mask: wp.array): | ||
| """Fills the buffers of the sensor data.""" | ||
| self._update_ray_infos(env_mask) | ||
|
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. 🟡 Warning: The If the Newton API supports a world-mask or if you can slice the flat arrays to only include active environments, this could be a meaningful optimization for large env counts with sparse updates. |
||
|
|
||
| model = NewtonManager._model | ||
| if model is None: | ||
| raise RuntimeError("Newton model is not initialized.") | ||
|
|
||
| # Cast rays against the Newton collision model; misses are encoded as -1 distance. | ||
| newton.intersect_ray( | ||
| model, | ||
| ray_origins=self._ray_starts_w_flat, | ||
| ray_directions=self._ray_directions_w_flat, | ||
| ray_worlds=self._ray_worlds, | ||
| enable_global_world=self.cfg.enable_global_world, | ||
| out_dist=self._ray_dist, | ||
| ) | ||
|
|
||
| wp.launch( | ||
| _resolve_ray_hits_kernel, | ||
| dim=(self._num_envs, self.num_rays), | ||
| inputs=[ | ||
| env_mask, | ||
| self._ray_starts_w, | ||
| self._ray_directions_w, | ||
| self._ray_dist, | ||
| int(self.num_rays), | ||
| float(self.cfg.max_distance), | ||
| ], | ||
| outputs=[self._data._ray_hits_w], | ||
| device=self._device, | ||
| ) | ||
|
|
||
| # Apply vertical drift to ray hits. | ||
| wp.launch( | ||
| apply_z_drift_kernel, | ||
| dim=(self._num_envs, self.num_rays), | ||
| inputs=[env_mask, self.ray_cast_drift.warp, self._data._ray_hits_w], | ||
| device=self._device, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,31 @@ | ||||||||||
| # Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | ||||||||||
| # All rights reserved. | ||||||||||
| # | ||||||||||
| # SPDX-License-Identifier: BSD-3-Clause | ||||||||||
|
|
||||||||||
| from __future__ import annotations | ||||||||||
|
|
||||||||||
| from typing import TYPE_CHECKING | ||||||||||
|
|
||||||||||
| from isaaclab.sensors.ray_caster.ray_caster_cfg import RayCasterCfg | ||||||||||
| from isaaclab.utils.configclass import configclass | ||||||||||
|
|
||||||||||
| if TYPE_CHECKING: | ||||||||||
| from .newton_ray_caster import NewtonRayCaster | ||||||||||
|
|
||||||||||
|
|
||||||||||
| @configclass | ||||||||||
| class NewtonRayCasterCfg(RayCasterCfg): | ||||||||||
| """Configuration for the Newton ray-cast sensor. | ||||||||||
|
|
||||||||||
| Casts rays via BVH-accelerated queries against the Newton model's shape BVH, which must be | ||||||||||
| refit after each physics update. | ||||||||||
| """ | ||||||||||
|
|
||||||||||
| class_type: type[NewtonRayCaster] | str = "{DIR}.newton_ray_caster:NewtonRayCaster" | ||||||||||
|
|
||||||||||
| mesh_prim_paths: list[str] = [] | ||||||||||
| """Unused by the Newton ray-caster.""" | ||||||||||
|
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. 🔵 Suggestion: More informative docstring.
Suggested change
|
||||||||||
|
|
||||||||||
| enable_global_world: bool = True | ||||||||||
| """Whether to also cast against the global Newton world (index ``-1``). Defaults to True.""" | ||||||||||
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.
🟡 Warning: Mixed
_view_count/_num_envsusage.Here
_view_countis used to size buffers:But in
_update_buffers_impl(line 93), kernel launches useself._num_envs:For single-site sensors these are equal, but the inconsistency is fragile. Consider using one consistently (preferably
self._num_envssince that's what the base class uses for kernel launches, and document that_view_count == _num_envsis a precondition).