15 #ifndef BELUGA_ROS_POINT_CLOUD_HPP
16 #define BELUGA_ROS_POINT_CLOUD_HPP
18 #include <range/v3/view/iota.hpp>
22 #include <beluga_ros/messages.hpp>
24 #include <sophus/se3.hpp>
26 #include <Eigen/Dense>
28 #include <beluga/eigen_compatibility.hpp>
29 #include <sensor_msgs/msg/point_cloud2.hpp>
30 #include <sensor_msgs/point_cloud2_iterator.hpp>
31 #include <sensor_msgs/point_field_conversion.hpp>
58 template <
typename T,
bool Strict = true>
66 std::is_same_v<Scalar, float> || std::is_same_v<Scalar, double>,
67 "Pointcloud3 only supports floating point types");
74 explicit PointCloud3(sensor_msgs::msg::PointCloud2::ConstSharedPtr cloud, Sophus::SE3d
origin = Sophus::SE3d())
76 assert(cloud_ !=
nullptr);
77 if (cloud_->fields.size() != 3) {
78 throw std::invalid_argument(
"point cloud must have exactly 3 fields");
80 const auto& field_0 = cloud_->fields.at(0);
81 const auto& field_1 = cloud_->fields.at(1);
82 const auto& field_2 = cloud_->fields.at(2);
83 if (field_0.name !=
"x" || field_1.name !=
"y" || field_2.name !=
"z") {
84 throw std::invalid_argument(
"point cloud layout is not xyz");
86 if (field_0.datatype != field_1.datatype || field_1.datatype != field_2.datatype) {
87 throw std::invalid_argument(
"point cloud xyz datatypes are not all the same");
89 if constexpr (Strict) {
90 if (field_0.datatype != sensor_msgs::typeAsPointFieldType<Scalar>::value) {
91 throw std::invalid_argument(
"xyz datatype does not match the expected type");
94 if (field_0.datatype != sensor_msgs::typeAsPointFieldType<float>::value &&
95 field_0.datatype != sensor_msgs::typeAsPointFieldType<double>::value) {
96 throw std::invalid_argument(
"xyz datatype is not floating point");
99 constexpr
auto float_datatype = sensor_msgs::typeAsPointFieldType<float>::value;
100 const auto size_of_datatype = field_0.datatype == float_datatype ?
sizeof(float) :
sizeof(
double);
101 if (cloud_->point_step % size_of_datatype != 0) {
102 throw std::invalid_argument(
"point cloud is not dense");
107 [[nodiscard]]
const auto&
origin()
const {
return origin_; }
111 if constexpr (!Strict) {
112 const auto datatype = cloud_->fields.at(0).datatype;
113 if (datatype != sensor_msgs::typeAsPointFieldType<Scalar>::value) {
114 if (datatype == sensor_msgs::typeAsPointFieldType<float>::value) {
115 return Eigen::Matrix3X<Scalar>(points_matrix<float>(*cloud_).template cast<Scalar>());
117 assert(datatype == sensor_msgs::typeAsPointFieldType<double>::value);
118 return Eigen::Matrix3X<Scalar>(points_matrix<double>(*cloud_).template cast<Scalar>());
120 return Eigen::Matrix3X<Scalar>(points_matrix<Scalar>(*cloud_));
122 return points_matrix<Scalar>(*cloud_);
127 template <
typename U>
128 static auto points_matrix(
const sensor_msgs::msg::PointCloud2& cloud) {
129 const auto stride =
static_cast<int>(cloud.point_step /
sizeof(U));
130 const sensor_msgs::PointCloud2ConstIterator<U> iter_points(cloud,
"x");
131 return Eigen::Map<const Eigen::Matrix3X<U>, 0, Eigen::OuterStride<>>(
132 &iter_points[0], 3, cloud.width * cloud.height, stride);
135 sensor_msgs::msg::PointCloud2::ConstSharedPtr cloud_;
136 Sophus::SE3d origin_;
Thin wrapper type for 3D sensor_msgs/PointCloud2 messages with dense layouts.
Definition: point_cloud.hpp:59
auto points() const
Get cartesian points in the point cloud as a matrix.
Definition: point_cloud.hpp:110
T Scalar
Expected PointCloud2 fields type.
Definition: point_cloud.hpp:62
PointCloud3(sensor_msgs::msg::PointCloud2::ConstSharedPtr cloud, Sophus::SE3d origin=Sophus::SE3d())
Definition: point_cloud.hpp:74
const auto & origin() const
Get the point cloud frame origin in the filter frame.
Definition: point_cloud.hpp:107
The main Beluga ROS namespace.
Definition: amcl.hpp:47