use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone, Copy)]
pub struct PmsmModel<S: ControlScalar> {
pub rs: S,
pub ld: S,
pub lq: S,
pub psi_f: S,
pub pole_pairs: u32,
pub j: S,
pub b_friction: S,
id: S,
iq: S,
omega_m: S,
theta_e: S,
}
impl<S: ControlScalar> PmsmModel<S> {
pub fn new(rs: S, ld: S, lq: S, psi_f: S, pole_pairs: u32, j: S, b_friction: S) -> Self {
Self {
rs,
ld,
lq,
psi_f,
pole_pairs,
j,
b_friction,
id: S::ZERO,
iq: S::ZERO,
omega_m: S::ZERO,
theta_e: S::ZERO,
}
}
pub fn small_pmsm() -> Self
where
S: From<f32>,
{
Self::new(
S::from_f64(0.5),
S::from_f64(3e-4),
S::from_f64(3e-4),
S::from_f64(0.008),
4,
S::from_f64(1.5e-5),
S::from_f64(1e-4),
)
}
pub fn step(&mut self, vd: S, vq: S, tau_load: S, dt: S) {
let p = S::from_f64(self.pole_pairs as f64);
let omega_e = self.omega_m * p;
let did = (vd - self.rs * self.id + omega_e * self.lq * self.iq) / self.ld;
let diq = (vq - self.rs * self.iq - omega_e * (self.ld * self.id + self.psi_f)) / self.lq;
let te = S::from_f64(1.5) * p * self.psi_f * self.iq;
let domega_m = (te - self.b_friction * self.omega_m - tau_load) / self.j;
self.id += did * dt;
self.iq += diq * dt;
self.omega_m += domega_m * dt;
self.theta_e += omega_e * dt;
}
pub fn id(&self) -> S {
self.id
}
pub fn iq(&self) -> S {
self.iq
}
pub fn omega_m(&self) -> S {
self.omega_m
}
pub fn omega_e(&self) -> S {
self.omega_m * S::from_f64(self.pole_pairs as f64)
}
pub fn theta_e_raw(&self) -> S {
self.theta_e
}
pub fn torque_estimate(&self) -> S {
let p = S::from_f64(self.pole_pairs as f64);
S::from_f64(1.5) * p * self.psi_f * self.iq
}
pub fn reset(&mut self) {
self.id = S::ZERO;
self.iq = S::ZERO;
self.omega_m = S::ZERO;
self.theta_e = S::ZERO;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rest_state_at_zero_input() {
let mut motor = PmsmModel::<f64>::new(0.5, 3e-4, 3e-4, 0.008, 4, 1.5e-5, 1e-4);
for _ in 0..1000 {
motor.step(0.0, 0.0, 0.0, 1e-4);
}
assert!(motor.id().abs() < 1e-10);
assert!(motor.iq().abs() < 1e-10);
assert!(motor.omega_m().abs() < 1e-10);
}
#[test]
fn vq_drives_rotation() {
let mut motor = PmsmModel::<f64>::new(0.5, 3e-4, 3e-4, 0.008, 4, 1.5e-5, 1e-4);
for _ in 0..10_000 {
motor.step(0.0, 5.0, 0.0, 1e-4);
}
assert!(motor.omega_m() > 0.0, "Motor should accelerate with vq>0");
}
#[test]
fn torque_proportional_to_iq() {
let motor = PmsmModel::<f64> {
rs: 0.5,
ld: 3e-4,
lq: 3e-4,
psi_f: 0.008,
pole_pairs: 4,
j: 1.5e-5,
b_friction: 1e-4,
id: 0.0,
iq: 5.0,
omega_m: 0.0,
theta_e: 0.0,
};
let te = motor.torque_estimate();
let expected = 1.5 * 4.0 * 0.008 * 5.0;
assert!((te - expected).abs() < 1e-10);
}
#[test]
fn reset_returns_to_zero() {
let mut motor = PmsmModel::<f64>::new(0.5, 3e-4, 3e-4, 0.008, 4, 1.5e-5, 1e-4);
for _ in 0..100 {
motor.step(5.0, 10.0, 0.5, 1e-4);
}
motor.reset();
assert_eq!(motor.id(), 0.0);
assert_eq!(motor.omega_m(), 0.0);
}
}