12#include <pcl/PointIndices.h>
13#include <pcl/common/centroid.h>
14#include <pcl/common/transforms.h>
15#include <pcl/correspondence.h>
16#include <pcl/filters/voxel_grid.h>
17#include <pcl/io/pcd_io.h>
18#include <pcl/pcl_macros.h>
19#include <pcl/registration/impl/transformation_estimation_2D.hpp>
20#include <pcl/registration/transformation_estimation_2D.h>
21#include <pcl/search/kdtree.h>
22#include <pcl/segmentation/extract_clusters.h>
24#include <opencv2/core/types.hpp>
25#include <opencv2/imgproc.hpp>
40#include <cartographer/mapping/map_builder_interface.h>
45 template <
typename Po
intT>
46 MapRegistration::ModelCorrection
47 align(
const pcl::PointCloud<PointT>& cloudSource,
49 const Eigen::Isometry3f& world_T_map)
51 typename pcl::PointCloud<PointT>::Ptr sharedCloud(
new pcl::PointCloud<PointT>);
54 pcl::transformPointCloud(cloudSource, *sharedCloud, world_T_map);
56 for (
auto& pt : sharedCloud->points)
66 std::for_each(model.
polygon.begin(),
75 typename pcl::PointCloud<PointT>::Ptr polygonCloud(
new pcl::PointCloud<PointT>);
76 std::transform(model.
polygon.begin(),
78 std::back_inserter(polygonCloud->points),
81 for (
auto& pt : polygonCloud->points)
94 const auto result = icp.
align();
97 typename pcl::PointCloud<PointT>::Ptr registeredPolygonCloud(
new pcl::PointCloud<PointT>);
98 pcl::transformPointCloud(*polygonCloud, *registeredPolygonCloud, trafo.inverse());
100 trafo.translation() *= 1000.F;
102 return {.correction = trafo,
103 .weight =
static_cast<float>(result.correspondences.size()) / sharedCloud->size(),
104 .covariance = result.covariance};
108 std::unique_ptr<::cartographer::mapping::MapBuilderInterface> mapBuilderPtr) :
109 mapBuilder(
std::move(mapBuilderPtr)),
110 optimizedGraphData(
utils::optimizedGraphData(*mapBuilder))
116 simox::math::InterpolatingTimeQueue<simox::math::PoseStamped>
119 simox::math::InterpolatingTimeQueue<simox::math::PoseStamped> queue;
123 Eigen::Isometry3f pose =
data.global_pose;
124 pose.translation() *= 1000.F;
126 simox::math::PoseStamped ps{.pose = pose, .timestamp =
data.timestamp};
127 queue.insert(std::make_shared<const simox::math::PoseStamped>(std::move(ps)));
136 constexpr float toMM = 1000.F;
139 pcl::VoxelGrid<pcl::PointXYZ> vg;
140 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(
new pcl::PointCloud<pcl::PointXYZ>);
141 vg.setInputCloud(cloud);
142 constexpr float leafSize = 0.005f *
toMM;
143 vg.setLeafSize(leafSize, leafSize, leafSize);
144 vg.filter(*cloud_filtered);
145 std::cout <<
"PointCloud after filtering has: " << cloud_filtered->size() <<
" data points."
149 pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(
new pcl::search::KdTree<pcl::PointXYZ>);
150 tree->setInputCloud(cloud_filtered);
152 std::vector<pcl::PointIndices> cluster_indices;
153 pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
154 ec.setClusterTolerance(0.01 *
toMM);
155 ec.setMinClusterSize(100);
157 ec.setSearchMethod(tree);
158 ec.setInputCloud(cloud_filtered);
159 ec.extract(cluster_indices);
161 pcl::PCDWriter writer;
166 for (
auto it = cluster_indices.begin(); it != cluster_indices.end(); ++it)
168 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudCluster(
new pcl::PointCloud<pcl::PointXYZ>);
169 for (
const auto& idx : it->indices)
171 cloudCluster->push_back((*cloud_filtered)[idx]);
174 cloudCluster->width = cloudCluster->size();
175 cloudCluster->height = 1;
176 cloudCluster->is_dense =
true;
178 std::cout <<
"PointCloud representing the Cluster: " << cloudCluster->size()
179 <<
" data points." << std::endl;
180 std::stringstream ss;
181 ss <<
"/tmp/cloud_cluster_" << j <<
".pcd";
182 writer.write<pcl::PointXYZ>(ss.str(), *cloudCluster,
false);
185 clusters.emplace_back(cloudCluster);
194 const Eigen::Isometry3f& world_T_map)
196 std::vector<float> distances;
197 distances.reserve(clusters.size());
199 std::transform(clusters.begin(),
201 std::back_inserter(distances),
202 [&](
const MapRegistration::Cluster& cluster) ->
float
203 { return (world_T_map * toEigen(cluster.centroid) - position).norm(); });
205 const std::size_t idx =
206 std::distance(distances.begin(), std::min_element(distances.begin(), distances.end()));
208 return clusters.at(idx);
214 const Eigen::Isometry3f& world_T_map)
const
220 const Cluster& cluster = findNearestCluster(model.center, clusters, world_T_map);
222 assocs.push_back(
Association{.cluster = cluster, .model = model});
231 const Eigen::Isometry3f& world_T_map)
const
248 MapRegistration::ModelCorrection
254 for (
const Association& association : associations)
257 mapPoints.
push_back(association.cluster.centroid);
260 Eigen::Isometry3f world_T_map = Eigen::Isometry3f::Identity();
262 ::pcl::registration::TransformationEstimation2D<Point, Point> transformEstimator;
263 transformEstimator.estimateRigidTransformation(
264 worldPoints, mapPoints, world_T_map.matrix());
269 return combinedCorrection;
274 const std::vector<wykobi::Model>& models,
275 const std::vector<ModelCorrection>& modelCorrections)
const
286 for (
size_t i = 0; i < models.size(); i++)
288 const auto& modelCorrection = modelCorrections.at(i);
289 const auto& model = models.at(i);
291 const auto& world_P_model = model.center;
292 const Eigen::Vector3f map_P_model = modelCorrection.correction * world_P_model;
301 Eigen::Isometry3f world_T_map = Eigen::Isometry3f::Identity();
303 pcl::registration::TransformationEstimation2D<Point, Point> transformEstimator;
304 transformEstimator.estimateRigidTransformation(
305 worldPoints, mapPoints, world_T_map.matrix());
317 return combinedCorrection;
341 const MapRegistration::PointCloud::ConstPtr& cloud,
342 const std::vector<wykobi::Model>& models,
373 return {.min = IceUtil::Time::microSeconds(optimizedGraphData.nodes_data.front().timestamp),
374 .max = IceUtil::Time::microSeconds(optimizedGraphData.nodes_data.back().timestamp)};
377 std::vector<Eigen::Isometry3f>
381 const auto robotPoseQ = robotPoseInterpolatingQueue.rawQueue();
385 std::transform(robotPoseQ.begin(),
388 [](
const auto& poseStampedPtr) { return poseStampedPtr->pose; });
396 return cv::Point2f(pt.x, pt.y);
399 inline Eigen::Vector2f
402 return Eigen::Vector2f{pt.x, pt.y};
408 std::vector<cv::Point2f> points;
409 points.reserve(cloud->size());
411 cloud->points.begin(), cloud->points.end(), std::back_inserter(points), toCV);
413 cv::RotatedRect rect = cv::minAreaRect(points);
415 const auto center =
toEigen(rect.center);
416 const auto extends =
toEigen(rect.size);
419 .center = center.head<2>(), .
angle = DEG2RAD(rect.angle), .extends = extends};
void push_back(const T &v)
void setInputCloud(const typename pcl::PointCloud< PointT >::ConstPtr &cloud)
RegistrationResult align()
void applyFilter(pcl::PointCloud< PointT > &cloud) const override
ModelCorrection alignModelToCluster(const wykobi::Model &model, const Cluster &cluster, const Eigen::Isometry3f &world_T_map) const
ModelCorrection computeCombinedCorrection(const std::vector< wykobi::Model > &models, const std::vector< ModelCorrection > &modelCorrections) const
Clusters detectClusters(const PointCloud::ConstPtr &cloud) const
void computeBoundingBox(const PointCloud::ConstPtr &cloud)
void visualizeResult(const PointCloud::ConstPtr &cloud, const std::vector< wykobi::Model > &models, const ModelCorrection &combinedCorrection) const
std::vector< Association > Associations
MapRegistration(std::unique_ptr<::cartographer::mapping::MapBuilderInterface > mapBuilder)
std::vector< Eigen::Isometry3f > robotPoses() const
TimeRange mapTimeRange() const
std::vector< Cluster > Clusters
pcl::PointCloud< Point > PointCloud
MapRegistration::ModelCorrection computeCorrection(const MapRegistration::Associations &associations)
simox::math::InterpolatingTimeQueue< simox::math::PoseStamped > robotPoseQueue() const
Associations matchModelsToClusters(const std::vector< wykobi::Model > &models, const Clusters &clusters, const Eigen::Isometry3f &world_T_map) const
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
cv::Point2f toCV(const pcl::PointXYZ &pt)
const MapRegistration::Cluster & findNearestCluster(const Eigen::Vector3f &position, const MapRegistration::Clusters &clusters, const Eigen::Isometry3f &world_T_map)
MapRegistration::ModelCorrection align(const pcl::PointCloud< PointT > &cloudSource, const wykobi::Model &model2, const Eigen::Isometry3f &world_T_map)
Eigen::Vector3f toMM(const Eigen::Vector3f &vec)
Eigen::Isometry3f toEigen(const ::cartographer::transform::Rigid3d &pose)
::pcl::PointCloud<::pcl::PointXYZ > toPCL(const std::vector<::cartographer::sensor::TimedRangefinderPoint > &points)
pcl::PointXYZ fromWykobi(const wykobi::Point &ptWykobi)
detail::PointRangeFilter< PointT, std::less< float > > PointMaxRangeFilter
void * align(size_t alignment, size_t bytes, void *&bufferPlace, size_t &bufferSpace) noexcept
double angle(const Point &a, const Point &b, const Point &c)
Eigen::Isometry3f transform
Cluster(const PointCloud::Ptr &points)
Eigen::Isometry3f correction