Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@
"toml",
"pink",
"pinocchio",
"qpsolvers",
"nvidia.srl",
"flatdict",
"filelock",
Expand Down
5 changes: 5 additions & 0 deletions source/isaaclab/changelog.d/fix-pink-ik-daqp-install.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fixed
^^^^^

* Fixed Pink IK setup checks to reinstall and report the required ``daqp``
solver when it is missing or incompatible.
45 changes: 24 additions & 21 deletions source/isaaclab/isaaclab/cli/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,22 @@ def _maybe_uninstall_prebundled_torch(
)


# Pinocchio stack required by isaaclab.controllers.pink_ik. Installed via the cmeel
# ``pin`` wheel, which provides the ``pinocchio`` Python module under
# Dependency stack required by isaaclab.controllers.pink_ik. Pinocchio is installed
# via the cmeel ``pin`` wheel, which provides the ``pinocchio`` Python module under
# ``cmeel.prefix/lib/python3.12/site-packages/`` and registers it on sys.path via a
# ``cmeel.pth`` hook.
_PINOCCHIO_STACK = ("pin", "pin-pink==3.1.0", "daqp==0.7.2")
# ``cmeel.pth`` hook. DAQP provides the QP solver selected by the Pink IK controller.
_PINK_IK_STACK = ("pin", "pin-pink==3.1.0", "daqp==0.8.5")


def _ensure_pinocchio_installed(python_exe: str, pip_cmd: list[str], *, probe_env: dict[str, str]) -> None:
"""Ensure ``pinocchio`` is importable, force-installing the cmeel pin stack if not.
def _ensure_pink_ik_dependencies_installed(python_exe: str, pip_cmd: list[str], *, probe_env: dict[str, str]) -> None:
"""Ensure the Pink IK dependency stack is importable, force-installing it if not.

Recent Isaac Sim base images preinstall ``pin-pink`` into the kit's bundled
``site-packages`` without its ``pin`` (cmeel pinocchio) dependency. Pip then
treats the ``pin-pink`` requirement as satisfied and never resolves the
transitive ``pin`` dep, leaving ``import pinocchio`` broken. This probes
for ``pinocchio`` at runtime and force-installs the cmeel stack when needed
so the pink IK controller and its tests work out of the box.
transitive ``pin`` dep, leaving ``import pinocchio`` broken. This checks
the runtime dependencies and force-installs the cmeel stack when needed so
the pink IK controller and its tests work out of the box.

Only runs on Linux x86_64 / aarch64 — the same platforms that have
pinocchio listed in :mod:`isaaclab`'s ``setup.py`` install requirements.
Expand All @@ -194,7 +194,13 @@ def _ensure_pinocchio_installed(python_exe: str, pip_cmd: list[str], *, probe_en
return

probe_result = run_command(
[python_exe, "-c", "import pinocchio"],
[
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Probe assertion may fail on older qpsolvers versions: The probe runs assert 'daqp' in qpsolvers.available_solvers. If qpsolvers is installed but at a version where available_solvers has a different type or naming scheme, this assertion could fail even if daqp is usable. Consider catching the assertion error in the probe gracefully (the probe already handles non-zero returncode, so this is fine as-is, but worth noting).

python_exe,
"-c",
"import inspect, pinocchio, daqp, qpsolvers; "
"assert 'daqp' in qpsolvers.available_solvers; "
"assert 'primal_start' in inspect.signature(daqp.solve).parameters",
],
env=probe_env,
check=False,
capture_output=True,
Expand All @@ -203,19 +209,16 @@ def _ensure_pinocchio_installed(python_exe: str, pip_cmd: list[str], *, probe_en
if probe_result.returncode == 0:
return

print_info(
"``import pinocchio`` failed — the kit-bundled ``pin-pink`` likely shipped without its"
" ``pin`` dep. Force-installing the cmeel pinocchio stack."
)
print_info("Pink IK dependency probe failed. Force-installing the cmeel pinocchio and DAQP stack.")
install_result = run_command(
pip_cmd + ["install", "--upgrade", "--force-reinstall", *_PINOCCHIO_STACK],
pip_cmd + ["install", "--upgrade", "--force-reinstall", *_PINK_IK_STACK],
check=False,
)
if install_result.returncode != 0:
print_warning(
"Force-installing the cmeel pinocchio stack failed (returncode "
"Force-installing the cmeel pinocchio and DAQP stack failed (returncode "
f"{install_result.returncode}). The pink IK controller and its tests will not be"
" usable until ``pin pin-pink==3.1.0 daqp==0.7.2`` is installed manually."
" usable until ``pin pin-pink==3.1.0 daqp==0.8.5`` is installed manually."
)


Expand Down Expand Up @@ -735,10 +738,10 @@ def command_install(install_type: str = "all") -> None:
# Can prevent that from happening.
_ensure_cuda_torch()

# Ensure ``pinocchio`` is actually importable. The kit-bundled ``pin-pink`` in recent
# Isaac Sim images ships without its cmeel ``pin`` dependency, so the transitive
# requirement from ``pip install -e source/isaaclab`` can be silently skipped.
_ensure_pinocchio_installed(python_exe, pip_cmd, probe_env=probe_env)
# Ensure Pink IK's runtime dependencies are actually importable. The kit-bundled
# ``pin-pink`` in recent Isaac Sim images can cause transitive dependencies from
# ``pip install -e source/isaaclab`` to be silently skipped.
_ensure_pink_ik_dependencies_installed(python_exe, pip_cmd, probe_env=probe_env)

# Repoint prebundled packages in Isaac Sim to the environment's copies so
# the active venv/conda versions are always loaded regardless of PYTHONPATH
Expand Down
45 changes: 32 additions & 13 deletions source/isaaclab/isaaclab/controllers/pink_ik/pink_ik.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import torch
from pink import solve_ik
from pink.tasks import Task
from qpsolvers.exceptions import SolverNotFound

from isaaclab.assets import ArticulationCfg
from isaaclab.controllers import utils as controller_utils
Expand All @@ -34,6 +35,9 @@
from .pink_ik_cfg import PinkIKControllerCfg


_QP_SOLVER = "daqp"


class PinkIKController:
"""Integration of Pink IK controller with Isaac Lab.

Expand Down Expand Up @@ -240,30 +244,45 @@ def compute(
# Update Pink's robot configuration with the current joint positions
self.pink_configuration.update(joint_positions_pink)

def _return_current_joint_positions(error: Exception) -> torch.Tensor:
if self.cfg.show_ik_warnings:
print(
"Warning: IK quadratic solver could not find a solution! Did not update the target joint"
f" positions.\nError: {error}"
)

if self.cfg.xr_enabled:
from isaaclab.ui.xr_widgets import XRVisualization

XRVisualization.push_event("ik_error", {"error": error})
return torch.tensor(curr_controlled_joint_pos, device=self.device, dtype=torch.float32)

# Solve IK using Pink's solver
try:
velocity = solve_ik(
self.pink_configuration,
self._variable_input_tasks + self._fixed_input_tasks,
dt,
solver="daqp",
solver=_QP_SOLVER,
safety_break=self.cfg.fail_on_joint_limit_violation,
)
assert not np.isnan(velocity).any(), "Solution to IK contains NaN."
joint_angle_changes = velocity * dt
except SolverNotFound as e:
raise RuntimeError(
f"Pink IK requires the '{_QP_SOLVER}' QP solver. Install the Pink IK stack with "
"``./isaaclab.sh -i`` or manually install ``pin pin-pink==3.1.0 daqp==0.8.5``."
) from e
except TypeError as e:
if "primal_start" in str(e):
raise RuntimeError(
"Pink IK requires a DAQP version compatible with qpsolvers warm-start arguments. "
"Install the Pink IK stack with ``./isaaclab.sh -i`` or manually install "
"``pin pin-pink==3.1.0 daqp==0.8.5``."
) from e
return _return_current_joint_positions(e)
except (AssertionError, Exception) as e:
# Print warning and return the current joint positions as the target
if self.cfg.show_ik_warnings:
print(
"Warning: IK quadratic solver could not find a solution! Did not update the target joint"
f" positions.\nError: {e}"
)

if self.cfg.xr_enabled:
from isaaclab.ui.xr_widgets import XRVisualization

XRVisualization.push_event("ik_error", {"error": e})
return torch.tensor(curr_controlled_joint_pos, device=self.device, dtype=torch.float32)
return _return_current_joint_positions(e)

# Reorder the joint angle changes back to Isaac Lab conventions
joint_vel_isaac_lab = torch.tensor(
Expand Down
2 changes: 1 addition & 1 deletion source/isaaclab/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
# required by isaaclab.isaaclab.controllers.pink_ik
f"pin ; platform_system == 'Linux' and ({SUPPORTED_ARCHS_ARM})",
f"pin-pink==3.1.0 ; platform_system == 'Linux' and ({SUPPORTED_ARCHS_ARM})",
f"daqp==0.7.2 ; platform_system == 'Linux' and ({SUPPORTED_ARCHS_ARM})",
f"daqp==0.8.5 ; platform_system == 'Linux' and ({SUPPORTED_ARCHS_ARM})",
]
# Adds OpenUSD dependencies based on architecture for Kit less mode.
INSTALL_REQUIRES += [
Expand Down
Loading