Biblioteca de unidades de C++

El lanzamiento de WPILib en 2020 está acoplado con una biblioteca de unidades para equipos C++. Esta biblioteca apalanca el sistema de escritura de C++ para imponer la dimensionalidad correcta de los parámetros de los métodos, que lleve a cabo automáticamente las conversiones de unidades, y que incluso permita a los usuarios definir tipos de unidades arbitrarias. Ya que el sistema de escritura de C++ es aplicado a la hora de compilar, la librería no tiene costo a la hora de ejecutarse.

Usar la Biblioteca de Unidades

La biblioteca de las unidades es una biblioteca de sólo cabecera. Debe incluir la cabecera correspondiente en sus archivos fuente para las unidades que desea utilizar. Aquí tienes una lista de las unidades disponibles.

#include <units/acceleration.h>
#include <units/angle.h>
#include <units/angular_acceleration.h>
#include <units/angular_velocity.h>
#include <units/area.h>
#include <units/capacitance.h>
#include <units/charge.h>
#include <units/concentration.h>
#include <units/conductance.h>
#include <units/current.h>
#include <units/curvature.h>
#include <units/data.h>
#include <units/data_transfer_rate.h>
#include <units/density.h>
#include <units/dimensionless.h>
#include <units/energy.h>
#include <units/force.h>
#include <units/frequency.h>
#include <units/illuminance.h>
#include <units/impedance.h>
#include <units/inductance.h>
#include <units/length.h>
#include <units/luminous_flux.h>
#include <units/luminous_intensity.h>
#include <units/magnetic_field_strength.h>
#include <units/magnetic_flux.h>
#include <units/mass.h>
#include <units/moment_of_inertia.h>
#include <units/power.h>
#include <units/pressure.h>
#include <units/radiation.h>
#include <units/solid_angle.h>
#include <units/substance.h>
#include <units/temperature.h>
#include <units/time.h>
#include <units/torque.h>
#include <units/velocity.h>
#include <units/voltage.h>
#include <units/volume.h>

El encabezado units/math.h proporciona funciones de reconocimiento de unidades como units::math::abs().

Tipos de Unidades y Tipos de Contenedores

Las librerías de unidades de C++ están basadas en dos clases de definiciones: tipos de unidades y tipos de contenedores.

Tipos de Unidades

Los tipos de unidades corresponden al concepto abstracto de una unidad, sin que tenga un valor actual guardado. Unit types son el “bloque de construcción” fundamental de las librerías de unidad - todos los tipos de unidades son definidos constructivamente (usando la plantilla compound_unit) de un número pequeño de unit types «básicas» (como meters, seconds, etc).

Mientras los Tipos de Unidades no pueden contener valores numéricos, su uso en construir otro sistema de Unidad significa que cuando otro sistema o método utiliza un parámetro de plantilla para especificar su dimensionalidad, ese parámetro será un tipo de unidad.

Tipos de contenedores

Los tipos de contenedores corresponden a una cantidad actual dimensionada de acuerdo con cierta unidad – que es, ellos son los que contienen realmente el valor numérico. Container types son hechos de unit types con el modelo unit_t. La mayoría de los unit types tienen un container type que les correponde que tiene el mismo sufijo de nombre por _t - por ejemplo, el unit type units::meter corresponde al container type units::meter_t.

Cuando una cantidad específica de unidad es utilizada (como variable o un parámetro de método), va a ser una instancia del tipo contenedor/container type. Por defecto, los tipos contenedores pueden almacenar el valor actual como double - usuarios avanzados pueden cambiar esto llamando al modelo unit_t manualmente.

Una lista completa de tipos unitarios y contenedores puede ser encontrada en la documentación.

Creando instancias de unidades

Para crear una instancia de una unidad específica, creamos una instancia con su tipo contenedor:

// The variable speed has a value of 5 meters per second.
units::meter_per_second_t speed{5.0};

Una alternativa es que la librería de unidades tiene tipos literales definidos por algunos de los tipos contenedores más comunes. Éstos pueden ser usados en conjunción con tipo de inferencia vía auto para definir una unidad más sucintamente:

// The variable speed has a value of 5 meters per second.
auto speed = 5_mps;

Las unidades también se pueden inicializar usando un valor de otro tipo contenedor, mientras que los tipos puedan ser convertidos en medio de los otros. Por ejemplo, un valor meter_t puede ser creado desde un valor foot_t.

auto feet = 6_ft;
units::meter_t meters{feet};

DE hecho, todos los tipos contenedor representando tipo unidad convertibles son implícitamente convertibles. Así, lo siguiente es perfectamente legal:

units::meter_t distance = 6_ft;

En breve, usamos ninguna unidad de medida en lugar de otra unidad de medida, en ningun lugar del codigo; las librerias de unidades pueden automáticamente realizar la conversión por nosotros.

Realizando aritmética con unidades

Los tipo contenedores soportan todas las operaciones aritméticas ordinarias de su tipo de datos subyacente, con la condición añadida que la operación debe estar dimensionalmente sólido. Así, las adiciones siempre tienen que estar realizadas en dos tipos de contenedores compatibles:

// Add two meter_t values together
auto sum = 5_m + 7_m; // sum is 12_m

// Adds meters to feet; both are length, so this is fine
auto sum = 5_m + 7_ft;

// Tries to add a meter_t to a second_t, will throw a compile-time error
auto sum = 5_m + 7_s;

La multiplicación debe de ser realizada en cualquier par de tipo contenedor, y dar al tipo contenedor de una unidad compuesta:

Nota

Cuando un calculo da un tipo de unidad compuesta, éste tipo será solo revisado para validar en el punto de operación si el tipo resultado es especificado explícitamente. Si auto es utilizado, esta verificación no ocurrirá. Por ejemplo, cuando nosotros dividimos distancia sobre tiempo, deberíamos querer asegurar que el resultado, en efecto, es velocidad (por ejemplo, units::meter_per_second_t). Si el tipo resultante es declarado como auto, esta verificación no se hará.

// Multiply two meter_t values, result is square_meter_t
auto product = 5_m * 7_m; // product is 35_sq_m
// Divide a meter_t value by a second_t, result is a meter_per_second_t
units::meter_per_second_t speed = 6_m / 0.5_s; // speed is 12_mps

<cmath> Funciones

Algunas funciones std (como clamp) son plantillas que aceptan cualquier tipo que realice las operaciones aritméticas. Las cantidades almacenadas como tipo contenedores pueden funcionar con estas funciones sin ningún problema.

De cualquier manera, otras funciones std funcionan solo con tipos numéricos ordinarios (por ejemplo, double). Los espacios para el nombre de las librerías unitarias units::math``contienen envolvedores para algunas de estas funciones que aceptan unidades. Ejemplos de estas funciones incluyen ``sqrt, pow, etc.

auto area = 36_sq_m;
units::meter_t sideLength = units::math::sqrt(area);

Quitar el envoltorio de la unidad

Para convertir un tipo contenedor a un valor numérico crudo, el método to<..>() puede ser utilizado, donde el argumento de plantilla es de tipo subyacente.

units::meter_t distance = 6.5_m;
double distanceMeters = distance.to<double>();

Ejemplo de librería de unidades en el código WPILib

Algunos argumentos de métodos en nuevas funciones de WPILib (ex. cinemáticas) usan las librerías de unidad. Aquí hay un ejemplo de probando una trayectoria.

// Sample the trajectory at 1.2 seconds. This represents where the robot
// should be after 1.2 seconds of traversal.
Trajectory::State point = trajectory.Sample(1.2_s);

// Since units of time are implicitly convertible, this is exactly equivalent to the above code
Trajectory::State point = trajectory.Sample(1200_ms);

Algunas clases de WPILib representan objetos que pueden trabajar naturalmente con múltiples opciones de tipos de unidades - por ejemplo, un perfil de movimiento puede operar en cualquier distancia lineal (por ejemplo, metros) o distancia angular (por ejemplo, radianes). Para esas clases, el tipo unitario es requerido como un parámetro modelo:

// Creates a new set of trapezoidal motion profile constraints
// Max velocity of 10 meters per second
// Max acceleration of 20 meters per second squared
frc::TrapezoidProfile<units::meters>::Constraints{10_mps, 20_mps_sq};

// Creates a new set of trapezoidal motion profile constraints
// Max velocity of 10 radians per second
// Max acceleration of 20 radians per second squared
frc::TrapezoidProfile<units::radians>::Constraints{10_rad_per_s, 20__rad_per_s / 1_s};

Para documentación más detallada, por favor visite laágina oficial de GitHub para las librerías de unidad.