Advanced and parallel IK#

The inverse kinematics (IK) solver introduced in Inverse kinematics and motion planning extends in two directions that matter for real manipulation and for training at scale: solving for several end-effector links at once, and solving every environment of a parallel scene in a single call. This page covers both.

The runnable sources for this page are advanced_IK_multilink.py and batched_IK.py.

IK across parallel environments#

When a scene is built with multiple environments, the same solver runs across all of them in one call, useful for generating demonstrations or driving policy rollouts without a Python loop over environments. The example spawns 16 environments and rotates each robot’s end-effector at a different angular speed.

The only structural change from single-environment IK is the batch dimension. Build the scene with n_envs, then give the solver batched targets whose leading dimension is n_envs:

n_envs = 16
scene.build(n_envs=n_envs, env_spacing=(1.0, 1.0))

target_quat = np.tile(np.array([0, 1, 0, 0]), [n_envs, 1])  # (n_envs, 4), pointing downwards
center = np.tile(np.array([0.4, -0.2, 0.25]), [n_envs, 1])
angular_speed = np.random.uniform(-10, 10, n_envs)
r = 0.1
ee_link = robot.get_link("hand")

Inside the loop, pos and quat carry the batch dimension and the solver returns one configuration per environment:

target_pos = np.zeros([n_envs, 3])
target_pos[:, 0] = center[:, 0] + np.cos(i / 360 * np.pi * angular_speed) * r
target_pos[:, 1] = center[:, 1] + np.sin(i / 360 * np.pi * angular_speed) * r
target_pos[:, 2] = center[:, 2]

q = robot.inverse_kinematics(
    link=ee_link,
    pos=target_pos,  # shape (n_envs, 3)
    quat=target_quat,  # shape (n_envs, 4)
    rot_mask=[False, False, True],  # for demo purpose: only restrict direction of z-axis
)

robot.set_qpos(q)
scene.step()

The returned q has shape ([n_envs,] n_dofs): batched when the scene has multiple environments, a plain (n_dofs,) vector otherwise. The same rule applies to the inputs: pos is ([n_envs,] 3) and quat is ([n_envs,] 4). The masks stay length-3 and are shared across the batch. inverse_kinematics_multilink batches the same way, with each entry of poss/quats carrying the leading n_envs dimension.

Tip

Parallel IK is most effective on a GPU backend, where all environments are solved together. See Parallel simulation for how batched environments run, and Control your robot for driving the solved configuration through the controllers instead of setting it directly.

Forward kinematics and the Jacobian#

Inverse kinematics is built on two lower-level operations the same solver exposes directly, and both are useful on their own for analytic control. Forward kinematics is IK’s inverse: it maps a joint configuration to the resulting link poses. The Jacobian relates joint velocities to the end-effector’s spatial velocity, which is the quantity IK differentiates to take each step; it also underpins Jacobian-transpose control and manipulability analysis.

Both require an entity created with requires_jac_and_IK=True, which is the default for the MJCF and URDF robot morphs.

ee_link = robot.get_link("hand")

# Jacobian at the link origin: rows 0-2 are linear, rows 3-5 angular, one column per dof.
jacobian = robot.get_jacobian(link=ee_link)  # shape ([n_envs,] 6, n_dofs)

# Forward kinematics: joint configuration -> world-frame link poses.
qpos = robot.get_qpos()
links_pos, links_quat = robot.forward_kinematics(qpos)
# links_pos:  shape ([n_envs,] n_links, 3)   link-frame origins, world coordinates
# links_quat: shape ([n_envs,] n_links, 4)   orientations, (w, x, y, z)

get_jacobian takes an optional local_point (a length-3 point in the link’s local frame) to evaluate the Jacobian somewhere other than the link origin. forward_kinematics accepts qs_idx_local, links_idx_local, and envs_idx to compute a subset of the configuration, links, or environments; the returned poses are in the world frame, consistent with the world-frame qpos input.

Note

forward_kinematics reuses the IK solver’s internal buffers, which are allocated the first time you call inverse_kinematics on the entity. Solve IK at least once before calling it standalone.