MapRegistration.cpp
Go to the documentation of this file.
1#include "MapRegistration.h"
2
3#include <algorithm>
4#include <deque>
5#include <iostream>
6#include <iterator>
7#include <string>
8#include <utility>
9
10#include <Eigen/Core>
11
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>
23
24#include <opencv2/core/types.hpp>
25#include <opencv2/imgproc.hpp>
26
30
34
36#include "PointToModelICP.h"
37#include "models.h"
38#include "pcl_filter.h"
39#include "wykobi_utils.h"
40#include <cartographer/mapping/map_builder_interface.h>
41
43{
44
45 template <typename PointT>
46 MapRegistration::ModelCorrection
47 align(const pcl::PointCloud<PointT>& cloudSource,
48 const wykobi::Model& model2,
49 const Eigen::Isometry3f& world_T_map)
50 {
51 typename pcl::PointCloud<PointT>::Ptr sharedCloud(new pcl::PointCloud<PointT>);
52
53 // the world_T_map trafo can be seen as a prior for
54 pcl::transformPointCloud(cloudSource, *sharedCloud, world_T_map);
55
56 for (auto& pt : sharedCloud->points)
57 {
58 pt.x /= 1000.F; // to m
59 pt.y /= 1000.F; // to m
60 pt.z /= 1000.F; // to m
61 }
62
63 wykobi::Model model = model2;
64 model.center /= 1000.F;
65
66 std::for_each(model.polygon.begin(),
67 model.polygon.end(),
68 [](auto& p)
69 {
70 p.x /= 1000.F;
71 p.y /= 1000.F;
72 });
73
74
75 typename pcl::PointCloud<PointT>::Ptr polygonCloud(new pcl::PointCloud<PointT>);
76 std::transform(model.polygon.begin(),
77 model.polygon.end(),
78 std::back_inserter(polygonCloud->points),
80
81 for (auto& pt : polygonCloud->points)
82 {
83 pt.x /= 1000.F; // to m
84 pt.y /= 1000.F; // to m
85 pt.z /= 1000.F; // to m
86 }
87
89 rangeFilter.applyFilter(*sharedCloud);
90
91
93 icp.setInputCloud(sharedCloud);
94 const auto result = icp.align();
95 /*const*/ auto trafo = result.transform;
96
97 typename pcl::PointCloud<PointT>::Ptr registeredPolygonCloud(new pcl::PointCloud<PointT>);
98 pcl::transformPointCloud(*polygonCloud, *registeredPolygonCloud, trafo.inverse());
99
100 trafo.translation() *= 1000.F;
101
102 return {.correction = trafo,
103 .weight = static_cast<float>(result.correspondences.size()) / sharedCloud->size(),
104 .covariance = result.covariance};
105 }
106
108 std::unique_ptr<::cartographer::mapping::MapBuilderInterface> mapBuilderPtr) :
109 mapBuilder(std::move(mapBuilderPtr)),
110 optimizedGraphData(utils::optimizedGraphData(*mapBuilder))
111 {
112 }
113
115
116 simox::math::InterpolatingTimeQueue<simox::math::PoseStamped>
118 {
119 simox::math::InterpolatingTimeQueue<simox::math::PoseStamped> queue;
120
121 for (const OptimizedNodeData& data : optimizedGraphData.nodes_data)
122 {
123 Eigen::Isometry3f pose = data.global_pose;
124 pose.translation() *= 1000.F; // toMM
125
126 simox::math::PoseStamped ps{.pose = pose, .timestamp = data.timestamp};
127 queue.insert(std::make_shared<const simox::math::PoseStamped>(std::move(ps)));
128 }
129
130 return queue;
131 }
132
134 MapRegistration::detectClusters(const PointCloud::ConstPtr& cloud) const
135 {
136 constexpr float toMM = 1000.F;
137
138 // Create the filtering object: downsample the dataset using a leaf size of 1cm
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."
146 << std::endl; //*
147
148 // Creating the KdTree object for the search method of the extraction
149 pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
150 tree->setInputCloud(cloud_filtered);
151
152 std::vector<pcl::PointIndices> cluster_indices;
153 pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
154 ec.setClusterTolerance(0.01 * toMM); // 2cm
155 ec.setMinClusterSize(100);
156 // ec.setMaxClusterSize(25000);
157 ec.setSearchMethod(tree);
158 ec.setInputCloud(cloud_filtered);
159 ec.extract(cluster_indices);
160
161 pcl::PCDWriter writer;
162
164
165 int j = 0;
166 for (auto it = cluster_indices.begin(); it != cluster_indices.end(); ++it)
167 {
168 pcl::PointCloud<pcl::PointXYZ>::Ptr cloudCluster(new pcl::PointCloud<pcl::PointXYZ>);
169 for (const auto& idx : it->indices)
170 {
171 cloudCluster->push_back((*cloud_filtered)[idx]);
172 }
173
174 cloudCluster->width = cloudCluster->size();
175 cloudCluster->height = 1;
176 cloudCluster->is_dense = true;
177
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); //*
183 j++;
184
185 clusters.emplace_back(cloudCluster);
186 }
187
188 return clusters;
189 }
190
192 findNearestCluster(const Eigen::Vector3f& position,
193 const MapRegistration::Clusters& clusters,
194 const Eigen::Isometry3f& world_T_map)
195 {
196 std::vector<float> distances;
197 distances.reserve(clusters.size());
198
199 std::transform(clusters.begin(),
200 clusters.end(),
201 std::back_inserter(distances),
202 [&](const MapRegistration::Cluster& cluster) -> float
203 { return (world_T_map * toEigen(cluster.centroid) - position).norm(); });
204
205 const std::size_t idx =
206 std::distance(distances.begin(), std::min_element(distances.begin(), distances.end()));
207
208 return clusters.at(idx);
209 }
210
212 MapRegistration::matchModelsToClusters(const std::vector<wykobi::Model>& models,
213 const Clusters& clusters,
214 const Eigen::Isometry3f& world_T_map) const
215 {
216 Associations assocs;
217
218 for (const wykobi::Model& model : models)
219 {
220 const Cluster& cluster = findNearestCluster(model.center, clusters, world_T_map);
221
222 assocs.push_back(Association{.cluster = cluster, .model = model});
223 }
224
225 return assocs;
226 }
227
230 const Cluster& cluster,
231 const Eigen::Isometry3f& world_T_map) const
232 {
233 return align<>(*cluster.points, model, world_T_map);
234 }
235
236 inline pcl::PointXYZ
237 toPCL(const Eigen::Vector3f& v)
238 {
239 pcl::PointXYZ p;
240
241 p.x = v.x();
242 p.y = v.y();
243 p.z = v.z();
244
245 return p;
246 }
247
248 MapRegistration::ModelCorrection
250 {
251 PointCloud worldPoints;
252 PointCloud mapPoints;
253
254 for (const Association& association : associations)
255 {
256 worldPoints.push_back(toPCL(association.model.center));
257 mapPoints.push_back(association.cluster.centroid);
258 }
259
260 Eigen::Isometry3f world_T_map = Eigen::Isometry3f::Identity();
261
262 ::pcl::registration::TransformationEstimation2D<Point, Point> transformEstimator;
263 transformEstimator.estimateRigidTransformation(
264 worldPoints, mapPoints, world_T_map.matrix());
265
266 ModelCorrection combinedCorrection;
267 combinedCorrection.correction = world_T_map;
268
269 return combinedCorrection;
270 }
271
274 const std::vector<wykobi::Model>& models,
275 const std::vector<ModelCorrection>& modelCorrections) const
276 {
277 // desire: obtain world -> map tranformation in Least Squares sense
278
279 // first shot: weighted mean
280
281 ARMARX_CHECK(models.size() == modelCorrections.size());
282
283 PointCloud worldPoints;
284 PointCloud mapPoints;
285
286 for (size_t i = 0; i < models.size(); i++)
287 {
288 const auto& modelCorrection = modelCorrections.at(i);
289 const auto& model = models.at(i);
290
291 const auto& world_P_model = model.center;
292 const Eigen::Vector3f map_P_model = modelCorrection.correction * world_P_model;
293
294 worldPoints.push_back(toPCL(world_P_model));
295 mapPoints.push_back(toPCL(map_P_model));
296
297 // modelCorrection.correction.translation().head<2>();
298 // ori += modelCorrection.weight * simox::math::mat3f_to_rpy(modelCorrection.correction.linear()).z();
299 }
300
301 Eigen::Isometry3f world_T_map = Eigen::Isometry3f::Identity();
302
303 pcl::registration::TransformationEstimation2D<Point, Point> transformEstimator;
304 transformEstimator.estimateRigidTransformation(
305 worldPoints, mapPoints, world_T_map.matrix());
306
307 // pos /= overallWeight;
308 // ori /= overallWeight;
309
310 ModelCorrection combinedCorrection;
311 // combinedCorrection.correction = Eigen::Isometry3f::Identity();
312 // combinedCorrection.correction.translation().head<2>() = pos;
313 // combinedCorrection.correction.linear() = Eigen::AngleAxisf(ori, Eigen::Vector3f::UnitZ()).toRotationMatrix();
314
315 combinedCorrection.correction = world_T_map;
316
317 return combinedCorrection;
318 }
319
320 // void drawPolygon(pcl::visualization::PCLVisualizer& visualizer,
321 // const wykobi::Polygon& polygon,
322 // const std::string& name)
323 // {
324 // typename pcl::PointCloud<pcl::PointXYZ>::Ptr polygonCloud(
325 // new pcl::PointCloud<pcl::PointXYZ>);
326
327 // std::transform(polygon.begin(),
328 // polygon.end(),
329 // std::back_inserter(polygonCloud->points),
330 // wykobi::fromWykobi);
331
332 // const Eigen::Isometry3f trafo(Eigen::Translation3f(0, 0, 0.1));
333
334 // pcl::transformPointCloud(*polygonCloud, *polygonCloud, trafo);
335
336 // // visualizer.addPolygon<pcl::PointXYZ>(polygonCloud, 1.0, 0., 0., "polygon_" + name);
337 // }
338
339 void
341 const MapRegistration::PointCloud::ConstPtr& cloud,
342 const std::vector<wykobi::Model>& models,
343 const MapRegistration::ModelCorrection& combinedCorrection) const
344 {
345
346 // pcl::visualization::PCLVisualizer visualizer("Viewer2");
347 // visualizer.addCoordinateSystem(1.0);
348 // // visualizer.addPointCloud(sharedCloud, "full_cloud");
349
350 // int i = 0;
351 // for (const auto& model : models)
352 // {
353 // std::string name = std::to_string(i++);
354 // drawPolygon(visualizer, model.polygon, name);
355 // }
356
357 // pcl::visualization::PointCloudColorHandlerCustom<Point> colorHandler(0., 1., 0.);
358
359 // // visualizer.addPointCloud(cloud, colorHandler, "full_cloud");
360
361 // PointCloud::Ptr alignedCloud(new PointCloud);
362 // pcl::transformPointCloud(*cloud, *alignedCloud, combinedCorrection.correction);
363 // // visualizer.addPointCloud(alignedCloud, "polygon_aligned");
364
365 // visualizer.spin();
366 }
367
370 {
371 ARMARX_CHECK(optimizedGraphData.nodes_data.size() > 1);
372
373 return {.min = IceUtil::Time::microSeconds(optimizedGraphData.nodes_data.front().timestamp),
374 .max = IceUtil::Time::microSeconds(optimizedGraphData.nodes_data.back().timestamp)};
375 }
376
377 std::vector<Eigen::Isometry3f>
379 {
380 const auto robotPoseInterpolatingQueue = robotPoseQueue();
381 const auto robotPoseQ = robotPoseInterpolatingQueue.rawQueue();
382
383 std::vector<Eigen::Isometry3f> robotPoses;
384 robotPoses.reserve(robotPoseQ.size());
385 std::transform(robotPoseQ.begin(),
386 robotPoseQ.end(),
387 std::back_inserter(robotPoses),
388 [](const auto& poseStampedPtr) { return poseStampedPtr->pose; });
389
390 return robotPoses;
391 }
392
393 inline cv::Point2f
394 toCV(const pcl::PointXYZ& pt)
395 {
396 return cv::Point2f(pt.x, pt.y);
397 }
398
399 inline Eigen::Vector2f
400 toEigen(const cv::Point2f& pt)
401 {
402 return Eigen::Vector2f{pt.x, pt.y};
403 }
404
405 void
406 MapRegistration::computeBoundingBox(const PointCloud::ConstPtr& cloud)
407 {
408 std::vector<cv::Point2f> points;
409 points.reserve(cloud->size());
410 std::transform(
411 cloud->points.begin(), cloud->points.end(), std::back_inserter(points), toCV);
412
413 cv::RotatedRect rect = cv::minAreaRect(points);
414
415 const auto center = toEigen(rect.center);
416 const auto extends = toEigen(rect.size);
417
418 boundingBox = {
419 .center = center.head<2>(), .angle = DEG2RAD(rect.angle), .extends = extends};
420 }
421
423 {
424 pcl::computeCentroid(*points, centroid);
425 }
426} // namespace armarx::cartographer
void push_back(const T &v)
Definition Vector.h:354
void setInputCloud(const typename pcl::PointCloud< PointT >::ConstPtr &cloud)
RegistrationResult align()
void applyFilter(pcl::PointCloud< PointT > &cloud) const override
Definition pcl_filter.h:96
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 visualizeResult(const PointCloud::ConstPtr &cloud, const std::vector< wykobi::Model > &models, const ModelCorrection &combinedCorrection) const
MapRegistration(std::unique_ptr<::cartographer::mapping::MapBuilderInterface > mapBuilder)
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
Definition pcl_filter.h:118
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)
Definition point.hpp:109
Eigen::Isometry3f transform
Eigen::Vector3f center
Definition models.h:34
wykobi::Polygon polygon
Definition models.h:35