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
use core::f32::consts::PI;
use serde::{Serialize, Deserialize};
use syunit::*;
use crate::ActuatorVars;
use crate::data::MicroSteps;
use crate::math::force::torque_dyn;
/// Stores data for generic components
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct StepperConfig {
/// Supply voltage of the components in Volts
pub voltage : f32,
/// Overload current of the stepper, can increase torque
pub overload_current : Option<f32>
}
impl StepperConfig {
/// Generic `StepperConfig` for testing purposes
pub const GEN : Self = Self {
voltage: 12.0,
overload_current: None
};
/// Comp data that will case an error in calculations as it has not been yet initialized.
///
/// (Has to be overwritten, will cause errors otherwise)
pub const ERROR : Self = Self {
voltage: 0.0,
overload_current: None
};
/// Creates a new StepperConfig instance
#[inline(always)]
pub fn new(voltage : f32, overload_current : Option<f32>) -> Self {
Self {
voltage,
overload_current
}
}
}
/// 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 default_current : f32,
/// Motor inductence [Unit H]
pub inductance : f32,
/// Coil resistance [Unit Ohm]
pub resistance : f32,
/// Step count per revolution [Unit (1)]
pub number_steps : u64,
/// Stall torque [Unit Nm]
pub torque_stall : Force,
/// Inhertia moment [Unit kg*m^2]
pub inertia_motor : Inertia
}
impl StepperConst {
// Constants
/// Error stepperdata with all zeros
pub const ERROR : Self = Self {
default_current: 0.0,
inductance: 0.0,
resistance: 0.0,
number_steps: 0,
torque_stall: Force::ZERO,
inertia_motor: 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 {
default_current: 1.5,
inductance: 0.004,
resistance: 2.3,
number_steps: 200,
torque_stall: Force(0.42),
inertia_motor: Inertia(0.000_005_7)
};
//
// Amperage
/// Maximum overload force with the given overload (or underload) voltage `u`
pub fn torque_overload_max(&self, voltage : f32) -> Force {
self.torque_stall * voltage / self.resistance / self.default_current
}
/// Torque created with the given overload (or underload) current `i`
pub fn torque_overload(&self, current : Option<f32>) -> Force {
self.torque_stall * current.unwrap_or(self.default_current) / self.default_current
}
//
// Acceleration
pub fn alpha_max_stall(&self, vars : &ActuatorVars, dir : Direction) -> Option<Acceleration> {
vars.force_after_load(self.torque_stall, dir).map(|f| f / vars.inertia_after_load(self.inertia_motor))
}
pub fn alpha_max_for_velocity(&self, vars : &ActuatorVars, config : &StepperConfig, velocity : Velocity, dir : Direction) -> Option<Acceleration> {
vars.force_after_load(torque_dyn(self, velocity , config.voltage, None), dir).map(|f| f / vars.inertia_after_load(self.inertia_motor))
}
//
// Speeds
/// The inductivity constant [Unit s]
#[inline(always)]
pub fn tau(&self) -> Time {
Time(self.inductance / self.resistance)
}
/// Maximum speed for a stepper motor where it can be guarantied that it works properly
#[inline(always)]
pub fn velocity_max(&self, voltage : f32) -> Velocity {
Velocity(PI * voltage / self.default_current / self.inductance / self.number_steps as f32)
}
//
/// Velocity 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::prelude::*;
///
/// let data = StepperConst::GEN;
///
/// assert!((data.velocity (Time(1.0/200.0), MicroSteps::default()) - Velocity(2.0 * PI)).abs() < Velocity(0.001));
/// ```
#[inline(always)]
pub fn velocity (&self, step_time : Time, microsteps : MicroSteps) -> Velocity {
if (step_time == Time(0.0)) | (step_time == Time(-0.0)) {
panic!("The given step time ({}) is zero!", step_time)
}
self.step_angle(microsteps) / step_time
}
// Step angles & times
/// Get the angular distance of a step in radians, considering microstepping
/// - `micro` is the amount of microsteps per full step
#[inline(always)]
pub fn step_angle(&self, microsteps : MicroSteps) -> Delta {
self.full_step_angle() / microsteps.as_u8() as f32
}
/// A full step angle of the motor, ignoring microstepping
#[inline(always)]
pub fn full_step_angle(&self) -> Delta {
Delta(2.0 * PI / self.number_steps as f32)
}
/// Time per step for the given velocity
///
/// # Unit
///
/// Returns the time in seconds
///
/// # Panics
///
/// Panics if the given `velocity ` is zero
#[inline]
pub fn step_time(&self, velocity : Velocity, microsteps : MicroSteps) -> Time {
// if (velocity == Velocity(0.0)) | (velocity == Velocity(-0.0)) {
// panic!("The given velocity ({}) is zero!", velocity );
// }
self.step_angle(microsteps) / velocity
}
/// Time per full step at the given velocity velocity
///
/// # Unit
///
/// Returns the time in seconds
#[inline]
pub fn full_step_time(&self, velocity : Velocity) -> Time {
if (velocity == Velocity(0.0)) | (velocity == Velocity(-0.0)) {
panic!("The given velocity ({}) is zero!", velocity );
}
self.full_step_angle() / velocity
}
//
// Steps & Angles - Conversions
/// Converts the given angle `ang` into a absolute number of steps (always positive).
#[inline(always)]
pub fn steps_from_angle_abs(&self, angle : Delta, microsteps : MicroSteps) -> u64 {
(angle.abs() / self.step_angle(microsteps)).round() as u64
}
/// Converts the given angle `ang` into a number of steps
#[inline(always)]
pub fn steps_from_angle(&self, angle : Delta, microsteps : MicroSteps) -> i64 {
(angle / self.step_angle(microsteps)).round() as i64
}
/// Converts the given number of steps into an angle
#[inline(always)]
pub fn angle_from_steps_abs(&self, steps : u64, microsteps : MicroSteps) -> Delta {
steps as f32 * self.step_angle(microsteps)
}
/// Converts the given number of steps into an angle
#[inline(always)]
pub fn angle_from_steps(&self, steps : i64, microsteps : MicroSteps) -> Delta {
steps as f32 * self.step_angle(microsteps)
}
pub fn round_angle_to_steps(&self, angle : Delta, microsteps : MicroSteps) -> Delta {
self.angle_from_steps(self.steps_from_angle(angle, microsteps), microsteps)
}
// 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, angle : Delta, microsteps : MicroSteps) -> bool {
self.steps_from_angle(angle, microsteps) == steps
}
//
}