fix(newton): resolve ArticulationData's per-instance axis for the flat builder - #7673
Conversation
…ilder _create_simulation_bindings indexed every per-instance sim binding (root pose, joint positions/velocities/limits/targets, body mass/inertia/wrench, tendon properties) with `[:, 0]`, which only holds for the replicated builder's (num_worlds, 1, ...) layout. The flat (non-replicated) builder used when replicate_physics=False produces the opposite layout, (1, num_instances, ...), so `[:, 0]` silently collapsed every non-replicated scene with more than one environment down to a single instance's data for every binding. Resolve the per-instance axis dynamically from the root transforms' shape instead of hardcoding it, and reuse that axis for every binding in the method.
| if _raw_root_transforms.shape[0] == self._num_instances: | ||
| self._instance_index = (slice(None), 0) | ||
| elif len(_raw_root_transforms.shape) > 1 and _raw_root_transforms.shape[1] == self._num_instances: | ||
| self._instance_index = (0, slice(None)) |
There was a problem hiding this comment.
This new layout selection controls every articulation state and command binding, but there is no automated regression covering multiple environments with replicate_physics=False. Add a test for the flat (1, num_instances, ...) layout that verifies both a representative read and a command write; otherwise, the original silent instance collapse or a similar indexing regression could return unnoticed.
Knowledge Base Used: Newton backend
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| # The root view's per-instance axis is world-major for a replicated builder | ||
| # ((num_worlds, 1, ...), one instance per world) and instance-major for the flat | ||
| # (non-replicated) builder ((1, num_instances, ...), every instance in one world). | ||
| # Index dynamically instead of hardcoding `[self._instance_index]`, which silently collapsed every |
There was a problem hiding this comment.
The comment says the code avoids hardcoding [self._instance_index], but self._instance_index is the dynamic index introduced here. The hardcoded expression being replaced was [:, 0]; naming the wrong expression could mislead future changes about what caused the layout bug.
| # Index dynamically instead of hardcoding `[self._instance_index]`, which silently collapsed every | |
| # Index dynamically instead of hardcoding `[:, 0]`, which silently collapsed every |
There was a problem hiding this comment.
Isaac Lab Review Bot
The dynamic per-instance axis resolves the flat-builder layout consistently across articulation bindings. Before merge, add the required isaaclab_newton changelog fragment and correct the misleading indexing comment.
- Design and architecture: Resolving the instance axis once from the root-transform shape and reusing it across bindings is a focused, backend-local solution that preserves the existing replicated layout while supporting the flat builder.
- API: The change restores the documented
num_instances-based shapes and write behavior underreplicate_physics=Falsewithout changing public signatures or dtypes. Because this is user-visible, repository policy requires a changelog fragment forisaaclab_newton. - Implementation: The binding and buffer paths consistently reuse
_instance_index. The new comment incorrectly says the code replaces hardcoded[self._instance_index]indexing, although that is the new form and the replaced expression was[:, 0]; it should be corrected to avoid misleading maintainers.
Minor fixes needed. Posted 2 actionable findings inline.
Automated review; human maintainers own approval decisions.
|
|
||
| # -- root properties | ||
| self._sim_bind_root_link_pose_w = self._root_view.get_root_transforms(SimulationManager.get_state_0())[:, 0] | ||
| _raw_root_transforms = self._root_view.get_root_transforms(SimulationManager.get_state_0()) |
There was a problem hiding this comment.
🟡 Warning · Implementation — Missing isaaclab_newton changelog fragment
This is a user-visible behavior fix (articulation data and write paths now cover all instances for replicate_physics=False), but the change set contains only this source file. Repository rules require one changelog fragment per changed source package for user-visible changes. Add an isaaclab_newton fragment describing the corrected flat-builder instance axis.
| # The root view's per-instance axis is world-major for a replicated builder | ||
| # ((num_worlds, 1, ...), one instance per world) and instance-major for the flat | ||
| # (non-replicated) builder ((1, num_instances, ...), every instance in one world). | ||
| # Index dynamically instead of hardcoding `[self._instance_index]`, which silently collapsed every |
There was a problem hiding this comment.
🔵 Suggestion · Implementation — Comment misstates the replaced indexing form
The comment says "instead of hardcoding [self._instance_index]", but that is exactly what the new code does; the replaced form was [:, 0]. This reads as a search-and-replace artifact and conflicts with the nearby inertia comment that still refers to [:, 0], misleading future maintainers about what changed.
Summary
_create_simulation_bindingsindexed every per-instance sim binding (root pose, joint positions/velocities/limits/targets, body mass/inertia/wrench, tendon properties) with[:, 0], which only holds for the replicated builder's(num_worlds, 1, ...)layout.replicate_physics=Falseproduces the opposite layout,(1, num_instances, ...), so[:, 0]silently collapsed every non-replicated scene with more than one environment down to a single instance's data for every binding — including write paths used to actually drive the robot (joint position/velocity targets, effort, etc).Scope note
This fix covers
ArticulationData, the asset type exercised by the reproduction below. The same[:, 0]pattern also appears inrigid_object_data.py,cable_object_data.py, andrigid_object_collection.py; I have not touched those since I couldn't verify a fix against a scene that exercises them.Test plan
replicate_physics=False:root_quat_w(and other root/body/joint properties) returned shape(1, ...)instead of(num_envs, ...)before the fix,(num_envs, ...)after.