Expand description
A small library that contains some basic units to help structuring kinematics and robotic programming in rust. The library uses rusts tuple structs to create a compile-time checking of correct unit, variable and function usage.
§Quick introduction
In many functions for kinematics and robotics, it sometimes becomes unclear which type of unit is desired to be used.
/// Relative movement
fn move_distance(dist : f32, vel : f32) {
// ...
}
/// Absolute movement
fn move_to_position(pos : f32, vel : f32) {
// ...
}
Even if most code is not as horribly documented, nothing stops a developer from accidently plugging in an absolute distance into a function that takes a relative one. Here comes this library into play:
use syunit::prelude::*;
/// Relative movement
fn move_distance(dist : Radians, vel : RadPerSecond) {
// ...
}
/// Absolute movement
fn move_to_position(pos : PositionRad, vel : RadPerSecond) {
// ...
}
Each unit is represented by a f32
enclosed into a tuple struct, making them simple but also their own type!
Why these units are helpful not only for documentation is explained in the flowing chapters:
§Explicit syntax
As rust always prefers explicit syntax, so does this library. The unit types cannot be converted back to a f32
without calling into()
.
use syunit::prelude::*;
fn requires_f32(value : f32) {
// ...
}
fn requires_velocity(value : MMPerSecond) {
// ...
}
// Every unit is created by the tuple struct constructor using a `f32` value
let abs_pos = PositionMM(10.0);
requires_f32(abs_pos);
// error[E0308]: mismatched types
// |
// | requires_f32(abs_pos) // ERROR! => Type `PositionMM` cannot be used as `f32`
// | ------------ ^^^^^ expected `f32`, found `PositionMM`
// | |
// | arguments to this function are incorrect
// |
requires_velocity(abs_pos);
// error[E0308]: mismatched types
// |
// | requires_f32(abs_pos);
// | ------------ ^^^^^ expected `MMPerSecond`, found `PositionMM`
// | |
// | arguments to this function are incorrect
// |
§Operations and automatic type evaluation
The library comes with a lot of implementations in addition to the units, making it possible to do a lot of operations with these units and letting the compiler automatically evaluate the resulting units for you
use syunit::prelude::*;
// Radial / Linear
assert_eq!(RadPerSecond(4.0) * Millimeters(2.0), MMPerSecond(8.0));
assert_eq!(RadPerSecond2(-3.0) * Millimeters(2.0), MMPerSecond2(-6.0));
// Seconds / Hertz
assert_eq!(Hertz(4.0), 1.0 / Seconds(0.25));
assert_eq!(1.0 / Hertz(5.0), Seconds(0.2));
// Time to build up speed
assert_eq!(MMPerSecond(6.0) / MMPerSecond2(2.0), Seconds(3.0));
// Forces
assert_eq!(NewtonMeters(-5.0) / KgMeter2(2.0), RadPerSecond2(-2.5));
assert!((Newtons(3.0) / Kilogramms(1.5) - MMPerSecond2(2000.0)).abs().0 < 0.001); // Automatic conversion
// ...
Another helpful unit type are Positions
, they help differentiating between absolute and relative distances.
use syunit::prelude::*;
// Difference between two positions is a relative distance
assert_eq!(PositionMM(5.0) - PositionMM(3.0), Millimeters(2.0));
// Radial position math
assert_eq!(PositionRad(3.0) + Radians(2.0), PositionRad(5.0));
assert_eq!(PositionRad(3.0) - PositionRad(2.0), Radians(1.0));
A very special unit is Seconds
, dividing or multipling by it often changes units.
use syunit::prelude::*;
// Travelling a distance of 6mm in 2 seconds gives a velocity of 3mm/s
assert_eq!(Millimeters(6.0) / Seconds(2.0), MMPerSecond(3.0));
// Accelerating to a velocity of 3mm/s in 2 seconds gives an acceleration of 1.5mm/s^2
assert_eq!(MMPerSecond(3.0) / Seconds(2.0), MMPerSecond2(1.5));
// Travelling with 3mm/s for 3 seconds gives a total distance of 9mm
assert_eq!(MMPerSecond(3.0) * Seconds(3.0), Millimeters(9.0));
§Unitsets
There is also a tool for defining functions in a more general way, with the help of UnitSets
!
use syunit::prelude::*;
fn get_distance<U : UnitSet>(vel : U::Velocity, time : U::Time) -> U::Distance {
vel * time // Compiler automatically checks if the types match
}
fn time_for_dist_accelerating<U : UnitSet>(distance : U::Distance, vel_start : U::Velocity, vel_end : U::Velocity) -> U::Time {
let vel_avg = (vel_start + vel_end) / 2.0;
distance / vel_avg
}
assert_eq!(get_distance::<MetricMM>(MMPerSecond(4.0), Seconds(2.0)), Millimeters(8.0)); // Using linear metric mm
assert_eq!(time_for_dist_accelerating::<Rotary>(Radians(3.0), RadPerSecond(2.0), RadPerSecond(4.0)), Seconds(1.0)); // Using rotary units
§Metric and Imperial
The library also includes imperial units and conversions between them.
use syunit::prelude::*;
use syunit::imperial::*;
let millimeters = Millimeters(25.4);
assert_eq!(Meters(1.0), Millimeters(1000.0).into());
assert_eq!(Inches(1.0), Millimeters(25.4).into());
§serde
implementation
All the units implement serde::Serialize
and serde::Deserialize
if the “serde” feature is enabled, which is the case by default.
§Issues and improvements
Please feel free to create issues on the github repo!
Re-exports§
Modules§
- imperial
- Imperial units of measurement
- macros
- Macros for creating units and fast implementations between them
- metric
- Metric units of measurement and useful UnitSets
- prelude
- Lazy import of the library
Macros§
- additive_
unit - Helper macro that implements everything needed to do +,-,+=,-= operations with the unit itself
- basic_
unit - Implements the basics for a unit
- basic_
unit_ helper - Implements the basics for a unit
- derive_
units - Implements everything required to form a “derive over time like”-connection between the given units
- impl_
conversion - Implements conversion between two units
- impl_
div - Automatically implements division between three units
- impl_
div_ bidir - Identical to impl_div!, however it implements the division in both directions, so
- impl_
full_ conversion - Implements everything required to form a “derive over time like”-connection between the given units
- impl_
mul - Automatically implements multiplication between three units
- impl_
mul_ bidir - Identical to impl_mul!, however it implements the division in both directions, so
- inertia_
unit - Automatically implement InertiaUnit for the given unit
- position_
unit - Helper macro for position units
Structs§
- Factor
- Represents a certain factor between 0 and 1
- Hertz
- Represents a freqency in Hertz (or 1 / Seconds)
- Position
Rad - Represents a position in Radians
- RadPer
Second - Represents Radians per second (rad/s)
- RadPer
Second2 - Represents Radians per second squared (rad/s^2)
- RadPer
Second3 - Represents Radians per second qubed (rad/s^3)
- Radians
- Represents Radians (rad)
- Seconds
- Represents a time in seconds as a f32
Enums§
- Direction
- Direction of movement
Traits§
- Additive
Unit - Marker traits for units, that can be added and subtracted from themselfs
- Derivable
Unit - Marker trait for units that can be derived by a variable
V
to form the resultResult
- Inertia
Unit - A helper trait for calculations with inertia units
- Integrable
Unit - Marker trait for units that can be integrated by a variable
V
to form the resultResult
- Unit
- General trait for all units, defines the basic constraints required to work with units
- UnitSet
- A set of units that have a strong relationship to each other
Functions§
- add_
unit_ arrays - Add two arrays of units
- compare_
unit_ arrays - Compare two unit arrays
- sub_
unit_ arrays - Subtract two arrays of units