1use crate::{Controller, ControllerError};
4use async_trait::async_trait;
5use mecha10_core::actuator::{MotorStatus, Twist};
6use serde::{Deserialize, Serialize};
7
8#[async_trait]
16pub trait MotorController: Controller {
17 async fn set_velocity(&mut self, left: f32, right: f32) -> Result<(), Self::Error>;
22
23 async fn set_twist(&mut self, twist: Twist) -> Result<(), Self::Error> {
27 let wheel_base = 0.3; let left = twist.linear - (twist.angular * wheel_base / 2.0);
31 let right = twist.linear + (twist.angular * wheel_base / 2.0);
32 self.set_velocity(left, right).await
33 }
34
35 async fn get_encoders(&mut self) -> Result<(i32, i32), Self::Error>;
39
40 async fn get_status(&mut self) -> Result<MotorStatus, Self::Error>;
42
43 async fn emergency_stop(&mut self) -> Result<(), Self::Error>;
45
46 async fn enable(&mut self) -> Result<(), Self::Error> {
48 Ok(())
49 }
50
51 async fn disable(&mut self) -> Result<(), Self::Error> {
53 Ok(())
54 }
55
56 async fn reset_encoders(&mut self) -> Result<(), Self::Error>
58 where
59 Self::Error: From<ControllerError>,
60 {
61 Err(Self::Error::from(ControllerError::NotSupported(
62 "Encoder reset not supported".to_string(),
63 )))
64 }
65
66 async fn set_pid_gains(&mut self, _gains: PidGains) -> Result<(), Self::Error>
68 where
69 Self::Error: From<ControllerError>,
70 {
71 Err(Self::Error::from(ControllerError::NotSupported(
72 "PID control not supported".to_string(),
73 )))
74 }
75
76 async fn get_pid_gains(&self) -> Result<PidGains, Self::Error>
78 where
79 Self::Error: From<ControllerError>,
80 {
81 Err(Self::Error::from(ControllerError::NotSupported(
82 "PID control not supported".to_string(),
83 )))
84 }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct MotorConfig {
94 pub mode: MotorControlMode,
96
97 pub max_velocity: f32,
99
100 pub max_acceleration: f32,
102
103 pub wheel_base: Option<f32>,
105
106 pub wheel_radius: Option<f32>,
108
109 pub encoder_tpr: Option<u32>,
111
112 pub gear_ratio: Option<f32>,
114
115 pub pid_gains: Option<PidGains>,
117
118 pub safety: SafetyLimits,
120}
121
122impl Default for MotorConfig {
123 fn default() -> Self {
124 Self {
125 mode: MotorControlMode::Velocity,
126 max_velocity: 1.0,
127 max_acceleration: 0.5,
128 wheel_base: Some(0.3),
129 wheel_radius: Some(0.05),
130 encoder_tpr: Some(1024),
131 gear_ratio: Some(1.0),
132 pid_gains: None,
133 safety: SafetyLimits::default(),
134 }
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
140pub enum MotorControlMode {
141 OpenLoop,
143
144 Velocity,
146
147 Position,
149
150 Torque,
152
153 Trajectory,
155}
156
157#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
159pub struct PidGains {
160 pub kp: f32,
162
163 pub ki: f32,
165
166 pub kd: f32,
168}
169
170impl Default for PidGains {
171 fn default() -> Self {
172 Self {
173 kp: 1.0,
174 ki: 0.1,
175 kd: 0.01,
176 }
177 }
178}
179
180impl PidGains {
181 pub fn new(kp: f32, ki: f32, kd: f32) -> Self {
182 Self { kp, ki, kd }
183 }
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct SafetyLimits {
189 pub max_current: Option<f32>,
191
192 pub max_temperature: Option<f32>,
194
195 pub max_voltage: Option<f32>,
197
198 pub min_voltage: Option<f32>,
200
201 pub watchdog_enabled: bool,
203
204 pub watchdog_timeout: f32,
206}
207
208impl Default for SafetyLimits {
209 fn default() -> Self {
210 Self {
211 max_current: Some(5.0),
212 max_temperature: Some(80.0),
213 max_voltage: Some(16.0),
214 min_voltage: Some(10.0),
215 watchdog_enabled: true,
216 watchdog_timeout: 1.0,
217 }
218 }
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct MotorStatusDetailed {
228 pub timestamp: u64,
230
231 pub left: MotorState,
233
234 pub right: MotorState,
236
237 pub battery_voltage: f32,
239
240 pub temperature: f32,
242
243 pub emergency_stop: bool,
245
246 pub enabled: bool,
248
249 pub errors: Vec<MotorError>,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct MotorState {
256 pub velocity: f32,
258
259 pub position: i32,
261
262 pub current: f32,
264
265 pub temperature: f32,
267
268 pub pwm: f32,
270
271 pub errors: Vec<String>,
273}
274
275impl Default for MotorState {
276 fn default() -> Self {
277 Self {
278 velocity: 0.0,
279 position: 0,
280 current: 0.0,
281 temperature: 25.0,
282 pwm: 0.0,
283 errors: Vec::new(),
284 }
285 }
286}
287
288#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
290pub enum MotorError {
291 Overcurrent,
293
294 Overtemperature,
296
297 Undervoltage,
299
300 Overvoltage,
302
303 EncoderFailure,
305
306 CommunicationTimeout,
308
309 MotorStall,
311
312 HardwareFault,
314
315 InvalidCommand,
317
318 WatchdogTimeout,
320}
321
322impl std::fmt::Display for MotorError {
323 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324 match self {
325 Self::Overcurrent => write!(f, "Overcurrent"),
326 Self::Overtemperature => write!(f, "Overtemperature"),
327 Self::Undervoltage => write!(f, "Undervoltage"),
328 Self::Overvoltage => write!(f, "Overvoltage"),
329 Self::EncoderFailure => write!(f, "Encoder failure"),
330 Self::CommunicationTimeout => write!(f, "Communication timeout"),
331 Self::MotorStall => write!(f, "Motor stall"),
332 Self::HardwareFault => write!(f, "Hardware fault"),
333 Self::InvalidCommand => write!(f, "Invalid command"),
334 Self::WatchdogTimeout => write!(f, "Watchdog timeout"),
335 }
336 }
337}
338
339pub struct DifferentialDrive {
345 pub wheel_base: f32,
347
348 pub wheel_radius: f32,
350}
351
352impl DifferentialDrive {
353 pub fn new(wheel_base: f32, wheel_radius: f32) -> Self {
354 Self {
355 wheel_base,
356 wheel_radius,
357 }
358 }
359
360 pub fn twist_to_wheels(&self, twist: &Twist) -> (f32, f32) {
362 let left = twist.linear - (twist.angular * self.wheel_base / 2.0);
363 let right = twist.linear + (twist.angular * self.wheel_base / 2.0);
364 (left, right)
365 }
366
367 pub fn wheels_to_twist(&self, left: f32, right: f32) -> Twist {
369 let linear = (left + right) / 2.0;
370 let angular = (right - left) / self.wheel_base;
371 Twist { linear, angular }
372 }
373
374 pub fn angular_to_linear(&self, left_rad: f32, right_rad: f32) -> (f32, f32) {
376 let left = left_rad * self.wheel_radius;
377 let right = right_rad * self.wheel_radius;
378 (left, right)
379 }
380
381 pub fn linear_to_angular(&self, left_m: f32, right_m: f32) -> (f32, f32) {
383 let left = left_m / self.wheel_radius;
384 let right = right_m / self.wheel_radius;
385 (left, right)
386 }
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct TrajectoryPoint {
396 pub time: f32,
398
399 pub position: f32,
401
402 pub velocity: f32,
404
405 pub acceleration: f32,
407}
408
409#[derive(Debug, Clone, Serialize, Deserialize)]
411pub struct Trajectory {
412 pub points: Vec<TrajectoryPoint>,
414
415 pub duration: f32,
417}
418
419impl Trajectory {
420 pub fn new() -> Self {
421 Self {
422 points: Vec::new(),
423 duration: 0.0,
424 }
425 }
426
427 pub fn add_point(&mut self, point: TrajectoryPoint) {
428 if point.time > self.duration {
429 self.duration = point.time;
430 }
431 self.points.push(point);
432 }
433
434 pub fn is_empty(&self) -> bool {
435 self.points.is_empty()
436 }
437}
438
439impl Default for Trajectory {
440 fn default() -> Self {
441 Self::new()
442 }
443}