Program Listing for File tf2_eigen.hpp

Return to documentation for file (include/tf2_eigen/tf2_eigen.hpp)

/*
 * Copyright (c) Koji Terada
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef TF2_EIGEN__TF2_EIGEN_HPP_
#define TF2_EIGEN__TF2_EIGEN_HPP_

#include <Eigen/Geometry>

#include "geometry_msgs/msg/point.hpp"
#include "geometry_msgs/msg/point_stamped.hpp"
#include "geometry_msgs/msg/pose_stamped.hpp"
#include "geometry_msgs/msg/quaternion.hpp"
#include "geometry_msgs/msg/quaternion_stamped.hpp"
#include "geometry_msgs/msg/twist.hpp"

#include "tf2/convert.h"
#include "tf2_ros/buffer.h"
#include "tf2_ros/buffer_interface.h"

namespace tf2
{

inline
Eigen::Isometry3d transformToEigen(const geometry_msgs::msg::Transform & t)
{
  return Eigen::Isometry3d(
    Eigen::Translation3d(t.translation.x, t.translation.y, t.translation.z) *
    Eigen::Quaterniond(t.rotation.w, t.rotation.x, t.rotation.y, t.rotation.z));
}

inline
Eigen::Isometry3d transformToEigen(const geometry_msgs::msg::TransformStamped & t)
{
  return transformToEigen(t.transform);
}

inline
geometry_msgs::msg::TransformStamped eigenToTransform(const Eigen::Affine3d & T)
{
  geometry_msgs::msg::TransformStamped t;
  t.transform.translation.x = T.translation().x();
  t.transform.translation.y = T.translation().y();
  t.transform.translation.z = T.translation().z();

  Eigen::Quaterniond q(T.linear());  // assuming that upper 3x3 matrix is orthonormal
  t.transform.rotation.x = q.x();
  t.transform.rotation.y = q.y();
  t.transform.rotation.z = q.z();
  t.transform.rotation.w = q.w();

  return t;
}

inline
geometry_msgs::msg::TransformStamped eigenToTransform(const Eigen::Isometry3d & T)
{
  geometry_msgs::msg::TransformStamped t;
  t.transform.translation.x = T.translation().x();
  t.transform.translation.y = T.translation().y();
  t.transform.translation.z = T.translation().z();

  Eigen::Quaterniond q(T.rotation());
  t.transform.rotation.x = q.x();
  t.transform.rotation.y = q.y();
  t.transform.rotation.z = q.z();
  t.transform.rotation.w = q.w();

  return t;
}

template<>
inline
void doTransform(
  const Eigen::Vector3d & t_in,
  Eigen::Vector3d & t_out,
  const geometry_msgs::msg::TransformStamped & transform)
{
  t_out = Eigen::Vector3d(transformToEigen(transform) * t_in);
}

template<>
inline
void doTransform(
  const Eigen::VectorXd & t_in,
  Eigen::VectorXd & t_out,
  const geometry_msgs::msg::TransformStamped & transform)
{
  // References:
  // https://core.ac.uk/download/pdf/154240607.pdf
  // http://hades.mech.northwestern.edu/images/7/7f/MR.pdf Eq'n 3.83

  Eigen::Isometry3d affine_transform = tf2::transformToEigen(transform);

  // Build the 6x6 transformation matrix
  Eigen::MatrixXd twist_transform(6, 6);
  // upper left 3x3 block is the rotation part
  twist_transform.block(0, 0, 3, 3) = affine_transform.rotation();
  // upper right 3x3 block is all zeros
  twist_transform.block(0, 3, 3, 3) = Eigen::MatrixXd::Zero(3, 3);
  // lower left 3x3 block is tricky. See https://core.ac.uk/download/pdf/154240607.pdf
  Eigen::MatrixXd pos_vector_3x3(3, 3);
  // Disable formatting checks so the matrix remains human-readable
  /* *INDENT-OFF* */
  pos_vector_3x3 << 0, -affine_transform.translation().z(), affine_transform.translation().y(),
                    affine_transform.translation().z(), 0, -affine_transform.translation().x(),
                    -affine_transform.translation().y(), affine_transform.translation().x(), 0;
  /* *INDENT-ON* */
  twist_transform.block(3, 0, 3, 3) = pos_vector_3x3 * affine_transform.rotation();
  // lower right 3x3 block is the rotation part
  twist_transform.block(3, 3, 3, 3) = affine_transform.rotation();

  t_out = twist_transform * t_in;
}

inline
geometry_msgs::msg::Point toMsg(const Eigen::Vector3d & in)
{
  geometry_msgs::msg::Point msg;
  msg.x = in.x();
  msg.y = in.y();
  msg.z = in.z();
  return msg;
}

inline
void fromMsg(const geometry_msgs::msg::Point & msg, Eigen::Vector3d & out)
{
  out.x() = msg.x;
  out.y() = msg.y;
  out.z() = msg.z;
}

inline
geometry_msgs::msg::Vector3 & toMsg(const Eigen::Vector3d & in, geometry_msgs::msg::Vector3 & out)
{
  out.x = in.x();
  out.y = in.y();
  out.z = in.z();
  return out;
}

inline
void fromMsg(const geometry_msgs::msg::Vector3 & msg, Eigen::Vector3d & out)
{
  out.x() = msg.x;
  out.y() = msg.y;
  out.z() = msg.z;
}

template<>
inline
void doTransform(
  const tf2::Stamped<Eigen::Vector3d> & t_in,
  tf2::Stamped<Eigen::Vector3d> & t_out,
  const geometry_msgs::msg::TransformStamped & transform)
{
  t_out = tf2::Stamped<Eigen::Vector3d>(
    transformToEigen(transform) * t_in,
    tf2_ros::fromMsg(transform.header.stamp),
    transform.header.frame_id);
}

inline
geometry_msgs::msg::PointStamped toMsg(const tf2::Stamped<Eigen::Vector3d> & in)
{
  geometry_msgs::msg::PointStamped msg;
  msg.header.stamp = tf2_ros::toMsg(in.stamp_);
  msg.header.frame_id = in.frame_id_;
  msg.point = toMsg(static_cast<const Eigen::Vector3d &>(in));
  return msg;
}

inline
void fromMsg(const geometry_msgs::msg::PointStamped & msg, tf2::Stamped<Eigen::Vector3d> & out)
{
  out.stamp_ = tf2_ros::fromMsg(msg.header.stamp);
  out.frame_id_ = msg.header.frame_id;
  fromMsg(msg.point, static_cast<Eigen::Vector3d &>(out));
}

template<>
inline
void doTransform(
  const Eigen::Affine3d & t_in,
  Eigen::Affine3d & t_out,
  const geometry_msgs::msg::TransformStamped & transform)
{
  t_out = Eigen::Affine3d(transformToEigen(transform) * t_in);
}

template<>
inline
void doTransform(
  const Eigen::Isometry3d & t_in,
  Eigen::Isometry3d & t_out,
  const geometry_msgs::msg::TransformStamped & transform)
{
  t_out = Eigen::Isometry3d(transformToEigen(transform) * t_in);
}

inline
geometry_msgs::msg::Quaternion toMsg(const Eigen::Quaterniond & in)
{
  geometry_msgs::msg::Quaternion msg;
  msg.w = in.w();
  msg.x = in.x();
  msg.y = in.y();
  msg.z = in.z();
  return msg;
}

inline
void fromMsg(const geometry_msgs::msg::Quaternion & msg, Eigen::Quaterniond & out)
{
  out = Eigen::Quaterniond(msg.w, msg.x, msg.y, msg.z);
}

template<>
inline
void doTransform(
  const Eigen::Quaterniond & t_in,
  Eigen::Quaterniond & t_out,
  const geometry_msgs::msg::TransformStamped & transform)
{
  Eigen::Quaterniond t;
  fromMsg(transform.transform.rotation, t);
  t_out = t * t_in;
}

inline
geometry_msgs::msg::QuaternionStamped toMsg(const Stamped<Eigen::Quaterniond> & in)
{
  geometry_msgs::msg::QuaternionStamped msg;
  msg.header.stamp = tf2_ros::toMsg(in.stamp_);
  msg.header.frame_id = in.frame_id_;
  msg.quaternion = toMsg(static_cast<const Eigen::Quaterniond &>(in));
  return msg;
}

inline
void fromMsg(const geometry_msgs::msg::QuaternionStamped & msg, Stamped<Eigen::Quaterniond> & out)
{
  out.frame_id_ = msg.header.frame_id;
  out.stamp_ = tf2_ros::fromMsg(msg.header.stamp);
  fromMsg(msg.quaternion, static_cast<Eigen::Quaterniond &>(out));
}

template<>
inline
void doTransform(
  const tf2::Stamped<Eigen::Quaterniond> & t_in,
  tf2::Stamped<Eigen::Quaterniond> & t_out,
  const geometry_msgs::msg::TransformStamped & transform)
{
  t_out.frame_id_ = transform.header.frame_id;
  t_out.stamp_ = tf2_ros::fromMsg(transform.header.stamp);
  doTransform(
    static_cast<const Eigen::Quaterniond &>(t_in),
    static_cast<Eigen::Quaterniond &>(t_out), transform);
}

inline
geometry_msgs::msg::Pose toMsg(const Eigen::Affine3d & in)
{
  geometry_msgs::msg::Pose msg;
  msg.position.x = in.translation().x();
  msg.position.y = in.translation().y();
  msg.position.z = in.translation().z();
  Eigen::Quaterniond q(in.linear());
  msg.orientation.x = q.x();
  msg.orientation.y = q.y();
  msg.orientation.z = q.z();
  msg.orientation.w = q.w();
  return msg;
}

inline
geometry_msgs::msg::Pose toMsg(const Eigen::Isometry3d & in)
{
  geometry_msgs::msg::Pose msg;
  msg.position.x = in.translation().x();
  msg.position.y = in.translation().y();
  msg.position.z = in.translation().z();
  Eigen::Quaterniond q(in.linear());
  msg.orientation.x = q.x();
  msg.orientation.y = q.y();
  msg.orientation.z = q.z();
  msg.orientation.w = q.w();
  return msg;
}

inline
geometry_msgs::msg::Vector3 toMsg2(const Eigen::Vector3d & in)
{
  geometry_msgs::msg::Vector3 msg;
  msg.x = in(0);
  msg.y = in(1);
  msg.z = in(2);
  return msg;
}

inline
void fromMsg(const geometry_msgs::msg::Pose & msg, Eigen::Affine3d & out)
{
  out = Eigen::Affine3d(
    Eigen::Translation3d(msg.position.x, msg.position.y, msg.position.z) *
    Eigen::Quaterniond(
      msg.orientation.w,
      msg.orientation.x,
      msg.orientation.y,
      msg.orientation.z));
}

inline
void fromMsg(const geometry_msgs::msg::Pose & msg, Eigen::Isometry3d & out)
{
  out = Eigen::Isometry3d(
    Eigen::Translation3d(msg.position.x, msg.position.y, msg.position.z) *
    Eigen::Quaterniond(
      msg.orientation.w,
      msg.orientation.x,
      msg.orientation.y,
      msg.orientation.z));
}

inline
geometry_msgs::msg::Twist toMsg(const Eigen::Matrix<double, 6, 1> & in)
{
  geometry_msgs::msg::Twist msg;
  msg.linear.x = in[0];
  msg.linear.y = in[1];
  msg.linear.z = in[2];
  msg.angular.x = in[3];
  msg.angular.y = in[4];
  msg.angular.z = in[5];
  return msg;
}

inline
void fromMsg(const geometry_msgs::msg::Twist & msg, Eigen::Matrix<double, 6, 1> & out)
{
  out[0] = msg.linear.x;
  out[1] = msg.linear.y;
  out[2] = msg.linear.z;
  out[3] = msg.angular.x;
  out[4] = msg.angular.y;
  out[5] = msg.angular.z;
}

template<>
inline
void doTransform(
  const tf2::Stamped<Eigen::Affine3d> & t_in,
  tf2::Stamped<Eigen::Affine3d> & t_out,
  const geometry_msgs::msg::TransformStamped & transform)
{
  t_out = tf2::Stamped<Eigen::Affine3d>(
    transformToEigen(transform) * t_in, tf2_ros::fromMsg(
      transform.header.stamp), transform.header.frame_id);
}

template<>
inline
void doTransform(
  const tf2::Stamped<Eigen::Isometry3d> & t_in,
  tf2::Stamped<Eigen::Isometry3d> & t_out,
  const geometry_msgs::msg::TransformStamped & transform)
{
  t_out = tf2::Stamped<Eigen::Isometry3d>(
    transformToEigen(transform) * t_in, tf2_ros::fromMsg(
      transform.header.stamp), transform.header.frame_id);
}

inline
geometry_msgs::msg::PoseStamped toMsg(const tf2::Stamped<Eigen::Affine3d> & in)
{
  geometry_msgs::msg::PoseStamped msg;
  msg.header.stamp = tf2_ros::toMsg(in.stamp_);
  msg.header.frame_id = in.frame_id_;
  msg.pose = toMsg(static_cast<const Eigen::Affine3d &>(in));
  return msg;
}

inline
geometry_msgs::msg::PoseStamped toMsg(const tf2::Stamped<Eigen::Isometry3d> & in)
{
  geometry_msgs::msg::PoseStamped msg;
  msg.header.stamp = tf2_ros::toMsg(in.stamp_);
  msg.header.frame_id = in.frame_id_;
  msg.pose = toMsg(static_cast<const Eigen::Isometry3d &>(in));
  return msg;
}

inline
void fromMsg(const geometry_msgs::msg::PoseStamped & msg, tf2::Stamped<Eigen::Affine3d> & out)
{
  out.stamp_ = tf2_ros::fromMsg(msg.header.stamp);
  out.frame_id_ = msg.header.frame_id;
  fromMsg(msg.pose, static_cast<Eigen::Affine3d &>(out));
}

inline
void fromMsg(const geometry_msgs::msg::PoseStamped & msg, tf2::Stamped<Eigen::Isometry3d> & out)
{
  out.stamp_ = tf2_ros::fromMsg(msg.header.stamp);
  out.frame_id_ = msg.header.frame_id;
  fromMsg(msg.pose, static_cast<Eigen::Isometry3d &>(out));
}

}  // namespace tf2


namespace Eigen
{
// This is needed to make the usage of the following conversion functions usable in tf2::convert().
// According to clangs error note 'fromMsg'/'toMsg' should be declared prior to the call site or
// in an associated namespace of one of its arguments. The stamped versions of this conversion
// functions work because they have tf2::Stamped as an argument which is the same namespace as
// which 'fromMsg'/'toMsg' is defined in. The non-stamped versions have no argument which is
// defined in tf2, so it take the following definitions in Eigen namespace to make them usable in
// tf2::convert().

inline
geometry_msgs::msg::Pose toMsg(const Eigen::Affine3d & in)
{
  return tf2::toMsg(in);
}

inline
geometry_msgs::msg::Pose toMsg(const Eigen::Isometry3d & in)
{
  return tf2::toMsg(in);
}

inline
void fromMsg(const geometry_msgs::msg::Point & msg, Eigen::Vector3d & out)
{
  tf2::fromMsg(msg, out);
}

inline
geometry_msgs::msg::Point toMsg(const Eigen::Vector3d & in)
{
  return tf2::toMsg(in);
}

inline
void fromMsg(const geometry_msgs::msg::Pose & msg, Eigen::Affine3d & out)
{
  tf2::fromMsg(msg, out);
}

inline
void fromMsg(const geometry_msgs::msg::Pose & msg, Eigen::Isometry3d & out)
{
  tf2::fromMsg(msg, out);
}

inline
geometry_msgs::msg::Quaternion toMsg(const Eigen::Quaterniond & in)
{
  return tf2::toMsg(in);
}

inline
geometry_msgs::msg::Twist toMsg(const Eigen::Matrix<double, 6, 1> & in)
{
  return tf2::toMsg(in);
}

inline
void fromMsg(const geometry_msgs::msg::Twist & msg, Eigen::Matrix<double, 6, 1> & out)
{
  tf2::fromMsg(msg, out);
}

}  // namespace Eigen

#endif  // TF2_EIGEN__TF2_EIGEN_HPP_