1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
use core::f32::consts::PI;
use serde::{Serialize, Deserialize};
use crate::{units::*, lib_error};
// Submodules
mod lk;
pub use lk::LinkedData;
/// Crate for servo motor data
pub mod servo;
/// Crate for variables read and written during runtime
mod var;
pub use var::CompVars;
//
/// A collection of the most relevant variables Unit stepper calculation
/// ```
/// use syact::StepperConst;
///
/// // Create the data from an standard motor
/// let mut data = StepperConst::MOT_17HE15_1504S;
///
/// ```
/// Supports JSON-Serialize and Deserialize with the `serde_json` library
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StepperConst
{
/// Max phase current [Unit A]
pub i_max : f32,
/// Motor inductence [Unit H]
pub l : f32,
/// Step count per revolution [Unit (1)]
pub n_s : u64,
/// Stall torque [Unit Nm]
pub t_s : Force,
/// Inhertia moment [Unit kg*m^2]
pub j_s : Inertia
}
impl StepperConst
{
/// Error stepperdata with all zeros
pub const ERROR : Self = Self {
i_max: 0.0,
l: 0.0,
n_s: 0,
t_s: Force::ZERO,
j_s: Inertia::ZERO
};
/// Generic stepper motor data, only in use when testing for simple syntax
pub const GEN : Self = Self::MOT_17HE15_1504S;
/// ### Stepper motor 17HE15-1504S
/// Values for standard stepper motor, see <https://github.com/SamuelNoesslboeck/syact/docs/datasheets/17HE15_1504S.pdf>
pub const MOT_17HE15_1504S : Self = Self {
i_max: 1.5,
l: 0.004,
n_s: 200,
t_s: Force(0.42),
j_s: Inertia(0.000_005_7)
};
/// The maximum angular acceleration of the motor (in stall) in consideration of the current loads
#[inline(always)]
pub fn alpha_max(&self, var : &CompVars) -> Result<Alpha, crate::Error> {
Ok(self.t(var.t_load)? / self.j(var.j_load))
}
/// The maximum angular acceleration of the motor, with a modified torque t_s
#[inline(always)]
pub fn alpha_max_dyn(&self, t_s : Force, var : &CompVars) -> Result<Alpha, crate::Error> {
Ok(Self::t_dyn(t_s, var.t_load)? / self.j(var.j_load))
}
/// The inductivity constant [Unit s]
#[inline(always)]
pub fn tau(&self, u : f32) -> Time {
Time(2.0 * self.i_max * self.l / u)
}
/// Maximum speed for a stepper motor where it can be guarantied that it works properly
#[inline(always)]
pub fn max_speed(&self, u : f32) -> Omega {
2.0 * PI / self.tau(u) / self.n_s as f32
}
/// Omega for time per step [Unit 1/s]
///
/// # Panics
///
/// Panics if the given `step_time` is zero (`-0.0` included)
///
/// ```rust
/// use core::f32::consts::PI;
///
/// use syact::data::StepperConst;
/// use syact::units::*;
///
/// let data = StepperConst::GEN;
///
/// assert!((data.omega(Time(1.0/200.0), 1) - Omega(2.0 * PI)).abs() < Omega(0.001));
/// ```
#[inline(always)]
pub fn omega(&self, step_time : Time, micro : u8) -> Omega {
if (step_time == Time(0.0)) | (step_time == Time(-0.0)) {
panic!("The given step time ({}) is zero!", step_time)
}
self.step_ang(micro) / step_time
}
// Steps
/// Get the angular distance of a step Unit rad [Unit 1]
#[inline(always)]
pub fn step_ang(&self, micro : u8) -> Delta {
Delta(2.0 * PI / self.n_s as f32 / micro as f32)
}
/// Time per step for the given omega [Unit s]
///
/// # Panics
///
/// Panics if the given `omega` is zero
#[inline(always)]
pub fn step_time(&self, omega : Omega, micro : u8) -> Time {
if (omega == Omega(0.0)) | (omega == Omega(-0.0)) {
panic!("The given omega ({}) is zero!", omega);
}
self.step_ang(micro) / omega
}
//
// Load calculations
/// Max motor torque when having a load [Unit Nm]
///
/// # Errors
///
/// Returns an error if the load torque `t_load` is larger than the stall torque `t_s`
///
/// # Pancis
///
/// Panics if the load force is either
#[inline(always)]
pub fn t(&self, t_load : Force) -> Result<Force, crate::Error> { // TODO: Add overload protection
if !t_load.is_finite() {
panic!("The given load force ({}) is invalid!", t_load);
}
#[cfg(feature = "std")]
if t_load > self.t_s {
Err(lib_error(format!("Overload! (Motor torque: {}, Load: {})", self.t_s, t_load)))
} else {
Ok(self.t_s - t_load)
}
#[cfg(not(feature = "std"))]
if t_load > self.t_s {
Err(crate::ErrorKind::Overload)
} else {
Ok(self.t_s - t_load)
}
}
/// Max motor torque when having a load, using a modified base torque t_s [Unit Nm]
///
/// # Errors
///
/// Returns an error if the load torque `t_load` is larger than the stall torque `t_s`
///
/// # Panics
///
/// Panics if either the given motor torque `t_s` or load torque `t_load` are negative, zero (-0.0 included), infinite or NAN
///
/// ```rust
/// use syact::data::StepperConst;
/// use syact::units::*;
///
/// // Base torque of the motor
/// const BASE : Force = Force(1.0);
///
/// // Load torque
/// const LOAD_1 : Force = Force(0.5);
/// const LOAD_2 : Force = Force(1.5);
///
/// // Compare
/// assert_eq!(StepperConst::t_dyn(BASE, LOAD_1).unwrap(), Force(0.5));
/// assert!(StepperConst::t_dyn(BASE, LOAD_2).is_err());
/// ```
#[inline(always)]
pub fn t_dyn(t_s : Force, t_load : Force) -> Result<Force, crate::Error> { // TODO: Add overload protection
if !t_s.is_normal() {
panic!("The given motor force ({}) is invalid!", t_load);
}
if !t_load.is_finite() {
panic!("The given load force ({}) is invalid!", t_load);
}
#[cfg(feature = "std")]
if t_load > t_s {
Err(lib_error(format!("Overload! (Motor torque: {}, Load: {})", t_s, t_load)))
} else {
Ok(t_s - t_load)
}
#[cfg(not(feature = "std"))]
if t_load > t_s {
Err(crate::ErrorKind::Overload)
} else {
Ok(t_s - t_load)
}
}
/// Motor inertia when having a load [Unit kg*m^2]
///
/// # Panics
///
/// Panics if the given inertia `j_load` is negative (-0.0 included)
#[inline(always)]
pub fn j(&self, j_load : Inertia) -> Inertia {
if j_load.is_sign_negative() | (!j_load.is_finite()) {
panic!("The given inertia ({}) is invalid!", j_load);
}
self.j_s + j_load
}
//
// Conversions
/// Converts the given angle `ang` into a absolute number of steps (always positive).
#[inline(always)]
pub fn steps_from_ang_abs(&self, ang : Delta, micro : u8) -> u64 {
(ang.abs() / self.step_ang(micro)).round() as u64
}
/// Converts the given angle `ang` into a number of steps
#[inline(always)]
pub fn steps_from_ang(&self, ang : Delta, micro : u8) -> i64 {
(ang / self.step_ang(micro)).round() as i64
}
/// Converts the given number of steps into an angle
#[inline(always)]
pub fn ang_from_steps_abs(&self, steps : u64, micro : u8) -> Delta {
steps as f32 * self.step_ang(micro)
}
/// Converts the given number of steps into an angle
#[inline(always)]
pub fn ang_from_steps(&self, steps : i64, micro : u8) -> Delta {
steps as f32 * self.step_ang(micro)
}
// Comparision
/// Checks wheither the given angle `ang` is in range (closes to) a given step count `steps`
#[inline(always)]
pub fn is_in_step_range(&self, steps : i64, ang : Delta, micro : u8) -> bool {
self.steps_from_ang(ang, micro) == steps
}
//
}