gtsam
Loading...
Searching...
No Matches
Quaternion.h
Go to the documentation of this file.
1/* ----------------------------------------------------------------------------
2
3 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4 * Atlanta, Georgia 30332-0415
5 * All Rights Reserved
6 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7
8 * See LICENSE for the license information
9
10 * -------------------------------------------------------------------------- */
11
17
18#pragma once
19
20#include <gtsam/base/Lie.h>
22#include <gtsam/base/concepts.h>
23#include <gtsam/geometry/SO3.h> // Logmap/Expmap derivatives
24
25#include <iostream>
26#include <limits>
27
28#define QUATERNION_TYPE Eigen::Quaternion<_Scalar,_Options>
29
30namespace gtsam {
31
32// Define traits
33template<typename _Scalar, int _Options>
34struct traits<QUATERNION_TYPE> {
35 typedef QUATERNION_TYPE ManifoldType;
36 typedef QUATERNION_TYPE Q;
37
38 typedef lie_group_tag structure_category;
39 typedef multiplicative_group_tag group_flavor;
40
43 static Q Identity() {
44 return Q::Identity();
45 }
46
50 inline constexpr static auto dimension = 3;
51 static int GetDimension(const Q& /* g */) { return 3; }
52 typedef OptionalJacobian<3, 3> ChartJacobian;
53 typedef Eigen::Matrix<_Scalar, 3, 1, _Options, 3, 1> TangentVector;
54
58 static Q Compose(const Q &g, const Q & h,
59 ChartJacobian Hg = {}, ChartJacobian Hh = {}) {
60 if (Hg) *Hg = h.toRotationMatrix().transpose();
61 if (Hh) *Hh = I_3x3;
62 return g * h;
63 }
64
65 static Q Between(const Q &g, const Q & h,
66 ChartJacobian Hg = {}, ChartJacobian Hh = {}) {
67 Q d = g.inverse() * h;
68 if (Hg) *Hg = -d.toRotationMatrix().transpose();
69 if (Hh) *Hh = I_3x3;
70 return d;
71 }
72
73 static Q Inverse(const Q &g,
74 ChartJacobian H = {}) {
75 if (H) *H = -g.toRotationMatrix();
76 return g.inverse();
77 }
78
80 static Q Expmap(const Eigen::Ref<const TangentVector>& omega,
81 ChartJacobian H = {}) {
82 using std::cos;
83 using std::sin;
84 if (H) *H = SO3::ExpmapDerivative(omega.template cast<double>());
85 _Scalar theta2 = omega.dot(omega);
86 if (theta2 > std::numeric_limits<_Scalar>::epsilon()) {
87 _Scalar theta = std::sqrt(theta2);
88 _Scalar ha = _Scalar(0.5) * theta;
89 Vector3 vec = (sin(ha) / theta) * omega;
90 return Q(cos(ha), vec.x(), vec.y(), vec.z());
91 } else {
92 // first order approximation sin(theta/2)/theta = 0.5
93 Vector3 vec = _Scalar(0.5) * omega;
94 return Q(1.0, vec.x(), vec.y(), vec.z());
95 }
96 }
97
99 static TangentVector Logmap(const Q& q, ChartJacobian H = {}) {
100 using std::atan;
101
102 // theta = 2 * atan2(|v|, w), omega = theta * v / |v|
103 // (C. Hertzberg et al., Information Fusion, 2011), written with atan()
104 // because it is measurably cheaper than atan2() and w >= 0 is arranged
105 // below anyway. Both theta and v/|v| are invariant to a positive rescaling
106 // of (w, v), so this stays correct when q is not of unit norm -- which
107 // matters because Rot3Q builds its quaternion straight from a rotation
108 // matrix supplied by the caller and never renormalizes.
109 //
110 // This also removes the need for the two Taylor branches the previous
111 // acos-based form required: atan is accurate as |v| -> 0, so the only
112 // degenerate case left is |v| == 0 exactly, i.e. the identity.
113 const _Scalar qw = q.w();
114 const _Scalar n = q.vec().norm();
115
116 TangentVector omega;
117 if (n == _Scalar(0)) {
118 omega = TangentVector::Zero();
119 } else {
120 // Branch exactly as the previous implementation did, so that the sign
121 // chosen at qw == 0 (an exact pi rotation, where both signs are valid
122 // logs) is unchanged. Note !(qw > 0) rather than qw < 0.
123 const bool negate = !(qw > _Scalar(0));
124 const _Scalar w = negate ? -qw : qw;
125 const _Scalar theta = (w > n) ? _Scalar(2) * atan(n / w)
126 : _Scalar(M_PI) - _Scalar(2) * atan(w / n);
127 omega = (negate ? -theta / n : theta / n) * q.vec();
128 }
129
130 if (H) *H = SO3::LogmapDerivative(omega.template cast<double>());
131 return omega;
132 }
133
134
135
136 static Matrix3 AdjointMap(const Q &g) {
137 return g.toRotationMatrix();
138 }
139
140 using LieAlgebra = Matrix3;
141
142 static Matrix3 Hat(const Vector3& v) {
143 return SO3::Hat(v);
144 }
145
146 static Vector3 Vee(const Matrix3& X) {
147 return SO3::Vee(X);
148 }
149
150 static Vector9 Vec(const Q& q, OptionalJacobian<9, 3> H = {}) {
151 return SO3(q.toRotationMatrix()).SO3::vec(H);
152 }
153
157
158 static TangentVector Local(const Q& g, const Q& h,
159 ChartJacobian H1 = {}, ChartJacobian H2 = {}) {
160 Q b = Between(g, h, H1, H2);
161 Matrix3 D_v_b;
162 TangentVector v = Logmap(b, (H1 || H2) ? &D_v_b : 0);
163 if (H1) *H1 = D_v_b * (*H1);
164 if (H2) *H2 = D_v_b * (*H2);
165 return v;
166 }
167
168 static Q Retract(const Q& g, const TangentVector& v,
169 ChartJacobian H1 = {}, ChartJacobian H2 = {}) {
170 Matrix3 D_h_v;
171 Q b = Expmap(v,H2 ? &D_h_v : 0);
172 Q h = Compose(g, b, H1, H2);
173 if (H2) *H2 = (*H2) * D_h_v;
174 return h;
175 }
176
180 static void Print(const Q& q, const std::string& str = "") {
181 if (str.size() == 0)
182 std::cout << "Eigen::Quaternion: ";
183 else
184 std::cout << str << " ";
185 std::cout << q.vec().transpose() << std::endl;
186 }
187 static bool Equals(const Q& q1, const Q& q2, double tol = 1e-8) {
188 return Between(q1, q2).vec().array().abs().maxCoeff() < tol;
189 }
191};
192
193typedef Eigen::Quaternion<double, Eigen::DontAlign> Quaternion;
194
195} // \namespace gtsam
Macros for Matrix constants to avoid excessive template instantiation.
Base class and basic functions for Lie types.
3*3 matrix representation of SO(3)
Global functions in a separate testing namespace.
Definition chartTesting.h:28
@ Logmap
Use the SE_2(3) NavState Logmap for every backend.
Definition PreintegrationParams.h:32
Group operator syntax flavors.
Definition Group.h:34
A manifold defines a space in which there is a notion of a linear tangent space that can be centered ...
Definition Group.h:37
tag to assert a type is a Lie group
Definition Lie.h:271
OptionalJacobian is an Eigen::Ref like class that can take be constructed using either a fixed size o...
Definition OptionalJacobian.h:40
static TangentVector Logmap(const Q &q, ChartJacobian H={})
We use our own Logmap, as there is a slight bug in Eigen.
Definition Quaternion.h:99
static Q Expmap(const Eigen::Ref< const TangentVector > &omega, ChartJacobian H={})
Exponential map, using the inlined code from Eigen's conversion from axis/angle.
Definition Quaternion.h:80
static TangentVector Vee(const MatrixNN &X)
static MatrixNN Hat(const TangentVector &xi)
static MatrixDD LogmapDerivative(const TangentVector &omega)=delete
static MatrixDD ExpmapDerivative(const TangentVector &omega)=delete