Configuring Joint Limits and PD Control for Joints in Newton: A Complete Guide

Use JointDofConfig to set per-degree-of-freedom limits with limit_lower/limit_upper and soft-limit stiffness limit_ke/limit_kd, while PD control gains target_ke/target_kd drive the joint toward desired positions and velocities via the JointTargetMode actuator system.

Configuring joint limits and PD control for joints in Newton requires understanding the JointDofConfig dataclass in the newton-physics/newton repository. This configuration system allows you to define per-DoF motion bounds and proportional-derivative controllers that the VBD and XPBD solvers enforce during simulation.

Understanding Joint Configuration in Newton

Newton’s physics simulation uses a builder pattern to construct models. The ModelBuilder class collects per-degree-of-freedom (DoF) configurations through the JointDofConfig dataclass before finalizing them into Warp arrays on the Model object.

The JointDofConfig Dataclass

Located in newton/_src/sim/builder.py (lines 380–460), JointDofConfig encapsulates every tunable parameter for a single joint axis:

class JointDofConfig:
    def __init__(
        self,
        axis: AxisType | Vec3 = Axis.X,
        limit_lower: float = -MAXVAL,
        limit_upper: float = MAXVAL,
        limit_ke: float = 1e4,
        limit_kd: float = 1e1,
        target_pos: float = 0.0,
        target_vel: float = 0.0,
        target_ke: float = 0.0,
        target_kd: float = 0.0,
        armature: float = 0.0,
        effort_limit: float = 1e6,
        velocity_limit: float = 1e6,
        friction: float = 0.0,
        actuator_mode: JointTargetMode | None = None,
    ):
        ...

Joint Limits vs. PD Control

Newton distinguishes between limit constraints and PD drives:

  • Limits (limit_lower, limit_upper) define the admissible range of motion. The soft-limit parameters (limit_ke, limit_kd) add stiffness and damping that activate only when the joint exceeds its bounds.
  • PD Control (target_pos, target_vel, target_ke, target_kd) defines a continuous drive that pushes the joint toward a desired state using the proportional-derivative law:

[ \tau = \text{target_ke} \cdot (\text{target_pos} - q) + \text{target_kd} \cdot (\text{target_vel} - \dot{q}) ]

Configuring Joint Limits and Soft Constraints

To enforce physical boundaries on joint motion, set the limit bounds and compliance parameters in JointDofConfig.

Setting Hard and Soft Limits

import newton
from newton import Axis

builder = newton.ModelBuilder()

# Configure a revolute joint with ±90 degree limits and soft compliance

dof_config = newton.ModelBuilder.JointDofConfig(
    axis=Axis.Z,
    limit_lower=-0.5,      # -0.5 radians (~ -28.6 degrees)

    limit_upper=0.5,       # +0.5 radians

    limit_ke=1e4,          # Soft-limit stiffness (N·m/rad)

    limit_kd=1e2,          # Soft-limit damping (N·m·s/rad)

)

The builder stores these values in internal lists (self.joint_limit_lower, self.joint_limit_ke, etc.) and copies them to Warp arrays during finalize() (around line 9995 in newton/_src/sim/builder.py).

Making Limits Rigid

To create effectively hard limits, increase the stiffness by several orders of magnitude:

dof_config.limit_ke = 1e8  # Near-rigid limit stiffness

dof_config.limit_kd = 1e4  # High damping to prevent oscillation

Implementing PD Control for Joint Actuation

PD controllers generate torques that track desired trajectories. Newton exposes this through the target_* fields of JointDofConfig.

Basic PD Drive Configuration


# Extend the previous config with PD control

dof_config.target_pos = 0.0        # Desired angle (radians)

dof_config.target_vel = 0.0        # Desired angular velocity

dof_config.target_ke = 200.0       # Proportional gain (Nm/rad)

dof_config.target_kd = 20.0        # Derivative gain (Nm·s/rad)

The solver computes the actuation torque each sub-step using the standard PD law. In newton/_src/solvers/vbd/rigid_vbd_kernels.py (lines 2843–2849), the VBD solver applies joint limits before evaluating the PD drive. The XPBD solver in newton/_src/solvers/xpbd/kernels.py (lines 1155–1158) implements the same torque calculation.

Understanding Actuator Modes with JointTargetMode

The JointTargetMode enum determines which control signals the physics engine exposes to the user API. Defined in newton/_src/sim/enums.py (lines 46–76), it provides five distinct modes:

class JointTargetMode(IntEnum):
    NONE = 0                 # No actuator installed

    POSITION = 1             # Position actuator (joint_target_pos)

    VELOCITY = 2             # Velocity actuator (joint_target_vel)

    POSITION_VELOCITY = 3    # Both position and velocity control

    EFFORT = 4               # Drive present but no gains → direct torque control

Automatic Mode Inference

If you omit actuator_mode in JointDofConfig, Newton infers it via JointTargetMode.from_gains(target_ke, target_kd, force_position_velocity, has_drive):

  • No drive (has_drive=False) → NONE
  • Zero gains (target_ke=0, target_kd=0) → EFFORT (direct torque)
  • Only position gain (target_ke>0) → POSITION
  • Only velocity gain (target_kd>0) → VELOCITY
  • Both gains or force_position_velocity=True → POSITION_VELOCITY

This logic is used by the URDF and MJCF importers in newton/_src/utils/import_urdf.py and newton/_src/utils/import_mjcf.py to map XML attributes to Newton's internal representation.

Practical Implementation Examples

Revolute Joint with Limits and PD Drive

import newton
from newton import Axis

builder = newton.ModelBuilder()

# Create static parent and dynamic child

parent = builder.add_body(mass=0.0)  # World/ground

child = builder.add_body(mass=1.0, com=(0, 0, 0))

# Configure the joint DoF

joint_cfg = newton.ModelBuilder.JointDofConfig(
    axis=Axis.Z,
    limit_lower=-0.5,           # -28.6 degrees

    limit_upper=0.5,            # +28.6 degrees

    limit_ke=1e4,               # Soft limit stiffness

    limit_kd=1e2,               # Soft limit damping

    target_pos=0.0,             # Target angle

    target_vel=0.0,             # Target velocity

    target_ke=200.0,            # PD proportional gain

    target_kd=20.0,             # PD derivative gain

)

# Add the joint

builder.add_joint_revolute(
    parent=parent,
    child=child,
    angular_axes=[joint_cfg]
)

model = builder.finalize()

Setting Global Default Gains

Instead of configuring every joint individually, modify builder.default_joint_cfg (initialized at line 775 in newton/_src/sim/builder.py):

builder = newton.ModelBuilder()

# Override defaults for all subsequent joints

builder.default_joint_cfg.target_ke = 150.0
builder.default_joint_cfg.target_kd = 15.0
builder.default_joint_cfg.limit_ke = 5e3
builder.default_joint_cfg.limit_kd = 50.0

# Now add joints without specifying gains explicitly

builder.add_joint_revolute(
    parent=p,
    child=c,
    angular_axes=[newton.ModelBuilder.JointDofConfig(axis=Axis.Z)]
)

Importing URDF with Automatic Configuration

The URDF importer in newton/_src/utils/import_urdf.py automatically maps XML tags to JointDofConfig fields:

builder = newton.ModelBuilder()
builder.add_urdf(
    "robots/ur5e.urdf",
    force_position_velocity_actuation=True
)

The importer parses <limit lower="..." upper="..."/> into limit_lower/limit_upper and <dynamics stiffness="..." damping="..."/> into target_ke/target_kd. It calls JointTargetMode.from_gains (similar to lines 202-207 in the USD importer) to set the actuator mode.

Solver Integration and Performance

Newton’s VBD and XPBD solvers read joint configuration directly from the Model object’s Warp arrays.

In newton/_src/solvers/vbd/rigid_vbd_kernels.py (lines 2843–2849), the VBD solver applies joint limits using joint_limit_ke and joint_limit_kd before evaluating the PD drive. The XPBD solver in newton/_src/solvers/xpbd/kernels.py (lines 1155–1158) computes actuation torque using joint_target_ke and joint_target_kd according to the standard PD law.

Both solvers treat limit parameters as soft constraints that activate only at boundary violations, while PD parameters provide continuous actuation forces when the corresponding JointTargetMode is active.

Advanced Configuration Techniques

  • Hard limits (zero compliance): Set limit_ke=1e8 and limit_kd=1e4 to minimize penetration and oscillation, creating effectively rigid stops as enforced in newton/_src/solvers/vbd/rigid_vbd_kernels.py.

  • Disable actuation: Set target_ke=0, target_kd=0, and actuator_mode=JointTargetMode.NONE, or rely on JointTargetMode.from_gains inference with zero gains to prevent the solver from applying drive forces.

  • Direct torque control: Set actuator_mode=JointTargetMode.EFFORT with zero PD gains, then write raw torques directly to Control.joint_f during simulation steps.

  • Joint friction: Use the friction field in JointDofConfig; the VBD solver incorporates this when computing joint forces in newton/_src/solvers/vbd/rigid_vbd_kernels.py.

  • Per-world limit overrides: After building the base model, use model.joint_limit_lower.assign(new_vals) inside world loops to vary limits across different simulation environments, as demonstrated in newton/tests/test_mujoco_solver.py.

Summary

  • Joint limits are defined by limit_lower and limit_upper in JointDofConfig, with soft-limit compliance controlled by limit_ke (stiffness) and limit_kd (damping).
  • PD control uses target_pos, target_vel, target_ke, and target_kd to compute actuation torques via the proportional-derivative law in the VBD and XPBD solvers.
  • Actuator modes (JointTargetMode) determine which control signals are exposed; Newton infers the mode automatically from gains using from_gains if not specified explicitly.
  • All configuration data flows from JointDofConfig through the ModelBuilder into Warp arrays (joint_limit_*, joint_target_*) that the physics kernels read directly.

Frequently Asked Questions

How do I configure hard joint limits with zero compliance in Newton?

Set the soft-limit stiffness limit_ke to a very large value (e.g., 1e8) and the damping limit_kd to a proportionally high value (e.g., 1e4). This minimizes penetration at the limit and prevents oscillation, effectively creating a hard stop as enforced in newton/_src/solvers/vbd/rigid_vbd_kernels.py.

How do I completely disable actuation on a specific joint?

Set both PD gains to zero (target_ke=0.0, target_kd=0.0) and explicitly set actuator_mode=JointTargetMode.NONE in the JointDofConfig. Alternatively, if both gains are zero and no drive is specified, JointTargetMode.from_gains automatically infers NONE mode, preventing the solver from applying any drive forces.

How do I switch a joint from position control to direct torque control?

Set actuator_mode=JointTargetMode.EFFORT and ensure both target_ke and target_kd are zero. This configuration disables the PD drive and exposes the joint_f array in the Control object, allowing you to write raw torques directly during simulation steps.

How do I add friction to a joint in Newton?

Use the friction field in JointDofConfig. The VBD solver incorporates this friction term when computing joint forces in newton/_src/solvers/vbd/rigid_vbd_kernels.py, applying a resistive torque proportional to the friction coefficient and joint velocity.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →