phoxal_model/robot/
motion.rs1use crate::ModelError;
4use crate::component::CapabilityRef;
5
6#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq)]
7#[serde(deny_unknown_fields)]
8pub struct MotionLimits {
9 pub max_linear_speed_mps: f64,
10 pub max_angular_speed_radps: f64,
11}
12
13impl MotionLimits {
14 pub fn validate(self) -> Result<Self, ModelError> {
15 if !(self.max_linear_speed_mps.is_finite()
16 && self.max_linear_speed_mps > 0.0
17 && self.max_linear_speed_mps <= f64::from(f32::MAX))
18 {
19 return Err(ModelError::Invalid(
20 "motion max_linear_speed_mps must be finite, positive, and fit in f32".into(),
21 ));
22 }
23 if !(self.max_angular_speed_radps.is_finite()
24 && self.max_angular_speed_radps > 0.0
25 && self.max_angular_speed_radps <= f64::from(f32::MAX))
26 {
27 return Err(ModelError::Invalid(
28 "motion max_angular_speed_radps must be finite, positive, and fit in f32".into(),
29 ));
30 }
31 Ok(self)
32 }
33}
34
35#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
36#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
37pub enum KinematicConfig {
38 Differential {
39 left_actuators: Vec<CapabilityRef>,
40 right_actuators: Vec<CapabilityRef>,
41 left_encoders: Vec<CapabilityRef>,
42 right_encoders: Vec<CapabilityRef>,
43 wheel_radius_m: f64,
44 wheel_base_m: f64,
45 },
46 Mecanum {
47 front_left_actuator: CapabilityRef,
48 front_right_actuator: CapabilityRef,
49 rear_left_actuator: CapabilityRef,
50 rear_right_actuator: CapabilityRef,
51 wheel_radius_m: f64,
52 wheel_base_m: f64,
53 track_m: f64,
54 },
55 Ackermann {
56 steering_actuator: CapabilityRef,
57 drive_actuator: CapabilityRef,
58 steering_encoder: Option<CapabilityRef>,
59 drive_encoder: Option<CapabilityRef>,
60 wheel_base_m: f64,
61 track_m: f64,
62 max_steering_angle_rad: f64,
63 },
64 Omnidirectional {
65 actuators: Vec<CapabilityRef>,
66 encoders: Vec<CapabilityRef>,
67 },
68}