Implementing Inverse Kinematics (IK) for Robot Control in Newton: A Complete Guide
Newton provides a GPU-accelerated IK framework that solves position and orientation constraints for articulated robots using a single high-level API call.
Implementing inverse kinematics for robot control in Newton leverages a modular, Warp-based pipeline that runs entirely on the GPU. The system, exposed through the newton/ik.py module, orchestrates objectives, optimizers, and samplers to solve thousands of parallel IK problems with minimal CPU overhead. This guide covers the architecture, implementation steps, and optimization strategies for production robot control.
How Newton's IK System Works
Newton's IK architecture separates concerns into three distinct components that communicate through GPU buffers. The entry point is the IKSolver class in newton/ik.py, which manages the optimization loop and seed selection.
Core Components: Objectives, Optimizers, and Samplers
Objectives encode the geometric constraints that the solver must satisfy. The built-in implementations in newton/_src/sim/ik/ik_objectives.py include:
IKObjectivePosition– Targets a specific world position for a linkIKObjectiveRotation– Targets a specific orientation using quaternionsIKObjectiveJointLimit– Applies soft penalties when joints exceed bounds
Optimizers perform the numerical optimization on residuals and Jacobians. Defined in newton/_src/sim/ik/ik_solver.py:
IKOptimizerLM– Levenberg-Marquardt for robust nonlinear least squaresIKOptimizerLBFGS– Limited-memory BFGS for large-scale problems with many degrees of freedom
Samplers generate initial seed configurations to improve convergence robustness. The IKSampler enum supports NONE, GAUSS, UNIFORM, and ROBERTS (low-discrepancy) strategies, implemented via kernels in ik_solver.py.
Setting Up Your First IK Solver in Newton
Implementing IK in Newton follows a four-step workflow: build the model, define objectives, configure the solver, and execute the solve step.
Step 1: Build the Robot Model
Load your robot definition using ModelBuilder and finalize it before passing to the IK system:
import newton as nd
builder = nd.ModelBuilder()
builder.add_mjcf(nd.utils.download_asset("franka_panda") / "mjcf/panda_with_hand.xml")
builder.add_ground_plane()
model = builder.finalize()
Step 2: Define IK Objectives
Create objective instances for each constraint. For a simple reach task:
import warp as wp
import newton.ik as ik
target = wp.array([wp.vec3(0.4, 0.0, 0.2)], dtype=wp.vec3)
pos_obj = ik.IKObjectivePosition(
link_index=7,
link_offset=wp.vec3(0, 0, 0),
target_positions=target,
weight=1.0,
)
Step 3: Configure the IKSolver
Instantiate the solver with your model, objectives, and optimization parameters:
solver = ik.IKSolver(
model=model,
n_problems=1,
objectives=[pos_obj],
optimizer=ik.IKOptimizer.LM,
jacobian_mode=ik.IKJacobianType.ANALYTIC,
sampler=ik.IKSampler.GAUSS,
n_seeds=8,
noise_std=0.05,
)
Step 4: Execute the Solve Step
Call step() each frame with input and output buffers. The solver writes solved configurations to joint_q_out:
q_in = model.joint_q.reshape((1, -1))
q_out = wp.zeros_like(q_in)
solver.step(q_in, q_out, iterations=24)
# q_out now contains the solved joint angles
Complete Code Examples for Newton IK
The following examples demonstrate production-ready implementations for common robot control scenarios.
Minimal Point-to-Point IK
This snippet solves a single position target for a Franka Emika Panda arm using LBFGS optimization:
import warp as wp
import newton as nd
import newton.ik as ik
# Build model
builder = nd.ModelBuilder()
builder.add_mjcf(nd.utils.download_asset("franka_panda") / "mjcf/panda_with_hand.xml")
model = builder.finalize()
# Define position objective for end-effector (link 7)
target_pos = wp.array([wp.vec3(0.4, 0.0, 0.2)], dtype=wp.vec3)
pos_obj = ik.IKObjectivePosition(
link_index=7,
link_offset=wp.vec3(0, 0, 0),
target_positions=target_pos,
weight=1.0,
)
# Configure solver with LBFGS
solver = ik.IKSolver(
model=model,
n_problems=1,
objectives=[pos_obj],
optimizer=ik.IKOptimizer.LBFGS,
jacobian_mode=ik.IKJacobianType.ANALYTIC,
)
# Solve
q = model.joint_q.reshape((1, -1))
solver.step(q, q, iterations=10)
print("Solved joint angles:", q.numpy())
Position, Rotation, and Joint Limits
This example combines multiple constraint types for precise end-effector control while respecting mechanical limits:
# Assume model is built as above
# ...
# Position objective
pos_obj = ik.IKObjectivePosition(
link_index=7,
link_offset=wp.vec3(0, 0, 0),
target_positions=wp.array([wp.vec3(0.5, 0.1, 0.3)], dtype=wp.vec3),
weight=1.0,
)
# Rotation objective (target identity quaternion)
target_rot = wp.array([wp.vec4(0, 0, 0, 1)], dtype=wp.vec4)
rot_obj = ik.IKObjectiveRotation(
link_index=7,
link_offset_rotation=wp.quat_identity(),
target_rotations=target_rot,
weight=0.5,
)
# Joint limit objective (soft penalty)
limit_obj = ik.IKObjectiveJointLimit(
joint_limit_lower=model.joint_limit_lower,
joint_limit_upper=model.joint_limit_upper,
weight=10.0,
)
# Assemble solver with Levenberg-Marquardt and Gaussian sampling
solver = ik.IKSolver(
model=model,
n_problems=1,
objectives=[pos_obj, rot_obj, limit_obj],
optimizer=ik.IKOptimizer.LM,
jacobian_mode=ik.IKJacobianType.ANALYTIC,
sampler=ik.IKSampler.GAUSS,
n_seeds=8,
noise_std=0.03,
)
# Solve
q = model.joint_q.reshape((1, -1))
solver.step(q, q, iterations=30)
Batch Solving Multiple Robots
Solve 100 identical robot instances with different target positions in parallel:
import numpy as np
import warp as wp
import newton as nd
import newton.ik as ik
# Build model
builder = nd.ModelBuilder()
builder.add_mjcf(nd.utils.download_asset("franka_panda") / "mjcf/panda_with_hand.xml")
model = builder.finalize()
# Batch configuration
n_problems = 100
q = wp.zeros((n_problems, model.joint_coord_count), dtype=wp.float32, device=model.device)
# Generate random targets
rng = np.random.default_rng(42)
targets_np = rng.uniform(-0.5, 0.5, size=(n_problems, 3)).astype(np.float32)
targets_wp = wp.array(targets_np, dtype=wp.vec3, device=model.device)
# Single objective applied to all problems
pos_obj = ik.IKObjectivePosition(
link_index=7,
link_offset=wp.vec3(0, 0, 0),
target_positions=targets_wp,
weight=1.0,
)
# Batch solver with uniform sampling
solver = ik.IKSolver(
model=model,
n_problems=n_problems,
objectives=[pos_obj],
optimizer=ik.IKOptimizer.LM,
jacobian_mode=ik.IKJacobianType.ANALYTIC,
sampler=ik.IKSampler.UNIFORM,
n_seeds=4,
)
# Solve all 100 problems in parallel
solver.step(q, q, iterations=20)
print("Batch solved joint angles shape:", q.shape)
Advanced Configuration and Performance Tuning
Optimizing IK performance in Newton requires selecting the right Jacobian mode, optimizer, and sampling strategy for your specific robot morphology.
Choosing Between Analytic and Autodiff Jacobians
Newton supports two Jacobian computation modes via the IKJacobianType enum:
- ANALYTIC – Uses hand-derived Jacobians for position and rotation objectives. This mode is orders of magnitude faster on the GPU and should be preferred when available.
- AUTODIFF – Uses Warp's automatic differentiation tape to compute Jacobians. Use this for custom objectives where analytic derivatives are not implemented, or for complex constraint chains.
Set the mode when constructing the solver:
solver = ik.IKSolver(
model=model,
n_problems=1,
objectives=[pos_obj],
jacobian_mode=ik.IKJacobianType.ANALYTIC, # or .AUTODIFF
)
Optimizer Selection: Levenberg-Marquardt vs LBFGS
Newton provides two optimization algorithms via the IKOptimizer enum:
- LM (Levenberg-Marquardt) – Robust nonlinear least squares solver ideal for standard IK problems with position and rotation objectives. Handles singularities well through damping.
- LBFGS – Quasi-Newton method better suited for highly redundant robots with many degrees of freedom where the Hessian approximation reduces iteration count.
For most robotic arms, LM provides the best balance of robustness and speed. For humanoid robots with 50+ joints, LBFGS often converges faster.
Sampling Strategies for Robustness
When n_seeds > 1, Newton generates multiple initial configurations to avoid local minima:
- GAUSS – Adds Gaussian noise to the current configuration, clamping to joint limits. Best for fine-tuning near valid solutions.
- UNIFORM – Samples each joint uniformly within its limits. Better for global exploration when the target is far from the current pose.
- ROBERTS – Deterministic low-discrepancy sequence for reproducible coverage across batches.
Limit n_seeds to small powers of two (4–16) to minimize GPU memory traffic while maintaining robustness.
Summary
- Newton's IK system lives in
newton/ik.pyand provides a GPU-accelerated pipeline for solving robot inverse kinematics. - The architecture separates Objectives (constraints), Optimizers (LM and LBFGS), and Samplers (seed generation) for modular configuration.
- Use
IKJacobianType.ANALYTICfor maximum performance on standard position/rotation tasks, andAUTODIFFfor custom constraints. - Batch solving supports
n_problems > 1for parallel IK computation across hundreds of robot instances. - Reference implementation available in
newton/examples/ik/example_ik_h1.pyfor interactive control of humanoid robots.
Frequently Asked Questions
What is the difference between analytic and autodiff Jacobians in Newton IK?
Analytic Jacobians are hand-derived mathematical gradients implemented in kernels like _pos_jac_analytic in newton/_src/sim/ik/ik_objectives.py. They execute directly on the GPU without recording operations to a tape, making them orders of magnitude faster for standard objectives. Autodiff Jacobians use Warp's automatic differentiation system to record the forward pass and compute gradients during backward propagation. Use autodiff when implementing custom objectives where manual Jacobian derivation is impractical.
How do I handle joint limits when implementing IK in Newton?
Newton provides the IKObjectiveJointLimit class in newton/_src/sim/ik/ik_objectives.py to enforce joint limits via soft penalties. Instantiate it with the model's joint_limit_lower and joint_limit_upper arrays, then include it in the solver's objectives list with an appropriate weight (typically 5.0–10.0). The objective adds a residual that pushes joints back into bounds when they exceed limits, and it supports analytic Jacobians for minimal performance overhead.
Can Newton solve IK for multiple robots simultaneously?
Yes. Set n_problems to the desired batch size when constructing IKSolver in newton/ik.py. The solver automatically reshapes all internal buffers to handle (n_problems, n_coords) arrays. Objectives like IKObjectivePosition accept target arrays where the first dimension matches n_problems, allowing you to specify different targets for each robot instance. This batching executes entirely on the GPU via Warp kernels, enabling parallel solving for hundreds of robots with a single API call.
Where can I find the complete source code for Newton's IK system?
The public API resides in [newton/ik.py](https://github.com/newton-physics/newton/blob/main/newton/ik.py), which exports IKSolver, objective classes, and configuration enums. The core implementation lives in [newton/_src/sim/ik/ik_solver.py](https://github.com/newton-physics/newton/blob/main/newton/_src/sim/ik/ik_solver.py) for optimization logic and [newton/_src/sim/ik/ik_objectives.py](https://github.com/newton-physics/newton/blob/main/newton/_src/sim/ik/ik_objectives.py) for constraint residuals and Jacobians. For a complete working example, see [newton/examples/ik/example_ik_h1.py](https://github.com/newton-physics/newton/blob/main/newton/examples/ik/example_ik_h1.py), which demonstrates interactive IK control of a Unitree H1 humanoid robot.
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 →