GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: unittest/explog.cpp Lines: 298 298 100.0 %
Date: 2024-01-23 21:41:47 Branches: 1373 2728 50.3 %

Line Branch Exec Source
1
//
2
// Copyright (c) 2016-2019 CNRS INRIA
3
// Copyright (c) 2015 Wandercraft, 86 rue de Paris 91400 Orsay, France.
4
//
5
6
#include "pinocchio/spatial/explog.hpp"
7
8
#include <boost/test/unit_test.hpp>
9
#include <boost/utility/binary.hpp>
10
11
#include "utils/macros.hpp"
12
13
using namespace pinocchio;
14
15
BOOST_AUTO_TEST_SUITE ( BOOST_TEST_MODULE )
16
17
















4
BOOST_AUTO_TEST_CASE(exp)
18
{
19
2
  SE3 M(SE3::Random());
20

2
  Motion v(Motion::Random()); v.linear().setZero();
21
22

2
  SE3::Matrix3 R = exp3(v.angular());
23






2
  BOOST_CHECK(R.isApprox(Eigen::AngleAxis<double>(v.angular().norm(), v.angular().normalized()).matrix()));
24
25

2
  SE3::Matrix3 R0 = exp3(SE3::Vector3::Zero());
26



2
  BOOST_CHECK(R0.isIdentity());
27
28
  // check the NAN case
29
  #ifdef NDEBUG
30
    Motion::Vector3 vec3_nan(Motion::Vector3::Constant(NAN));
31
    SE3::Matrix3 R_nan = exp3(vec3_nan);
32
    BOOST_CHECK(R_nan != R_nan);
33
  #endif
34
35
2
  M = exp6(v);
36
37




2
  BOOST_CHECK(R.isApprox(M.rotation()));
38
39
  // check the NAN case
40
  #ifdef NDEBUG
41
    Motion::Vector6 vec6_nan(Motion::Vector6::Constant(NAN));
42
    SE3 M_nan = exp6(vec6_nan);
43
    BOOST_CHECK(M_nan != M_nan);
44
  #endif
45
46

2
  R = exp3(SE3::Vector3::Zero());
47



2
  BOOST_CHECK(R.isIdentity());
48
49
  // Quaternion
50
2
  Eigen::Quaterniond quat;
51

2
  quaternion::exp3(v.angular(),quat);
52




2
  BOOST_CHECK(quat.toRotationMatrix().isApprox(M.rotation()));
53
54

2
  quaternion::exp3(SE3::Vector3::Zero(),quat);
55




2
  BOOST_CHECK(quat.toRotationMatrix().isIdentity());
56






2
  BOOST_CHECK(quat.vec().isZero() && quat.coeffs().tail<1>().isOnes());
57
58
  // Check QuaternionMap
59
2
  Eigen::Vector4d vec4;
60

2
  Eigen::QuaternionMapd quat_map(vec4.data());
61

2
  quaternion::exp3(v.angular(),quat_map);
62




2
  BOOST_CHECK(quat_map.toRotationMatrix().isApprox(M.rotation()));
63
2
}
64
65
















4
BOOST_AUTO_TEST_CASE(log)
66
{
67
2
  SE3 M(SE3::Identity());
68

2
  Motion v(Motion::Random()); v.linear().setZero();
69
70

2
  SE3::Vector3 omega = log3(M.rotation());
71



2
  BOOST_CHECK(omega.isZero());
72
73
  // check the NAN case
74
#ifdef NDEBUG
75
  SE3::Matrix3 mat3_nan(SE3::Matrix3::Constant(NAN));
76
  SE3::Vector3 omega_nan = log3(mat3_nan);
77
  BOOST_CHECK(omega_nan != omega_nan);
78
#endif
79
80
2
  M.setRandom();
81

2
  M.translation().setZero();
82
83
2
  v = log6(M);
84

2
  omega = log3(M.rotation());
85




2
  BOOST_CHECK(omega.isApprox(v.angular()));
86
87
  // check the NAN case
88
  #ifdef NDEBUG
89
    SE3::Matrix4 mat4_nan(SE3::Matrix4::Constant(NAN));
90
    Motion::Vector6 v_nan = log6(mat4_nan);
91
    BOOST_CHECK(v_nan != v_nan);
92
  #endif
93
94
  // Quaternion
95

2
  Eigen::Quaterniond quat(SE3::Matrix3::Identity());
96
2
  omega = quaternion::log3(quat);
97



2
  BOOST_CHECK(omega.isZero());
98
99
2002
  for(int k = 0; k < 1e3; ++k)
100
  {
101

2000
    SE3::Vector3 w; w.setRandom();
102
2000
    quaternion::exp3(w,quat);
103
2000
    SE3::Matrix3 rot = exp3(w);
104
105




2000
    BOOST_CHECK(quat.toRotationMatrix().isApprox(rot));
106
    double theta;
107
2000
    omega = quaternion::log3(quat,theta);
108
2000
    const double PI_value = PI<double>();
109



2000
    BOOST_CHECK(omega.norm() <= PI_value);
110
    double theta_ref;
111

2000
    SE3::Vector3 omega_ref = log3(quat.toRotationMatrix(),theta_ref);
112
113



2000
    BOOST_CHECK(omega.isApprox(omega_ref));
114
  }
115
116
117
  // Check QuaternionMap
118
2
  Eigen::Vector4d vec4;
119

2
  Eigen::QuaternionMapd quat_map(vec4.data());
120

2
  quat_map = SE3::Random().rotation();
121





2
  BOOST_CHECK(quaternion::log3(quat_map).isApprox(log3(quat_map.toRotationMatrix())));
122
2
}
123
124
















4
BOOST_AUTO_TEST_CASE(explog3)
125
{
126
2
  SE3 M(SE3::Random());
127

2
  SE3::Matrix3 M_res = exp3(log3(M.rotation()));
128




2
  BOOST_CHECK(M_res.isApprox(M.rotation()));
129
130

2
  Motion::Vector3 v; v.setRandom();
131

2
  Motion::Vector3 v_res = log3(exp3(v));
132



2
  BOOST_CHECK(v_res.isApprox(v));
133
2
}
134
135
















4
BOOST_AUTO_TEST_CASE(explog3_quaternion)
136
{
137
2
  SE3 M(SE3::Random());
138
2
  Eigen::Quaterniond quat;
139

2
  quat = M.rotation();
140
2
  Eigen::Quaterniond quat_res;
141

2
  quaternion::exp3(quaternion::log3(quat),quat_res);
142





2
  BOOST_CHECK(quat_res.isApprox(quat) || quat_res.coeffs().isApprox(-quat.coeffs()));
143
144

2
  Motion::Vector3 v; v.setRandom();
145
2
  quaternion::exp3(v,quat);
146




2
  BOOST_CHECK(quaternion::log3(quat).isApprox(v));
147
148


2
  SE3::Matrix3 R_next = M.rotation() * exp3(v);
149


2
  Motion::Vector3 v_est = log3(M.rotation().transpose() * R_next);
150



2
  BOOST_CHECK(v_est.isApprox(v));
151
152
2
  SE3::Quaternion quat_v;
153
2
  quaternion::exp3(v,quat_v);
154
2
  SE3::Quaternion quat_next = quat * quat_v;
155

2
  v_est = quaternion::log3(quat.conjugate() * quat_next);
156



2
  BOOST_CHECK(v_est.isApprox(v));
157
2
}
158
159
















4
BOOST_AUTO_TEST_CASE(Jlog3_fd)
160
{
161
2
  SE3 M(SE3::Random());
162

2
  SE3::Matrix3 R (M.rotation());
163
164

2
  SE3::Matrix3 Jfd, Jlog;
165
2
  Jlog3 (R, Jlog);
166
2
  Jfd.setZero();
167
168

2
  Motion::Vector3 dR; dR.setZero();
169
2
  const double eps = 1e-8;
170
8
  for (int i = 0; i < 3; ++i)
171
  {
172
6
    dR[i] = eps;
173

6
    SE3::Matrix3 R_dR = R * exp3(dR);
174



6
    Jfd.col(i) = (log3(R_dR) - log3(R)) / eps;
175
6
    dR[i] = 0;
176
  }
177



2
  BOOST_CHECK(Jfd.isApprox(Jlog, std::sqrt(eps)));
178
2
}
179
180
















4
BOOST_AUTO_TEST_CASE(Jexp3_fd)
181
{
182
2
  SE3 M(SE3::Random());
183

2
  SE3::Matrix3 R (M.rotation());
184
185
2
  Motion::Vector3 v = log3(R);
186
187

2
  SE3::Matrix3 Jexp_fd, Jexp;
188
189

2
  Jexp3(Motion::Vector3::Zero(), Jexp);
190



2
  BOOST_CHECK(Jexp.isIdentity());
191
192
2
  Jexp3(v, Jexp);
193
194

2
  Motion::Vector3 dv; dv.setZero();
195
2
  const double eps = 1e-8;
196
8
  for (int i = 0; i < 3; ++i)
197
  {
198
6
    dv[i] = eps;
199

6
    SE3::Matrix3 R_next = exp3(v+dv);
200



6
    Jexp_fd.col(i) = log3(R.transpose()*R_next) / eps;
201
6
    dv[i] = 0;
202
  }
203



2
  BOOST_CHECK(Jexp_fd.isApprox(Jexp, std::sqrt(eps)));
204
2
}
205
206
template<typename QuaternionLike, typename Matrix43Like>
207
2
void Jexp3QuatLocal(const Eigen::QuaternionBase<QuaternionLike> & quat,
208
                    const Eigen::MatrixBase<Matrix43Like> & Jexp)
209
{
210
2
  Matrix43Like & Jout = PINOCCHIO_EIGEN_CONST_CAST(Matrix43Like,Jexp);
211
212

2
  skew(0.5 * quat.vec(),Jout.template topRows<3>());
213


2
  Jout.template topRows<3>().diagonal().array() += 0.5 * quat.w();
214


2
  Jout.template bottomRows<1>() = -0.5 * quat.vec().transpose();
215
2
}
216
217
















4
BOOST_AUTO_TEST_CASE(Jexp3_quat_fd)
218
{
219
  typedef double Scalar;
220

2
  SE3::Vector3 w; w.setRandom();
221

2
  SE3::Quaternion quat; quaternion::exp3(w,quat);
222
223
  typedef Eigen::Matrix<Scalar,4,3> Matrix43;
224

2
  Matrix43 Jexp3, Jexp3_fd;
225
2
  quaternion::Jexp3CoeffWise(w,Jexp3);
226

2
  SE3::Vector3 dw; dw.setZero();
227
2
  const double eps = 1e-8;
228
229
8
  for(int i = 0; i < 3; ++i)
230
  {
231
6
    dw[i] = eps;
232

6
    SE3::Quaternion quat_plus; quaternion::exp3(w + dw,quat_plus);
233


6
    Jexp3_fd.col(i) = (quat_plus.coeffs() - quat.coeffs()) / eps;
234
6
    dw[i] = 0;
235
  }
236



2
  BOOST_CHECK(Jexp3.isApprox(Jexp3_fd,sqrt(eps)));
237
238
2
  SE3::Matrix3 Jlog;
239

2
  pinocchio::Jlog3(quat.toRotationMatrix(),Jlog);
240
241
2
  Matrix43 Jexp_quat_local;
242
2
  Jexp3QuatLocal(quat,Jexp_quat_local);
243
244
245

2
  Matrix43 Jcompositon = Jexp3 * Jlog;
246



2
  BOOST_CHECK(Jcompositon.isApprox(Jexp_quat_local));
247
//  std::cout << "Jcompositon\n" << Jcompositon << std::endl;
248
//  std::cout << "Jexp_quat_local\n" << Jexp_quat_local << std::endl;
249
250
  // Arount zero
251
2
  w.setZero();
252
2
  w.fill(1e-6);
253
2
  quaternion::exp3(w,quat);
254
2
  quaternion::Jexp3CoeffWise(w,Jexp3);
255
8
  for(int i = 0; i < 3; ++i)
256
  {
257
6
    dw[i] = eps;
258

6
    SE3::Quaternion quat_plus; quaternion::exp3(w + dw,quat_plus);
259


6
    Jexp3_fd.col(i) = (quat_plus.coeffs() - quat.coeffs()) / eps;
260
6
    dw[i] = 0;
261
  }
262



2
  BOOST_CHECK(Jexp3.isApprox(Jexp3_fd,sqrt(eps)));
263
264
2
}
265
266
















4
BOOST_AUTO_TEST_CASE(Jexp3_quat)
267
{
268
2
  SE3 M(SE3::Random());
269

2
  SE3::Quaternion quat(M.rotation());
270
271
2
  Motion dv(Motion::Zero());
272
2
  const double eps = 1e-8;
273
274
  typedef Eigen::Matrix<double,7,6> Matrix76;
275

2
  Matrix76 Jexp6_fd, Jexp6_quat; Jexp6_quat.setZero();
276
  typedef Eigen::Matrix<double,4,3> Matrix43;
277

2
  Matrix43 Jexp3_quat; Jexp3QuatLocal(quat,Jexp3_quat);
278
2
  SE3 M_next;
279
280


2
  Jexp6_quat.middleRows<3>(Motion::LINEAR).middleCols<3>(Motion::LINEAR) = M.rotation();
281

2
  Jexp6_quat.middleRows<4>(Motion::ANGULAR).middleCols<3>(Motion::ANGULAR) = Jexp3_quat;// * Jlog6_SE3.middleRows<3>(Motion::ANGULAR);
282
14
  for(int i = 0; i < 6; ++i)
283
  {
284

12
    dv.toVector()[i] = eps;
285

12
    M_next = M * exp6(dv);
286

12
    const SE3::Quaternion quat_next(M_next.rotation());
287



12
    Jexp6_fd.middleRows<3>(Motion::LINEAR).col(i) = (M_next.translation() - M.translation())/eps;
288


12
    Jexp6_fd.middleRows<4>(Motion::ANGULAR).col(i) = (quat_next.coeffs() - quat.coeffs())/eps;
289

12
    dv.toVector()[i] = 0.;
290
  }
291
292



2
  BOOST_CHECK(Jexp6_quat.isApprox(Jexp6_fd,sqrt(eps)));
293
2
}
294
295
















4
BOOST_AUTO_TEST_CASE(Jexplog3)
296
{
297
2
  Motion v(Motion::Random());
298
299

2
  Eigen::Matrix3d R (exp3(v.angular())),
300

2
    Jexp, Jlog;
301

2
  Jexp3 (v.angular(), Jexp);
302
2
  Jlog3 (R          , Jlog);
303
304




2
  BOOST_CHECK((Jlog * Jexp).isIdentity());
305
306
2
  SE3 M(SE3::Random());
307

2
  R = M.rotation();
308

2
  v.angular() = log3(R);
309
2
  Jlog3 (R          , Jlog);
310

2
  Jexp3 (v.angular(), Jexp);
311
312




2
  BOOST_CHECK((Jexp * Jlog).isIdentity());
313
2
}
314
315
















4
BOOST_AUTO_TEST_CASE(Jlog3_quat)
316
{
317

2
  SE3::Vector3 w; w.setRandom();
318

2
  SE3::Quaternion quat; quaternion::exp3(w,quat);
319
320
2
  SE3::Matrix3 R(quat.toRotationMatrix());
321
322

2
  SE3::Matrix3 res, res_ref;
323
2
  quaternion::Jlog3(quat,res);
324
2
  Jlog3(R,res_ref);
325
326



2
  BOOST_CHECK(res.isApprox(res_ref));
327
2
}
328
329
















4
BOOST_AUTO_TEST_CASE(explog6)
330
{
331
2
  SE3 M(SE3::Random());
332

2
  SE3 M_res = exp6(log6(M));
333



2
  BOOST_CHECK(M_res.isApprox(M));
334
335
2
  Motion v(Motion::Random());
336

2
  Motion v_res = log6(exp6(v));
337




2
  BOOST_CHECK(v_res.toVector().isApprox(v.toVector()));
338
2
}
339
340
















4
BOOST_AUTO_TEST_CASE(Jlog6_fd)
341
{
342
2
  SE3 M(SE3::Random());
343
344

2
  SE3::Matrix6 Jfd, Jlog;
345
2
  Jlog6 (M, Jlog);
346
2
  Jfd.setZero();
347
348

2
  Motion dM; dM.setZero();
349
2
  double step = 1e-8;
350
14
  for (int i = 0; i < 6; ++i)
351
  {
352

12
    dM.toVector()[i] = step;
353

12
    SE3 M_dM = M * exp6(dM);
354




12
    Jfd.col(i) = (log6(M_dM).toVector() - log6(M).toVector()) / step;
355

12
    dM.toVector()[i] = 0;
356
  }
357
358



2
  BOOST_CHECK(Jfd.isApprox(Jlog, sqrt(step)));
359
2
}
360
361
















4
BOOST_AUTO_TEST_CASE(Jlog6_of_product_fd)
362
{
363
2
  SE3 Ma(SE3::Random());
364
2
  SE3 Mb(SE3::Random());
365
366


2
  SE3::Matrix6 Jlog, Ja, Jb, Jfda, Jfdb;
367

2
  Jlog6 (Ma.inverse() * Mb, Jlog);
368



2
  Ja = - Jlog * (Ma.inverse() * Mb).toActionMatrixInverse();
369
2
  Jb = Jlog;
370
2
  Jfda.setZero();
371
2
  Jfdb.setZero();
372
373

2
  Motion dM; dM.setZero();
374
2
  double step = 1e-8;
375
14
  for (int i = 0; i < 6; ++i)
376
  {
377

12
    dM.toVector()[i] = step;
378







12
    Jfda.col(i) = (log6((Ma*exp6(dM)).inverse()*Mb).toVector() - log6(Ma.inverse()*Mb).toVector()) / step;
379







12
    Jfdb.col(i) = (log6(Ma.inverse()*Mb*exp6(dM)).toVector() - log6(Ma.inverse()*Mb).toVector()) / step;
380

12
    dM.toVector()[i] = 0;
381
  }
382
383



2
  BOOST_CHECK(Jfda.isApprox(Ja, sqrt(step)));
384



2
  BOOST_CHECK(Jfdb.isApprox(Jb, sqrt(step)));
385
2
}
386
387
















4
BOOST_AUTO_TEST_CASE(Jexplog6)
388
{
389
2
  Motion v(Motion::Random());
390
391
2
  SE3 M (exp6(v));
392
  {
393

2
    Eigen::Matrix<double, 6, 6, Eigen::RowMajor> Jexp, Jlog;
394
2
    Jexp6 (v, Jexp);
395
2
    Jlog6 (M, Jlog);
396
397




2
    BOOST_CHECK((Jlog * Jexp).isIdentity());
398
  }
399
400
2
  M.setRandom();
401
402
2
  v = log6(M);
403
  {
404

2
    Eigen::Matrix<double, 6, 6> Jexp, Jlog;
405
2
    Jlog6 (M, Jlog);
406
2
    Jexp6 (v, Jexp);
407
408




2
    BOOST_CHECK((Jexp * Jlog).isIdentity());
409
  }
410
2
}
411
412
















4
BOOST_AUTO_TEST_CASE(Hlog3_fd)
413
{
414
  typedef SE3::Vector3 Vector3;
415
  typedef SE3::Matrix3 Matrix3;
416

2
  SE3::Quaternion q; quaternion::uniformRandom(q);
417
2
  Matrix3 R (q.matrix());
418
419
  // Hlog3(R, v) returns the matrix H * v
420
  // We check that H * e_k matches the finite difference of Hlog3(R, e_k)
421

2
  Vector3 dR; dR.setZero();
422
2
  double step = 1e-8;
423
8
  for (int k = 0; k < 3; ++k)
424
  {
425

6
    Vector3 e_k (Vector3::Zero());
426
6
    e_k[k] = 1.;
427
428
6
    Matrix3 Hlog_e_k;
429
6
    Hlog3(R, e_k, Hlog_e_k);
430
431


6
    Matrix3 R_dR = R * exp3(step * e_k);
432

6
    Matrix3 Jlog_R, Jlog_R_dR;
433
6
    Jlog3(R, Jlog_R);
434
6
    Jlog3(R_dR, Jlog_R_dR);
435
436

6
    Matrix3 Hlog_e_k_fd = (Jlog_R_dR - Jlog_R ) / step;
437
438



6
    BOOST_CHECK(Hlog_e_k.isApprox(Hlog_e_k_fd, sqrt(step)));
439
  }
440
2
}
441
442
















4
BOOST_AUTO_TEST_CASE (test_basic)
443
{
444
  typedef pinocchio::SE3::Vector3 Vector3;
445
  typedef pinocchio::SE3::Matrix3 Matrix3;
446
  typedef Eigen::Matrix4d Matrix4;
447
  typedef pinocchio::Motion::Vector6 Vector6;
448
449
2
  const double EPSILON = 1e-12;
450
451
  // exp3 and log3.
452

2
  Vector3 v3(Vector3::Random());
453
2
  Matrix3 R(pinocchio::exp3(v3));
454




2
  BOOST_CHECK(R.transpose().isApprox(R.inverse(), EPSILON));
455



2
  BOOST_CHECK_SMALL(R.determinant() - 1.0, EPSILON);
456
2
  Vector3 v3FromLog(pinocchio::log3(R));
457



2
  BOOST_CHECK(v3.isApprox(v3FromLog, EPSILON));
458
459
  // exp6 and log6.
460
2
  pinocchio::Motion nu = pinocchio::Motion::Random();
461
2
  pinocchio::SE3 m = pinocchio::exp6(nu);
462





2
  BOOST_CHECK(m.rotation().transpose().isApprox(m.rotation().inverse(),
463
                                                EPSILON));
464



2
  BOOST_CHECK_SMALL(m.rotation().determinant() - 1.0, EPSILON);
465
2
  pinocchio::Motion nuFromLog(pinocchio::log6(m));
466




2
  BOOST_CHECK(nu.linear().isApprox(nuFromLog.linear(), EPSILON));
467




2
  BOOST_CHECK(nu.angular().isApprox(nuFromLog.angular(), EPSILON));
468
469

2
  Vector6 v6(Vector6::Random());
470
2
  pinocchio::SE3 m2(pinocchio::exp6(v6));
471





2
  BOOST_CHECK(m2.rotation().transpose().isApprox(m2.rotation().inverse(),
472
                                                 EPSILON));
473



2
  BOOST_CHECK_SMALL(m2.rotation().determinant() - 1.0, EPSILON);
474
2
  Matrix4 M = m2.toHomogeneousMatrix();
475
2
  pinocchio::Motion nu2FromLog(pinocchio::log6(M));
476

2
  Vector6 v6FromLog(nu2FromLog.toVector());
477



2
  BOOST_CHECK(v6.isApprox(v6FromLog, EPSILON));
478
2
}
479
480
















4
BOOST_AUTO_TEST_CASE(test_SE3_interpolate)
481
{
482
2
  SE3 A = SE3::Random();
483
2
  SE3 B = SE3::Random();
484
485
2
  SE3 A_bis = SE3::Interpolate(A,B,0.);
486



2
  BOOST_CHECK(A_bis.isApprox(A));
487
2
  SE3 B_bis = SE3::Interpolate(A,B,1.);
488



2
  BOOST_CHECK(B_bis.isApprox(B));
489
490
2
  A_bis = SE3::Interpolate(A,A,1.);
491



2
  BOOST_CHECK(A_bis.isApprox(A));
492
493
2
  B_bis = SE3::Interpolate(B,B,1.);
494



2
  BOOST_CHECK(B_bis.isApprox(B));
495
496
2
  SE3 C = SE3::Interpolate(A,B,0.5);
497
2
  SE3 D = SE3::Interpolate(B,A,0.5);
498



2
  BOOST_CHECK(D.isApprox(C));
499
2
}
500
501
BOOST_AUTO_TEST_SUITE_END()