Skip to content

Commit 3d6f55b

Browse files
Fixes the implementation of quat_inv() (#2797)
# Description Corrects `quat_inv()` in utils/math.py: the inverse is now computed as: `quat_conjugate(q) / q.pow(2).sum(dim=-1, keepdim=True).clamp(min=eps)` ensuring correct results for **non-unit** quaternions. Fixes #1263 (see discussion for details). Also updated `CHANGELOG.rst` and corrected a few minor typos within. ## Type of change <!-- As you go through the list, delete the ones that are not applicable. --> - Bug fix (non-breaking change which fixes an issue) - This change requires a documentation update ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Co-authored-by: Kelly Guo <[email protected]>
1 parent 4a7d15d commit 3d6f55b

File tree

4 files changed

+53
-9
lines changed

4 files changed

+53
-9
lines changed

source/isaaclab/config/extension.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
# Note: Semantic Versioning is used: https://semver.org/
4-
version = "0.40.10"
4+
version = "0.40.11"
55

66
# Description
77
title = "Isaac Lab framework for Robot Learning"

source/isaaclab/docs/CHANGELOG.rst

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
Changelog
22
---------
33

4+
0.40.11 (2025-06-27)
5+
~~~~~~~~~~~~~~~~~~~~
6+
7+
Added
8+
^^^^^
9+
10+
* Added unit test for :func:`~isaaclab.utils.math.quat_inv`.
11+
12+
Fixed
13+
^^^^^
14+
15+
* Fixed the implementation mistake in :func:`~isaaclab.utils.math.quat_inv`.
16+
17+
418
0.40.10 (2025-06-25)
519
~~~~~~~~~~~~~~~~~~~~
620

721
Fixed
822
^^^^^
923

10-
* Fixed :meth:`omni.isaac.lab.utils.dict.update_class_from_dict` preventing setting flat Iterables with different lengths.
24+
* Fixed :func:`~isaaclab.utils.dict.update_class_from_dict` preventing setting flat Iterables with different lengths.
1125

1226

1327
0.40.9 (2025-06-25)
@@ -17,7 +31,7 @@ Added
1731
^^^^^
1832

1933
* Added ``sample_bias_per_component`` flag to :class:`~isaaclab.utils.noise.noise_model.NoiseModelWithAdditiveBias` to enable independent per-component bias
20-
sampling, which is now the default behavior. If set to False, the previous behavior of sharing the same bias value across all components is retained.
34+
sampling, which is now the default behavior. If set to False, the previous behavior of sharing the same bias value across all components is retained.
2135

2236

2337
0.40.8 (2025-06-18)
@@ -27,8 +41,8 @@ Fixed
2741
^^^^^
2842

2943
* Fixed data inconsistency between read_body, read_link, read_com when write_body, write_com, write_joint performed, in
30-
:class:`~isaaclab.assets.Articulation`, :class:`~isaaclab.assets.RigidObject`, and
31-
:class:`~isaaclab.assets.RigidObjectCollection`
44+
:class:`~isaaclab.assets.Articulation`, :class:`~isaaclab.assets.RigidObject`, and
45+
:class:`~isaaclab.assets.RigidObjectCollection`
3246
* added pytest that check against these data consistencies
3347

3448

@@ -38,7 +52,7 @@ Fixed
3852
Added
3953
^^^^^
4054

41-
* :class:`NoiseModel` support for manager-based workflows.
55+
* :class:`~isaaclab.utils.noise.NoiseModel` support for manager-based workflows.
4256

4357
Changed
4458
^^^^^^^

source/isaaclab/isaaclab/utils/math.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,16 +254,17 @@ def quat_conjugate(q: torch.Tensor) -> torch.Tensor:
254254

255255

256256
@torch.jit.script
257-
def quat_inv(q: torch.Tensor) -> torch.Tensor:
258-
"""Compute the inverse of a quaternion.
257+
def quat_inv(q: torch.Tensor, eps: float = 1e-9) -> torch.Tensor:
258+
"""Computes the inverse of a quaternion.
259259
260260
Args:
261261
q: The quaternion orientation in (w, x, y, z). Shape is (N, 4).
262+
eps: A small value to avoid division by zero. Defaults to 1e-9.
262263
263264
Returns:
264265
The inverse quaternion in (w, x, y, z). Shape is (N, 4).
265266
"""
266-
return normalize(quat_conjugate(q))
267+
return quat_conjugate(q) / q.pow(2).sum(dim=-1, keepdim=True).clamp(min=eps)
267268

268269

269270
@torch.jit.script

source/isaaclab/test/utils/test_math.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,35 @@ def test_quat_apply_inverse(device):
593593
torch.testing.assert_close(scipy_result.to(device=device), apply_result, atol=2e-4, rtol=2e-4)
594594

595595

596+
@pytest.mark.parametrize("device", ["cpu", "cuda:0"])
597+
def test_quat_inv(device):
598+
"""Test for quat_inv method.
599+
600+
For random unit and non-unit quaternions q, the Hamilton products
601+
q ⊗ q⁻¹ and q⁻¹ ⊗ q must both equal the identity quaternion (1,0,0,0)
602+
within numerical precision.
603+
"""
604+
num = 2048
605+
606+
# -------- non-unit sample (average ‖q‖ ≈ 10) --------
607+
q_nonunit = torch.randn(num, 4, device=device) * 5.0
608+
609+
# -------- unit sample (‖q‖ = 1) --------
610+
q_unit = torch.randn(num, 4, device=device)
611+
q_unit = q_unit / q_unit.norm(dim=-1, keepdim=True)
612+
613+
identity = torch.tensor([1.0, 0.0, 0.0, 0.0], device=device)
614+
615+
for q in (q_nonunit, q_unit):
616+
q_inv = math_utils.quat_inv(q)
617+
618+
id_batch = identity.expand_as(q)
619+
620+
# left and right products must both be identity
621+
torch.testing.assert_close(math_utils.quat_mul(q, q_inv), id_batch, atol=1e-4, rtol=1e-4)
622+
torch.testing.assert_close(math_utils.quat_mul(q_inv, q), id_batch, atol=1e-4, rtol=1e-4)
623+
624+
596625
def test_quat_apply_benchmarks():
597626
"""Test for quat_apply and quat_apply_inverse methods compared to old methods using torch.bmm and torch.einsum.
598627
The new implementation uses :meth:`torch.einsum` instead of `torch.bmm` which allows

0 commit comments

Comments
 (0)