Line |
Branch |
Exec |
Source |
1 |
|
|
// |
2 |
|
|
// Copyright (c) 2016-2021 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 <iostream> |
9 |
|
|
#include <boost/test/unit_test.hpp> |
10 |
|
|
#include <boost/utility/binary.hpp> |
11 |
|
|
|
12 |
|
|
#include "utils/macros.hpp" |
13 |
|
|
|
14 |
|
|
using namespace pinocchio; |
15 |
|
|
|
16 |
|
|
BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE) |
17 |
|
|
|
18 |
|
✗ |
BOOST_AUTO_TEST_CASE(exp) |
19 |
|
|
{ |
20 |
|
✗ |
SE3 M(SE3::Random()); |
21 |
|
✗ |
Motion v(Motion::Random()); |
22 |
|
✗ |
v.linear().setZero(); |
23 |
|
|
|
24 |
|
✗ |
SE3::Matrix3 R = exp3(v.angular()); |
25 |
|
✗ |
BOOST_CHECK( |
26 |
|
|
R.isApprox(Eigen::AngleAxis<double>(v.angular().norm(), v.angular().normalized()).matrix())); |
27 |
|
|
|
28 |
|
✗ |
SE3::Matrix3 R0 = exp3(SE3::Vector3::Zero()); |
29 |
|
✗ |
BOOST_CHECK(R0.isIdentity()); |
30 |
|
|
|
31 |
|
|
// check the NAN case |
32 |
|
|
#ifdef NDEBUG |
33 |
|
|
Motion::Vector3 vec3_nan(Motion::Vector3::Constant(NAN)); |
34 |
|
|
SE3::Matrix3 R_nan = exp3(vec3_nan); |
35 |
|
|
BOOST_CHECK(R_nan != R_nan); |
36 |
|
|
#endif |
37 |
|
|
|
38 |
|
✗ |
M = exp6(v); |
39 |
|
|
|
40 |
|
✗ |
BOOST_CHECK(R.isApprox(M.rotation())); |
41 |
|
|
|
42 |
|
✗ |
Eigen::Matrix<double, 7, 1> q0; |
43 |
|
✗ |
q0 << 0., 0., 0., 0., 0., 0., 1.; |
44 |
|
✗ |
Eigen::Matrix<double, 7, 1> q = quaternion::exp6(v); |
45 |
|
✗ |
Eigen::Quaterniond quat0(q.tail<4>()); |
46 |
|
✗ |
BOOST_CHECK(R.isApprox(quat0.matrix())); |
47 |
|
|
|
48 |
|
|
// check the NAN case |
49 |
|
|
#ifdef NDEBUG |
50 |
|
|
Motion::Vector6 vec6_nan(Motion::Vector6::Constant(NAN)); |
51 |
|
|
SE3 M_nan = exp6(vec6_nan); |
52 |
|
|
BOOST_CHECK(M_nan != M_nan); |
53 |
|
|
#endif |
54 |
|
|
|
55 |
|
✗ |
R = exp3(SE3::Vector3::Zero()); |
56 |
|
✗ |
BOOST_CHECK(R.isIdentity()); |
57 |
|
|
|
58 |
|
|
// Quaternion |
59 |
|
✗ |
Eigen::Quaterniond quat; |
60 |
|
✗ |
quaternion::exp3(v.angular(), quat); |
61 |
|
✗ |
BOOST_CHECK(quat.toRotationMatrix().isApprox(M.rotation())); |
62 |
|
|
|
63 |
|
✗ |
quaternion::exp3(SE3::Vector3::Zero(), quat); |
64 |
|
✗ |
BOOST_CHECK(quat.toRotationMatrix().isIdentity()); |
65 |
|
✗ |
BOOST_CHECK(quat.vec().isZero() && quat.coeffs().tail<1>().isOnes()); |
66 |
|
|
|
67 |
|
|
// Check QuaternionMap |
68 |
|
✗ |
Eigen::Vector4d vec4; |
69 |
|
✗ |
Eigen::QuaternionMapd quat_map(vec4.data()); |
70 |
|
✗ |
quaternion::exp3(v.angular(), quat_map); |
71 |
|
✗ |
BOOST_CHECK(quat_map.toRotationMatrix().isApprox(M.rotation())); |
72 |
|
|
} |
73 |
|
|
|
74 |
|
✗ |
BOOST_AUTO_TEST_CASE(renorm_rotation) |
75 |
|
|
{ |
76 |
|
✗ |
SE3 M0, M1; |
77 |
|
✗ |
SE3::Matrix3 R0, R1; |
78 |
|
✗ |
SE3::Matrix3 R_normed; |
79 |
|
✗ |
SE3::Matrix3 Id(SE3::Matrix3::Identity()); |
80 |
|
✗ |
SE3::Vector3 vals; |
81 |
|
|
double tr0, tr; |
82 |
|
✗ |
const size_t num_tries = 20; |
83 |
|
|
|
84 |
|
✗ |
for (size_t i = 0; i < num_tries; i++) |
85 |
|
|
{ |
86 |
|
✗ |
M0.setRandom(); |
87 |
|
✗ |
M1 = M0.actInv(M0); |
88 |
|
✗ |
R1 = M1.rotation(); |
89 |
|
✗ |
R_normed = pinocchio::renormalize_rotation_matrix(R1); |
90 |
|
✗ |
BOOST_CHECK((R_normed.transpose() * R_normed).isApprox(Id)); |
91 |
|
✗ |
tr0 = R1.trace(); |
92 |
|
|
|
93 |
|
✗ |
tr = R_normed.trace(); |
94 |
|
✗ |
vals = 2. * R_normed.diagonal().array() - tr + 1.; |
95 |
|
|
} |
96 |
|
|
} |
97 |
|
|
|
98 |
|
✗ |
BOOST_AUTO_TEST_CASE(log) |
99 |
|
|
{ |
100 |
|
✗ |
SE3 M(SE3::Identity()); |
101 |
|
✗ |
Motion v(Motion::Random()); |
102 |
|
✗ |
v.linear().setZero(); |
103 |
|
|
|
104 |
|
✗ |
SE3::Vector3 omega = log3(M.rotation()); |
105 |
|
✗ |
BOOST_CHECK(omega.isZero()); |
106 |
|
|
|
107 |
|
|
// check the NAN case |
108 |
|
|
#ifdef NDEBUG |
109 |
|
|
SE3::Matrix3 mat3_nan(SE3::Matrix3::Constant(NAN)); |
110 |
|
|
SE3::Vector3 omega_nan = log3(mat3_nan); |
111 |
|
|
BOOST_CHECK(omega_nan != omega_nan); |
112 |
|
|
#endif |
113 |
|
|
|
114 |
|
✗ |
M.setRandom(); |
115 |
|
✗ |
M.translation().setZero(); |
116 |
|
|
|
117 |
|
✗ |
v = log6(M); |
118 |
|
✗ |
omega = log3(M.rotation()); |
119 |
|
✗ |
BOOST_CHECK(omega.isApprox(v.angular())); |
120 |
|
|
|
121 |
|
|
// check the NAN case |
122 |
|
|
#ifdef NDEBUG |
123 |
|
|
SE3::Matrix4 mat4_nan(SE3::Matrix4::Constant(NAN)); |
124 |
|
|
Motion::Vector6 v_nan = log6(mat4_nan); |
125 |
|
|
BOOST_CHECK(v_nan != v_nan); |
126 |
|
|
#endif |
127 |
|
|
|
128 |
|
|
// Quaternion |
129 |
|
✗ |
Eigen::Quaterniond quat(SE3::Matrix3::Identity()); |
130 |
|
✗ |
omega = quaternion::log3(quat); |
131 |
|
✗ |
BOOST_CHECK(omega.isZero()); |
132 |
|
|
|
133 |
|
✗ |
for (int k = 0; k < 1e3; ++k) |
134 |
|
|
{ |
135 |
|
✗ |
SE3::Vector3 w; |
136 |
|
✗ |
w.setRandom(); |
137 |
|
✗ |
quaternion::exp3(w, quat); |
138 |
|
✗ |
SE3::Matrix3 rot = exp3(w); |
139 |
|
|
|
140 |
|
✗ |
BOOST_CHECK(quat.toRotationMatrix().isApprox(rot)); |
141 |
|
|
double theta; |
142 |
|
✗ |
omega = quaternion::log3(quat, theta); |
143 |
|
✗ |
const double PI_value = PI<double>(); |
144 |
|
✗ |
BOOST_CHECK(omega.norm() <= PI_value); |
145 |
|
|
double theta_ref; |
146 |
|
✗ |
SE3::Vector3 omega_ref = log3(quat.toRotationMatrix(), theta_ref); |
147 |
|
|
|
148 |
|
✗ |
BOOST_CHECK(omega.isApprox(omega_ref)); |
149 |
|
|
} |
150 |
|
|
|
151 |
|
|
// Check QuaternionMap |
152 |
|
✗ |
Eigen::Vector4d vec4; |
153 |
|
✗ |
Eigen::QuaternionMapd quat_map(vec4.data()); |
154 |
|
✗ |
quat_map = SE3::Random().rotation(); |
155 |
|
✗ |
BOOST_CHECK(quaternion::log3(quat_map).isApprox(log3(quat_map.toRotationMatrix()))); |
156 |
|
|
} |
157 |
|
|
|
158 |
|
✗ |
BOOST_AUTO_TEST_CASE(explog3) |
159 |
|
|
{ |
160 |
|
✗ |
SE3 M(SE3::Random()); |
161 |
|
✗ |
SE3::Matrix3 M_res = exp3(log3(M.rotation())); |
162 |
|
✗ |
BOOST_CHECK(M_res.isApprox(M.rotation())); |
163 |
|
|
|
164 |
|
✗ |
Motion::Vector3 v; |
165 |
|
✗ |
v.setRandom(); |
166 |
|
✗ |
Motion::Vector3 v_res = log3(exp3(v)); |
167 |
|
✗ |
BOOST_CHECK(v_res.isApprox(v)); |
168 |
|
|
} |
169 |
|
|
|
170 |
|
✗ |
BOOST_AUTO_TEST_CASE(explog3_quaternion) |
171 |
|
|
{ |
172 |
|
✗ |
SE3 M(SE3::Random()); |
173 |
|
✗ |
Eigen::Quaterniond quat; |
174 |
|
✗ |
quat = M.rotation(); |
175 |
|
✗ |
Eigen::Quaterniond quat_res; |
176 |
|
✗ |
quaternion::exp3(quaternion::log3(quat), quat_res); |
177 |
|
✗ |
BOOST_CHECK(quat_res.isApprox(quat) || quat_res.coeffs().isApprox(-quat.coeffs())); |
178 |
|
|
|
179 |
|
✗ |
Motion::Vector3 v; |
180 |
|
✗ |
v.setRandom(); |
181 |
|
✗ |
quaternion::exp3(v, quat); |
182 |
|
✗ |
BOOST_CHECK(quaternion::log3(quat).isApprox(v)); |
183 |
|
|
|
184 |
|
✗ |
SE3::Matrix3 R_next = M.rotation() * exp3(v); |
185 |
|
✗ |
Motion::Vector3 v_est = log3(M.rotation().transpose() * R_next); |
186 |
|
✗ |
BOOST_CHECK(v_est.isApprox(v)); |
187 |
|
|
|
188 |
|
✗ |
SE3::Quaternion quat_v; |
189 |
|
✗ |
quaternion::exp3(v, quat_v); |
190 |
|
✗ |
SE3::Quaternion quat_next = quat * quat_v; |
191 |
|
✗ |
v_est = quaternion::log3(quat.conjugate() * quat_next); |
192 |
|
✗ |
BOOST_CHECK(v_est.isApprox(v)); |
193 |
|
|
} |
194 |
|
|
|
195 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jlog3_fd) |
196 |
|
|
{ |
197 |
|
✗ |
SE3 M(SE3::Random()); |
198 |
|
✗ |
SE3::Matrix3 R(M.rotation()); |
199 |
|
|
|
200 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH |
201 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_MAYBE_UNINITIALIZED |
202 |
|
✗ |
SE3::Matrix3 Jfd, Jlog; |
203 |
|
✗ |
Jlog3(R, Jlog); |
204 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_POP |
205 |
|
✗ |
Jfd.setZero(); |
206 |
|
|
|
207 |
|
✗ |
Motion::Vector3 dR; |
208 |
|
✗ |
dR.setZero(); |
209 |
|
✗ |
const double eps = 1e-8; |
210 |
|
✗ |
for (int i = 0; i < 3; ++i) |
211 |
|
|
{ |
212 |
|
✗ |
dR[i] = eps; |
213 |
|
✗ |
SE3::Matrix3 R_dR_plus = R * exp3(dR); |
214 |
|
✗ |
SE3::Matrix3 R_dR_minus = R * exp3(-dR); |
215 |
|
✗ |
Jfd.col(i) = (log3(R_dR_plus) - log3(R_dR_minus)) / (2 * eps); |
216 |
|
✗ |
dR[i] = 0; |
217 |
|
|
} |
218 |
|
✗ |
BOOST_CHECK(Jfd.isApprox(Jlog, std::sqrt(eps))); |
219 |
|
|
} |
220 |
|
|
|
221 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jexp3_fd) |
222 |
|
|
{ |
223 |
|
✗ |
SE3 M(SE3::Random()); |
224 |
|
✗ |
SE3::Matrix3 R(M.rotation()); |
225 |
|
|
|
226 |
|
✗ |
Motion::Vector3 v = log3(R); |
227 |
|
|
|
228 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH |
229 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_MAYBE_UNINITIALIZED |
230 |
|
✗ |
SE3::Matrix3 Jexp_fd, Jexp; |
231 |
|
|
|
232 |
|
✗ |
Jexp3(Motion::Vector3::Zero(), Jexp); |
233 |
|
✗ |
BOOST_CHECK(Jexp.isIdentity()); |
234 |
|
|
|
235 |
|
✗ |
Jexp3(v, Jexp); |
236 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_POP |
237 |
|
|
|
238 |
|
✗ |
Motion::Vector3 dv; |
239 |
|
✗ |
dv.setZero(); |
240 |
|
✗ |
const double eps = 1e-3; |
241 |
|
✗ |
for (int i = 0; i < 3; ++i) |
242 |
|
|
{ |
243 |
|
✗ |
dv[i] = eps; |
244 |
|
✗ |
SE3::Matrix3 R_next = exp3(v + dv); |
245 |
|
✗ |
SE3::Matrix3 R_prev = exp3(v - dv); |
246 |
|
✗ |
SE3::Matrix3 Rpn = R_prev.transpose() * R_next; |
247 |
|
✗ |
Jexp_fd.col(i) = log3(Rpn) / (2. * eps); |
248 |
|
✗ |
dv[i] = 0; |
249 |
|
|
} |
250 |
|
✗ |
BOOST_CHECK(Jexp_fd.isApprox(Jexp, std::sqrt(eps))); |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
template<typename QuaternionLike, typename Matrix43Like> |
254 |
|
✗ |
void Jexp3QuatLocal( |
255 |
|
|
const Eigen::QuaternionBase<QuaternionLike> & quat, const Eigen::MatrixBase<Matrix43Like> & Jexp) |
256 |
|
|
{ |
257 |
|
✗ |
Matrix43Like & Jout = PINOCCHIO_EIGEN_CONST_CAST(Matrix43Like, Jexp); |
258 |
|
|
|
259 |
|
✗ |
skew(0.5 * quat.vec(), Jout.template topRows<3>()); |
260 |
|
✗ |
Jout.template topRows<3>().diagonal().array() += 0.5 * quat.w(); |
261 |
|
✗ |
Jout.template bottomRows<1>() = -0.5 * quat.vec().transpose(); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jexp3_quat_fd) |
265 |
|
|
{ |
266 |
|
|
typedef double Scalar; |
267 |
|
✗ |
SE3::Vector3 w; |
268 |
|
✗ |
w.setRandom(); |
269 |
|
✗ |
SE3::Quaternion quat; |
270 |
|
✗ |
quaternion::exp3(w, quat); |
271 |
|
|
|
272 |
|
|
typedef Eigen::Matrix<Scalar, 4, 3> Matrix43; |
273 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH |
274 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_MAYBE_UNINITIALIZED |
275 |
|
✗ |
Matrix43 Jexp3, Jexp3_fd; |
276 |
|
✗ |
quaternion::Jexp3CoeffWise(w, Jexp3); |
277 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_POP |
278 |
|
✗ |
SE3::Vector3 dw; |
279 |
|
✗ |
dw.setZero(); |
280 |
|
✗ |
const double eps = 1e-8; |
281 |
|
|
|
282 |
|
✗ |
for (int i = 0; i < 3; ++i) |
283 |
|
|
{ |
284 |
|
✗ |
dw[i] = eps; |
285 |
|
✗ |
SE3::Quaternion quat_plus; |
286 |
|
✗ |
quaternion::exp3(w + dw, quat_plus); |
287 |
|
✗ |
Jexp3_fd.col(i) = (quat_plus.coeffs() - quat.coeffs()) / eps; |
288 |
|
✗ |
dw[i] = 0; |
289 |
|
|
} |
290 |
|
✗ |
BOOST_CHECK(Jexp3.isApprox(Jexp3_fd, sqrt(eps))); |
291 |
|
|
|
292 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH |
293 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_MAYBE_UNINITIALIZED |
294 |
|
✗ |
SE3::Matrix3 Jlog; |
295 |
|
✗ |
pinocchio::Jlog3(quat.toRotationMatrix(), Jlog); |
296 |
|
|
|
297 |
|
✗ |
Matrix43 Jexp_quat_local; |
298 |
|
✗ |
Jexp3QuatLocal(quat, Jexp_quat_local); |
299 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_POP |
300 |
|
|
|
301 |
|
✗ |
Matrix43 Jcompositon = Jexp3 * Jlog; |
302 |
|
✗ |
BOOST_CHECK(Jcompositon.isApprox(Jexp_quat_local)); |
303 |
|
|
// std::cout << "Jcompositon\n" << Jcompositon << std::endl; |
304 |
|
|
// std::cout << "Jexp_quat_local\n" << Jexp_quat_local << std::endl; |
305 |
|
|
|
306 |
|
|
// Arount zero |
307 |
|
✗ |
w.setZero(); |
308 |
|
✗ |
w.fill(1e-6); |
309 |
|
✗ |
quaternion::exp3(w, quat); |
310 |
|
✗ |
quaternion::Jexp3CoeffWise(w, Jexp3); |
311 |
|
✗ |
for (int i = 0; i < 3; ++i) |
312 |
|
|
{ |
313 |
|
✗ |
dw[i] = eps; |
314 |
|
✗ |
SE3::Quaternion quat_plus; |
315 |
|
✗ |
quaternion::exp3(w + dw, quat_plus); |
316 |
|
✗ |
Jexp3_fd.col(i) = (quat_plus.coeffs() - quat.coeffs()) / eps; |
317 |
|
✗ |
dw[i] = 0; |
318 |
|
|
} |
319 |
|
✗ |
BOOST_CHECK(Jexp3.isApprox(Jexp3_fd, sqrt(eps))); |
320 |
|
|
} |
321 |
|
|
|
322 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jexp3_quat) |
323 |
|
|
{ |
324 |
|
✗ |
SE3 M(SE3::Random()); |
325 |
|
✗ |
SE3::Quaternion quat(M.rotation()); |
326 |
|
|
|
327 |
|
✗ |
Motion dv(Motion::Zero()); |
328 |
|
✗ |
const double eps = 1e-8; |
329 |
|
|
|
330 |
|
|
typedef Eigen::Matrix<double, 7, 6> Matrix76; |
331 |
|
✗ |
Matrix76 Jexp6_fd, Jexp6_quat; |
332 |
|
✗ |
Jexp6_quat.setZero(); |
333 |
|
|
typedef Eigen::Matrix<double, 4, 3> Matrix43; |
334 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH |
335 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_MAYBE_UNINITIALIZED |
336 |
|
✗ |
Matrix43 Jexp3_quat; |
337 |
|
✗ |
Jexp3QuatLocal(quat, Jexp3_quat); |
338 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_POP |
339 |
|
✗ |
SE3 M_next; |
340 |
|
|
|
341 |
|
✗ |
Jexp6_quat.middleRows<3>(Motion::LINEAR).middleCols<3>(Motion::LINEAR) = M.rotation(); |
342 |
|
✗ |
Jexp6_quat.middleRows<4>(Motion::ANGULAR).middleCols<3>(Motion::ANGULAR) = |
343 |
|
✗ |
Jexp3_quat; // * Jlog6_SE3.middleRows<3>(Motion::ANGULAR); |
344 |
|
✗ |
for (int i = 0; i < 6; ++i) |
345 |
|
|
{ |
346 |
|
✗ |
dv.toVector()[i] = eps; |
347 |
|
✗ |
M_next = M * exp6(dv); |
348 |
|
✗ |
const SE3::Quaternion quat_next(M_next.rotation()); |
349 |
|
✗ |
Jexp6_fd.middleRows<3>(Motion::LINEAR).col(i) = (M_next.translation() - M.translation()) / eps; |
350 |
|
✗ |
Jexp6_fd.middleRows<4>(Motion::ANGULAR).col(i) = (quat_next.coeffs() - quat.coeffs()) / eps; |
351 |
|
✗ |
dv.toVector()[i] = 0.; |
352 |
|
|
} |
353 |
|
|
|
354 |
|
✗ |
BOOST_CHECK(Jexp6_quat.isApprox(Jexp6_fd, sqrt(eps))); |
355 |
|
|
} |
356 |
|
|
|
357 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jexplog3) |
358 |
|
|
{ |
359 |
|
✗ |
Motion v(Motion::Random()); |
360 |
|
|
|
361 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH |
362 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_MAYBE_UNINITIALIZED |
363 |
|
✗ |
Eigen::Matrix3d R(exp3(v.angular())), Jexp, Jlog; |
364 |
|
✗ |
Jexp3(v.angular(), Jexp); |
365 |
|
✗ |
Jlog3(R, Jlog); |
366 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_POP |
367 |
|
|
|
368 |
|
✗ |
BOOST_CHECK((Jlog * Jexp).isIdentity()); |
369 |
|
|
|
370 |
|
✗ |
SE3 M(SE3::Random()); |
371 |
|
✗ |
R = M.rotation(); |
372 |
|
✗ |
v.angular() = log3(R); |
373 |
|
✗ |
Jlog3(R, Jlog); |
374 |
|
✗ |
Jexp3(v.angular(), Jexp); |
375 |
|
|
|
376 |
|
✗ |
BOOST_CHECK((Jexp * Jlog).isIdentity()); |
377 |
|
|
} |
378 |
|
|
|
379 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jlog3_quat) |
380 |
|
|
{ |
381 |
|
✗ |
SE3::Vector3 w; |
382 |
|
✗ |
w.setRandom(); |
383 |
|
✗ |
SE3::Quaternion quat; |
384 |
|
✗ |
quaternion::exp3(w, quat); |
385 |
|
|
|
386 |
|
✗ |
SE3::Matrix3 R(quat.toRotationMatrix()); |
387 |
|
|
|
388 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH |
389 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_MAYBE_UNINITIALIZED |
390 |
|
✗ |
SE3::Matrix3 res, res_ref; |
391 |
|
✗ |
quaternion::Jlog3(quat, res); |
392 |
|
✗ |
Jlog3(R, res_ref); |
393 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_POP |
394 |
|
|
|
395 |
|
✗ |
BOOST_CHECK(res.isApprox(res_ref)); |
396 |
|
|
} |
397 |
|
|
|
398 |
|
✗ |
BOOST_AUTO_TEST_CASE(explog6) |
399 |
|
|
{ |
400 |
|
✗ |
SE3 M(SE3::Random()); |
401 |
|
✗ |
SE3 M_res = exp6(log6(M)); |
402 |
|
✗ |
BOOST_CHECK(M_res.isApprox(M)); |
403 |
|
|
|
404 |
|
✗ |
Motion v(Motion::Random()); |
405 |
|
✗ |
Motion v_res = log6(exp6(v)); |
406 |
|
✗ |
BOOST_CHECK(v_res.toVector().isApprox(v.toVector())); |
407 |
|
|
} |
408 |
|
|
|
409 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jlog6_fd) |
410 |
|
|
{ |
411 |
|
✗ |
SE3 M(SE3::Random()); |
412 |
|
|
|
413 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH |
414 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_MAYBE_UNINITIALIZED |
415 |
|
✗ |
SE3::Matrix6 Jfd, Jlog; |
416 |
|
✗ |
Jlog6(M, Jlog); |
417 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_POP |
418 |
|
✗ |
Jfd.setZero(); |
419 |
|
|
|
420 |
|
✗ |
Motion dM; |
421 |
|
✗ |
dM.setZero(); |
422 |
|
✗ |
double step = 1e-8; |
423 |
|
✗ |
for (int i = 0; i < 6; ++i) |
424 |
|
|
{ |
425 |
|
✗ |
dM.toVector()[i] = step; |
426 |
|
✗ |
SE3 M_dM = M * exp6(dM); |
427 |
|
✗ |
Jfd.col(i) = (log6(M_dM).toVector() - log6(M).toVector()) / step; |
428 |
|
✗ |
dM.toVector()[i] = 0; |
429 |
|
|
} |
430 |
|
|
|
431 |
|
✗ |
BOOST_CHECK(Jfd.isApprox(Jlog, sqrt(step))); |
432 |
|
|
|
433 |
|
✗ |
SE3::Matrix6 Jlog2 = Jlog6(M); |
434 |
|
✗ |
BOOST_CHECK(Jlog2.isApprox(Jlog)); |
435 |
|
|
} |
436 |
|
|
|
437 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jlog6_singular) |
438 |
|
|
{ |
439 |
|
✗ |
for (size_t i = 0; i < 15; i++) |
440 |
|
|
{ |
441 |
|
✗ |
SE3 M0; |
442 |
|
✗ |
if (i == 0) |
443 |
|
|
{ |
444 |
|
✗ |
M0 = SE3::Identity(); |
445 |
|
|
} |
446 |
|
|
else |
447 |
|
|
{ |
448 |
|
✗ |
M0 = SE3::Random(); |
449 |
|
|
} |
450 |
|
✗ |
SE3 dM(M0.actInv(M0)); |
451 |
|
|
|
452 |
|
✗ |
BOOST_CHECK(dM.isApprox(SE3::Identity())); |
453 |
|
✗ |
SE3::Matrix6 J0(SE3::Matrix6::Identity()); |
454 |
|
|
|
455 |
|
✗ |
SE3::Matrix6 J_val = Jlog6(dM); |
456 |
|
✗ |
BOOST_CHECK(J0.isApprox(J_val)); |
457 |
|
|
} |
458 |
|
|
} |
459 |
|
|
|
460 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jexp6_fd) |
461 |
|
|
{ |
462 |
|
✗ |
SE3 M(SE3::Random()); |
463 |
|
|
|
464 |
|
✗ |
const Motion v = log6(M); |
465 |
|
|
|
466 |
|
✗ |
SE3::Matrix6 Jexp_fd, Jexp; |
467 |
|
|
|
468 |
|
✗ |
Jexp6(Motion::Zero(), Jexp); |
469 |
|
✗ |
BOOST_CHECK(Jexp.isIdentity()); |
470 |
|
|
|
471 |
|
✗ |
Jexp6(v, Jexp); |
472 |
|
|
|
473 |
|
✗ |
Motion::Vector6 dv; |
474 |
|
✗ |
dv.setZero(); |
475 |
|
✗ |
const double eps = 1e-8; |
476 |
|
✗ |
for (int i = 0; i < 6; ++i) |
477 |
|
|
{ |
478 |
|
✗ |
dv[i] = eps; |
479 |
|
✗ |
SE3 M_next = exp6(v + Motion(dv)); |
480 |
|
✗ |
Jexp_fd.col(i) = log6(M.actInv(M_next)).toVector() / eps; |
481 |
|
✗ |
dv[i] = 0; |
482 |
|
|
} |
483 |
|
✗ |
BOOST_CHECK(Jexp_fd.isApprox(Jexp, std::sqrt(eps))); |
484 |
|
|
|
485 |
|
✗ |
SE3::Matrix6 Jexp2 = Jexp6(v); |
486 |
|
✗ |
BOOST_CHECK(Jexp2.isApprox(Jexp)); |
487 |
|
|
} |
488 |
|
|
|
489 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jlog6_of_product_fd) |
490 |
|
|
{ |
491 |
|
✗ |
SE3 Ma(SE3::Random()); |
492 |
|
✗ |
SE3 Mb(SE3::Random()); |
493 |
|
|
|
494 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH |
495 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_MAYBE_UNINITIALIZED |
496 |
|
✗ |
SE3::Matrix6 Jlog, Ja, Jb, Jfda, Jfdb; |
497 |
|
✗ |
Jlog6(Ma.inverse() * Mb, Jlog); |
498 |
|
|
PINOCCHIO_COMPILER_DIAGNOSTIC_POP |
499 |
|
✗ |
Ja = -Jlog * (Ma.inverse() * Mb).toActionMatrixInverse(); |
500 |
|
✗ |
Jb = Jlog; |
501 |
|
✗ |
Jfda.setZero(); |
502 |
|
✗ |
Jfdb.setZero(); |
503 |
|
|
|
504 |
|
✗ |
Motion dM; |
505 |
|
✗ |
dM.setZero(); |
506 |
|
✗ |
double step = 1e-8; |
507 |
|
✗ |
for (int i = 0; i < 6; ++i) |
508 |
|
|
{ |
509 |
|
✗ |
dM.toVector()[i] = step; |
510 |
|
✗ |
Jfda.col(i) = |
511 |
|
✗ |
(log6((Ma * exp6(dM)).inverse() * Mb).toVector() - log6(Ma.inverse() * Mb).toVector()) / step; |
512 |
|
✗ |
Jfdb.col(i) = |
513 |
|
✗ |
(log6(Ma.inverse() * Mb * exp6(dM)).toVector() - log6(Ma.inverse() * Mb).toVector()) / step; |
514 |
|
✗ |
dM.toVector()[i] = 0; |
515 |
|
|
} |
516 |
|
|
|
517 |
|
✗ |
BOOST_CHECK(Jfda.isApprox(Ja, sqrt(step))); |
518 |
|
✗ |
BOOST_CHECK(Jfdb.isApprox(Jb, sqrt(step))); |
519 |
|
|
} |
520 |
|
|
|
521 |
|
✗ |
BOOST_AUTO_TEST_CASE(Jexplog6) |
522 |
|
|
{ |
523 |
|
✗ |
Motion v(Motion::Random()); |
524 |
|
|
|
525 |
|
✗ |
SE3 M(exp6(v)); |
526 |
|
|
{ |
527 |
|
✗ |
Eigen::Matrix<double, 6, 6, Eigen::RowMajor> Jexp, Jlog; |
528 |
|
✗ |
Jexp6(v, Jexp); |
529 |
|
✗ |
Jlog6(M, Jlog); |
530 |
|
|
|
531 |
|
✗ |
BOOST_CHECK((Jlog * Jexp).isIdentity()); |
532 |
|
|
} |
533 |
|
|
|
534 |
|
✗ |
M.setRandom(); |
535 |
|
|
|
536 |
|
✗ |
v = log6(M); |
537 |
|
|
{ |
538 |
|
✗ |
Eigen::Matrix<double, 6, 6> Jexp, Jlog; |
539 |
|
✗ |
Jlog6(M, Jlog); |
540 |
|
✗ |
Jexp6(v, Jexp); |
541 |
|
|
|
542 |
|
✗ |
BOOST_CHECK((Jexp * Jlog).isIdentity()); |
543 |
|
|
} |
544 |
|
|
} |
545 |
|
|
|
546 |
|
✗ |
BOOST_AUTO_TEST_CASE(Hlog3_fd) |
547 |
|
|
{ |
548 |
|
|
typedef SE3::Vector3 Vector3; |
549 |
|
|
typedef SE3::Matrix3 Matrix3; |
550 |
|
✗ |
SE3::Quaternion q; |
551 |
|
✗ |
quaternion::uniformRandom(q); |
552 |
|
✗ |
Matrix3 R(q.matrix()); |
553 |
|
|
|
554 |
|
|
// Hlog3(R, v) returns the matrix H * v |
555 |
|
|
// We check that H * e_k matches the finite difference of Hlog3(R, e_k) |
556 |
|
✗ |
Vector3 dR; |
557 |
|
✗ |
dR.setZero(); |
558 |
|
✗ |
double step = 1e-8; |
559 |
|
✗ |
for (int k = 0; k < 3; ++k) |
560 |
|
|
{ |
561 |
|
✗ |
Vector3 e_k(Vector3::Zero()); |
562 |
|
✗ |
e_k[k] = 1.; |
563 |
|
|
|
564 |
|
✗ |
Matrix3 Hlog_e_k; |
565 |
|
✗ |
Hlog3(R, e_k, Hlog_e_k); |
566 |
|
|
|
567 |
|
✗ |
Matrix3 R_dR = R * exp3(step * e_k); |
568 |
|
✗ |
Matrix3 Jlog_R, Jlog_R_dR; |
569 |
|
✗ |
Jlog3(R, Jlog_R); |
570 |
|
✗ |
Jlog3(R_dR, Jlog_R_dR); |
571 |
|
|
|
572 |
|
✗ |
Matrix3 Hlog_e_k_fd = (Jlog_R_dR - Jlog_R) / step; |
573 |
|
|
|
574 |
|
✗ |
BOOST_CHECK(Hlog_e_k.isApprox(Hlog_e_k_fd, sqrt(step))); |
575 |
|
|
} |
576 |
|
|
} |
577 |
|
|
|
578 |
|
✗ |
BOOST_AUTO_TEST_CASE(test_basic) |
579 |
|
|
{ |
580 |
|
|
typedef pinocchio::SE3::Vector3 Vector3; |
581 |
|
|
typedef pinocchio::SE3::Matrix3 Matrix3; |
582 |
|
|
typedef Eigen::Matrix4d Matrix4; |
583 |
|
|
typedef pinocchio::Motion::Vector6 Vector6; |
584 |
|
|
|
585 |
|
✗ |
const double EPSILON = 1e-12; |
586 |
|
|
|
587 |
|
|
// exp3 and log3. |
588 |
|
✗ |
Vector3 v3(Vector3::Random()); |
589 |
|
✗ |
Matrix3 R(pinocchio::exp3(v3)); |
590 |
|
✗ |
BOOST_CHECK(R.transpose().isApprox(R.inverse(), EPSILON)); |
591 |
|
✗ |
BOOST_CHECK_SMALL(R.determinant() - 1.0, EPSILON); |
592 |
|
✗ |
Vector3 v3FromLog(pinocchio::log3(R)); |
593 |
|
✗ |
BOOST_CHECK(v3.isApprox(v3FromLog, EPSILON)); |
594 |
|
|
|
595 |
|
|
// exp6 and log6. |
596 |
|
✗ |
pinocchio::Motion nu = pinocchio::Motion::Random(); |
597 |
|
✗ |
pinocchio::SE3 m = pinocchio::exp6(nu); |
598 |
|
✗ |
BOOST_CHECK(m.rotation().transpose().isApprox(m.rotation().inverse(), EPSILON)); |
599 |
|
✗ |
BOOST_CHECK_SMALL(m.rotation().determinant() - 1.0, EPSILON); |
600 |
|
✗ |
pinocchio::Motion nuFromLog(pinocchio::log6(m)); |
601 |
|
✗ |
BOOST_CHECK(nu.linear().isApprox(nuFromLog.linear(), EPSILON)); |
602 |
|
✗ |
BOOST_CHECK(nu.angular().isApprox(nuFromLog.angular(), EPSILON)); |
603 |
|
|
|
604 |
|
✗ |
Vector6 v6(Vector6::Random()); |
605 |
|
✗ |
pinocchio::SE3 m2(pinocchio::exp6(v6)); |
606 |
|
✗ |
BOOST_CHECK(m2.rotation().transpose().isApprox(m2.rotation().inverse(), EPSILON)); |
607 |
|
✗ |
BOOST_CHECK_SMALL(m2.rotation().determinant() - 1.0, EPSILON); |
608 |
|
✗ |
Matrix4 M = m2.toHomogeneousMatrix(); |
609 |
|
✗ |
pinocchio::Motion nu2FromLog(pinocchio::log6(M)); |
610 |
|
✗ |
Vector6 v6FromLog(nu2FromLog.toVector()); |
611 |
|
✗ |
BOOST_CHECK(v6.isApprox(v6FromLog, EPSILON)); |
612 |
|
|
} |
613 |
|
|
|
614 |
|
✗ |
BOOST_AUTO_TEST_CASE(test_SE3_interpolate) |
615 |
|
|
{ |
616 |
|
✗ |
SE3 A = SE3::Random(); |
617 |
|
✗ |
SE3 B = SE3::Random(); |
618 |
|
|
|
619 |
|
✗ |
SE3 A_bis = SE3::Interpolate(A, B, 0.); |
620 |
|
✗ |
BOOST_CHECK(A_bis.isApprox(A)); |
621 |
|
✗ |
SE3 B_bis = SE3::Interpolate(A, B, 1.); |
622 |
|
✗ |
BOOST_CHECK(B_bis.isApprox(B)); |
623 |
|
|
|
624 |
|
✗ |
A_bis = SE3::Interpolate(A, A, 1.); |
625 |
|
✗ |
BOOST_CHECK(A_bis.isApprox(A)); |
626 |
|
|
|
627 |
|
✗ |
B_bis = SE3::Interpolate(B, B, 1.); |
628 |
|
✗ |
BOOST_CHECK(B_bis.isApprox(B)); |
629 |
|
|
|
630 |
|
✗ |
SE3 C = SE3::Interpolate(A, B, 0.5); |
631 |
|
✗ |
SE3 D = SE3::Interpolate(B, A, 0.5); |
632 |
|
✗ |
BOOST_CHECK(D.isApprox(C)); |
633 |
|
|
} |
634 |
|
|
|
635 |
|
✗ |
BOOST_AUTO_TEST_CASE(test_Jlog6_robustness) |
636 |
|
|
{ |
637 |
|
✗ |
const int num_tests = 1e1; |
638 |
|
✗ |
for (int k = 0; k < num_tests; ++k) |
639 |
|
|
{ |
640 |
|
✗ |
const SE3 M = SE3::Random(); |
641 |
|
✗ |
SE3::Matrix6 res = Jlog6(M.actInv(M)); |
642 |
|
|
|
643 |
|
✗ |
BOOST_CHECK(res.isIdentity()); |
644 |
|
|
} |
645 |
|
|
} |
646 |
|
|
|
647 |
|
|
BOOST_AUTO_TEST_SUITE_END() |
648 |
|
|
|