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>
35 #include "../InterpolatingTimeQueue.h"
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>);
78 std::back_inserter(polygonCloud->points),
81 for (
auto& pt : polygonCloud->points)
94 const auto result = icp.
align();
95 auto trafo = result.transform;
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};
107 MapRegistration::MapRegistration(
108 std::unique_ptr<::cartographer::mapping::MapBuilderInterface> mapBuilderPtr) :
109 mapBuilder(
std::move(mapBuilderPtr)),
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());
201 std::back_inserter(distances),
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
231 const Eigen::Isometry3f& world_T_map)
const
233 return align<>(*cluster.
points, model, world_T_map);
248 MapRegistration::ModelCorrection
254 for (
const Association& association : associations)
257 mapPoints.
push_back(association.cluster.centroid);
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;
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();
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);
416 const auto extends =
toEigen(rect.size);