Coordinate frame semantics

Representing poses without confusion is notoriously difficult, and mistakes such as using a quantity expressed in the wrong coordinate frame have bogged down many a robotics project. wave_geometry provides built-in coordinate frame semantics checking. It extends Furgale’s recommended notation by putting semantic information into the language type system.

Some examples:

In math In English In wave_geometry
\({}_A\mathbf{p}_{BC}\) The vector from frame B to frame C, expressed in frame A Framed<Translationd, A, B, C> or TranslationFd<A, B, C>
\({}_A\mathbf\omega_{BC}\) The angular velocity of frame C with respect to frame B, expressed in frame A Framed<RelativeRotatiod, A, B, C> or RelativeRotationFd<A, B, C>
\(\mathbf{R}_{AB}\) The rotation between frame A and frame B, such that \({}_A\mathbf{p}_{BC}={}\mathbf{R}_{AB}({}_B\mathbf{p}_{BC})\) Framed<RotationMd, A, B> or RotationMFd<A, B>

Here, A, B and C are types which do nothing but represent a coordinate frame; more descriptive names can be used if desired.

Coordinate frame semantics checking can detect invalid operations at compile time. Consider this code:

struct BodyFrame;  
struct CameraFrame;  
struct WorldFrame;  
  
wave::RotationMFd<WorldFrame, BodyFrame> r1;  
wave::RotationMFd<CameraFrame, WorldFrame> r2;  
  
// Let's get the rotation between World and Camera (maybe)
wave::RotationMFd<WorldFrame, CameraFrame> result = r1 * r2;

This code has a mistake, but is saved by semantics checking. When we try to compile it, we get the following error message (from clang 5, not showing colours):

In file included from /.../example.cpp:1:
In file included from /.../wave/geometry/geometry.hpp:45:
/.../wave/geometry/src/geometry/op/Compose.hpp:22:5: error: static_assert failed "Adjacent frames do not match"
    static_assert(std::is_same<RightFrameOf<Lhs>, LeftFrameOf<Rhs>>(),
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/.../example.cpp:31:60: note: in instantiation of template class 'wave::Compose<wave::Framed<wave::MatrixRotation<Eigen::Matrix<double, 3, 3, 0, 3, 3> >, WorldFrame, BodyFrame>, wave::Framed<wave::MatrixRotation<Eigen::Matrix<double, 3, 3, 0, 3, 3> >, CameraFrame, WorldFrame> >' requested here
    wave::RotationMFd<WorldFrame, CameraFrame> result = r1 * r2;
                                                           ^

The corrected code compiles successfully.

// Let's get the rotation between World and Camera (fixed)  
wave::RotationMFd<WorldFrame, CameraFrame> result = r1 * inverse(r2);

In addition to making code safer and more readable, using coordinate frame semantics actually makes wave_geometry’s autodiff faster by providing extra type information.