MIRA
Math

# Overview

MIRA integrates Eigen and its datatypes such as Eigen::Matrix, etc. It provides several methods and tools for frequently needed maths, such as angles and rotations. The most important ones are described below. Others can be found in the API Reference.

# Angles

The <math/Angle.h> header provides classes in different flavors to represent angles. Degree represents angles using degrees, hence its values range from 0 to 360:

Degree<float> angle(45.0f); // create angle of 45 degree

Radian represents angles using radians from 0 to 2pi:

Radian<float> angle(pi<float>()/2.0f); // create angle of pi/2

Since the same angle can be represented by different values (e.g. by adding multiples of 2pi), the angle classes automatically normalize the angle values after operations like additions, multiplications with scalars etc. Values that become larger then 360 or 2pi and values that become smaller than 0 are wrapped to make sure that the values will always stay within the given ranges.

Degree<float> angle(390.0f); // create angle of 30 degree (30=390-360)
Degree<float> angle(-10.0f); // create angle of 350 degree (350=360-10)

If you prefer to use angles in the symmetric interval -180 to 180, you can use the "Signed" counterparts of the above classes. SignedDegree represents signed angles from -180 to 180 degrees and SignedRadian represents signed angles from -pi to pi.

SignedDegree<float> angle(-10.0f); // create angle of -10 degree

All of these classes can be used with different floating point datatypes:

Degree<float> angle(1.2345f); // single precision angle
Radian<double> angle(1.23456789); // double precision angle

The Degree classes can also be used with integer datatypes. Radian cannot be used with integers since the precision is not sufficient:

Degree<int> angle(45); // OK
Radian<int> angle(2); // COMPILER ERROR: integer radians make no sense

For convenience, there are several typedefs to shorten the declaration, e.g: Degreei, Degreef, Degreed, Radianf, Radiand.

The different angle types are interchangeable with each other, conversions will be done automatically between them:

Degreef angle = Radianf(pi<float>()); // will convert pi to 180 degree
Degreei angle = Radianf(pi<float>()/2.0f); // will convert pi/2 to 90 degree

Please note that conversions between angles of different types (e.g. float to int or double to float) need to be made explicitly, while conversions between different angle representations (e.g. degree to radian) are done implicitly:

Radianf a = v+Degreef(90.0f); // OK, degree will be converted to radian implicitly
Radiand b = v+Radiand(0.2345); // COMPILER ERROR, v is Radianf (float) while the rest is double and we need explicit conversion
Radiand c = Radiand(v) + Radiand(0.2345); // OK, v is explicitly casted into Radiand (double)

Also note that for conversions from floating point angles to integer type angles the same rules apply that are used for conversions between floating point numbers and integer values: The number is truncated and the decimal part is dropped, i.e. the value will NOT be rounded.

As shown in the above example, the angle types support different operators, like addition, subtraction, multiplication, division and comparison operators. For more information, please refer to the API documentation.

An often used computation with angles is the calculation of the difference between two angles.

Degreei d1 = Degreei(350) - Degreei(10); // will yield 340 degrees (since 350-10=340)
Degreei d2 = Degreei(10) - Degreei(350); // will yield 20 degrees (since 10-350=-340 corresponding to 20 degrees)
SignedDegreei d3 = Degreei(350) - Degreei(10); // will yield -20 degrees (since 350-10=340 corresponding to 340 degrees)
SignedDegreei d4 = Degreei(10) - Degreei(350); // will yield 20 degrees (since 10-350=20 corresponding to 20 degrees)

Beside the subtraction operator, the angle classes provide a smallestDifference() method. This method takes into account that there are always two differences between two angles (remember that an angle is defined in a cyclic interval and you can go into two different directions to reach any different angle). The smallestDifference() method will always return the smallest of these two angles. Also note, that smallestDifference() returns a signed angle:

SignedDegreei d1 = Degreei(350).smallestDifference(Degreei(10)); // will yield -20 degrees (since 350-10=340 corresponding to 340 degrees)
SignedDegreei d2 = Degreei(10).smallestDifference(Degreei(350)); // will yield 20 degrees (since 10-350=20 corresponding to 20 degrees)

Beside the above Degree and Radian classes there exists an Angle and SignedAngle class. Both are almost the same as Radian and SignedRadian, i.e. they also store the angle using radians. The only difference is that the angle values are serialized using degrees. This allows a more user-friendly handling of angles e.g. in config files, while internally they are represented in radians.

The table below gives an overview of all angle types:

angle classunitrangeunit for serialization
Degreedegree[0, 360)degree
SignedDegreedegree[-180, 180)degree

# Rotations

2D rotations are represented using a single angle. Usually, it describes a rotation within the xy-plane.

The standard representation for 3D rotations are Quaternions. Quaternions have several desirable properties:

• they can be easily constructed using a given axis and angle
• two quaternions can be efficiently concatenated
• quaternions can be interpolated using spherical linear interpolation
• they do not suffer from the gimbal lock problem that occurs with yaw/pitch/roll angles
• they are a more compact representation compared to rotation matrices

Alternatively, rotations can also be represented using a 3x3 rotation matrix or using yaw/pitch/roll angles. To convert between those three representations, several methods are provided in the <math/YawPitchRoll.h> header:

Conversions between quaternions and rotation matrices are provided directly by Eigen:

• Rotation matrix -> Quaternion
Eigen::Quaternionf q (matrix);
• Quaternion -> Rotation matrix
Eigen::Matrix3f R = q.toRotationMatrix();

When using Yaw/Pitch/Roll angles, the following rotation order has to be taken into account:

1. Rotate around the z-axis (yaw)
2. Rotate around the y-axis (pitch)
3. Rotate around the x-axis (roll)

where:

• the x-axis points forward
• the y-axis points left
• the z-axis points upwards

# Matrix Input/Output and Formatting

For input and output of formatted matrices, the format() method is provided by <math/EigenFormat.h>. It allows to read and write matrices from streams using different formats. Currently, the following matrix formats are supported:

• Matlab format
[1, 2, 3;
4, 5, 6;
7, 8, 9]
• Python format
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
• Eigen format
1 2 3
4 5 6
7 8 9
• Clean format
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]

To write a matrix in Matlab format, use:

std::cout << format(matrix, EigenFormat::matlab());

To parse a matrix in Python format, use:

std::cin >> format(matrix, EigenFormat::python());

If an error occurs during parsing, an XIO Exception will be thrown.

# Random Number Generators

MIRA provides different ways to produce random numbers:

1. using the MIRA_RANDOM macro
2. by using the boost::random library and several helper classes such as RandomGenerator<>

Both methods have different pros and cons:

MIRA_RANDOMboost::random
Pros
• easy to use
• up to 2 times faster
• full control of the random number sequence for each random generator
• deterministic behaviour of randomized algorithms, if the same seed is used
Cons
• all user share the same random generator singleton
• non-deterministic random number sequence in multi-threaded applications even if the same seed is used
• more difficult to use since a separate member must be created for each random generator

## MIRA_RANDOM

MIRA_RANDOM can be used whenever you need a simple random number and when you do not have any requirements to the random number sequence and when you do not need special random distributions.

Sampling from a uniform distribution:

#include <math/Random.h>
...
// sample uniform random number between 10 and 30
int rndnumber = MIRA_RANDOM.uniform(10,30);

Sampling from a univariate normal distribution:

#include <math/Random.h>
...
// sample normal distributed random number with sigma=2
float rndnumber = MIRA_RANDOM.normal(2.0);

## boost::random

The boost::random library provides different random distributions and different random generator engines.

To simplify their usage, MIRA provides the RandomGenerator<> template.

A random generator with a certain random distribution, e.g. boost::boost::uniform_01 can be used as follows:

RandomGenerator<boost::uniform_01<float>> rnd;
// sample 100 random numbers
for(int i=0; i<100; ++i)
double random_numer=rnd();

## UniformRandomGenerator

MIRA provides the UniformRandomGenerator class - a random generator that samples random numbers within the interval that is passed to the contructor. The UniformRandomGenerator template class can be used with floating point and integer datatypes. For integer types, the sampled values are within the interval [vmin,vmax]. For floating point types, the sampled values are within the interval [vmin,vmax).

Example:

// create uniform random number generator for numbers between [1.0f,2.0f)
UniformRandomGenerator<float> rnd(1.0f,2.0f);
// draw a sample:
float sample = rnd();

## NormalRandomGenerator

MIRA also provides NormalRandomGenerator, a random generator for drawing samples from zero-mean univariate or multivariate normal distributions. The dimension of the generated samples are specified as template parameter D (univariate distribution if D==1 or multivariate if D>1).

For the multivariate distributions, the covariance matrix must be specified via the constructor or using the setSigma method.

Example:

Eigen::Matrix2f sigma;
sigma << 2.0f, 1.0f,
1.0f, 3.0f;
NormalRandomGenerator<2> rnd(sigma);
// draw a sample:
Eigen::Vector2f sample = rnd();
// set a different covariance matrix:
sigma << 3.0f, 0.1f,
0.1f, 5.0f;
rnd->setSigma(sigma);
// draw a sample:
sample = rnd();

To produce non-zero mean samples you can simply add the mean to the returned samples.

# Auxiliary Methods and Tools

• isApprox()
Checks, whether two values are approximatly the same.
• lerp()
Linear interpolation of different types.
• modulo()
Computes the modulo for different datatypes (no matter if integer or floating point).
• normcdf()
Computes the normal cumulative distribution function.
• RandomGenerator
Class for generating random numbers.
• saturate()
Saturate a value by limiting it to a certain interval.
• SchmittTriggerHysteresis
Realizes a Schmitt trigger.
• square()
Computes the square of the given value.
• truncate()
Truncates a floating point value to a given number of decimals
• TPower
Computes the power base^exponent at compile time.