newton.controllers.ControllerDifferentialIKModelFree#

class newton.controllers.ControllerDifferentialIKModelFree(*, controlled_dofs_per_robot, axis_weight=None, bandwidth, damping, ik_method=DifferentialIKMethod.DAMPED_LEAST_SQUARES, adaptive_damping_min=None, adaptive_damping_max=None, adaptive_damping_threshold=None, truncated_svd_threshold=None, use_joint_limit_avoidance=False, joint_limit_avoidance_gain=0.0, joint_limit_avoidance_margin=0.0, joint_pos_lower=None, joint_pos_upper=None, use_null_space_posture_control=False, null_space_stiffness=None, null_space_damping=None, null_space_axes=None, device=None, requires_grad=False)[source]#

Bases: ControllerBase

Differential-kinematics (Jacobian-based) controller with a caller-supplied Jacobian.

Implements a differential-kinematics control law, selectable per instance via DifferentialIKMethod (damped least squares by default). This model-free variant expects the tool-point Jacobian and the current tool pose to be computed externally — it is the caller’s responsibility to provide them, and to keep them consistent with inputs.joint_q, before every step().

Every per-DOF port is compact: a 1-D array with one entry per controlled DOF, ordered robot 0’s DOFs first, then robot 1’s, matching controlled_dofs_per_robot. Every per-robot port has one entry per robot, since the task-space storage is always 6D regardless of a robot’s own axis_weight. A port may be bound either to a plain array or to an indexed view of a simulation-sized array, which is how a caller expresses a gather or scatter without the controller owning an index table — for example, using a paired model-based controller’s own q_start/qd_start properties (see ControllerDifferentialIK.q_start):

inputs.joint_q = state.joint_q[ctrl.q_start]  # gather
outputs.joint_q_target = control.joint_target_q[ctrl.q_start]  # scatter

Views are live and graph-capturable: bind them once, and each step (or graph replay) reads through to the current contents of the underlying array.

Array shapes and devices are validated on each direct call to step(), but not when a captured graph is replayed, since the checks run in Python at capture time only.

Supports heterogeneous robot fleets — robots may have different controlled-DOF counts. The Jacobian is padded to max_controlled_dofs; every other buffer is compact.

Allocate input and output structs via input() and output(). All field names on those structs are fixed — see Inputs and Outputs for the typed schema.

Parameters:
  • controlled_dofs_per_robot (wp.array[wp.int32]) – Controlled-DOF count for each robot. Its length sets controlled_robot_count, its sum sets total_controlled_dofs (the length of every compact port), and its maximum sets max_controlled_dofs (the padded width of the Jacobian). Every entry must be positive.

  • axis_weight (wp.array[wp.spatial_vector] | wp.spatial_vector | None) – Non-negative per-axis weight for each of the 6 canonical task axes (position x, y, z, then orientation x, y, z), diag(w) applied to both the Jacobian and the pose error for that axis before the solve (J_w = diag(w) @ J, e_w = diag(w) @ e) — a genuine soft weight for any nonzero value. An axis weighted exactly 0 is different in kind, not just degree: it is excluded from the solve structurally (its error and Jacobian rows never enter it at all), not merely driven toward zero by a very small weight — this also shrinks the task’s own dimension, so a robot with fewer than 6 controlled DOFs can still be redundant if enough axes are zeroed. Any combination of active axes is allowed, not just a leading prefix. Pass a single wp.spatial_vector to apply the same weights to every robot, or an array of shape [controlled_robot_count] to set them per robot. None (the default) means every axis is weighted 1 for every robot — full, equally-trusted 6D pose.

  • bandwidth (wp.array[wp.float32] | float | None) – Output velocity scale gain, applied per controlled DOF after the Jacobian solve. Must be non-negative, since a negative value would flip the output velocity’s direction. Checked at construction when baked; a live value is the caller’s own responsibility, since checking it every step would cost a host sync and break is_graphable(). Pass a scalar to apply the same gain to every controlled DOF, an array of shape [total_controlled_dofs] to set them individually, or None to read inputs.bandwidth each step.

  • damping (wp.array[wp.float32] | float | None) – Damped-least-squares regularization λ, applied per robot to the task-space normal-equations matrix. Pass a scalar to apply the same damping to every robot, an array of shape [controlled_robot_count] to set them individually, or None to read inputs.damping each step. Only meaningful when ik_method=DifferentialIKMethod.DAMPED_LEAST_SQUARES (the default); must be None for every other DifferentialIKMethod, which has no λ to set.

  • ik_method (DifferentialIKMethod) – Inverse-Jacobian solve method, a DifferentialIKMethod. Defaults to DifferentialIKMethod.DAMPED_LEAST_SQUARES.

  • adaptive_damping_min (float | None) – λ used when the smallest singular value of the task Jacobian is at or above adaptive_damping_threshold. Required (and must be non-negative) when ik_method=DifferentialIKMethod.ADAPTIVE_DAMPING; must be None otherwise.

  • adaptive_damping_max (float | None) – λ used at a full singularity (smallest singular value zero), ramping down to adaptive_damping_min as the smallest singular value rises to adaptive_damping_threshold. Required (and must exceed adaptive_damping_min) when ik_method=DifferentialIKMethod.ADAPTIVE_DAMPING; must be None otherwise.

  • adaptive_damping_threshold (float | None) – Smallest-singular-value threshold below which damping starts ramping from adaptive_damping_min toward adaptive_damping_max. Required (and must be positive) when ik_method=DifferentialIKMethod.ADAPTIVE_DAMPING; must be None otherwise.

  • truncated_svd_threshold (float | None) – Per-direction singular-value threshold — a task-space direction with singular value above this is inverted exactly, one at or below it is dropped from the solve entirely. Required (and must be positive) when ik_method=DifferentialIKMethod.TRUNCATED_SVD; must be None otherwise.

  • use_joint_limit_avoidance (bool) – Project a joint-limit-avoidance bias through the null-space projector. Requires joint_limit_avoidance_gain, joint_limit_avoidance_margin, joint_pos_lower, and joint_pos_upper.

  • joint_limit_avoidance_gain (float) – Joint-centering gain, applied once a DOF comes within joint_limit_avoidance_margin of either limit. Required (and must be positive) when use_joint_limit_avoidance=True.

  • joint_limit_avoidance_margin (float) – Distance from either limit at which the avoidance bias starts ramping in [m or rad], same units as joint_pos_lower/joint_pos_upper. Required (and must be positive) when use_joint_limit_avoidance=True.

  • joint_pos_lower (wp.array[wp.float32] | None) – Lower joint position limit per controlled DOF [m or rad], shape [total_controlled_dofs]. Required when use_joint_limit_avoidance=True; baked at construction, not a live port.

  • joint_pos_upper (wp.array[wp.float32] | None) – Upper joint position limit per controlled DOF [m or rad], shape [total_controlled_dofs]. Required when use_joint_limit_avoidance=True; baked at construction, not a live port.

  • use_null_space_posture_control (bool) – Project a proportional pull toward inputs.q_des_null through the null-space projector. Enables null_space_stiffness.

  • null_space_stiffness (wp.array[wp.float32] | float | None) – Posture-control proportional gain, applied per controlled DOF. Must be non-negative; checked the same way as bandwidth. Pass a scalar to apply the same gain to every controlled DOF, an array of shape [total_controlled_dofs] to set them individually, or None to read inputs.null_space_stiffness each step. Must be None when use_null_space_posture_control=False.

  • null_space_damping (wp.array[wp.float32] | float | None) – Damping λ_null for the null-space projector’s own (JJᵀ + λ_null²I)⁻¹, independent of the primary task’s damping. Must be non-negative; checked the same way as bandwidth. Only meaningful when use_joint_limit_avoidance or use_null_space_posture_control is enabled — pass a scalar or an array of shape [controlled_robot_count] to bake a value, or leave it None to read inputs.null_space_damping each step (the default, and the only valid value when both are disabled). Unlike the primary damping, λ_null = 0 is only safe when every robot has at least as many controlled DOFs as its own task dimension (the number of nonzero null_space_axes entries) — otherwise the projector’s own JJᵀ is rank-deficient. That stronger, per-robot requirement is checked at construction only when baked; a live value is the caller’s responsibility there, since it needs the null-space task dimension and controlled_dofs_per_robot compared per robot, not just a sign check.

  • null_space_axes (wp.array[wp.spatial_vector] | wp.spatial_vector | None) – Which of the 6 canonical axes the null-space projector guarantees the secondary objective (joint-limit avoidance/posture control) won’t disturb — zero leaves that axis unprotected, nonzero protects it; only the sign matters, unlike axis_weight’s own soft magnitude. Defaults to axis_weight (every solved axis protected), but the two are independent: an axis can be softly solved for yet left unprotected, e.g. an under-actuated arm with too few DOFs to protect every solved axis and still have a usable null space left over. Unlike axis_weight, an all-zero row is legal — it protects no axes, so the secondary objective is free to move all of them. Only meaningful when use_joint_limit_avoidance or use_null_space_posture_control is enabled.

  • device (Any) – Warp device.

  • requires_grad (bool) – Not supported at this time; must be False.

class Inputs#

Bases: object

Input struct returned by input().

Every compact 1-D field has shape [total_controlled_dofs]; every per-robot field has shape [controlled_robot_count]. Optional fields are None when the corresponding gain is baked at construction.

bandwidth: wp.array[wp.float32] | wp.indexedarray[wp.float32] | None#

Output velocity scale gain, shape [total_controlled_dofs]. None when baked at construction.

damping: wp.array[wp.float32] | wp.indexedarray[wp.float32] | None#

Damped-least-squares regularization λ, shape [controlled_robot_count]. None when baked at construction.

desired_tool_pose_world: wp.array[wp.transformf] | wp.indexedarray[wp.transformf]#

Desired tool pose [m, unitless quaternion], world frame, shape [controlled_robot_count].

jacobian_tool_world: wp.array3d[wp.float32] | <warp._src.types.indexedarray object>#

Tool-point Jacobian, world frame, shape [controlled_robot_count, 6, max_controlled_dofs]; columns beyond a robot’s own controlled-DOF count are unused. Rows 0-2 map a controlled DOF’s velocity to the tool point’s linear velocity [1 or m], rows 3-5 to its angular velocity [1/m or 1].

joint_q: wp.array[wp.float32] | wp.indexedarray[wp.float32]#

Current joint positions [m or rad], shape [total_controlled_dofs].

null_space_damping: wp.array[wp.float32] | wp.indexedarray[wp.float32] | None#

Null-space projector damping λ_null, shape [controlled_robot_count]. None when both secondary objectives are disabled, or when baked at construction.

null_space_stiffness: wp.array[wp.float32] | wp.indexedarray[wp.float32] | None#

Posture-control proportional gain, shape [total_controlled_dofs]. None when disabled, or when baked at construction.

q_des_null: wp.array[wp.float32] | wp.indexedarray[wp.float32] | None#

Null-space posture target [m or rad], shape [total_controlled_dofs]. None unless use_null_space_posture_control=True.

tool_pose_world: wp.array[wp.transformf] | wp.indexedarray[wp.transformf]#

Current tool pose [m, unitless quaternion], world frame, shape [controlled_robot_count].

class Outputs#

Bases: object

Output struct returned by output().

joint_q_target: wp.array[wp.float32] | wp.indexedarray[wp.float32]#

One-step-ahead target joint position [m or rad], shape [total_controlled_dofs] = joint_q + joint_qd_target * dt.

joint_qd_target: wp.array[wp.float32] | wp.indexedarray[wp.float32]#

Target joint velocity [m/s or rad/s], shape [total_controlled_dofs].

__init__(*, controlled_dofs_per_robot, axis_weight=None, bandwidth, damping, ik_method=DifferentialIKMethod.DAMPED_LEAST_SQUARES, adaptive_damping_min=None, adaptive_damping_max=None, adaptive_damping_threshold=None, truncated_svd_threshold=None, use_joint_limit_avoidance=False, joint_limit_avoidance_gain=0.0, joint_limit_avoidance_margin=0.0, joint_pos_lower=None, joint_pos_upper=None, use_null_space_posture_control=False, null_space_stiffness=None, null_space_damping=None, null_space_axes=None, device=None, requires_grad=False)#
input()#

Return a pre-allocated Inputs with zero-initialised arrays.

is_graphable()#
output()#

Return a pre-allocated Outputs with compact velocity/position arrays.

set_joint_limits(*, joint_pos_lower, joint_pos_upper)#

Update the joint position limits used by joint-limit avoidance, in place.

Unlike bandwidth/damping/etc., joint limits have no live inputs.* port – they change far less often, so a setter suits them better than a per-step read. The caller is responsible for keeping inputs.joint_q consistent with the new limits.

Parameters:
  • joint_pos_lower (wp.array[wp.float32]) – Lower joint position limit per controlled DOF [m or rad], shape [total_controlled_dofs].

  • joint_pos_upper (wp.array[wp.float32]) – Upper joint position limit per controlled DOF [m or rad], shape [total_controlled_dofs].

step(*, inputs, outputs, dt)#

Compute one differential-kinematics step and write joint velocity/position targets.

Parameters:
  • inputs (Inputs) – Populated Inputs struct. The Jacobian and tool pose fields must be filled by the caller before each call, consistent with inputs.joint_q.

  • outputs (Outputs) – Outputs struct to write into.

  • dt (float | wp.array[wp.float32]) – Step duration [s], used to integrate joint_qd_target into joint_q_target.

property controlled_robot_count: int#

Number of robots, i.e. the length of controlled_dofs_per_robot.

property max_controlled_dofs: int#

Largest controlled-DOF count over the robots, the padded width of inputs.jacobian_tool_world.

property total_controlled_dofs: int#

Total controlled-DOF count across all robots, the length of every compact port.