1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// Copyright (c) 2021 Marco Boneberger
// Licensed under the EUPL-1.2-or-later
#![deny(missing_docs)]
//! This library generate smooth trajectories in cartesian space in a functional style.
//!
//! # Overview
//! The idea is to specify a function that takes a start pose and a progress between 0 and 1 and spits out a pose.
//! This function is the [`PoseGenerator`](`PoseGenerator`). It is possible to directly define the function
//! or to combine a [`PositionGenerator`](`PositionGenerator`) and an [`OrientationGenerator`](`OrientationGenerator`).
//! Further, there are predefined PoseGenerators available. See [`pose_generators`](`crate::pose_generators`).
//!
//! You should make sure that your own PoseGenerators have a constant derivative, that means that all if you would sample
//! 1000 equidistant points, the length between neighboring points should be the same. Otherwise, some parts of
//! your trajectory will end up faster than other ones.
//!
//! PoseGenerators can be multiplied to generate a new PoseGenerator. Multiplication means that the
//! homogenous matrices of both pose generators will be multiplied together for a certain progress.
//!
//! Further, it is possible to combine PoseGenerators with the [`append`](`PoseGenerator::append`) method,
//! which will concatenate both PoseGenerators. Be aware that concatenating PoseGenerators can result in unsmooth
//! trajectories. For example, if you append a circle PoseGenerator to a linear PoseGenerator, there will be an infinite
//! jerk at the transition as the acceleration suddenly changes.
//!
//! With the final PoseGenerator it is now possible to add a VelocityProfile to it, which turns
//! the PoseGenerator into a [`CartesianTrajectory`](`crate::cartesian_trajectory::CartesianTrajectory`) which can be queried
//! with the [`get_pose`](`crate::cartesian_trajectory::CartesianTrajectory::get_pose`) method
//! with a start pose and a Duration.
//!
//! The following images shows how this library can be used to create a complex trajectory
//! ![](https://i.imgur.com/UkqynLa.png)
//!
use nalgebra::{Isometry3, UnitQuaternion, Vector3};

/// Struct that wraps a function that generates a pose
pub struct PoseGenerator(Box<dyn FnMut(&Isometry3<f64>, f64) -> Isometry3<f64>>);

/// Struct that wraps a function that generates a position
pub struct PositionGenerator(Box<dyn FnMut(&Vector3<f64>, f64) -> Vector3<f64>>);

/// Struct that wraps a function that generates an orientation
pub struct OrientationGenerator(Box<dyn FnMut(&UnitQuaternion<f64>, f64) -> UnitQuaternion<f64>>);

impl PositionGenerator {
    /// generates a new PositionGenerator by defining a function that takes
    /// an initial position and a progress between 0 and 1 and returns a position.
    pub fn new(
        position_generator_function: Box<dyn FnMut(&Vector3<f64>, f64) -> Vector3<f64>>,
    ) -> Self {
        PositionGenerator(position_generator_function)
    }
    /// Evaluates the function of the PositionGenerator
    /// # Arguments
    /// * `start_position` - the position at the start time of the PositionGenerator
    /// * `progress` - a progress from 0, indicating the start, and 1 , indicating the end of the
    /// trajectory.
    pub fn get_position(&mut self, start_position: &Vector3<f64>, progress: f64) -> Vector3<f64> {
        (self.0)(start_position, progress)
    }
    /// creates a position generator that does not move and therefore always returns the start position.
    pub fn constant_position_generator() -> Self {
        let position_generator = move |start_position: &Vector3<f64>,
                                       _progress: f64|
              -> Vector3<f64> { start_position.clone_owned() };
        PositionGenerator(Box::new(position_generator))
    }
}

impl OrientationGenerator {
    /// generates a new OrientationGenerator by defining a function that takes
    /// an initial orientation and a progress between 0 and 1 and returns an orientation.
    pub fn new(
        orientation_generator_function: Box<
            dyn FnMut(&UnitQuaternion<f64>, f64) -> UnitQuaternion<f64>,
        >,
    ) -> Self {
        OrientationGenerator(orientation_generator_function)
    }
    /// Evaluates the function of the OrientationGenerator
    /// # Arguments
    /// * `start_orientation` - the orientation at the start time of the OrientationGenerator
    /// * `progress` - a progress from 0, indicating the start, and 1 , indicating the end of the
    /// trajectory.
    pub fn get_orientation(
        &mut self,
        start_orientation: &UnitQuaternion<f64>,
        progress: f64,
    ) -> UnitQuaternion<f64> {
        (self.0)(start_orientation, progress)
    }
    /// creates an orientation generator that does not rotate and therefore always returns the start orientation.
    pub fn constant_orientation_generator() -> Self {
        let orientation_generator = move |start_orientation: &UnitQuaternion<f64>,
                                          _progress: f64|
              -> UnitQuaternion<f64> { *start_orientation };
        OrientationGenerator(Box::new(orientation_generator))
    }
}

impl PoseGenerator {
    /// generates a new PoseGenerator by defining a function that takes
    /// an initial position and a progress between 0 and 1 and returns a position.
    pub fn new(
        pose_generator_function: Box<dyn FnMut(&Isometry3<f64>, f64) -> Isometry3<f64>>,
    ) -> Self {
        PoseGenerator(pose_generator_function)
    }
    /// Generates a PoseGenerator from a position and an orientation generator.
    pub fn from_parts(
        mut position_generator: PositionGenerator,
        mut orientation_generator: OrientationGenerator,
    ) -> Self {
        let pose_generator =
            move |initial_pose: &Isometry3<f64>, progress: f64| -> Isometry3<f64> {
                let position =
                    position_generator.get_position(&initial_pose.translation.vector, progress);
                let orientation =
                    orientation_generator.get_orientation(&initial_pose.rotation, progress);
                Isometry3::from_parts(position.into(), orientation)
            };
        PoseGenerator(Box::new(pose_generator))
    }
    /// Evaluates the function of the PoseGenerator
    /// # Arguments
    /// * `start_pose` - the pose at the start time of the PoseGenerator
    /// * `progress` - a progress from 0, indicating the start, and 1 , indicating the end of the
    /// trajectory.
    pub fn get_pose(&mut self, start: &Isometry3<f64>, progress: f64) -> Isometry3<f64> {
        (self.0)(start, progress)
    }
    /// returns the approximate length of the trajectory in meter. The rotation is ignored.
    pub fn get_approximate_length(
        &mut self,
        start_pose: &Isometry3<f64>,
        sample_size: usize,
    ) -> f64 {
        (0..sample_size)
            .map(|x| {
                (
                    x as f64 / sample_size as f64,
                    (x as f64 + 1.) / sample_size as f64,
                )
            })
            .map(|(t1, t2)| {
                (self.get_pose(&start_pose, t1).translation.inverse()
                    * self.get_pose(&start_pose, t2).translation)
                    .vector
                    .norm()
            })
            .sum()
    }
    /// concatenates two pose generators.
    /// Be aware that concatenating PoseGenerators can result in unsmooth
    /// trajectories. For example, if you append a circle PoseGenerator to a linear PoseGenerator,
    /// there will be an infinite jerk at the transition as the acceleration suddenly changes.
    /// # Arguments
    /// * `start_pose` - a rough estimation of the start pose of the trajectory.
    /// * `other_generator` - the pose generator that should be appended
    pub fn append(
        mut self,
        start_pose: &Isometry3<f64>,
        mut other_generator: PoseGenerator,
    ) -> PoseGenerator {
        let length_self = self.get_approximate_length(start_pose, 10000);
        let end_pose_self = self.get_pose(start_pose, 1.);
        let length_other = other_generator.get_approximate_length(&end_pose_self, 10000);
        let length_split = length_self / (length_self + length_other);

        let pose_generator =
            move |initial_pose: &Isometry3<f64>, progress: f64| -> Isometry3<f64> {
                if progress < length_split {
                    self.get_pose(initial_pose, progress / length_split)
                } else {
                    other_generator.get_pose(
                        &end_pose_self,
                        (progress - length_split) / (1. - length_split),
                    )
                }
            };
        PoseGenerator(Box::new(pose_generator))
    }
}

impl std::ops::Mul for PoseGenerator {
    type Output = PoseGenerator;

    fn mul(mut self, mut rhs: Self) -> Self::Output {
        let pose_generator =
            move |initial_pose: &Isometry3<f64>, progress: f64| -> Isometry3<f64> {
                (self.0)(initial_pose, progress) * (rhs.0)(initial_pose, progress)
            };
        PoseGenerator(Box::new(pose_generator))
    }
}
mod cartesian_trajectory;
pub mod pose_generators;
mod velocity_profile;
pub use crate::cartesian_trajectory::{
    CartesianTrajectory, CartesianTrajectoryOutput, VelocityProfile,
};
pub use crate::pose_generators::{
    generate_absolute_motion, generate_circle_motion, generate_relative_motion, RelativeMotionFrame,
};
pub use crate::velocity_profile::{
    generate_cosine_velocity_profile, generate_linear_velocity_profile, generate_s_curve_profile,
    VelocityProfileMapping, VelocityProfileOutput,
};
pub use s_curve;