use crate::util::si::{QLength, QTime};
use libm::sqrt;
#[derive(Debug, Clone, Copy)]
pub struct MotionState {
pub time: QTime,
pub position: QLength,
pub velocity: f64,
pub acceleration: f64,
}
#[derive(Debug, Clone, Copy)]
pub struct TrapezoidalConstraints {
pub max_velocity: f64,
pub max_acceleration: f64,
}
impl TrapezoidalConstraints {
pub fn new() -> Self {
Self {
max_velocity: 0.,
max_acceleration: 0.,
}
}
pub fn set_gains(mut self, max_vel: f64, max_acc: f64) -> Self {
self.max_velocity = max_vel;
self.max_acceleration = max_acc;
self
}
pub fn generate_profile(&self, total_distance: QLength) -> Vec<MotionState> {
let distance = total_distance.as_meters();
let max_v = self.max_velocity;
let max_a = self.max_acceleration;
let t_accel = max_v / max_a;
let d_accel = 0.5 * max_a * t_accel.powi(2); let d_cruise = distance - 2.0 * d_accel;
if d_cruise < 0.0 {
let v_peak_tri = sqrt(distance * max_a);
let t_accel_only = v_peak_tri / max_a;
let t_total = 2.0 * t_accel_only;
let d_half = 0.5 * distance;
let samples = 100;
let dt = t_total / (samples as f64 - 1.0);
let mut states = Vec::with_capacity(samples);
for i in 0..samples {
let t = dt * i as f64;
let (velocity, acceleration, position) = if t <= t_accel_only {
let v = max_a * t;
let a = max_a;
let p = 0.5 * max_a * t.powi(2);
(v, a, p)
} else {
let t_dec = t - t_accel_only;
let v = v_peak_tri - max_a * t_dec;
let a = -max_a;
let p_dec = v_peak_tri * t_dec - 0.5 * max_a * t_dec.powi(2);
(v, a, d_half + p_dec)
};
states.push(MotionState {
time: QTime::from_sec(t),
position: QLength::from_meters(position),
velocity,
acceleration,
});
}
states
} else {
let t_cruise = d_cruise / max_v;
let t_total = 2.0 * t_accel + t_cruise;
let samples = 100; let dt = t_total / (samples as f64 - 1.0);
let mut states = Vec::with_capacity(samples);
for i in 0..samples {
let t = dt * i as f64;
let (velocity, acceleration, position) = if t < t_accel {
let v = max_a * t;
let a = max_a;
let p = 0.5 * max_a * t.powi(2);
(v, a, p)
} else if t < t_accel + t_cruise {
let t_cruise_elapsed = t - t_accel;
let v = max_v;
let a = 0.0;
let p = d_accel + max_v * t_cruise_elapsed;
(v, a, p)
} else {
let t_dec_elapsed = t - (t_accel + t_cruise);
let v = max_v - max_a * t_dec_elapsed;
let a = -max_a;
let p_accel_cruise = d_accel + max_v * t_cruise;
let p_dec = max_v * t_dec_elapsed - 0.5 * max_a * t_dec_elapsed.powi(2);
(v, a, p_accel_cruise + p_dec)
};
states.push(MotionState {
time: QTime::from_sec(t),
position: QLength::from_meters(position),
velocity,
acceleration,
});
}
states
}
}
}