Port transforms library to ROS2 (#301)

* Port transforms library to ROS2

- Port the transforms library to ROS2
- Update CMakeLists
- Update package.xml
- Enable the package

Signed-off-by: Sean Kelly <sean@seankelly.dev>

* Feedback from PR
This commit is contained in:
Sean Kelly
2020-08-12 15:12:51 -04:00
committed by GitHub
parent 420f5b032b
commit d7a79b927f
7 changed files with 517 additions and 392 deletions
+86 -46
View File
@@ -37,9 +37,22 @@
#ifndef PCL_ROS__IMPL__TRANSFORMS_HPP_
#define PCL_ROS__IMPL__TRANSFORMS_HPP_
#include <pcl_conversions/pcl_conversions.h>
#include <string>
#include "pcl_ros/transforms.hpp"
#include <pcl/common/transforms.h>
#include <pcl_conversions/pcl_conversions.h>
#include <tf2/convert.h>
#include <tf2/exceptions.h>
#include <tf2/LinearMath/Transform.h>
#include <tf2/LinearMath/Quaternion.h>
#include <tf2/LinearMath/Vector3.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <tf2_ros/buffer.h>
#include <tf2_ros/transform_listener.h>
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <rclcpp/logging.hpp>
#include <rclcpp/time.hpp>
#include <Eigen/Dense>
#include <string>
using pcl_conversions::fromPCL;
using pcl_conversions::toPCL;
@@ -51,16 +64,16 @@ template<typename PointT>
void
transformPointCloudWithNormals(
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out, const tf::Transform & transform)
pcl::PointCloud<PointT> & cloud_out, const tf2::Transform & transform)
{
// Bullet (used by tf) and Eigen both store quaternions in x,y,z,w order, despite the ordering
// of arguments in Eigen's constructor. We could use an Eigen Map to convert without copy, but
// this only works if Bullet uses floats, that is if BT_USE_DOUBLE_PRECISION is not defined.
// Rather that risking a mistake, we copy the quaternion, which is a small cost compared to
// the conversion of the point cloud anyway. Idem for the origin.
tf::Quaternion q = transform.getRotation();
tf2::Quaternion q = transform.getRotation();
Eigen::Quaternionf rotation(q.w(), q.x(), q.y(), q.z()); // internally stored as (x,y,z,w)
tf::Vector3 v = transform.getOrigin();
tf2::Vector3 v = transform.getOrigin();
Eigen::Vector3f origin(v.x(), v.y(), v.z());
// Eigen::Translation3f translation(v);
// Assemble an Eigen Transform
@@ -69,21 +82,33 @@ transformPointCloudWithNormals(
transformPointCloudWithNormals(cloud_in, cloud_out, origin, rotation);
}
//////////////////////////////////////////////////////////////////////////////////////////////
template<typename PointT>
void
transformPointCloudWithNormals(
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out, const geometry_msgs::msg::TransformStamped & transform)
{
tf2::Transform tf;
tf2::convert(transform.transform, tf);
transformPointCloudWithNormals(cloud_in, cloud_out, tf);
}
//////////////////////////////////////////////////////////////////////////////////////////////
template<typename PointT>
void
transformPointCloud(
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out, const tf::Transform & transform)
pcl::PointCloud<PointT> & cloud_out, const tf2::Transform & transform)
{
// Bullet (used by tf) and Eigen both store quaternions in x,y,z,w order, despite the ordering
// of arguments in Eigen's constructor. We could use an Eigen Map to convert without copy, but
// this only works if Bullet uses floats, that is if BT_USE_DOUBLE_PRECISION is not defined.
// Rather that risking a mistake, we copy the quaternion, which is a small cost compared to
// the conversion of the point cloud anyway. Idem for the origin.
tf::Quaternion q = transform.getRotation();
tf2::Quaternion q = transform.getRotation();
Eigen::Quaternionf rotation(q.w(), q.x(), q.y(), q.z()); // internally stored as (x,y,z,w)
tf::Vector3 v = transform.getOrigin();
tf2::Vector3 v = transform.getOrigin();
Eigen::Vector3f origin(v.x(), v.y(), v.z());
// Eigen::Translation3f translation(v);
// Assemble an Eigen Transform
@@ -92,6 +117,18 @@ transformPointCloud(
transformPointCloud(cloud_in, cloud_out, origin, rotation);
}
//////////////////////////////////////////////////////////////////////////////////////////////
template<typename PointT>
void
transformPointCloud(
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out, const geometry_msgs::msg::TransformStamped & transform)
{
tf2::Transform tf;
tf2::convert(transform.transform, tf);
transformPointCloud(cloud_in, cloud_out, tf);
}
//////////////////////////////////////////////////////////////////////////////////////////////
template<typename PointT>
bool
@@ -99,23 +136,24 @@ transformPointCloudWithNormals(
const std::string & target_frame,
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out,
const tf::TransformListener & tf_listener)
const tf2_ros::Buffer & tf_buffer)
{
if (cloud_in.header.frame_id == target_frame) {
cloud_out = cloud_in;
return true;
}
tf::StampedTransform transform;
geometry_msgs::msg::TransformStamped transform;
try {
tf_listener.lookupTransform(
target_frame, cloud_in.header.frame_id, fromPCL(
cloud_in.header).stamp, transform);
} catch (tf::LookupException & e) {
ROS_ERROR("%s", e.what());
transform =
tf_buffer.lookupTransform(
target_frame, cloud_in.header.frame_id,
fromPCL(cloud_in.header.stamp));
} catch (tf2::LookupException & e) {
RCLCPP_ERROR(rclcpp::get_logger("pcl_ros"), "%s", e.what());
return false;
} catch (tf::ExtrapolationException & e) {
ROS_ERROR("%s", e.what());
} catch (tf2::ExtrapolationException & e) {
RCLCPP_ERROR(rclcpp::get_logger("pcl_ros"), "%s", e.what());
return false;
}
@@ -131,25 +169,27 @@ transformPointCloud(
const std::string & target_frame,
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out,
const tf::TransformListener & tf_listener)
const tf2_ros::Buffer & tf_buffer)
{
if (cloud_in.header.frame_id == target_frame) {
cloud_out = cloud_in;
return true;
}
tf::StampedTransform transform;
geometry_msgs::msg::TransformStamped transform;
try {
tf_listener.lookupTransform(
target_frame, cloud_in.header.frame_id, fromPCL(
cloud_in.header).stamp, transform);
} catch (tf::LookupException & e) {
ROS_ERROR("%s", e.what());
transform =
tf_buffer.lookupTransform(
target_frame, cloud_in.header.frame_id,
fromPCL(cloud_in.header.stamp));
} catch (tf2::LookupException & e) {
RCLCPP_ERROR(rclcpp::get_logger("pcl_ros"), "%s", e.what());
return false;
} catch (tf::ExtrapolationException & e) {
ROS_ERROR("%s", e.what());
} catch (tf2::ExtrapolationException & e) {
RCLCPP_ERROR(rclcpp::get_logger("pcl_ros"), "%s", e.what());
return false;
}
transformPointCloud(cloud_in, cloud_out, transform);
cloud_out.header.frame_id = target_frame;
return true;
@@ -160,28 +200,28 @@ template<typename PointT>
bool
transformPointCloud(
const std::string & target_frame,
const ros::Time & target_time,
const rclcpp::Time & target_time,
const pcl::PointCloud<PointT> & cloud_in,
const std::string & fixed_frame,
pcl::PointCloud<PointT> & cloud_out,
const tf::TransformListener & tf_listener)
const tf2_ros::Buffer & tf_buffer)
{
tf::StampedTransform transform;
geometry_msgs::msg::TransformStamped transform;
try {
tf_listener.lookupTransform(
transform = tf_buffer.lookupTransform(
target_frame, target_time, cloud_in.header.frame_id,
fromPCL(cloud_in.header).stamp, fixed_frame, transform);
} catch (tf::LookupException & e) {
ROS_ERROR("%s", e.what());
fromPCL(cloud_in.header.stamp), fixed_frame);
} catch (tf2::LookupException & e) {
RCLCPP_ERROR(rclcpp::get_logger("pcl_ros"), "%s", e.what());
return false;
} catch (tf::ExtrapolationException & e) {
ROS_ERROR("%s", e.what());
} catch (tf2::ExtrapolationException & e) {
RCLCPP_ERROR(rclcpp::get_logger("pcl_ros"), "%s", e.what());
return false;
}
transformPointCloud(cloud_in, cloud_out, transform);
cloud_out.header.frame_id = target_frame;
std_msgs::Header header;
std_msgs::msg::Header header;
header.stamp = target_time;
cloud_out.header = toPCL(header);
return true;
@@ -192,28 +232,28 @@ template<typename PointT>
bool
transformPointCloudWithNormals(
const std::string & target_frame,
const ros::Time & target_time,
const rclcpp::Time & target_time,
const pcl::PointCloud<PointT> & cloud_in,
const std::string & fixed_frame,
pcl::PointCloud<PointT> & cloud_out,
const tf::TransformListener & tf_listener)
const tf2_ros::Buffer & tf_buffer)
{
tf::StampedTransform transform;
geometry_msgs::msg::TransformStamped transform;
try {
tf_listener.lookupTransform(
transform = tf_buffer.lookupTransform(
target_frame, target_time, cloud_in.header.frame_id,
fromPCL(cloud_in.header).stamp, fixed_frame, transform);
} catch (tf::LookupException & e) {
ROS_ERROR("%s", e.what());
fromPCL(cloud_in.header.stamp), fixed_frame);
} catch (tf2::LookupException & e) {
RCLCPP_ERROR(rclcpp::get_logger("pcl_ros"), "%s", e.what());
return false;
} catch (tf::ExtrapolationException & e) {
ROS_ERROR("%s", e.what());
} catch (tf2::ExtrapolationException & e) {
RCLCPP_ERROR(rclcpp::get_logger("pcl_ros"), "%s", e.what());
return false;
}
transformPointCloudWithNormals(cloud_in, cloud_out, transform);
cloud_out.header.frame_id = target_frame;
std_msgs::Header header;
std_msgs::msg::Header header;
header.stamp = target_time;
cloud_out.header = toPCL(header);
return true;
+76 -25
View File
@@ -37,10 +37,14 @@
#ifndef PCL_ROS__TRANSFORMS_HPP_
#define PCL_ROS__TRANSFORMS_HPP_
#include <sensor_msgs/PointCloud2.h>
#include <pcl/common/transforms.h>
#include <tf/transform_datatypes.h>
#include <tf/transform_listener.h>
#include <tf2/LinearMath/Transform.h>
#include <tf2_ros/buffer.h>
#include <tf2_ros/transform_listener.h>
#include <rclcpp/time.hpp>
#include <sensor_msgs/msg/point_cloud2.hpp>
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <Eigen/Dense>
#include <string>
namespace pcl_ros
@@ -56,13 +60,26 @@ void
transformPointCloudWithNormals(
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out,
const tf::Transform & transform);
const tf2::Transform & transform);
/** \brief Transform a point cloud and rotate its normals using an Eigen transform.
* \param cloud_in the input point cloud
* \param cloud_out the input point cloud
* \param transform a rigid transformation from tf
* \note calls the Eigen version
*/
template<typename PointT>
void
transformPointCloudWithNormals(
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out,
const geometry_msgs::msg::TransformStamped & transform);
/** \brief Transforms a point cloud in a given target TF frame using a TransformListener
* \param target_frame the target TF frame the point cloud should be transformed to
* \param cloud_in the input point cloud
* \param cloud_out the input point cloud
* \param tf_listener a TF listener object
* \param tf_buffer a TF buffer object
*/
template<typename PointT>
bool
@@ -70,7 +87,7 @@ transformPointCloudWithNormals(
const std::string & target_frame,
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out,
const tf::TransformListener & tf_listener);
const tf2_ros::Buffer & tf_buffer);
/** \brief Transforms a point cloud in a given target TF frame using a TransformListener
* \param target_frame the target TF frame the point cloud should be transformed to
@@ -78,17 +95,17 @@ transformPointCloudWithNormals(
* \param cloud_in the input point cloud
* \param fixed_frame fixed TF frame
* \param cloud_out the input point cloud
* \param tf_listener a TF listener object
* \param tf_buffer a TF buffer object
*/
template<typename PointT>
bool
transformPointCloudWithNormals(
const std::string & target_frame,
const ros::Time & target_time,
const rclcpp::Time & target_time,
const pcl::PointCloud<PointT> & cloud_in,
const std::string & fixed_frame,
pcl::PointCloud<PointT> & cloud_out,
const tf::TransformListener & tf_listener);
const tf2_ros::Buffer & tf_buffer);
/** \brief Apply a rigid transform defined by a 3D offset and a quaternion
* \param cloud_in the input point cloud
@@ -101,13 +118,26 @@ void
transformPointCloud(
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out,
const tf::Transform & transform);
const tf2::Transform & transform);
/** \brief Apply a rigid transform defined by a 3D offset and a quaternion
* \param cloud_in the input point cloud
* \param cloud_out the input point cloud
* \param transform a rigid transformation from tf
* \note calls the Eigen version
*/
template<typename PointT>
void
transformPointCloud(
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out,
const geometry_msgs::msg::TransformStamped & transform);
/** \brief Transforms a point cloud in a given target TF frame using a TransformListener
* \param target_frame the target TF frame the point cloud should be transformed to
* \param cloud_in the input point cloud
* \param cloud_out the input point cloud
* \param tf_listener a TF listener object
* \param tf_buffer a TF buffer object
*/
template<typename PointT>
bool
@@ -115,7 +145,7 @@ transformPointCloud(
const std::string & target_frame,
const pcl::PointCloud<PointT> & cloud_in,
pcl::PointCloud<PointT> & cloud_out,
const tf::TransformListener & tf_listener);
const tf2_ros::Buffer & tf_buffer);
/** \brief Transforms a point cloud in a given target TF frame using a TransformListener
* \param target_frame the target TF frame the point cloud should be transformed to
@@ -123,29 +153,30 @@ transformPointCloud(
* \param cloud_in the input point cloud
* \param fixed_frame fixed TF frame
* \param cloud_out the input point cloud
* \param tf_listener a TF listener object
* \param tf_buffer a TF buffer object
*/
template<typename PointT>
bool
transformPointCloud(
const std::string & target_frame, const ros::Time & target_time,
const std::string & target_frame,
const rclcpp::Time & target_time,
const pcl::PointCloud<PointT> & cloud_in,
const std::string & fixed_frame,
pcl::PointCloud<PointT> & cloud_out,
const tf::TransformListener & tf_listener);
const tf2_ros::Buffer & tf_buffer);
/** \brief Transform a sensor_msgs::PointCloud2 dataset from its frame to a given TF target frame.
* \param target_frame the target TF frame
* \param in the input PointCloud2 dataset
* \param out the resultant transformed PointCloud2 dataset
* \param tf_listener a TF listener object
* \param tf_buffer a TF buffer object
*/
bool
transformPointCloud(
const std::string & target_frame,
const sensor_msgs::PointCloud2 & in,
sensor_msgs::PointCloud2 & out,
const tf::TransformListener & tf_listener);
const sensor_msgs::msg::PointCloud2 & in,
sensor_msgs::msg::PointCloud2 & out,
const tf2_ros::Buffer & tf_buffer);
/** \brief Transform a sensor_msgs::PointCloud2 dataset from its frame to a given TF target frame.
* \param target_frame the target TF frame
@@ -156,9 +187,22 @@ transformPointCloud(
void
transformPointCloud(
const std::string & target_frame,
const tf::Transform & net_transform,
const sensor_msgs::PointCloud2 & in,
sensor_msgs::PointCloud2 & out);
const tf2::Transform & net_transform,
const sensor_msgs::msg::PointCloud2 & in,
sensor_msgs::msg::PointCloud2 & out);
/** \brief Transform a sensor_msgs::PointCloud2 dataset from its frame to a given TF target frame.
* \param target_frame the target TF frame
* \param net_transform the TF transformer object
* \param in the input PointCloud2 dataset
* \param out the resultant transformed PointCloud2 dataset
*/
void
transformPointCloud(
const std::string & target_frame,
const geometry_msgs::msg::TransformStamped & net_transform,
const sensor_msgs::msg::PointCloud2 & in,
sensor_msgs::msg::PointCloud2 & out);
/** \brief Transform a sensor_msgs::PointCloud2 dataset using an Eigen 4x4 matrix.
* \param transform the transformation to use on the points
@@ -168,15 +212,22 @@ transformPointCloud(
void
transformPointCloud(
const Eigen::Matrix4f & transform,
const sensor_msgs::PointCloud2 & in,
sensor_msgs::PointCloud2 & out);
const sensor_msgs::msg::PointCloud2 & in,
sensor_msgs::msg::PointCloud2 & out);
/** \brief Obtain the transformation matrix from TF into an Eigen form
* \param bt the TF transformation
* \param out_mat the Eigen transformation
*/
void
transformAsMatrix(const tf::Transform & bt, Eigen::Matrix4f & out_mat);
transformAsMatrix(const tf2::Transform & bt, Eigen::Matrix4f & out_mat);
/** \brief Obtain the transformation matrix from TF into an Eigen form
* \param bt the TF transformation
* \param out_mat the Eigen transformation
*/
void
transformAsMatrix(const geometry_msgs::msg::TransformStamped & bt, Eigen::Matrix4f & out_mat);
} // namespace pcl_ros
#endif // PCL_ROS__TRANSFORMS_HPP_