Tasks and limits#

Differential inverse kinematics computes a velocity \(v = \Delta q / \mathrm{d}t\) that reduces weighted task errors. Each task defines a residual \(e(q) \in \mathbb{R}^k\) driven toward zero and a Jacobian \(J(q)\) such that \(J \Delta q \approx -\alpha e\) at first order (see Notation).

mink-warp uses the same tasks vs limits split as Mink:

Tasks (soft objectives)

Weighted least-squares terms stacked into \(W, e\). Can conflict; the solver minimises combined error.

Limits (hard constraints)

Enforced by ConstrainedSolver via ADMM on the same \(H, c\) as DLS. Mink’s G Δq h form; two GPU paths (box / general inequality).

Frame task#

task = mw.FrameTask(
    "attachment_site", "site",
    position_cost=1.0,
    orientation_cost=1.0,
    gain=0.8,
    lm_damping=1.0,
)
task.set_target_from_configuration(cfg)

Posture and damping#

Regularize redundant dofs and avoid drift:

posture = mw.PostureTask(model, cost=1e-2)
posture.set_target_from_configuration(cfg)
damping = mw.DampingTask(model, cost=1e-3)

Center of mass#

com = mw.ComTask(cost=np.array([1.0, 1.0, 0.1]))
com.set_target_from_configuration(cfg)

Relative frame#

Regulate a frame pose in another frame’s coordinates (e.g. hand relative to torso):

rel = mw.RelativeFrameTask(
    "left_palm", "site",
    "torso_link", "body",
    position_cost=5.0, orientation_cost=0.5,
)
rel.set_target_from_configuration(cfg)

Equality constraints#

Regulate MuJoCo equality rows (closed chains). Uses host mj_forward per world:

eq = mw.EqualityConstraintTask(model, cost=500.0, gain=0.5)
tasks = [frame, posture, eq]

Collision avoidance#

Configuration-dependent inequalities G Δq h (forces the general ADMM path):

limits = [
    mw.ConfigurationLimit(model),
    mw.CollisionAvoidanceLimit(
        model,
        geom_pairs=[(["wrist_3_link"], ["floor", "wall"])],
    ),
]
v = mw.solve_ik(cfg, tasks, dt, limits=limits)

Soft vs hard joint limits#

Soft (penalty in the task stack, unconstrained DLS):

soft = mw.JointLimitTask(model, cost=10.0)

Hard (never violate bounds — Mink limits=None default):

v = mw.solve_ik(cfg, tasks, dt, limits=None)  # default ConfigurationLimit

# or explicit
v = mw.solve_ik(cfg, tasks, dt, limits=[mw.ConfigurationLimit(model)])

Hard velocity cap#

limits = [
    mw.ConfigurationLimit(model),
    mw.VelocityLimit(model, 3.0),
]
v = mw.solve_ik(cfg, tasks, dt, limits=limits)

General inequalities#

For half-spaces or coupled bounds that are not a per-dof box, use LinearInequalityLimit or subclass Limit. See Constrained IK (hard limits) for the box vs general ADMM paths and tuning.

Typical stack#

tasks = [frame, posture, mw.DampingTask(model, cost=1e-3)]
limits = [mw.ConfigurationLimit(model)]
solver = mw.ConstrainedSolver(cfg, limits=limits)
solver.solve_and_integrate(tasks, dt=0.01)

See Solver backends, Constrained IK (hard limits), and Tasks for full API details.

Examples#

Runnable mjviser demos (numbered by complexity):

#

Script

Uses

01

examples/01_panda_ik.py

FrameTask, JointLimitTask (soft)

02

examples/02_constrained_ur5e.py

CollisionAvoidanceLimit, VelocityLimit (hard)

03

examples/03_equality_cassie.py

EqualityConstraintTask

04

examples/04_self_collision_dual_iiwa.py

Self-collision CollisionAvoidanceLimit

05

examples/05_relative_frame_g1.py

RelativeFrameTask + collision

See Examples.