{"id":8419,"library":"pin-pink","title":"Inverse Kinematics for Articulated Robot Models (pink)","description":"Pink (pin-pink) is a Python library for inverse kinematics (IK) of articulated robot models, built upon the Pinocchio robotics framework. It provides a flexible task-based IK solver, allowing users to define various objectives (e.g., end-effector tracking, joint limits, velocity control) that are resolved using a quadratic program (QP). The current version is 4.1.0, and it maintains an active development cycle with several releases per year.","status":"active","version":"4.1.0","language":"en","source_language":"en","source_url":"https://github.com/stephane-caron/pink","tags":["robotics","inverse kinematics","control","pinocchio","manipulation","qp solver"],"install":[{"cmd":"pip install pin-pink","lang":"bash","label":"Install with pip"},{"cmd":"conda install -c conda-forge pin-pink","lang":"bash","label":"Install with conda (recommended for performance)"}],"dependencies":[{"reason":"Core dependency for robot model representation and kinematics.","package":"pinocchio","optional":false},{"reason":"Numerical operations, required by pinocchio and pink.","package":"numpy","optional":false}],"imports":[{"symbol":"solve_ik","correct":"from pink import solve_ik"},{"note":"BodyTask was renamed to FrameTask in v2.0.0.","wrong":"from pink.tasks import BodyTask","symbol":"FrameTask","correct":"from pink.tasks import FrameTask"},{"symbol":"JointVelocityTask","correct":"from pink.tasks import JointVelocityTask"},{"symbol":"RollingTask","correct":"from pink.tasks import RollingTask"},{"symbol":"RelativeFrameTask","correct":"from pink.tasks import RelativeFrameTask"},{"note":"Exceptions were moved to the `pink.exceptions` submodule in v3.3.0.","wrong":"from pink import PinkError","symbol":"PinkError","correct":"from pink.exceptions import PinkError"},{"note":"NoSolutionFound exception was introduced in v3.2.0, replacing AssertionErrors for non-convergent QP solutions.","wrong":"raise AssertionError(...) for no solution","symbol":"NoSolutionFound","correct":"from pink.exceptions import NoSolutionFound"},{"symbol":"FloatingBaseVelocityLimit","correct":"from pink.limits import FloatingBaseVelocityLimit"},{"symbol":"JointLimit","correct":"from pink.limits import JointLimit"}],"quickstart":{"code":"import numpy as np\nimport pinocchio as pin\nfrom pink import solve_ik\nfrom pink.tasks import FrameTask\n\n# 1. Load a sample robot model from Pinocchio\nmodel = pin.buildSampleModelManipulator()\nrobot = pin.RobotWrapper(model)\n\n# 2. Initial configuration (q0 is usually the home configuration)\nq = robot.q0\n\n# 3. Define tasks\n# Target an end-effector frame (e.g., the last frame in the model)\ntip_name = robot.model.frames[-1].name\ntip_id = robot.model.getFrameId(tip_name)\n\n# Get initial end-effector pose\nrobot.data = robot.model.createData()\npin.framesForwardKinematics(robot.model, robot.data, q)\ninitial_tip_pose = robot.data.oMf[tip_id].copy()\n\n# Define a target pose: move the tip 10cm upwards from its initial position\ntarget_pose = initial_tip_pose.copy()\ntarget_pose.translation += np.array([0.0, 0.0, 0.1])\n\n# Create a FrameTask to track the target pose\ntask = FrameTask(\n    tip_name,\n    lm_damping=1e-6 # Levenberg-Marquardt damping for stability\n)\ntask.set_target(target_pose)\n\ntasks = [task]\n\n# 4. Solve IK iteratively\ndt = 0.001  # Integration timestep\nnum_iterations = 500\nsolution_found = False\n\nprint(f\"Starting IK for {tip_name} to reach target position: {target_pose.translation}\")\n\nfor i in range(num_iterations):\n    try:\n        # Solve for joint velocities to achieve tasks\n        velocity = solve_ik(robot.model, robot.data, q, tasks, dt)\n    except Exception as e:\n        # print(f\"IK failed at iteration {i}: {e}\")\n        break\n\n    # Integrate joint velocities to update configuration\n    q = pin.integrate(robot.model, q, velocity * dt)\n\n    # Update forward kinematics for error calculation\n    pin.framesForwardKinematics(robot.model, robot.data, q)\n    current_tip_pose = robot.data.oMf[tip_id]\n\n    # Calculate position error\n    error = np.linalg.norm(current_tip_pose.translation - target_pose.translation)\n\n    if error < 1e-4: # Tolerance for reaching target\n        solution_found = True\n        # print(f\"Target reached in {i+1} iterations. Final error: {error:.6f}\")\n        break\n\nassert np.linalg.norm(current_tip_pose.translation - target_pose.translation) < 1e-3, \\\n    \"IK did not converge to target position within tolerance.\"\nprint(\"IK Quickstart Example: Success!\")","lang":"python","description":"This example demonstrates how to use `pin-pink` to perform inverse kinematics for a simple manipulator robot. It loads a sample robot model, defines an end-effector tracking task to move it to a target position, and then iteratively solves for joint velocities to reach that target."},"warnings":[{"fix":"Update calls to `compute_qp_inequalities` to pass a `pin.Configuration` object. Review the new `Limits` API for details.","message":"The API for `Limit.compute_qp_inequalities` changed in v4.0.0. It now expects a full configuration instance as an argument, rather than just a configuration vector.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"Replace `from pink.tasks import BodyTask` with `from pink.tasks import FrameTask` and update all instantiations of `BodyTask` to `FrameTask`.","message":"The `BodyTask` class was renamed to `FrameTask` in v2.0.0. Code using `BodyTask` will fail with an `AttributeError`.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Update exception imports to `from pink.exceptions import PinkError` (or other specific exceptions like `NoSolutionFound`).","message":"All custom exceptions were moved to the `pink.exceptions` submodule in v3.3.0. Direct imports like `from pink import PinkError` will no longer work.","severity":"gotcha","affected_versions":">=3.3.0"},{"fix":"Replace `except AssertionError` with `except NoSolutionFound` for cases where the IK solver fails to find a valid velocity. Consider checking `exn.results.x is not None` to distinguish between solver failure and non-convergence to a solution.","message":"The `NoSolutionFound` exception was introduced in v3.2.0, replacing previous `AssertionError` instances when the QP solver converged but did not find a solution. Code relying on catching `AssertionError` for this specific case will need updating.","severity":"gotcha","affected_versions":">=3.2.0"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"The `BodyTask` class was renamed to `FrameTask`. Update your code to `from pink.tasks import FrameTask`.","cause":"Using the old class name `BodyTask` after v2.0.0.","error":"AttributeError: module 'pink.tasks' has no attribute 'BodyTask'"},{"fix":"Pass a full Pinocchio `Configuration` instance (e.g., `robot.q`) to the `compute_qp_inequalities` method.","cause":"After v4.0.0, `Limit.compute_qp_inequalities` expects a `pin.Configuration` object, not just a configuration vector.","error":"TypeError: compute_qp_inequalities() missing 1 required positional argument: 'configuration'"},{"fix":"Exceptions were moved to a submodule. Change `from pink import PinkError` to `from pink.exceptions import PinkError`.","cause":"Attempting to import `PinkError` directly from `pink` after v3.3.0.","error":"NameError: name 'PinkError' is not defined"},{"fix":"Update your exception handling to `except NoSolutionFound` (introduced in v3.2.0) instead of `except AssertionError` for solver non-convergence cases. Import it with `from pink.exceptions import NoSolutionFound`.","cause":"Before v3.2.0, certain conditions where the IK solver failed to converge would raise an `AssertionError`. This is now handled by a specific exception.","error":"AssertionError: QP solver did not find a solution"}]}