From f6df6c05dd83b19fa90044106ebaca40957998ae Mon Sep 17 00:00:00 2001 From: Mike Vitus Date: Thu, 18 Aug 2016 19:27:43 -0700 Subject: [PATCH] Add an example for modeling and solving a 3D pose graph SLAM problem. Change-Id: I750ca5f20c495edfee5f60ffedccc5bd8ba2bb37 --- docs/source/nnls_tutorial.rst | 135 +++++++++++--- docs/source/pose_graph_3d_ex.png | Bin 0 -> 105676 bytes examples/slam/CMakeLists.txt | 6 + .../read_g2o.cc => common/read_g2o.h} | 93 ++++++---- examples/slam/pose_graph_2d/CMakeLists.txt | 3 +- examples/slam/pose_graph_2d/pose_graph_2d.cc | 2 +- examples/slam/pose_graph_2d/read_g2o.h | 64 ------- examples/slam/pose_graph_2d/types.h | 37 ++++ examples/slam/pose_graph_3d/CMakeLists.txt | 32 ++++ examples/slam/pose_graph_3d/README.md | 54 ++++++ examples/slam/pose_graph_3d/plot_results.py | 80 ++++++++ examples/slam/pose_graph_3d/pose_graph_3d.cc | 175 ++++++++++++++++++ .../pose_graph_3d/pose_graph_3d_error_term.h | 131 +++++++++++++ examples/slam/pose_graph_3d/types.h | 114 ++++++++++++ 14 files changed, 799 insertions(+), 127 deletions(-) create mode 100644 docs/source/pose_graph_3d_ex.png rename examples/slam/{pose_graph_2d/read_g2o.cc => common/read_g2o.h} (56%) delete mode 100644 examples/slam/pose_graph_2d/read_g2o.h create mode 100644 examples/slam/pose_graph_3d/CMakeLists.txt create mode 100644 examples/slam/pose_graph_3d/README.md create mode 100755 examples/slam/pose_graph_3d/plot_results.py create mode 100644 examples/slam/pose_graph_3d/pose_graph_3d.cc create mode 100644 examples/slam/pose_graph_3d/pose_graph_3d_error_term.h create mode 100644 examples/slam/pose_graph_3d/types.h diff --git a/docs/source/nnls_tutorial.rst b/docs/source/nnls_tutorial.rst index 3a2cc65eb..8882cadb4 100644 --- a/docs/source/nnls_tutorial.rst +++ b/docs/source/nnls_tutorial.rst @@ -913,36 +913,125 @@ directory contains a number of other examples: The executable :member:`pose_graph_2d` expects the first argument to be the path to the problem definition. To run the executable, - .. code-block:: bash + .. code-block:: bash - /path/to/bin/pose_graph_2d /path/to/dataset/dataset.g2o + /path/to/bin/pose_graph_2d /path/to/dataset/dataset.g2o - A python script is provided to visualize the resulting output files. + A python script is provided to visualize the resulting output files. - .. code-block:: bash + .. code-block:: bash - /path/to/repo/examples/slam/pose_graph_2d/plot_results.py --optimized_poses ./poses_optimized.txt --initial_poses ./poses_original.txt + /path/to/repo/examples/slam/pose_graph_2d/plot_results.py --optimized_poses ./poses_optimized.txt --initial_poses ./poses_original.txt - As an example, a standard synthetic benchmark dataset [#f10]_ created by Edwin - Olson which has 3500 nodes in a grid world with a total of 5598 edges was - solved. Visualizing the results with the provided script produces: + As an example, a standard synthetic benchmark dataset [#f10]_ created by + Edwin Olson which has 3500 nodes in a grid world with a total of 5598 edges + was solved. Visualizing the results with the provided script produces: - .. figure:: manhattan_olson_3500_result.png - :figwidth: 600px - :height: 600px - :align: center + .. figure:: manhattan_olson_3500_result.png + :figwidth: 600px + :height: 600px + :align: center - with the original poses in green and the optimized poses in blue. As shown, - the optimized poses more closely match the underlying grid world. Note, the - left side of the graph has a small yaw drift due to a lack of relative - constraints to provide enough information to reconstruct the trajectory. + with the original poses in green and the optimized poses in blue. As shown, + the optimized poses more closely match the underlying grid world. Note, the + left side of the graph has a small yaw drift due to a lack of relative + constraints to provide enough information to reconstruct the trajectory. - .. rubric:: Footnotes + .. rubric:: Footnotes - .. [#f9] Giorgio Grisetti, Rainer Kummerle, Cyrill Stachniss, Wolfram - Burgard. A Tutorial on Graph-Based SLAM. IEEE Intelligent Transportation - Systems Magazine, 52(3):199–222, 2010. + .. [#f9] Giorgio Grisetti, Rainer Kummerle, Cyrill Stachniss, Wolfram + Burgard. A Tutorial on Graph-Based SLAM. IEEE Intelligent Transportation + Systems Magazine, 52(3):199–222, 2010. - .. [#f10] E. Olson, J. Leonard, and S. Teller, “Fast iterative optimization of - pose graphs with poor initial estimates,” in Robotics and Automation - (ICRA), IEEE International Conference on, 2006, pp. 2262–2269. + .. [#f10] E. Olson, J. Leonard, and S. Teller, “Fast iterative optimization of + pose graphs with poor initial estimates,” in Robotics and Automation + (ICRA), IEEE International Conference on, 2006, pp. 2262–2269. + +#. `slam/pose_graph_3d/pose_graph_3d.cc + `_ + The following explains how to formulate the pose graph based SLAM problem in + 3-Dimensions with relative pose constraints. The example also illustrates how + to use Eigen's geometry module with Ceres's automatic differentiation + functionality. + + The robot at timestamp :math:`t` has state :math:`x_t = [p^T, q^T]^T` where + :math:`p` is a 3D vector that represents the position and :math:`q` is the + orientation represented as an Eigen quaternion. The measurement of the + relative transform between the robot state at two timestamps :math:`a` and + :math:`b` is given as: :math:`z_{ab} = [\hat{p}_{ab}^T, \hat{q}_{ab}^T]^T`. + The residual implemented in the Ceres cost function which computes the error + between the measurement and the predicted measurement is: + + .. math:: r_{ab} = + \left[ + \begin{array}{c} + R(q_a)^{T} (p_b - p_a) - \hat{p}_{ab} \\ + 2.0 \mathrm{vec}\left((q_a^{-1} q_b) \hat{q}_{ab}^{-1}\right) + \end{array} + \right] + + where the function :math:`\mathrm{vec}()` returns the vector part of the + quaternion, i.e. :math:`[q_x, q_y, q_z]`, and :math:`R(q)` is the rotation + matrix for the quaternion. + + To finish the cost function, we need to weight the residual by the + uncertainty of the measurement. Hence, we pre-multiply the residual by the + inverse square root of the covariance matrix for the measurement, + i.e. :math:`\Sigma_{ab}^{-\frac{1}{2}} r_{ab}` where :math:`\Sigma_{ab}` is + the covariance. + + Given that we are using a quaternion to represent the orientation, we need to + use a local parameterization (:class:`EigenQuaternionParameterization`) to + only apply updates orthogonal to the 4-vector defining the + quaternion. Eigen's quaternion uses a different internal memory layout for + the elements of the quaternion than what is commonly used. Specifically, + Eigen stores the elements in memory as :math:`[x, y, z, w]` where the real + part is last whereas it is typically stored first. Note, when creating an + Eigen quaternion through the constructor the elements are accepted in + :math:`w`, :math:`x`, :math:`y`, :math:`z` order. Since Ceres operates on + parameter blocks which are raw double pointers this difference is important + and requires a different parameterization. + + This package includes an executable :member:`pose_graph_3d` that will read a + problem definition file. This executable can work with any 3D problem + definition that uses the g2o format with quaternions used for the orientation + representation. It would be relatively straightforward to implement a new + reader for a different format such as TORO or others. :member:`pose_graph_3d` + will print the Ceres solver full summary and then output to disk the original + and optimized poses (``poses_original.txt`` and ``poses_optimized.txt``, + respectively) of the robot in the following format: + + .. code-block:: bash + + pose_id x y z q_x q_y q_z q_w + pose_id x y z q_x q_y q_z q_w + pose_id x y z q_x q_y q_z q_w + ... + + where ``pose_id`` is the corresponding integer ID from the file + definition. Note, the file will be sorted in ascending order for the + ``pose_id``. + + The executable :member:`pose_graph_3d` expects the first argument to be the + path to the problem definition. The executable can be run via + + .. code-block:: bash + + /path/to/bin/pose_graph_3d /path/to/dataset/dataset.g2o + + A script is provided to visualize the resulting output files. There is also + an option to enable equal axes using ``--axes_equal`` + + .. code-block:: bash + + /path/to/repo/examples/slam/pose_graph_3d/plot_results.py --optimized_poses ./poses_optimized.txt --initial_poses ./poses_original.txt + + As an example, a standard synthetic benchmark dataset [#f9]_ where the robot is + traveling on the surface of a sphere which has 2500 nodes with a total of + 4949 edges was solved. Visualizing the results with the provided script + produces: + + .. figure:: pose_graph_3d_ex.png + :figwidth: 600px + :height: 300px + :align: center diff --git a/docs/source/pose_graph_3d_ex.png b/docs/source/pose_graph_3d_ex.png new file mode 100644 index 0000000000000000000000000000000000000000..ae2cfc389a0f04c3e3ab1cb5b1eb7616a1da354c GIT binary patch literal 105676 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 Pd}*m2Q+=vpaqa&B=yJ_J literal 0 HcmV?d00001 diff --git a/examples/slam/CMakeLists.txt b/examples/slam/CMakeLists.txt index d2c0d42f8..a5f12c45b 100644 --- a/examples/slam/CMakeLists.txt +++ b/examples/slam/CMakeLists.txt @@ -27,4 +27,10 @@ # POSSIBILITY OF SUCH DAMAGE. # # Author: vitus@google.com (Michael Vitus) +include_directories(./) + add_subdirectory(pose_graph_2d) + +if (GFLAGS) + add_subdirectory(pose_graph_3d) +endif (GFLAGS) \ No newline at end of file diff --git a/examples/slam/pose_graph_2d/read_g2o.cc b/examples/slam/common/read_g2o.h similarity index 56% rename from examples/slam/pose_graph_2d/read_g2o.cc rename to examples/slam/common/read_g2o.h index 22f424b73..71b071c7e 100644 --- a/examples/slam/pose_graph_2d/read_g2o.cc +++ b/examples/slam/common/read_g2o.h @@ -27,63 +27,81 @@ // POSSIBILITY OF SUCH DAMAGE. // // Author: vitus@google.com (Michael Vitus) +// +// Reads a file in the g2o filename format that describes a pose graph problem. -#include "read_g2o.h" +#ifndef EXAMPLES_CERES_READ_G2O_H_ +#define EXAMPLES_CERES_READ_G2O_H_ -#include #include +#include -#include "Eigen/Core" #include "glog/logging.h" -#include "normalize_angle.h" namespace ceres { namespace examples { -namespace { + // Reads a single pose from the input and inserts it into the map. Returns false // if there is a duplicate entry. -bool ReadVertex(std::ifstream* infile, std::map* poses) { +template +bool ReadVertex(std::ifstream* infile, + std::map, Allocator>* poses) { int id; - Pose2d pose; - *infile >> id >> pose.x >> pose.y >> pose.yaw_radians; - // Normalize the angle between -pi to pi. - pose.yaw_radians = NormalizeAngle(pose.yaw_radians); + Pose pose; + *infile >> id >> pose; + // Ensure we don't have duplicate poses. if (poses->find(id) != poses->end()) { - std::cerr << "Duplicate vertex with ID: " << id << '\n'; + LOG(ERROR) << "Duplicate vertex with ID: " << id; return false; } (*poses)[id] = pose; + return true; } // Reads the contraints between two vertices in the pose graph +template void ReadConstraint(std::ifstream* infile, - std::vector* constraints) { - Constraint2d constraint; - - // Read in the constraint data which is the x, y, yaw_radians and then the - // upper triangular part of the information matrix. - *infile >> constraint.id_begin >> constraint.id_end >> constraint.x >> - constraint.y >> constraint.yaw_radians >> - constraint.information(0, 0) >> constraint.information(0, 1) >> - constraint.information(0, 2) >> constraint.information(1, 1) >> - constraint.information(1, 2) >> constraint.information(2, 2); - - // Set the lower triangular part of the information matrix. - constraint.information(1, 0) = constraint.information(0, 1); - constraint.information(2, 0) = constraint.information(0, 2); - constraint.information(2, 1) = constraint.information(1, 2); - - // Normalize the angle between -pi to pi. - constraint.yaw_radians = NormalizeAngle(constraint.yaw_radians); + std::vector* constraints) { + Constraint constraint; + *infile >> constraint; constraints->push_back(constraint); } -} -bool ReadG2oFile(const std::string& filename, std::map* poses, - std::vector* constraints) { +// Reads a file in the g2o filename format that describes a pose graph +// problem. The g2o format consists of two entries, vertices and constraints. +// +// In 2D, a vertex is defined as follows: +// +// VERTEX_SE2 ID x_meters y_meters yaw_radians +// +// A constraint is defined as follows: +// +// EDGE_SE2 ID_A ID_B A_x_B A_y_B A_yaw_B I_11 I_12 I_13 I_22 I_23 I_33 +// +// where I_ij is the (i, j)-th entry of the information matrix for the +// measurement. +// +// +// In 3D, a vertex is defined as follows: +// +// VERTEX_SE3:QUAT ID x y z q_x q_y q_z q_w +// +// where the quaternion is in Hamilton form. +// A constraint is defined as follows: +// +// EDGE_SE3:QUAT ID_a ID_b x_ab y_ab z_ab q_x_ab q_y_ab q_z_ab q_w_ab I_11 I_12 I_13 ... I_16 I_22 I_23 ... I_26 ... I_66 // NOLINT +// +// where I_ij is the (i, j)-th entry of the information matrix for the +// measurement. Only the upper-triangular part is stored. The measurement order +// is the delta position followed by the delta orientation. +template +bool ReadG2oFile(const std::string& filename, + std::map, MapAllocator>* poses, + std::vector* constraints) { CHECK(poses != NULL); CHECK(constraints != NULL); @@ -92,7 +110,6 @@ bool ReadG2oFile(const std::string& filename, std::map* poses, std::ifstream infile(filename.c_str()); if (!infile) { - std::cerr << "Error reading the file: " << filename << '\n'; return false; } @@ -100,18 +117,18 @@ bool ReadG2oFile(const std::string& filename, std::map* poses, while (infile.good()) { // Read whether the type is a node or a constraint. infile >> data_type; - if (data_type == "VERTEX_SE2") { + if (data_type == Pose::name()) { if (!ReadVertex(&infile, poses)) { return false; } - } else if (data_type == "EDGE_SE2") { + } else if (data_type == Constraint::name()) { ReadConstraint(&infile, constraints); } else { - std::cerr << "Unknown data type: " << data_type << '\n'; + LOG(ERROR) << "Unknown data type: " << data_type; return false; } - // Clear any trailing whitespace from the file. + // Clear any trailing whitespace from the line. infile >> std::ws; } @@ -120,3 +137,5 @@ bool ReadG2oFile(const std::string& filename, std::map* poses, } // namespace examples } // namespace ceres + +#endif // EXAMPLES_CERES_READ_G2O_H_ diff --git a/examples/slam/pose_graph_2d/CMakeLists.txt b/examples/slam/pose_graph_2d/CMakeLists.txt index 117f4cd30..5574ad1e4 100644 --- a/examples/slam/pose_graph_2d/CMakeLists.txt +++ b/examples/slam/pose_graph_2d/CMakeLists.txt @@ -33,7 +33,6 @@ add_executable(pose_graph_2d normalize_angle.h pose_graph_2d.cc pose_graph_2d_error_term.h - read_g2o.cc - ) + types.h) target_link_libraries(pose_graph_2d ceres) diff --git a/examples/slam/pose_graph_2d/pose_graph_2d.cc b/examples/slam/pose_graph_2d/pose_graph_2d.cc index e6caf7e36..969bdb5b0 100644 --- a/examples/slam/pose_graph_2d/pose_graph_2d.cc +++ b/examples/slam/pose_graph_2d/pose_graph_2d.cc @@ -41,8 +41,8 @@ #include "angle_local_parameterization.h" #include "ceres/ceres.h" +#include "common/read_g2o.h" #include "pose_graph_2d_error_term.h" -#include "read_g2o.h" #include "types.h" namespace ceres { diff --git a/examples/slam/pose_graph_2d/read_g2o.h b/examples/slam/pose_graph_2d/read_g2o.h deleted file mode 100644 index dfa30d674..000000000 --- a/examples/slam/pose_graph_2d/read_g2o.h +++ /dev/null @@ -1,64 +0,0 @@ -// Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2016 Google Inc. All rights reserved. -// http://ceres-solver.org/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of Google Inc. nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// Author: vitus@google.com (Michael Vitus) -// -// Reads a 2D pose graph problem formulation in g2o format. - -#ifndef CERES_EXAMPLES_POSE_GRAPH_2D_READ_G2O_H_ -#define CERES_EXAMPLES_POSE_GRAPH_2D_READ_G2O_H_ - -#include -#include -#include - -#include "types.h" - -namespace ceres { -namespace examples { - -// Reads a file in the g2o filename format that describes a 2D pose graph -// problem. The g2o format consists of two entries, vertices and constraints. A -// vertex is defined as follows: -// -// VERTEX_SE2 ID x_meters y_meters yaw_radians -// -// A constraint is defined as follows: -// -// EDGE_SE2 ID_A ID_B A_x_B A_y_B A_yaw_B I_11 I_12 I_13 I_22 I_23 I_33 -// -// where I_ij is the (i, j)-th entry of the information matrix for the -// measurement. -bool ReadG2oFile(const std::string& filename, - std::map* poses, - std::vector* constraints); - -} // namespace examples -} // namespace ceres - -#endif // CERES_EXAMPLES_POSE_GRAPH_2D_READ_G2O_H_ diff --git a/examples/slam/pose_graph_2d/types.h b/examples/slam/pose_graph_2d/types.h index d54c733d1..a54d9bf2f 100644 --- a/examples/slam/pose_graph_2d/types.h +++ b/examples/slam/pose_graph_2d/types.h @@ -35,7 +35,10 @@ #ifndef CERES_EXAMPLES_POSE_GRAPH_2D_TYPES_H_ #define CERES_EXAMPLES_POSE_GRAPH_2D_TYPES_H_ +#include + #include "Eigen/Core" +#include "normalize_angle.h" namespace ceres { namespace examples { @@ -45,8 +48,20 @@ struct Pose2d { double x; double y; double yaw_radians; + + // The name of the data type in the g2o file format. + static std::string name() { + return "VERTEX_SE2"; + } }; +std::istream& operator>>(std::istream& input, Pose2d& pose) { + input >> pose.x >> pose.y >> pose.yaw_radians; + // Normalize the angle between -pi to pi. + pose.yaw_radians = NormalizeAngle(pose.yaw_radians); + return input; +} + // The constraint between two vertices in the pose graph. The constraint is the // transformation from vertex id_begin to vertex id_end. struct Constraint2d { @@ -60,8 +75,30 @@ struct Constraint2d { // The inverse of the covariance matrix for the measurement. The order of the // entries are x, y, and yaw. Eigen::Matrix3d information; + + // The name of the data type in the g2o file format. + static std::string name() { + return "EDGE_SE2"; + } }; +std::istream& operator>>(std::istream& input, Constraint2d& constraint) { + input >> constraint.id_begin >> constraint.id_end >> constraint.x >> + constraint.y >> constraint.yaw_radians >> + constraint.information(0, 0) >> constraint.information(0, 1) >> + constraint.information(0, 2) >> constraint.information(1, 1) >> + constraint.information(1, 2) >> constraint.information(2, 2); + + // Set the lower triangular part of the information matrix. + constraint.information(1, 0) = constraint.information(0, 1); + constraint.information(2, 0) = constraint.information(0, 2); + constraint.information(2, 1) = constraint.information(1, 2); + + // Normalize the angle between -pi to pi. + constraint.yaw_radians = NormalizeAngle(constraint.yaw_radians); + return input; +} + } // namespace examples } // namespace ceres diff --git a/examples/slam/pose_graph_3d/CMakeLists.txt b/examples/slam/pose_graph_3d/CMakeLists.txt new file mode 100644 index 000000000..1be4e8562 --- /dev/null +++ b/examples/slam/pose_graph_3d/CMakeLists.txt @@ -0,0 +1,32 @@ +# Ceres Solver - A fast non-linear least squares minimizer +# Copyright 2016 Google Inc. All rights reserved. +# http://ceres-solver.org/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Google Inc. nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# Author: vitus@google.com (Michael Vitus) + +add_executable(pose_graph_3d pose_graph_3d.cc) +target_link_libraries(pose_graph_3d ceres ${GFLAGS_LIBRARIES}) diff --git a/examples/slam/pose_graph_3d/README.md b/examples/slam/pose_graph_3d/README.md new file mode 100644 index 000000000..a16aa093e --- /dev/null +++ b/examples/slam/pose_graph_3d/README.md @@ -0,0 +1,54 @@ +Pose Graph 3D +---------------- + +The Simultaneous Localization and Mapping (SLAM) problem consists of building a +map of an unknown environment while simultaneously localizing against this +map. The main difficulty of this problem stems from not having any additional +external aiding information such as GPS. SLAM has been considered one of the +fundamental challenges of robotics. A pose graph optimization problem is one +example of a SLAM problem. + +The example also illustrates how to use Eigen's geometry module with Ceres' +automatic differentiation functionality. To represent the orientation, we will +use Eigen's quaternion which uses the Hamiltonian convention but has different +element ordering as compared with Ceres's rotation representation. Specifically +they differ by whether the scalar component q_w is first or last; the element +order for Ceres's quaternion is [q_w, q_x, q_y, q_z] where as Eigen's quaternion +is [q_x, q_y, q_z, q_w]. + +This package defines the necessary Ceres cost functions needed to model the +3-dimensional pose graph optimization problem as well as a binary to build and +solve the problem. The cost functions are shown for instruction purposes and can +be speed up by using analytical derivatives which take longer to implement. + + +Running +----------- +This package includes an executable `pose_graph_3d` that will read a problem +definition file. This executable can work with any 3D problem definition that +uses the g2o format with quaternions used for the orientation representation. It +would be relatively straightforward to implement a new reader for a different +format such as TORO or others. `pose_graph_3d` will print the Ceres solver full +summary and then output to disk the original and optimized poses +(`poses_original.txt` and `poses_optimized.txt`, respectively) of the robot in +the following format: +``` +pose_id x y z q_x q_y q_z q_w +pose_id x y z q_x q_y q_z q_w +pose_id x y z q_x q_y q_z q_w +... +``` +where `pose_id` is the corresponding integer ID from the file definition. Note, +the file will be sorted in ascending order for the ```pose_id```. + +The executable `pose_graph_3d` expects the first argument to be the path to the +problem definition. To run the executable, +``` +/path/to/bin/pose_graph_3d /path/to/dataset/dataset.g2o +``` + +A script is provided to visualize the resulting output files. There is also an +option to enable equal axes using ```--axes_equal```. +``` +/path/to/repo/examples/slam/pose_graph_3d/plot_results.py --optimized_poses ./poses_optimized.txt --initial_poses ./poses_original.txt +``` diff --git a/examples/slam/pose_graph_3d/plot_results.py b/examples/slam/pose_graph_3d/plot_results.py new file mode 100755 index 000000000..defb9e634 --- /dev/null +++ b/examples/slam/pose_graph_3d/plot_results.py @@ -0,0 +1,80 @@ +#!/usr/bin/python +# +# Plots the results from the 3D pose graph optimization. It will draw a line +# between consecutive vertices. The commandline expects two optional filenames: +# +# ./plot_results.py --initial_poses filename --optimized_poses filename +# +# The files have the following format: +# ID x y z q_x q_y q_z q_w + +from mpl_toolkits.mplot3d import Axes3D +import matplotlib.pyplot as plot +import numpy +import sys +from optparse import OptionParser + +def set_axes_equal(axes): + ''' Sets the axes of a 3D plot to have equal scale. ''' + x_limits = axes.get_xlim3d() + y_limits = axes.get_ylim3d() + z_limits = axes.get_zlim3d() + + x_range = abs(x_limits[1] - x_limits[0]) + x_middle = numpy.mean(x_limits) + y_range = abs(y_limits[1] - y_limits[0]) + y_middle = numpy.mean(y_limits) + z_range = abs(z_limits[1] - z_limits[0]) + z_middle = numpy.mean(z_limits) + + length = 0.5 * max([x_range, y_range, z_range]) + + axes.set_xlim3d([x_middle - length, x_middle + length]) + axes.set_ylim3d([y_middle - length, y_middle + length]) + axes.set_zlim3d([z_middle - length, z_middle + length]) + +parser = OptionParser() +parser.add_option("--initial_poses", dest="initial_poses", + default="", help="The filename that contains the original poses.") +parser.add_option("--optimized_poses", dest="optimized_poses", + default="", help="The filename that contains the optimized poses.") +parser.add_option("-e", "--axes_equal", action="store_true", dest="axes_equal", + default="", help="Make the plot axes equal.") +(options, args) = parser.parse_args() + +# Read the original and optimized poses files. +poses_original = None +if options.initial_poses != '': + poses_original = numpy.genfromtxt(options.initial_poses, + usecols = (1, 2, 3)) + +poses_optimized = None +if options.optimized_poses != '': + poses_optimized = numpy.genfromtxt(options.optimized_poses, + usecols = (1, 2, 3)) + +# Plots the results for the specified poses. +figure = plot.figure() + +if poses_original is not None: + axes = plot.subplot(1, 2, 1, projection='3d') + plot.plot(poses_original[:, 0], poses_original[:, 1], poses_original[:, 2], + '-', alpha=0.5, color="green") + plot.title('Original') + if options.axes_equal: + axes.set_aspect('equal') + set_axes_equal(axes) + + +if poses_optimized is not None: + axes = plot.subplot(1, 2, 2, projection='3d') + plot.plot(poses_optimized[:, 0], poses_optimized[:, 1], poses_optimized[:, 2], + '-', alpha=0.5, color="blue") + plot.title('Optimized') + if options.axes_equal: + axes.set_aspect('equal') + set_axes_equal(plot.gca()) + + +# Show the plot and wait for the user to close. +plot.show() diff --git a/examples/slam/pose_graph_3d/pose_graph_3d.cc b/examples/slam/pose_graph_3d/pose_graph_3d.cc new file mode 100644 index 000000000..c7c47fd7c --- /dev/null +++ b/examples/slam/pose_graph_3d/pose_graph_3d.cc @@ -0,0 +1,175 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2016 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: vitus@google.com (Michael Vitus) + +#include +#include +#include + +#include "ceres/ceres.h" +#include "common/read_g2o.h" +#include "glog/logging.h" +#include "pose_graph_3d_error_term.h" +#include "types.h" + +DEFINE_string(input_filename, "", + "The pose graph definition filename in g2o format."); + +namespace ceres { +namespace examples { + +// Constructs the nonlinear least squares optimization problem from the pose +// graph constraints. +void BuildOptimizationProblem(const VectorOfConstraints& constraints, + MapOfPoses* poses, ceres::Problem* problem) { + CHECK(poses != NULL); + CHECK(problem != NULL); + if (constraints.empty()) { + LOG(INFO) << "No constraints, no problem to optimize."; + return; + } + + ceres::LossFunction* loss_function = NULL; + ceres::LocalParameterization* quaternion_local_parameterization = + new EigenQuaternionParameterization; + + for (VectorOfConstraints::const_iterator constraints_iter = + constraints.begin(); + constraints_iter != constraints.end(); ++constraints_iter) { + const Constraint3d& constraint = *constraints_iter; + + MapOfPoses::iterator pose_begin_iter = poses->find(constraint.id_begin); + CHECK(pose_begin_iter != poses->end()) + << "Pose with ID: " << constraint.id_begin << " not found."; + MapOfPoses::iterator pose_end_iter = poses->find(constraint.id_end); + CHECK(pose_end_iter != poses->end()) + << "Pose with ID: " << constraint.id_end << " not found."; + + const Eigen::Matrix sqrt_information = + constraint.information.llt().matrixL(); + // Ceres will take ownership of the pointer. + ceres::CostFunction* cost_function = + PoseGraph3dErrorTerm::Create(constraint.t_be, sqrt_information); + + problem->AddResidualBlock(cost_function, loss_function, + pose_begin_iter->second.p.data(), + pose_begin_iter->second.q.coeffs().data(), + pose_end_iter->second.p.data(), + pose_end_iter->second.q.coeffs().data()); + + problem->SetParameterization(pose_begin_iter->second.q.coeffs().data(), + quaternion_local_parameterization); + problem->SetParameterization(pose_end_iter->second.q.coeffs().data(), + quaternion_local_parameterization); + } + + // The pose graph optimization problem has six DOFs that are not fully + // constrained. This is typically referred to as gauge freedom. You can apply + // a rigid body transformation to all the nodes and the optimization problem + // will still have the exact same cost. The Levenberg-Marquardt algorithm has + // internal damping which mitigates this issue, but it is better to properly + // constrain the gauge freedom. This can be done by setting one of the poses + // as constant so the optimizer cannot change it. + MapOfPoses::iterator pose_start_iter = poses->begin(); + CHECK(pose_start_iter != poses->end()) << "There are no poses."; + problem->SetParameterBlockConstant(pose_start_iter->second.p.data()); + problem->SetParameterBlockConstant(pose_start_iter->second.q.coeffs().data()); +} + +// Returns true if the solve was successful. +bool SolveOptimizationProblem(ceres::Problem* problem) { + CHECK(problem != NULL); + + ceres::Solver::Options options; + options.max_num_iterations = 200; + options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY; + + ceres::Solver::Summary summary; + ceres::Solve(options, problem, &summary); + + std::cout << summary.FullReport() << '\n'; + + return summary.IsSolutionUsable(); +} + +// Output the poses to the file with format: id x y z q_x q_y q_z q_w. +bool OutputPoses(const std::string& filename, const MapOfPoses& poses) { + std::fstream outfile; + outfile.open(filename.c_str(), std::istream::out); + if (!outfile) { + LOG(ERROR) << "Error opening the file: " << filename; + return false; + } + for (std::map, + Eigen::aligned_allocator > >:: + const_iterator poses_iter = poses.begin(); + poses_iter != poses.end(); ++poses_iter) { + const std::map, + Eigen::aligned_allocator > >:: + value_type& pair = *poses_iter; + outfile << pair.first << " " << pair.second.p.transpose() << " " + << pair.second.q.x() << " " << pair.second.q.y() << " " + << pair.second.q.z() << " " << pair.second.q.w() << '\n'; + } + return true; +} + +} // namespace examples +} // namespace ceres + +int main(int argc, char** argv) { + google::InitGoogleLogging(argv[0]); + CERES_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true); + + CHECK(FLAGS_input_filename != "") << "Need to specify the filename to read."; + + ceres::examples::MapOfPoses poses; + ceres::examples::VectorOfConstraints constraints; + + CHECK( + ceres::examples::ReadG2oFile(FLAGS_input_filename, &poses, &constraints)) + << "Error reading the file: " << FLAGS_input_filename; + + std::cout << "Number of poses: " << poses.size() << '\n'; + std::cout << "Number of constraints: " << constraints.size() << '\n'; + + CHECK(ceres::examples::OutputPoses("poses_original.txt", poses)) + << "Error outputting to poses_original.txt"; + + ceres::Problem problem; + ceres::examples::BuildOptimizationProblem(constraints, &poses, &problem); + + CHECK(ceres::examples::SolveOptimizationProblem(&problem)) + << "The solve was not successful, exiting."; + + CHECK(ceres::examples::OutputPoses("poses_optimized.txt", poses)) + << "Error outputting to poses_original.txt"; + + return 0; +} diff --git a/examples/slam/pose_graph_3d/pose_graph_3d_error_term.h b/examples/slam/pose_graph_3d/pose_graph_3d_error_term.h new file mode 100644 index 000000000..aca819e2a --- /dev/null +++ b/examples/slam/pose_graph_3d/pose_graph_3d_error_term.h @@ -0,0 +1,131 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2016 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: vitus@google.com (Michael Vitus) + +#ifndef EXAMPLES_CERES_POSE_GRAPH_3D_ERROR_TERM_H_ +#define EXAMPLES_CERES_POSE_GRAPH_3D_ERROR_TERM_H_ + +#include "Eigen/Core" +#include "ceres/autodiff_cost_function.h" + +#include "types.h" + +namespace ceres { +namespace examples { + +// Computes the error term for two poses that have a relative pose measurement +// between them. Let the hat variables be the measurement. We have two poses x_a +// and x_b. Through sensor measurements we can measure the transformation of +// frame B w.r.t frame A denoted as t_ab_hat. We can compute an error metric +// between the current estimate of the poses and the measurement. +// +// In this formulation, we have chosen to represent the rigid transformation as +// a Hamiltonian quaternion, q, and position, p. The quaternion ordering is +// [x, y, z, w]. + +// The estimated measurement is: +// t_ab = [ p_ab ] = [ R(q_a)^T * (p_b - p_a) ] +// [ q_ab ] [ q_a^{-1] * q_b ] +// +// where ^{-1} denotes the inverse and R(q) is the rotation matrix for the +// quaternion. Now we can compute an error metric between the estimated and +// measurement transformation. For the orientation error, we will use the +// standard multiplicative error resulting in: +// +// error = [ p_ab - \hat{p}_ab ] +// [ 2.0 * Vec(q_ab * \hat{q}_ab^{-1}) ] +// +// where Vec(*) returns the vector (imaginary) part of the quaternion. Since +// the measurement has an uncertainty associated with how accurate it is, we +// will weight the errors by the square root of the measurement information +// matrix: +// +// residuals = I^{1/2) * error +// where I is the information matrix which is the inverse of the covariance. +class PoseGraph3dErrorTerm { + public: + PoseGraph3dErrorTerm(const Pose3d& t_ab_measured, + const Eigen::Matrix& sqrt_information) + : t_ab_measured_(t_ab_measured), sqrt_information_(sqrt_information) {} + + template + bool operator()(const T* const p_a_ptr, const T* const q_a_ptr, + const T* const p_b_ptr, const T* const q_b_ptr, + T* residuals_ptr) const { + Eigen::Map > p_a(p_a_ptr); + Eigen::Map > q_a(q_a_ptr); + + Eigen::Map > p_b(p_b_ptr); + Eigen::Map > q_b(q_b_ptr); + + // Compute the relative transformation between the two frames. + Eigen::Quaternion q_a_inverse = q_a.conjugate(); + Eigen::Quaternion q_ab_estimated = q_a_inverse * q_b; + + // Represent the displacement between the two frames in the A frame. + Eigen::Matrix p_ab_estimated = q_a_inverse * (p_b - p_a); + + // Compute the error between the two orientation estimates. + Eigen::Quaternion delta_q = + t_ab_measured_.q.template cast() * q_ab_estimated.conjugate(); + + // Compute the residuals. + // [ position ] [ delta_p ] + // [ orientation (3x1)] = [ 2 * delta_q(0:2) ] + Eigen::Map > residuals(residuals_ptr); + residuals.template block<3, 1>(0, 0) = + p_ab_estimated - t_ab_measured_.p.template cast(); + residuals.template block<3, 1>(3, 0) = T(2.0) * delta_q.vec(); + + // Scale the residuals by the measurement uncertainty. + residuals.applyOnTheLeft(sqrt_information_.template cast()); + + return true; + } + + static ceres::CostFunction* Create( + const Pose3d& t_ab_measured, + const Eigen::Matrix& sqrt_information) { + return new ceres::AutoDiffCostFunction( + new PoseGraph3dErrorTerm(t_ab_measured, sqrt_information)); + } + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + private: + // The measurement for the position of B relative to A in the A frame. + const Pose3d t_ab_measured_; + // The square root of the measurement information matrix. + const Eigen::Matrix sqrt_information_; +}; + +} // namespace examples +} // namespace ceres + +#endif // EXAMPLES_CERES_POSE_GRAPH_3D_ERROR_TERM_H_ diff --git a/examples/slam/pose_graph_3d/types.h b/examples/slam/pose_graph_3d/types.h new file mode 100644 index 000000000..2f125010f --- /dev/null +++ b/examples/slam/pose_graph_3d/types.h @@ -0,0 +1,114 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2016 Google Inc. All rights reserved. +// http://ceres-solver.org/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Author: vitus@google.com (Michael Vitus) + +#ifndef EXAMPLES_CERES_TYPES_H_ +#define EXAMPLES_CERES_TYPES_H_ + +#include +#include +#include +#include + +#include "Eigen/Core" +#include "Eigen/Geometry" + +namespace ceres { +namespace examples { + +struct Pose3d { + Eigen::Vector3d p; + Eigen::Quaterniond q; + + // The name of the data type in the g2o file format. + static std::string name() { + return "VERTEX_SE3:QUAT"; + } + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; + +std::istream& operator>>(std::istream& input, Pose3d& pose) { + input >> pose.p.x() >> pose.p.y() >> pose.p.z() >> pose.q.x() >> + pose.q.y() >> pose.q.z() >> pose.q.w(); + // Normalize the quaternion to account for precision loss due to + // serialization. + pose.q.normalize(); + return input; +} + +typedef std::map, + Eigen::aligned_allocator > > + MapOfPoses; + +// The constraint between two vertices in the pose graph. The constraint is the +// transformation from vertex id_begin to vertex id_end. +struct Constraint3d { + int id_begin; + int id_end; + + // The transformation that represents the pose of the end frame E w.r.t. the + // begin frame B. In other words, it transforms a vector in the E frame to + // the B frame. + Pose3d t_be; + + // The inverse of the covariance matrix for the measurement. The order of the + // entries are x, y, z, delta orientation. + Eigen::Matrix information; + + // The name of the data type in the g2o file format. + static std::string name() { + return "EDGE_SE3:QUAT"; + } + + EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; + +std::istream& operator>>(std::istream& input, Constraint3d& constraint) { + Pose3d& t_be = constraint.t_be; + input >> constraint.id_begin >> constraint.id_end >> t_be; + + for (int i = 0; i < 6 && input.good(); ++i) { + for (int j = i; j < 6 && input.good(); ++j) { + input >> constraint.information(i, j); + if (i != j) { + constraint.information(j, i) = constraint.information(i, j); + } + } + } + return input; +} + +typedef std::vector > + VectorOfConstraints; + +} // namespace examples +} // namespace ceres + +#endif // EXAMPLES_CERES_TYPES_H_