use crate::*;
use stulta::AbsoluteValue;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MotionProfilePiece {
BeforeStart,
InitialAcceleration,
ConstantVelocity,
EndAcceleration,
Complete,
}
#[derive(Clone, Debug, PartialEq)]
pub struct MotionProfile<C: GenericCommand> {
start_pos: C::Position,
start_vel: C::Velocity,
t1: Time,
t2: Time,
t3: Time,
max_acc: C::Acceleration,
end_command: C,
}
impl<C: GenericCommand> Chronology<C> for MotionProfile<C> {
fn get(&self, time: Time) -> Option<Datum<C>> {
let mode = match self.get_mode(time) {
Some(value) => value,
None => {
return None;
}
};
let command = match mode {
PositionDerivative::Position => C::from(
self.get_position(time)
.expect("If mode is Position, this should be Some."),
),
PositionDerivative::Velocity => C::from(
self.get_velocity(time)
.expect("If mode is Velocity, this should be Some."),
),
PositionDerivative::Acceleration => C::from(
self.get_acceleration(time)
.expect("If mode is Acceleration, this should be Some."),
),
};
Some(Datum::new(time, command))
}
}
impl<C: GenericCommand> MotionProfile<C> {
pub fn new(
start_state: C::CorrespondingState,
end_state: C::CorrespondingState,
max_vel: C::Velocity,
max_acc: C::Acceleration,
) -> Self {
let sign = Dimensionless::new(
if end_state.generic_position() < start_state.generic_position() {
-1.0
} else {
1.0
},
);
let max_vel = max_vel.rrtk_abs() * sign;
let max_acc = max_acc.rrtk_abs() * sign;
let d_t1_vel = max_vel - start_state.generic_velocity();
let t1 = d_t1_vel / max_acc;
assert!(t1.into_inner() >= 0.0);
let d_t1_pos = (start_state.generic_velocity() + max_vel) / Dimensionless::new(2.0) * t1;
let d_t3_vel = end_state.generic_velocity() - max_vel;
let d_t3 = d_t3_vel / -max_acc;
assert!(d_t3.into_inner() >= 0.0);
let d_t3_pos = (max_vel + end_state.generic_velocity()) / Dimensionless::new(2.0) * d_t3;
let d_t2_pos =
(end_state.generic_position() - start_state.generic_position()) - (d_t1_pos + d_t3_pos);
let d_t2 = d_t2_pos / max_vel;
assert!(d_t2.into_inner() >= 0.0);
let t2 = t1 + d_t2;
let t3 = t2 + d_t3;
let end_command = C::from(end_state);
Self {
start_pos: start_state.generic_position(),
start_vel: start_state.generic_velocity(),
t1: Time::try_from(t1).expect(
"t1 must always be in seconds in max_vel and max_acc have correct dimensions",
),
t2: Time::try_from(t2).expect(
"t2 must always be in seconds in max_vel and max_acc have correct dimensions",
),
t3: Time::try_from(t3).expect(
"t3 must always be in seconds in max_vel and max_acc have correct dimensions",
),
max_acc,
end_command,
}
}
pub fn get_mode(&self, t: Time) -> Option<PositionDerivative> {
if t < Time::default() {
None
} else if t < self.t1 {
return Some(PositionDerivative::Acceleration);
} else if t < self.t2 {
return Some(PositionDerivative::Velocity);
} else if t < self.t3 {
return Some(PositionDerivative::Acceleration);
} else {
return Some(self.end_command.into());
}
}
pub fn get_piece(&self, t: Time) -> MotionProfilePiece {
if t < Time::default() {
MotionProfilePiece::BeforeStart
} else if t < self.t1 {
return MotionProfilePiece::InitialAcceleration;
} else if t < self.t2 {
return MotionProfilePiece::ConstantVelocity;
} else if t < self.t3 {
return MotionProfilePiece::EndAcceleration;
} else {
return MotionProfilePiece::Complete;
}
}
pub fn get_acceleration(&self, t: Time) -> Option<C::Acceleration> {
if t < Time::default() {
None
} else if t < self.t1 {
return Some(self.max_acc);
} else if t < self.t2 {
return Some(C::Acceleration::default());
} else if t < self.t3 {
return Some(-self.max_acc);
} else {
return Some(self.end_command.generic_get_acceleration());
}
}
pub fn get_velocity(&self, t: Time) -> Option<C::Velocity> {
if t < Time::default() {
None
} else if t < self.t1 {
return Some(self.max_acc * t + self.start_vel);
} else if t < self.t2 {
return Some(self.max_acc * self.t1 + self.start_vel);
} else if t < self.t3 {
return Some(self.max_acc * (self.t1 + self.t2 - t) + self.start_vel);
} else {
return self.end_command.generic_get_velocity();
}
}
pub fn get_position(&self, t: Time) -> Option<C::Position> {
if t < Time::default() {
None
} else if t < self.t1 {
return Some(
self.max_acc * t * t * Dimensionless::new(0.5)
+ self.start_vel * t
+ self.start_pos,
);
} else if t < self.t2 {
return Some(
self.max_acc * (self.t1.as_seconds() * (-self.t1 / DimensionlessInteger(2) + t))
+ self.start_vel * t
+ self.start_pos,
);
} else if t < self.t3 {
return Some(
self.max_acc
* (self.t1.as_seconds() * (-self.t1 / DimensionlessInteger(2) + self.t2))
- self.max_acc
* Dimensionless::new(0.5)
* ((t - self.t2).as_seconds()
* (t - DimensionlessInteger(2) * self.t1 - self.t2))
+ self.start_vel * t
+ self.start_pos,
);
} else {
return self.end_command.generic_get_position();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn motion_profile_new_1() {
let motion_profile = MotionProfile::<LinearCommand>::new(
LinearState::new(
Millimeter::new(0.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
LinearState::new(
Millimeter::new(3.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
MillimeterPerSecond::new(0.1),
MillimeterPerSecondSquared::new(0.01),
);
assert_eq!(motion_profile.t1, Time::from_nanoseconds(10_000_000_000));
assert_eq!(
motion_profile.t2 / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(30_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(motion_profile.t3, Time::from_nanoseconds(40_000_000_000));
assert_eq!(
motion_profile.max_acc,
MillimeterPerSecondSquared::new(0.01)
);
}
#[test]
fn motion_profile_new_2() {
let motion_profile = MotionProfile::<LinearCommand>::new(
LinearState::new(
Millimeter::new(1.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
LinearState::new(
Millimeter::new(3.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
MillimeterPerSecond::new(0.1),
MillimeterPerSecondSquared::new(0.01),
);
assert_eq!(motion_profile.t1, Time::from_nanoseconds(10_000_000_000));
assert_eq!(motion_profile.t2, Time::from_nanoseconds(20_000_000_000));
assert_eq!(
motion_profile.t3 / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(30_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(
motion_profile.max_acc,
MillimeterPerSecondSquared::new(0.01)
);
}
#[test]
fn motion_profile_new_3() {
let motion_profile = MotionProfile::<LinearCommand>::new(
LinearState::new(
Millimeter::new(0.0),
MillimeterPerSecond::new(0.1),
MillimeterPerSecondSquared::new(0.0),
),
LinearState::new(
Millimeter::new(3.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
MillimeterPerSecond::new(0.1),
MillimeterPerSecondSquared::new(0.01),
);
assert_eq!(motion_profile.t1, Time::from_nanoseconds(0));
assert_eq!(
(motion_profile.t2 + Time::from_nanoseconds(1000)) / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(25_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(
motion_profile.t3 / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(35_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(
motion_profile.max_acc,
MillimeterPerSecondSquared::new(0.01)
);
}
#[test]
fn motion_profile_new_4() {
let motion_profile = MotionProfile::<LinearCommand>::new(
LinearState::new(
Millimeter::new(0.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.01),
),
LinearState::new(
Millimeter::new(3.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
MillimeterPerSecond::new(0.1),
MillimeterPerSecondSquared::new(0.01),
);
assert_eq!(motion_profile.t1, Time::from_nanoseconds(10_000_000_000));
assert_eq!(
motion_profile.t2 / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(30_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(motion_profile.t3, Time::from_nanoseconds(40_000_000_000));
assert_eq!(
motion_profile.max_acc,
MillimeterPerSecondSquared::new(0.01)
);
}
#[test]
fn motion_profile_new_5() {
let motion_profile = MotionProfile::<LinearCommand>::new(
LinearState::new(
Millimeter::new(0.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
LinearState::new(
Millimeter::new(6.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
MillimeterPerSecond::new(0.2),
MillimeterPerSecondSquared::new(0.01),
);
assert_eq!(motion_profile.t1, Time::from_nanoseconds(20_000_000_000));
assert_eq!(
motion_profile.t2 / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(30_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(
(motion_profile.t3 + Time::from_nanoseconds(10000)) / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(50_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(
motion_profile.max_acc,
MillimeterPerSecondSquared::new(0.01)
);
}
#[test]
fn motion_profile_new_6() {
let motion_profile = MotionProfile::<LinearCommand>::new(
LinearState::new(
Millimeter::new(0.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
LinearState::new(
Millimeter::new(3.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
MillimeterPerSecond::new(0.1),
MillimeterPerSecondSquared::new(0.02),
);
assert_eq!(motion_profile.t1, Time::from_nanoseconds(5_000_000_000));
assert_eq!(
motion_profile.t2 / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(30_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(
motion_profile.t3 / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(35_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(
motion_profile.max_acc,
MillimeterPerSecondSquared::new(0.02)
);
}
#[test]
fn motion_profile_new_7() {
let motion_profile = MotionProfile::<LinearCommand>::new(
LinearState::new(
Millimeter::new(0.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
LinearState::new(
Millimeter::new(-3.0),
MillimeterPerSecond::new(0.0),
MillimeterPerSecondSquared::new(0.0),
),
MillimeterPerSecond::new(0.1),
MillimeterPerSecondSquared::new(0.01),
);
assert_eq!(motion_profile.t1, Time::from_nanoseconds(10_000_000_000));
assert_eq!(
motion_profile.t2 / DimensionlessInteger(1_000_000),
Time::from_nanoseconds(30_000_000_000) / DimensionlessInteger(1_000_000)
);
assert_eq!(motion_profile.t3, Time::from_nanoseconds(40_000_000_000));
assert_eq!(
motion_profile.max_acc,
MillimeterPerSecondSquared::new(-0.01)
);
}
}