Skip to content

Commit bb7b6b3

Browse files
authored
Merge pull request #1930 from borglab/feature/NavStateGroup
Endow NavState with group operations
2 parents 3af5360 + f7a678e commit bb7b6b3

File tree

5 files changed

+779
-100
lines changed

5 files changed

+779
-100
lines changed

gtsam/geometry/Pose3.cpp

Lines changed: 67 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,10 @@
2121

2222
#include <cmath>
2323
#include <iostream>
24-
#include <limits>
2524
#include <string>
2625

2726
namespace gtsam {
2827

29-
using std::vector;
30-
3128
/** instantiate concept checks */
3229
GTSAM_CONCEPT_POSE_INST(Pose3)
3330

@@ -167,21 +164,23 @@ Pose3 Pose3::interpolateRt(const Pose3& T, double t) const {
167164
/* ************************************************************************* */
168165
/** Modified from Murray94book version (which assumes w and v normalized?) */
169166
Pose3 Pose3::Expmap(const Vector6& xi, OptionalJacobian<6, 6> Hxi) {
170-
if (Hxi) *Hxi = ExpmapDerivative(xi);
171-
172-
// get angular velocity omega and translational velocity v from twist xi
173-
Vector3 omega(xi(0), xi(1), xi(2)), v(xi(3), xi(4), xi(5));
174-
175-
Rot3 R = Rot3::Expmap(omega);
176-
double theta2 = omega.dot(omega);
177-
if (theta2 > std::numeric_limits<double>::epsilon()) {
178-
Vector3 t_parallel = omega * omega.dot(v); // translation parallel to axis
179-
Vector3 omega_cross_v = omega.cross(v); // points towards axis
180-
Vector3 t = (omega_cross_v - R * omega_cross_v + t_parallel) / theta2;
181-
return Pose3(R, t);
182-
} else {
183-
return Pose3(R, v);
167+
// Get angular velocity omega and translational velocity v from twist xi
168+
const Vector3 w = xi.head<3>(), v = xi.tail<3>();
169+
170+
// Compute rotation using Expmap
171+
Rot3 R = Rot3::Expmap(w);
172+
173+
// Compute translation and optionally its Jacobian in w
174+
Matrix3 Q;
175+
const Vector3 t = ExpmapTranslation(w, v, Hxi ? &Q : nullptr, R);
176+
177+
if (Hxi) {
178+
const Matrix3 Jw = Rot3::ExpmapDerivative(w);
179+
*Hxi << Jw, Z_3x3,
180+
Q, Jw;
184181
}
182+
183+
return Pose3(R, t);
185184
}
186185

187186
/* ************************************************************************* */
@@ -240,55 +239,68 @@ Vector6 Pose3::ChartAtOrigin::Local(const Pose3& pose, ChartJacobian Hpose) {
240239
}
241240

242241
/* ************************************************************************* */
243-
Matrix3 Pose3::ComputeQforExpmapDerivative(const Vector6& xi, double nearZeroThreshold) {
242+
Matrix3 Pose3::ComputeQforExpmapDerivative(const Vector6& xi,
243+
double nearZeroThreshold) {
244+
Matrix3 Q;
244245
const auto w = xi.head<3>();
245246
const auto v = xi.tail<3>();
246-
const Matrix3 V = skewSymmetric(v);
247-
const Matrix3 W = skewSymmetric(w);
248-
249-
Matrix3 Q;
247+
ExpmapTranslation(w, v, Q, {}, nearZeroThreshold);
248+
return Q;
249+
}
250250

251-
#ifdef NUMERICAL_EXPMAP_DERIV
252-
Matrix3 Qj = Z_3x3;
253-
double invFac = 1.0;
254-
Q = Z_3x3;
255-
Matrix3 Wj = I_3x3;
256-
for (size_t j=1; j<10; ++j) {
257-
Qj = Qj*W + Wj*V;
258-
invFac = -invFac/(j+1);
259-
Q = Q + invFac*Qj;
260-
Wj = Wj*W;
251+
/* ************************************************************************* */
252+
Vector3 Pose3::ExpmapTranslation(const Vector3& w, const Vector3& v,
253+
OptionalJacobian<3, 3> Q,
254+
const std::optional<Rot3>& R,
255+
double nearZeroThreshold) {
256+
const double theta2 = w.dot(w);
257+
bool nearZero = (theta2 <= nearZeroThreshold);
258+
259+
if (Q) {
260+
const Matrix3 V = skewSymmetric(v);
261+
const Matrix3 W = skewSymmetric(w);
262+
const Matrix3 WVW = W * V * W;
263+
const double theta = w.norm();
264+
265+
if (nearZero) {
266+
static constexpr double one_sixth = 1. / 6.;
267+
static constexpr double one_twenty_fourth = 1. / 24.;
268+
static constexpr double one_one_hundred_twentieth = 1. / 120.;
269+
270+
*Q = -0.5 * V + one_sixth * (W * V + V * W - WVW) -
271+
one_twenty_fourth * (W * W * V + V * W * W - 3 * WVW) +
272+
one_one_hundred_twentieth * (WVW * W + W * WVW);
273+
} else {
274+
const double s = sin(theta), c = cos(theta);
275+
const double theta3 = theta2 * theta, theta4 = theta3 * theta,
276+
theta5 = theta4 * theta;
277+
278+
// Invert the sign of odd-order terms to have the right Jacobian
279+
*Q = -0.5 * V + (theta - s) / theta3 * (W * V + V * W - WVW) +
280+
(1 - theta2 / 2 - c) / theta4 * (W * W * V + V * W * W - 3 * WVW) -
281+
0.5 *
282+
((1 - theta2 / 2 - c) / theta4 -
283+
3 * (theta - s - theta3 / 6.) / theta5) *
284+
(WVW * W + W * WVW);
285+
}
261286
}
262-
#else
263-
// The closed-form formula in Barfoot14tro eq. (102)
264-
double phi = w.norm();
265-
const Matrix3 WVW = W * V * W;
266-
if (std::abs(phi) > nearZeroThreshold) {
267-
const double s = sin(phi), c = cos(phi);
268-
const double phi2 = phi * phi, phi3 = phi2 * phi, phi4 = phi3 * phi,
269-
phi5 = phi4 * phi;
270-
// Invert the sign of odd-order terms to have the right Jacobian
271-
Q = -0.5 * V + (phi - s) / phi3 * (W * V + V * W - WVW) +
272-
(1 - phi2 / 2 - c) / phi4 * (W * W * V + V * W * W - 3 * WVW) -
273-
0.5 * ((1 - phi2 / 2 - c) / phi4 - 3 * (phi - s - phi3 / 6.) / phi5) *
274-
(WVW * W + W * WVW);
287+
288+
// TODO(Frank): this threshold is *different*. Why?
289+
if (nearZero) {
290+
return v + 0.5 * w.cross(v);
275291
} else {
276-
Q = -0.5 * V + 1. / 6. * (W * V + V * W - WVW) -
277-
1. / 24. * (W * W * V + V * W * W - 3 * WVW) +
278-
1. / 120. * (WVW * W + W * WVW);
292+
Vector3 t_parallel = w * w.dot(v); // translation parallel to axis
293+
Vector3 w_cross_v = w.cross(v); // translation orthogonal to axis
294+
Rot3 rotation = R.value_or(Rot3::Expmap(w));
295+
Vector3 t = (w_cross_v - rotation * w_cross_v + t_parallel) / theta2;
296+
return t;
279297
}
280-
#endif
281-
282-
return Q;
283298
}
284299

285300
/* ************************************************************************* */
286301
Matrix6 Pose3::ExpmapDerivative(const Vector6& xi) {
287-
const Vector3 w = xi.head<3>();
288-
const Matrix3 Jw = Rot3::ExpmapDerivative(w);
289-
const Matrix3 Q = ComputeQforExpmapDerivative(xi);
290302
Matrix6 J;
291-
J << Jw, Z_3x3, Q, Jw;
303+
Expmap(xi, J);
292304
return J;
293305
}
294306

gtsam/geometry/Pose3.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,25 @@ class GTSAM_EXPORT Pose3: public LieGroup<Pose3, 6> {
217217
* (see Chirikjian11book2, pg 44, eq 10.95.
218218
* The closed-form formula is identical to formula 102 in Barfoot14tro where
219219
* Q_l of the SE3 Expmap left derivative matrix is given.
220+
* This is the Jacobian of ExpmapTranslation and computed there.
220221
*/
221222
static Matrix3 ComputeQforExpmapDerivative(
222223
const Vector6& xi, double nearZeroThreshold = 1e-5);
223224

225+
/**
226+
* Compute the translation part of the exponential map, with derivative.
227+
* @param w 3D angular velocity
228+
* @param v 3D velocity
229+
* @param Q Optionally, compute 3x3 Jacobian wrpt w
230+
* @param R Optionally, precomputed as Rot3::Expmap(w)
231+
* @param nearZeroThreshold threshold for small values
232+
* Note Q is 3x3 bottom-left block of SE3 Expmap right derivative matrix
233+
*/
234+
static Vector3 ExpmapTranslation(const Vector3& w, const Vector3& v,
235+
OptionalJacobian<3, 3> Q = {},
236+
const std::optional<Rot3>& R = {},
237+
double nearZeroThreshold = 1e-5);
238+
224239
using LieGroup<Pose3, 6>::inverse; // version with derivative
225240

226241
/**

gtsam/navigation/NavState.cpp

Lines changed: 170 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,13 @@ Vector3 NavState::bodyVelocity(OptionalJacobian<3, 9> H) const {
7777
}
7878

7979
//------------------------------------------------------------------------------
80-
Matrix7 NavState::matrix() const {
80+
Matrix5 NavState::matrix() const {
8181
Matrix3 R = this->R();
82-
Matrix7 T;
83-
T << R, Z_3x3, t(), Z_3x3, R, v(), Vector6::Zero().transpose(), 1.0;
82+
83+
Matrix5 T = Matrix5::Identity();
84+
T.block<3, 3>(0, 0) = R;
85+
T.block<3, 1>(0, 3) = t_;
86+
T.block<3, 1>(0, 4) = v_;
8487
return T;
8588
}
8689

@@ -103,6 +106,161 @@ bool NavState::equals(const NavState& other, double tol) const {
103106
&& equal_with_abs_tol(v_, other.v_, tol);
104107
}
105108

109+
//------------------------------------------------------------------------------
110+
NavState NavState::inverse() const {
111+
Rot3 Rt = R_.inverse();
112+
return NavState(Rt, Rt * (-t_), Rt * -(v_));
113+
}
114+
115+
//------------------------------------------------------------------------------
116+
NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> Hxi) {
117+
// Get angular velocity w, translational velocity v, and acceleration a
118+
Vector3 w = xi.head<3>();
119+
Vector3 rho = xi.segment<3>(3);
120+
Vector3 nu = xi.tail<3>();
121+
122+
// Compute rotation using Expmap
123+
Rot3 R = Rot3::Expmap(w);
124+
125+
// Compute translations and optionally their Jacobians
126+
Matrix3 Qt, Qv;
127+
Vector3 t = Pose3::ExpmapTranslation(w, rho, Hxi ? &Qt : nullptr, R);
128+
Vector3 v = Pose3::ExpmapTranslation(w, nu, Hxi ? &Qv : nullptr, R);
129+
130+
if (Hxi) {
131+
const Matrix3 Jw = Rot3::ExpmapDerivative(w);
132+
*Hxi << Jw, Z_3x3, Z_3x3,
133+
Qt, Jw, Z_3x3,
134+
Qv, Z_3x3, Jw;
135+
}
136+
137+
return NavState(R, t, v);
138+
}
139+
140+
//------------------------------------------------------------------------------
141+
Vector9 NavState::Logmap(const NavState& state, OptionalJacobian<9, 9> Hstate) {
142+
if (Hstate) *Hstate = LogmapDerivative(state);
143+
144+
const Vector3 phi = Rot3::Logmap(state.rotation());
145+
const Vector3& p = state.position();
146+
const Vector3& v = state.velocity();
147+
const double t = phi.norm();
148+
if (t < 1e-8) {
149+
Vector9 log;
150+
log << phi, p, v;
151+
return log;
152+
153+
} else {
154+
const Matrix3 W = skewSymmetric(phi / t);
155+
156+
const double Tan = tan(0.5 * t);
157+
const Vector3 Wp = W * p;
158+
const Vector3 Wv = W * v;
159+
const Vector3 rho = p - (0.5 * t) * Wp + (1 - t / (2. * Tan)) * (W * Wp);
160+
const Vector3 nu = v - (0.5 * t) * Wv + (1 - t / (2. * Tan)) * (W * Wv);
161+
Vector9 log;
162+
// Order is ω, p, v
163+
log << phi, rho, nu;
164+
return log;
165+
}
166+
}
167+
168+
//------------------------------------------------------------------------------
169+
Matrix9 NavState::AdjointMap() const {
170+
const Matrix3 R = R_.matrix();
171+
Matrix3 A = skewSymmetric(t_.x(), t_.y(), t_.z()) * R;
172+
Matrix3 B = skewSymmetric(v_.x(), v_.y(), v_.z()) * R;
173+
// Eqn 2 in Barrau20icra
174+
Matrix9 adj;
175+
adj << R, Z_3x3, Z_3x3, A, R, Z_3x3, B, Z_3x3, R;
176+
return adj;
177+
}
178+
179+
//------------------------------------------------------------------------------
180+
Vector9 NavState::Adjoint(const Vector9& xi_b, OptionalJacobian<9, 9> H_state,
181+
OptionalJacobian<9, 9> H_xib) const {
182+
const Matrix9 Ad = AdjointMap();
183+
184+
// Jacobians
185+
if (H_state) *H_state = -Ad * adjointMap(xi_b);
186+
if (H_xib) *H_xib = Ad;
187+
188+
return Ad * xi_b;
189+
}
190+
191+
//------------------------------------------------------------------------------
192+
Matrix9 NavState::adjointMap(const Vector9& xi) {
193+
Matrix3 w_hat = skewSymmetric(xi(0), xi(1), xi(2));
194+
Matrix3 v_hat = skewSymmetric(xi(3), xi(4), xi(5));
195+
Matrix3 a_hat = skewSymmetric(xi(6), xi(7), xi(8));
196+
Matrix9 adj;
197+
adj << w_hat, Z_3x3, Z_3x3, v_hat, w_hat, Z_3x3, a_hat, Z_3x3, w_hat;
198+
return adj;
199+
}
200+
201+
//------------------------------------------------------------------------------
202+
Vector9 NavState::adjoint(const Vector9& xi, const Vector9& y,
203+
OptionalJacobian<9, 9> Hxi,
204+
OptionalJacobian<9, 9> H_y) {
205+
if (Hxi) {
206+
Hxi->setZero();
207+
for (int i = 0; i < 9; ++i) {
208+
Vector9 dxi;
209+
dxi.setZero();
210+
dxi(i) = 1.0;
211+
Matrix9 Gi = adjointMap(dxi);
212+
Hxi->col(i) = Gi * y;
213+
}
214+
}
215+
216+
const Matrix9& ad_xi = adjointMap(xi);
217+
if (H_y) *H_y = ad_xi;
218+
219+
return ad_xi * y;
220+
}
221+
222+
//------------------------------------------------------------------------------
223+
Matrix9 NavState::ExpmapDerivative(const Vector9& xi) {
224+
Matrix9 J;
225+
Expmap(xi, J);
226+
return J;
227+
}
228+
229+
//------------------------------------------------------------------------------
230+
Matrix9 NavState::LogmapDerivative(const NavState& state) {
231+
const Vector9 xi = Logmap(state);
232+
const Vector3 w = xi.head<3>();
233+
Vector3 rho = xi.segment<3>(3);
234+
Vector3 nu = xi.tail<3>();
235+
236+
Matrix3 Qt, Qv;
237+
const Rot3 R = Rot3::Expmap(w);
238+
Pose3::ExpmapTranslation(w, rho, Qt, R);
239+
Pose3::ExpmapTranslation(w, nu, Qv, R);
240+
const Matrix3 Jw = Rot3::LogmapDerivative(w);
241+
const Matrix3 Qt2 = -Jw * Qt * Jw;
242+
const Matrix3 Qv2 = -Jw * Qv * Jw;
243+
244+
Matrix9 J;
245+
J << Jw, Z_3x3, Z_3x3,
246+
Qt2, Jw, Z_3x3,
247+
Qv2, Z_3x3, Jw;
248+
return J;
249+
}
250+
251+
252+
//------------------------------------------------------------------------------
253+
NavState NavState::ChartAtOrigin::Retract(const Vector9& xi,
254+
ChartJacobian Hxi) {
255+
return Expmap(xi, Hxi);
256+
}
257+
258+
//------------------------------------------------------------------------------
259+
Vector9 NavState::ChartAtOrigin::Local(const NavState& state,
260+
ChartJacobian Hstate) {
261+
return Logmap(state, Hstate);
262+
}
263+
106264
//------------------------------------------------------------------------------
107265
NavState NavState::retract(const Vector9& xi, //
108266
OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const {
@@ -142,15 +300,16 @@ Vector9 NavState::localCoordinates(const NavState& g, //
142300
Matrix3 D_xi_R;
143301
xi << Rot3::Logmap(dR, (H1 || H2) ? &D_xi_R : 0), dP, dV;
144302
if (H1) {
145-
*H1 << D_xi_R * D_dR_R, Z_3x3, Z_3x3, //
146-
D_dt_R, -I_3x3, Z_3x3, //
147-
D_dv_R, Z_3x3, -I_3x3;
303+
*H1 << D_xi_R * D_dR_R, Z_3x3, Z_3x3, //
304+
D_dt_R, -I_3x3, Z_3x3, //
305+
D_dv_R, Z_3x3, -I_3x3;
148306
}
149307
if (H2) {
150-
*H2 << D_xi_R, Z_3x3, Z_3x3, //
151-
Z_3x3, dR.matrix(), Z_3x3, //
152-
Z_3x3, Z_3x3, dR.matrix();
308+
*H2 << D_xi_R, Z_3x3, Z_3x3, //
309+
Z_3x3, dR.matrix(), Z_3x3, //
310+
Z_3x3, Z_3x3, dR.matrix();
153311
}
312+
154313
return xi;
155314
}
156315

@@ -213,7 +372,8 @@ NavState NavState::update(const Vector3& b_acceleration, const Vector3& b_omega,
213372
//------------------------------------------------------------------------------
214373
Vector9 NavState::coriolis(double dt, const Vector3& omega, bool secondOrder,
215374
OptionalJacobian<9, 9> H) const {
216-
auto [nRb, n_t, n_v] = (*this);
375+
Rot3 nRb = R_;
376+
Point3 n_t = t_, n_v = v_;
217377

218378
const double dt2 = dt * dt;
219379
const Vector3 omega_cross_vel = omega.cross(n_v);

0 commit comments

Comments
 (0)