use na::{self, DVector, Isometry3, RealField, Vector3, Vector6};
use chain::*;
use errors::*;
use funcs::*;
fn calc_pose_diff<T>(a: &Isometry3<T>, b: &Isometry3<T>) -> Vector6<T>
where
T: RealField,
{
let p_diff = a.translation.vector - b.translation.vector;
let w_diff = b.rotation.rotation_to(&a.rotation).scaled_axis();
Vector6::new(
p_diff[0], p_diff[1], p_diff[2], w_diff[0], w_diff[1], w_diff[2],
)
}
fn calc_pose_diff_with_constraints<T>(
a: &Isometry3<T>,
b: &Isometry3<T>,
constraints_array: [bool; 6],
) -> DVector<T>
where
T: RealField,
{
let full_diff = calc_pose_diff(a, b);
let use_dof = constraints_array.into_iter().filter(|x| **x).count();
let mut diff = DVector::from_element(use_dof, na::zero());
let mut index = 0;
for (i, use_i) in constraints_array.iter().enumerate() {
if *use_i {
diff[index] = full_diff[i];
index += 1;
}
}
diff
}
#[derive(Clone, Copy, Debug)]
pub struct Constraints {
pub position_x: bool,
pub position_y: bool,
pub position_z: bool,
pub rotation_x: bool,
pub rotation_y: bool,
pub rotation_z: bool,
}
impl Default for Constraints {
fn default() -> Self {
Self {
position_x: true,
position_y: true,
position_z: true,
rotation_x: true,
rotation_y: true,
rotation_z: true,
}
}
}
fn constraints_to_bool_array(constraints: Constraints) -> [bool; 6] {
let mut arr = [true; 6];
arr[0] = constraints.position_x;
arr[1] = constraints.position_y;
arr[2] = constraints.position_z;
arr[3] = constraints.rotation_x;
arr[4] = constraints.rotation_y;
arr[5] = constraints.rotation_z;
arr
}
pub trait InverseKinematicsSolver<T>
where
T: RealField,
{
fn solve(&self, arm: &SerialChain<T>, target_pose: &Isometry3<T>) -> Result<(), IKError> {
self.solve_with_constraints(arm, target_pose, &Constraints::default())
}
fn solve_with_constraints(
&self,
arm: &SerialChain<T>,
target_pose: &Isometry3<T>,
constraints: &Constraints,
) -> Result<(), IKError>;
}
pub struct JacobianIKSolver<T: RealField> {
pub allowable_target_distance: T,
pub allowable_target_angle: T,
pub jacobian_multiplier: T,
pub num_max_try: usize,
nullspace_function: Option<Box<dyn Fn(&[T]) -> Vec<T>>>,
}
impl<T> JacobianIKSolver<T>
where
T: RealField,
{
pub fn new(
allowable_target_distance: T,
allowable_target_angle: T,
jacobian_multiplier: T,
num_max_try: usize,
) -> JacobianIKSolver<T> {
JacobianIKSolver {
allowable_target_distance,
allowable_target_angle,
jacobian_multiplier,
num_max_try,
nullspace_function: None,
}
}
pub fn set_nullspace_function(&mut self, func: Box<dyn Fn(&[T]) -> Vec<T>>) {
self.nullspace_function = Some(func);
}
pub fn clear_nullspace_function(&mut self) {
self.nullspace_function = None;
}
fn add_positions_with_multiplier(&self, input: &[T], add_values: &[T]) -> Vec<T> {
input
.iter()
.zip(add_values.iter())
.map(|(ang, add)| *ang + self.jacobian_multiplier * *add)
.collect()
}
fn solve_one_loop_with_constraints(
&self,
arm: &SerialChain<T>,
target_pose: &Isometry3<T>,
constraints_array: [bool; 6],
) -> Result<DVector<T>, IKError> {
let orig_positions = arm.joint_positions();
let dof = orig_positions.len();
let t_n = arm.end_transform();
let err = calc_pose_diff_with_constraints(target_pose, &t_n, constraints_array);
let orig_positions = arm.joint_positions();
let mut jacobi = jacobian(arm);
let use_dof = constraints_array.into_iter().filter(|x| **x).count();
let mut removed_count = 0;
for (i, use_i) in constraints_array.iter().enumerate() {
if !use_i {
jacobi = jacobi.remove_row(i - removed_count);
removed_count += 1;
}
}
let positions_vec = if dof > use_dof {
const EPS: f64 = 0.0001;
match self.nullspace_function {
Some(ref f) => {
let jacobi_inv = jacobi.clone().pseudo_inverse(na::convert(EPS)).unwrap();
let d_q = jacobi_inv.clone() * err
+ (na::DMatrix::identity(dof, dof) - jacobi_inv * jacobi)
* na::DVector::from_vec(f(&orig_positions));
self.add_positions_with_multiplier(&orig_positions, d_q.as_slice())
}
None => self.add_positions_with_multiplier(
&orig_positions,
jacobi
.svd(true, true)
.solve(&err, na::convert(EPS))
.unwrap()
.as_slice(),
),
}
} else {
self.add_positions_with_multiplier(
&orig_positions,
jacobi
.lu()
.solve(&err)
.ok_or(IKError::InverseMatrixError)?
.as_slice(),
)
};
arm.set_joint_positions_unchecked(&positions_vec);
Ok(calc_pose_diff_with_constraints(
target_pose,
&arm.end_transform(),
constraints_array,
))
}
fn solve_with_constraints_internal(
&self,
arm: &SerialChain<T>,
target_pose: &Isometry3<T>,
constraints: &Constraints,
) -> Result<(), IKError> {
let constraints_array = constraints_to_bool_array(*constraints);
let orig_positions = arm.joint_positions();
let use_dof = constraints_array.into_iter().filter(|x| **x).count();
if orig_positions.len() < use_dof {
return Err(IKError::PreconditionError {
error: format!(
"Input Dof={}, must be greater than {}",
orig_positions.len(),
use_dof
),
});
}
let mut last_target_distance = None;
for _ in 0..self.num_max_try {
let target_diff =
self.solve_one_loop_with_constraints(&arm, target_pose, constraints_array)?;
let (len_diff, rot_diff) = target_diff_to_len_rot_diff(&target_diff, constraints_array);
if len_diff.norm() < self.allowable_target_distance
&& rot_diff.norm() < self.allowable_target_angle
{
let non_checked_positions = arm.joint_positions();
arm.set_joint_positions(&non_checked_positions)?;
return Ok(());
}
last_target_distance = Some((len_diff, rot_diff));
}
arm.set_joint_positions(&orig_positions)?;
Err(IKError::NotConvergedError {
error: format!(
"iteration has not converged: tried {} timed, diff = {}, {}",
self.num_max_try,
last_target_distance.unwrap().0,
last_target_distance.unwrap().1,
),
})
}
}
fn target_diff_to_len_rot_diff<T>(
target_diff: &DVector<T>,
constraints_array: [bool; 6],
) -> (Vector3<T>, Vector3<T>)
where
T: RealField,
{
let mut len_diff = Vector3::zeros();
let mut index = 0;
for i in 0..3 {
if constraints_array[i] {
len_diff[i] = target_diff[index];
index += 1;
}
}
let mut rot_diff = Vector3::zeros();
for i in 0..3 {
if constraints_array[i + 3] {
rot_diff[i] = target_diff[index];
index += 1;
}
}
(len_diff, rot_diff)
}
impl<T> InverseKinematicsSolver<T> for JacobianIKSolver<T>
where
T: RealField,
{
fn solve(&self, arm: &SerialChain<T>, target_pose: &Isometry3<T>) -> Result<(), IKError> {
self.solve_with_constraints(arm, target_pose, &Constraints::default())
}
fn solve_with_constraints(
&self,
arm: &SerialChain<T>,
target_pose: &Isometry3<T>,
constraints: &Constraints,
) -> Result<(), IKError> {
let orig_positions = arm.joint_positions();
let re = self.solve_with_constraints_internal(arm, target_pose, constraints);
if re.is_err() {
arm.set_joint_positions(&orig_positions)?;
};
re
}
}
impl<T> Default for JacobianIKSolver<T>
where
T: RealField,
{
fn default() -> Self {
Self::new(na::convert(0.001), na::convert(0.005), na::convert(0.5), 10)
}
}
pub fn create_reference_positions_nullspace_function<T: RealField>(
reference_positions: Vec<T>,
weight_vector: Vec<T>,
) -> impl Fn(&[T]) -> Vec<T> {
let dof = reference_positions.len();
assert_eq!(dof, weight_vector.len());
move |positions| {
let mut derivative_vec = vec![na::convert(0.0); dof];
for i in 0..dof {
derivative_vec[i] = weight_vector[i] * (positions[i] - reference_positions[i]);
}
derivative_vec
}
}
#[test]
fn test_nullspace_func() {
let f = create_reference_positions_nullspace_function(vec![0.0, 1.0], vec![0.5, 0.1]);
let pos1 = vec![0.5, 0.5];
let values = f(&pos1);
assert_eq!(values.len(), 2);
assert_eq!(values[0], 0.25);
assert_eq!(values[1], -0.05);
}