La librairie d’unités C++

La version 2020 de WPILib est associée à une librairie Units pour les équipes C++. Cette librairie exploite le C++ type system pour appliquer la dimensionnalité appropriée pour les paramètres des méthodes, effectuer automatiquement des conversions d’unités et même permettre aux utilisateurs de définir arbitrairement un type d’unités. Étant donné que le « C++ type system » est appliqué au moment de la compilation, la libraire n’affecte pas le temps d’exécution du programme final.

Utilisation de la librairie d’unités

La librairie d’unités est une bibliothèque d’en-tête uniquement. Vous devez inclure l’en-tête approprié dans vos fichiers source pour les unités que vous souhaitez utiliser. Voici une liste des unités 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>

L’en-tête units/math.h fournit des fonctions adaptées aux unités telles que units::math::abs().

Types d’unités et types de conteneurs

La librairie d’unités C++ est basée sur deux sortes de définitions de types: les types d’unités et les types de conteneurs.

Types d’unités

Les types d’unités correspondent au concept abstrait d’une unité, sans aucune valeur stockée réelle. Les types d’unités sont le « bloc de construction » fondamental de la librairie d’unités - tous les types d’unités sont définis de manière constructive (en utilisant le modèle compound_unit) à partir d’un petit nombre de types d’unités « de base » (tels que mètres, secondes, etc.).

Bien que les types d’unités ne puissent pas contenir de valeurs numériques, leur utilisation dans la création d’autres types d’unités signifie que lorsqu’un type ou une méthode utilise un paramètre de modèle pour spécifier sa dimensionnalité, ce paramètre sera alors un type d’unité.

Types de conteneurs

Les types de conteneurs correspondent à une quantité réelle dimensionnée en fonction d’une unité, c’est-à-dire qu’ils détiennent réellement la valeur numérique. Les types de conteneurs sont construits à partir de types d’unités avec le modèle unit_t. La plupart des types d’unités ont un type de conteneur correspondant qui porte le même nom suffixé par _t - par exemple, le type d’unité units :: meter correspond au type de conteneur units :: meter_t.

Chaque fois qu’une quantité spécifique d’une unité est utilisée (en tant que variable ou paramètre de méthode), ce sera une instance du type de conteneur. Par défaut, les types de conteneurs stockent la valeur réelle sous forme de double - les utilisateurs plus avancés peuvent changer cela en appelant le modèle unit_t manuellement.

Vous trouverez une liste complète des types d’unités et de conteneurs dans la documentation.

Création d’instances d’unités

Pour créer une instance d’une unité spécifique, nous créons une instance de son type de conteneur:

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

Alternativement, la librairie d’unités a des types de littéraux définis pour certains des types de conteneurs les plus courants. Ceux-ci peuvent être utilisés en conjonction avec l’inférence de type via auto pour définir une unité plus succinctement:

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

Les unités peuvent également être initialisées à l’aide d’une valeur d’un autre type de conteneur, en autant que les types peuvent être convertis entre eux. Par exemple, une valeur mètre_t peut être créée à partir d’une valeur pied_t.

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

En fait, tous les types de conteneurs représentant des types d’unités convertibles sont implicitement convertibles. Ainsi, ce qui suit est parfaitement légal:

units::meter_t distance = 6_ft;

En bref, nous pouvons utiliser n’importe quelle unité de longueur à la place de toute autre unité de longueur, n’importe où dans notre code; la librairie d’unités effectuera automatiquement la conversion correcte pour nous.

Exécution de l’arithmétique avec des unités

Les types de conteneurs prennent en charge toutes les opérations arithmétiques ordinaires de leur type de données sous-jacent, avec la condition supplémentaire que l’opération doit être dimensionnellement valide. Ainsi, l’addition doit toujours être effectuée sur deux types de conteneurs 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 multiplication peut être effectuée sur n’importe quelle paire de types de conteneurs et donne le type de conteneur d’une unité composée:

Note

Lorsqu’un calcul donne un type d’unité composé, ce type ne sera vérifié pour la validité au point d’opération que si le type de résultat est spécifié explicitement. Si auto est utilisé, cette vérification n’aura pas lieu. Par exemple, lorsque nous divisons la distance par le temps, nous pouvons vouloir nous assurer que le résultat est bien une vitesse (c’est-à-dire units :: meter_per_second_t). Si le type de retour est déclaré auto, cette vérification ne sera pas effectuée.

// 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

Les fonctions <cmath>

Certaines fonctions std (telles que clamp) sont conçues pour accepter tout type sur lequel les opérations arithmétiques peuvent être effectuées. Les quantités stockées en tant que types de conteneurs fonctionneront sans problème avec ces fonctions.

Cependant, d’autres fonctions std ne fonctionnent que sur les types numériques ordinaires (par exemple double). L’espace de noms units :: math de la librairie d’unités contient des « wrappers » pour plusieurs de ces fonctions qui acceptent les unités. Des exemples de telles fonctions incluent sqrt, pow, etc.

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

Retrait du « Unit Wrapper »

Pour convertir un type de conteneur en une valeur numérique brute, la méthode to <..> () peut être utilisée, où l’argument modèle est le type sous-jacent.

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

Exemple de la librairie d’unités dans le code WPILib

Plusieurs arguments pour des méthodes dans les nouvelles fonctionnalités de WPILib (ex. kinematics) utilisent la librairie d’unités. Voici un exemple d' échantillonnage d’une trajectoire.

// 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);

Certaines classes WPILib représentent des objets qui pourraient naturellement fonctionner avec plusieurs choix de types d’unités - par exemple, un profil de mouvement peut fonctionner sur une distance linéaire (par exemple mètres) ou angulaire (par exemple radians). Pour ces classes, le type d’unité est requis comme paramètre de modèle:

// 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};

Pour une documentation plus détaillée, veuillez visiter la page officielle GitHub pour la librairie des unités.