|
| 1 | + |
| 2 | +# A manifold geometry library for robotics |
| 3 | + |
| 4 | +**wave_geometry** is a header-only C++ library for working with rotations and transformations in robotics and computer vision applications. It differs from similar libraries by offering: |
| 5 | + |
| 6 | +* Fast operations using expression templates |
| 7 | +* Fast on-manifold automatic differentiation |
| 8 | +* Compile-time coordinate frame semantics checking |
| 9 | + |
| 10 | +The code is available under the [MIT License](LICENSE). |
| 11 | + |
| 12 | +## Features |
| 13 | + |
| 14 | +### Manifold operations |
| 15 | + |
| 16 | +`wave_geometry` includes operations on SO(3), the Lie group of 3D rotations, and SE(3), the group of 3D rigid transformations. |
| 17 | + |
| 18 | +#### Supported operations |
| 19 | + |
| 20 | +| Operation | | Code | |
| 21 | +| :--- | :---: | :---: | |
| 22 | +| Sum | <img src="https://latex.codecogs.com/png.latex?\mathbf{v}+\mathbf{v}" title="\mathbf{v}+\mathbf{v}" /> | `v + v` | |
| 23 | +| Difference | <img src="https://latex.codecogs.com/png.latex?\mathbf{v}-\mathbf{v}" title="\mathbf{v}-\mathbf{v}" /> | `v - v` | |
| 24 | +| Negative | <img src="https://latex.codecogs.com/png.latex?-\mathbf&space;v" title="-\mathbf v" /> | `-v` | |
| 25 | +| Composition | <img src="https://latex.codecogs.com/png.latex?\mathbf&space;\Phi&space;\circ&space;\mathbf&space;\Phi" title="\mathbf \Phi \circ \mathbf \Phi" /> | `R * R` | |
| 26 | +| Inverse | <img src="https://latex.codecogs.com/png.latex?\mathbf&space;\Phi^{-1}" title="\mathbf \Phi^{-1}" /> | `inverse(R)` | |
| 27 | +| Coordinate map | <img src="https://latex.codecogs.com/png.latex?\mathbf&space;\Phi&space;(\mathbf&space;p)" title="\mathbf \Phi (\mathbf p)" /> | `R * p` | |
| 28 | +| Exponential map | <img src="https://latex.codecogs.com/png.latex?\exp(\mathbf\varphi)" title="\exp(\mathbf \varphi)" /> | `exp(w)` | |
| 29 | +| Logarithmic map | <img src="https://latex.codecogs.com/png.latex?\log(\mathbf&space;\Phi)" title="\log(\mathbf \Phi)" /> | `log(R)` | |
| 30 | +| Manifold plus | <img src="https://latex.codecogs.com/png.latex?\mathbf\Phi\boxplus\mathbf\varphi=\exp(\mathbf\varphi)\circ\mathbf\Phi" title="\mathbf\Phi \boxplus \mathbf\varphi = \exp(\mathbf\varphi) \circ \mathbf\Phi" /> | `R + w` | |
| 31 | +| Manifold minus | <img src="https://latex.codecogs.com/png.latex?\mathbf\Phi_1\boxminus\mathbf\Phi_2=\log(\mathbf\Phi_1\circ\mathbf\Phi_2^{-1})" title="\mathbf\Phi_1 \boxminus \mathbf\Phi_2 = \log(\mathbf\Phi_1 \circ \mathbf\Phi_2^{-1})" /> | `R - R` | |
| 32 | + |
| 33 | +Above, <img src="https://latex.codecogs.com/png.latex?\mathbf\Phi" alt="Phi" /> or `R` represents a Lie group element, <img src="https://latex.codecogs.com/png.latex?\mathbf\varphi" alt="small phi" /> or `w` represents a Lie algebra element, <img src="https://latex.codecogs.com/png.latex?\mathbf{p}" alt="p" /> or `p` represents a translation, and <img src="https://latex.codecogs.com/png.latex?\mathbf{v}" alt="v" /> or `v` represents any element of R^3, so(3) or se(3). |
| 34 | + |
| 35 | +### Automatic differentiation |
| 36 | + |
| 37 | +Any expression can be differentiated with respect to its variables: |
| 38 | + |
| 39 | +```cpp |
| 40 | +wave::RotationMd R = wave::RotationMd::Random(); |
| 41 | +wave::Translationd p1 = wave::Translationd::Random(); |
| 42 | +wave::Translationd p2 = R * p1; |
| 43 | +Eigen::Matrix3d J_p2_wrt_R = (R * p1).jacobian(R); |
| 44 | +``` |
| 45 | + |
| 46 | +Above, `J_p2_wrt_R` is the 3x3 Jacobian of `p2 = R * p1` with respect to small changes in `R`. `wave_geometry` computes *local Jacobians*, which are independent of the choice of parametrization for `R` (e.g., rotation matrix or quaternion), and are useful for on-manifold parametrization. |
| 47 | + |
| 48 | +It is possible (and more efficient) to combine evaluation and multiple Jacobian calculations: |
| 49 | + |
| 50 | +```cpp |
| 51 | +// Using C++17 |
| 52 | +auto [p2, J_p2_wrt_R, J_p2_wrt_p1] = (R * p1).evalWithJacobians(R, p1); |
| 53 | + |
| 54 | +// Or, using C++11 |
| 55 | +wave::Translationd p2; |
| 56 | +Eigen::Matrix3d J_p2_wrt_R, J_p2_wrt_p1; |
| 57 | +std::tie(p2, J_p2_wrt_R, J_p2_wrt_p1) = (R * p1).evalWithJacobians(R, p1); |
| 58 | +``` |
| 59 | +
|
| 60 | +We can also get all Jacobians at once by providing no arguments: |
| 61 | +
|
| 62 | +```cpp |
| 63 | +auto [p2, J_p2_wrt_R, J_p2_wrt_p1] = (R * p1).evalWithJacobians(); |
| 64 | +``` |
| 65 | + |
| 66 | +Currently, the call `.evalWithJacobians(R, p1)` uses forward-mode automatic differentiation while `.evalWithJacobians()` uses reverse mode; however, future versions of `wave_geometry` may simply choose the fastest mode for the arguments given. |
| 67 | + |
| 68 | +`wave_geometry`'s expression template-based autodiff algorithm produces efficient code which runs nearly as fast as (or in some cases, just as fast as) hand-optimized code for manually-derived derivatives. |
| 69 | + |
| 70 | +## Coordinate frame semantics |
| 71 | + |
| 72 | +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](http://paulfurgale.info/news/2014/6/9/representing-robot-pose-the-good-the-bad-and-the-ugly) by putting semantic information into the language type system. |
| 73 | + |
| 74 | +Some examples: |
| 75 | + |
| 76 | +| In math | In English | In wave_geometry | |
| 77 | +| :--- | :--- | :--- | |
| 78 | +| <img src="https://latex.codecogs.com/png.latex?{}_A\mathbf{p}_{BC}" title="{}_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>`| |
| 79 | +| <img src="https://latex.codecogs.com/png.latex?{}_A\mathbf\omega_{BC}" title="{}_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>` | |
| 80 | +| <img src="https://latex.codecogs.com/png.latex?\mathbf{R}_{AB}" title="\mathbf{R}_{AB}" /> | The rotation between frame A and frame B, such that <img src="https://latex.codecogs.com/png.latex?{}_A\mathbf{p}_{BC}={}\mathbf{R}_{AB}({}_B\mathbf{p}_{BC})" title="{}_A\mathbf{p}_{BC}={}\mathbf{R}_{AB}({}_B\mathbf{p}_{BC})" /> | `Framed<RotationMd, A, B>` or `RotationMFd<A, B>` | |
| 81 | + |
| 82 | +Here, `A`, `B` and `C` are types which do nothing but represent a coordinate frame; more descriptive names can be used if desired. |
| 83 | + |
| 84 | +Coordinate frame semantics checking can detect invalid operations *at compile time*. Consider this code: |
| 85 | +```cpp |
| 86 | +struct BodyFrame; |
| 87 | +struct CameraFrame; |
| 88 | +struct WorldFrame; |
| 89 | + |
| 90 | +wave::RotationMFd<WorldFrame, BodyFrame> r1; |
| 91 | +wave::RotationMFd<CameraFrame, WorldFrame> r2; |
| 92 | + |
| 93 | +// Let's get the rotation between World and Camera (maybe) |
| 94 | +wave::RotationMFd<WorldFrame, CameraFrame> result = r1 * r2; |
| 95 | +``` |
| 96 | +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): |
| 97 | +``` |
| 98 | +In file included from /.../example.cpp:1: |
| 99 | +In file included from /.../wave/geometry/geometry.hpp:45: |
| 100 | +/.../wave/geometry/src/geometry/op/Compose.hpp:22:5: error: static_assert failed "Adjacent frames do not match" |
| 101 | + static_assert(std::is_same<RightFrameOf<Lhs>, LeftFrameOf<Rhs>>(), |
| 102 | + ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 103 | +/.../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 |
| 104 | + wave::RotationMFd<WorldFrame, CameraFrame> result = r1 * r2; |
| 105 | + ^ |
| 106 | +``` |
| 107 | + |
| 108 | +The corrected code compiles successfully. |
| 109 | +``` |
| 110 | +// Let's get the rotation between World and Camera (fixed) |
| 111 | +wave::RotationMFd<WorldFrame, CameraFrame> result = r1 * inverse(r2); |
| 112 | +``` |
| 113 | + |
| 114 | +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. |
| 115 | + |
| 116 | + |
| 117 | +### Supported parametrizations |
| 118 | +| Group | Parametrization | Class template | Alias for `double` as scalar | |
| 119 | +| :--- | :--- | :--- | :--- | |
| 120 | +| SO(3) rotation | 3x3 Matrix | `MatrixRotation<T>` | `RotationMd` | |
| 121 | +| | Quaternion | `QuaternionRotation<T>` | `RotationQd` | |
| 122 | +| | Angle-axis | `AngleAxisRotation<T>` | `RotationAd` | |
| 123 | +| SE(3) transformation | 4x4 homogeneous matrix |`MatrixRigidTransform<T>`| `RigidTransformMd` | |
| 124 | +| | 7-vector (quaternion + translation) | `CompactRigidTransform` | `RigidTransformQd` | |
| 125 | +| R^3 translation | 3-vector | `Translation<T>` | `Translationd` | |
| 126 | +| so(3) diff. rotation | 3-vector | `RelativeRotation<T>` | `RelativeRotationd` | |
| 127 | +| se(3) diff. transformation | 6-vector (angular + linear) | `Twist<T>` | `Twistd` | |
| 128 | + |
| 129 | +Note the template parameter `<T>` is not a scalar type, but the full type of the underlying Eigen class (for example, `Eigen::Vector3d` or `Map<Matrix<double, 3, 3, RowMajor>>`). Thus `wave_geometry` classes easily work with arbitrary Eigen Maps, memory layouts, and block expressions. |
| 130 | + |
| 131 | +Quaternions use the conventions of `Eigen::Quaternion`: Hamilton product, storage order `x,y,z,w` (despite different order in the constructor), and homomorphic quaternion-matrix conversion: `C(q1)*C(q2) = C(q1*q2)`. |
| 132 | + |
| 133 | + |
| 134 | +## How to install |
| 135 | + |
| 136 | +`wave_geometry` is a header-only library, meaning no compilation is required to use it in your project. Put the `include` directory into your compiler's search path, and write |
| 137 | + |
| 138 | +``` |
| 139 | +#include <wave/geometry/geometry.hpp> |
| 140 | +``` |
| 141 | + |
| 142 | +CMake install and link commands will be added soon. |
| 143 | + |
| 144 | +### Dependencies |
| 145 | + |
| 146 | +`wave_geometry` requires: |
| 147 | + * an existing installation of [Eigen](http://eigen.tuxfamily.org) 3.3-beta2 or above |
| 148 | + * an existing installation of Boost (only the header-only Optional library is used) |
| 149 | + * a compiler supporting C++11 |
| 150 | + |
| 151 | +### Development status |
| 152 | + |
| 153 | +`wave_geometry` is in the initial development stage and the API may change at any time. |
| 154 | + |
| 155 | + |
| 156 | +## More information |
| 157 | +The library is described in a conference paper: |
| 158 | +``` |
| 159 | +@inproceedings{koppel2018manifold, |
| 160 | + title={Manifold Geometry with Fast Automatic Derivatives and Coordinate Frame Semantics Checking in {C++}}, |
| 161 | + author={Koppel, Leonid and Waslander, Steven L.}, |
| 162 | + booktitle={15th Conference on Computer and Robot Vision (CRV)}, |
| 163 | + year={2018}, |
| 164 | + note = {to be published} |
| 165 | +} |
| 166 | +``` |
| 167 | + |
0 commit comments