use crate::error::KinematicsError;
use crate::linear_algebra::Vector;
use crate::scalar::Numeric;
use crate::spatial::SE2;
#[inline]
fn to_body<T: Numeric>(r: T, b: T, left: T, right: T) -> (T, T) {
(r * (right + left) * T::HALF, r * (right - left) / b)
}
#[inline]
fn to_wheels<T: Numeric>(r: T, b: T, linear: T, angular: T) -> (T, T) {
let half_span = angular * b * T::HALF;
((linear - half_span) / r, (linear + half_span) / r)
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WheelVelocities<T: Numeric = f64> {
left: T,
right: T,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct WheelRotations<T: Numeric = f64> {
left: T,
right: T,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BodyTwist<T: Numeric = f64> {
linear: T,
angular: T,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BodyArc<T: Numeric = f64> {
linear: T,
angular: T,
}
impl<T: Numeric> WheelVelocities<T> {
#[inline]
pub fn new(left: T, right: T) -> Self {
WheelVelocities { left, right }
}
#[inline]
pub fn left(self) -> T {
self.left
}
#[inline]
pub fn right(self) -> T {
self.right
}
#[inline]
pub fn zeros() -> Self {
WheelVelocities {
left: T::ZERO,
right: T::ZERO,
}
}
}
impl<T: Numeric> WheelRotations<T> {
#[inline]
pub fn new(left: T, right: T) -> Self {
WheelRotations { left, right }
}
#[inline]
pub fn left(self) -> T {
self.left
}
#[inline]
pub fn right(self) -> T {
self.right
}
#[inline]
pub fn zeros() -> Self {
WheelRotations {
left: T::ZERO,
right: T::ZERO,
}
}
}
impl<T: Numeric> BodyTwist<T> {
#[inline]
pub fn new(linear: T, angular: T) -> Self {
BodyTwist { linear, angular }
}
#[inline]
pub fn linear(self) -> T {
self.linear
}
#[inline]
pub fn angular(self) -> T {
self.angular
}
#[inline]
pub fn zeros() -> Self {
BodyTwist {
linear: T::ZERO,
angular: T::ZERO,
}
}
#[inline]
pub fn to_tangent(self) -> Vector<3, T> {
Vector::new([self.linear, T::ZERO, self.angular])
}
#[inline]
pub fn project_tangent(xi: Vector<3, T>) -> Self {
let [linear, _, angular] = *xi.as_array();
BodyTwist { linear, angular }
}
#[inline]
pub fn tangent_slip(xi: Vector<3, T>) -> T {
let [_, lateral, _] = *xi.as_array();
lateral
}
#[inline]
pub fn integrate_over(self, dt: T) -> BodyArc<T> {
BodyArc {
linear: self.linear * dt,
angular: self.angular * dt,
}
}
}
impl<T: Numeric> BodyArc<T> {
#[inline]
pub fn new(linear: T, angular: T) -> Self {
BodyArc { linear, angular }
}
#[inline]
pub fn linear(self) -> T {
self.linear
}
#[inline]
pub fn angular(self) -> T {
self.angular
}
#[inline]
pub fn zeros() -> Self {
BodyArc {
linear: T::ZERO,
angular: T::ZERO,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DifferentialDrive<T: Numeric = f64> {
wheel_radius: T,
wheelbase: T,
}
impl<T: Numeric> DifferentialDrive<T> {
pub fn new(wheel_radius: T, wheelbase: T) -> Result<Self, KinematicsError> {
if !wheel_radius.is_finite() || !wheelbase.is_finite() {
return Err(KinematicsError::NonFinite);
}
if wheel_radius <= T::ZERO || wheelbase <= T::ZERO {
return Err(KinematicsError::NonPositiveParameter);
}
Ok(DifferentialDrive {
wheel_radius,
wheelbase,
})
}
#[inline]
pub fn wheel_radius(self) -> T {
self.wheel_radius
}
#[inline]
pub fn wheelbase(self) -> T {
self.wheelbase
}
#[inline]
pub fn forward(self, w: WheelVelocities<T>) -> BodyTwist<T> {
let (linear, angular) = to_body(self.wheel_radius, self.wheelbase, w.left(), w.right());
BodyTwist::new(linear, angular)
}
#[inline]
pub fn inverse(self, c: BodyTwist<T>) -> WheelVelocities<T> {
let (left, right) = to_wheels(self.wheel_radius, self.wheelbase, c.linear(), c.angular());
WheelVelocities::new(left, right)
}
#[inline]
pub fn forward_arc(self, d: WheelRotations<T>) -> BodyArc<T> {
let (linear, angular) = to_body(self.wheel_radius, self.wheelbase, d.left(), d.right());
BodyArc::new(linear, angular)
}
#[inline]
pub fn inverse_arc(self, d: BodyArc<T>) -> WheelRotations<T> {
let (left, right) = to_wheels(self.wheel_radius, self.wheelbase, d.linear(), d.angular());
WheelRotations::new(left, right)
}
#[inline]
pub fn wheel_rotations_from_travel(self, left_m: T, right_m: T) -> WheelRotations<T> {
WheelRotations::new(left_m / self.wheel_radius, right_m / self.wheel_radius)
}
#[inline]
pub fn wheel_travel(self, d: WheelRotations<T>) -> (T, T) {
(d.left() * self.wheel_radius, d.right() * self.wheel_radius)
}
#[inline]
pub fn odometry_step(self, pose: SE2<T>, d: WheelRotations<T>) -> SE2<T> {
crate::kinematics::odometry::integrate(pose, self.forward_arc(d))
}
}