arcos-kdl 0.3.3

ARCOS-Lab Kinematics and Dynamics Library
Documentation
// Copyright (c) 2019 Autonomous Robots and Cognitive Systems Laboratory
// Author: Daniel Garcia-Vaglio <degv364@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

use crate::chains::Chain;
use crate::geometry::Frame;
use crate::joint::JointType;

/// Solver for forward kinematics. Has the capacity for calculating the pose
/// of any joint from the current joint confiuration.
#[derive(Clone, Debug)]
pub struct ForwardKinematicsSolver {
    chain: Chain,
}

impl ForwardKinematicsSolver {
    /// Create a default empty solver
    pub fn default() -> Self {
        Self {
            chain: Chain::default(),
        }
    }

    /// Create a direct kinematics solver from a chain
    pub fn new(in_chain: Chain) -> Self {
        Self { chain: in_chain }
    }

    /// Solver for forward kinematics at an arbitrary segment
    ///
    /// `chain`: kinematic chain to use by the solver\
    /// `angles`: vector with each joint state.\
    /// `segment_index`: segment at which the pose should be computed\
    ///
    /// Calculates the local pose of each segments and they are recirsively
    /// multiplied to get the global pose at the specified segment.
    pub fn solve_at_segment(&self, angles: &Vec<f64>, segment_index: usize) -> Frame {
        let num_joints = self.chain.get_num_joints();
        let num_segments = self.chain.get_num_segments();
        if num_joints != angles.len() {
            panic!(
                "Got {} angles, but the chain has {} joints",
                angles.len(),
                num_joints
            );
        }

        if segment_index >= num_segments {
            panic!(
                "Asked for pose at segment {}, but the chain has {} segments",
                segment_index, num_segments
            );
        }
        let mut counter = 0;
        let mut pose = Frame::identity();
        for i in 0..segment_index + 1 {
            let segment = self.chain.get_segment(i);
            pose *= segment.pose(angles[counter]);
            if segment.get_joint_type() != JointType::NoJoint {
                counter += 1;
            }
        }
        let result = pose;
        result
    }

    /// Solver for forward kinematics at the end-effector\
    /// The same as the solver for arbitrary segments but defaults to the last one
    pub fn solve(&self, angles: &Vec<f64>) -> Frame {
        self.solve_at_segment(angles, self.chain.get_num_segments() - 1)
    }
}
#[cfg(test)]
mod tests {
    use crate::chains::tests::create_testing_chain;
    use crate::forward_kinematics::ForwardKinematicsSolver;
    use crate::geometry::{get_frame_error, Frame};
    use nalgebra::geometry::{Isometry3, Translation3, UnitQuaternion};

    #[test]
    fn base_pose_correct() {
        let chain = create_testing_chain();
        let angles = vec![0.1, -0.95, 0.57, 0.68, -0.27, 0.39, 0.47];
        let solver = ForwardKinematicsSolver::new(chain);
        let base_pose = solver.solve_at_segment(&angles, 0);

        assert_eq!(
            base_pose,
            Isometry3::from_parts(
                Translation3::new(0.1, -0.2, 0.3),
                UnitQuaternion::from_euler_angles(-0.1, -0.2, 0.3)
            )
            .to_homogeneous()
        );
    }

    #[test]
    fn end_pose_correct() {
        let chain = create_testing_chain();
        let angles = vec![0.1, -0.95, 0.57, 0.68, -0.27, 0.39, 0.47];
        let solver = ForwardKinematicsSolver::new(chain);
        let end_pose = solver.solve(&angles);
        let error = get_frame_error(
            end_pose,
            Frame::new(
                0.464207, -0.287552, -0.83775, -1.6869, 0.531446, 0.847084, 0.0037244, 0.183458,
                0.708574, -0.446948, 0.546041, 1.46649, 0.0, 0.0, 0.0, 1.0,
            ),
        );
        assert!(error < 0.0001);
    }

    #[test]
    fn middle_pose_correct() {
        let chain = create_testing_chain();
        let angles = vec![0.1, -0.95, 0.57, 0.68, -0.27, 0.39, 0.47];
        let solver = ForwardKinematicsSolver::new(chain);
        let middle_pose = solver.solve_at_segment(&angles, 4);
        let error = get_frame_error(
            middle_pose,
            Frame::new(
                -0.721694, -0.280953, 0.632632, -0.398326, -0.418169, -0.551359, -0.721899,
                0.0611589, 0.551627, -0.785537, 0.280427, 0.90368, 0.0, 0.0, 0.0, 1.0,
            ),
        );
        assert!(error < 0.00001);
    }
}