Unverified Commit 65d6087f authored by James Tigue's avatar James Tigue Committed by GitHub

Adds `SensorBase` tests (#3160)

# Description

Adds tests for SensorBase

## Type of change

<!-- As you go through the list, delete the ones that are not
applicable. -->

- New feature (non-breaking change which adds functionality)

## Checklist

- [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [ ] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->

---------
Signed-off-by: 's avatarJames Tigue <166445701+jtigue-bdai@users.noreply.github.com>
Signed-off-by: 's avatarooctipus <zhengyuz@nvidia.com>
Co-authored-by: 's avatarKelly Guo <kellyg@nvidia.com>
Co-authored-by: 's avatarooctipus <zhengyuz@nvidia.com>
parent 1401b50f
[package] [package]
# Note: Semantic Versioning is used: https://semver.org/ # Note: Semantic Versioning is used: https://semver.org/
version = "0.45.3" version = "0.45.4"
# Description # Description
title = "Isaac Lab framework for Robot Learning" title = "Isaac Lab framework for Robot Learning"
......
Changelog Changelog
--------- ---------
0.45.4 (2025-08-21)
~~~~~~~~~~~~~~~~~~~~
Added
^^^^^
* Added unit tests for :class:`~isaaclab.sensor.sensor_base`
0.45.3 (2025-08-20) 0.45.3 (2025-08-20)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
...@@ -11,6 +20,7 @@ Fixed ...@@ -11,6 +20,7 @@ Fixed
effort limit has been violated. Previously, the implementation marked a violation when the applied and computed effort limit has been violated. Previously, the implementation marked a violation when the applied and computed
torques were equal; in fact, equality should indicate no violation, and vice versa. torques were equal; in fact, equality should indicate no violation, and vice versa.
0.45.2 (2025-08-18) 0.45.2 (2025-08-18)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
...@@ -82,7 +92,6 @@ instantaneous term done count at reset. This let to inaccurate aggregation of te ...@@ -82,7 +92,6 @@ instantaneous term done count at reset. This let to inaccurate aggregation of te
happeningduring the traing. Instead we log the episodic term done. happeningduring the traing. Instead we log the episodic term done.
0.44.9 (2025-07-30) 0.44.9 (2025-07-30)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
......
# Copyright (c) 2022-2025, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
"""Launch Isaac Sim Simulator first."""
from isaaclab.app import AppLauncher
# launch omniverse app
app_launcher = AppLauncher(headless=True)
simulation_app = app_launcher.app
"""Rest everything follows."""
import torch
from collections.abc import Sequence
from dataclasses import dataclass
import isaacsim.core.utils.prims as prim_utils
import isaacsim.core.utils.stage as stage_utils
import pytest
import isaaclab.sim as sim_utils
from isaaclab.sensors import SensorBase, SensorBaseCfg
from isaaclab.utils import configclass
@dataclass
class DummyData:
count: torch.Tensor = None
class DummySensor(SensorBase):
def __init__(self, cfg):
super().__init__(cfg)
self._data = DummyData()
def _initialize_impl(self):
super()._initialize_impl()
self._data.count = torch.zeros((self._num_envs), dtype=torch.int, device=self.device)
@property
def data(self):
# update sensors if needed
self._update_outdated_buffers()
# return the data (where `_data` is the data for the sensor)
return self._data
def _update_buffers_impl(self, env_ids: Sequence[int]):
self._data.count[env_ids] += 1
def reset(self, env_ids: Sequence[int] | None = None):
super().reset(env_ids=env_ids)
# Resolve sensor ids
if env_ids is None:
env_ids = slice(None)
self._data.count[env_ids] = 0
@configclass
class DummySensorCfg(SensorBaseCfg):
class_type = DummySensor
prim_path = "/World/envs/env_.*/Cube/dummy_sensor"
def _populate_scene():
""""""
# Ground-plane
cfg = sim_utils.GroundPlaneCfg()
cfg.func("/World/defaultGroundPlane", cfg)
# Lights
cfg = sim_utils.SphereLightCfg()
cfg.func("/World/Light/GreySphere", cfg, translation=(4.5, 3.5, 10.0))
cfg.func("/World/Light/WhiteSphere", cfg, translation=(-4.5, 3.5, 10.0))
# create prims
for i in range(5):
_ = prim_utils.create_prim(
f"/World/envs/env_{i:02d}/Cube",
"Cube",
translation=(i * 1.0, 0.0, 0.0),
scale=(0.25, 0.25, 0.25),
)
@pytest.fixture
def create_dummy_sensor(request, device):
# Create a new stage
stage_utils.create_new_stage()
# Simulation time-step
dt = 0.01
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(dt=dt, device=device)
sim = sim_utils.SimulationContext(sim_cfg)
# create sensor
_populate_scene()
sensor_cfg = DummySensorCfg()
stage_utils.update_stage()
yield sensor_cfg, sim, dt
# stop simulation
# note: cannot use self.sim.stop() since it does one render step after stopping!! This doesn't make sense :(
sim._timeline.stop()
# clear the stage
sim.clear_all_callbacks()
sim.clear_instance()
@pytest.mark.parametrize("device", ("cpu", "cuda"))
def test_sensor_init(create_dummy_sensor, device):
"""Test that the sensor initializes, steps without update, and forces update."""
sensor_cfg, sim, dt = create_dummy_sensor
sensor = DummySensor(cfg=sensor_cfg)
# Play sim
sim.step()
sim.reset()
assert sensor.is_initialized
assert int(sensor.num_instances) == 5
# test that the data is not updated
for i in range(10):
sim.step()
sensor.update(dt=dt, force_recompute=True)
expected_value = i + 1
torch.testing.assert_close(
sensor.data.count,
torch.tensor(expected_value, device=device, dtype=torch.int32).repeat(sensor.num_instances),
)
assert sensor.data.count.shape[0] == 5
# test that the data is not updated if sensor.data is not accessed
for _ in range(5):
sim.step()
sensor.update(dt=dt, force_recompute=False)
torch.testing.assert_close(
sensor._data.count,
torch.tensor(expected_value, device=device, dtype=torch.int32).repeat(sensor.num_instances),
)
@pytest.mark.parametrize("device", ("cpu", "cuda"))
def test_sensor_update_rate(create_dummy_sensor, device):
"""Test that the update_rate configuration parameter works by checking the value of the data is old for an update
period of 2.
"""
sensor_cfg, sim, dt = create_dummy_sensor
sensor_cfg.update_period = 2 * dt
sensor = DummySensor(cfg=sensor_cfg)
# Play sim
sim.step()
sim.reset()
assert sensor.is_initialized
assert int(sensor.num_instances) == 5
expected_value = 1
for i in range(10):
sim.step()
sensor.update(dt=dt, force_recompute=True)
# count should he half of the number of steps
torch.testing.assert_close(
sensor.data.count,
torch.tensor(expected_value, device=device, dtype=torch.int32).repeat(sensor.num_instances),
)
expected_value += i % 2
@pytest.mark.parametrize("device", ("cpu", "cuda"))
def test_sensor_reset(create_dummy_sensor, device):
"""Test that sensor can be reset for all or partial env ids."""
sensor_cfg, sim, dt = create_dummy_sensor
sensor = DummySensor(cfg=sensor_cfg)
# Play sim
sim.step()
sim.reset()
assert sensor.is_initialized
assert int(sensor.num_instances) == 5
for i in range(5):
sim.step()
sensor.update(dt=dt)
# count should he half of the number of steps
torch.testing.assert_close(
sensor.data.count,
torch.tensor(i + 1, device=device, dtype=torch.int32).repeat(sensor.num_instances),
)
sensor.reset()
for j in range(5):
sim.step()
sensor.update(dt=dt)
# count should he half of the number of steps
torch.testing.assert_close(
sensor.data.count,
torch.tensor(j + 1, device=device, dtype=torch.int32).repeat(sensor.num_instances),
)
reset_ids = [2, 4]
cont_ids = [0, 1, 3]
sensor.reset(env_ids=reset_ids)
for k in range(5):
sim.step()
sensor.update(dt=dt)
# count should he half of the number of steps
torch.testing.assert_close(
sensor.data.count[reset_ids],
torch.tensor(k + 1, device=device, dtype=torch.int32).repeat(len(reset_ids)),
)
torch.testing.assert_close(
sensor.data.count[cont_ids],
torch.tensor(k + 6, device=device, dtype=torch.int32).repeat(len(cont_ids)),
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment