Skip to main content

motor_codec/
command.rs

1//! Vendor-agnostic motor command surface.
2
3/// A control command directed at a single motor.
4///
5/// Vendor-specific commands (e.g. Damiao's `0x7FF` parameter sub-protocol) do
6/// not appear here; reach them via vendor-extension traits exposed by the
7/// vendor's codec crate.
8#[derive(Copy, Clone, Debug, PartialEq)]
9#[non_exhaustive]
10pub enum Command {
11    /// MIT impedance control: kp, kd gains plus q (position), dq (velocity),
12    /// tau (feed-forward torque) setpoints.
13    Mit {
14        /// Position gain.
15        kp: f64,
16        /// Velocity gain.
17        kd: f64,
18        /// Position setpoint (radians).
19        q: f64,
20        /// Velocity setpoint (rad/s).
21        dq: f64,
22        /// Feed-forward torque (Nm).
23        tau: f64,
24    },
25    /// Position + velocity setpoint control.
26    PosVel {
27        /// Position setpoint (radians).
28        q: f64,
29        /// Velocity setpoint (rad/s).
30        dq: f64,
31    },
32    /// Pure velocity control.
33    Vel {
34        /// Velocity setpoint (rad/s).
35        dq: f64,
36    },
37    /// Position + velocity + current-percent-units control.
38    PosForce {
39        /// Position setpoint (radians).
40        q: f64,
41        /// Velocity setpoint (rad/s).
42        dq: f64,
43        /// Current setpoint in per-unit (0.0 to 1.0).
44        i_pu: f64,
45    },
46}
47
48/// Tagged discriminant of [`Command`], suitable for error-reporting
49/// "this codec does not support mode X".
50#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
51#[non_exhaustive]
52pub enum CommandKind {
53    /// Corresponds to [`Command::Mit`].
54    Mit,
55    /// Corresponds to [`Command::PosVel`].
56    PosVel,
57    /// Corresponds to [`Command::Vel`].
58    Vel,
59    /// Corresponds to [`Command::PosForce`].
60    PosForce,
61}
62
63impl Command {
64    /// Tagged discriminant.
65    pub fn kind(&self) -> CommandKind {
66        match self {
67            Command::Mit { .. } => CommandKind::Mit,
68            Command::PosVel { .. } => CommandKind::PosVel,
69            Command::Vel { .. } => CommandKind::Vel,
70            Command::PosForce { .. } => CommandKind::PosForce,
71        }
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn all_four_modes_construct() {
81        let mit = Command::Mit {
82            kp: 50.0,
83            kd: 1.0,
84            q: 0.0,
85            dq: 0.0,
86            tau: 0.0,
87        };
88        let pv = Command::PosVel { q: 1.0, dq: 2.0 };
89        let v = Command::Vel { dq: 1.0 };
90        let pf = Command::PosForce {
91            q: 1.0,
92            dq: 2.0,
93            i_pu: 0.5,
94        };
95        assert_eq!(mit.kind(), CommandKind::Mit);
96        assert_eq!(pv.kind(), CommandKind::PosVel);
97        assert_eq!(v.kind(), CommandKind::Vel);
98        assert_eq!(pf.kind(), CommandKind::PosForce);
99    }
100
101    #[test]
102    fn debug_round_trip() {
103        let cmd = Command::Mit {
104            kp: 50.0,
105            kd: 1.0,
106            q: 0.0,
107            dq: 0.0,
108            tau: 0.0,
109        };
110        let s = alloc::format!("{:?}", cmd);
111        assert!(s.contains("Mit"));
112        assert!(s.contains("50"));
113    }
114}