use crate::velocity_profile::{
generate_cosine_velocity_profile, generate_linear_velocity_profile, generate_s_curve_profile,
VelocityProfileMapping,
};
use crate::PoseGenerator;
use nalgebra::Isometry3;
use s_curve::SCurveConstraints;
use std::time::Duration;
#[derive(Debug)]
pub enum VelocityProfile {
Linear {
total_duration: Duration,
},
Cosine {
total_duration: Duration,
},
SCurve {
start_pose: Isometry3<f64>,
constraints: SCurveConstraints,
},
}
#[derive(Debug)]
pub struct CartesianTrajectoryOutput {
pub pose: Isometry3<f64>,
pub finished: bool,
}
pub struct CartesianTrajectory {
function: Box<dyn FnMut(&Isometry3<f64>, Duration) -> CartesianTrajectoryOutput>,
total_duration: Duration,
}
impl CartesianTrajectory {
pub fn new(mut pose_generator: PoseGenerator, velocity_profile: VelocityProfile) -> Self {
let velocity_profile_mapping = match velocity_profile {
VelocityProfile::Linear { total_duration } => {
generate_linear_velocity_profile(total_duration)
}
VelocityProfile::Cosine { total_duration } => {
generate_cosine_velocity_profile(total_duration)
}
VelocityProfile::SCurve {
start_pose,
constraints,
} => {
let length = pose_generator.get_approximate_length(&start_pose, 10000);
generate_s_curve_profile(length, constraints)
}
};
Self::with_custom_velocity_profile_mapping(pose_generator, velocity_profile_mapping)
}
pub fn with_custom_velocity_profile_mapping(
mut pose_generator: PoseGenerator,
mut velocity_profile_mapping: VelocityProfileMapping,
) -> Self {
let total_duration = velocity_profile_mapping.get_total_duration();
let cartesian_trajectory =
move |start_pose: &Isometry3<f64>, time: Duration| -> CartesianTrajectoryOutput {
let velocity_output = velocity_profile_mapping.get(time);
let pose = pose_generator.get_pose(start_pose, velocity_output.progress);
CartesianTrajectoryOutput {
pose,
finished: velocity_output.finished,
}
};
CartesianTrajectory {
function: Box::new(cartesian_trajectory),
total_duration,
}
}
pub fn get_pose(
&mut self,
start_pose: &Isometry3<f64>,
time: Duration,
) -> CartesianTrajectoryOutput {
(self.function)(start_pose, time)
}
pub fn get_total_duration(&self) -> Duration {
self.total_duration
}
}
impl CartesianTrajectory {}
#[cfg(test)]
mod tests {
use crate::cartesian_trajectory::CartesianTrajectory;
use crate::cartesian_trajectory::VelocityProfile::{Linear, SCurve};
use crate::pose_generators::generate_absolute_motion;
use nalgebra::Isometry3;
use s_curve::SCurveConstraints;
use std::time::Duration;
#[test]
fn can_create_cartesian_trajectory() {
let start_pose = Isometry3::identity();
let pose_generator = generate_absolute_motion(Isometry3::translation(1., 1., 0.));
let mut trajectory = CartesianTrajectory::new(
pose_generator,
Linear {
total_duration: Duration::from_secs(8),
},
);
for i in 0..=100 {
let time = Duration::from_secs_f64(i as f64 / 10.);
let output = trajectory.get_pose(&start_pose, time);
let progress = i as f64 / 80.;
println!("{:?}", output);
assert!(f64::abs(output.pose.translation.x - progress) < 1e-7);
assert!(f64::abs(output.pose.translation.y - progress) < 1e-7);
assert!(f64::abs(output.pose.translation.z) < 1e-7);
if time.as_secs() < 8 {
assert!(!output.finished)
} else {
assert!(output.finished)
}
}
}
#[test]
fn can_create_cartesian_trajectory_with_scurve() {
let start_pose = Isometry3::translation(2., 2., 0.);
let pose_generator = generate_absolute_motion(Isometry3::translation(1., 1., 0.));
let mut trajectory = CartesianTrajectory::new(
pose_generator,
SCurve {
start_pose: start_pose.clone(),
constraints: SCurveConstraints {
max_jerk: 5.,
max_acceleration: 5.,
max_velocity: 5.,
},
},
);
for &i in [0., 0.5, 1.0].iter() {
let time = trajectory.total_duration.mul_f64(i);
let output = trajectory.get_pose(&start_pose, time);
println!("{:?}", output);
assert!(f64::abs(output.pose.translation.x + i - 2.) < 1e-7);
assert!(f64::abs(output.pose.translation.y + i - 2.) < 1e-7);
if i < 1. {
assert!(!output.finished);
} else {
assert!(output.finished);
}
}
}
}