newton.actuators.JointSpaceResponse#

class newton.actuators.JointSpaceResponse(model)[source]#

Bases: object

Effective inverse-mass response for each articulation.

inverse_blocks holds H_a^{-1} for each articulation [1/kg or 1/(kg·m²)], indexed by articulation-local DOF. Articulations with no entry have a zero response.

refresh() computes it from a mass matrix it assembles itself. refresh_from_solve() reuses the solver’s own inertia, which is more faithful to the dynamics the effort is fed into. Both run entirely in device kernels.

__init__(model)#

Initialize the response and its scratch buffers for a model.

Parameters:

model (Model) – A finalized Model with articulations.

refresh(state)#

Recompute inverse_blocks for state.

Reads state without modifying it. Includes joint_armature. Joint damping, joint limits, friction, contacts, constraint regularization and kinematic loop closures are absent, so the response comes out larger than anticipated and the solve yields a smaller effort than it otherwise would. Use refresh_from_solve() for a solver-faithful response; loop closures are missing from that path too, since a solver enforces them as constraints rather than folding them into its inertia.

Parameters:

state (newton.State) – Simulation state providing joint_q / joint_qd.

refresh_from_solve(solve_inverse, dof_map=None)#

Recompute inverse_blocks from a solver’s own joint-space inertia.

Prefer this over refresh() when the solver can apply its inertia: the response then carries what the solver folds in (armature, tendon armature). The inertia never has to be materialized, so factorized solvers work too – the inverse is recovered one column at a time by back-substituting unit vectors.

That is one solve per DOF, all on device, so this is CUDA-graph capturable if solve_inverse is. Call it once outside capture when dof_map has a different width than the model’s DOF layout; that first call resizes the scratch buffers.

With SolverMuJoCo, which factorizes its inertia each step:

def solve_inverse(x, y):
    mujoco_warp.solve_m(solver.mjw_model, solver.mjw_data, x, y)


# Simulation loop
response.refresh_from_solve(solve_inverse, dof_map=solver.mjc_dof_to_newton_dof)
Parameters:
  • solve_inverse (Callable[[wp.array2d[wp.float32], wp.array2d[wp.float32]], None]) – Callable (x, y) writing x = M^-1 y, both shaped [world_count, dof_count] in the solver’s own DOF order.

  • dof_map (wp.array2d[wp.int32] | None) – Mapping from solver [world, dof] to Newton DOF index, negative where a solver DOF has no Newton counterpart. If None, the solver is assumed to use Newton DOF order with the same DOF count in every world.

property inverse_blocks: wp.array3d[wp.float32]#

Read-only per-articulation inverse mass blocks, shape [art_count, max_dofs, max_dofs].

inverse_blocks[a, i, j] is the (i, j) entry of articulation a’s inverse mass matrix H_a^{-1} (indices local to the articulation, 0-padded beyond its DOF count). The implicit effort mode uses the submatrix indexed by the actuator group’s DOFs.

Update it through refresh() or refresh_from_solve(). Writing into the array directly is not supported: the padding beyond each articulation’s DOF count is assumed zero by the solve, and a partial write leaves no way to tell a stale response from a fresh one.