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.

SUPPORTED ROBOTS

Available on all supported manipulators.

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)

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()

The Explanation of the Code

Inverse kinematics is a purely kinematic computation that runs offline against the URDF - no controller connection required.

python
from telekinesis.synapse.robots.manipulators import universal_robots

robot = universal_robots.UniversalRobotsUR10E()

Create the robot. UR10e is used here for illustration; the same pattern works for every supported brand.

python
target_pose = [0.5, 0.2, 0.3, 180.0, 0.0, 0.0]

The target is a 6-element vector [x, y, z, rx, ry, rz]. Position is in meters; orientation is Euler XYZ in degrees. Transformation matrices and pose objects are not accepted - convert them to this vector form before calling.

python
q = robot.inverse_kinematics(target_pose=target_pose)

With no other arguments, IK runs the default multi-start CLIK solver, seeded from the robot's current joint state, and returns the joint positions in degrees.

python
q = robot.inverse_kinematics(
    target_pose=target_pose,
    q_init=q_init,
    solver="multi_start_clik",
)

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.

python
q = robot.inverse_kinematics(
    target_pose=target_pose,
    collision_free_ik=True,
)

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.

python
q, timing = robot.inverse_kinematics(
    target_pose=target_pose,
    profile=True,
)

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.

How to Tune the Parameters

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.

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.

Return Value

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.

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