use crate::motion::trajectory::TrajectoryPoint;
use crate::odom::pose::Pose;
use crate::util::si::QAngle;
#[derive(Debug, Clone, Copy)]
pub struct RamseteController {
b: f64,
zeta: f64,
epsilon: f64,
}
#[derive(Debug, Clone, Copy)]
pub struct RamseteReference {
pub pose: Pose,
pub linear_velocity: f64,
pub angular_velocity: f64,
}
impl RamseteReference {
#[inline]
pub fn new(pose: Pose, linear_velocity: f64, angular_velocity: f64) -> Self {
Self {
pose,
linear_velocity,
angular_velocity,
}
}
}
impl From<TrajectoryPoint> for RamseteReference {
fn from(point: TrajectoryPoint) -> Self {
Self {
pose: point.pose,
linear_velocity: point.linear_velocity,
angular_velocity: point.angular_velocity,
}
}
}
impl RamseteController {
#[inline]
pub fn new() -> Self {
Self {
b: 0.,
zeta: 0.,
epsilon: 1e-6,
}
}
pub fn set(self, b: f64, zeta: f64) -> Self {
Self {
b,
zeta,
epsilon: 1e-6,
}
}
#[inline]
pub fn with_epsilon(mut self, epsilon: f64) -> Self {
self.epsilon = epsilon;
self
}
pub fn calculate(&self, current: Pose, reference: RamseteReference) -> (f64, f64) {
let coords = current.position();
let refer = reference.pose.position();
let dx = refer.x - coords.x;
let dy = refer.y - coords.y;
let heading = current.heading();
let cos_h = heading.cos();
let sin_h = heading.sin();
let e_x = cos_h * dx + sin_h * dy;
let e_y = -sin_h * dx + cos_h * dy;
let e_theta = (reference.pose.heading() - heading).remainder(QAngle::TAU);
let v_d = reference.linear_velocity;
let w_d = reference.angular_velocity;
let k = 2.0 * self.zeta * libm::sqrt(w_d * w_d + self.b * v_d * v_d);
let sinc = sinc(e_theta.as_radians(), self.epsilon);
let v = v_d * e_theta.cos() + k * e_x;
let w = w_d + k * e_theta.as_radians() + self.b * v_d * sinc * e_y;
(v, w)
}
}
fn sinc(theta: f64, epsilon: f64) -> f64 {
if libm::fabs(theta) < epsilon {
1.0
} else {
libm::sin(theta) / theta
}
}