#![allow(unused)]
use crate::core::matrix::{matmul, Matrix};
use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone, Copy)]
pub struct PreFilter<S: ControlScalar, const N: usize, const I: usize> {
pub n_x: Matrix<S, N, I>,
pub n_u: Matrix<S, I, I>,
}
impl<S: ControlScalar, const N: usize, const I: usize> PreFilter<S, N, I> {
pub fn new(n_x: Matrix<S, N, I>, n_u: Matrix<S, I, I>) -> Self {
Self { n_x, n_u }
}
}
pub struct ServoController<S: ControlScalar, const N: usize, const I: usize> {
pub k_gain: Matrix<S, I, N>,
pub pre_filter: PreFilter<S, N, I>,
pub x_hat: Matrix<S, N, 1>,
}
impl<S: ControlScalar, const N: usize, const I: usize> ServoController<S, N, I> {
pub fn new(k_gain: Matrix<S, I, N>, pre_filter: PreFilter<S, N, I>) -> Self {
Self {
k_gain,
pre_filter,
x_hat: Matrix::zeros(),
}
}
pub fn control(&self, x: &Matrix<S, N, 1>, r: &Matrix<S, I, 1>) -> Matrix<S, I, 1> {
let x_ref = matmul(&self.pre_filter.n_x, r);
let error = x.sub_mat(&x_ref);
let kx = matmul(&self.k_gain, &error);
let u_fb = kx.neg();
let u_ff = matmul(&self.pre_filter.n_u, r);
u_fb.add_mat(&u_ff)
}
pub fn reset(&mut self) {
self.x_hat = Matrix::zeros();
}
}
pub fn design_prefilter<S: ControlScalar, const N: usize, const I: usize, const M: usize>(
a: &Matrix<S, N, N>,
b: &Matrix<S, N, I>,
c: &Matrix<S, M, N>,
k: &Matrix<S, I, N>,
) -> Option<PreFilter<S, N, I>> {
let bk = matmul(b, k);
let a_cl = a.sub_mat(&bk);
let i_minus_acl = Matrix::<S, N, N>::identity().sub_mat(&a_cl);
let i_minus_acl_inv = i_minus_acl.inv()?;
let t = matmul(&i_minus_acl_inv, b);
let ct = matmul(c, &t);
let ct_t = ct.transpose();
let cct = matmul(&ct, &ct_t);
let cct_inv = cct.inv()?;
let nu_mat = matmul(&ct_t, &cct_inv);
let mut n_u = Matrix::<S, I, I>::zeros();
for row in 0..I {
for col in 0..I.min(M) {
n_u.data[row][col] = nu_mat.data[row][col];
}
}
let n_x = Matrix::<S, N, I>::zeros();
Some(PreFilter { n_x, n_u })
}
#[cfg(test)]
mod tests {
use super::*;
fn siso_system() -> (
Matrix<f64, 1, 1>,
Matrix<f64, 1, 1>,
Matrix<f64, 1, 1>,
Matrix<f64, 1, 1>,
) {
let mut a = Matrix::<f64, 1, 1>::zeros();
a.data[0][0] = 0.9;
let mut b = Matrix::<f64, 1, 1>::zeros();
b.data[0][0] = 1.0;
let mut c = Matrix::<f64, 1, 1>::zeros();
c.data[0][0] = 1.0;
let mut k = Matrix::<f64, 1, 1>::zeros();
k.data[0][0] = 0.5;
(a, b, c, k)
}
#[test]
fn prefilter_design_siso() {
let (a, b, c, k) = siso_system();
let pf = design_prefilter::<f64, 1, 1, 1>(&a, &b, &c, &k);
assert!(
pf.is_some(),
"Pre-filter design should succeed for stable system"
);
let pf = pf.unwrap();
assert!(pf.n_u.data[0][0].abs() > 0.0, "Nu should be nonzero");
}
#[test]
fn servo_control_computation() {
let (a, b, c, k) = siso_system();
let pf = design_prefilter::<f64, 1, 1, 1>(&a, &b, &c, &k).unwrap();
let ctrl = ServoController::new(k, pf);
let mut x = Matrix::<f64, 1, 1>::zeros();
x.data[0][0] = 0.0;
let mut r = Matrix::<f64, 1, 1>::zeros();
r.data[0][0] = 1.0;
let u = ctrl.control(&x, &r);
assert!(
u.data[0][0].abs() > 0.0,
"Control should be nonzero: {}",
u.data[0][0]
);
}
#[test]
fn servo_zero_reference_zero_control() {
let (a, b, c, k) = siso_system();
let pf = design_prefilter::<f64, 1, 1, 1>(&a, &b, &c, &k).unwrap();
let ctrl = ServoController::new(k, pf);
let x = Matrix::<f64, 1, 1>::zeros();
let r = Matrix::<f64, 1, 1>::zeros();
let u = ctrl.control(&x, &r);
assert!(
u.data[0][0].abs() < 1e-12,
"u should be zero at equilibrium: {}",
u.data[0][0]
);
}
#[test]
fn servo_tracking_convergence() {
let mut a = Matrix::<f64, 2, 2>::identity();
a.data[0][1] = 0.1;
let mut b = Matrix::<f64, 2, 1>::zeros();
b.data[0][0] = 0.005;
b.data[1][0] = 0.1;
let mut c = Matrix::<f64, 1, 2>::zeros();
c.data[0][0] = 1.0;
let mut k = Matrix::<f64, 1, 2>::zeros();
k.data[0][0] = 3.0;
k.data[0][1] = 1.5;
let pf = design_prefilter::<f64, 2, 1, 1>(&a, &b, &c, &k);
assert!(pf.is_some(), "Pre-filter should be designed");
let pf = pf.unwrap();
let ctrl = ServoController::new(k, pf);
let mut x = Matrix::<f64, 2, 1>::zeros();
let mut r = Matrix::<f64, 1, 1>::zeros();
r.data[0][0] = 1.0;
for _ in 0..500 {
let u = ctrl.control(&x, &r);
let ax = matmul(&a, &x);
let bu = matmul(&b, &u);
x = ax.add_mat(&bu);
}
assert!(
(x.data[0][0] - 1.0).abs() < 0.05,
"Output should track reference: x[0]={}",
x.data[0][0]
);
}
}