/*! @file spatial.h * @brief Utility functions for manipulating spatial quantities * * This file contains functions for working with spatial vectors and * transformation matrices. */ #ifndef LIBBIOMIMETICS_SPATIAL_H #define LIBBIOMIMETICS_SPATIAL_H #include #include #include #include "Math/orientation_tools.h" namespace spatial { using namespace ori; enum class JointType { Prismatic, Revolute, FloatingBase, Nothing }; /*! * Calculate the spatial coordinate transform from A to B where B is rotate by * theta about axis. */ template SXform spatialRotation(CoordinateAxis axis, T theta) { static_assert(std::is_floating_point::value, "must use floating point value"); RotMat R = coordinateRotation(axis, theta); SXform X = SXform::Zero(); X.template topLeftCorner<3, 3>() = R; X.template bottomRightCorner<3, 3>() = R; return X; } /*! * Compute the spatial motion cross product matrix. * Prefer motionCrossProduct when possible. */ template auto motionCrossMatrix(const Eigen::MatrixBase& v) { static_assert(T::ColsAtCompileTime == 1 && T::RowsAtCompileTime == 6, "Must have 6x1 vector"); Mat6 m; m << 0, -v(2), v(1), 0, 0, 0, v(2), 0, -v(0), 0, 0, 0, -v(1), v(0), 0, 0, 0, 0, 0, -v(5), v(4), 0, -v(2), v(1), v(5), 0, -v(3), v(2), 0, -v(0), -v(4), v(3), 0, -v(1), v(0), 0; return m; } /*! * Compute spatial force cross product matrix. * Prefer forceCrossProduct when possible */ template auto forceCrossMatrix(const Eigen::MatrixBase& v) { Mat6 f; f << 0, -v(2), v(1), 0, -v(5), v(4), v(2), 0, -v(0), v(5), 0, -v(3), -v(1), v(0), 0, -v(4), v(3), 0, 0, 0, 0, 0, -v(2), v(1), 0, 0, 0, v(2), 0, -v(0), 0, 0, 0, -v(1), v(0), 0; return f; } /*! * Compute spatial motion cross product. Faster than the matrix multiplication * version */ template auto motionCrossProduct(const Eigen::MatrixBase& a, const Eigen::MatrixBase& b) { static_assert(T::ColsAtCompileTime == 1 && T::RowsAtCompileTime == 6, "Must have 6x1 vector"); SVec mv; mv << a(1) * b(2) - a(2) * b(1), a(2) * b(0) - a(0) * b(2), a(0) * b(1) - a(1) * b(0), a(1) * b(5) - a(2) * b(4) + a(4) * b(2) - a(5) * b(1), a(2) * b(3) - a(0) * b(5) - a(3) * b(2) + a(5) * b(0), a(0) * b(4) - a(1) * b(3) + a(3) * b(1) - a(4) * b(0); return mv; } /*! * Compute spatial force cross product. Faster than the matrix multiplication * version */ template auto forceCrossProduct(const Eigen::MatrixBase& a, const Eigen::MatrixBase& b) { static_assert(T::ColsAtCompileTime == 1 && T::RowsAtCompileTime == 6, "Must have 6x1 vector"); SVec mv; mv << b(2) * a(1) - b(1) * a(2) - b(4) * a(5) + b(5) * a(4), b(0) * a(2) - b(2) * a(0) + b(3) * a(5) - b(5) * a(3), b(1) * a(0) - b(0) * a(1) - b(3) * a(4) + b(4) * a(3), b(5) * a(1) - b(4) * a(2), b(3) * a(2) - b(5) * a(0), b(4) * a(0) - b(3) * a(1); return mv; } /*! * Convert a spatial transform to a homogeneous coordinate transformation */ template auto sxformToHomogeneous(const Eigen::MatrixBase& X) { static_assert(T::ColsAtCompileTime == 6 && T::RowsAtCompileTime == 6, "Must have 6x6 matrix"); Mat4 H = Mat4::Zero(); RotMat R = X.template topLeftCorner<3, 3>(); Mat3 skewR = X.template bottomLeftCorner<3, 3>(); H.template topLeftCorner<3, 3>() = R; H.template topRightCorner<3, 1>() = matToSkewVec(skewR * R.transpose()); H(3, 3) = 1; return H; } /*! * Convert a homogeneous coordinate transformation to a spatial one */ template auto homogeneousToSXform(const Eigen::MatrixBase& H) { static_assert(T::ColsAtCompileTime == 4 && T::RowsAtCompileTime == 4, "Must have 4x4 matrix"); Mat3 R = H.template topLeftCorner<3, 3>(); Vec3 translate = H.template topRightCorner<3, 1>(); Mat6 X = Mat6::Zero(); X.template topLeftCorner<3, 3>() = R; X.template bottomLeftCorner<3, 3>() = vectorToSkewMat(translate) * R; X.template bottomRightCorner<3, 3>() = R; return X; } /*! * Create spatial coordinate transformation from rotation and translation */ template auto createSXform(const Eigen::MatrixBase& R, const Eigen::MatrixBase& r) { static_assert(T::ColsAtCompileTime == 3 && T::RowsAtCompileTime == 3, "Must have 3x3 matrix"); static_assert(T2::ColsAtCompileTime == 1 && T2::RowsAtCompileTime == 3, "Must have 3x1 matrix"); Mat6 X = Mat6::Zero(); X.template topLeftCorner<3, 3>() = R; X.template bottomRightCorner<3, 3>() = R; X.template bottomLeftCorner<3, 3>() = -R * vectorToSkewMat(r); return X; } /*! * Get rotation matrix from spatial transformation */ template auto rotationFromSXform(const Eigen::MatrixBase& X) { static_assert(T::ColsAtCompileTime == 6 && T::RowsAtCompileTime == 6, "Must have 6x6 matrix"); RotMat R = X.template topLeftCorner<3, 3>(); return R; } /*! * Get translation vector from spatial transformation */ template auto translationFromSXform(const Eigen::MatrixBase& X) { static_assert(T::ColsAtCompileTime == 6 && T::RowsAtCompileTime == 6, "Must have 6x6 matrix"); RotMat R = rotationFromSXform(X); Vec3 r = -matToSkewVec(R.transpose() * X.template bottomLeftCorner<3, 3>()); return r; } /*! * Invert a spatial transformation (much faster than matrix inverse) */ template auto invertSXform(const Eigen::MatrixBase& X) { static_assert(T::ColsAtCompileTime == 6 && T::RowsAtCompileTime == 6, "Must have 6x6 matrix"); RotMat R = rotationFromSXform(X); Vec3 r = -matToSkewVec(R.transpose() * X.template bottomLeftCorner<3, 3>()); SXform Xinv = createSXform(R.transpose(), -R * r); return Xinv; } /*! * Compute joint motion subspace vector */ template SVec jointMotionSubspace(JointType joint, CoordinateAxis axis) { Vec3 v(0, 0, 0); SVec phi = SVec::Zero(); if (axis == CoordinateAxis::X) v(0) = 1; else if (axis == CoordinateAxis::Y) v(1) = 1; else v(2) = 1; if (joint == JointType::Prismatic) phi.template bottomLeftCorner<3, 1>() = v; else if (joint == JointType::Revolute) phi.template topLeftCorner<3, 1>() = v; else throw std::runtime_error("Unknown motion subspace"); return phi; } /*! * Compute joint transformation */ template Mat6 jointXform(JointType joint, CoordinateAxis axis, T q) { Mat6 X = Mat6::Zero(); if (joint == JointType::Revolute) { X = spatialRotation(axis, q); } else if (joint == JointType::Prismatic) { Vec3 v(0, 0, 0); if (axis == CoordinateAxis::X) v(0) = q; else if (axis == CoordinateAxis::Y) v(1) = q; else if (axis == CoordinateAxis::Z) v(2) = q; X = createSXform(RotMat::Identity(), v); } else { throw std::runtime_error("Unknown joint xform\n"); } return X; } /*! * Construct the rotational inertia of a uniform density box with a given mass. * @param mass Mass of the box * @param dims Dimensions of the box */ template Mat3 rotInertiaOfBox(typename T::Scalar mass, const Eigen::MatrixBase& dims) { static_assert(T::ColsAtCompileTime == 1 && T::RowsAtCompileTime == 3, "Must have 3x1 vector"); Mat3 I = Mat3::Identity() * dims.norm() * dims.norm(); for (int i = 0; i < 3; i++) I(i, i) -= dims(i) * dims(i); I = I * mass / 12; return I; } /*! * Convert from spatial velocity to linear velocity. * Uses spatial velocity at the given point. */ template auto spatialToLinearVelocity(const Eigen::MatrixBase& v, const Eigen::MatrixBase& x) { static_assert(T::ColsAtCompileTime == 1 && T::RowsAtCompileTime == 6, "Must have 6x1 vector"); static_assert(T2::ColsAtCompileTime == 1 && T2::RowsAtCompileTime == 3, "Must have 3x1 vector"); Vec3 vsAng = v.template topLeftCorner<3, 1>(); Vec3 vsLin = v.template bottomLeftCorner<3, 1>(); Vec3 vLinear = vsLin + vsAng.cross(x); return vLinear; } /*! * Convert from spatial velocity to angular velocity. */ template auto spatialToAngularVelocity(const Eigen::MatrixBase& v) { static_assert(T::ColsAtCompileTime == 1 && T::RowsAtCompileTime == 6, "Must have 6x1 vector"); Vec3 vsAng = v.template topLeftCorner<3, 1>(); return vsAng; } /*! * Compute the classical lienear accleeration of a frame given its spatial * acceleration and velocity */ template auto spatialToLinearAcceleration(const Eigen::MatrixBase& a, const Eigen::MatrixBase& v) { static_assert(T::ColsAtCompileTime == 1 && T::RowsAtCompileTime == 6, "Must have 6x1 vector"); static_assert(T2::ColsAtCompileTime == 1 && T2::RowsAtCompileTime == 6, "Must have 6x1 vector"); Vec3 acc; // classical accleration = spatial linear acc + omega x v acc = a.template tail<3>() + v.template head<3>().cross(v.template tail<3>()); return acc; } /*! * Compute the classical lienear acceleration of a frame given its spatial * acceleration and velocity */ template auto spatialToLinearAcceleration(const Eigen::MatrixBase& a, const Eigen::MatrixBase& v, const Eigen::MatrixBase& x) { static_assert(T::ColsAtCompileTime == 1 && T::RowsAtCompileTime == 6, "Must have 6x1 vector"); static_assert(T2::ColsAtCompileTime == 1 && T2::RowsAtCompileTime == 6, "Must have 6x1 vector"); static_assert(T3::ColsAtCompileTime == 1 && T3::RowsAtCompileTime == 3, "Must have 3x1 vector"); Vec3 alin_x = spatialToLinearVelocity(a, x); Vec3 vlin_x = spatialToLinearVelocity(v, x); // classical accleration = spatial linear acc + omega x v Vec3 acc = alin_x + v.template head<3>().cross(vlin_x); return acc; } /*! * Apply spatial transformation to a point. */ template auto sXFormPoint(const Eigen::MatrixBase& X, const Eigen::MatrixBase& p) { static_assert(T::ColsAtCompileTime == 6 && T::RowsAtCompileTime == 6, "Must have 6x6 vector"); static_assert(T2::ColsAtCompileTime == 1 && T2::RowsAtCompileTime == 3, "Must have 3x1 vector"); Mat3 R = rotationFromSXform(X); Vec3 r = translationFromSXform(X); Vec3 Xp = R * (p - r); return Xp; } /*! * Convert a force at a point to a spatial force * @param f : force * @param p : point */ template auto forceToSpatialForce(const Eigen::MatrixBase& f, const Eigen::MatrixBase& p) { static_assert(T::ColsAtCompileTime == 1 && T::RowsAtCompileTime == 3, "Must have 3x1 vector"); static_assert(T2::ColsAtCompileTime == 1 && T2::RowsAtCompileTime == 3, "Must have 3x1 vector"); SVec fs; fs.template topLeftCorner<3, 1>() = p.cross(f); fs.template bottomLeftCorner<3, 1>() = f; return fs; } } // namespace spatial #endif // LIBBIOMIMETICS_SPATIAL_H