Newer
Older
#ifndef RIGID_BODY_MOTION_HH
#define RIGID_BODY_MOTION_HH
#include <dune/common/fvector.hh>
/** \brief A rigid-body motion in R^N, i.e., a member of SE(N) */
template <class T, int N>
struct RigidBodyMotion
/** \brief Dimension of manifold */
static const int dim = N + Rotation<T,N>::dim;
/** \brief Dimension of the embedding space */
static const int embeddedDim = N + Rotation<T,N>::embeddedDim;
/** \brief Type of an infinitesimal rigid body motion */
typedef Dune::FieldVector<T, dim> TangentVector;
/** \brief Type of an infinitesimal rigid body motion */
typedef Dune::FieldVector<T, embeddedDim> EmbeddedTangentVector;
/** \brief The type used for coordinates */
typedef T ctype;
/** \brief The type used for global coordinates */
typedef Dune::FieldVector<T,embeddedDim> CoordinateType;
/** \brief The global convexity radius of the rigid body motions */
static constexpr double convexityRadius = Rotation<T,N>::convexityRadius;
/** \brief Default constructor */
RigidBodyMotion()
{}
/** \brief Constructor from a translation and a rotation */
RigidBodyMotion(const Dune::FieldVector<ctype, N>& translation,
const Rotation<ctype,N>& rotation)
: r(translation), q(rotation)
{}

Oliver Sander
committed
RigidBodyMotion(const CoordinateType& globalCoordinates)
{
for (int i=0; i<N; i++)
r[i] = globalCoordinates[i];
for (int i=N; i<embeddedDim; i++)
q[i-N] = globalCoordinates[i];
// Turn this into a unit quaternion if it isn't already
q.normalize();
}
/** \brief Assigment from RigidBodyMotion with different type -- used for automatic differentiation with ADOL-C */
template <class T2>
RigidBodyMotion& operator <<= (const RigidBodyMotion<T2,N>& other) {
for (int i=0; i<N; i++)
r[i] <<= other.r[i];
q <<= other.q;
return *this;
}
/** \brief Rebind the RigidBodyMotion to another coordinate type */
template<class U>
struct rebind
{
typedef RigidBodyMotion<U,N> other;
};
/** \brief The exponential map from a given point $p \in SE(d)$.
Why the template parameter? Well, it should work with both TangentVector and EmbeddedTangentVector.
In general these differ and we could just have two exp methods. However in 2d they do _not_ differ,
and then the compiler complains about having two methods with the same signature.
*/
template <class TVector>
static RigidBodyMotion<ctype,N> exp(const RigidBodyMotion<ctype,N>& p, const TVector& v) {

Oliver Sander
committed
RigidBodyMotion<ctype,N> result;

Oliver Sander
committed
// Add translational correction
for (int i=0; i<N; i++)

Oliver Sander
committed
result.r[i] = p.r[i] + v[i];
// Add rotational correction
typedef typename std::conditional<Dune::is_same<TVector,TangentVector>::value,
typename Rotation<ctype,N>::TangentVector,
typename Rotation<ctype,N>::EmbeddedTangentVector>::type RotationTangentVector;
RotationTangentVector qCorr;
for (int i=0; i<RotationTangentVector::dimension; i++)
qCorr[i] = v[N+i];

Oliver Sander
committed
result.q = Rotation<ctype,N>::exp(p.q, qCorr);

Oliver Sander
committed
return result;
}
/** \brief Compute geodesic distance from a to b */
static T distance(const RigidBodyMotion<ctype,N>& a, const RigidBodyMotion<ctype,N>& b) {
T euclideanDistanceSquared = (a.r - b.r).two_norm2();
T rotationDistance = Rotation<ctype,N>::distance(a.q, b.q);
return std::sqrt(euclideanDistanceSquared + rotationDistance*rotationDistance);
}
/** \brief Compute difference vector from a to b on the tangent space of a */
static TangentVector difference(const RigidBodyMotion<ctype,N>& a,
const RigidBodyMotion<ctype,N>& b) {
TangentVector result;
// Usual linear difference
for (int i=0; i<N; i++)
result[i] = a.r[i] - b.r[i];
// Subtract orientations on the tangent space of 'a'
typename Rotation<ctype,N>::TangentVector v = Rotation<ctype,N>::difference(a.q, b.q).axial();
for (int i=0; i<Rotation<ctype,N>::TangentVector::dimension; i++)
result[i+N] = v[i];
static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const RigidBodyMotion<ctype,N>& a,
const RigidBodyMotion<ctype,N>& b) {
// linear part
Dune::FieldVector<ctype,N> linearDerivative = a.r;
linearDerivative -= b.r;
linearDerivative *= -2;
// rotation part
typename Rotation<ctype,N>::EmbeddedTangentVector rotationDerivative
= Rotation<ctype,N>::derivativeOfDistanceSquaredWRTSecondArgument(a.q, b.q);
return concat(linearDerivative, rotationDerivative);
}

Oliver Sander
committed
/** \brief Compute the Hessian of the squared distance function keeping the first argument fixed */
static Dune::FieldMatrix<T,embeddedDim,embeddedDim> secondDerivativeOfDistanceSquaredWRTSecondArgument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q)
Dune::FieldMatrix<T,embeddedDim,embeddedDim> result(0);
// The linear part
Dune::FieldMatrix<T,N,N> linearPart = RealTuple<T,N>::secondDerivativeOfDistanceSquaredWRTSecondArgument(p.r,q.r);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[i][j] = linearPart[i][j];
// The rotation part
Dune::FieldMatrix<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart
= Rotation<ctype,N>::secondDerivativeOfDistanceSquaredWRTSecondArgument(p.q,q.q);
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
for (int j=0; j<Rotation<T,N>::embeddedDim; j++)
result[N+i][N+j] = rotationPart[i][j];
return result;

Oliver Sander
committed
}

Oliver Sander
committed
/** \brief Compute the mixed second derivate \partial d^2 / \partial da db
Unlike the distance itself the squared distance is differentiable at zero
*/
static Dune::FieldMatrix<T,embeddedDim,embeddedDim> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q)
Dune::FieldMatrix<T,embeddedDim,embeddedDim> result(0);
// The linear part
Dune::FieldMatrix<T,N,N> linearPart = RealTuple<T,N>::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(p.r,q.r);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
result[i][j] = linearPart[i][j];
// The rotation part
Dune::FieldMatrix<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart
= Rotation<ctype,N>::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(p.q,q.q);
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
for (int j=0; j<Rotation<T,N>::embeddedDim; j++)
result[N+i][N+j] = rotationPart[i][j];
return result;

Oliver Sander
committed
}

Oliver Sander
committed
/** \brief Compute the third derivative \partial d^3 / \partial dq^3
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,embeddedDim,embeddedDim,embeddedDim> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q)
Tensor3<T,embeddedDim,embeddedDim,embeddedDim> result(0);
// The linear part
Tensor3<T,N,N,N> linearPart = RealTuple<T,N>::thirdDerivativeOfDistanceSquaredWRTSecondArgument(p.r,q.r);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++)
result[i][j][k] = linearPart[i][j][k];
// The rotation part
Tensor3<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart
= Rotation<ctype,N>::thirdDerivativeOfDistanceSquaredWRTSecondArgument(p.q,q.q);
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
for (int j=0; j<Rotation<T,N>::embeddedDim; j++)
for (int k=0; k<Rotation<T,N>::embeddedDim; k++)
result[N+i][N+j][N+k] = rotationPart[i][j][k];
return result;

Oliver Sander
committed
}

Oliver Sander
committed
/** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2
Unlike the distance itself the squared distance is differentiable at zero
*/
static Tensor3<T,embeddedDim,embeddedDim,embeddedDim> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q)
Tensor3<T,embeddedDim,embeddedDim,embeddedDim> result(0);
// The linear part
Tensor3<T,N,N,N> linearPart = RealTuple<T,N>::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(p.r,q.r);
for (int i=0; i<N; i++)
for (int j=0; j<N; j++)
for (int k=0; k<N; k++)
result[i][j][k] = linearPart[i][j][k];
// The rotation part
Tensor3<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart = Rotation<ctype,N>::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(p.q,q.q);
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
for (int j=0; j<Rotation<T,N>::embeddedDim; j++)
for (int k=0; k<Rotation<T,N>::embeddedDim; k++)
result[N+i][N+j][N+k] = rotationPart[i][j][k];
return result;

Oliver Sander
committed
}

Oliver Sander
committed
/** \brief Project tangent vector of R^n onto the tangent space */
EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const {
EmbeddedTangentVector result;
// translation part
for (int i=0; i<N; i++)
result[i] = v[i];
// rotation part
typename Rotation<T,N>::EmbeddedTangentVector rotV;
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
rotV[i] = v[i+N];
rotV = q.projectOntoTangentSpace(rotV);
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
result[i+N] = rotV[i];
return result;

Oliver Sander
committed
}
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/** \brief Project tangent vector of R^n onto the normal space space */
EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const {
EmbeddedTangentVector result;
// translation part
for (int i=0; i<N; i++)
result[i] = v[i];
// rotation part
T sp = 0;
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
sp += v[i+N] * q[i];
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
result[i+N] = sp * q[i];
return result;
}
/** \brief The Weingarten map */
EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const {
EmbeddedTangentVector result;
// translation part: nothing, the space is flat
for (int i=0; i<N; i++)
result[i] = 0;
// rotation part
T sp = 0;
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
sp += v[i+N] * q[i];
for (int i=0; i<Rotation<T,N>::embeddedDim; i++)
result[i+N] = -sp * z[i+N];
return result;
}

Oliver Sander
committed
/** \brief Compute an orthonormal basis of the tangent space of SE(3).
This basis may not be globally continuous.

Oliver Sander
committed
*/
Dune::FieldMatrix<T,dim,embeddedDim> orthonormalFrame() const {
Dune::FieldMatrix<T,dim,embeddedDim> result(0);

Oliver Sander
committed
// Get the R^d part
for (int i=0; i<N; i++)

Oliver Sander
committed
result[i][i] = 1;
Dune::FieldMatrix<T,Rotation<T,N>::dim,Rotation<T,N>::embeddedDim> SO3Part = q.orthonormalFrame();

Oliver Sander
committed
for (int i=0; i<Rotation<T,N>::dim; i++)
for (int j=0; j<Rotation<T,N>::embeddedDim; j++)
result[N+i][N+j] = SO3Part[i][j];

Oliver Sander
committed
return result;
}
/** \brief The global coordinates, if you really want them */
CoordinateType globalCoordinates() const {
return concat(r, q.globalCoordinates());
}

Oliver Sander
committed
Dune::FieldVector<ctype, N> r;
Rotation<ctype,N> q;
private:
/** \brief Concatenate two FieldVectors */
template <int NN, int M>
static Dune::FieldVector<ctype,NN+M> concat(const Dune::FieldVector<ctype,NN>& a,
const Dune::FieldVector<ctype,M>& b)
{
Dune::FieldVector<ctype,NN+M> result;
for (int i=0; i<NN; i++)
result[i] = a[i];
for (int i=0; i<M; i++)
result[i+NN] = b[i];
return result;
}
template <int N, class ctype>
std::ostream& operator<< (std::ostream& s, const RigidBodyMotion<ctype,N>& c)
{
s << "(" << c.r << ") (" << c.q << ")";
return s;
}