Skip to content

Commit 6c0fada

Browse files
Merge branch 'develop' into mtrepte/fix-viz-docs-review-feedback
2 parents 7da188b + 5c78379 commit 6c0fada

11 files changed

Lines changed: 206 additions & 12 deletions

File tree

docs/source/concepts/actuators.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,9 @@ estimates effort telemetry from the current state when the backend does not expo
227227
torque limit at :math:`\pm\,\tau_{max}`.
228228

229229
**DCMotor.** Adds a linear four-quadrant torque-speed curve. ``saturation_effort`` is the stall
230-
torque, and ``actuator_velocity_limit`` is the no-load speed.
230+
torque, and ``actuator_velocity_limit`` is the no-load speed. Both accept a joint-name-pattern
231+
dictionary, so joints behind different gear reductions can share one group and still get their own
232+
curve — for example a quadruped whose knee sits behind an extra reduction relative to its hip.
231233

232234
**DelayedPDActuator.** An ideal PD controller with delayed position, velocity, and effort commands.
233235
The delay is sampled uniformly from ``[min_delay, max_delay]`` at reset.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Changed
2+
^^^^^^^
3+
4+
* Changed :attr:`~isaaclab.actuators.DCMotorCfg.saturation_effort` to accept a joint-name-pattern
5+
dictionary in addition to a scalar. Joints in one actuator group that sit behind different gear
6+
reductions can now be given their own stall torque, which previously required splitting them into
7+
separate actuator groups. Existing scalar configurations are unaffected.

source/isaaclab/isaaclab/actuators/actuator_pd.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,9 @@ def __init__(self, cfg: DCMotorCfg, *args, **kwargs):
405405
# parse configuration
406406
if self.cfg.saturation_effort is None:
407407
raise ValueError("The saturation_effort must be provided for the DC motor actuator model.")
408-
self._saturation_effort = self.cfg.saturation_effort
408+
self._saturation_effort = resolve_joint_parameter(
409+
self.cfg.saturation_effort, None, self._joint_names, self._num_envs, self._device
410+
)
409411
# check that quantities are provided
410412
if self.cfg.actuator_velocity_limit is None:
411413
raise ValueError("The velocity limit must be provided for the DC motor actuator model.")

source/isaaclab/isaaclab/actuators/actuator_pd_cfg.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,13 @@ class DCMotorCfg(IdealPDActuatorCfg):
4747

4848
class_type: type["DCMotor"] | str = "{DIR}.actuator_pd:DCMotor"
4949

50-
saturation_effort: float = MISSING
51-
"""Peak motor force/torque of the electric DC motor (in N-m)."""
50+
saturation_effort: dict[str, float] | float = MISSING
51+
"""Peak motor force/torque of the electric DC motor [N or N·m, depending on joint type].
52+
53+
The motor's stall torque reflected at the joint, i.e. the torque produced at zero speed.
54+
Joints in the same group that sit behind different gear reductions need different values,
55+
so this accepts a joint-name-pattern dictionary as well as a scalar.
56+
"""
5257

5358

5459
@configclass

source/isaaclab/test/actuators/test_dc_motor.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,36 @@ def test_dc_motor_clip(num_envs, num_joints, device, test_point):
183183
expected_clipped_effort[test_point] * torch.ones(num_envs, num_joints, device=device),
184184
clipped_effort,
185185
)
186+
187+
188+
@pytest.mark.parametrize("device", ["cuda:0", "cpu"])
189+
def test_dc_motor_clip_with_per_joint_saturation_effort(device):
190+
"""Test that a per-joint ``saturation_effort`` gives each joint its own torque-speed curve.
191+
192+
Joints behind different gear reductions belong to one actuator group but do not share a stall
193+
torque, e.g. the Unitree Go2 calf, which sits behind an extra knee reduction.
194+
"""
195+
joint_names = ["hip", "calf"]
196+
actuator_cfg = DCMotorCfg(
197+
joint_names_expr=joint_names,
198+
stiffness=200.0,
199+
damping=10.0,
200+
actuator_effort_limit=100.0,
201+
actuator_velocity_limit=50.0,
202+
saturation_effort={"hip": 100.0, "calf": 190.0},
203+
)
204+
actuator = actuator_cfg.class_type(
205+
actuator_cfg,
206+
joint_names=joint_names,
207+
joint_ids=[0, 1],
208+
num_envs=1,
209+
device=device,
210+
stiffness=actuator_cfg.stiffness,
211+
damping=actuator_cfg.damping,
212+
)
213+
214+
# at half the no-load speed each joint delivers half of its own stall torque, and the shared
215+
# effort limit is high enough to clip neither
216+
actuator._joint_vel[:] = 25.0
217+
clipped_effort = actuator._clip_effort(torch.full((1, 2), 500.0, device=device))
218+
torch.testing.assert_close(clipped_effort, torch.tensor([[50.0, 95.0]], device=device))
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed the Unitree Go1 and Go2 leg actuator limits ignoring the knee reduction. Both robots applied
5+
the hip and thigh limits to the calf joints, which capped calf torque well below its rated value and
6+
let the torque-speed curve keep motoring past its rated speed. The calf joints now use the limits
7+
authored in ``go1.usd`` and ``go2.usd`` (Go1: 35.55 N·m, 20.06 rad/s; Go2: 45.43 N·m, 15.70 rad/s),
8+
and the hip and thigh limits were aligned with the same assets (23.7 N·m, 30.1 rad/s).
9+
10+
These robots now produce more calf torque at lower calf speeds, so policies trained on the previous
11+
configuration should be retrained rather than reused directly.

source/isaaclab_assets/isaaclab_assets/robots/unitree.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@
3939
torque_scale=1.0,
4040
input_order="pos_vel",
4141
input_idx=[0, 1, 2],
42-
actuator_effort_limit=23.7, # taken from spec sheet
43-
actuator_velocity_limit=30.0, # taken from spec sheet
44-
saturation_effort=23.7, # same as effort limit
42+
# the calf sits behind an extra 1.5:1 knee reduction, so its joint-side torque is higher
43+
# and its joint-side speed lower than the hip and thigh. Values match ``go1.usd``.
44+
actuator_effort_limit={".*_hip_joint": 23.7, ".*_thigh_joint": 23.7, ".*_calf_joint": 35.55},
45+
actuator_velocity_limit={".*_hip_joint": 30.1, ".*_thigh_joint": 30.1, ".*_calf_joint": 20.06},
46+
saturation_effort={".*_hip_joint": 23.7, ".*_thigh_joint": 23.7, ".*_calf_joint": 35.55},
4547
)
4648
"""Configuration of Go1 actuators using MLP model.
4749
@@ -171,9 +173,11 @@
171173
actuators={
172174
"base_legs": DCMotorCfg(
173175
joint_names_expr=[".*_hip_joint", ".*_thigh_joint", ".*_calf_joint"],
174-
actuator_effort_limit=23.5,
175-
saturation_effort=23.5,
176-
actuator_velocity_limit=30.0,
176+
# the calf sits behind an extra 1.92:1 knee reduction, so its joint-side torque is
177+
# higher and its joint-side speed lower than the hip and thigh. Values match ``go2.usd``.
178+
actuator_effort_limit={".*_hip_joint": 23.7, ".*_thigh_joint": 23.7, ".*_calf_joint": 45.43},
179+
saturation_effort={".*_hip_joint": 23.7, ".*_thigh_joint": 23.7, ".*_calf_joint": 45.43},
180+
actuator_velocity_limit={".*_hip_joint": 30.1, ".*_thigh_joint": 30.1, ".*_calf_joint": 15.70},
177181
stiffness=25.0,
178182
damping=0.5,
179183
friction=0.0,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Added
2+
^^^^^
3+
4+
* Added :attr:`~isaaclab_ov.tensor_types.DOF_DRIVE_TYPE`,
5+
:attr:`~isaaclab_ov.tensor_types.DOF_DRIVE_MODEL`,
6+
:attr:`~isaaclab_ov.tensor_types.BODY_DISABLE_GRAVITY`,
7+
:attr:`~isaaclab_ov.tensor_types.CONTACT_OFFSET`,
8+
:attr:`~isaaclab_ov.tensor_types.REST_OFFSET`,
9+
:attr:`~isaaclab_ov.tensor_types.RIGID_BODY_DISABLE_GRAVITY`,
10+
:attr:`~isaaclab_ov.tensor_types.RIGID_BODY_CONTACT_OFFSET`, and
11+
:attr:`~isaaclab_ov.tensor_types.RIGID_BODY_REST_OFFSET` tensor type aliases,
12+
documenting the shape, dtype and units of each.
13+
14+
Fixed
15+
^^^^^
16+
17+
* Fixed :class:`~isaaclab_ov.sim.views.OvPhysxView` routing eight CPU-resident
18+
tensor types to the simulation device. The per-collision-shape contact and rest
19+
offsets, the articulation and rigid-body gravity-disable flags, and the DOF drive
20+
type and drive model are CPU-resident even on a GPU simulation, but were absent
21+
from the internal CPU-only classification. Reads and writes of these types
22+
incurred a hidden per-call host-to-device staging copy, and a correctly placed
23+
host buffer was rejected with ``OvPhysxView.DeviceMismatch``. Residency was
24+
measured on a GPU simulation by counting CUDA memcpys around a binding read.
25+
* **Breaking:** Fixed ``articulation_dof_drive_type`` not being classified as
26+
read-only. The underlying tensor type is read-only, but
27+
:meth:`~isaaclab_ov.sim.views.OvPhysxView.set_attribute` previously accepted
28+
writes to it and silently forwarded them. Such calls now raise
29+
``OvPhysxView.ReadOnlyAttribute``. Remove any write to this attribute; drive
30+
type is authored through the USD drive schema, not the tensor path.

source/isaaclab_ov/isaaclab_ov/sim/views/ovphysx_view.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"articulation_body_inv_mass",
8080
"articulation_body_inv_inertia",
8181
"articulation_dof_projected_joint_force",
82+
"articulation_dof_drive_type",
8283
"articulation_jacobian",
8384
"articulation_mass_center_world",
8485
"articulation_mass_center_local",

source/isaaclab_ov/isaaclab_ov/tensor_types.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,19 @@
149149
Shape is ``[N, D, 3]``, dtype ``float32``.
150150
"""
151151

152+
DOF_DRIVE_TYPE = _TT.ARTICULATION_DOF_DRIVE_TYPE
153+
"""DOF drive type (0 = none, 1 = force, 2 = acceleration).
154+
155+
Shape is ``[N, D]``, dtype ``uint8``. Read-only.
156+
"""
157+
158+
DOF_DRIVE_MODEL = _TT.ARTICULATION_DOF_DRIVE_MODEL
159+
"""DOF performance-envelope drive model (speed-effort gradient, maximum actuator
160+
velocity, velocity-dependent resistance).
161+
162+
Shape is ``[N, D, 3]``, dtype ``float32``.
163+
"""
164+
152165
"""
153166
External wrench (GPU, write-only)
154167
"""
@@ -193,6 +206,24 @@
193206
Shape is ``[N, L, 9]``, dtype ``float32``.
194207
"""
195208

209+
BODY_DISABLE_GRAVITY = _TT.ARTICULATION_BODY_DISABLE_GRAVITY
210+
"""Per-link gravity disable flag (nonzero disables gravity for that link).
211+
212+
Shape is ``[N, L]``, dtype ``uint8``.
213+
"""
214+
215+
CONTACT_OFFSET = _TT.ARTICULATION_CONTACT_OFFSET
216+
"""Contact offset of each collision shape.
217+
218+
Shape is ``[N, S]``, dtype ``float32`` [m].
219+
"""
220+
221+
REST_OFFSET = _TT.ARTICULATION_REST_OFFSET
222+
"""Rest offset of each collision shape.
223+
224+
Shape is ``[N, S]``, dtype ``float32`` [m].
225+
"""
226+
196227
"""
197228
Rigid-body TensorTypes
198229
@@ -225,6 +256,16 @@
225256
``(N, 9)``, row-major flatten of the 3×3 inertia matrix
226257
``(Ixx, Ixy, Ixz, Iyx, Iyy, Iyz, Izx, Izy, Izz)`` [kg·m²]."""
227258

259+
RIGID_BODY_DISABLE_GRAVITY = _TT.RIGID_BODY_DISABLE_GRAVITY
260+
"""Gravity disable flag — read/write, CPU. Shape ``(N,)``, dtype ``uint8``;
261+
nonzero disables gravity for that actor."""
262+
263+
RIGID_BODY_CONTACT_OFFSET = _TT.RIGID_BODY_CONTACT_OFFSET
264+
"""Contact offset of each collision shape — read/write, CPU. Shape ``(N, S)`` [m]."""
265+
266+
RIGID_BODY_REST_OFFSET = _TT.RIGID_BODY_REST_OFFSET
267+
"""Rest offset of each collision shape — read/write, CPU. Shape ``(N, S)`` [m]."""
268+
228269
# These three aliases are pending an upcoming ovphysx wheel update.
229270
# When the wheel ships them, the corresponding ``hasattr`` checks below
230271
# in IsaacLab consumers will start returning True and the bindings will
@@ -433,15 +474,23 @@
433474
DOF_MAX_FORCE,
434475
DOF_ARMATURE,
435476
DOF_FRICTION_PROPERTIES,
477+
DOF_DRIVE_TYPE,
478+
DOF_DRIVE_MODEL,
436479
BODY_MASS,
437480
BODY_COM_POSE,
438481
BODY_INERTIA,
439482
BODY_INV_MASS,
440483
BODY_INV_INERTIA,
484+
BODY_DISABLE_GRAVITY,
485+
CONTACT_OFFSET,
486+
REST_OFFSET,
441487
# Rigid-body CPU-only entries (always available)
442488
RIGID_BODY_MASS,
443489
RIGID_BODY_COM_POSE,
444490
RIGID_BODY_INERTIA,
491+
RIGID_BODY_DISABLE_GRAVITY,
492+
RIGID_BODY_CONTACT_OFFSET,
493+
RIGID_BODY_REST_OFFSET,
445494
DEFORMABLE_MATERIAL_DYNAMIC_FRICTION,
446495
DEFORMABLE_MATERIAL_YOUNGS_MODULUS,
447496
DEFORMABLE_MATERIAL_POISSONS_RATIO,

0 commit comments

Comments
 (0)