Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions .github/actions/run-package-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ inputs:
spawned by tools/conftest.py (combined with device-split selectors).
default: ''
required: false
test-jobs:
description: >-
Number of test-file slots tools/conftest.py runs at once. Each file still runs in
its own pytest process; files listed in tools/test_settings.py PYTEST_WORKERS split
across several slots with pytest-xdist. Empty or 1 runs files one by one.
default: ''
required: false
shard-index:
description: 'Zero-based shard index'
default: ''
Expand Down Expand Up @@ -333,6 +340,7 @@ runs:
filter-pattern: ${{ inputs.filter-pattern }}
exclude-pattern: ${{ inputs.exclude-pattern }}
test-k-expr: ${{ inputs.test-k-expr }}
test-jobs: ${{ inputs.test-jobs }}
shard-index: ${{ inputs.shard-index }}
shard-count: ${{ inputs.shard-count }}
curobo-only: ${{ inputs.curobo-only }}
Expand Down
10 changes: 9 additions & 1 deletion .github/actions/run-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ inputs:
can deselect parametrized cases (e.g. "not ovphysx").
default: ''
required: false
test-jobs:
description: >-
Number of test-file slots tools/conftest.py runs at once. Each file still runs in
its own pytest process; files listed in tools/test_settings.py PYTEST_WORKERS split
across several slots with pytest-xdist. Empty or 1 runs files one by one.
default: ''
required: false
curobo-only:
description: 'Run only cuRobo and SkillGen tests (requires the cuRobo Docker image)'
default: 'false'
Expand Down Expand Up @@ -155,13 +162,14 @@ runs:
TEST_NODE_IDS_KEY: ${{ inputs.test-node-ids-key }}
TEST_PATH: ${{ inputs.test-path }}
TEST_K_EXPR_INPUT: ${{ inputs.test-k-expr }}
TEST_JOBS_INPUT: ${{ inputs.test-jobs }}
CI_MARKER_INPUT: ${{ inputs.ci-marker }}
VOLUME_MOUNT_SOURCE: ${{ inputs.volume-mount-source }}
WARP_CACHE_HOST_DIR: ${{ inputs.warp-cache-host-dir }}
WHEELHOUSE_HOST_DIR: ${{ inputs.wheelhouse-host-dir }}
WHEELHOUSE_PACKAGES: ${{ inputs.wheelhouse-packages }}
run: |
bash .github/actions/run-tests/run_tests.sh "$TEST_PATH" "$RESULT_FILE" "$CONTAINER_NAME" "$IMAGE_TAG" "$REPORTS_DIR" "$PYTEST_OPTIONS" "$FILTER_PATTERN" "$EXCLUDE_PATTERN" "$CUROBO_ONLY" "$INCLUDE_FILES" "$QUARANTINED_ONLY" "$SHARD_INDEX" "$SHARD_COUNT" "$VOLUME_MOUNT_SOURCE" "$EXTRA_PIP_PACKAGES" "$TEST_NODE_IDS_FILE" "$TEST_NODE_IDS_KEY" "$WHEELHOUSE_HOST_DIR" "$WHEELHOUSE_PACKAGES" "$TEST_K_EXPR_INPUT" "$CI_MARKER_INPUT" "$STANDALONE_SCRIPT_SCOPE" "$STANDALONE_SCRIPT_VISUALIZER" "$STANDALONE_SCRIPT_RUNTIME_GROUP" "$WARP_CACHE_HOST_DIR" "$EXTRA_UV_PACKAGES" "$OVRTX_SHADER_CACHE_HOST_DIR"
bash .github/actions/run-tests/run_tests.sh "$TEST_PATH" "$RESULT_FILE" "$CONTAINER_NAME" "$IMAGE_TAG" "$REPORTS_DIR" "$PYTEST_OPTIONS" "$FILTER_PATTERN" "$EXCLUDE_PATTERN" "$CUROBO_ONLY" "$INCLUDE_FILES" "$QUARANTINED_ONLY" "$SHARD_INDEX" "$SHARD_COUNT" "$VOLUME_MOUNT_SOURCE" "$EXTRA_PIP_PACKAGES" "$TEST_NODE_IDS_FILE" "$TEST_NODE_IDS_KEY" "$WHEELHOUSE_HOST_DIR" "$WHEELHOUSE_PACKAGES" "$TEST_K_EXPR_INPUT" "$CI_MARKER_INPUT" "$STANDALONE_SCRIPT_SCOPE" "$STANDALONE_SCRIPT_VISUALIZER" "$STANDALONE_SCRIPT_RUNTIME_GROUP" "$WARP_CACHE_HOST_DIR" "$EXTRA_UV_PACKAGES" "$OVRTX_SHADER_CACHE_HOST_DIR" "$TEST_JOBS_INPUT"
- name: Kill container on cancellation
if: cancelled()
shell: bash
Expand Down
6 changes: 6 additions & 0 deletions .github/actions/run-tests/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ run_tests() {
local warp_cache_host_dir="${25}"
local extra_uv_packages="${26}"
local ovrtx_shader_cache_host_dir="${27}"
local test_jobs="${28}"
local logs_pid=""
local wait_pid=""
local docker_wait_file="/tmp/.docker_exit_${container_name}"
Expand Down Expand Up @@ -194,6 +195,11 @@ run_tests() {
echo "Setting per-file pytest -k expression: $test_k_expr"
fi

if [ -n "$test_jobs" ]; then
docker_env_args+=(-e "TEST_JOBS=$test_jobs")
echo "Setting TEST_JOBS=$test_jobs"
fi

if [ -n "$ci_marker" ]; then
docker_env_args+=(-e "CI_MARKER=$ci_marker")
echo "Setting CI_MARKER=$ci_marker"
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,8 @@ jobs:
shard-index: "0"
shard-count: "3"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-tasks-1-test

test-isaaclab-tasks-2:
Expand All @@ -301,6 +303,8 @@ jobs:
shard-index: "1"
shard-count: "3"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-tasks-2-test

test-isaaclab-tasks-3:
Expand All @@ -327,6 +331,8 @@ jobs:
shard-index: "2"
shard-count: "3"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-tasks-3-test

test-isaaclab-core:
Expand All @@ -352,6 +358,8 @@ jobs:
shard-index: "0"
shard-count: "3"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-core-1-test

test-isaaclab-core-2:
Expand All @@ -377,6 +385,8 @@ jobs:
shard-index: "1"
shard-count: "3"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-core-2-test

test-isaaclab-core-3:
Expand All @@ -402,6 +412,8 @@ jobs:
shard-index: "2"
shard-count: "3"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-core-3-test

# Kit and non-Kit are written out rather than expressed as a matrix: these job
Expand Down Expand Up @@ -486,6 +498,8 @@ jobs:
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_rl"
extra-pip-packages: "leapp==0.6.1 torchrl>=0.13"
warp-cache: restore
test-jobs: "4"
container-name: isaac-lab-rl-test

test-isaaclab-mimic:
Expand All @@ -507,6 +521,8 @@ jobs:
isaacsim-base-image: ${{ needs.config.outputs.isaacsim_image_name }}
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_mimic"
warp-cache: restore
test-jobs: "4"
container-name: isaac-lab-mimic-test

test-isaaclab-contrib:
Expand All @@ -528,6 +544,9 @@ jobs:
isaacsim-base-image: ${{ needs.config.outputs.isaacsim_image_name }}
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_contrib"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-contrib-test

test-isaaclab-teleop:
Expand All @@ -549,6 +568,9 @@ jobs:
isaacsim-base-image: ${{ needs.config.outputs.isaacsim_image_name }}
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_teleop"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-teleop-test

test-isaaclab-visualizers:
Expand All @@ -570,6 +592,9 @@ jobs:
isaacsim-base-image: ${{ needs.config.outputs.isaacsim_image_name }}
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_visualizers"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-visualizers-test

test-isaaclab-assets:
Expand All @@ -591,6 +616,8 @@ jobs:
isaacsim-base-image: ${{ needs.config.outputs.isaacsim_image_name }}
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_assets"
warp-cache: restore
test-jobs: "4"
container-name: isaac-lab-assets-test

test-isaaclab-experimental:
Expand All @@ -612,6 +639,8 @@ jobs:
isaacsim-base-image: ${{ needs.config.outputs.isaacsim_image_name }}
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_experimental"
warp-cache: restore
test-jobs: "4"
container-name: isaac-lab-experimental-test

test-isaaclab-newton:
Expand All @@ -634,6 +663,8 @@ jobs:
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_newton"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-newton-test

test-isaaclab-physx:
Expand All @@ -655,6 +686,9 @@ jobs:
isaacsim-base-image: ${{ needs.config.outputs.isaacsim_image_name }}
isaacsim-version: ${{ needs.config.outputs.isaacsim_image_tag }}
filter-pattern: "isaaclab_physx"
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-physx-test

test-isaaclab-ov:
Expand Down Expand Up @@ -684,6 +718,9 @@ jobs:
extra-pip-packages: ${{ env.USE_OVPHYSX_WHEELHOUSE == 'true' && steps.ov_pins.outputs.ovrtx || format('{0} {1}', steps.ov_pins.outputs.ovrtx, steps.ov_pins.outputs.ovphysx) }}
wheelhouse-image: ${{ env.USE_OVPHYSX_WHEELHOUSE == 'true' && needs.config.outputs.ovphysx_wheelhouse_image || '' }}
wheelhouse-packages: ${{ env.USE_OVPHYSX_WHEELHOUSE == 'true' && 'ovphysx' || '' }}
warp-cache: restore
ovrtx-shader-cache: restore
test-jobs: "4"
container-name: isaac-lab-ov-test

# Folded from the former standalone verify-base-non-root job: reuses the
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/tools-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ jobs:
# flaky, so they run without an Isaac Sim install or a full project sync. flaky drives the
# rerun in test_crash_during_a_flaky_retry_is_blamed_on_the_retried_test; without it
# installed that test skips itself rather than failing, so keep it in this list.
# pytest-xdist likewise drives test_an_xdist_run_journals_each_event_once.
- name: Install test dependencies
run: bash "$GITHUB_WORKSPACE/.github/actions/_lib/with-python-package-retries.sh" python3 -m pip install pytest junitparser flaky pyyaml
run: bash "$GITHUB_WORKSPACE/.github/actions/_lib/with-python-package-retries.sh" python3 -m pip install pytest pytest-xdist junitparser flaky pyyaml

- name: Run tools tests
env:
Expand All @@ -65,5 +66,5 @@ jobs:
# --noconftest keeps tools/conftest.py out of the session. That file is the CI test
# orchestrator - its pytest_sessionstart scans source/ and scripts/ and runs the whole
# suite, so loading it here would ignore the files named below.
run: python3 -m pytest tools/test_crash_journal.py tools/test_device_split.py
run: python3 -m pytest tools/test_crash_journal.py tools/test_device_split.py tools/test_file_scheduler.py
.github/actions/_lib/test_registry_credential_fallback.py -v --noconftest
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
- Use modern Python type hints, including `X | None` instead of `Optional[X]`.
- Use `snake_case` for methods, functions, and CLI arguments.
- Keep related public symbols discoverable through consistent prefixes.
- Keep Newton solver schema registration in the active manager's builder factory; the cloner must not depend on solver modules.
- Resolve Newton raycast BVH requirements before builder finalization; sensor task registration must not add a late BVH fallback.
- Keep joint-wrench sensor coverage separate from articulation control-joint selection. Reuse cached
body bindings without changing the shared view's joint filters or creating a second view for sensing.
- Keep articulation ordering maps on articulation data; do not mirror maps or add cached ordering flags.
Expand Down
9 changes: 9 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,19 @@ def _journal_write(record: dict) -> None:
The per-record flush is the whole point: it puts the data in the OS page cache before the
next test starts, so a process killed by a signal cannot take down verdicts it had already
reported. Journaling failures are swallowed — losing debug context must never fail a run.

Under ``pytest-xdist`` the controller receives every worker's start, report and finish, so only
it journals those; each worker journaling too would record every event twice and leave a
worker crash that xdist recovered from looking like an in-flight test. The controller never
collects, so the ``collected`` record comes from the first worker instead (every worker
collects the same items).
"""
path = os.environ.get(JOURNAL_ENV_VAR)
if not path:
return
worker = os.environ.get("PYTEST_XDIST_WORKER")
if worker and (record["event"] != "collected" or worker != "gw0"):
return
try:
with open(path, "a", encoding="utf-8") as handle:
handle.write(json.dumps(record, separators=(",", ":")) + "\n")
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ importers = [
test = [
"pytest",
"pytest-mock",
# Splits a test file across worker processes; see TEST_PYTEST_WORKERS in tools/conftest.py.
"pytest-xdist",
"junitparser",
"flaky",
# numba subclasses coverage.types.Tracer at import; >=7.6.1 restores that shim
Expand Down
2 changes: 1 addition & 1 deletion scripts/tutorials/04_sensors/run_frame_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def define_sensor() -> FrameTransformer:
"""Defines the FrameTransformer sensor to add to the scene."""
# define offset
rot_offset = math_utils.quat_from_euler_xyz(torch.zeros(1), torch.zeros(1), torch.tensor(-math.pi / 2))
pos_offset = math_utils.quat_apply(rot_offset, torch.tensor([0.08795, 0.01305, -0.33797]))
pos_offset = math_utils.quat_apply(rot_offset[0], torch.tensor([0.08795, 0.01305, -0.33797]))

# Example using .* to get full body + LF_FOOT
frame_transformer_cfg = FrameTransformerCfg(
Expand Down
9 changes: 9 additions & 0 deletions source/isaaclab/changelog.d/core-cleanup-benchmark-fixes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Fixed
^^^^^

* Fixed :meth:`~isaaclab.benchmark.measurements.TestPhase.from_json` raising ``TypeError`` for phases
serialized with :class:`~isaaclab.benchmark.measurements.TestPhaseEncoder` that have measurements, and
dropping the metadata of phases without measurements. This also affected
:meth:`~isaaclab.benchmark.measurements.TestPhase.aggregate_json_files`.
* Fixed :meth:`~isaaclab.benchmark.recorders.record_cpu_info.CPUInfoRecorder.get_data` raising ``KeyError``
when called before the first :meth:`~isaaclab.benchmark.recorders.record_cpu_info.CPUInfoRecorder.update`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Fixed
^^^^^

* Fixed :class:`~isaaclab.actuators.ActuatorNetLSTM` clipping its output with a zero joint velocity. The DC-motor
torque-speed limits now use the current joint velocity, as :class:`~isaaclab.actuators.ActuatorNetMLP` does.
* Passed measured joint velocity directly into DC-motor clipping, removing the cached velocity and its redundant
copies. The base ``_clip_effort`` accepts ``(effort, *args, **kwargs)``; custom overrides used by explicit PD and
neural-network actuators must accept ``(effort, joint_vel)``. Implicit actuator clipping still takes only effort.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Fixed
^^^^^

* Fixed :func:`~isaaclab.utils.math.quat_apply` and :func:`~isaaclab.utils.math.quat_apply_inverse` to broadcast
leading dimensions following NumPy rules. This fixed :func:`~isaaclab.envs.mdp.observations.body_projected_gravity_b`
for multiple selected bodies. **Breaking change:** results retain the broadcast batch shape, including singleton
dimensions. Use a quaternion of shape ``(4,)`` for an unbatched vector result of shape ``(3,)``. Incompatible batch
shapes now raise an error even when their element counts match; callers relying on flattened pairing must explicitly
reshape their inputs to matching batch shapes. Outputs may be noncontiguous for transposed inputs; use ``reshape``
instead of ``view`` when flattening these results.
6 changes: 6 additions & 0 deletions source/isaaclab/changelog.d/ground-plane-walking-margin.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fixed
^^^^^

* Extended the default visual ground plane 50 m beyond the outermost environment
origins so locomotion robots did not immediately leave the visible floor in
large batches. Preserved metric texture tiling and the infinite collision plane.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Regression coverage for performance test isolation and updated test fixtures for the parallel runner.
5 changes: 5 additions & 0 deletions source/isaaclab/changelog.d/pink-ik-concurrent-conversion.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fixed
^^^^^

* Fixed concurrent Pink IK controller initialization corrupting shared USD-to-URDF exports by locking
the output directory through conversion and model loading.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fixed
^^^^^

* Followed USD dependencies through both local layers and remote assets before adding references, resolving nested remote references in working copies without editing authored layers or raw downloads. Preserved renderer-provided MDL identifiers and self-contained USDZ packages. Completed local trees skipped repeated traversal until their files changed or disappeared.
5 changes: 5 additions & 0 deletions source/isaaclab/changelog.d/test-audit-newton-tests.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fixed
^^^^^

* Fixed :func:`~isaaclab.utils.warp.math_ops.transform_to_vec_quat` raising a Warp ``RuntimeError`` instead of its
documented ``ValueError`` for 4D transform arrays.
8 changes: 6 additions & 2 deletions source/isaaclab/isaaclab/actuators/actuator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,15 @@ def compute(
Helper functions.
"""

def _clip_effort(self, effort: torch.Tensor) -> torch.Tensor:
"""Clip the desired torques based on the motor limits.
def _clip_effort(self, effort: torch.Tensor, *args, **kwargs) -> torch.Tensor:
"""Clip the desired effort using actuator effort limits.

Model-specific inputs are handled by subclasses.

Args:
effort: The effort to clip [N or N·m, depending on joint type].
*args: Model-specific positional inputs. Unused by the base implementation.
**kwargs: Model-specific keyword inputs. Unused by the base implementation.

Returns:
The clipped effort [N or N·m, depending on joint type].
Expand Down
7 changes: 2 additions & 5 deletions source/isaaclab/isaaclab/actuators/actuator_net.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def compute(
self.computed_effort = torques.reshape(self._num_envs, self.num_joints)

# clip the computed effort based on the motor limits
self.applied_effort = self._clip_effort(self.computed_effort)
self.applied_effort = self._clip_effort(self.computed_effort, joint_vel)

# return torques
control_action.joint_efforts = self.applied_effort
Expand Down Expand Up @@ -152,9 +152,6 @@ def compute(
# -- velocity
self._joint_vel_history = self._joint_vel_history.roll(1, 1)
self._joint_vel_history[:, 0] = joint_vel
# save current joint vel for dc-motor clipping
self._joint_vel[:] = joint_vel

# compute network inputs
# -- positions
pos_input = torch.cat([self._joint_pos_error_history[:, i].unsqueeze(2) for i in self.cfg.input_idx], dim=2)
Expand All @@ -178,7 +175,7 @@ def compute(
self.computed_effort = torques.view(self._num_envs, self.num_joints) * self.cfg.torque_scale

# clip the computed effort based on the motor limits
self.applied_effort = self._clip_effort(self.computed_effort)
self.applied_effort = self._clip_effort(self.computed_effort, joint_vel)

# return torques
control_action.joint_efforts = self.applied_effort
Expand Down
Loading
Loading