Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
ebceba7
Add custom triangular mesh spawning
maxkra15 Oct 6, 2026
e7d3aa3
Stage conveyor adapters in Contrib
maxkra15 Oct 6, 2026
d9b3e79
Combine conveyor prerequisites for task review
maxkra15 Oct 6, 2026
a558e25
Add contributed conveyor racetrack transfer task
maxkra15 Oct 6, 2026
8e36eb7
Add warehouse sorting with the shared conveyor policy
maxkra15 Oct 6, 2026
bb34794
Advance warehouse animation during headless Kit playback
maxkra15 Oct 7, 2026
f54d05c
Trim unused conveyor adapter state and compatibility APIs
maxkra15 Oct 8, 2026
3e20689
Merge develop into custom mesh support
maxkra15 Oct 8, 2026
911a7d4
Merge develop into conveyor adapter support
maxkra15 Oct 8, 2026
2f06b12
Update custom mesh prerequisite from develop
maxkra15 Oct 8, 2026
d1aa9a1
Reuse conveyor prism geometry and simplify task helpers
maxkra15 Oct 8, 2026
b2aa1be
Update lean conveyor adapter prerequisite
maxkra15 Oct 8, 2026
cf845ba
Update conveyor foundations from develop
maxkra15 Oct 8, 2026
1c91d6e
Remove conveyor test-only lifecycle seams
maxkra15 Oct 8, 2026
94cc7dc
Use current schema and actuator APIs in conveyor tasks
maxkra15 Oct 8, 2026
8a7d057
Trim warehouse slot bookkeeping and task documentation
maxkra15 Oct 8, 2026
c2dfb91
Update conveyor lifecycle prerequisite
maxkra15 Oct 8, 2026
27243d5
Update conveyor lifecycle prerequisite
maxkra15 Oct 8, 2026
c6543f4
Update shared conveyor task and develop
maxkra15 Oct 8, 2026
ffbfb8d
Share conveyor path resolution across backends
maxkra15 Oct 8, 2026
77a9e12
Merge branch 'maximiliank/conveyor-support-contrib' into maximiliank/…
maxkra15 Oct 8, 2026
783815b
Merge branch 'maximiliank/conveyor-foundations' into maximiliank/conv…
maxkra15 Oct 8, 2026
e1c2fe6
Merge branch 'maximiliank/conveyor-racetrack-transfer' into maximilia…
maxkra15 Oct 8, 2026
7a8c754
Merge remote-tracking branch 'origin/develop' into maximiliank/custom…
maxkra15 Oct 12, 2026
2412c13
Merge develop and align conveyor lifecycle cleanup
maxkra15 Oct 12, 2026
de3d752
Detach conveyor callbacks from captured CUDA graphs
maxkra15 Oct 12, 2026
54613ef
Use shared mesh cooking properties and reject lossy inputs
maxkra15 Oct 12, 2026
a4acdd8
Merge current mesh foundation with latest develop
maxkra15 Oct 12, 2026
608379d
Merge branch 'maximiliank/conveyor-foundations' into maximiliank/conv…
maxkra15 Oct 12, 2026
6beed8f
Merge branch 'maximiliank/conveyor-support-contrib' into maximiliank/…
maxkra15 Oct 12, 2026
da6cd57
Merge branch 'maximiliank/conveyor-racetrack-transfer' into maximilia…
maxkra15 Oct 12, 2026
72500f0
Correct conveyor tool velocity and reuse mesh cooking settings
maxkra15 Oct 12, 2026
a21ea3b
Document authored warehouse layers and license support overrides
maxkra15 Oct 12, 2026
af6d277
Keep authored conveyor overrides reviewable as text
maxkra15 Oct 12, 2026
4fa636e
Merge branch 'maximiliank/conveyor-racetrack-transfer' into maximilia…
maxkra15 Oct 12, 2026
cffb357
Persist evaluation reports before Kit shutdown
maxkra15 Oct 12, 2026
9604332
Integrate #8326 at 54613ef14cf2
maxkra15 Oct 12, 2026
ad140b0
Integrate #8327 at de3d752c5030
maxkra15 Oct 12, 2026
9a00429
Integrate #8331 at cffb3570ce12
maxkra15 Oct 12, 2026
bdd451d
Integrate #8332 at 4fa636eae3a0
maxkra15 Oct 12, 2026
0eacad7
Record the tested conveyor integration snapshot
maxkra15 Oct 12, 2026
14c00cc
chore: Merge develop into custom mesh PR
maxkra15 Oct 16, 2026
88ab417
chore: Sync develop into custom mesh PR
maxkra15 Oct 19, 2026
5276842
chore: Sync main into custom mesh PR
maxkra15 Oct 22, 2026
dae0d8c
chore: Merge main into PR #8326 branch
maxkra15 Oct 23, 2026
e6d6108
Reuse success-guided sampling for conveyor transfer
maxkra15 Oct 23, 2026
e1dda73
Reuse success-guided sampling for conveyor transfer
maxkra15 Oct 23, 2026
b0a947b
Refresh conveyor integration for shared sampling
maxkra15 Oct 23, 2026
886a2c9
Keep experimental conveyor support Newton-only
maxkra15 Oct 23, 2026
c88bc09
Integrate Newton-only conveyor support
maxkra15 Oct 23, 2026
0232e23
Simplify calibrated conveyor resets and remove retired acquisition logic
maxkra15 Oct 23, 2026
75be9a0
Simplify conveyor scene geometry configuration
maxkra15 Oct 23, 2026
17994b6
Simplify conveyor scene geometry configuration
maxkra15 Oct 23, 2026
e7050b2
Extract optional PhysX support from the Newton conveyor task
maxkra15 Oct 23, 2026
44d6aac
Merge commit 'e7050b2b7' into maximiliank/conveyor-lean-warehouse-202…
maxkra15 Oct 23, 2026
4d63c39
Update the Newton-only conveyor foundation
maxkra15 Oct 23, 2026
f6bde8d
Update the task review foundation
maxkra15 Oct 23, 2026
ba20632
Merge commit 'f6bde8d2e' into maximiliank/conveyor-lean-warehouse-202…
maxkra15 Oct 23, 2026
5bc02eb
Remove unused conveyor command reset metadata
maxkra15 Oct 23, 2026
b6a0b72
Add optional CPU PhysX support for conveyor transfer
maxkra15 Oct 23, 2026
28e0b07
Merge commit '5bc02eb32' into maximiliank/conveyor-lean-warehouse-202…
maxkra15 Oct 23, 2026
17bea8b
Simplify warehouse presentation and keep grasp checks local
maxkra15 Oct 23, 2026
0f8f361
Keep conveyor stack tracking out of the warehouse review
maxkra15 Oct 23, 2026
ffed4f4
Integrate optional CPU PhysX conveyor support last
maxkra15 Oct 23, 2026
cfe1b6e
Record the lean conveyor stack with optional PhysX validation
maxkra15 Oct 23, 2026
4305eba
Narrow conveyor surface descriptions to authored task inputs
maxkra15 Oct 23, 2026
dbe6bc0
Narrow conveyor surface descriptions to authored task inputs
maxkra15 Oct 23, 2026
a0d4a29
Limit Newton conveyors to authored surface velocities
maxkra15 Oct 23, 2026
1a861b3
Remove unused conveyor task control entry points
maxkra15 Oct 23, 2026
67a5f1b
Update the fixed-speed Newton conveyor foundation
maxkra15 Oct 23, 2026
7ee7333
Use the configured-speed conveyor runtime
maxkra15 Oct 23, 2026
ccb8140
Update warehouse sorting to the fixed-speed conveyor runtime
maxkra15 Oct 23, 2026
4782a56
Merge commit '7ee7333a36d170f0082f28d574d93ca6432892a2' into maximili…
maxkra15 Oct 23, 2026
a0cd35f
Integrate the minimal configured-speed conveyor runtime
maxkra15 Oct 23, 2026
70bb88d
Simplify native PhysX conveyor playback and schema authoring
maxkra15 Oct 23, 2026
422336e
Integrate the minimal optional PhysX lifecycle
maxkra15 Oct 23, 2026
769c21e
Record validation of the minimal conveyor runtime
maxkra15 Oct 23, 2026
619e4d2
Merge main and migrate conveyor forces to solver observables
maxkra15 Oct 23, 2026
973e6c2
Merge the current-main Newton conveyor foundation
maxkra15 Oct 23, 2026
6605aa7
Update custom meshes to their current reviewed head
maxkra15 Oct 23, 2026
4c83011
Merge main and update conveyor task manager and viewer APIs
maxkra15 Oct 23, 2026
46f1aea
Use the resolved warehouse viewer configuration
maxkra15 Oct 23, 2026
da0194d
Bind native conveyor callbacks to the simulation manager
maxkra15 Oct 23, 2026
5ae6dc5
Merge commit '4c83011cb' into maximiliank/conveyor-main-warehouse-202…
maxkra15 Oct 23, 2026
95c9039
Merge current conveyor task and main into optional PhysX support
maxkra15 Oct 23, 2026
e195a19
Preserve the optional PhysX browser entry after merging
maxkra15 Oct 23, 2026
7f3c6dc
Merge main and Newton 1.8 conveyor dependencies into integration
maxkra15 Oct 23, 2026
f0a7b1b
Integrate the current-main warehouse viewer changes
maxkra15 Oct 23, 2026
bbdd14f
Integrate current-main optional PhysX support and retain conveyor pre…
maxkra15 Oct 23, 2026
645b740
Use the owned backend manager in sorter contact checks
maxkra15 Oct 23, 2026
f8b9c5d
Update sorter contact checks to the current manager API
maxkra15 Oct 23, 2026
fa4e8fe
Keep optional PhysX additions separate from warehouse append points
maxkra15 Oct 23, 2026
8e11815
Make optional PhysX and warehouse changes compose independently
maxkra15 Oct 23, 2026
6f6a1d9
Record current-main conveyor integration validation
maxkra15 Oct 23, 2026
21bc31b
Simplify conveyor action state and bind the Panda tool once
maxkra15 Oct 25, 2026
85c2a3c
Reuse native mesh and cuboid spawners in conveyor scene
maxkra15 Oct 25, 2026
96afdac
Drop the redundant custom mesh foundation
maxkra15 Oct 25, 2026
137a746
Use the conveyor foundation without a custom mesh API
maxkra15 Oct 25, 2026
b6d91df
Align warehouse presentation with the policy workspace
maxkra15 Oct 25, 2026
5c865d7
Batch warehouse arrival dispatch and resolve parking joints once
maxkra15 Oct 25, 2026
9a619ac
Protect the robot mounting frame during conveyor resets
maxkra15 Oct 25, 2026
3d26b39
Reuse the simplified racetrack task and native mesh foundation
maxkra15 Oct 25, 2026
e42b819
Merge commit '137a746b4c3c7c511aca2c29d00f34ef71182cce' into maximili…
maxkra15 Oct 25, 2026
a7abf33
Use the standard mesh spawner for native PhysX conveyors
maxkra15 Oct 25, 2026
16fd640
Order conveyor lifecycle methods according to Lab style
maxkra15 Oct 25, 2026
7ef95f3
Keep presentation offsets separate from policy coordinates
maxkra15 Oct 25, 2026
4fc9748
Reuse the final conveyor lifecycle layout
maxkra15 Oct 25, 2026
4f9bac7
Reuse the final conveyor lifecycle layout
maxkra15 Oct 25, 2026
ef48e9f
Integrate the simplified racetrack and native mesh foundation
maxkra15 Oct 25, 2026
210e854
Integrate the standard warehouse scene and batched dispatch
maxkra15 Oct 25, 2026
1011a20
Integrate native mesh spawning for optional PhysX
maxkra15 Oct 25, 2026
4f48bef
Update the combined conveyor preview to current main
maxkra15 Oct 25, 2026
6e8ce03
Integrate final warehouse task conventions
maxkra15 Oct 25, 2026
e07445a
Match Lab import and scene annotation conventions
maxkra15 Oct 25, 2026
1834c4a
Reuse the final shared task annotations
maxkra15 Oct 25, 2026
b0b4202
Integrate final optional PhysX task conventions
maxkra15 Oct 25, 2026
a6383d7
Reuse the final shared task annotations
maxkra15 Oct 25, 2026
b702f94
Keep external named exports compatible with Lab lazy loading
maxkra15 Oct 25, 2026
fbe9220
Reuse the supported external lazy-export declaration
maxkra15 Oct 25, 2026
5295132
Integrate the final task export declaration
maxkra15 Oct 25, 2026
452d1da
Reuse the supported external lazy-export declaration
maxkra15 Oct 25, 2026
324564a
Integrate the final optional task export declaration
maxkra15 Oct 25, 2026
8ff9bf9
Document the bounded warehouse policy evaluation
maxkra15 Oct 25, 2026
5000ddf
Record the warehouse policy validation limit
maxkra15 Oct 25, 2026
ef7889e
Record the lean conveyor stack and capability checks
maxkra15 Oct 25, 2026
c309f7d
chore: Sync main into PR branch
maxkra15 Oct 26, 2026
2495b35
fix: Use shared conveyor clone defaults
maxkra15 Oct 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
*.usd filter=lfs diff=lfs merge=lfs -text
*.usda filter=lfs diff=lfs merge=lfs -text
source/isaaclab_tasks/isaaclab_tasks/contrib/conveyor_franka/assets/conveyor_*_supported.usda -filter diff merge text
source/isaaclab_tasks/isaaclab_tasks/contrib/conveyor_franka/assets/parcel*.usda -filter diff merge text
*.psd filter=lfs diff=lfs merge=lfs -text
*.hdr filter=lfs diff=lfs merge=lfs -text
*.dae filter=lfs diff=lfs merge=lfs -text
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# No USD files allowed in the repo
**/*.usd
**/*.usda
!source/isaaclab_tasks/isaaclab_tasks/contrib/conveyor_franka/assets/*.usda
**/*.usdc
**/*.usdz

Expand Down
3 changes: 3 additions & 0 deletions docs/source/_static/css/environment-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
["IsaacContrib-AutoMate-Disassembly-Direct", "rl_games", "", "", "", "environment-previews/automate_disassembly.jpg"],
["IsaacContrib-Cartpole-Camera-Showcase-Direct", "skrl", "", "", "box_box,box_discrete,box_multidiscrete,dict_box,dict_discrete,dict_multidiscrete,tuple_box,tuple_discrete,tuple_multidiscrete", "", false, {}, {}, "", null, ["||", "||box_box", "||box_discrete", "||box_multidiscrete", "||dict_box", "||dict_discrete", "||dict_multidiscrete", "||tuple_box", "||tuple_discrete", "||tuple_multidiscrete"]],
["IsaacContrib-Cartpole-Showcase-Direct", "skrl", "isaacsim_physx,newton_kamino,newton_mjwarp,ovphysx", "", "box_box,box_discrete,box_multidiscrete,dict_box,dict_discrete,dict_multidiscrete,discrete_box,discrete_discrete,discrete_multidiscrete,multidiscrete_box,multidiscrete_discrete,multidiscrete_multidiscrete,tuple_box,tuple_discrete,tuple_multidiscrete", "environment-previews/cartpole.jpg", false, {}, {}, "", null, ["||", "||box_box", "||box_discrete", "||box_multidiscrete", "||dict_box", "||dict_discrete", "||dict_multidiscrete", "||discrete_box", "||discrete_discrete", "||discrete_multidiscrete", "||multidiscrete_box", "||multidiscrete_discrete", "||multidiscrete_multidiscrete", "||tuple_box", "||tuple_discrete", "||tuple_multidiscrete", "isaacsim_physx||", "isaacsim_physx||box_box", "isaacsim_physx||box_discrete", "isaacsim_physx||box_multidiscrete", "isaacsim_physx||dict_box", "isaacsim_physx||dict_discrete", "isaacsim_physx||dict_multidiscrete", "isaacsim_physx||discrete_box", "isaacsim_physx||discrete_discrete", "isaacsim_physx||discrete_multidiscrete", "isaacsim_physx||multidiscrete_box", "isaacsim_physx||multidiscrete_discrete", "isaacsim_physx||multidiscrete_multidiscrete", "isaacsim_physx||tuple_box", "isaacsim_physx||tuple_discrete", "isaacsim_physx||tuple_multidiscrete", "newton_kamino||", "newton_kamino||box_box", "newton_kamino||box_discrete", "newton_kamino||box_multidiscrete", "newton_kamino||dict_box", "newton_kamino||dict_discrete", "newton_kamino||dict_multidiscrete", "newton_kamino||discrete_box", "newton_kamino||discrete_discrete", "newton_kamino||discrete_multidiscrete", "newton_kamino||multidiscrete_box", "newton_kamino||multidiscrete_discrete", "newton_kamino||multidiscrete_multidiscrete", "newton_kamino||tuple_box", "newton_kamino||tuple_discrete", "newton_kamino||tuple_multidiscrete", "newton_mjwarp||", "newton_mjwarp||box_box", "newton_mjwarp||box_discrete", "newton_mjwarp||box_multidiscrete", "newton_mjwarp||dict_box", "newton_mjwarp||dict_discrete", "newton_mjwarp||dict_multidiscrete", "newton_mjwarp||discrete_box", "newton_mjwarp||discrete_discrete", "newton_mjwarp||discrete_multidiscrete", "newton_mjwarp||multidiscrete_box", "newton_mjwarp||multidiscrete_discrete", "newton_mjwarp||multidiscrete_multidiscrete", "newton_mjwarp||tuple_box", "newton_mjwarp||tuple_discrete", "newton_mjwarp||tuple_multidiscrete", "ovphysx||", "ovphysx||box_box", "ovphysx||box_discrete", "ovphysx||box_multidiscrete", "ovphysx||dict_box", "ovphysx||dict_discrete", "ovphysx||dict_multidiscrete", "ovphysx||discrete_box", "ovphysx||discrete_discrete", "ovphysx||discrete_multidiscrete", "ovphysx||multidiscrete_box", "ovphysx||multidiscrete_discrete", "ovphysx||multidiscrete_multidiscrete", "ovphysx||tuple_box", "ovphysx||tuple_discrete", "ovphysx||tuple_multidiscrete"]],
["IsaacContrib-Conveyor-Racetrack-Transfer-PhysX-CPU-v0", "rsl_rl", "", "", ""],
["IsaacContrib-Conveyor-Racetrack-Transfer-v0", "rsl_rl", "", "", "", "./tasks/conveyor/racetrack_transfer.jpg"],
["IsaacContrib-Conveyor-Warehouse-Sorting-v0", "rsl_rl", "", "", "", "./tasks/conveyor/warehouse_sorting.jpg"],
["IsaacContrib-Deploy-GearAssembly-Rizon4s-Grav", "rsl_rl", "", "", "", "environment-previews/rizon4s_gear_assembly.jpg"],
["IsaacContrib-Deploy-GearAssembly-Rizon4s-Grav-ROS-Inference", "rsl_rl", "", "", "", "environment-previews/rizon4s_gear_assembly.jpg"],
["IsaacContrib-Deploy-GearAssembly-UR10e-2F140", "rsl_rl", "", "", "", "environment-previews/ur10e_2f140_gear_assembly.jpg"],
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Fixed conveyor namespace defaults to follow the shared clone configuration.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added an experimental CPU PhysX conveyor adapter using native surface velocity.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added experimental straight and curved conveyor surfaces using Newton's upstream force model.
Empty file.
24 changes: 24 additions & 0 deletions source/isaaclab_contrib/isaaclab_contrib/conveyors/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Experimental conveyor support

This adapter stages Newton conveyor integration in `isaaclab_contrib`; its API is
experimental and is not exported from Isaac Lab core or the backend packages.

- `surface_velocity.SurfaceVelocitySpec` describes a straight or curved surface
in the collision prim's local frame.
- `newton.SurfaceVelocity` uses Newton's upstream `ConveyorForceModel`. The
adapter owns replicated surface/body bindings and solver lifecycle integration,
including hard resets and registration before CUDA graph capture. It supports
the Newton MJWarp solver; other Newton solvers are outside this adapter's scope.
Pass the simulation context's `backend_manager`; solved contact forces use
Newton's solver observables rather than the legacy contact-force attribute.

Surface speeds and traction parameters come from the authored descriptions.
Full resets restart the startup ramp; episode resets clear stale forces for
only the selected worlds.

No meshes, assets, task registrations, or policy checkpoints are bundled here.

The optional `physx.SurfaceVelocity` adapter uses native `PhysxSurfaceVelocityAPI`
on CPU. Author its kinematic collision surfaces with `apply_surface_velocity_api`
before simulation starts, then bind the live adapter after PhysX parses the scene.
It requires Isaac Sim and uses authored contact materials for traction.
10 changes: 10 additions & 0 deletions source/isaaclab_contrib/isaaclab_contrib/conveyors/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# 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

"""Experimental conveyor support for contributed tasks.

The Newton adapter is imported explicitly from :mod:`.newton` so importing the
surface descriptions does not load the optional backend.
"""
258 changes: 258 additions & 0 deletions source/isaaclab_contrib/isaaclab_contrib/conveyors/newton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
# 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

"""Bind authored conveyor surfaces to Newton's upstream force model and solver lifecycle."""

from __future__ import annotations

import re
from collections.abc import Sequence
from typing import Any

import numpy as np
import torch
import warp as wp
from isaaclab_newton.physics import NewtonManager
from newton.examples.basic.example_basic_conveyor_forces import ConveyorForceModel
from newton.solvers import SolverBase

from isaaclab.cloner.cloner_cfg import DEFAULT_ENV_TEMPLATE
from isaaclab.physics import PhysicsEvent

from .surface_velocity import SurfaceVelocitySpec, resolve_surface_velocity_paths


@wp.kernel
def _filter_body_forces(body_is_tracked: wp.array[wp.int32], body_force: wp.array[wp.spatial_vector]):
body_id = wp.tid()
if body_is_tracked[body_id] == 0:
body_force[body_id] = wp.spatial_vector()


@wp.kernel
def _advance_startup_scale(
dt: wp.float32, duration: wp.float32, elapsed: wp.array[wp.float32], scale: wp.array[wp.float32]
):
elapsed[0] += dt
scale[0] = wp.min(1.0, elapsed[0] / duration)


@wp.kernel
def _clear_selected_body_forces(
body_world: wp.array[wp.int32], world_mask: wp.array[wp.bool], body_force: wp.array[wp.spatial_vector]
):
body_id = wp.tid()
world_id = body_world[body_id]
if world_id >= 0 and world_mask[world_id]:
body_force[body_id] = wp.spatial_vector()


def _world_vector(transform: wp.transform, vector: tuple[float, float, float]) -> wp.vec3:
"""Rotate and normalize an authored direction or surface normal."""
rotated = wp.transform_vector(transform, wp.vec3(*vector))
values = np.asarray([float(rotated[index]) for index in range(3)], dtype=np.float32)
values /= float(np.linalg.norm(values))
return wp.vec3(*(float(value) for value in values))


class SurfaceVelocity:
"""Own replicated conveyor bindings from model initialization through graph teardown.

Register after the simulation context is created and before its first reset.
Speeds come from the authored surface descriptions. Newton computes traction
after each solver substep and applies it on the next one. Hard resets rebuild
the binding before CUDA graph capture; episode resets clear selected-world forces.
"""

def __init__(
self,
num_envs: int,
surface_specs: Sequence[SurfaceVelocitySpec],
*,
manager: NewtonManager,
body_pattern: str,
body_count_per_env: int,
startup_duration_s: float = 1.0,
env_path_format: str = DEFAULT_ENV_TEMPLATE,
) -> None:
"""Register model and solver callbacks for the authored surfaces.

Args:
num_envs: Number of replicated simulation environments.
surface_specs: Surface descriptions in within-environment order.
manager: Newton backend manager owned by the simulation context.
body_pattern: Regular expression selecting transported bodies.
body_count_per_env: Expected transported-body count.
startup_duration_s: Initial traction ramp duration [s].
env_path_format: Clone-template format resolving environment roots.
"""
self._surface_specs = tuple(surface_specs)
self._prim_paths = resolve_surface_velocity_paths(num_envs, self._surface_specs, env_path_format)
self._body_pattern = re.compile(body_pattern)
if not np.isfinite(startup_duration_s) or startup_duration_s <= 0.0:
raise ValueError(f"Conveyor startup duration must be positive, got {startup_duration_s}.")
self._num_envs = num_envs
self._manager = manager
self._body_count_per_env = body_count_per_env
self._startup_duration_s = startup_duration_s
self._model = None
self._conveyor: ConveyorForceModel | None = None
self._closed = False
manager.request_solver_observables(SolverBase.ObservableKind.CONTACT_F)
self._physics_ready_handle = manager.register_callback(
self._queue_solver_binding, PhysicsEvent.PHYSICS_READY, name="surface_velocity_solver_init"
)

def _queue_solver_binding(self, _event: Any) -> None:
"""Bind after solver allocation on initial construction and every hard reset."""
self._manager.register_solver_init_callback(self._bind_solver)

def _bind_solver(self) -> None:
"""Resolve the newly finalized model and register its graph callbacks."""
model = self._manager.get_model()
contacts = self._manager.get_contacts()
observables = self._manager.get_solver_observables()
if contacts is None or observables is None or observables.contact_f is None:
raise RuntimeError("The conveyor requires solved per-contact force observables.")
if model.world_count != self._num_envs:
raise RuntimeError(f"Newton model has {model.world_count} worlds, expected {self._num_envs}.")

surfaces_per_env = len(self._surface_specs)
surface_shapes = [-1] * len(self._prim_paths)
shape_world = model.shape_world.numpy()
shape_body = model.shape_body.numpy()
for shape_id, label in enumerate(model.shape_label):
world_id = int(shape_world[shape_id])
if not 0 <= world_id < self._num_envs:
continue
for spec_id in range(surfaces_per_env):
surface_id = world_id * surfaces_per_env + spec_id
path = self._prim_paths[surface_id]
if label != path and not label.startswith(f"{path}/"):
continue
if shape_body[shape_id] >= 0:
raise ValueError(f"Conveyor shape must be static: {label}")
if surface_shapes[surface_id] >= 0:
raise RuntimeError(f"Multiple collision shapes match conveyor surface {path!r}.")
surface_shapes[surface_id] = shape_id
missing = [path for path, shape_id in zip(self._prim_paths, surface_shapes, strict=True) if shape_id < 0]
if missing:
raise RuntimeError(f"Missing {len(missing)} conveyor collision sections: {missing[:8]}.")

body_is_tracked = np.zeros(model.body_count, dtype=np.int32)
tracked_counts = np.zeros(self._num_envs, dtype=np.int32)
body_world = model.body_world.numpy()
for body_id, label in enumerate(model.body_label):
if self._body_pattern.search(label) is None:
continue
world_id = int(body_world[body_id])
if not 0 <= world_id < self._num_envs:
raise RuntimeError(f"Transported body {label!r} belongs to invalid world {world_id}.")
body_is_tracked[body_id] = 1
tracked_counts[world_id] += 1
if np.any(tracked_counts != self._body_count_per_env):
raise RuntimeError(
f"Body pattern {self._body_pattern.pattern!r} expected {self._body_count_per_env} bodies per world, "
f"got {tracked_counts.tolist()}."
)
if not np.any(body_is_tracked):
raise RuntimeError(f"Body pattern {self._body_pattern.pattern!r} matched no Newton bodies.")

conveyor = ConveyorForceModel(model, solver_type="mujoco")
transforms = model.shape_transform.numpy()
for surface_id, shape_id in enumerate(surface_shapes):
spec = self._surface_specs[surface_id % surfaces_per_env]
values = transforms[shape_id]
transform = wp.transform(wp.vec3(*values[:3]), wp.quat(*values[3:]))
direction = np.asarray(_world_vector(transform, spec.direction), dtype=np.float32)
speed = np.float32(spec.velocity)
parameters = {
"surface_normal": _world_vector(transform, spec.surface_normal),
"friction": spec.friction_coefficient,
"threshold": spec.contact_threshold,
}
if spec.curved:
conveyor.add_pivot_belt(
shape_id,
wp.transform_point(transform, wp.vec3(*spec.pivot_point)),
wp.vec3(*(direction * (speed / np.float32(spec.radius)))),
**parameters,
)
else:
conveyor.add_constant_belt(shape_id, wp.vec3(*(direction * speed)), **parameters)
conveyor.finalize(contacts, observables)
conveyor.set_speed_scale(0.0)

self._model = model
self._conveyor = conveyor
self._body_is_tracked = wp.array(body_is_tracked, dtype=wp.int32, device=model.device)
self._elapsed_time = wp.zeros(1, dtype=wp.float32, device=model.device)
self._world_mask_host = np.zeros(self._num_envs, dtype=np.bool_)
self._world_mask = wp.zeros(self._num_envs, dtype=wp.bool, device=model.device)
self._manager.register_state_force_callback(self._apply)
self._manager.register_post_solver_substep_callback(self._update)

def _apply(self, state) -> None:
"""Apply the wrench from the preceding solve."""
self._conveyor.apply(state)

def _update(self, contacts, observables, state, dt: float) -> None:
"""Compute the next wrench using Newton's upstream traction model."""
wp.launch(
_advance_startup_scale,
dim=1,
inputs=[dt, self._startup_duration_s],
outputs=[self._elapsed_time, self._conveyor.global_velocity_scale],
device=self._model.device,
)
self._conveyor.update(contacts, observables, state, dt)
wp.launch(
_filter_body_forces,
dim=self._model.body_count,
inputs=[self._body_is_tracked],
outputs=[self._conveyor.conveyor_body_f],
device=self._model.device,
)

def reset(self, env_ids: Sequence[int] | torch.Tensor | None = None) -> None:
"""Clear selected-world forces; a full reset also restarts the startup ramp."""
if self._conveyor is None:
raise RuntimeError("Surface velocity is not bound to an initialized Newton solver.")
if env_ids is None:
self._conveyor.conveyor_body_f.zero_()
self._elapsed_time.zero_()
self._conveyor.global_velocity_scale.zero_()
return
ids = env_ids.detach().cpu().numpy() if isinstance(env_ids, torch.Tensor) else np.asarray(env_ids)
if not np.issubdtype(ids.dtype, np.integer):
raise IndexError(f"Surface reset environment indices must be integers, got {env_ids!r}.")
ids = ids.reshape(-1)
if np.any((ids < 0) | (ids >= self._num_envs)):
raise IndexError(f"Conveyor reset environment indices are out of range: {ids.tolist()}.")
self._world_mask_host.fill(False)
self._world_mask_host[ids] = True
if np.all(self._world_mask_host):
self.reset()
return
self._world_mask.assign(self._world_mask_host)
wp.launch(
_clear_selected_body_forces,
dim=self._model.body_count,
inputs=[self._model.body_world, self._world_mask],
outputs=[self._conveyor.conveyor_body_f],
device=self._model.device,
)

def close(self) -> None:
"""Detach lifecycle and graph callbacks before the model is released."""
if self._closed:
return
self._manager.unregister_solver_init_callback(self._bind_solver)
self._physics_ready_handle.deregister()
self._manager.unregister_state_force_callback(self._apply)
self._manager.unregister_post_solver_substep_callback(self._update)
self._conveyor = None
self._model = None
self._closed = True
Loading
Loading