Source code for concepts.simulator.urdf_utils.scipy_ik
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# File : scipy_ik.py
# Author : Jiayuan Mao
# Email : maojiayuan@gmail.com
# Date : 12/18/2024
#
# This file is part of Project Concepts.
# Distributed under terms of the MIT license.
import numpy as np
from scipy.optimize import fmin
from concepts.math.rotationlib_xyzw import quat_diff
__all__ = ['scipy_inverse_kinematics']
def _gen_ik_min_func(
fk_func, pos, quat, lower_bound, upper_bound,
additional_constraint_func=None,
pos_weight=1.0, quat_weight=1.0, boundary_repulsion_weight=0.1, additional_loss_func=None, additional_loss_weight=1.0
):
def func(qpos):
if not np.all(lower_bound <= qpos) or not np.all(qpos <= upper_bound):
return 1e-9
if additional_constraint_func is not None:
if not additional_constraint_func(qpos):
return 1e-9
fk_pos, fk_quat = fk_func(qpos)
pos_error = np.linalg.norm(fk_pos - pos)
quat_error = quat_diff(fk_quat, quat)
rv = pos_error * pos_weight + quat_error * quat_weight
lower_bound_distance = np.maximum(qpos - lower_bound, 0)
upper_bound_distance = np.maximum(upper_bound - qpos, 0)
if boundary_repulsion_weight > 0:
# Only activate when the joint is close to the bound (within 0.1 rad).
rv -= boundary_repulsion_weight * np.sum(np.minimum(lower_bound_distance, 0.1) ** 2) # Maximize the distance to the lower bound.
rv -= boundary_repulsion_weight * np.sum(np.minimum(upper_bound_distance, 0.1) ** 2) # Maximize the distance to the upper bound.
if additional_loss_func is not None:
additional_loss = additional_loss_func(qpos)
rv += additional_loss * additional_loss_weight
return rv
return func
[docs]
def scipy_inverse_kinematics(
fk_func, pos, quat, lower_bound, upper_bound,
q0=None, sample_func=None,
pos_weight=1.0, quat_weight=1.0, boundary_repulsion_weight=0.1,
additional_constraint_func=None, additional_loss_func=None, additional_loss_weight=1.0,
nr_trials=50, verbose=False
):
if sample_func is None and q0 is None:
raise ValueError('Either q0 or sample_func should be provided.')
if sample_func is None and q0 is not None:
sample_func = lambda: q0
func = _gen_ik_min_func(
fk_func, pos, quat,
lower_bound, upper_bound,
pos_weight=pos_weight, quat_weight=quat_weight,
boundary_repulsion_weight=boundary_repulsion_weight,
additional_constraint_func=additional_constraint_func,
additional_loss_func=additional_loss_func,
additional_loss_weight=additional_loss_weight
)
best_loss = np.inf
best_qpos = None
for i in range(nr_trials):
init_guess = sample_func()
qpos = fmin(func, x0=init_guess, disp=verbose)
loss = func(qpos)
if loss < best_loss:
best_loss = loss
best_qpos = qpos
return best_qpos