Skip to content

Commit a338d7d

Browse files
Merge branch 'develop' into offline_mode
2 parents e47ccf1 + 8e7f1b9 commit a338d7d

226 files changed

Lines changed: 23500 additions & 7559 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/source/migration/migrating_to_isaaclab_3-0.rst

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,209 @@ quaternions in XYZW format:
571571
- And all other quaternion utilities
572572

573573

574+
Warp Backend for Asset and Sensor Data
575+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
576+
577+
All ``.data.*`` properties on asset and sensor classes now return ``wp.array`` instead of
578+
``torch.Tensor``. This change applies to all asset classes (:class:`~isaaclab.assets.Articulation`,
579+
:class:`~isaaclab.assets.RigidObject`, :class:`~isaaclab.assets.RigidObjectCollection`,
580+
:class:`~isaaclab_physx.assets.DeformableObject`) and all sensor classes
581+
(:class:`~isaaclab_physx.sensors.ContactSensor`, :class:`~isaaclab_physx.sensors.Imu`,
582+
:class:`~isaaclab_physx.sensors.FrameTransformer`).
583+
584+
To convert back to ``torch.Tensor`` for use with PyTorch operations, wrap the property
585+
access with ``wp.to_torch()``:
586+
587+
.. code-block:: python
588+
589+
import warp as wp
590+
591+
# Before (Isaac Lab 2.x)
592+
root_pos = robot.data.root_pos_w # torch.Tensor
593+
joint_pos = robot.data.joint_pos # torch.Tensor
594+
contact_forces = sensor.data.net_forces_w # torch.Tensor
595+
596+
# After (Isaac Lab 3.x)
597+
root_pos = robot.data.root_pos_w # wp.array
598+
joint_pos = robot.data.joint_pos # wp.array
599+
contact_forces = sensor.data.net_forces_w # wp.array
600+
601+
# To use with torch operations, wrap with wp.to_torch()
602+
root_pos_torch = wp.to_torch(robot.data.root_pos_w) # torch.Tensor
603+
joint_pos_torch = wp.to_torch(robot.data.joint_pos) # torch.Tensor
604+
contact_torch = wp.to_torch(sensor.data.net_forces_w) # torch.Tensor
605+
606+
Common patterns that need updating:
607+
608+
.. code-block:: python
609+
610+
# Cloning data
611+
# Before:
612+
pos = robot.data.root_pos_w.clone()
613+
# After:
614+
pos = wp.to_torch(robot.data.root_pos_w).clone()
615+
616+
# Creating zero tensors with matching shape
617+
# Before:
618+
zeros = torch.zeros_like(robot.data.root_pos_w)
619+
# After:
620+
zeros = torch.zeros_like(wp.to_torch(robot.data.root_pos_w))
621+
622+
# Assertions in tests
623+
# Before:
624+
torch.testing.assert_close(robot.data.root_pos_w, expected)
625+
# After:
626+
torch.testing.assert_close(wp.to_torch(robot.data.root_pos_w), expected)
627+
628+
.. list-table:: Affected classes
629+
:header-rows: 1
630+
:widths: 40 60
631+
632+
* - Class
633+
- Package
634+
* - :class:`~isaaclab.assets.Articulation`
635+
- ``isaaclab`` / ``isaaclab_physx``
636+
* - :class:`~isaaclab.assets.RigidObject`
637+
- ``isaaclab`` / ``isaaclab_physx``
638+
* - :class:`~isaaclab.assets.RigidObjectCollection`
639+
- ``isaaclab`` / ``isaaclab_physx``
640+
* - :class:`~isaaclab_physx.assets.DeformableObject`
641+
- ``isaaclab_physx``
642+
* - :class:`~isaaclab_physx.sensors.ContactSensor`
643+
- ``isaaclab_physx``
644+
* - :class:`~isaaclab_physx.sensors.Imu`
645+
- ``isaaclab_physx``
646+
* - :class:`~isaaclab_physx.sensors.FrameTransformer`
647+
- ``isaaclab_physx``
648+
649+
.. note::
650+
651+
An automated migration tool is provided at ``scripts/tools/wrap_warp_to_torch.py``.
652+
It scans Python files for ``.data.<property>`` accesses and wraps them with
653+
``wp.to_torch()``. Usage:
654+
655+
.. code-block:: bash
656+
657+
# Dry run (preview changes)
658+
python scripts/tools/wrap_warp_to_torch.py path/to/your/code --dry-run
659+
660+
# Apply changes in-place
661+
python scripts/tools/wrap_warp_to_torch.py path/to/your/code
662+
663+
Always review the changes after running the tool, as some accesses (e.g., those
664+
already passed to warp-native functions) should not be wrapped.
665+
666+
667+
Write Method Index/Mask Split
668+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
669+
670+
All asset write methods have been split into two explicit variants:
671+
672+
- ``write_*_to_sim_index(data, env_ids)`` — accepts partial data for a sparse set of
673+
environment indices. The ``data`` tensor has shape ``(len(env_ids), ...)``.
674+
- ``write_*_to_sim_mask(data, env_mask)`` — accepts full data for all environments with a
675+
boolean mask selecting which environments to update. The ``data`` tensor has shape
676+
``(num_envs, ...)``.
677+
678+
The previous ``write_*_to_sim(data, env_ids)`` methods have been removed.
679+
680+
.. code-block:: python
681+
682+
# Before (Isaac Lab 2.x)
683+
robot.write_root_pose_to_sim(pose_data, env_ids)
684+
685+
# After (Isaac Lab 3.x) — indexed variant (partial data)
686+
robot.write_root_pose_to_sim_index(pose_data, env_ids)
687+
688+
# After (Isaac Lab 3.x) — mask variant (full data, boolean mask)
689+
robot.write_root_pose_to_sim_mask(pose_data, env_mask)
690+
691+
.. list-table:: Affected write methods (RigidObject / Articulation)
692+
:header-rows: 1
693+
:widths: 50 50
694+
695+
* - Old method
696+
- New methods
697+
* - ``write_root_pose_to_sim``
698+
- ``write_root_pose_to_sim_index`` / ``write_root_pose_to_sim_mask``
699+
* - ``write_root_link_pose_to_sim``
700+
- ``write_root_link_pose_to_sim_index`` / ``write_root_link_pose_to_sim_mask``
701+
* - ``write_root_com_pose_to_sim``
702+
- ``write_root_com_pose_to_sim_index`` / ``write_root_com_pose_to_sim_mask``
703+
* - ``write_root_velocity_to_sim``
704+
- ``write_root_velocity_to_sim_index`` / ``write_root_velocity_to_sim_mask``
705+
* - ``write_root_com_velocity_to_sim``
706+
- ``write_root_com_velocity_to_sim_index`` / ``write_root_com_velocity_to_sim_mask``
707+
* - ``write_root_link_velocity_to_sim``
708+
- ``write_root_link_velocity_to_sim_index`` / ``write_root_link_velocity_to_sim_mask``
709+
710+
.. list-table:: Additional Articulation-specific write methods
711+
:header-rows: 1
712+
:widths: 50 50
713+
714+
* - Old method
715+
- New methods
716+
* - ``write_joint_position_to_sim``
717+
- ``write_joint_position_to_sim_index`` / ``write_joint_position_to_sim_mask``
718+
* - ``write_joint_velocity_to_sim``
719+
- ``write_joint_velocity_to_sim_index`` / ``write_joint_velocity_to_sim_mask``
720+
* - ``write_joint_stiffness_to_sim``
721+
- ``write_joint_stiffness_to_sim_index`` / ``write_joint_stiffness_to_sim_mask``
722+
* - ``write_joint_damping_to_sim``
723+
- ``write_joint_damping_to_sim_index`` / ``write_joint_damping_to_sim_mask``
724+
* - ``write_joint_position_limit_to_sim``
725+
- ``write_joint_position_limit_to_sim_index`` / ``write_joint_position_limit_to_sim_mask``
726+
* - ``write_joint_velocity_limit_to_sim``
727+
- ``write_joint_velocity_limit_to_sim_index`` / ``write_joint_velocity_limit_to_sim_mask``
728+
* - ``write_joint_effort_limit_to_sim``
729+
- ``write_joint_effort_limit_to_sim_index`` / ``write_joint_effort_limit_to_sim_mask``
730+
* - ``write_joint_armature_to_sim``
731+
- ``write_joint_armature_to_sim_index`` / ``write_joint_armature_to_sim_mask``
732+
* - ``write_joint_friction_coefficient_to_sim``
733+
- ``write_joint_friction_coefficient_to_sim_index`` / ``write_joint_friction_coefficient_to_sim_mask``
734+
735+
.. list-table:: RigidObjectCollection write methods
736+
:header-rows: 1
737+
:widths: 50 50
738+
739+
* - Old method
740+
- New methods
741+
* - ``write_body_pose_to_sim``
742+
- ``write_body_pose_to_sim_index`` / ``write_body_pose_to_sim_mask``
743+
* - ``write_body_link_pose_to_sim``
744+
- ``write_body_link_pose_to_sim_index`` / ``write_body_link_pose_to_sim_mask``
745+
* - ``write_body_com_pose_to_sim``
746+
- ``write_body_com_pose_to_sim_index`` / ``write_body_com_pose_to_sim_mask``
747+
* - ``write_body_velocity_to_sim``
748+
- ``write_body_velocity_to_sim_index`` / ``write_body_velocity_to_sim_mask``
749+
* - ``write_body_com_velocity_to_sim``
750+
- ``write_body_com_velocity_to_sim_index`` / ``write_body_com_velocity_to_sim_mask``
751+
* - ``write_body_link_velocity_to_sim``
752+
- ``write_body_link_velocity_to_sim_index`` / ``write_body_link_velocity_to_sim_mask``
753+
754+
755+
TimestampedBufferWarp
756+
~~~~~~~~~~~~~~~~~~~~~
757+
758+
If you have custom asset or sensor data classes that subclass the Isaac Lab base data classes,
759+
note that internal buffers have changed from :class:`~isaaclab.utils.buffers.TimestampedBuffer`
760+
to :class:`~isaaclab.utils.buffers.TimestampedBufferWarp`. The new class takes ``(shape, device,
761+
wp_dtype)`` as constructor arguments instead of a ``torch.Tensor``:
762+
763+
.. code-block:: python
764+
765+
import warp as wp
766+
from isaaclab.utils.buffers import TimestampedBufferWarp
767+
768+
# Before (Isaac Lab 2.x)
769+
self._data.root_pos_w = TimestampedBuffer(torch.zeros(num_envs, 3, device=device))
770+
771+
# After (Isaac Lab 3.x)
772+
self._data.root_pos_w = TimestampedBufferWarp(
773+
shape=(num_envs,), device=device, wp_dtype=wp.vec3f
774+
)
775+
776+
574777
Need Help?
575778
~~~~~~~~~~
576779

docs/source/tutorials/01_assets/run_deformable_object.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ the average position of all the nodes in the mesh.
149149
.. literalinclude:: ../../../../scripts/tutorials/01_assets/run_deformable_object.py
150150
:language: python
151151
:start-at: # update buffers
152-
:end-at: print(f"Root position (in world): {cube_object.data.root_pos_w[:, :3]}")
152+
:end-at: print(f"Root position (in world): {wp.to_torch(cube_object.data.root_pos_w)[:, :3]}")
153153

154154

155155
The Code Execution

scripts/benchmarks/benchmark_load_robot.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
imports_time_begin = time.perf_counter_ns()
5858

5959
import torch
60+
import warp as wp
6061

6162
import isaaclab.sim as sim_utils
6263
from isaaclab.assets import ArticulationCfg, AssetBaseCfg
@@ -133,19 +134,22 @@ def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene):
133134
# root state
134135
# we offset the root state by the origin since the states are written in simulation world frame
135136
# if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
136-
root_state = robot.data.default_root_state.clone()
137+
root_state = wp.to_torch(robot.data.default_root_state).clone()
137138
root_state[:, :3] += scene.env_origins
138139
robot.write_root_pose_to_sim(root_state[:, :7])
139140
robot.write_root_velocity_to_sim(root_state[:, 7:])
140141
# set joint positions with some noise
141-
joint_pos, joint_vel = robot.data.default_joint_pos.clone(), robot.data.default_joint_vel.clone()
142+
joint_pos, joint_vel = (
143+
wp.to_torch(robot.data.default_joint_pos).clone(),
144+
wp.to_torch(robot.data.default_joint_vel).clone(),
145+
)
142146
joint_pos += torch.rand_like(joint_pos) * 0.1
143147
robot.write_joint_state_to_sim(joint_pos, joint_vel)
144148
# clear internal buffers
145149
scene.reset()
146150
# Apply random action
147151
# -- generate random joint efforts
148-
efforts = torch.randn_like(robot.data.joint_pos) * 5.0
152+
efforts = torch.randn_like(wp.to_torch(robot.data.joint_pos)) * 5.0
149153
# -- apply action to the robot
150154
robot.set_joint_effort_target(efforts)
151155
# -- write data to sim

scripts/demos/arms.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
import numpy as np
3636
import torch
37+
import warp as wp
3738

3839
import isaaclab.sim as sim_utils
3940
from isaaclab.assets import Articulation
@@ -188,10 +189,11 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula
188189
# apply random actions to the robots
189190
for robot in entities.values():
190191
# generate random joint positions
191-
joint_pos_target = robot.data.default_joint_pos + torch.randn_like(robot.data.joint_pos) * 0.1
192-
joint_pos_target = joint_pos_target.clamp_(
193-
robot.data.soft_joint_pos_limits[..., 0], robot.data.soft_joint_pos_limits[..., 1]
192+
joint_pos_target = (
193+
wp.to_torch(robot.data.default_joint_pos) + torch.randn_like(wp.to_torch(robot.data.joint_pos)) * 0.1
194194
)
195+
soft_limits = wp.to_torch(robot.data.soft_joint_pos_limits)
196+
joint_pos_target = joint_pos_target.clamp_(soft_limits[..., 0], soft_limits[..., 1])
195197
# apply action to the robot
196198
robot.set_joint_position_target(joint_pos_target)
197199
# write data to sim

scripts/demos/bipeds.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"""Rest everything follows."""
3434

3535
import torch
36+
import warp as wp
3637

3738
import isaaclab.sim as sim_utils
3839
from isaaclab.assets import Articulation
@@ -88,9 +89,12 @@ def run_simulator(sim: sim_utils.SimulationContext, robots: list[Articulation],
8889
count = 0
8990
for index, robot in enumerate(robots):
9091
# reset dof state
91-
joint_pos, joint_vel = robot.data.default_joint_pos, robot.data.default_joint_vel
92+
joint_pos, joint_vel = (
93+
wp.to_torch(robot.data.default_joint_pos),
94+
wp.to_torch(robot.data.default_joint_vel),
95+
)
9296
robot.write_joint_state_to_sim(joint_pos, joint_vel)
93-
root_state = robot.data.default_root_state.clone()
97+
root_state = wp.to_torch(robot.data.default_root_state).clone()
9498
root_state[:, :3] += origins[index]
9599
robot.write_root_pose_to_sim(root_state[:, :7])
96100
robot.write_root_velocity_to_sim(root_state[:, 7:])
@@ -99,7 +103,7 @@ def run_simulator(sim: sim_utils.SimulationContext, robots: list[Articulation],
99103
print(">>>>>>>> Reset!")
100104
# apply action to the robot
101105
for robot in robots:
102-
robot.set_joint_position_target(robot.data.default_joint_pos.clone())
106+
robot.set_joint_position_target(wp.to_torch(robot.data.default_joint_pos).clone())
103107
robot.write_data_to_sim()
104108
# perform step
105109
sim.step()

scripts/demos/deformables.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import numpy as np
3737
import torch
3838
import tqdm
39+
import warp as wp
3940

4041
import isaaclab.sim as sim_utils
4142
from isaaclab.assets import DeformableObject, DeformableObjectCfg
@@ -160,7 +161,7 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Deformab
160161
# reset deformable object state
161162
for _, deform_body in enumerate(entities.values()):
162163
# root state
163-
nodal_state = deform_body.data.default_nodal_state_w.clone()
164+
nodal_state = wp.to_torch(deform_body.data.default_nodal_state_w).clone()
164165
deform_body.write_nodal_state_to_sim(nodal_state)
165166
# reset the internal state
166167
deform_body.reset()

scripts/demos/h1_locomotion.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"""Rest everything follows."""
4444

4545
import torch
46+
import warp as wp
4647
from rsl_rl.runners import OnPolicyRunner
4748

4849
import carb
@@ -196,8 +197,10 @@ def _update_camera(self):
196197
"""Updates the per-frame transform of the third-person view camera to follow
197198
the selected robot's torso transform."""
198199

199-
base_pos = self.env.unwrapped.scene["robot"].data.root_pos_w[self._selected_id, :] # - env.scene.env_origins
200-
base_quat = self.env.unwrapped.scene["robot"].data.root_quat_w[self._selected_id, :]
200+
base_pos = wp.to_torch(self.env.unwrapped.scene["robot"].data.root_pos_w)[
201+
self._selected_id, :
202+
] # - env.scene.env_origins
203+
base_quat = wp.to_torch(self.env.unwrapped.scene["robot"].data.root_quat_w)[self._selected_id, :]
201204

202205
camera_pos = quat_apply(base_quat, self._camera_local_transform) + base_pos
203206

scripts/demos/hands.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
import numpy as np
3636
import torch
37+
import warp as wp
3738

3839
import isaaclab.sim as sim_utils
3940
from isaaclab.assets import Articulation
@@ -109,12 +110,15 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula
109110
# reset robots
110111
for index, robot in enumerate(entities.values()):
111112
# root state
112-
root_state = robot.data.default_root_state.clone()
113+
root_state = wp.to_torch(robot.data.default_root_state).clone()
113114
root_state[:, :3] += origins[index]
114115
robot.write_root_pose_to_sim(root_state[:, :7])
115116
robot.write_root_velocity_to_sim(root_state[:, 7:])
116117
# joint state
117-
joint_pos, joint_vel = robot.data.default_joint_pos.clone(), robot.data.default_joint_vel.clone()
118+
joint_pos, joint_vel = (
119+
wp.to_torch(robot.data.default_joint_pos).clone(),
120+
wp.to_torch(robot.data.default_joint_vel).clone(),
121+
)
118122
robot.write_joint_state_to_sim(joint_pos, joint_vel)
119123
# reset the internal state
120124
robot.reset()
@@ -125,7 +129,7 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula
125129
# apply default actions to the hands robots
126130
for robot in entities.values():
127131
# generate joint positions
128-
joint_pos_target = robot.data.soft_joint_pos_limits[..., grasp_mode]
132+
joint_pos_target = wp.to_torch(robot.data.soft_joint_pos_limits)[..., grasp_mode]
129133
# apply action to the robot
130134
robot.set_joint_position_target(joint_pos_target)
131135
# write data to sim

0 commit comments

Comments
 (0)