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=1e8andlimit_kd=1e4to minimize penetration and oscillation, creating effectively rigid stops as enforced innewton/_src/solvers/vbd/rigid_vbd_kernels.py. -
Disable actuation: Set
target_ke=0,target_kd=0, andactuator_mode=JointTargetMode.NONE, or rely onJointTargetMode.from_gainsinference with zero gains to prevent the solver from applying drive forces. -
Direct torque control: Set
actuator_mode=JointTargetMode.EFFORTwith zero PD gains, then write raw torques directly toControl.joint_fduring simulation steps. -
Joint friction: Use the
frictionfield inJointDofConfig; the VBD solver incorporates this when computing joint forces innewton/_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 innewton/tests/test_mujoco_solver.py.
Summary
- Joint limits are defined by
limit_lowerandlimit_upperinJointDofConfig, with soft-limit compliance controlled bylimit_ke(stiffness) andlimit_kd(damping). - PD control uses
target_pos,target_vel,target_ke, andtarget_kdto 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 usingfrom_gainsif not specified explicitly. - All configuration data flows from
JointDofConfigthrough theModelBuilderinto 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →