use std::result::Result;
use crate::chains::Chain;
use crate::geometry::Twist;
use crate::jacobian::{Jacobian, JacobianSolver};
use crate::svd_eigen::SvdResult;
use nalgebra::{DMatrix, DVector, Matrix6};
pub type WeightTaskSpace = Matrix6<f64>;
pub type WeightJointSpace = DMatrix<f64>;
type JointQdots = DVector<f64>;
fn transform_vectors(in_vector: JointQdots) -> Vec<f64> {
let mut result = Vec::with_capacity(in_vector.nrows());
for i in 0..in_vector.nrows() {
result.push(in_vector[i]);
}
result
}
#[derive(Clone, Debug)]
pub struct InverseDiffKinematicsSolver {
chain: Chain,
weight_task_space: WeightTaskSpace,
weight_joint_space: WeightJointSpace,
jacobian: Jacobian,
temp_qdot: JointQdots,
svd_result: SvdResult,
lambda: f64,
epsilon: f64,
maxiter: usize,
n_joints: usize,
}
impl InverseDiffKinematicsSolver {
pub fn new(in_chain: Chain) -> Self {
let num_joints = in_chain.get_num_joints();
Self {
chain: in_chain,
weight_task_space: WeightTaskSpace::identity(),
weight_joint_space: WeightJointSpace::identity(num_joints, num_joints),
jacobian: Jacobian::zeros(num_joints),
temp_qdot: JointQdots::zeros(num_joints),
svd_result: SvdResult::new(num_joints),
lambda: 0.0,
epsilon: 1e-300,
maxiter: 150,
n_joints: num_joints,
}
}
pub fn set_weights(&mut self, task_space: WeightTaskSpace, joint_space: WeightJointSpace) {
if joint_space.ncols() != joint_space.nrows() {
panic!(
"Joint space matrix must be square. Got {}x{}",
joint_space.nrows(),
joint_space.ncols()
);
}
if joint_space.ncols() != self.n_joints {
panic!(
"Wrong joint space matrix size. Expected {},\
got {}",
self.n_joints,
joint_space.nrows()
);
}
self.weight_task_space = task_space;
self.weight_joint_space = joint_space;
}
pub fn set_lambda(&mut self, new_lambda: f64) {
self.lambda = new_lambda;
}
pub fn set_convergence(&mut self, new_epsilon: f64, new_maxiter: usize) {
if new_epsilon <= 0.0 {
panic!("Epsilon must be positive! got {}", new_epsilon);
}
self.epsilon = new_epsilon;
self.maxiter = new_maxiter;
}
pub fn solve(&mut self, twist: &Twist, init_angles: &Vec<f64>) -> Result<Vec<f64>, &str> {
let mut num_small_sigmas = 0;
self.jacobian.solve_from_chain(&self.chain, init_angles);
let weight_jac = self.weight_task_space * &self.jacobian * &self.weight_joint_space;
let result = self
.svd_result
.compute(&weight_jac, self.epsilon, self.maxiter);
if result.is_err() {
Err("Unable to compute SVD with damped least squares, max iter reached")
} else {
let u_matrix = &self.svd_result.u_matrix;
let v_matrix = &self.svd_result.v_matrix;
let singular_values = &self.svd_result.s_vector;
let temp_ts = self.weight_task_space * u_matrix.fixed_view::<6, 6>(0, 0);
let temp_js = &self.weight_joint_space * v_matrix;
let sigma_min = if self.n_joints >= 6 {
singular_values[5]
} else {
0.0
};
for col in 0..self.n_joints {
let mut sum = 0.0;
for row in 0..6 {
if col < 6 {
sum += temp_ts[(row, col)] * twist[row]
}
}
let lambda_scaled = if sigma_min < self.epsilon {
(1.0 - (sigma_min / self.epsilon) * (sigma_min / self.epsilon)).sqrt()
* self.lambda
} else {
0.0
};
self.temp_qdot[col] = if singular_values[col].abs() < self.epsilon {
num_small_sigmas += 1;
if col < 6 {
let sigma = singular_values[col];
sum * (sigma / (sigma * sigma + lambda_scaled * lambda_scaled))
} else {
0.0
}
} else {
sum / singular_values[col]
};
}
let qdot_out = self.chain.get_coupling_matrix() * temp_js * &self.temp_qdot;
if num_small_sigmas > (self.n_joints - 6) {
Err("The pseudo inverse converged but it is Singular")
} else {
Ok(transform_vectors(qdot_out))
}
}
}
}
#[cfg(test)]
mod test {
use crate::chains::tests::create_testing_chain;
use crate::forward_diff_kinematics::ForwardDiffKinematicsSolver;
use crate::geometry::{get_twist_error, Twist};
use crate::inverse_diff_kinematics::InverseDiffKinematicsSolver;
#[test]
fn inverse_kinematics_correct() {
let chain = create_testing_chain();
let for_solver = ForwardDiffKinematicsSolver::new(chain.clone());
let angles = vec![0.1, -0.95, 0.57, 0.68, -0.27, 0.39, 0.47];
let twist = Twist::new(-0.423878, -1.01074, 0.123592, -2.28297, 0.209941, 1.80897);
let mut inv_solver = InverseDiffKinematicsSolver::new(chain.clone());
let inv_result = inv_solver.solve(&twist, &angles).unwrap();
assert_eq!(7, inv_result.len());
let forward_result = for_solver.solve(&angles, &inv_result);
assert!(get_twist_error(forward_result, twist) < 0.0001);
}
}