Skip to content

Commit f1ec832

Browse files
authored
Merge pull request #2 from aravindskumar-nvidia/aravikumar/ik-qol
Fix Jacobian column offset for Newton floating-base
2 parents 44a2209 + d1d038d commit f1ec832

5 files changed

Lines changed: 52 additions & 14 deletions

File tree

source/isaaclab/isaaclab/assets/articulation/base_articulation.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,24 @@ def root_view(self):
173173
"""
174174
raise NotImplementedError()
175175

176+
@property
177+
def num_jacobi_joints(self) -> int:
178+
"""Size of the Jacobian's joint axis (its last dimension).
179+
180+
For most backends this equals :attr:`num_joints`. Some
181+
backends prepend the 6 floating-base DoFs to the Jacobian's
182+
joint axis without counting them in :attr:`num_joints`; on
183+
those, ``num_jacobi_joints`` is ``num_joints + 6`` for
184+
floating-base assets. The default returns :attr:`num_joints`;
185+
backends that carry extra leading columns override.
186+
187+
To convert a logical joint index (from :meth:`find_joints`)
188+
into the matching Jacobian column index, add the difference
189+
``num_jacobi_joints - num_joints`` — ``0`` for the default
190+
convention, ``6`` for the prepend-floating-base convention.
191+
"""
192+
return self.num_joints
193+
176194
def get_jacobians(self) -> wp.array:
177195
"""Per-env geometric Jacobians, referenced at each link origin in world frame.
178196
@@ -181,26 +199,30 @@ def get_jacobians(self) -> wp.array:
181199
182200
.. code-block:: text
183201
184-
J[:, body_idx, 0:3, :] @ q_dot == data.body_link_lin_vel_w[:, body_idx]
185-
J[:, body_idx, 3:6, :] @ q_dot == data.body_link_ang_vel_w[:, body_idx]
202+
J[:, jacobi_body_idx, 0:3, :] @ q_dot == data.body_link_lin_vel_w[:, body_idx]
203+
J[:, jacobi_body_idx, 3:6, :] @ q_dot == data.body_link_ang_vel_w[:, body_idx]
186204
187205
Linear rows ``[0:3]`` give the velocity at the link origin (the
188206
body's USD prim transform / actor frame) in world frame.
189207
Angular rows ``[3:6]`` give the body's angular velocity in
190208
world frame. Both share the contract used by
191209
:attr:`~isaaclab.assets.ArticulationData.body_link_pos_w` and
192210
:attr:`~isaaclab.assets.ArticulationData.body_link_lin_vel_w`.
211+
For fixed-base articulations, ``jacobi_body_idx`` excludes the
212+
fixed root body and is therefore ``body_idx - 1``. For
213+
floating-base articulations, ``jacobi_body_idx == body_idx``.
193214
194215
Implementations whose native Jacobian is expressed at a
195216
different reference point (e.g. body center of mass) MUST shift
196217
the linear rows to the link origin before returning so the
197218
contract above holds across backends.
198219
199-
Floating-base joint-dim convention differs across backends:
200-
some prepend the floating-base 6 DoFs to the joint dimension,
201-
others fold them into the native joint-DoF count. The total
202-
joint dimension is the same; only how :attr:`num_joints` is
203-
reported differs.
220+
The joint-dimension size is reported by
221+
:attr:`num_jacobi_joints`. Backends that prepend floating-base
222+
DoFs to the Jacobian columns expose the offset implicitly via
223+
``num_jacobi_joints - num_joints``; callers that index into
224+
the joint axis should consult that property rather than
225+
hard-coding a constant.
204226
205227
Returns:
206228
The per-env geometric Jacobian. Shape

source/isaaclab/isaaclab/envs/mdp/actions/rmpflow_task_space_actions.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,12 @@ def __init__(self, cfg: rmpflow_actions_cfg.RMPFlowActionCfg, env: ManagerBasedE
6161
# this means that number of bodies is one less than the articulation's number of bodies
6262
if self._asset.is_fixed_base:
6363
self._jacobi_body_idx = self._body_idx - 1
64-
self._jacobi_joint_ids = self._joint_ids
6564
else:
6665
self._jacobi_body_idx = self._body_idx
67-
self._jacobi_joint_ids = [i + 6 for i in self._joint_ids]
66+
# See ``DifferentialInverseKinematicsAction.__init__`` for the rationale
67+
# behind this offset.
68+
jacobi_joint_offset = self._asset.num_jacobi_joints - self._asset.num_joints
69+
self._jacobi_joint_ids = [i + jacobi_joint_offset for i in self._joint_ids]
6870

6971
# log info for debugging
7072
logger.info(

source/isaaclab/isaaclab/envs/mdp/actions/task_space_actions.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,14 @@ def __init__(self, cfg: actions_cfg.DifferentialInverseKinematicsActionCfg, env:
7777
# this means that number of bodies is one less than the articulation's number of bodies
7878
if self._asset.is_fixed_base:
7979
self._jacobi_body_idx = self._body_idx - 1
80-
self._jacobi_joint_ids = self._joint_ids
8180
else:
8281
self._jacobi_body_idx = self._body_idx
83-
self._jacobi_joint_ids = [i + 6 for i in self._joint_ids]
82+
# Map logical joint indices to Jacobian column indices. Some backends
83+
# (e.g. PhysX, floating-base) prepend the 6 floating-base DoFs to the
84+
# Jacobian's joint axis without counting them in ``num_joints``;
85+
# ``num_jacobi_joints - num_joints`` is that leading offset (0 or 6).
86+
jacobi_joint_offset = self._asset.num_jacobi_joints - self._asset.num_joints
87+
self._jacobi_joint_ids = [i + jacobi_joint_offset for i in self._joint_ids]
8488

8589
# log info for debugging
8690
logger.info(
@@ -305,10 +309,12 @@ def __init__(self, cfg: actions_cfg.OperationalSpaceControllerActionCfg, env: Ma
305309
# this means that number of bodies is one less than the articulation's number of bodies
306310
if self._asset.is_fixed_base:
307311
self._jacobi_ee_body_idx = self._ee_body_idx - 1
308-
self._jacobi_joint_idx = self._joint_ids
309312
else:
310313
self._jacobi_ee_body_idx = self._ee_body_idx
311-
self._jacobi_joint_idx = [i + 6 for i in self._joint_ids]
314+
# See ``DifferentialInverseKinematicsAction.__init__`` for the rationale
315+
# behind this offset.
316+
jacobi_joint_offset = self._asset.num_jacobi_joints - self._asset.num_joints
317+
self._jacobi_joint_idx = [i + jacobi_joint_offset for i in self._joint_ids]
312318

313319
# log info for debugging
314320
logger.info(

source/isaaclab_newton/test/assets/test_articulation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"""Rest everything follows."""
1919

2020
import sys
21+
from copy import deepcopy
2122

2223
import pytest
2324
import torch
@@ -465,7 +466,7 @@ def sim(request):
465466
else:
466467
add_ground_plane = False # default to no ground plane
467468
articulation_type = request.getfixturevalue("articulation_type")
468-
sim_cfg = SIM_CFGs[articulation_type]
469+
sim_cfg = deepcopy(SIM_CFGs[articulation_type])
469470
sim_cfg.device = device
470471
# ``gravity_enabled`` is silently ignored by ``build_simulation_context``
471472
# when an explicit ``sim_cfg`` is also passed; apply it here so the

source/isaaclab_physx/isaaclab_physx/assets/articulation/articulation.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,13 @@ def root_view(self) -> physx.ArticulationView:
179179
"""
180180
return self._root_view
181181

182+
@property
183+
def num_jacobi_joints(self) -> int:
184+
# PhysX's Jacobian prepends 6 floating-base columns for floating-base
185+
# articulations; ``num_joints`` does not count them. Fixed-base assets
186+
# have no prepended columns.
187+
return self.num_joints if self.is_fixed_base else self.num_joints + 6
188+
182189
def get_jacobians(self) -> wp.array:
183190
return self._root_view.get_jacobians()
184191

0 commit comments

Comments
 (0)