pinocchio  UNKNOWN
special-orthogonal.hpp
1 //
2 // Copyright (c) 2016-2018 CNRS
3 //
4 // This file is part of Pinocchio
5 // Pinocchio is free software: you can redistribute it
6 // and/or modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation, either version
8 // 3 of the License, or (at your option) any later version.
9 //
10 // Pinocchio is distributed in the hope that it will be
11 // useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12 // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Lesser Public License for more details. You should have
14 // received a copy of the GNU Lesser General Public License along with
15 // Pinocchio If not, see
16 // <http://www.gnu.org/licenses/>.
17 
18 #ifndef __se3_special_orthogonal_operation_hpp__
19 #define __se3_special_orthogonal_operation_hpp__
20 
21 #include <limits>
22 
23 #include <pinocchio/spatial/explog.hpp>
24 #include <pinocchio/math/quaternion.hpp>
25 #include <pinocchio/multibody/liegroup/operation-base.hpp>
26 
27 namespace se3
28 {
29  template<int N> struct SpecialOrthogonalOperation {};
30  template<int N> struct traits<SpecialOrthogonalOperation<N> > {};
31 
32  template <> struct traits<SpecialOrthogonalOperation<2> > {
33  typedef double Scalar;
34  enum {
35  NQ = 2,
36  NV = 1
37  };
38  };
39 
40  template <> struct traits<SpecialOrthogonalOperation<3> > {
41  typedef double Scalar;
42  enum {
43  NQ = 4,
44  NV = 3
45  };
46  };
47 
48  template<>
49  struct SpecialOrthogonalOperation<2> : public LieGroupBase <SpecialOrthogonalOperation<2> >
50  {
51  SE3_LIE_GROUP_PUBLIC_INTERFACE(SpecialOrthogonalOperation);
52  typedef Eigen::Matrix<Scalar,2,2> Matrix2;
53 
54  static Scalar log(const Matrix2 & R)
55  {
56  Scalar theta;
57  const Scalar tr = R.trace();
58  const bool pos = (R (1, 0) > Scalar(0));
59  const Scalar PI_value = PI<Scalar>();
60  if (tr > Scalar(2)) theta = Scalar(0); // acos((3-1)/2)
61  else if (tr < Scalar(-2)) theta = (pos ? PI_value : -PI_value); // acos((-1-1)/2)
62  // Around 0, asin is numerically more stable than acos because
63  // acos(x) = PI/2 - x and asin(x) = x (the precision of x is not lost in PI/2).
64  else if (tr > Scalar(2) - 1e-2) theta = asin ((R(1,0) - R(0,1)) / Scalar(2));
65  else theta = (pos ? acos (tr/Scalar(2)) : -acos(tr/Scalar(2)));
66  assert (theta == theta); // theta != NaN
67  assert (fabs(theta - atan2 (R(1,0), R(0,0))) < 1e-6);
68  return theta;
69  }
70 
71  static Scalar Jlog (const Matrix2&)
72  {
73  return 1;
74  }
75 
80  Index nq () const
81  {
82  return NQ;
83  }
85  Index nv () const
86  {
87  return NV;
88  }
89 
90  ConfigVector_t neutral () const
91  {
92  ConfigVector_t n; n.setZero(); n[0] = Scalar(1);
93  return n;
94  }
95 
96  std::string name () const
97  {
98  return std::string ("SO(2)");
99  }
100 
101  template <class ConfigL_t, class ConfigR_t, class Tangent_t>
102  static void difference_impl(const Eigen::MatrixBase<ConfigL_t> & q0,
103  const Eigen::MatrixBase<ConfigR_t> & q1,
104  const Eigen::MatrixBase<Tangent_t> & d)
105  {
106  if (q0 == q1) {
107  (const_cast < Tangent_t& > (d.derived())).setZero ();
108  return;
109  }
110  Matrix2 R; // R0.transpose() * R1;
111  R(0,0) = R(1,1) = q0.dot(q1);
112  R(1,0) = q0(0) * q1(1) - q0(1) * q1(0);
113  R(0,1) = - R(1,0);
114  const_cast < Tangent_t& > (d.derived()) [0] = log (R);
115  }
116 
117  template <class ConfigL_t, class ConfigR_t, class JacobianLOut_t, class JacobianROut_t>
118  static void Jdifference_impl(const Eigen::MatrixBase<ConfigL_t> & q0,
119  const Eigen::MatrixBase<ConfigR_t> & q1,
120  const Eigen::MatrixBase<JacobianLOut_t>& J0,
121  const Eigen::MatrixBase<JacobianROut_t>& J1)
122  {
123  Matrix2 R; // R0.transpose() * R1;
124  R(0,0) = R(1,1) = q0.dot(q1);
125  R(1,0) = q0(0) * q1(1) - q0(1) * q1(0);
126  R(0,1) = - R(1,0);
127 
128  Scalar w (Jlog(R));
129  const_cast< JacobianLOut_t& > (J0.derived()).coeffRef(0,0) = -w;
130  const_cast< JacobianROut_t& > (J1.derived()).coeffRef(0,0) = w;
131  }
132 
133  template <class ConfigIn_t, class Velocity_t, class ConfigOut_t>
134  static void integrate_impl(const Eigen::MatrixBase<ConfigIn_t> & q,
135  const Eigen::MatrixBase<Velocity_t> & v,
136  const Eigen::MatrixBase<ConfigOut_t> & qout)
137  {
138  ConfigOut_t& out = (const_cast< Eigen::MatrixBase<ConfigOut_t>& >(qout)).derived();
139 
140  const Scalar & ca = q(0);
141  const Scalar & sa = q(1);
142  const Scalar & omega = v(0);
143 
144  Scalar cosOmega,sinOmega; SINCOS(omega, &sinOmega, &cosOmega);
145  // TODO check the cost of atan2 vs SINCOS
146 
147  out << cosOmega * ca - sinOmega * sa,
148  sinOmega * ca + cosOmega * sa;
149  const Scalar norm2 = q.squaredNorm();
150  out *= (3 - norm2) / 2;
151  }
152 
153  template <class Config_t, class Tangent_t, class JacobianOut_t>
154  static void dIntegrate_dq_impl(const Eigen::MatrixBase<Config_t > & /*q*/,
155  const Eigen::MatrixBase<Tangent_t> & /*v*/,
156  const Eigen::MatrixBase<JacobianOut_t>& J)
157  {
158  JacobianOut_t& Jout = const_cast< JacobianOut_t& >(J.derived());
159  Jout(0,0) = 1;
160  }
161 
162  template <class Config_t, class Tangent_t, class JacobianOut_t>
163  static void dIntegrate_dv_impl(const Eigen::MatrixBase<Config_t > & /*q*/,
164  const Eigen::MatrixBase<Tangent_t> & /*v*/,
165  const Eigen::MatrixBase<JacobianOut_t>& J)
166  {
167  JacobianOut_t& Jout = const_cast< JacobianOut_t& >(J.derived());
168  Jout(0,0) = 1;
169  }
170 
171  template <class ConfigL_t, class ConfigR_t, class ConfigOut_t>
172  static void interpolate_impl(const Eigen::MatrixBase<ConfigL_t> & q0,
173  const Eigen::MatrixBase<ConfigR_t> & q1,
174  const Scalar& u,
175  const Eigen::MatrixBase<ConfigOut_t>& qout)
176  {
177  ConfigOut_t& out = (const_cast< Eigen::MatrixBase<ConfigOut_t>& >(qout)).derived();
178 
179  assert ( (q0.norm() - 1) < 1e-8 && "initial configuration not normalized");
180  assert ( (q1.norm() - 1) < 1e-8 && "final configuration not normalized");
181  Scalar cosTheta = q0.dot(q1);
182  Scalar sinTheta = q0(0)*q1(1) - q0(1)*q1(0);
183  Scalar theta = atan2(sinTheta, cosTheta);
184  assert (fabs (sin (theta) - sinTheta) < 1e-8);
185 
186  const Scalar PI_value = PI<Scalar>();
187 
188  if (fabs (theta) > 1e-6 && fabs (theta) < PI_value - 1e-6)
189  {
190  out = (sin ((1-u)*theta)/sinTheta) * q0
191  + (sin ( u *theta)/sinTheta) * q1;
192  }
193  else if (fabs (theta) < 1e-6) // theta = 0
194  {
195  out = (1-u) * q0 + u * q1;
196  }
197  else // theta = +-PI
198  {
199  Scalar theta0 = atan2 (q0(1), q0(0));
200  SINCOS(theta0,&out[1],&out[0]);
201  }
202  }
203 
204  // template <class ConfigL_t, class ConfigR_t>
205  // static double squaredDistance_impl(const Eigen::MatrixBase<ConfigL_t> & q0,
206  // const Eigen::MatrixBase<ConfigR_t> & q1)
207 
208  template <class Config_t>
209  static void normalize_impl (const Eigen::MatrixBase<Config_t>& qout)
210  {
211  Config_t& qout_ = (const_cast< Eigen::MatrixBase<Config_t>& >(qout)).derived();
212  qout_.normalize();
213  }
214 
215  template <class Config_t>
216  void random_impl (const Eigen::MatrixBase<Config_t>& qout) const
217  {
218  Config_t& out = (const_cast< Eigen::MatrixBase<Config_t>& >(qout)).derived();
219 
220  const Scalar PI_value = PI<Scalar>();
221  const Scalar angle = -PI_value + Scalar(2)* PI_value * ((Scalar)rand())/RAND_MAX;
222  SINCOS(angle, &out(1), &out(0));
223  }
224 
225  template <class ConfigL_t, class ConfigR_t, class ConfigOut_t>
226  void randomConfiguration_impl(const Eigen::MatrixBase<ConfigL_t> &,
227  const Eigen::MatrixBase<ConfigR_t> &,
228  const Eigen::MatrixBase<ConfigOut_t> & qout)
229  const
230  {
231  random_impl(qout);
232  }
233  }; // struct SpecialOrthogonalOperation<2>
234 
235  template<>
236  struct SpecialOrthogonalOperation<3> : public LieGroupBase <SpecialOrthogonalOperation<3> >
237  {
238  SE3_LIE_GROUP_PUBLIC_INTERFACE(SpecialOrthogonalOperation);
239 
240  typedef Eigen::Quaternion<Scalar> Quaternion_t;
241  typedef Eigen::Map< Quaternion_t> QuaternionMap_t;
242  typedef Eigen::Map<const Quaternion_t> ConstQuaternionMap_t;
243 
248  Index nq () const
249  {
250  return NQ;
251  }
253  Index nv () const
254  {
255  return NV;
256  }
257 
258  ConfigVector_t neutral () const
259  {
260  ConfigVector_t n; n.setZero (); n[3] = Scalar(1);
261  return n;
262  }
263 
264  std::string name () const
265  {
266  return std::string ("SO(3)");
267  }
268 
269  template <class ConfigL_t, class ConfigR_t, class Tangent_t>
270  static void difference_impl(const Eigen::MatrixBase<ConfigL_t> & q0,
271  const Eigen::MatrixBase<ConfigR_t> & q1,
272  const Eigen::MatrixBase<Tangent_t> & d)
273  {
274  if (q0 == q1) {
275  (const_cast < Eigen::MatrixBase<Tangent_t>& > (d)).setZero ();
276  return;
277  }
278  ConstQuaternionMap_t p0 (q0.derived().data());
279  ConstQuaternionMap_t p1 (q1.derived().data());
280  const_cast < Eigen::MatrixBase<Tangent_t>& > (d)
281  = log3((p0.matrix().transpose() * p1.matrix()).eval());
282  }
283 
284  template <class ConfigL_t, class ConfigR_t, class JacobianLOut_t, class JacobianROut_t>
285  static void Jdifference_impl(const Eigen::MatrixBase<ConfigL_t> & q0,
286  const Eigen::MatrixBase<ConfigR_t> & q1,
287  const Eigen::MatrixBase<JacobianLOut_t>& J0,
288  const Eigen::MatrixBase<JacobianROut_t>& J1)
289  {
290  ConstQuaternionMap_t p0 (q0.derived().data());
291  ConstQuaternionMap_t p1 (q1.derived().data());
292  Eigen::Matrix<Scalar, 3, 3> R = p0.matrix().transpose() * p1.matrix();
293 
294  Jlog3 (R, J1);
295 
296  JacobianLOut_t& J0v = const_cast< JacobianLOut_t& > (J0.derived());
297  J0v.noalias() = - J1 * R.transpose();
298  }
299 
300  template <class ConfigIn_t, class Velocity_t, class ConfigOut_t>
301  static void integrate_impl(const Eigen::MatrixBase<ConfigIn_t> & q,
302  const Eigen::MatrixBase<Velocity_t> & v,
303  const Eigen::MatrixBase<ConfigOut_t> & qout)
304  {
305  ConstQuaternionMap_t quat(q.derived().data());
306  QuaternionMap_t quaternion_result (
307  (const_cast< Eigen::MatrixBase<ConfigOut_t>& >(qout)).derived().data()
308  );
309 
310  Quaternion_t pOmega(exp3(v));
311  quaternion_result = quat * pOmega;
312  firstOrderNormalize(quaternion_result);
313  }
314 
315  template <class Config_t, class Tangent_t, class JacobianOut_t>
316  static void dIntegrate_dq_impl(const Eigen::MatrixBase<Config_t > & /*q*/,
317  const Eigen::MatrixBase<Tangent_t> & v,
318  const Eigen::MatrixBase<JacobianOut_t>& J)
319  {
320  JacobianOut_t& Jout = const_cast< JacobianOut_t& >(J.derived());
321  Jout = exp3(v).transpose();
322  }
323 
324  template <class Config_t, class Tangent_t, class JacobianOut_t>
325  static void dIntegrate_dv_impl(const Eigen::MatrixBase<Config_t > & /*q*/,
326  const Eigen::MatrixBase<Tangent_t> & v,
327  const Eigen::MatrixBase<JacobianOut_t>& J)
328  {
329  Jexp3 (v, J.derived());
330  }
331 
332  template <class ConfigL_t, class ConfigR_t, class ConfigOut_t>
333  static void interpolate_impl(const Eigen::MatrixBase<ConfigL_t> & q0,
334  const Eigen::MatrixBase<ConfigR_t> & q1,
335  const Scalar& u,
336  const Eigen::MatrixBase<ConfigOut_t>& qout)
337  {
338  ConstQuaternionMap_t p0 (q0.derived().data());
339  ConstQuaternionMap_t p1 (q1.derived().data());
340  QuaternionMap_t quaternion_result (
341  (const_cast< Eigen::MatrixBase<ConfigOut_t>& >(qout)).derived().data()
342  );
343 
344  quaternion_result = p0.slerp(u, p1);
345  }
346 
347  template <class ConfigL_t, class ConfigR_t>
348  static double squaredDistance_impl(const Eigen::MatrixBase<ConfigL_t> & q0,
349  const Eigen::MatrixBase<ConfigR_t> & q1)
350  {
351  TangentVector_t t;
352  difference_impl(q0, q1, t);
353  return t.squaredNorm();
354  }
355 
356  template <class Config_t>
357  static void normalize_impl (const Eigen::MatrixBase<Config_t>& qout)
358  {
359  Config_t& qout_ = (const_cast< Eigen::MatrixBase<Config_t>& >(qout)).derived();
360  qout_.normalize();
361  }
362 
363  template <class Config_t>
364  void random_impl (const Eigen::MatrixBase<Config_t>& qout) const
365  {
366  QuaternionMap_t out (
367  (const_cast< Eigen::MatrixBase<Config_t>& >(qout)).derived().data()
368  );
369  uniformRandom(out);
370  }
371 
372  template <class ConfigL_t, class ConfigR_t, class ConfigOut_t>
373  void randomConfiguration_impl(const Eigen::MatrixBase<ConfigL_t> &,
374  const Eigen::MatrixBase<ConfigR_t> &,
375  const Eigen::MatrixBase<ConfigOut_t> & qout)
376  const
377  {
378  random_impl(qout);
379  }
380 
381  template <class ConfigL_t, class ConfigR_t>
382  static bool isSameConfiguration_impl(const Eigen::MatrixBase<ConfigL_t> & q0,
383  const Eigen::MatrixBase<ConfigR_t> & q1,
384  const Scalar & prec)
385  {
386  ConstQuaternionMap_t quat1(q0.derived().data());
387  ConstQuaternionMap_t quat2(q1.derived().data());
388 
389  return defineSameRotation(quat1,quat2,prec);
390  }
391  }; // struct SpecialOrthogonalOperation<3>
392 } // namespace se3
393 
394 #endif // ifndef __se3_special_orthogonal_operation_hpp__
void Jexp3(const Eigen::MatrixBase< Vector3Like > &r, const Eigen::MatrixBase< Matrix3Like > &Jexp)
Derivative of .
Definition: explog.hpp:154
std::string name(const LieGroupVariant &lg)
Visit a LieGroupVariant to get the name of it.
Eigen::Matrix< typename Matrix3Like::Scalar, 3, 1, Eigen::internal::traits< Matrix3Like >::Options > log3(const Eigen::MatrixBase< Matrix3Like > &R, S2 &theta)
Same as log3.
Definition: explog.hpp:91
Eigen::VectorXd neutral(const Model &model)
Return the neutral configuration element related to the model configuration space.
void uniformRandom(const Eigen::QuaternionBase< D > &q)
Uniformly random quaternion sphere.
Definition: quaternion.hpp:107
Index nv() const
Get dimension of Lie Group tangent space.
Eigen::Matrix< typename Vector3Like::Scalar, 3, 3, EIGEN_PLAIN_TYPE(Vector3Like)::Options > exp3(const Eigen::MatrixBase< Vector3Like > &v)
Exp: so3 -> SO3.
Definition: explog.hpp:43
void firstOrderNormalize(const Eigen::QuaternionBase< D > &q)
Definition: quaternion.hpp:88
Index nv() const
Get dimension of Lie Group tangent space.
bool defineSameRotation(const Eigen::QuaternionBase< D1 > &q1, const Eigen::QuaternionBase< D2 > &q2, const typename D1::RealScalar &prec=Eigen::NumTraits< typename D1::Scalar >::dummy_precision())
Check if two quaternions define the same rotations.
Definition: quaternion.hpp:59