Point Cloud Library (PCL)  1.8.1-dev
lum.h
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2010-2012, Willow Garage, Inc.
6  * Copyright (c) 2012-, Open Perception, Inc.
7  *
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * * Redistributions of source code must retain the above copyright
15  * notice, this list of conditions and the following disclaimer.
16  * * Redistributions in binary form must reproduce the above
17  * copyright notice, this list of conditions and the following
18  * disclaimer in the documentation and/or other materials provided
19  * with the distribution.
20  * * Neither the name of the copyright holder(s) nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  *
37  * $Id: lum.h 5663 2012-05-02 13:49:39Z florentinus $
38  *
39  */
40 
41 #ifndef PCL_REGISTRATION_LUM_H_
42 #define PCL_REGISTRATION_LUM_H_
43 
44 #include <pcl/pcl_base.h>
45 #include <pcl/registration/eigen.h>
46 #include <pcl/registration/boost.h>
47 #include <pcl/common/transforms.h>
48 #include <pcl/correspondence.h>
49 #include <pcl/registration/boost_graph.h>
50 
51 namespace Eigen
52 {
53  typedef Eigen::Matrix<float, 6, 1> Vector6f;
54  typedef Eigen::Matrix<float, 6, 6> Matrix6f;
55 }
56 
57 namespace pcl
58 {
59  namespace registration
60  {
61  /** \brief Globally Consistent Scan Matching based on an algorithm by Lu and Milios.
62  * \details A GraphSLAM algorithm where registration data is managed in a graph:
63  * <ul>
64  * <li>Vertices represent poses and hold the point cloud data and relative transformations.</li>
65  * <li>Edges represent pose constraints and hold the correspondence data between two point clouds.</li>
66  * </ul>
67  * Computation uses the first point cloud in the SLAM graph as a reference pose and attempts to align all other point clouds to it simultaneously.
68  * For more information:
69  * <ul><li>
70  * F. Lu, E. Milios,
71  * Globally Consistent Range Scan Alignment for Environment Mapping,
72  * Autonomous Robots 4, April 1997
73  * </li><li>
74  * Dorit Borrmann, Jan Elseberg, Kai Lingemann, Andreas Nüchter, and Joachim Hertzberg,
75  * The Efficient Extension of Globally Consistent Scan Matching to 6 DoF,
76  * In Proceedings of the 4th International Symposium on 3D Data Processing, Visualization and Transmission (3DPVT '08), June 2008
77  * </li></ul>
78  * Usage example:
79  * \code
80  * pcl::registration::LUM<pcl::PointXYZ> lum;
81  * // Add point clouds as vertices to the SLAM graph
82  * lum.addPointCloud (cloud_0);
83  * lum.addPointCloud (cloud_1);
84  * lum.addPointCloud (cloud_2);
85  * // Use your favorite pairwise correspondence estimation algorithm(s)
86  * corrs_0_to_1 = someAlgo (cloud_0, cloud_1);
87  * corrs_1_to_2 = someAlgo (cloud_1, cloud_2);
88  * corrs_2_to_0 = someAlgo (lum.getPointCloud (2), lum.getPointCloud (0));
89  * // Add the correspondence results as edges to the SLAM graph
90  * lum.setCorrespondences (0, 1, corrs_0_to_1);
91  * lum.setCorrespondences (1, 2, corrs_1_to_2);
92  * lum.setCorrespondences (2, 0, corrs_2_to_0);
93  * // Change the computation parameters
94  * lum.setMaxIterations (5);
95  * lum.setConvergenceThreshold (0.0);
96  * // Perform the actual LUM computation
97  * lum.compute ();
98  * // Return the concatenated point cloud result
99  * cloud_out = lum.getConcatenatedCloud ();
100  * // Return the separate point cloud transformations
101  * for(int i = 0; i < lum.getNumVertices (); i++)
102  * {
103  * transforms_out[i] = lum.getTransformation (i);
104  * }
105  * \endcode
106  * \author Frits Florentinus, Jochen Sprickerhof
107  * \ingroup registration
108  */
109  template<typename PointT>
110  class LUM
111  {
112  public:
113  typedef boost::shared_ptr<LUM<PointT> > Ptr;
114  typedef boost::shared_ptr<const LUM<PointT> > ConstPtr;
115 
117  typedef typename PointCloud::Ptr PointCloudPtr;
119 
121  {
124  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
125  };
127  {
131  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
132  };
133 
134  typedef boost::adjacency_list<boost::eigen_vecS, boost::eigen_vecS, boost::bidirectionalS, VertexProperties, EdgeProperties, boost::no_property, boost::eigen_listS> SLAMGraph;
135  typedef boost::shared_ptr<SLAMGraph> SLAMGraphPtr;
136  typedef typename SLAMGraph::vertex_descriptor Vertex;
137  typedef typename SLAMGraph::edge_descriptor Edge;
138 
139  /** \brief Empty constructor.
140  */
141  LUM ()
142  : slam_graph_ (new SLAMGraph)
143  , max_iterations_ (5)
144  , convergence_threshold_ (0.0)
145  {
146  }
147 
148  /** \brief Set the internal SLAM graph structure.
149  * \details All data used and produced by LUM is stored in this boost::adjacency_list.
150  * It is recommended to use the LUM class itself to build the graph.
151  * This method could otherwise be useful for managing several SLAM graphs in one instance of LUM.
152  * \param[in] slam_graph The new SLAM graph.
153  */
154  inline void
155  setLoopGraph (const SLAMGraphPtr &slam_graph);
156 
157  /** \brief Get the internal SLAM graph structure.
158  * \details All data used and produced by LUM is stored in this boost::adjacency_list.
159  * It is recommended to use the LUM class itself to build the graph.
160  * This method could otherwise be useful for managing several SLAM graphs in one instance of LUM.
161  * \return The current SLAM graph.
162  */
163  inline SLAMGraphPtr
164  getLoopGraph () const;
165 
166  /** \brief Get the number of vertices in the SLAM graph.
167  * \return The current number of vertices in the SLAM graph.
168  */
169  typename SLAMGraph::vertices_size_type
170  getNumVertices () const;
171 
172  /** \brief Set the maximum number of iterations for the compute() method.
173  * \details The compute() method finishes when max_iterations are met or when the convergence criteria is met.
174  * \param[in] max_iterations The new maximum number of iterations (default = 5).
175  */
176  void
177  setMaxIterations (int max_iterations);
178 
179  /** \brief Get the maximum number of iterations for the compute() method.
180  * \details The compute() method finishes when max_iterations are met or when the convergence criteria is met.
181  * \return The current maximum number of iterations (default = 5).
182  */
183  inline int
184  getMaxIterations () const;
185 
186  /** \brief Set the convergence threshold for the compute() method.
187  * \details When the compute() method computes the new poses relative to the old poses, it will determine the length of the difference vector.
188  * When the average length of all difference vectors becomes less than the convergence_threshold the convergence is assumed to be met.
189  * \param[in] convergence_threshold The new convergence threshold (default = 0.0).
190  */
191  void
192  setConvergenceThreshold (float convergence_threshold);
193 
194  /** \brief Get the convergence threshold for the compute() method.
195  * \details When the compute() method computes the new poses relative to the old poses, it will determine the length of the difference vector.
196  * When the average length of all difference vectors becomes less than the convergence_threshold the convergence is assumed to be met.
197  * \return The current convergence threshold (default = 0.0).
198  */
199  inline float
200  getConvergenceThreshold () const;
201 
202  /** \brief Add a new point cloud to the SLAM graph.
203  * \details This method will add a new vertex to the SLAM graph and attach a point cloud to that vertex.
204  * Optionally you can specify a pose estimate for this point cloud.
205  * A vertex' pose is always relative to the first vertex in the SLAM graph, i.e. the first point cloud that was added.
206  * Because this first vertex is the reference, you can not set a pose estimate for this vertex.
207  * Providing pose estimates to the vertices in the SLAM graph will reduce overall computation time of LUM.
208  * \note Vertex descriptors are typecastable to int.
209  * \param[in] cloud The new point cloud.
210  * \param[in] pose (optional) The pose estimate relative to the reference pose (first point cloud that was added).
211  * \return The vertex descriptor of the newly created vertex.
212  */
213  Vertex
214  addPointCloud (const PointCloudPtr &cloud, const Eigen::Vector6f &pose = Eigen::Vector6f::Zero ());
215 
216  /** \brief Change a point cloud on one of the SLAM graph's vertices.
217  * \details This method will change the point cloud attached to an existing vertex and will not alter the SLAM graph structure.
218  * Note that the correspondences attached to this vertex will not change and may need to be updated manually.
219  * \note Vertex descriptors are typecastable to int.
220  * \param[in] vertex The vertex descriptor of which to change the point cloud.
221  * \param[in] cloud The new point cloud for that vertex.
222  */
223  inline void
224  setPointCloud (const Vertex &vertex, const PointCloudPtr &cloud);
225 
226  /** \brief Return a point cloud from one of the SLAM graph's vertices.
227  * \note Vertex descriptors are typecastable to int.
228  * \param[in] vertex The vertex descriptor of which to return the point cloud.
229  * \return The current point cloud for that vertex.
230  */
231  inline PointCloudPtr
232  getPointCloud (const Vertex &vertex) const;
233 
234  /** \brief Change a pose estimate on one of the SLAM graph's vertices.
235  * \details A vertex' pose is always relative to the first vertex in the SLAM graph, i.e. the first point cloud that was added.
236  * Because this first vertex is the reference, you can not set a pose estimate for this vertex.
237  * Providing pose estimates to the vertices in the SLAM graph will reduce overall computation time of LUM.
238  * \note Vertex descriptors are typecastable to int.
239  * \param[in] vertex The vertex descriptor of which to set the pose estimate.
240  * \param[in] pose The new pose estimate for that vertex.
241  */
242  inline void
243  setPose (const Vertex &vertex, const Eigen::Vector6f &pose);
244 
245  /** \brief Return a pose estimate from one of the SLAM graph's vertices.
246  * \note Vertex descriptors are typecastable to int.
247  * \param[in] vertex The vertex descriptor of which to return the pose estimate.
248  * \return The current pose estimate of that vertex.
249  */
250  inline Eigen::Vector6f
251  getPose (const Vertex &vertex) const;
252 
253  /** \brief Return a pose estimate from one of the SLAM graph's vertices as an affine transformation matrix.
254  * \note Vertex descriptors are typecastable to int.
255  * \param[in] vertex The vertex descriptor of which to return the transformation matrix.
256  * \return The current transformation matrix of that vertex.
257  */
258  inline Eigen::Affine3f
259  getTransformation (const Vertex &vertex) const;
260 
261  /** \brief Add/change a set of correspondences for one of the SLAM graph's edges.
262  * \details The edges in the SLAM graph are directional and point from source vertex to target vertex.
263  * The query indices of the correspondences, index the points at the source vertex' point cloud.
264  * The matching indices of the correspondences, index the points at the target vertex' point cloud.
265  * If no edge was present at the specified location, this method will add a new edge to the SLAM graph and attach the correspondences to that edge.
266  * If the edge was already present, this method will overwrite the correspondence information of that edge and will not alter the SLAM graph structure.
267  * \note Vertex descriptors are typecastable to int.
268  * \param[in] source_vertex The vertex descriptor of the correspondences' source point cloud.
269  * \param[in] target_vertex The vertex descriptor of the correspondences' target point cloud.
270  * \param[in] corrs The new set of correspondences for that edge.
271  */
272  void
273  setCorrespondences (const Vertex &source_vertex,
274  const Vertex &target_vertex,
275  const pcl::CorrespondencesPtr &corrs);
276 
277  /** \brief Return a set of correspondences from one of the SLAM graph's edges.
278  * \note Vertex descriptors are typecastable to int.
279  * \param[in] source_vertex The vertex descriptor of the correspondences' source point cloud.
280  * \param[in] target_vertex The vertex descriptor of the correspondences' target point cloud.
281  * \return The current set of correspondences of that edge.
282  */
284  getCorrespondences (const Vertex &source_vertex, const Vertex &target_vertex) const;
285 
286  /** \brief Perform LUM's globally consistent scan matching.
287  * \details Computation uses the first point cloud in the SLAM graph as a reference pose and attempts to align all other point clouds to it simultaneously.
288  * <br>
289  * Things to keep in mind:
290  * <ul>
291  * <li>Only those parts of the graph connected to the reference pose will properly align to it.</li>
292  * <li>All sets of correspondences should span the same space and need to be sufficient to determine a rigid transformation.</li>
293  * <li>The algorithm draws it strength from loops in the graph because it will distribute errors evenly amongst those loops.</li>
294  * </ul>
295  * Computation ends when either of the following conditions hold:
296  * <ul>
297  * <li>The number of iterations reaches max_iterations. Use setMaxIterations() to change.</li>
298  * <li>The convergence criteria is met. Use setConvergenceThreshold() to change.</li>
299  * </ul>
300  * Computation will change the pose estimates for the vertices of the SLAM graph, not the point clouds attached to them.
301  * The results can be retrieved with getPose(), getTransformation(), getTransformedCloud() or getConcatenatedCloud().
302  */
303  void
304  compute ();
305 
306  /** \brief Return a point cloud from one of the SLAM graph's vertices compounded onto its current pose estimate.
307  * \note Vertex descriptors are typecastable to int.
308  * \param[in] vertex The vertex descriptor of which to return the transformed point cloud.
309  * \return The transformed point cloud of that vertex.
310  */
311  PointCloudPtr
312  getTransformedCloud (const Vertex &vertex) const;
313 
314  /** \brief Return a concatenated point cloud of all the SLAM graph's point clouds compounded onto their current pose estimates.
315  * \return The concatenated transformed point clouds of the entire SLAM graph.
316  */
317  PointCloudPtr
318  getConcatenatedCloud () const;
319 
320  protected:
321  /** \brief Linearized computation of C^-1 and C^-1*D (results stored in slam_graph_). */
322  void
323  computeEdge (const Edge &e);
324 
325  /** \brief Returns a pose corrected 6DoF incidence matrix. */
326  inline Eigen::Matrix6f
327  incidenceCorrection (const Eigen::Vector6f &pose);
328 
329  private:
330  /** \brief The internal SLAM graph structure. */
331  SLAMGraphPtr slam_graph_;
332 
333  /** \brief The maximum number of iterations for the compute() method. */
334  int max_iterations_;
335 
336  /** \brief The convergence threshold for the summed vector lengths of all poses. */
337  float convergence_threshold_;
338  };
339  }
340 }
341 
342 #ifdef PCL_NO_PRECOMPILE
343 #include <pcl/registration/impl/lum.hpp>
344 #endif
345 
346 #endif // PCL_REGISTRATION_LUM_H_
347 
void setCorrespondences(const Vertex &source_vertex, const Vertex &target_vertex, const pcl::CorrespondencesPtr &corrs)
Add/change a set of correspondences for one of the SLAM graph's edges.
Definition: lum.hpp:172
pcl::CorrespondencesPtr corrs_
Definition: lum.h:128
Vertex addPointCloud(const PointCloudPtr &cloud, const Eigen::Vector6f &pose=Eigen::Vector6f::Zero())
Add a new point cloud to the SLAM graph.
Definition: lum.hpp:95
boost::shared_ptr< const PointCloud< PointT > > ConstPtr
Definition: point_cloud.h:429
boost::shared_ptr< const LUM< PointT > > ConstPtr
Definition: lum.h:114
Eigen::Matrix< float, 6, 6 > Matrix6f
Definition: lum.h:54
PointCloudPtr getPointCloud(const Vertex &vertex) const
Return a point cloud from one of the SLAM graph's vertices.
Definition: lum.hpp:123
PointCloud::Ptr PointCloudPtr
Definition: lum.h:117
LUM()
Empty constructor.
Definition: lum.h:141
SLAMGraphPtr getLoopGraph() const
Get the internal SLAM graph structure.
Definition: lum.hpp:53
boost::shared_ptr< PointCloud< PointT > > Ptr
Definition: point_cloud.h:428
PointCloudPtr getTransformedCloud(const Vertex &vertex) const
Return a point cloud from one of the SLAM graph's vertices compounded onto its current pose estimate...
Definition: lum.hpp:273
int getMaxIterations() const
Get the maximum number of iterations for the compute() method.
Definition: lum.hpp:74
Eigen::Matrix< float, 6, 1 > Vector6f
Definition: lum.h:53
void compute()
Perform LUM's globally consistent scan matching.
Definition: lum.hpp:209
Globally Consistent Scan Matching based on an algorithm by Lu and Milios.
Definition: lum.h:110
void setLoopGraph(const SLAMGraphPtr &slam_graph)
Set the internal SLAM graph structure.
Definition: lum.hpp:46
Definition: edge.h:48
Eigen::Matrix6f incidenceCorrection(const Eigen::Vector6f &pose)
Returns a pose corrected 6DoF incidence matrix.
Definition: lum.hpp:401
PointCloud::ConstPtr PointCloudConstPtr
Definition: lum.h:118
SLAMGraph::edge_descriptor Edge
Definition: lum.h:137
pcl::CorrespondencesPtr getCorrespondences(const Vertex &source_vertex, const Vertex &target_vertex) const
Return a set of correspondences from one of the SLAM graph's edges.
Definition: lum.hpp:189
boost::adjacency_list< boost::eigen_vecS, boost::eigen_vecS, boost::bidirectionalS, VertexProperties, EdgeProperties, boost::no_property, boost::eigen_listS > SLAMGraph
Definition: lum.h:134
Eigen::Vector6f getPose(const Vertex &vertex) const
Return a pose estimate from one of the SLAM graph's vertices.
Definition: lum.hpp:152
SLAMGraph::vertices_size_type getNumVertices() const
Get the number of vertices in the SLAM graph.
Definition: lum.hpp:60
Eigen::Affine3f getTransformation(const Vertex &vertex) const
Return a pose estimate from one of the SLAM graph's vertices as an affine transformation matrix...
Definition: lum.hpp:164
boost::shared_ptr< LUM< PointT > > Ptr
Definition: lum.h:113
PointCloudPtr getConcatenatedCloud() const
Return a concatenated point cloud of all the SLAM graph's point clouds compounded onto their current ...
Definition: lum.hpp:282
void setPointCloud(const Vertex &vertex, const PointCloudPtr &cloud)
Change a point cloud on one of the SLAM graph's vertices.
Definition: lum.hpp:111
boost::shared_ptr< Correspondences > CorrespondencesPtr
SLAMGraph::vertex_descriptor Vertex
Definition: lum.h:136
pcl::PointCloud< PointT > PointCloud
Definition: lum.h:116
void setMaxIterations(int max_iterations)
Set the maximum number of iterations for the compute() method.
Definition: lum.hpp:67
void computeEdge(const Edge &e)
Linearized computation of C^-1 and C^-1*D (results stored in slam_graph_).
Definition: lum.hpp:297
void setConvergenceThreshold(float convergence_threshold)
Set the convergence threshold for the compute() method.
Definition: lum.hpp:81
float getConvergenceThreshold() const
Get the convergence threshold for the compute() method.
Definition: lum.hpp:88
boost::shared_ptr< SLAMGraph > SLAMGraphPtr
Definition: lum.h:135
void setPose(const Vertex &vertex, const Eigen::Vector6f &pose)
Change a pose estimate on one of the SLAM graph's vertices.
Definition: lum.hpp:135