Blame docs/source/nnls_tutorial.rst

Packit ea1746
.. highlight:: c++
Packit ea1746
Packit ea1746
.. default-domain:: cpp
Packit ea1746
Packit ea1746
.. _chapter-nnls_tutorial:
Packit ea1746
Packit ea1746
========================
Packit ea1746
Non-linear Least Squares
Packit ea1746
========================
Packit ea1746
Packit ea1746
Introduction
Packit ea1746
============
Packit ea1746
Packit ea1746
Ceres can solve bounds constrained robustified non-linear least
Packit ea1746
squares problems of the form
Packit ea1746
Packit ea1746
.. math:: :label: ceresproblem
Packit ea1746
Packit ea1746
   \min_{\mathbf{x}} &\quad \frac{1}{2}\sum_{i} \rho_i\left(\left\|f_i\left(x_{i_1}, ... ,x_{i_k}\right)\right\|^2\right) \\
Packit ea1746
   \text{s.t.} &\quad l_j \le x_j \le u_j
Packit ea1746
Packit ea1746
Problems of this form comes up in a broad range of areas across
Packit ea1746
science and engineering - from `fitting curves`_ in statistics, to
Packit ea1746
constructing `3D models from photographs`_ in computer vision.
Packit ea1746
Packit ea1746
.. _fitting curves: http://en.wikipedia.org/wiki/Nonlinear_regression
Packit ea1746
.. _3D models from photographs: http://en.wikipedia.org/wiki/Bundle_adjustment
Packit ea1746
Packit ea1746
In this chapter we will learn how to solve :eq:`ceresproblem` using
Packit ea1746
Ceres Solver. Full working code for all the examples described in this
Packit ea1746
chapter and more can be found in the `examples
Packit ea1746
<https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/>`_
Packit ea1746
directory.
Packit ea1746
Packit ea1746
The expression
Packit ea1746
:math:`\rho_i\left(\left\|f_i\left(x_{i_1},...,x_{i_k}\right)\right\|^2\right)`
Packit ea1746
is known as a ``ResidualBlock``, where :math:`f_i(\cdot)` is a
Packit ea1746
:class:`CostFunction` that depends on the parameter blocks
Packit ea1746
:math:`\left[x_{i_1},... , x_{i_k}\right]`. In most optimization
Packit ea1746
problems small groups of scalars occur together. For example the three
Packit ea1746
components of a translation vector and the four components of the
Packit ea1746
quaternion that define the pose of a camera. We refer to such a group
Packit ea1746
of small scalars as a ``ParameterBlock``. Of course a
Packit ea1746
``ParameterBlock`` can just be a single parameter. :math:`l_j` and
Packit ea1746
:math:`u_j` are bounds on the parameter block :math:`x_j`.
Packit ea1746
Packit ea1746
:math:`\rho_i` is a :class:`LossFunction`. A :class:`LossFunction` is
Packit ea1746
a scalar function that is used to reduce the influence of outliers on
Packit ea1746
the solution of non-linear least squares problems.
Packit ea1746
Packit ea1746
As a special case, when :math:`\rho_i(x) = x`, i.e., the identity
Packit ea1746
function, and :math:`l_j = -\infty` and :math:`u_j = \infty` we get
Packit ea1746
the more familiar `non-linear least squares problem
Packit ea1746
<http://en.wikipedia.org/wiki/Non-linear_least_squares>`_.
Packit ea1746
Packit ea1746
.. math:: \frac{1}{2}\sum_{i} \left\|f_i\left(x_{i_1}, ... ,x_{i_k}\right)\right\|^2.
Packit ea1746
   :label: ceresproblem2
Packit ea1746
Packit ea1746
.. _section-hello-world:
Packit ea1746
Packit ea1746
Hello World!
Packit ea1746
============
Packit ea1746
Packit ea1746
To get started, consider the problem of finding the minimum of the
Packit ea1746
function
Packit ea1746
Packit ea1746
.. math:: \frac{1}{2}(10 -x)^2.
Packit ea1746
Packit ea1746
This is a trivial problem, whose minimum is located at :math:`x = 10`,
Packit ea1746
but it is a good place to start to illustrate the basics of solving a
Packit ea1746
problem with Ceres [#f1]_.
Packit ea1746
Packit ea1746
The first step is to write a functor that will evaluate this the
Packit ea1746
function :math:`f(x) = 10 - x`:
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
   struct CostFunctor {
Packit ea1746
      template <typename T>
Packit ea1746
      bool operator()(const T* const x, T* residual) const {
Packit ea1746
        residual[0] = T(10.0) - x[0];
Packit ea1746
        return true;
Packit ea1746
      }
Packit ea1746
   };
Packit ea1746
Packit ea1746
The important thing to note here is that ``operator()`` is a templated
Packit ea1746
method, which assumes that all its inputs and outputs are of some type
Packit ea1746
``T``. The use of templating here allows Ceres to call
Packit ea1746
``CostFunctor::operator<T>()``, with ``T=double`` when just the value
Packit ea1746
of the residual is needed, and with a special type ``T=Jet`` when the
Packit ea1746
Jacobians are needed. In :ref:`section-derivatives` we will discuss the
Packit ea1746
various ways of supplying derivatives to Ceres in more detail.
Packit ea1746
Packit ea1746
Once we have a way of computing the residual function, it is now time
Packit ea1746
to construct a non-linear least squares problem using it and have
Packit ea1746
Ceres solve it.
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
   int main(int argc, char** argv) {
Packit ea1746
     google::InitGoogleLogging(argv[0]);
Packit ea1746
Packit ea1746
     // The variable to solve for with its initial value.
Packit ea1746
     double initial_x = 5.0;
Packit ea1746
     double x = initial_x;
Packit ea1746
Packit ea1746
     // Build the problem.
Packit ea1746
     Problem problem;
Packit ea1746
Packit ea1746
     // Set up the only cost function (also known as residual). This uses
Packit ea1746
     // auto-differentiation to obtain the derivative (jacobian).
Packit ea1746
     CostFunction* cost_function =
Packit ea1746
         new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
Packit ea1746
     problem.AddResidualBlock(cost_function, NULL, &x);
Packit ea1746
Packit ea1746
     // Run the solver!
Packit ea1746
     Solver::Options options;
Packit ea1746
     options.linear_solver_type = ceres::DENSE_QR;
Packit ea1746
     options.minimizer_progress_to_stdout = true;
Packit ea1746
     Solver::Summary summary;
Packit ea1746
     Solve(options, &problem, &summary);
Packit ea1746
Packit ea1746
     std::cout << summary.BriefReport() << "\n";
Packit ea1746
     std::cout << "x : " << initial_x
Packit ea1746
               << " -> " << x << "\n";
Packit ea1746
     return 0;
Packit ea1746
   }
Packit ea1746
Packit ea1746
:class:`AutoDiffCostFunction` takes a ``CostFunctor`` as input,
Packit ea1746
automatically differentiates it and gives it a :class:`CostFunction`
Packit ea1746
interface.
Packit ea1746
Packit ea1746
Compiling and running `examples/helloworld.cc
Packit ea1746
<https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/helloworld.cc>`_
Packit ea1746
gives us
Packit ea1746
Packit ea1746
.. code-block:: bash
Packit ea1746
Packit ea1746
   iter      cost      cost_change  |gradient|   |step|    tr_ratio  tr_radius  ls_iter  iter_time  total_time
Packit ea1746
      0  4.512500e+01    0.00e+00    9.50e+00   0.00e+00   0.00e+00  1.00e+04       0    5.33e-04    3.46e-03
Packit ea1746
      1  4.511598e-07    4.51e+01    9.50e-04   9.50e+00   1.00e+00  3.00e+04       1    5.00e-04    4.05e-03
Packit ea1746
      2  5.012552e-16    4.51e-07    3.17e-08   9.50e-04   1.00e+00  9.00e+04       1    1.60e-05    4.09e-03
Packit ea1746
   Ceres Solver Report: Iterations: 2, Initial cost: 4.512500e+01, Final cost: 5.012552e-16, Termination: CONVERGENCE
Packit ea1746
   x : 0.5 -> 10
Packit ea1746
Packit ea1746
Starting from a :math:`x=5`, the solver in two iterations goes to 10
Packit ea1746
[#f2]_. The careful reader will note that this is a linear problem and
Packit ea1746
one linear solve should be enough to get the optimal value.  The
Packit ea1746
default configuration of the solver is aimed at non-linear problems,
Packit ea1746
and for reasons of simplicity we did not change it in this example. It
Packit ea1746
is indeed possible to obtain the solution to this problem using Ceres
Packit ea1746
in one iteration. Also note that the solver did get very close to the
Packit ea1746
optimal function value of 0 in the very first iteration. We will
Packit ea1746
discuss these issues in greater detail when we talk about convergence
Packit ea1746
and parameter settings for Ceres.
Packit ea1746
Packit ea1746
.. rubric:: Footnotes
Packit ea1746
Packit ea1746
.. [#f1] `examples/helloworld.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/helloworld.cc>`_
Packit ea1746
.. [#f2] Actually the solver ran for three iterations, and it was
Packit ea1746
   by looking at the value returned by the linear solver in the third
Packit ea1746
   iteration, it observed that the update to the parameter block was too
Packit ea1746
   small and declared convergence. Ceres only prints out the display at
Packit ea1746
   the end of an iteration, and terminates as soon as it detects
Packit ea1746
   convergence, which is why you only see two iterations here and not
Packit ea1746
   three.
Packit ea1746
Packit ea1746
.. _section-derivatives:
Packit ea1746
Packit ea1746
Packit ea1746
Derivatives
Packit ea1746
===========
Packit ea1746
Packit ea1746
Ceres Solver like most optimization packages, depends on being able to
Packit ea1746
evaluate the value and the derivatives of each term in the objective
Packit ea1746
function at arbitrary parameter values. Doing so correctly and
Packit ea1746
efficiently is essential to getting good results.  Ceres Solver
Packit ea1746
provides a number of ways of doing so. You have already seen one of
Packit ea1746
them in action --
Packit ea1746
Automatic Differentiation in `examples/helloworld.cc
Packit ea1746
<https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/helloworld.cc>`_
Packit ea1746
Packit ea1746
We now consider the other two possibilities. Analytic and numeric
Packit ea1746
derivatives.
Packit ea1746
Packit ea1746
Packit ea1746
Numeric Derivatives
Packit ea1746
-------------------
Packit ea1746
Packit ea1746
In some cases, its not possible to define a templated cost functor,
Packit ea1746
for example when the evaluation of the residual involves a call to a
Packit ea1746
library function that you do not have control over.  In such a
Packit ea1746
situation, numerical differentiation can be used. The user defines a
Packit ea1746
functor which computes the residual value and construct a
Packit ea1746
:class:`NumericDiffCostFunction` using it. e.g., for :math:`f(x) = 10 - x`
Packit ea1746
the corresponding functor would be
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
  struct NumericDiffCostFunctor {
Packit ea1746
    bool operator()(const double* const x, double* residual) const {
Packit ea1746
      residual[0] = 10.0 - x[0];
Packit ea1746
      return true;
Packit ea1746
    }
Packit ea1746
  };
Packit ea1746
Packit ea1746
Which is added to the :class:`Problem` as:
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
  CostFunction* cost_function =
Packit ea1746
    new NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>(
Packit ea1746
        new NumericDiffCostFunctor);
Packit ea1746
  problem.AddResidualBlock(cost_function, NULL, &x);
Packit ea1746
Packit ea1746
Notice the parallel from when we were using automatic differentiation
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
  CostFunction* cost_function =
Packit ea1746
      new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
Packit ea1746
  problem.AddResidualBlock(cost_function, NULL, &x);
Packit ea1746
Packit ea1746
The construction looks almost identical to the one used for automatic
Packit ea1746
differentiation, except for an extra template parameter that indicates
Packit ea1746
the kind of finite differencing scheme to be used for computing the
Packit ea1746
numerical derivatives [#f3]_. For more details see the documentation
Packit ea1746
for :class:`NumericDiffCostFunction`.
Packit ea1746
Packit ea1746
**Generally speaking we recommend automatic differentiation instead of
Packit ea1746
numeric differentiation. The use of C++ templates makes automatic
Packit ea1746
differentiation efficient, whereas numeric differentiation is
Packit ea1746
expensive, prone to numeric errors, and leads to slower convergence.**
Packit ea1746
Packit ea1746
Packit ea1746
Analytic Derivatives
Packit ea1746
--------------------
Packit ea1746
Packit ea1746
In some cases, using automatic differentiation is not possible. For
Packit ea1746
example, it may be the case that it is more efficient to compute the
Packit ea1746
derivatives in closed form instead of relying on the chain rule used
Packit ea1746
by the automatic differentiation code.
Packit ea1746
Packit ea1746
In such cases, it is possible to supply your own residual and jacobian
Packit ea1746
computation code. To do this, define a subclass of
Packit ea1746
:class:`CostFunction` or :class:`SizedCostFunction` if you know the
Packit ea1746
sizes of the parameters and residuals at compile time. Here for
Packit ea1746
example is ``SimpleCostFunction`` that implements :math:`f(x) = 10 -
Packit ea1746
x`.
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
  class QuadraticCostFunction : public ceres::SizedCostFunction<1, 1> {
Packit ea1746
   public:
Packit ea1746
    virtual ~QuadraticCostFunction() {}
Packit ea1746
    virtual bool Evaluate(double const* const* parameters,
Packit ea1746
                          double* residuals,
Packit ea1746
                          double** jacobians) const {
Packit ea1746
      const double x = parameters[0][0];
Packit ea1746
      residuals[0] = 10 - x;
Packit ea1746
Packit ea1746
      // Compute the Jacobian if asked for.
Packit ea1746
      if (jacobians != NULL && jacobians[0] != NULL) {
Packit ea1746
        jacobians[0][0] = -1;
Packit ea1746
      }
Packit ea1746
      return true;
Packit ea1746
    }
Packit ea1746
  };
Packit ea1746
Packit ea1746
Packit ea1746
``SimpleCostFunction::Evaluate`` is provided with an input array of
Packit ea1746
``parameters``, an output array ``residuals`` for residuals and an
Packit ea1746
output array ``jacobians`` for Jacobians. The ``jacobians`` array is
Packit ea1746
optional, ``Evaluate`` is expected to check when it is non-null, and
Packit ea1746
if it is the case then fill it with the values of the derivative of
Packit ea1746
the residual function. In this case since the residual function is
Packit ea1746
linear, the Jacobian is constant [#f4]_ .
Packit ea1746
Packit ea1746
As can be seen from the above code fragments, implementing
Packit ea1746
:class:`CostFunction` objects is a bit tedious. We recommend that
Packit ea1746
unless you have a good reason to manage the jacobian computation
Packit ea1746
yourself, you use :class:`AutoDiffCostFunction` or
Packit ea1746
:class:`NumericDiffCostFunction` to construct your residual blocks.
Packit ea1746
Packit ea1746
More About Derivatives
Packit ea1746
----------------------
Packit ea1746
Packit ea1746
Computing derivatives is by far the most complicated part of using
Packit ea1746
Ceres, and depending on the circumstance the user may need more
Packit ea1746
sophisticated ways of computing derivatives. This section just
Packit ea1746
scratches the surface of how derivatives can be supplied to
Packit ea1746
Ceres. Once you are comfortable with using
Packit ea1746
:class:`NumericDiffCostFunction` and :class:`AutoDiffCostFunction` we
Packit ea1746
recommend taking a look at :class:`DynamicAutoDiffCostFunction`,
Packit ea1746
:class:`CostFunctionToFunctor`, :class:`NumericDiffFunctor` and
Packit ea1746
:class:`ConditionedCostFunction` for more advanced ways of
Packit ea1746
constructing and computing cost functions.
Packit ea1746
Packit ea1746
.. rubric:: Footnotes
Packit ea1746
Packit ea1746
.. [#f3] `examples/helloworld_numeric_diff.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/helloworld_numeric_diff.cc>`_.
Packit ea1746
.. [#f4] `examples/helloworld_analytic_diff.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/helloworld_analytic_diff.cc>`_.
Packit ea1746
Packit ea1746
Packit ea1746
.. _section-powell:
Packit ea1746
Packit ea1746
Powell's Function
Packit ea1746
=================
Packit ea1746
Packit ea1746
Consider now a slightly more complicated example -- the minimization
Packit ea1746
of Powell's function. Let :math:`x = \left[x_1, x_2, x_3, x_4 \right]`
Packit ea1746
and
Packit ea1746
Packit ea1746
.. math::
Packit ea1746
Packit ea1746
  \begin{align}
Packit ea1746
     f_1(x) &= x_1 + 10x_2 \\
Packit ea1746
     f_2(x) &= \sqrt{5}  (x_3 - x_4)\\
Packit ea1746
     f_3(x) &= (x_2 - 2x_3)^2\\
Packit ea1746
     f_4(x) &= \sqrt{10}  (x_1 - x_4)^2\\
Packit ea1746
       F(x) &= \left[f_1(x),\ f_2(x),\ f_3(x),\ f_4(x) \right]
Packit ea1746
  \end{align}
Packit ea1746
Packit ea1746
Packit ea1746
:math:`F(x)` is a function of four parameters, has four residuals
Packit ea1746
and we wish to find :math:`x` such that :math:`\frac{1}{2}\|F(x)\|^2`
Packit ea1746
is minimized.
Packit ea1746
Packit ea1746
Again, the first step is to define functors that evaluate of the terms
Packit ea1746
in the objective functor. Here is the code for evaluating
Packit ea1746
:math:`f_4(x_1, x_4)`:
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
 struct F4 {
Packit ea1746
   template <typename T>
Packit ea1746
   bool operator()(const T* const x1, const T* const x4, T* residual) const {
Packit ea1746
     residual[0] = T(sqrt(10.0)) * (x1[0] - x4[0]) * (x1[0] - x4[0]);
Packit ea1746
     return true;
Packit ea1746
   }
Packit ea1746
 };
Packit ea1746
Packit ea1746
Packit ea1746
Similarly, we can define classes ``F1``, ``F2`` and ``F3`` to evaluate
Packit ea1746
:math:`f_1(x_1, x_2)`, :math:`f_2(x_3, x_4)` and :math:`f_3(x_2, x_3)`
Packit ea1746
respectively. Using these, the problem can be constructed as follows:
Packit ea1746
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
  double x1 =  3.0; double x2 = -1.0; double x3 =  0.0; double x4 = 1.0;
Packit ea1746
Packit ea1746
  Problem problem;
Packit ea1746
Packit ea1746
  // Add residual terms to the problem using the using the autodiff
Packit ea1746
  // wrapper to get the derivatives automatically.
Packit ea1746
  problem.AddResidualBlock(
Packit ea1746
    new AutoDiffCostFunction<F1, 1, 1, 1>(new F1), NULL, &x1, &x2;;
Packit ea1746
  problem.AddResidualBlock(
Packit ea1746
    new AutoDiffCostFunction<F2, 1, 1, 1>(new F2), NULL, &x3, &x4;;
Packit ea1746
  problem.AddResidualBlock(
Packit ea1746
    new AutoDiffCostFunction<F3, 1, 1, 1>(new F3), NULL, &x2, &x3)
Packit ea1746
  problem.AddResidualBlock(
Packit ea1746
    new AutoDiffCostFunction<F4, 1, 1, 1>(new F4), NULL, &x1, &x4;;
Packit ea1746
Packit ea1746
Packit ea1746
Note that each ``ResidualBlock`` only depends on the two parameters
Packit ea1746
that the corresponding residual object depends on and not on all four
Packit ea1746
parameters. Compiling and running `examples/powell.cc
Packit ea1746
<https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/powell.cc>`_
Packit ea1746
gives us:
Packit ea1746
Packit ea1746
.. code-block:: bash
Packit ea1746
Packit ea1746
    Initial x1 = 3, x2 = -1, x3 = 0, x4 = 1
Packit ea1746
    iter      cost      cost_change  |gradient|   |step|    tr_ratio  tr_radius  ls_iter  iter_time  total_time
Packit ea1746
       0  1.075000e+02    0.00e+00    1.55e+02   0.00e+00   0.00e+00  1.00e+04       0    4.95e-04    2.30e-03
Packit ea1746
       1  5.036190e+00    1.02e+02    2.00e+01   2.16e+00   9.53e-01  3.00e+04       1    4.39e-05    2.40e-03
Packit ea1746
       2  3.148168e-01    4.72e+00    2.50e+00   6.23e-01   9.37e-01  9.00e+04       1    9.06e-06    2.43e-03
Packit ea1746
       3  1.967760e-02    2.95e-01    3.13e-01   3.08e-01   9.37e-01  2.70e+05       1    8.11e-06    2.45e-03
Packit ea1746
       4  1.229900e-03    1.84e-02    3.91e-02   1.54e-01   9.37e-01  8.10e+05       1    6.91e-06    2.48e-03
Packit ea1746
       5  7.687123e-05    1.15e-03    4.89e-03   7.69e-02   9.37e-01  2.43e+06       1    7.87e-06    2.50e-03
Packit ea1746
       6  4.804625e-06    7.21e-05    6.11e-04   3.85e-02   9.37e-01  7.29e+06       1    5.96e-06    2.52e-03
Packit ea1746
       7  3.003028e-07    4.50e-06    7.64e-05   1.92e-02   9.37e-01  2.19e+07       1    5.96e-06    2.55e-03
Packit ea1746
       8  1.877006e-08    2.82e-07    9.54e-06   9.62e-03   9.37e-01  6.56e+07       1    5.96e-06    2.57e-03
Packit ea1746
       9  1.173223e-09    1.76e-08    1.19e-06   4.81e-03   9.37e-01  1.97e+08       1    7.87e-06    2.60e-03
Packit ea1746
      10  7.333425e-11    1.10e-09    1.49e-07   2.40e-03   9.37e-01  5.90e+08       1    6.20e-06    2.63e-03
Packit ea1746
      11  4.584044e-12    6.88e-11    1.86e-08   1.20e-03   9.37e-01  1.77e+09       1    6.91e-06    2.65e-03
Packit ea1746
      12  2.865573e-13    4.30e-12    2.33e-09   6.02e-04   9.37e-01  5.31e+09       1    5.96e-06    2.67e-03
Packit ea1746
      13  1.791438e-14    2.69e-13    2.91e-10   3.01e-04   9.37e-01  1.59e+10       1    7.15e-06    2.69e-03
Packit ea1746
Packit ea1746
    Ceres Solver v1.12.0 Solve Report
Packit ea1746
    ----------------------------------
Packit ea1746
                                         Original                  Reduced
Packit ea1746
    Parameter blocks                            4                        4
Packit ea1746
    Parameters                                  4                        4
Packit ea1746
    Residual blocks                             4                        4
Packit ea1746
    Residual                                    4                        4
Packit ea1746
Packit ea1746
    Minimizer                        TRUST_REGION
Packit ea1746
Packit ea1746
    Dense linear algebra library            EIGEN
Packit ea1746
    Trust region strategy     LEVENBERG_MARQUARDT
Packit ea1746
Packit ea1746
                                            Given                     Used
Packit ea1746
    Linear solver                        DENSE_QR                 DENSE_QR
Packit ea1746
    Threads                                     1                        1
Packit ea1746
    Linear solver threads                       1                        1
Packit ea1746
Packit ea1746
    Cost:
Packit ea1746
    Initial                          1.075000e+02
Packit ea1746
    Final                            1.791438e-14
Packit ea1746
    Change                           1.075000e+02
Packit ea1746
Packit ea1746
    Minimizer iterations                       14
Packit ea1746
    Successful steps                           14
Packit ea1746
    Unsuccessful steps                          0
Packit ea1746
Packit ea1746
    Time (in seconds):
Packit ea1746
    Preprocessor                            0.002
Packit ea1746
Packit ea1746
      Residual evaluation                   0.000
Packit ea1746
      Jacobian evaluation                   0.000
Packit ea1746
      Linear solver                         0.000
Packit ea1746
    Minimizer                               0.001
Packit ea1746
Packit ea1746
    Postprocessor                           0.000
Packit ea1746
    Total                                   0.005
Packit ea1746
Packit ea1746
    Termination:                      CONVERGENCE (Gradient tolerance reached. Gradient max norm: 3.642190e-11 <= 1.000000e-10)
Packit ea1746
Packit ea1746
    Final x1 = 0.000292189, x2 = -2.92189e-05, x3 = 4.79511e-05, x4 = 4.79511e-05
Packit ea1746
Packit ea1746
It is easy to see that the optimal solution to this problem is at
Packit ea1746
:math:`x_1=0, x_2=0, x_3=0, x_4=0` with an objective function value of
Packit ea1746
:math:`0`. In 10 iterations, Ceres finds a solution with an objective
Packit ea1746
function value of :math:`4\times 10^{-12}`.
Packit ea1746
Packit ea1746
.. rubric:: Footnotes
Packit ea1746
Packit ea1746
.. [#f5] `examples/powell.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/powell.cc>`_.
Packit ea1746
Packit ea1746
Packit ea1746
.. _section-fitting:
Packit ea1746
Packit ea1746
Curve Fitting
Packit ea1746
=============
Packit ea1746
Packit ea1746
The examples we have seen until now are simple optimization problems
Packit ea1746
with no data. The original purpose of least squares and non-linear
Packit ea1746
least squares analysis was fitting curves to data. It is only
Packit ea1746
appropriate that we now consider an example of such a problem
Packit ea1746
[#f6]_. It contains data generated by sampling the curve :math:`y =
Packit ea1746
e^{0.3x + 0.1}` and adding Gaussian noise with standard deviation
Packit ea1746
:math:`\sigma = 0.2`. Let us fit some data to the curve
Packit ea1746
Packit ea1746
.. math::  y = e^{mx + c}.
Packit ea1746
Packit ea1746
We begin by defining a templated object to evaluate the
Packit ea1746
residual. There will be a residual for each observation.
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
 struct ExponentialResidual {
Packit ea1746
   ExponentialResidual(double x, double y)
Packit ea1746
       : x_(x), y_(y) {}
Packit ea1746
Packit ea1746
   template <typename T>
Packit ea1746
   bool operator()(const T* const m, const T* const c, T* residual) const {
Packit ea1746
     residual[0] = T(y_) - exp(m[0] * T(x_) + c[0]);
Packit ea1746
     return true;
Packit ea1746
   }
Packit ea1746
Packit ea1746
  private:
Packit ea1746
   // Observations for a sample.
Packit ea1746
   const double x_;
Packit ea1746
   const double y_;
Packit ea1746
 };
Packit ea1746
Packit ea1746
Assuming the observations are in a :math:`2n` sized array called
Packit ea1746
``data`` the problem construction is a simple matter of creating a
Packit ea1746
:class:`CostFunction` for every observation.
Packit ea1746
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
 double m = 0.0;
Packit ea1746
 double c = 0.0;
Packit ea1746
Packit ea1746
 Problem problem;
Packit ea1746
 for (int i = 0; i < kNumObservations; ++i) {
Packit ea1746
   CostFunction* cost_function =
Packit ea1746
        new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1>(
Packit ea1746
            new ExponentialResidual(data[2 * i], data[2 * i + 1]));
Packit ea1746
   problem.AddResidualBlock(cost_function, NULL, &m, &c);
Packit ea1746
 }
Packit ea1746
Packit ea1746
Compiling and running `examples/curve_fitting.cc
Packit ea1746
<https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/curve_fitting.cc>`_
Packit ea1746
gives us:
Packit ea1746
Packit ea1746
.. code-block:: bash
Packit ea1746
Packit ea1746
    iter      cost      cost_change  |gradient|   |step|    tr_ratio  tr_radius  ls_iter  iter_time  total_time
Packit ea1746
       0  1.211734e+02    0.00e+00    3.61e+02   0.00e+00   0.00e+00  1.00e+04       0    5.34e-04    2.56e-03
Packit ea1746
       1  1.211734e+02   -2.21e+03    0.00e+00   7.52e-01  -1.87e+01  5.00e+03       1    4.29e-05    3.25e-03
Packit ea1746
       2  1.211734e+02   -2.21e+03    0.00e+00   7.51e-01  -1.86e+01  1.25e+03       1    1.10e-05    3.28e-03
Packit ea1746
       3  1.211734e+02   -2.19e+03    0.00e+00   7.48e-01  -1.85e+01  1.56e+02       1    1.41e-05    3.31e-03
Packit ea1746
       4  1.211734e+02   -2.02e+03    0.00e+00   7.22e-01  -1.70e+01  9.77e+00       1    1.00e-05    3.34e-03
Packit ea1746
       5  1.211734e+02   -7.34e+02    0.00e+00   5.78e-01  -6.32e+00  3.05e-01       1    1.00e-05    3.36e-03
Packit ea1746
       6  3.306595e+01    8.81e+01    4.10e+02   3.18e-01   1.37e+00  9.16e-01       1    2.79e-05    3.41e-03
Packit ea1746
       7  6.426770e+00    2.66e+01    1.81e+02   1.29e-01   1.10e+00  2.75e+00       1    2.10e-05    3.45e-03
Packit ea1746
       8  3.344546e+00    3.08e+00    5.51e+01   3.05e-02   1.03e+00  8.24e+00       1    2.10e-05    3.48e-03
Packit ea1746
       9  1.987485e+00    1.36e+00    2.33e+01   8.87e-02   9.94e-01  2.47e+01       1    2.10e-05    3.52e-03
Packit ea1746
      10  1.211585e+00    7.76e-01    8.22e+00   1.05e-01   9.89e-01  7.42e+01       1    2.10e-05    3.56e-03
Packit ea1746
      11  1.063265e+00    1.48e-01    1.44e+00   6.06e-02   9.97e-01  2.22e+02       1    2.60e-05    3.61e-03
Packit ea1746
      12  1.056795e+00    6.47e-03    1.18e-01   1.47e-02   1.00e+00  6.67e+02       1    2.10e-05    3.64e-03
Packit ea1746
      13  1.056751e+00    4.39e-05    3.79e-03   1.28e-03   1.00e+00  2.00e+03       1    2.10e-05    3.68e-03
Packit ea1746
    Ceres Solver Report: Iterations: 13, Initial cost: 1.211734e+02, Final cost: 1.056751e+00, Termination: CONVERGENCE
Packit ea1746
    Initial m: 0 c: 0
Packit ea1746
    Final   m: 0.291861 c: 0.131439
Packit ea1746
Packit ea1746
Starting from parameter values :math:`m = 0, c=0` with an initial
Packit ea1746
objective function value of :math:`121.173` Ceres finds a solution
Packit ea1746
:math:`m= 0.291861, c = 0.131439` with an objective function value of
Packit ea1746
:math:`1.05675`. These values are a bit different than the
Packit ea1746
parameters of the original model :math:`m=0.3, c= 0.1`, but this is
Packit ea1746
expected. When reconstructing a curve from noisy data, we expect to
Packit ea1746
see such deviations. Indeed, if you were to evaluate the objective
Packit ea1746
function for :math:`m=0.3, c=0.1`, the fit is worse with an objective
Packit ea1746
function value of :math:`1.082425`.  The figure below illustrates the fit.
Packit ea1746
Packit ea1746
.. figure:: least_squares_fit.png
Packit ea1746
   :figwidth: 500px
Packit ea1746
   :height: 400px
Packit ea1746
   :align: center
Packit ea1746
Packit ea1746
   Least squares curve fitting.
Packit ea1746
Packit ea1746
Packit ea1746
.. rubric:: Footnotes
Packit ea1746
Packit ea1746
.. [#f6] `examples/curve_fitting.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/curve_fitting.cc>`_
Packit ea1746
Packit ea1746
Packit ea1746
Robust Curve Fitting
Packit ea1746
=====================
Packit ea1746
Packit ea1746
Now suppose the data we are given has some outliers, i.e., we have
Packit ea1746
some points that do not obey the noise model. If we were to use the
Packit ea1746
code above to fit such data, we would get a fit that looks as
Packit ea1746
below. Notice how the fitted curve deviates from the ground truth.
Packit ea1746
Packit ea1746
.. figure:: non_robust_least_squares_fit.png
Packit ea1746
   :figwidth: 500px
Packit ea1746
   :height: 400px
Packit ea1746
   :align: center
Packit ea1746
Packit ea1746
To deal with outliers, a standard technique is to use a
Packit ea1746
:class:`LossFunction`. Loss functions reduce the influence of
Packit ea1746
residual blocks with high residuals, usually the ones corresponding to
Packit ea1746
outliers. To associate a loss function with a residual block, we change
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
   problem.AddResidualBlock(cost_function, NULL , &m, &c);
Packit ea1746
Packit ea1746
to
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
   problem.AddResidualBlock(cost_function, new CauchyLoss(0.5) , &m, &c);
Packit ea1746
Packit ea1746
:class:`CauchyLoss` is one of the loss functions that ships with Ceres
Packit ea1746
Solver. The argument :math:`0.5` specifies the scale of the loss
Packit ea1746
function. As a result, we get the fit below [#f7]_. Notice how the
Packit ea1746
fitted curve moves back closer to the ground truth curve.
Packit ea1746
Packit ea1746
.. figure:: robust_least_squares_fit.png
Packit ea1746
   :figwidth: 500px
Packit ea1746
   :height: 400px
Packit ea1746
   :align: center
Packit ea1746
Packit ea1746
   Using :class:`LossFunction` to reduce the effect of outliers on a
Packit ea1746
   least squares fit.
Packit ea1746
Packit ea1746
Packit ea1746
.. rubric:: Footnotes
Packit ea1746
Packit ea1746
.. [#f7] `examples/robust_curve_fitting.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/robust_curve_fitting.cc>`_
Packit ea1746
Packit ea1746
Packit ea1746
Bundle Adjustment
Packit ea1746
=================
Packit ea1746
Packit ea1746
One of the main reasons for writing Ceres was our need to solve large
Packit ea1746
scale bundle adjustment problems [HartleyZisserman]_, [Triggs]_.
Packit ea1746
Packit ea1746
Given a set of measured image feature locations and correspondences,
Packit ea1746
the goal of bundle adjustment is to find 3D point positions and camera
Packit ea1746
parameters that minimize the reprojection error. This optimization
Packit ea1746
problem is usually formulated as a non-linear least squares problem,
Packit ea1746
where the error is the squared :math:`L_2` norm of the difference between
Packit ea1746
the observed feature location and the projection of the corresponding
Packit ea1746
3D point on the image plane of the camera. Ceres has extensive support
Packit ea1746
for solving bundle adjustment problems.
Packit ea1746
Packit ea1746
Let us solve a problem from the `BAL
Packit ea1746
<http://grail.cs.washington.edu/projects/bal/>`_ dataset [#f8]_.
Packit ea1746
Packit ea1746
The first step as usual is to define a templated functor that computes
Packit ea1746
the reprojection error/residual. The structure of the functor is
Packit ea1746
similar to the ``ExponentialResidual``, in that there is an
Packit ea1746
instance of this object responsible for each image observation.
Packit ea1746
Packit ea1746
Each residual in a BAL problem depends on a three dimensional point
Packit ea1746
and a nine parameter camera. The nine parameters defining the camera
Packit ea1746
are: three for rotation as a Rodriques' axis-angle vector, three
Packit ea1746
for translation, one for focal length and two for radial distortion.
Packit ea1746
The details of this camera model can be found the `Bundler homepage
Packit ea1746
<http://phototour.cs.washington.edu/bundler/>`_ and the `BAL homepage
Packit ea1746
<http://grail.cs.washington.edu/projects/bal/>`_.
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
 struct SnavelyReprojectionError {
Packit ea1746
   SnavelyReprojectionError(double observed_x, double observed_y)
Packit ea1746
       : observed_x(observed_x), observed_y(observed_y) {}
Packit ea1746
Packit ea1746
   template <typename T>
Packit ea1746
   bool operator()(const T* const camera,
Packit ea1746
                   const T* const point,
Packit ea1746
                   T* residuals) const {
Packit ea1746
     // camera[0,1,2] are the angle-axis rotation.
Packit ea1746
     T p[3];
Packit ea1746
     ceres::AngleAxisRotatePoint(camera, point, p);
Packit ea1746
     // camera[3,4,5] are the translation.
Packit ea1746
     p[0] += camera[3]; p[1] += camera[4]; p[2] += camera[5];
Packit ea1746
Packit ea1746
     // Compute the center of distortion. The sign change comes from
Packit ea1746
     // the camera model that Noah Snavely's Bundler assumes, whereby
Packit ea1746
     // the camera coordinate system has a negative z axis.
Packit ea1746
     T xp = - p[0] / p[2];
Packit ea1746
     T yp = - p[1] / p[2];
Packit ea1746
Packit ea1746
     // Apply second and fourth order radial distortion.
Packit ea1746
     const T& l1 = camera[7];
Packit ea1746
     const T& l2 = camera[8];
Packit ea1746
     T r2 = xp*xp + yp*yp;
Packit ea1746
     T distortion = T(1.0) + r2  * (l1 + l2  * r2);
Packit ea1746
Packit ea1746
     // Compute final projected point position.
Packit ea1746
     const T& focal = camera[6];
Packit ea1746
     T predicted_x = focal * distortion * xp;
Packit ea1746
     T predicted_y = focal * distortion * yp;
Packit ea1746
Packit ea1746
     // The error is the difference between the predicted and observed position.
Packit ea1746
     residuals[0] = predicted_x - T(observed_x);
Packit ea1746
     residuals[1] = predicted_y - T(observed_y);
Packit ea1746
     return true;
Packit ea1746
   }
Packit ea1746
Packit ea1746
    // Factory to hide the construction of the CostFunction object from
Packit ea1746
    // the client code.
Packit ea1746
    static ceres::CostFunction* Create(const double observed_x,
Packit ea1746
                                       const double observed_y) {
Packit ea1746
      return (new ceres::AutoDiffCostFunction<SnavelyReprojectionError, 2, 9, 3>(
Packit ea1746
                  new SnavelyReprojectionError(observed_x, observed_y)));
Packit ea1746
    }
Packit ea1746
Packit ea1746
   double observed_x;
Packit ea1746
   double observed_y;
Packit ea1746
 };
Packit ea1746
Packit ea1746
Packit ea1746
Note that unlike the examples before, this is a non-trivial function
Packit ea1746
and computing its analytic Jacobian is a bit of a pain. Automatic
Packit ea1746
differentiation makes life much simpler. The function
Packit ea1746
:func:`AngleAxisRotatePoint` and other functions for manipulating
Packit ea1746
rotations can be found in ``include/ceres/rotation.h``.
Packit ea1746
Packit ea1746
Given this functor, the bundle adjustment problem can be constructed
Packit ea1746
as follows:
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
 ceres::Problem problem;
Packit ea1746
 for (int i = 0; i < bal_problem.num_observations(); ++i) {
Packit ea1746
   ceres::CostFunction* cost_function =
Packit ea1746
       SnavelyReprojectionError::Create(
Packit ea1746
            bal_problem.observations()[2 * i + 0],
Packit ea1746
            bal_problem.observations()[2 * i + 1]);
Packit ea1746
   problem.AddResidualBlock(cost_function,
Packit ea1746
                            NULL /* squared loss */,
Packit ea1746
                            bal_problem.mutable_camera_for_observation(i),
Packit ea1746
                            bal_problem.mutable_point_for_observation(i));
Packit ea1746
 }
Packit ea1746
Packit ea1746
Packit ea1746
Notice that the problem construction for bundle adjustment is very
Packit ea1746
similar to the curve fitting example -- one term is added to the
Packit ea1746
objective function per observation.
Packit ea1746
Packit ea1746
Since this is a large sparse problem (well large for ``DENSE_QR``
Packit ea1746
anyways), one way to solve this problem is to set
Packit ea1746
:member:`Solver::Options::linear_solver_type` to
Packit ea1746
``SPARSE_NORMAL_CHOLESKY`` and call :member:`Solve`. And while this is
Packit ea1746
a reasonable thing to do, bundle adjustment problems have a special
Packit ea1746
sparsity structure that can be exploited to solve them much more
Packit ea1746
efficiently. Ceres provides three specialized solvers (collectively
Packit ea1746
known as Schur-based solvers) for this task. The example code uses the
Packit ea1746
simplest of them ``DENSE_SCHUR``.
Packit ea1746
Packit ea1746
.. code-block:: c++
Packit ea1746
Packit ea1746
 ceres::Solver::Options options;
Packit ea1746
 options.linear_solver_type = ceres::DENSE_SCHUR;
Packit ea1746
 options.minimizer_progress_to_stdout = true;
Packit ea1746
 ceres::Solver::Summary summary;
Packit ea1746
 ceres::Solve(options, &problem, &summary);
Packit ea1746
 std::cout << summary.FullReport() << "\n";
Packit ea1746
Packit ea1746
For a more sophisticated bundle adjustment example which demonstrates
Packit ea1746
the use of Ceres' more advanced features including its various linear
Packit ea1746
solvers, robust loss functions and local parameterizations see
Packit ea1746
`examples/bundle_adjuster.cc
Packit ea1746
<https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/bundle_adjuster.cc>`_
Packit ea1746
Packit ea1746
Packit ea1746
.. rubric:: Footnotes
Packit ea1746
Packit ea1746
.. [#f8] `examples/simple_bundle_adjuster.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/simple_bundle_adjuster.cc>`_
Packit ea1746
Packit ea1746
Other Examples
Packit ea1746
==============
Packit ea1746
Packit ea1746
Besides the examples in this chapter, the  `example
Packit ea1746
<https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/>`_
Packit ea1746
directory contains a number of other examples:
Packit ea1746
Packit ea1746
#. `bundle_adjuster.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/bundle_adjuster.cc>`_
Packit ea1746
   shows how to use the various features of Ceres to solve bundle
Packit ea1746
   adjustment problems.
Packit ea1746
Packit ea1746
#. `circle_fit.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/circle_fit.cc>`_
Packit ea1746
   shows how to fit data to a circle.
Packit ea1746
Packit ea1746
#. `ellipse_approximation.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/ellipse_approximation.cc>`_
Packit ea1746
   fits points randomly distributed on an ellipse with an approximate
Packit ea1746
   line segment contour. This is done by jointly optimizing the
Packit ea1746
   control points of the line segment contour along with the preimage
Packit ea1746
   positions for the data points. The purpose of this example is to
Packit ea1746
   show an example use case for ``Solver::Options::dynamic_sparsity``,
Packit ea1746
   and how it can benefit problems which are numerically dense but
Packit ea1746
   dynamically sparse.
Packit ea1746
Packit ea1746
#. `denoising.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/denoising.cc>`_
Packit ea1746
   implements image denoising using the `Fields of Experts
Packit ea1746
   <http://www.gris.informatik.tu-darmstadt.de/~sroth/research/foe/index.html>`_
Packit ea1746
   model.
Packit ea1746
Packit ea1746
#. `nist.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/nist.cc>`_
Packit ea1746
   implements and attempts to solves the `NIST
Packit ea1746
   <http://www.itl.nist.gov/div898/strd/nls/nls_main.shtm>`_
Packit ea1746
   non-linear regression problems.
Packit ea1746
Packit ea1746
#. `more_garbow_hillstrom.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/more_garbow_hillstrom.cc>`_
Packit ea1746
   A subset of the test problems from the paper
Packit ea1746
Packit ea1746
   Testing Unconstrained Optimization Software
Packit ea1746
   Jorge J. More, Burton S. Garbow and Kenneth E. Hillstrom
Packit ea1746
   ACM Transactions on Mathematical Software, 7(1), pp. 17-41, 1981
Packit ea1746
Packit ea1746
   which were augmented with bounds and used for testing bounds
Packit ea1746
   constrained optimization algorithms by
Packit ea1746
Packit ea1746
   A Trust Region Approach to Linearly Constrained Optimization
Packit ea1746
   David M. Gay
Packit ea1746
   Numerical Analysis (Griffiths, D.F., ed.), pp. 72-105
Packit ea1746
   Lecture Notes in Mathematics 1066, Springer Verlag, 1984.
Packit ea1746
Packit ea1746
Packit ea1746
#. `libmv_bundle_adjuster.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/libmv_bundle_adjuster.cc>`_
Packit ea1746
   is the bundle adjustment algorithm used by `Blender <www.blender.org>`_/libmv.
Packit ea1746
Packit ea1746
#. `libmv_homography.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/libmv_homography.cc>`_
Packit ea1746
   This file demonstrates solving for a homography between two sets of
Packit ea1746
   points and using a custom exit criterion by having a callback check
Packit ea1746
   for image-space error.
Packit ea1746
Packit ea1746
#. `robot_pose_mle.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/robot_pose_mle.cc>`_
Packit ea1746
   This example demonstrates how to use the ``DynamicAutoDiffCostFunction``
Packit ea1746
   variant of CostFunction. The ``DynamicAutoDiffCostFunction`` is meant to
Packit ea1746
   be used in cases where the number of parameter blocks or the sizes are not
Packit ea1746
   known at compile time.
Packit ea1746
Packit ea1746
   This example simulates a robot traversing down a 1-dimension hallway with
Packit ea1746
   noise odometry readings and noisy range readings of the end of the hallway.
Packit ea1746
   By fusing the noisy odometry and sensor readings this example demonstrates
Packit ea1746
   how to compute the maximum likelihood estimate (MLE) of the robot's pose at
Packit ea1746
   each timestep.
Packit ea1746
Packit ea1746
#. `slam/pose_graph_2d/pose_graph_2d.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/slam/pose_graph_2d/pose_graph_2d.cc>`_
Packit ea1746
   The Simultaneous Localization and Mapping (SLAM) problem consists of building
Packit ea1746
   a map of an unknown environment while simultaneously localizing against this
Packit ea1746
   map. The main difficulty of this problem stems from not having any additional
Packit ea1746
   external aiding information such as GPS. SLAM has been considered one of the
Packit ea1746
   fundamental challenges of robotics. There are many resources on SLAM
Packit ea1746
   [#f9]_. A pose graph optimization problem is one example of a SLAM
Packit ea1746
   problem. The following explains how to formulate the pose graph based SLAM
Packit ea1746
   problem in 2-Dimensions with relative pose constraints.
Packit ea1746
Packit ea1746
   Consider a robot moving in a 2-Dimensional plane. The robot has access to a
Packit ea1746
   set of sensors such as wheel odometry or a laser range scanner. From these
Packit ea1746
   raw measurements, we want to estimate the trajectory of the robot as well as
Packit ea1746
   build a map of the environment. In order to reduce the computational
Packit ea1746
   complexity of the problem, the pose graph approach abstracts the raw
Packit ea1746
   measurements away.  Specifically, it creates a graph of nodes which represent
Packit ea1746
   the pose of the robot, and edges which represent the relative transformation
Packit ea1746
   (delta position and orientation) between the two nodes. The edges are virtual
Packit ea1746
   measurements derived from the raw sensor measurements, e.g. by integrating
Packit ea1746
   the raw wheel odometry or aligning the laser range scans acquired from the
Packit ea1746
   robot. A visualization of the resulting graph is shown below.
Packit ea1746
Packit ea1746
   .. figure:: slam2d.png
Packit ea1746
      :figwidth: 500px
Packit ea1746
      :height: 400px
Packit ea1746
      :align: center
Packit ea1746
Packit ea1746
      Visual representation of a graph SLAM problem.
Packit ea1746
Packit ea1746
   The figure depicts the pose of the robot as the triangles, the measurements
Packit ea1746
   are indicated by the connecting lines, and the loop closure measurements are
Packit ea1746
   shown as dotted lines. Loop closures are measurements between non-sequential
Packit ea1746
   robot states and they reduce the accumulation of error over time. The
Packit ea1746
   following will describe the mathematical formulation of the pose graph
Packit ea1746
   problem.
Packit ea1746
Packit ea1746
   The robot at timestamp :math:`t` has state :math:`x_t = [p^T, \psi]^T` where
Packit ea1746
   :math:`p` is a 2D vector that represents the position in the plane and
Packit ea1746
   :math:`\psi` is the orientation in radians. The measurement of the relative
Packit ea1746
   transform between the robot state at two timestamps :math:`a` and :math:`b`
Packit ea1746
   is given as: :math:`z_{ab} = [\hat{p}_{ab}^T, \hat{\psi}_{ab}]`. The residual
Packit ea1746
   implemented in the Ceres cost function which computes the error between the
Packit ea1746
   measurement and the predicted measurement is:
Packit ea1746
Packit ea1746
   .. math:: r_{ab} =
Packit ea1746
	     \left[
Packit ea1746
	     \begin{array}{c}
Packit ea1746
	       R_a^T\left(p_b - p_a\right) - \hat{p}_{ab} \\
Packit ea1746
	       \mathrm{Normalize}\left(\psi_b - \psi_a - \hat{\psi}_{ab}\right)
Packit ea1746
	     \end{array}
Packit ea1746
	     \right]
Packit ea1746
Packit ea1746
   where the function :math:`\mathrm{Normalize}()` normalizes the angle in the range
Packit ea1746
   :math:`[-\pi,\pi)`, and :math:`R` is the rotation matrix given by
Packit ea1746
Packit ea1746
   .. math:: R_a =
Packit ea1746
	     \left[
Packit ea1746
	     \begin{array}{cc}
Packit ea1746
	       \cos \psi_a & -\sin \psi_a \\
Packit ea1746
	       \sin \psi_a & \cos \psi_a \\
Packit ea1746
	     \end{array}
Packit ea1746
	     \right]
Packit ea1746
Packit ea1746
   To finish the cost function, we need to weight the residual by the
Packit ea1746
   uncertainty of the measurement. Hence, we pre-multiply the residual by the
Packit ea1746
   inverse square root of the covariance matrix for the measurement,
Packit ea1746
   i.e. :math:`\Sigma_{ab}^{-\frac{1}{2}} r_{ab}` where :math:`\Sigma_{ab}` is
Packit ea1746
   the covariance.
Packit ea1746
Packit ea1746
   Lastly, we use a local parameterization to normalize the orientation in the
Packit ea1746
   range which is normalized between :math:`[-\pi,\pi)`.  Specially, we define
Packit ea1746
   the :member:`AngleLocalParameterization::operator()` function to be:
Packit ea1746
   :math:`\mathrm{Normalize}(\psi + \delta \psi)`.
Packit ea1746
Packit ea1746
   This package includes an executable :member:`pose_graph_2d` that will read a
Packit ea1746
   problem definition file. This executable can work with any 2D problem
Packit ea1746
   definition that uses the g2o format. It would be relatively straightforward
Packit ea1746
   to implement a new reader for a different format such as TORO or
Packit ea1746
   others. :member:`pose_graph_2d` will print the Ceres solver full summary and
Packit ea1746
   then output to disk the original and optimized poses (``poses_original.txt``
Packit ea1746
   and ``poses_optimized.txt``, respectively) of the robot in the following
Packit ea1746
   format:
Packit ea1746
Packit ea1746
   .. code-block:: bash
Packit ea1746
Packit ea1746
      pose_id x y yaw_radians
Packit ea1746
      pose_id x y yaw_radians
Packit ea1746
      pose_id x y yaw_radians
Packit ea1746
Packit ea1746
   where ``pose_id`` is the corresponding integer ID from the file
Packit ea1746
   definition. Note, the file will be sorted in ascending order for the
Packit ea1746
   ``pose_id``.
Packit ea1746
Packit ea1746
   The executable :member:`pose_graph_2d` expects the first argument to be
Packit ea1746
   the path to the problem definition. To run the executable,
Packit ea1746
Packit ea1746
   .. code-block:: bash
Packit ea1746
Packit ea1746
      /path/to/bin/pose_graph_2d /path/to/dataset/dataset.g2o
Packit ea1746
Packit ea1746
   A python script is provided to visualize the resulting output files.
Packit ea1746
Packit ea1746
   .. code-block:: bash
Packit ea1746
Packit ea1746
      /path/to/repo/examples/slam/pose_graph_2d/plot_results.py --optimized_poses ./poses_optimized.txt --initial_poses ./poses_original.txt
Packit ea1746
Packit ea1746
   As an example, a standard synthetic benchmark dataset [#f10]_ created by
Packit ea1746
   Edwin Olson which has 3500 nodes in a grid world with a total of 5598 edges
Packit ea1746
   was solved.  Visualizing the results with the provided script produces:
Packit ea1746
Packit ea1746
   .. figure:: manhattan_olson_3500_result.png
Packit ea1746
      :figwidth: 600px
Packit ea1746
      :height: 600px
Packit ea1746
      :align: center
Packit ea1746
Packit ea1746
   with the original poses in green and the optimized poses in blue. As shown,
Packit ea1746
   the optimized poses more closely match the underlying grid world. Note, the
Packit ea1746
   left side of the graph has a small yaw drift due to a lack of relative
Packit ea1746
   constraints to provide enough information to reconstruct the trajectory.
Packit ea1746
Packit ea1746
   .. rubric:: Footnotes
Packit ea1746
Packit ea1746
   .. [#f9] Giorgio Grisetti, Rainer Kummerle, Cyrill Stachniss, Wolfram
Packit ea1746
      Burgard. A Tutorial on Graph-Based SLAM. IEEE Intelligent Transportation
Packit ea1746
      Systems Magazine, 52(3):199–222, 2010.
Packit ea1746
Packit ea1746
   .. [#f10] E. Olson, J. Leonard, and S. Teller, “Fast iterative optimization of
Packit ea1746
      pose graphs with poor initial estimates,” in Robotics and Automation
Packit ea1746
      (ICRA), IEEE International Conference on, 2006, pp. 2262–2269.
Packit ea1746
Packit ea1746
#. `slam/pose_graph_3d/pose_graph_3d.cc
Packit ea1746
   <https://ceres-solver.googlesource.com/ceres-solver/+/master/examples/slam/pose_graph_3d/pose_graph_3d.cc>`_
Packit ea1746
   The following explains how to formulate the pose graph based SLAM problem in
Packit ea1746
   3-Dimensions with relative pose constraints. The example also illustrates how
Packit ea1746
   to use Eigen's geometry module with Ceres's automatic differentiation
Packit ea1746
   functionality.
Packit ea1746
Packit ea1746
   The robot at timestamp :math:`t` has state :math:`x_t = [p^T, q^T]^T` where
Packit ea1746
   :math:`p` is a 3D vector that represents the position and :math:`q` is the
Packit ea1746
   orientation represented as an Eigen quaternion. The measurement of the
Packit ea1746
   relative transform between the robot state at two timestamps :math:`a` and
Packit ea1746
   :math:`b` is given as: :math:`z_{ab} = [\hat{p}_{ab}^T, \hat{q}_{ab}^T]^T`.
Packit ea1746
   The residual implemented in the Ceres cost function which computes the error
Packit ea1746
   between the measurement and the predicted measurement is:
Packit ea1746
Packit ea1746
   .. math:: r_{ab} =
Packit ea1746
             \left[
Packit ea1746
             \begin{array}{c}
Packit ea1746
                R(q_a)^{T} (p_b - p_a) - \hat{p}_{ab} \\
Packit ea1746
                2.0 \mathrm{vec}\left((q_a^{-1} q_b) \hat{q}_{ab}^{-1}\right)
Packit ea1746
             \end{array}
Packit ea1746
             \right]
Packit ea1746
Packit ea1746
   where the function :math:`\mathrm{vec}()` returns the vector part of the
Packit ea1746
   quaternion, i.e. :math:`[q_x, q_y, q_z]`, and :math:`R(q)` is the rotation
Packit ea1746
   matrix for the quaternion.
Packit ea1746
Packit ea1746
   To finish the cost function, we need to weight the residual by the
Packit ea1746
   uncertainty of the measurement. Hence, we pre-multiply the residual by the
Packit ea1746
   inverse square root of the covariance matrix for the measurement,
Packit ea1746
   i.e. :math:`\Sigma_{ab}^{-\frac{1}{2}} r_{ab}` where :math:`\Sigma_{ab}` is
Packit ea1746
   the covariance.
Packit ea1746
Packit ea1746
   Given that we are using a quaternion to represent the orientation, we need to
Packit ea1746
   use a local parameterization (:class:`EigenQuaternionParameterization`) to
Packit ea1746
   only apply updates orthogonal to the 4-vector defining the
Packit ea1746
   quaternion. Eigen's quaternion uses a different internal memory layout for
Packit ea1746
   the elements of the quaternion than what is commonly used. Specifically,
Packit ea1746
   Eigen stores the elements in memory as :math:`[x, y, z, w]` where the real
Packit ea1746
   part is last whereas it is typically stored first. Note, when creating an
Packit ea1746
   Eigen quaternion through the constructor the elements are accepted in
Packit ea1746
   :math:`w`, :math:`x`, :math:`y`, :math:`z` order. Since Ceres operates on
Packit ea1746
   parameter blocks which are raw double pointers this difference is important
Packit ea1746
   and requires a different parameterization.
Packit ea1746
Packit ea1746
   This package includes an executable :member:`pose_graph_3d` that will read a
Packit ea1746
   problem definition file. This executable can work with any 3D problem
Packit ea1746
   definition that uses the g2o format with quaternions used for the orientation
Packit ea1746
   representation. It would be relatively straightforward to implement a new
Packit ea1746
   reader for a different format such as TORO or others. :member:`pose_graph_3d`
Packit ea1746
   will print the Ceres solver full summary and then output to disk the original
Packit ea1746
   and optimized poses (``poses_original.txt`` and ``poses_optimized.txt``,
Packit ea1746
   respectively) of the robot in the following format:
Packit ea1746
Packit ea1746
   .. code-block:: bash
Packit ea1746
Packit ea1746
      pose_id x y z q_x q_y q_z q_w
Packit ea1746
      pose_id x y z q_x q_y q_z q_w
Packit ea1746
      pose_id x y z q_x q_y q_z q_w
Packit ea1746
      ...
Packit ea1746
Packit ea1746
   where ``pose_id`` is the corresponding integer ID from the file
Packit ea1746
   definition. Note, the file will be sorted in ascending order for the
Packit ea1746
   ``pose_id``.
Packit ea1746
Packit ea1746
   The executable :member:`pose_graph_3d` expects the first argument to be the
Packit ea1746
   path to the problem definition. The executable can be run via
Packit ea1746
Packit ea1746
   .. code-block:: bash
Packit ea1746
Packit ea1746
      /path/to/bin/pose_graph_3d /path/to/dataset/dataset.g2o
Packit ea1746
Packit ea1746
   A script is provided to visualize the resulting output files. There is also
Packit ea1746
   an option to enable equal axes using ``--axes_equal``
Packit ea1746
Packit ea1746
   .. code-block:: bash
Packit ea1746
Packit ea1746
      /path/to/repo/examples/slam/pose_graph_3d/plot_results.py --optimized_poses ./poses_optimized.txt --initial_poses ./poses_original.txt
Packit ea1746
Packit ea1746
   As an example, a standard synthetic benchmark dataset [#f9]_ where the robot is
Packit ea1746
   traveling on the surface of a sphere which has 2500 nodes with a total of
Packit ea1746
   4949 edges was solved. Visualizing the results with the provided script
Packit ea1746
   produces:
Packit ea1746
Packit ea1746
   .. figure:: pose_graph_3d_ex.png
Packit ea1746
      :figwidth: 600px
Packit ea1746
      :height: 300px
Packit ea1746
      :align: center