use crate::error::ControlError;
use crate::linear_algebra::{Matrix3D, Vector3D};
use crate::scalar::Numeric;
use crate::spatial::SO3;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GeometricAttitudeController<T: Numeric = f64> {
attitude_gain: T,
rate_gain: T,
inertia: Matrix3D<T>,
}
impl<T: Numeric> GeometricAttitudeController<T> {
pub fn new(attitude_gain: T, rate_gain: T, inertia: Matrix3D<T>) -> Result<Self, ControlError> {
if !attitude_gain.is_finite() || !rate_gain.is_finite() || !inertia.is_finite() {
return Err(ControlError::NonFinite);
}
if attitude_gain <= T::ZERO || rate_gain <= T::ZERO {
return Err(ControlError::NonPositiveGain);
}
if !inertia.is_symmetric() {
return Err(ControlError::NotSymmetricInertia);
}
if inertia.cholesky().is_err() {
return Err(ControlError::NonPositiveInertia);
}
Ok(Self {
attitude_gain,
rate_gain,
inertia,
})
}
pub fn attitude_error(attitude: SO3<T>, desired_attitude: SO3<T>) -> Vector3D<T> {
let relative = attitude.to_matrix().transpose() * desired_attitude.to_matrix();
SO3::vee(relative.transpose() - relative).scale(T::HALF)
}
pub fn torque(
&self,
attitude: SO3<T>,
body_rate: Vector3D<T>,
desired_attitude: SO3<T>,
desired_body_rate: Vector3D<T>,
desired_body_rate_derivative: Vector3D<T>,
) -> Vector3D<T> {
let relative = attitude.to_matrix().transpose() * desired_attitude.to_matrix();
let attitude_error = SO3::vee(relative.transpose() - relative).scale(T::HALF);
let carried_rate = relative * desired_body_rate;
let carried_rate_change = relative * desired_body_rate_derivative;
let rate_error = body_rate - carried_rate;
let spin_resistance = body_rate.cross(self.inertia * body_rate);
let following = self.inertia * (body_rate.cross(carried_rate) - carried_rate_change);
attitude_error.scale(-self.attitude_gain) - rate_error.scale(self.rate_gain)
+ spin_resistance
- following
}
#[inline]
#[must_use]
pub fn attitude_gain(&self) -> T {
self.attitude_gain
}
#[inline]
#[must_use]
pub fn rate_gain(&self) -> T {
self.rate_gain
}
#[inline]
pub fn inertia(&self) -> Matrix3D<T> {
self.inertia
}
}