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 
35 #include "../InterpolatingTimeQueue.h"
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 
88  armarx::PointMaxRangeFilter<pcl::PointXYZ> rangeFilter(0.5F, model.center);
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 
107  MapRegistration::MapRegistration(
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 
163  MapRegistration::Clusters clusters;
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 
368  TimeRange
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());
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 
422  MapRegistration::Cluster::Cluster(const PointCloud::Ptr& points) : points(points)
423  {
424  pcl::computeCentroid(*points, centroid);
425  }
426 } // namespace armarx::cartographer
armarx::wykobi::Model
Definition: models.h:32
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::Association
Definition: MapRegistration.h:133
armarx::PointToModelICP::align
RegistrationResult align()
Definition: PointToModelICP.h:90
armarx::localization_and_mapping::cartographer_adapter::OptimizedGraphData::nodes_data
std::vector< OptimizedNodeData > nodes_data
Definition: types.h:130
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::ModelCorrection
Definition: MapRegistration.h:145
armarx::cartographer::toCV
cv::Point2f toCV(const pcl::PointXYZ &pt)
Definition: MapRegistration.cpp:394
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::Cluster::Cluster
Cluster(const PointCloud::Ptr &points)
Definition: MapRegistration.cpp:422
armarx::cartographer::align
MapRegistration::ModelCorrection align(const pcl::PointCloud< PointT > &cloudSource, const wykobi::Model &model2, const Eigen::Isometry3f &world_T_map)
Definition: MapRegistration.cpp:47
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::Cluster
Definition: MapRegistration.h:112
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::Clusters
std::vector< Cluster > Clusters
Definition: MapRegistration.h:121
MiscLib::Vector::push_back
void push_back(const T &v)
Definition: Vector.h:354
wykobi_utils.h
pcl_filter.h
armarx::localization_and_mapping::cartographer_adapter::TimeRange
Definition: MapRegistration.h:56
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::~MapRegistration
~MapRegistration()
armarx::wykobi::Model::center
Eigen::Vector3f center
Definition: models.h:34
utils.h
StringHelpers.h
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::matchModelsToClusters
Associations matchModelsToClusters(const std::vector< wykobi::Model > &models, const Clusters &clusters, const Eigen::Isometry3f &world_T_map) const
Definition: MapRegistration.cpp:212
armarx::cartographer
Definition: MapRegistration.cpp:42
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::detectClusters
Clusters detectClusters(const PointCloud::ConstPtr &cloud) const
Definition: MapRegistration.cpp:134
armarx::localization_and_mapping::cartographer_adapter::toEigen
Eigen::Isometry3f toEigen(const ::cartographer::transform::Rigid3d &pose)
Definition: eigen_conversions.cpp:10
armarx::detail::PointRangeFilter
Definition: pcl_filter.h:87
GfxTL::Identity
void Identity(MatrixXX< N, N, T > *a)
Definition: MatrixXX.h:570
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::computeCombinedCorrection
ModelCorrection computeCombinedCorrection(const std::vector< wykobi::Model > &models, const std::vector< ModelCorrection > &modelCorrections) const
Definition: MapRegistration.cpp:273
wykobi_types.h
armarx::localization_and_mapping::cartographer_adapter::OptimizedNodeData
Definition: types.h:60
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::ModelCorrection::correction
Eigen::Isometry3f correction
Definition: MapRegistration.h:147
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::Cluster::centroid
pcl::PointXYZ centroid
Definition: MapRegistration.h:116
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::Cluster::points
PointCloud::Ptr points
Definition: MapRegistration.h:114
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::alignModelToCluster
ModelCorrection alignModelToCluster(const wykobi::Model &model, const Cluster &cluster, const Eigen::Isometry3f &world_T_map) const
Definition: MapRegistration.cpp:229
armarx::wykobi::Model::polygon
wykobi::Polygon polygon
Definition: models.h:35
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
armarx::localization_and_mapping::cartographer_adapter::toPCL
::pcl::PointCloud<::pcl::PointXYZ > toPCL(const std::vector<::cartographer::sensor::TimedRangefinderPoint > &points)
Definition: pcl_conversions.cpp:35
armarx::localization_and_mapping::cartographer_adapter::utils::optimizedGraphData
OptimizedGraphData optimizedGraphData(::cartographer::mapping::MapBuilderInterface &mapBuilder)
Definition: utils.cpp:27
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::Associations
std::vector< Association > Associations
Definition: MapRegistration.h:139
models.h
MiscLib::Vector::at
T & at(size_type i)
Definition: Vector.h:314
armarx::localization_and_mapping::cartographer_adapter::toMM
Eigen::Vector3f toMM(const Eigen::Vector3f &vec)
Definition: ArVizDrawerMapBuilder.cpp:29
armarx::transform
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT >>
Convenience function (with less typing) to transform a container of type InputT into the same contain...
Definition: algorithm.h:351
ExpressionException.h
PointToModelICP.h
armarx::wykobi::fromWykobi
pcl::PointXYZ fromWykobi(const wykobi::Point &ptWykobi)
Definition: wykobi_utils.h:37
PointCloud
Definition: PointCloud.h:85
armarx::cartographer::findNearestCluster
const MapRegistration::Cluster & findNearestCluster(const Eigen::Vector3f &position, const MapRegistration::Clusters &clusters, const Eigen::Isometry3f &world_T_map)
Definition: MapRegistration.cpp:192
armarx::localization_and_mapping::cartographer_adapter::RotatedRect::center
Eigen::Vector2f center
Definition: MapRegistration.h:70
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:39
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::computeBoundingBox
void computeBoundingBox(const PointCloud::ConstPtr &cloud)
Definition: MapRegistration.cpp:406
std
Definition: Application.h:66
armarx::PointToModelICP::setInputCloud
void setInputCloud(const typename pcl::PointCloud< PointT >::ConstPtr &cloud)
Definition: PointToModelICP.h:84
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::robotPoseQueue
simox::math::InterpolatingTimeQueue< simox::math::PoseStamped > robotPoseQueue() const
Definition: MapRegistration.cpp:117
armarx::view_selection::skills::direction::state::center
state::Type center(state::Type previous)
Definition: LookDirection.cpp:233
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::Association::cluster
const Cluster cluster
Definition: MapRegistration.h:135
types.h
angle
double angle(const Point &a, const Point &b, const Point &c)
Definition: point.hpp:109
armarx::detail::PointRangeFilter::applyFilter
void applyFilter(pcl::PointCloud< PointT > &cloud) const override
Definition: pcl_filter.h:96
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::visualizeResult
void visualizeResult(const PointCloud::ConstPtr &cloud, const std::vector< wykobi::Model > &models, const ModelCorrection &combinedCorrection) const
Definition: MapRegistration.cpp:340
MapRegistration.h
distance
double distance(const Point &a, const Point &b)
Definition: point.hpp:95
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::robotPoses
std::vector< Eigen::Isometry3f > robotPoses() const
Definition: MapRegistration.cpp:378
Logging.h
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::mapTimeRange
TimeRange mapTimeRange() const
Definition: MapRegistration.cpp:369
armarx::navigation::human::Cluster
memory::LaserScannerFeature Cluster
Definition: HumanTracker.h:41
armarx::localization_and_mapping::cartographer_adapter::MapRegistration::computeCorrection
MapRegistration::ModelCorrection computeCorrection(const MapRegistration::Associations &associations)
Definition: MapRegistration.cpp:249
armarx::PointToModelICP
Definition: PointToModelICP.h:73