Skip to content

Inverse Kinematics

SUMMARY

Inverse Kinematics computes joint configurations that achieve a requested end-effector pose.

This skill enables task-space targeting, robot pose solving, and execution planning for manipulation and industrial automation workflows.

UNITS

target_pose is [x, y, z, rx, ry, rz] in meters and Euler XYZ degrees. The returned joint configuration is in degrees.

The Skill

python
q = robot.inverse_kinematics(target_pose=target_pose)
A UR10e manipulator solved to reach a target tool0 pose via inverse kinematics

The Code

Example 1: Simple with default seed and solver

python
"""
Inverse Kinematics with an explicit seed and solver for the Synapse SDK.

Universal Robots (UR10e) is used here purely for illustration. It supports all robots.

Usage:
    python inverse_kinematics_with_seed.py
"""

import numpy as np
from loguru import logger

from telekinesis.synapse.robots.manipulators import universal_robots

def main():
    """Solve IK with an explicit initial joint seed. Supports all robots."""

    # Create the robot (no connect required — IK runs on the kinematic model)
    robot = universal_robots.UniversalRobotsUR10E()

    # Pick a known-reachable configuration and derive its forward-kinematic pose
    q_deg = np.asarray([0, -90 - 5.7, -90, 0, 90, 0], dtype=float)
    target_pose = robot.forward_kinematics(q_deg)

    # Perturb the configuration to use as an IK seed
    q_init = q_deg + np.random.normal(loc=0.0, scale=30.0, size=q_deg.shape)

    # Solve IK with the explicit seed and report the result
    try:
        q = robot.inverse_kinematics(
            target_pose=target_pose,
            q_init=q_init,
            solver="multi_start_clik",
        )
        logger.success(f"IK solution: {q}")
    except (RuntimeError, TypeError, ValueError) as e:
        logger.error(f"IK failed: {type(e).__name__}: {e}")

if __name__ == "__main__":
    main()

Example 2: Advanced with custom q_init and solver.

python
"""
Collision-free Inverse Kinematics example for the Synapse SDK.

Passing ``collision_free_ik=True`` discards solutions that self-collide.
This is most useful on redundant manipulators (e.g. 7-DOF arms) where
multiple joint solutions exist for the same TCP pose.

Universal Robots (UR10e) is used here purely for illustration. It supports all robots.

Usage:
    python inverse_kinematics_collision_free.py
"""

from loguru import logger

from telekinesis.synapse.robots.manipulators import universal_robots

def main():
    """Solve IK with self-collision filtering enabled. Supports all robots."""

    # Create the robot (no connect required — IK runs on the kinematic model)
    robot = universal_robots.UniversalRobotsUR10E()

    # Solve IK for a fixed target pose [x, y, z, rx, ry, rz] (m, deg), rejecting
    # solutions that self-collide.
    target_pose = [0.5, 0.2, 0.3, 180.0, 0.0, 0.0]
    try:
        q = robot.inverse_kinematics(
            target_pose=target_pose,
            collision_free_ik=True,
        )
        logger.success(f"Collision-free IK solution: {q}")
    except (RuntimeError, TypeError, ValueError) as e:
        logger.error(f"IK failed: {type(e).__name__}: {e}")

if __name__ == "__main__":
    main()

Example 3: Advanced with collision-free filtering

python
from loguru import logger

from telekinesis.synapse.robots.manipulators import universal_robots

def main():
    """Solve IK with self-collision filtering enabled. Supports all robots."""

    # Create the robot (no connect required — IK runs on the kinematic model)
    robot = universal_robots.UniversalRobotsUR10E()

    # Solve IK for a fixed target pose [x, y, z, rx, ry, rz] (m, deg), rejecting
    # solutions that self-collide.
    target_pose = [0.5, 0.2, 0.3, 180.0, 0.0, 0.0]
    try:
        q = robot.inverse_kinematics(
            target_pose=target_pose,
            collision_free_ik=True,
        )
        logger.success(f"Collision-free IK solution: {q}")
    except (RuntimeError, TypeError, ValueError) as e:
        logger.error(f"IK failed: {type(e).__name__}: {e}")

if __name__ == "__main__":
    main()

Example 4: Advanced with profiling diagnostics

python
"""
Inverse Kinematics with profiling diagnostics for the Synapse SDK.

Pass ``profile=True`` to return profile diagnostics.

Universal Robots (UR10e) is used here purely for illustration. It supports all robots.

Usage:
    python inverse_kinematics_with_profile.py
"""

from loguru import logger

from telekinesis.synapse.robots.manipulators import universal_robots

def main():
    """Solve IK with ``profile=True`` and log the timing diagnostics. Supports all robots."""

    # Create the robot (no connect required — IK runs on the kinematic model)
    robot = universal_robots.UniversalRobotsUR10E()

    # Solve IK with profiling enabled to return (q, timing) and log both
    target_pose = [0.5, 0.2, 0.3, 180.0, 0.0, 0.0]
    try:
        q, timing = robot.inverse_kinematics(
            target_pose=target_pose,
            profile=True,
        )
        logger.success(f"IK solution: {q}")
        logger.info(f"Total time: {timing['total_s']:.4f} s")
        logger.info(f"Seeds tried: {timing['num_seeds_tried']}")
        logger.info(f"Winning seed: {timing['winning_seed_index']}")
        logger.info(f"Linear error (m):  {timing['linear_error_norm_meters']:.6f}")
        logger.info(f"Angular error (rad): {timing['angular_error_norm_rad']:.6f}")
    except (RuntimeError, TypeError, ValueError) as e:
        logger.error(f"IK failed: {type(e).__name__}: {e}")

if __name__ == "__main__":
    main()

Parameter Configuration

ParameterTypeDefaultDescription
target_poselist[float] | np.ndarray-6-element pose [x, y, z, rx, ry, rz]. Position in meters; orientation as Euler XYZ in degrees.
frame_namestr | NoneNoneTCP frame to solve for. None selects the active TCP.
solverstr"multi_start_clik"IK solver name. Common: "clik", "multi_start_clik", "tracik".
q_initnp.ndarray | NoneNoneOptional initial seed in degrees, length equal to robot DOF, inside joint limits.
epsfloat1e-4Convergence tolerance on the SE(3) pose error norm.
it_maxint1000Maximum solver iterations per seed.
dtfloat0.35Integration step applied to each joint update.
dampfloat1e-12Damping factor for the damped least-squares step.
collision_free_ikboolFalseIf True, reject converged solutions in self-collision.
is_elbow_upboolTruePreferred elbow configuration when elbow-frame metadata is available.
timeoutfloat | NoneNoneOptional wall-clock timeout per solve in seconds.
verboseboolFalseIf True, print diagnostics on rejected or non-converging solutions.
profileboolFalseIf True, return (q, timing) instead of just q.

Returns

ModeTypeDescription
profile=False (default)np.ndarray1D joint configuration in degrees, length equal to robot DOF.
profile=Truetuple[np.ndarray, dict](q, timing) - joint configuration plus a timing/diagnostics dict.

Raises

ExceptionCondition
TypeErrorAn argument's type is invalid - e.g. target_pose is not a list/np.ndarray, frame_name is not a str or None, or eps/it_max/dt/damp/timeout/collision_free_ik/is_elbow_up/profile don't match their expected types
ValueErrorA numeric argument is out of range (e.g. eps <= 0, it_max <= 0, dt <= 0, damp < 0, timeout <= 0); target_pose is not a 6-element 1D vector; q_init has the wrong shape, contains non-finite values, or lies outside the joint limits; frame_name is not a known frame; or solver is not a recognized solver name
RuntimeErrorThe underlying IK solver could not produce a solution - e.g. no C++ solver is available for the requested solver, or the solve failed to converge within it_max iterations / timeout

How to Tune the Parameters

Pass q_init to seed the solver with a known-good configuration - faster and more reliable than starting from scratch. Pass solver to pick a specific backend: "clik" (fastest, single seed), "multi_start_clik" (default, multiple seeds, more robust), or "tracik". Inspect robot.supported_solvers to see what's available.

collision_free_ik=True discards converged solutions that self-collide. Useful on redundant manipulators (7+ DOF) where multiple joint solutions exist for the same TCP pose.

profile=True changes the return type to (q, timing). The timing dict contains total_s, num_seeds_tried, per-seed iteration counts and convergence flags, the winning_seed_index, and the residual linear_error_norm_meters / angular_error_norm_rad - useful for debugging non-converging solves.

By default the active TCP frame is targeted. Pass frame_name to solve for any other configured frame in the URDF.

Choosing a solver

Use "multi_start_clik" (default) when the seed is unknown or far from the workspace. Use "clik" for the tightest latency when you have a reliable seed (e.g. the previous joint state). Use "tracik" when it is built into your install and you need its convergence behaviour.

WARNING

Poses outside the reachable workspace or near kinematic singularities may fail to converge. With profile=True, inspect linear_error_norm_meters and angular_error_norm_rad to detect non-convergence.

Where to Use the Skill

  • Task-space motion planning - Convert a Cartesian target (from a grasp planner or vision pipeline) into joint commands
  • Pick-and-place - Compute joint configurations for approach, grasp, and release poses defined in Cartesian space
  • Trajectory generation - Solve IK at waypoints along a Cartesian path to build a joint-space trajectory
  • Reachability checks - Verify offline whether a set of task poses are kinematically feasible before deployment

When Not to Use the Skill

Do not use Inverse Kinematics when:

  • You already have a joint configuration and need the Cartesian pose - use Forward Kinematics instead
  • The target pose is near a kinematic singularity - IK solutions become unstable; apply replanning or singularity avoidance upstream
  • Low-latency real-time joint commands are required - pre-compute IK solutions offline and look them up at runtime, or use Servo Cartesian which handles IK internally at servo rate