1\documentclass[a4paper]{article}
2\usepackage{graphicx}
3\usepackage{fullpage}
4\usepackage{graphicx} % for pdf, bitmapped graphics files
5\usepackage{amsmath} % assumes amsmath package installed
6\usepackage{amssymb}  % assumes amsmath package installed\ref
7\usepackage{psfrag}
8\usepackage{algorithmic}
9\usepackage{algorithm}
10\usepackage{color}
11\usepackage{xcolor}
12\usepackage{listings}
13\begin{document}
14
15\lstset{
16language=C++,
17captionpos=b,
18tabsize=2,
19keywordstyle=\color{blue},
20commentstyle=\color{green},
21stringstyle=\color{red},
22breaklines=true,
23showstringspaces=false,
24frame=shadowbox,
25rulesepcolor=\color{gray},
26basicstyle=\footnotesize%
27}
28
29
30\newcommand{\gopt}{g\ensuremath{^2}o}
31\newcommand{\defeq}{\stackrel{\text{def.}}{=}}
32\newcommand{\gcomment}[1]{\textcolor{red}{\textbf{Giorgio:}~\emph{#1}}}
33\newcommand{\rcomment}[1]{\textcolor{red}{\textbf{Rainer:}~\emph{#1}}}
34
35\def\secref#1{Section~\ref{#1}}
36\def\figref#1{Figure~\ref{#1}}
37\def\tabref#1{Table~\ref{#1}}
38\def\eqref#1{Eq.~\ref{#1}}
39
40\newcommand{\Dom}{\mathtt{Dom}}
41\newcommand{\bZero}{\mathbf{0}}
42\newcommand{\bb}{\mathbf{b}}
43\newcommand{\bc}{\mathbf{c}}
44\newcommand{\bh}{\mathbf{h}}
45\newcommand{\bH}{\mathbf{H}}
46\newcommand{\bA}{\mathbf{A}}
47\newcommand{\bM}{\mathbf{M}}
48\newcommand{\bF}{\mathbf{F}}
49\newcommand{\bG}{\mathbf{G}}
50\newcommand{\bI}{\mathbf{I}}
51\newcommand{\bB}{\mathbf{B}}
52\newcommand{\bR}{\mathbf{R}}
53\newcommand{\bp}{\mathbf{p}}
54\newcommand{\bq}{\mathbf{q}}
55\newcommand{\bqTilde}{\mathbf{\tilde q}}
56\newcommand{\ec}{\mathbf{e}}
57\newcommand{\be}{\mathbf{e}}
58\newcommand{\br}{\mathbf{r}}
59\newcommand{\bx}{\mathbf{x}}
60\newcommand\bbx{\breve{\bx}}
61\newcommand{\bu}{\mathbf{u}}
62\newcommand{\bz}{\mathbf{z}}
63\newcommand{\bt}{\mathbf{t}}
64\newcommand{\bDeltax}{\mathbf{\Delta x}}
65\newcommand{\bDeltaAlpha}{\mathbf{\Delta \alpha}}
66\newcommand{\bDeltaAlphaTilde}{\mathbf{\Delta\tilde\alpha}}
67\newcommand{\bTDeltax}{\mathbf{\Delta \tilde x}}
68\newcommand{\bDelta}{\mathbf{\Delta}}
69\newcommand{\balpha}{\mathbf{\alpha}}
70\newcommand{\bOmega}{\mathbf{\Omega}}
71\newcommand{\bSigma}{\mathbf{\Sigma}}
72\newcommand{\bJ}{\mathbf{J}}
73\newcommand{\diff}{\partial}
74\def\argmax{\mathop{\rm argmax}}
75\def\argmin{\mathop{\rm argmin}}
76
77\newcommand{\Rstar}{{\cal R} }
78\newcommand{\Rx}{R}
79\newcommand{\tx}{t}
80
81\newcommand{\angleOf}{\mathbf{angleOf}}
82\newcommand{\axisOf}{\mathbf{axisOf}}
83\newcommand{\slerp}{\mathbf{slerp}}
84
85\title{\gopt: A general Framework for (Hyper) Graph Optimization}
86\author{Giorgio Grisetti, Rainer K\"ummerle, Hauke Strasdat, Kurt Konolige\\
87	email: \texttt{\{grisetti,kuemmerl\}@informatik.uni-freiburg.de}\\
88        \texttt{strasdat@gmail.com konolige@willowgarage.com}
89}
90\maketitle
91
92In this document we describe a C++ framework for performing the
93optimization of nonlinear least squares problems that can be embedded
94as a graph or in a hyper-graph. A hyper-graph is an extension of a
95graph where an edge can connect multiple nodes and not only two.
96Several problems in robotics and in computer vision require to find
97the optimum of an error function with respect of a set of parameters.
98Examples include, popular applications like SLAM and Bundle
99adjustment.
100
101In the literature, many approaches have been proposed to address this
102class of problems. The naive implementation of standard methods, like
103Levenberg-Marquardt or Gauss-Newton can lead to acceptable results for
104most applications, when the correct parameterization is
105chosen. However, to achieve the maximum performances substantial
106efforts might be required.
107
108\gopt\ stands for General (Hyper) Graph Optimization. The purposes of
109this framework are the following:
110\begin{itemize}
111\item To provide an easy-to-extend and easy-to-use general library for
112  graph optimization that can be easily applied to different problems,
113\item To provide people who want to understand SLAM or BA with an
114  easy-to-read implementation that focuses on the relevant details of
115  the problem specification.
116\item Achieve state-of-the-art performances,
117  while being as general as possible.
118\end{itemize}
119
120In the remainder of this document we will first characterize the
121(hyper) graph-embeddable problems, and we will give an introduction to
122their solution via the popular Levenberg-Marquardt or Gauss-Newton
123algorithms implemented in this library.  Subsequently, we will
124describe the high-level behavior of the library, and the basic
125structures.  Finally, we will introduce how to implement 2D SLAM as a
126simple example.\\
127\vspace{.5cm}\\
128\textbf{This document is not a replacement for the
129  in-line documentation.  Instead, it is a digest to help the
130  user/reader to read/browse and extend the code.}
131
132  \vspace{.5cm}
133\noindent Please cite this when using \gopt:\\
134R. K\"ummerle, G. Grisetti, H. Strasdat, K. Konolige, and W. Burgard.
135g2o: A General Framework for Graph Optimization.
136In Proc. of the IEEE Int. Conf. on Robotics and Automation (ICRA). Shanghai, China, May 2011.
137
138\section{(Hyper)Graph-Embeddable Optimization Problems}
139A least squares minimization problem can be described by the following equation:
140\begin{eqnarray}
141\bF(\bx)&=& \sum_{k \in\mathcal{C}}
142\underbrace{\be_k(\bx_k, \bz_{k})^T \bOmega_{k} \be_k(\bx_k, \bz_{k})}_{\bF_{k}}
143\label{eq:sumOfFactors}\\
144\bx^*&=&\argmin_{\bx} \bF(\bx).
145\label{eq:toMinimize}
146\end{eqnarray}
147Here
148\begin{itemize}
149  \item  $\bx=(\bx_1^T,\;\ldots\;,\bx_n^T)^T$ is a vector of
150    parameters, where each $\bx_i$ represents a generic parameter block.
151  \item $\bx_k=(\bx_{k_1}^T,\;\ldots\;,\bx_{k_q}^T)^T \subset
152    (\bx_1^T,\;\ldots\;,\bx_n^T)^T$ is the subset of the parameters
153    involved in the $k^\mathrm{th}$ constraint.
154  \item $\bz_{k}$ and $\bOmega_{k}$ represent
155    respectively the mean and the information matrix of a constraint
156    relating the parameters in $\bx_k$.
157  \item $\be_k(\bx_k \bz_{k})$ is a vector error function
158    that measures how well the parameter blocks in $\bx_k$ satisfy the
159    constraint $\bz_{k}$. It is $\bZero$ when $\bx_k$ and $\bx_j$
160    perfectly match the constraint.  As an example, if one has a
161    measurement function $\hat \bz_k = \bh_k(\bx_k)$ that generates a
162    synthetic measurement $\hat \bz_k$ given an actual configuration
163    of the nodes in $\bx_k$.  A straightforward error function would
164    then be $\be(\bx_k, \bz_{k}) = \bh_k(\bx_k) - \bz_k$.
165\end{itemize}
166For simplicity of notation, in the rest of this paper we will encode
167the measurement in the indices of the error function:
168\begin{equation}
169\be_k(\bx_k, \bz_{k}) \; \defeq \; \be_{k}(\bx_k) \; \defeq \; \be_{k}(\bx).
170\end{equation}
171Note that each parameter block and each error
172function can span over a different space.  A problem in this form can
173be effectively represented by a directed hyper-graph. A node $i$ of
174the graph represents the parameter block $\bx_i \in \bx_k$ and an
175hyper-edge among the nodes $\bx_{i} \in \bx_k $ represents a
176constraint involving all nodes in $\bx_k$. In case the hyper edges
177have size 2, the hyper-graph becomes an ordinary graph.
178Figure~\ref{fig:graph-example} shows an
179example of mapping between a hyper-graph and an objective function.
180
181\begin{figure}
182\centering
183\psfrag{p0}{$\bp_0$}
184\psfrag{p1}{$\bp_1$}
185\psfrag{p2}{$\bp_2$}
186\psfrag{pi}{$\bp_i$}
187\psfrag{pj}{$\bp_j$}
188\psfrag{pim1}{$\bp_{i-1}$}
189\psfrag{z2i}{$\bz_{2i}$}
190\psfrag{K}{$\mathbf{K}$}
191\psfrag{zij}{$\bz_{ij}$}
192\psfrag{u0}{$\bu_{0}$}
193\psfrag{u1}{$\bu_{1}$}
194\psfrag{ui1}{$\bu_{i-1}$}
195\includegraphics{pics/hgraph.eps}
196\caption{This example illustrates how to represent an objective
197  function by a hyper-graph. Here we illustrate a portion of a small
198  SLAM problem~\cite{konolige10iros}. In this example we assume that
199  where the measurement functions are governed by some unknown
200  calibration parameters $\mathbf{K}$. The robot poses are represented
201  by the variables $\bp_{1:n}$. These variables are connected by
202  constraints $\bz_{ij}$ depicted by the square boxes. The constraints
203  arise, for instance, by matching nearby laser scans \emph{in the
204    laser reference frame}. The relation between a laser match and a
205  robot pose, however, depends on the position of the sensor on the
206  robot, which is modeled by the calibration parameters
207  $\mathbf{K}$. Conversely, subsequent robot poses are connected by
208  binary constraints $\bu_{k}$ arising from odometry
209  measurements. These measurements are made in the frame of the robot
210  mobile base.}
211\label{fig:graph-example}
212\end{figure}
213
214\section{Least Squares Optimization}
215If a good initial guess $\breve{\bx}$ of the parameters is known, a
216numerical solution of \eqref{eq:toMinimize} can be obtained by using
217the popular Gauss-Newton or Levenberg-Marquardt
218algorithms~\cite[\S15.5]{Press92Book}.  The idea is to approximate the
219error function by its first order Taylor expansion around the current
220initial guess $\breve{\bx}$
221\begin{eqnarray}
222\be_{k}(\breve{\bx}_k + \bDeltax_k) &=& \be_{k}(\breve{\bx} + \bDeltax)\\
223&\simeq& \ec_{k} + \bJ_{k} \bDeltax.
224\label{eq:taylor}
225\end{eqnarray}
226Here $\bJ_{k}$ is the Jacobian of $\be_{k}(\bx)$ computed in
227$\breve{\bx}$ and $\ec_{k} \defeq \be_{k}(\breve{\bx})$.
228Substituting \eqref{eq:taylor} in the error terms $\bF_{k}$ of
229\eqref{eq:sumOfFactors}, we obtain
230
231\vspace{-3.5mm}
232{\small
233\begin{eqnarray}
234\lefteqn{\bF_{k}(\breve{\bx} + \bDeltax) }\\
235&=&  \be_{k}(\breve{\bx} + \bDeltax)^T \bOmega_{k} \be_{k}(\breve{\bx} + \bDeltax)  \\
236\label{eq:errorQuad1}
237             &\simeq& \left(\ec_{k} + \bJ_{k} \bDeltax \right)^T \bOmega_{k} \left(\ec_{k} + \bJ_{k} \bDeltax \right)  \\
238\label{eq:errorQuad2}
239             &=& \underbrace{\ec_{k}^T \bOmega_{k}\ec_{k}}_{\mathrm{c}_{k}} + 2 \underbrace{\ec_{k}^T \bOmega_{k} \bJ_{k}}_{\bb_{k}} \bDeltax +
240 \bDeltax^T \underbrace{\bJ_{k}^T \bOmega_{k}\bJ_{k}}_{\bH_{k}} \bDeltax \\
241             &=&\mathrm{c}_{k} + 2 \bb_{k} \bDeltax + \bDeltax^T \bH_{k} \bDeltax
242\label{eq:errorQuad}
243\end{eqnarray}
244}
245With this local approximation, we can rewrite the function
246$\bF(\bx)$ given in \eqref{eq:sumOfFactors} as
247
248\vspace{-3.5mm}
249{\small
250\begin{eqnarray}
251\bF(\breve{\bx} + \bDeltax) &=& \sum_{k \in\mathcal{C}} \bF_{k}(\breve{\bx} + \bDeltax)
252\label{eq:optNetwork0}\\
253                      &\simeq& \sum_{k \in\mathcal{C}} \mathrm{c}_{k} + 2 \bb_{k} \bDeltax + \bDeltax^T \bH_{k} \bDeltax
254\label{eq:optNetwork1}\\
255                      &=&\mathrm{c} + 2 \bb^T \bDeltax + \bDeltax^T \bH \bDeltax.
256\label{eq:optNetwork2}
257\end{eqnarray}
258} The quadratic form in \eqref{eq:optNetwork2} is obtained from
259\eqref{eq:optNetwork1} by setting $\mathrm{c}=\sum \mathrm{c}_{k}$,
260$\bb=\sum \bb_{k}$ and $\bH=\sum \bH_{k}$. It can be minimized in
261$\bDeltax$ by solving the linear system
262\begin{eqnarray}
263       \bH\,\bDeltax^* &=& - \bb.
264\label{eq:oneLinearIteration}
265\end{eqnarray}
266The matrix $\bH$ is the information matrix of the system and is sparse
267by construction, having non-zeros only between blocks connected by a
268constraint.  Its number of non-zero blocks is twice the number of
269constrains plus the number of nodes. This allows to solve
270\eqref{eq:oneLinearIteration} with efficient approaches like sparse
271Cholesky factorization or Preconditioned Conjugate Gradients (PCG). An
272highly efficient implementation of sparse Cholesky factorization can
273be found in publicly available packages like CSparse~\cite{csparse} or
274CHOLMOD~\cite{cholmod}.  The linearized solution is then obtained by
275adding to the initial guess the computed increments
276\begin{eqnarray}
277  \bx^*&=&\breve{\bx}+\bDeltax^*.
278\label{eq:linearPropagation}
279\end{eqnarray}
280The popular Gauss-Newton algorithm iterates the linearization in
281\eqref{eq:optNetwork2}, the solution in \eqref{eq:oneLinearIteration}
282and the update step in \eqref{eq:linearPropagation}. In every
283iteration, the previous solution is used as linearization point and as
284initial guess.
285
286The Levenberg-Marquardt (LM) algorithm is a nonlinear variant to Gauss-Newton that introduces
287a damping factor and backup actions to control the convergence.
288Instead of solving directly Eq.~\ref{eq:oneLinearIteration}
289LM solves a damped version of it
290\begin{eqnarray}
291       (\bH +\lambda \bI)\,\bDeltax^* &=& - \bb.
292\label{eq:oneLinearIterationLevenberg}
293\end{eqnarray}
294Here $\lambda$ is a damping factor: the larger $\lambda$ is the
295smaller are the $\bDeltax$. This is useful to control the step size in
296case of non-linear surfaces.  The idea behind the LM algorithm is to
297dynamically control the damping factor.  At each iteration the error
298of the new configuration is monitored.  If the new error is lower than
299the previous one, lambda is decreased for the next iteration.
300Otherwise, the solution is reverted and lambda is increased.
301For a more detailed explanation of the LM algorithm implemented in our package
302we refer to~\cite{lourakis2009toms}.
303
304The procedures described above are a general approach to multivariate
305function minimization. The general approach, however, assumes that the
306space of parameters $\bx$ is Euclidean, which is not valid for several
307problems like SLAM or bundle adjustment. This may lead to sub-optimal
308solutions. In the remainder of this section we discuss first the
309general solution when the space of the parameters is Euclidean, and
310subsequently we extend this solution to more general non-Euclidean
311spaces.
312
313\section{Considerations about the Structure of the Linearized System}
314According to \eqref{eq:optNetwork2}, the matrix $\bH$ and the vector
315$\bb$ are obtained by summing up a set of matrices and vectors, one
316for every constraint.  If we set $\bb_{k}=\bJ_{k}^T \bOmega_{k}
317\ec_{k}$ and $\bH_{k}= \bJ_{k}^T \bOmega_{k}\bJ_{k}$ we can
318rewrite $\bH$ and $\bb$ as
319\begin{eqnarray}
320  \bb&=&\sum_{k \in\mathcal{C}} \bb_{ij}\\
321  \bH&=&\sum_{k \in\mathcal{C}} \bH_{ij}. \label{eq:addendTerm}
322\end{eqnarray}
323Every constraint will contribute to the system with an addend
324term. The \emph{structure} of this addend depends on the Jacobian of
325the error function.  Since the error function of a constraint depends
326only on the values of the nodes $\bx_i \in \bx_k$, the Jacobian in
327\eqref{eq:taylor} has the following form:
328\begin{eqnarray}
329\bJ_{k} &=& \left(\bZero \cdots \bZero \; \bJ_{k_1} \; \cdots \; \bJ_{k_i} \;\cdots \bZero\; \cdots\;  \bJ_{k_q} \bZero \cdots \bZero \right).
330\label{eq:jacExpanded}
331\end{eqnarray}
332Here $\bJ_{k_i}= \frac{\partial \be(\bx_k)}{\partial \bx_{k_i}}$ are the
333derivatives of the error function with respect to the nodes connected
334by the $k^\mathrm{th}$ hyper-edge, with respect to the parameter block
335$\bx_{k_i} \in \bx_k$.
336
337From \eqref{eq:errorQuad2} we obtain the following structure for the block matrix $\bH_{ij}$:
338\begin{eqnarray}
339\bH_{k}&=&\left(
340\begin{array}{cccccccc}
341\ddots & & & & & & &\\
342& \bJ_{k_1}^T \bOmega_{k} \bJ_{k_1} & \cdots & \bJ_{k_1}^T \bOmega_{k} \bJ_{k_i} & \cdots & \bJ_{k_1}^T \bOmega_{k} \bJ_{k_q} &\\
343& \vdots & & \vdots & & \vdots & \\
344& \bJ_{k_i}^T \bOmega_{k} \bJ_{k_1} & \cdots & \bJ_{k_i}^T \bOmega_{k} \bB_{k_i} & \cdots & \bJ_{k_i}^T \bOmega_{k} \bJ_{k_q} & \\
345& \vdots & & \vdots & & \vdots & \\
346& \bJ_{k_q}^T \bOmega_{k} \bJ_{k_1} & \cdots & \bJ_{k_q}^T \bOmega_{k} \bB_{k_i} & \cdots & \bJ_{k_q}^T \bOmega_{k} \bJ_{k_q} & \\
347& &&&&&& \ddots \\
348\end{array}
349\right) \\
350\bb_{k}&=&\left(
351\begin{array}{c}
352\vdots\\
353\bJ_{k_1} \bOmega_{k} \ec_{k}\\
354\vdots\\
355\bJ_{k_i}^T \bOmega_{k} \ec_{k}\\
356\vdots\\
357\bJ_{k_q}^T \bOmega_{k} \ec_{k}\\
358\vdots\\
359\end{array}
360\right)
361\end{eqnarray}
362For simplicity of notation we omitted the zero blocks.  The reader
363might notice that the block structure of the matrix $\bH$ is the
364adjacency matrix of the hyper graph.  Additionally the Hessian $\bH$
365is a symmetric matrix, since all the $\bH_k$ are symmetric. A single
366hyper-edge connecting $q$ vertices will introduce $q^2$ non zero
367blocks in the Hessian, in correspondence of each pair $\left<
368\bx_{k_i}, \bx_{k_j} \right>$, of nodes connected.
369
370
371\section{Least Squares on Manifold}
372\label{sec:manifold}
373
374To deal with parameter blocks that span over a non-Euclidean spaces,
375it is common to apply the error minimization on a manifold.  A
376manifold is a mathematical space that is not necessarily Euclidean on
377a global scale, but can be seen as Euclidean on a local
378scale~\cite{Lee2003SmoothManifolds}.
379
380For example, in the context of SLAM problem, each parameter block
381$\bx_i$ consists of a translation vector $\bt_i$ and a rotational
382component $\balpha_i$. The translation~$\bt_i$ clearly forms a Euclidean
383space. In contrast to that, the rotational components~$\balpha_i$ span
384over the non-Euclidean 2D or 3D rotation group $SO(2)$ or $SO(3)$.  To
385avoid singularities, these spaces are usually described in an
386over-parameterized way, e.g., by rotation matrices or quaternions.
387Directly applying \eqref{eq:linearPropagation} to these
388over-parameterized representations breaks the constraints induced by the
389over-parameterization. The over-parameterization results in additional
390degrees of freedom and thus introduces errors in the solution.  To
391overcome this problem, one can use a minimal representation for the
392rotation (like Euler angles in 3D). This, however, is then subject to
393singularities.
394
395An alternative idea is to consider the underlying space as a manifold
396and to define an operator $\boxplus$ that maps a local variation
397$\bDeltax$ in the Euclidean space to a variation on the manifold,
398$\bDeltax\mapsto\bx\boxplus\bDeltax$.  We refer the reader to
399\cite[\S1.3]{hertzberg08diplom} for more mathematical details. With
400this operator, a new error function can be defined as
401\begin{eqnarray}
402\breve \be_{k}(\bTDeltax_k) &\defeq&
403  \be_{k}(\bbx_k \boxplus \bTDeltax_k) \\
404  &=& \be_{k}(\bbx \boxplus \bTDeltax) \label{eq:manifoldTaylor0}
405  \simeq \breve \be_{k} + \tilde \bJ_{k} \bTDeltax,
406\label{eq:manifoldTaylor}
407\end{eqnarray}
408where $\bbx$ spans over the original over-parameterized space, for
409instance quaternions. The term $\bTDeltax$ is a small increment around
410the original position $\bbx$ and is expressed in a minimal
411representation. A common choice for $SO(3)$ is to use the vector part
412of the unit quaternion.
413%
414In more detail, one can represent the increments $\bTDeltax$ as 6D vectors
415$\bTDeltax^T = ( {\bDelta\tilde\bt}^T \, {\bqTilde}^T)$,
416where $\bDelta \tilde \bt$ denotes the translation and $\bqTilde^T
417= ( \Delta q_x \, \Delta q_y \, \Delta q_z)^T$ is the
418vector part of the unit quaternion representing the 3D rotation.  Conversely, $\bbx^T=(
419\breve \bt^T \, \breve \bq^T)$ uses a quaternion $\breve \bq$ to
420encode the rotational part.  Thus, the operator $\boxplus$ can be
421expressed by first converting $\bDelta \tilde \bq$ to a full quaternion $\bDelta
422\bq$ and then applying the transformation $\bDelta \bx ^T = ( \bDelta
423\bt^T \, \bDelta \bq^T)$ to $\bbx$.  In the equations
424describing the error minimization, these operations can nicely be
425encapsulated by the $\boxplus$ operator.  The Jacobian $\tilde
426\bJ_{k}$ can be expressed by
427\begin{eqnarray}
428\tilde \bJ_{k} &=& \left. \frac{\partial \be_{k}(\breve{\bx} \boxplus \bTDeltax)} {\partial \bTDeltax} \right|_{\bTDeltax=\bZero}.
429\label{eq:manifoldJacobian}
430\end{eqnarray}
431Since in the previous equation $\breve{\be}$ depends only on $\bTDeltax_{k_i} \in \bTDeltax_{k}$ we can further expand it as follows:
432\begin{eqnarray}
433\tilde \bJ_{k} &=&
434\left.
435\frac{\partial \be_{k}(\breve{\bx} \boxplus \bTDeltax)} {\partial \bTDeltax} \right|_{\bTDeltax=\bZero}\\
436&=& \left(\bZero \cdots \bZero \; \tilde \bJ_{k_1} \; \cdots \; \tilde \bJ_{k_i} \;\cdots \bZero\; \cdots\;  \tilde \bJ_{k_q} \bZero \cdots \bZero \right).
437\label{eq:manifoldJacobianSparse}
438\end{eqnarray}
439With a straightforward extension of notation, we set
440\begin{equation}
441\tilde \bJ_{k_i} = \left. \frac{ \partial \be_{k}(\breve{\bx} \boxplus \bTDeltax)}{\partial \bTDeltax_{k_i}} \right|_{\bTDeltax=\bZero}
442\end{equation}
443
444%% Using the rule on the partial derivatives and exploiting the fact
445%% that the Jacobian is evaluated in $\bTDeltax=\bZero$, the non-zero
446%% blocks become:
447%% \begin{eqnarray}
448%% \frac{\partial \be_{k}(\breve{\bx_k} \boxplus \bTDeltax_k)} {\partial \bTDeltax_i} &=&
449%% \underbrace{\frac{\partial \be_{k}(\breve{\bx})} {\partial \breve{\bx}_i}}_{\bJ_{k_i}}
450%% \cdot
451%% \left.
452%% \underbrace{
453%% \frac{\breve{\bx}_{k_i} \boxplus \bTDeltax_{k_i}} {\partial \bTDeltax_{k_i}}
454%% }_{\bM_{k_i}}
455%% \right|_{\bTDeltax=\bZero}
456%% \label{eq:manifoldJacobianBlocks}
457%% \end{eqnarray}
458%% Accordingly, one can easily derive from the a Jacobian \emph{not} defined on a manifold
459%% of Eq.~\ref{eq:jacExpanded} a Jacobian on a manifold just by multiplying its non-zero blocks
460%% by the derivative of the $\boxplus$ operator computed in $\breve{\bx_i}$ and $\breve{\bx_j}$.
461%% Let the Jacobians of $\boxplus$ be denoted by $\bM_{k_i}$. By using the notation in
462%% Eq.~\ref{eq:jacExpanded} we can rewrite Eq.~\ref{eq:manifoldJacobian} as
463%% \begin{eqnarray}
464%% \tilde \bJ_{ij} &=&
465%% \left(\bZero \cdots \bZero \; \bJ_{k_1} \bM_{k_1} \; \cdots \; \bJ_{k_i} \bM_{k_i} \;\cdots \bZero\; \cdots\; \bJ_{k_q}\bM_{k_q} \bZero \cdots \bZero \right).
466%% \label{eq:jacManExpanded}
467%% \end{eqnarray}
468
469With a straightforward extension of the notation, we can insert
470\eqref{eq:manifoldTaylor} in \eqref{eq:errorQuad1} and
471\eqref{eq:optNetwork0}. This leads to the following increments:
472\begin{eqnarray}
473       \tilde \bH \, \bTDeltax^* &=& - \tilde \bb .
474\label{eq:oneLinearManifoldIteration}
475\end{eqnarray}
476Since the increments $\bTDeltax^*$ are computed in the local Euclidean
477surroundings of the initial guess $\breve{\bx}$, they need to be
478re-mapped into the original redundant space by the $\boxplus$
479operator. Accordingly, the update rule of \eqref{eq:linearPropagation}
480becomes
481\begin{eqnarray}
482  \bx^*& =& \breve{\bx} \boxplus \bTDeltax^*.
483\label{eq:manifoldPropagation}
484\end{eqnarray}
485In summary, formalizing the minimization problem on a manifold consists
486of first computing a set of increments in a local Euclidean
487approximation around the initial guess by
488\eqref{eq:oneLinearManifoldIteration}, and second accumulating the
489increments in the global non-Euclidean space by
490\eqref{eq:manifoldPropagation}.  Note that the linear system computed on
491a manifold representation has the same structure like the linear system
492computed on an Euclidean space.  One can easily derive a manifold
493version of a graph minimization from a non-manifold version, only by
494defining an $\boxplus$ operator and its Jacobian $\tilde \bJ_{k_{i}}$
495w.r.t.  the corresponding parameter block.  In \gopt{} we provide tools
496for numerically computing the Jacobians on the manifold space.  This
497requires the user to implement the error function and the $\boxplus$
498operator only. As a design choice, we do not address the non-manifold
499case since it is already contained in the manifold one.  However, to
500achieve the maximum performances and accuracy we recommend the user to
501implement analytic Jacobians, once the system is functioning with the
502numeric ones.
503
504\section{Robust Least Squares\label{sec:robust_kernel}}
505Optionally, the least squares optimization can be robustified.
506Note, that the error terms in Eq.~\ref{eq:sumOfFactors}  have the following form:
507\begin{eqnarray}
508\bF_k = \be_k^T\Omega_k \be_k = \rho_2\left(\sqrt{\be_k^T\Omega_k \be_k}\right) \quad \text{with} \quad \rho_2(x):=x^2.
509\end{eqnarray}
510Thus, the error vector $\be_k$ has quadratic influence on $\bF$,
511so that a single potential outlier would have major negative impact.
512In order be more outlier robust, the quadratic error function $\rho_2$
513can be replaced by a more robust cost function which weighs large errors less.
514In \gopt, the Huber cost function $\rho_H$ can be used
515\begin{eqnarray}
516\rho_H(x) := \begin{cases}
517                 x^2         & \text{if } |x|<b\\
518                  2b |x| - b^2          & \text{else},
519   \end{cases}
520\end{eqnarray}
521which is quadratic for small $|x|$ but linear for large $|x|$. Compared to other,
522even more robust cost functions, the Huber kernel has to advantage that it is
523still convex and thus does not introduce new local minima in $\bF$~\cite[pp.616]{Hartley:Zisserman:Book2004}.
524In practice, we do not need to modify Eq.~$\ref{eq:sumOfFactors}$. Instead, the following scheme
525is applied. First the  error $\be_k$ is computed as usual. Then, $\be_k$ is replaced by
526a weighted version $w_k\be_k$ such that
527\begin{eqnarray}
528(w_k\be_k)^T\Omega_k (w_k\be_k) = \rho_H\left(\sqrt{\be_k^T\Omega_k \be_k}\right).
529\end{eqnarray}
530Here, the weights $w_k$ are calculated as follows
531\begin{eqnarray}
532w_k = \frac{\sqrt{\rho_H\left(||\be_k||_\Omega \right)}}{||\be_k||_\Omega } \quad \text{with} \quad ||\be_k||_\Omega := \sqrt{\be_k^T\Omega_k \be_k}.
533\end{eqnarray}
534In \gopt, the user has fine-grained control and can enable/disable the robust cost function for each edge individually (see Section~\ref{sec:error}).
535
536
537
538
539\section{Library Overview}
540From the above sections it should be clear that a graph-optimization problem is entirely defined by:
541\begin{itemize}
542\item The types of the vertices in the graph (that are the parameters blocks $\{\bx_i\}$.
543  For each of those one has to specify:
544  \begin{itemize}
545    \item the domain $\Dom(\bx_i)$ of the internal parameterization,
546    \item the domain $\Dom(\bDeltax_i)$ of the increments $\bDeltax_i$,
547    \item $\boxplus: \Dom(\bx_i) \times \Dom(\bDeltax_i) \rightarrow \Dom(\bx_i)$ that
548      applies the increment $\bDeltax_i$ to the previous solution $\bx_i$.
549  \end{itemize}
550\item the error function for every type of hyper-edge
551  $\be_{k}:\Dom(\bDeltax_{k_1}) \times \Dom(\bDeltax_{k_2}) \times \dots \times
552  \Dom(\bDeltax_{k_q}) \rightarrow \Dom(\bz_{k})$ that should be zero when
553  the perturbated estimate $\bx_{k} \boxplus \bDeltax_{k}$ perfectly satisfies the constraint $\bz_{k}$.
554\end{itemize}
555By default the Jacobians are computed numerically by our
556framework. However to achieve the maximum performances in a specific
557implementation one can specify the Jacobian of the error functions and
558of the manifold operators.
559
560In the reminder we will shortly discuss some basic concepts to use and
561extend \gopt.  This documentation is by no means complete, but it is
562intended to help you browsing the automatically generated
563documentation. To better visualize the interplay of the components of
564\gopt{} we refer to the class diagram of Figure~\ref{fig:classes}.
565\begin{figure}
566\centering
567\includegraphics[width=0.7\columnwidth]{pics/classes.eps}
568\caption{Class diagram of \gopt{}.}
569%% TODO update by adding OptimizationAlgorithm
570\label{fig:classes}
571\end{figure}
572
573\subsection {Representation of an Optimization Problem}
574\label{sec:representation}
575All in all our system utilizes a generic hyper-graph structure to
576represent a problem instance (defined in \verb+hyper_graph.h+).  This
577generic hyper graph is specialized to represent an optimization
578problem by the class \verb+OptimizableGraph+, defined in
579\verb+optimizable_graph.h+.  Within the \verb+OptimizableGraph+ the
580inner classes \verb+OptimizableGraph::Vertex+ and
581\verb+OptimizableGraph::Edge+ are used to represent generic hyper
582edges and hyper vertices.  Whereas the specific implementation might
583be done by directly extending these classes, we provided a template
584specialization that implements automatically most of the methods that
585are mandatory for the system to work.
586
587These classes are \verb+BaseVertex+ and \verb+BaseUnaryEdge+,
588\verb+BaseBinaryEdge+ and \verb+BaseMultiEdge+.
589\begin{description}
590\item \verb+BaseVertex+ templatizes the dimension of a parameter block
591  $\bx_i$ and of the corresponding manifold $\bDeltax_i$, thus it can
592  use blocks of memory whose layout is known at compile-time (means
593  efficiency). Furthermore, it implements some mapping operators to
594  store the Hessian and the parameter blocks of the linearized
595  problem, and a stack of previous values that can be used to
596  save/restore parts of the graph.  The method \verb+oplusImpl(double* v)+
597  that applies the perturbation $\bDeltax_i$ represented by \verb+v+,
598  to the member variable \verb+_estimate+ should be implemented. This
599  is the $\boxplus$ operator. Additionally,
600  \verb+setToOriginImpl()+ that should set the internal state of the vertex
601  to $\bZero$ has to specified.
602
603\item \verb+BaseUnaryEdge+ is a template class to model a unary
604  hyper-edge, which can be used to represent a prior. It offers for
605  free the calculation of the Jacobians, via an implementation of the
606  \verb+linearizeOplus+ method. It requires to specify the types of
607  the (single) vertex $\bx_i$, and type and dimension of the error
608  $\be(\bx_k)$ as template parameters.  The function
609  \verb+computeError+ that stores the result of the error $\be(\bx_k)$ in the
610  member \verb+Eigen::Matrix _error+ should be implemented.
611
612\item \verb+BaseBinaryEdge+ is a template class that models a binary
613  constraint, namely an error function in the form $\be_k(\bx_{k_1},
614  \bx_{k_2})$. It offers the same facilities of \verb+BaseUnaryEdge+, and it
615  requires to specify the following template parameters: the type of the nodes
616  $\bx_{k_1}$ and $\bx_{k_2}$  and the  type and the dimension of the measurement.
617  Again, it implements the numeric Jacobians via
618  a default implementation of the \verb+linearizeOplus+ method.
619  Again, the \verb+computeError+ should be implemented in a derived class.
620
621\item \verb+BaseMultiEdge+ is a template class that models a
622  multi-vertex constraint in the form of $\be_k(\bx_{k_1}, \bx_{k_2},
623  \ldots, \bx_{k_q})$. It offers the same facilities of the types
624  above, and it requires to specify only the type and dimension of the
625  measurement as template parameters. The specialized class should
626  take care of resizing the connected vertices to the correct size
627  $q$.  This class relies on a dynamic memory, since too many
628  parameters are unknown, and if you need of an efficient
629  implementation for a specific problem you can program it yourself.
630  Numeric Jacobian comes for free, but you should implement the
631  \verb+computeError+ in a derived class, as usual
632\end{description}
633
634In short, all you need to do to define a new problem instance is to
635derive a set of classes from those above listed, one for each type of
636parameter block and one for each type of (hyper)edge. Always try to
637derive from the class which does the most work for you.  If you want to
638have a look at a simple example look at \verb+vertex_se2+ and
639\verb+edge_se2+. Those two types define a simple 2D graph SLAM
640problem, like the one described in many SLAM papers.
641
642\begin{lstlisting}[float,label=lst:inittypes,caption=Registering types
643  by a constructor from a library]
644#include "g2o/core/factory.h"
645
646namespace g2o {
647  G2O_REGISTER_TYPE_GROUP(slam2d);
648
649  G2O_REGISTER_TYPE(VERTEX_SE2, VertexSE2);
650  G2O_REGISTER_TYPE(VERTEX_XY, VertexPointXY);
651
652  // ...
653}
654\end{lstlisting}
655
656Of course, for every type you construct you should define also the
657\verb+read+ and \verb+write+ functions to read and write your data to a
658stream.  Finally, once you define a new type, to enable the loading and
659the saving of the new type you should ``register'' it to a factory.
660This is easily done by assigning a string tag to a new type, via the
661\verb+registerType+ function. This should be called once before
662all files are loaded.
663
664To this end, \gopt{} provides an easy macro to carry out the registration of the
665class to the factory.  See Listing~\ref{lst:inittypes} for an example,
666the full example can be found in \verb+types_slam2d.cpp+.  The first
667parameter given to the macro \verb+G2O_REGISTER_TYPE+ specifies the tag
668under which a certain vertex / edge is known. \gopt\ will use this
669information while loading files and for saving the current graph into a
670file. In the example given in Listing~\ref{lst:inittypes} we register
671the tags \verb+VERTEX_SE2+ and \verb+EDGE_SE2+ with the classes
672\verb+VertexSE2+ and \verb+EdgeSE2+, respectively.
673
674Furthermore, the macro \verb+G2O_REGISTER_TYPE_GROUP+ allows to declare
675a type group. This is necessary if we use the factory to construct the
676types and we have to enforce that our code is linked to a specific type
677group. Otherwise the linker may drop our library, since we do not
678explicitly use any symbol provided by the library containing our type.
679Declaring the usage of a specific type library and hence enforcing the
680linking is done by the macro called \verb+G2O_USE_TYPE_GROUP+.
681
682\subsection{Construction and Representation of the Linearized Problem}
683The construction and the solution can be separated into individual
684steps which are iterated.
685\begin{itemize}
686  \item Initialization of the optimization (only before the first
687    iteration).
688  \item Computing the error vector for each constraint.
689  \item Linearize each constraint.
690  \item Build the linear system.
691  \item Updating the Levenberg-Marquardt damping factor.
692\end{itemize}
693
694Within the following sections we will describe the steps.
695
696\subsubsection{Initialization}
697The class \verb+SparseOptimizer+ offers several methods to initialize
698the underlying data structure. The methods
699\verb+initializeOptimization()+ either takes a subset of vertices or a
700subset of edges which will be considered for the next optimization runs.
701Additionally, all vertices and edges can be considered for optimization.
702We refer to the vertices and edges currently considered as \emph{active}
703vertices and edges, respectively.
704
705Within the initialization procedure, the optimizer assigns a temporary
706index to each active vertex. This temporary index corresponds to the
707block column / row of the vertex in the Hessian. Some of the vertices
708might need to be kept fixed during the optimization, to resolve
709arbitrary degrees of freedom (gauge freedom). This can be done by
710setting the \verb+_fixed+ attribute of a vertex.
711
712\subsubsection{Compute error\label{sec:error}}
713The \verb+computeActiveErrors()+ function takes the current estimate of
714the active vertices and for each active edge calls
715\verb+computeError()+ for computing the current error vector. Using
716the base edge classes described in Section~\ref{sec:representation}
717the error should be cached in the member variable \verb+_error+.
718
719 If \verb+robustKernel()+ is set to true for a particular active edge,
720\verb+robustifyError()+ is called and \verb+_error+ is robustified
721as described in Section~\ref{sec:robust_kernel}.
722
723\subsubsection{Linearizing the system}
724Each active edge is linearized by calling its
725\verb+linearizeOplus()+ function. Again the Jacobians can be cached by
726member variables provided by the templatized base classes described in
727Section~\ref{sec:representation}. If the \verb+linearizeOplus()+
728function is not re-implemented the Jacobian will be computed
729numerically as follows:
730\begin{eqnarray}
731  \tilde \bJ_k^{\bullet l} = \frac{1}{2\delta} \left(
732  \be_k (\bx_k \boxplus \delta\mathbf{1}_l)
733  -
734  \be_k (\bx_k \boxplus -\delta\mathbf{1}_l)
735  \right),
736  \label{eqn:jacobiannumeric}
737\end{eqnarray}
738where $\delta > 0$ is a small constant ($10^{-9}$ in our
739implementation) and $\mathbf{1}_l$ is the unit vector along dimension
740$l$. Note that we only store and calculate the non-zero entries of
741$\tilde \bJ_k$ that have not been fixed during the initialization.
742
743\subsubsection{Building the system}
744For each active edge the addend term for Eq.~\ref{eq:addendTerm} is
745computed by multiplying the corresponding blocks of the Jacobians and
746the information matrix of the edge. The addend term is calculated in
747each edge by calling \verb+constructQuadraticForm()+.
748
749\subsubsection{Updating Levenberg-Marquardt}
750As illustrated in \eqref{eq:oneLinearIterationLevenberg} the
751Levenberg-Marquardt algorithm requires updates to the linear system.
752However, only the elements along the main diagonal need to be modified.
753To this end, the methods \verb+updateLevenbergSystem(double lambda)+ and
754\verb+recoverSystem(double lambda)+ of the \verb+Solver+ class apply the
755modifications by respectively adding or subtracting $\lambda$ along the
756main diagonal of $\bH$.
757
758\subsection{Solvers}
759A central component of these least-squares approaches is the solution
760of the linear system $\tilde \bH \, \bTDeltax^* = - \tilde \bb$. To
761this end there are several approaches available, some of them exploit
762the known structure of certain problems and perform intermediate
763reductions of the system, like by applying the Schur complement to a
764subset of variables.  In \gopt{} we do not select any particular solver,
765but we rely on external libraries.  To this end, we decouple these
766\emph{structural} operations (like the Schur complement)
767from the solution of the linear system.
768
769The construction of the linear problem from the Jacobian matrices and the error vectors
770in the hyper-graph elements are controlled by a so-called \verb+Solver+ class.
771To use a specific factorization of the system, the user has to extend the
772\verb+Solver+ class, and to implement the virtual functions.
773Namely a solver should be able to extract from an
774hyper-graph the linear system, and to return a solution.  This is
775done in several steps: at the beginning of the optimization the
776function \verb+initializeStructure+ is called, to allocate the
777necessary memory that will be overwritten in the subsequent
778operations. This is possible since the structure of the system does
779not change between iterations. Then the user should provide means to
780access to the increment vector $\bTDeltax$ and $\tilde \bb$, via the
781functions \verb+b()+ and \verb+x()+. To support Levenberg-Marquardt
782one should also implement a function to perturb the Hessian with the
783$\lambda \bI$ term. This function is called
784\verb+setLambda(double lambda)+ and needs to be implemented by the
785specific solver.
786
787We provide a templatized implementation of the solver class, the
788\verb+BlockSolver<>+ that stores the linear system in a
789\verb+SparseBlockMatrix<>+ class. The \verb+BlockSolver<>+ implements
790also the Schur complement, and relies on another abstract class, the
791\verb+LinearSolver+ to solve the system.
792An implementation of the linear solver does the actual work
793of solving the (reduced) linear system, and has to implement a few methods.
794In this release of \gopt{} we provide linear solvers that use respectively
795preconditioned gradient descent, CSparse, and CHOLMOD.
796
797
798\subsection{Actions}
799To the extent of \gopt{}, the entities stored in a hyper-graph have a
800pure mathematical meaning. They either represent variables to be
801optimized (vertices), or they encode optimization constraints.
802However, in general these variables are usually related to more
803``concrete'' objects, like laser scans, robot poses, camera parameters
804and so on.  Some variable type may support only a subset of feasible
805operations.  For instance it is possible to ``draw'' a robot pose, but
806it is not possible to ``draw'' the calibration parameters.  More in
807general we cannot know a priori the kind of operations that will be
808supported by the user types of \gopt.  However, we want to design a set
809of tools and of functions that rely on certain operations. These include,
810for instance viewers, or functions to save/load the graph in a specific format.
811
812A possibility to do this would be to ``overload'' the base classes of
813the hyper-graph elements (vertices and edges) with many virtual
814functions, one for each of the functionality we want to support.  This
815is of course not elegant, because we would need to patch the base
816classes with the new function every time something new is added.
817Another possibility would be to make use of the multiple inheritance
818of C++, and to define an abstract ``drawable'' object, on which the
819viewer operates.  This solution is a bit better, however we cannot
820have more than one ``drawing'' function for each object.
821
822The solution used in \gopt{} consists in creating a library of
823function objects that operate on the elements (vertices or edges) of
824the graph.  One of these function objects is identified by a function
825name and by a type on which it operates.  These function objects can
826be registered into an action library.  Once these objects are loaded
827in the action library it is possible to call them on a graph.  These
828functionalities are defined in \verb+hyper_graph_action.h+.  It is
829common to register and create the actions when defining the types for
830the edges and the vertices.  You can see many examples in
831\verb+types_*/*.h+.
832
833\section{\gopt\ Tools}
834\gopt\ comes with two tools which allow to process data stored in
835files. The data can be loaded from a file and stored again after
836processing. In the following we will give a brief introduction to these
837tools, namely a command line interface and a graphical user interface.
838
839\subsection{\gopt\ Command Line Interface}
840
841\verb+g2o+ is the command line interface included in \gopt. It
842allows to optimize graphs stored in files and save the result back to a
843file. This allows a fast prototyping of optimization problems, as it is
844only required to implement the new types or solvers. The \gopt\
845distribution includes a data folder which comprises some data files on
846which \verb+g2o+ can be applied.
847
848\subsection{\gopt\ Viewer}
849
850\begin{figure}
851  \centering
852  \includegraphics[width=0.7\columnwidth]{pics/viewer}
853  \caption{Graphical interface to \gopt. The GUI allows to select
854  different suitable optimizers and perform the optimization.}
855  \label{fig:viewer}
856\end{figure}
857
858The Graphical User Interface depicted in Figure~\ref{fig:viewer} allows
859to visualize the optimization problem. Additionally, the various
860parameters of the algorithms can be controlled.
861
862\subsection{\gopt{} incremental}
863
864\gopt{} includes an experimental binary for performing optimization in
865an incremental fashion, i.e., optimizing after inserting one or several
866nodes along with their measurements. In this case, \gopt{} performs
867rank updates on the Hessian matrix to update the linear system. Please
868see the \verb+README+ in the \verb+g2o_incremental+ sub-folder for
869additional information.
870
871Example for the Manhattan3500 dataset:
872\begin{verbatim}
873g2o_incremental -i manhattanOlson3500.g2o
874\end{verbatim}
875
876\subsection{Plug-in Architecture}
877
878\begin{lstlisting}[float,label=lst:initsolvers,caption=Registering
879  solvers by a constructor from a library]
880class PCGSolverCreator : public AbstractOptimizationAlgorithmCreator
881{
882  public:
883    PCGSolverCreator(const OptimizationAlgorithmProperty& p) : AbstractOptimizationAlgorithmCreator(p) {}
884    virtual OptimizationAlgorithm* construct()
885    {
886      // create the optimization algorithm
887      // see g2o/solver_pcg/solver_pcg.cpp for the details
888    }
889};
890
891G2O_REGISTER_OPTIMIZATION_LIBRARY(pcg);
892
893G2O_REGISTER_OPTIMIZATION_ALGORITHM(gn_pcg, new PCGSolverCreator(OptimizationAlgorithmProperty("gn_pcg", "Gauss-Newton: PCG solver using block-Jacobi pre-conditioner (variable blocksize)", "PCG", false, Eigen::Dynamic, Eigen::Dynamic)));
894
895G2O_REGISTER_OPTIMIZATION_ALGORITHM(gn_pcg3_2, new PCGSolverCreator(OptimizationAlgorithmProperty("gn_pcg3_2", "Gauss-Newton: PCG solver using block-Jacobi pre-conditioner (fixed blocksize)", "PCG", true, 3, 2)));
896
897//...
898\end{lstlisting}
899
900Both tools support the loading of types and optimization algorithms at
901run-time from dynamic libraries. This is realized as follows. The tools
902load from the libs folder all libraries matching ``*\_types\_*'' and
903``*\_solver\_*'' to register types and optimization algorithms,
904respectively. We assume that by loading the libraries the types and the
905algorithms register via their respective constructors to the system.
906Listing~\ref{lst:inittypes} shows how to register types to the system
907and Listing~\ref{lst:initsolvers} is an example, which shows how to
908register an optimization algorithm via the plug-in architecture.
909
910For loading dynamic library containing types or optimization algorithms, we support two
911different methods:
912\begin{itemize}
913  \item The tools recognize the command line switch \verb+-typeslib+
914    and \verb+-solverlib+ to load a specific library.
915  \item You may specify the environment variables \verb+G2O_TYPES_DIR+
916    and \verb+G2O_SOLVER_DIR+ which are scanned at start and libraries
917    matching ``*\_types\_*'' and ``*\_solver\_*'' are automatically
918    loaded.
919\end{itemize}
920
921\section{2D SLAM: An Example}
922SLAM is a well known problem in robotics and this acronym stands for
923``Simultaneous Localization And Mapping''. The problem can be stated
924as follows: given a moving robot equipped with some sensors, we want
925to estimate both the map and the pose of the robot in the environment
926from the sensor measurements. Usually, the sensors can be classified
927in exteroceptive or proprioceptive.  An exteroceptive sensor is a
928device that measures quantities relative to the environment where the
929robot moves.  Examples of these sensors can be cameras that acquire an
930image of the world at a particular location, laser scanners that
931measure a set of distances around the robot or accelerometers in
932presence of gravity that measure the gravity vector or GPS that derive
933a pose estimate by observing the constellation of known satellites.
934In contrast, proprioceptive sensors measure the change of the robot's
935state (the position), relative to the previous robot position. Example
936include odometers, that measure the relative movement of the robot
937between two time steps or gyroscopes. In traditional approaches to
938SLAM, like EKF these two sensors play a substantially different role
939in the system.  The proprioceptive measurements are used to evolve a
940set of state variables, while the exteroceptive measurements are used
941to correct these estimates, by feeding back the measurement errors.
942This is not the case of smoothing methods (like the ones that can be
943implemented with \gopt{}), where all measurements are treated in a
944substantially similar manner.
945
946A complete solution to SLAM is typically rather complex and involves
947processing raw sensor data and determining correspondences between
948previously seen parts of the environment and actual measurements (data
949association). Describing a complete solution to the problem is out of
950the scope of this document.  However, in the reminder we will present a
951simplified but meaningful version of the problem that contains all the relevant elements
952and that is well suited to be implemented with \gopt.
953
954The scenario is a robot moving on a plane. The robot is equipped
955with an odometry sensor that is able to measure the relative movement
956of the robot between two time frames and of a ``landmark'' sensor
957that is able to measure the position of some environment landmarks
958nearby the robot \emph{in the robot reference frame}. One could
959implement this landmark detector, for instance, by extracting corners
960from a laser scan or by detecting the position of relevant features
961from a stereo image pair. A simplification that we make in this
962section is that the landmarks are uniquely identifiable. In other
963words whenever the robot sees a landmark, it can tell if it is a new
964one or if it has already seen it and when.
965
966Clearly both odometers and landmark sensors are affected by noise.  In
967principle, if the odometry would not be affected by noise one could
968reconstruct the trajectory of the robot simply by chaining the
969odometry measurements. However, this is not the case and integrating
970the odometry leads to an increasing positioning error that becomes
971evident when the robot reenters a known region. In a similar way,
972if the robot would have unlimited perception range, it could acquire
973all the map in one shot and the position could be retrieved by simple
974geometric constructions. Again this is not the case and the robot
975perceives the position of the landmarks that are located within a
976maximum range. These measurements are affected by a noise, that
977usually increases with the distance of a landmark from the robot.
978
979In the remainder of this section we will walk through all essential
980steps that are required to characterize a problem within \gopt{}.
981These are:
982\begin{itemize}
983\item identification of the state variables $\bx_i$ and of their domain,
984\item characterization of the constraints and identification of the graph structure,
985\item choice of the parameterization for the increments $\bTDeltax_i$, and definition of the
986$\boxplus$ operator.
987\item construction of the error functions $e_k(\bx_k)$.
988\end{itemize}
989
990\subsection{Identification of the State Variables}
991Figure~\ref{fig:slam} illustrates a fragment of a SLAM graph.  The
992robot positions are denoted by the nodes $\bx^\mathrm{s}_t$, while the
993landmarks are denoted by the nodes $\bx^\mathrm{l}_i$. We assume that
994our landmark sensor is able to detect only the 2D pose of a landmark,
995but not its orientation.  In other words the landmarks ``live'' in
996$\Re^2$.  Conversely, the robot poses are parameterized by the robot
997location $(x,y)$ on the plane and its orientation $\theta$, thus they
998belong to the group of 2D transformations $SE(2)$.  More
999formally, the nodes of a 2D SLAM graph are of two types
1000\begin{itemize}
1001\item Robot positions $\bx^\mathrm{s}_t = ( x^\mathrm{s}_t  \; y^\mathrm{s}_t \; \theta^\mathrm{s}_t)^T \in SE(2)$
1002\item Landmark positions $\bx^\mathrm{l}_i = ( x^\mathrm{l}_i  \; y^\mathrm{l}_i )^T \in \Re^2$
1003\end{itemize}
1004
1005\begin{figure}
1006\psfrag{p1}{$\bx^\mathrm{s}_1$}
1007\psfrag{p2}{$\bx^\mathrm{s}_2$}
1008\psfrag{pt1}{$\bx^\mathrm{s}_{t-1}$}
1009\psfrag{pt}{$\bx^\mathrm{s}_{t}$}
1010\psfrag{L1}{$\bx^\mathrm{l}_1$}
1011\psfrag{L2}{$\bx^\mathrm{l}_2$}
1012\psfrag{Li1}{$\bx^\mathrm{l}_{i-1}$}
1013\psfrag{Li}{$\bx^\mathrm{l}_{i}$}
1014\psfrag{u12}{$\bz^\mathrm{s}_{1,2}$}
1015\psfrag{utt1}{$\bz^\mathrm{s}_{t-1,t}$}
1016\psfrag{l11}{$\bz^\mathrm{l}_{1,1}$}
1017\psfrag{l12}{$\bz^\mathrm{l}_{1,2}$}
1018\psfrag{lit}{$\bz^\mathrm{l}_{t,i}$}
1019\psfrag{l1i}{$\bz^\mathrm{l}_{1,i}$}
1020\psfrag{lti1}{$\bz^\mathrm{l}_{t,i-1}$}
1021\psfrag{lt1i}{$\bz^\mathrm{l}_{t-1,i-1}$}
1022\centering
1023\includegraphics[width=0.8\columnwidth]{pics/slam.eps}
1024\caption{Graphical representation of a SLAM process. The vertices of
1025  the graph, depicted with circular nodes, denote either robot poses
1026  $\bx^\mathrm{s}_*$ or landmarks $\bx^\mathrm{l}_*$. The measurement
1027  of a landmark from a robot pose is captured by a constraints
1028  $\bz^{\mathrm{l}}_*$ and odometry measurements connecting subsequent
1029robot poses are modeled by the constraints $\bz^\mathrm{s}_*$.}
1030\label{fig:slam}
1031\end{figure}
1032
1033\subsection{Modeling of the Constraints}
1034Two subsequent robot positions $\bx^\mathrm{s}_t$ and
1035$\bx^\mathrm{s}_{t+1}$ are related by an odometry measurement, that
1036represent the relative motion that brings the robot from
1037$\bx^\mathrm{s}_t$ to $\bx^\mathrm{s}_{t+1}$ \emph{measured} by the
1038odometry. This measurement will be typically slightly different from
1039the \emph{real} transformation between the two pose because of the
1040noise affecting the sensors.  Being an odometry measurement, an
1041Euclidean transformation, it is also a member of $SE(2)$ group.
1042Assuming the noise affecting the measurement being white and Gaussian,
1043it can be modeled by an $3 \times 3$ symmetric positive definite
1044information matrix.  In real applications the entries of this matrix
1045depend on the motion of the robot.  i.e., the bigger the movement is
1046the larger the uncertainty will be.
1047Thus an odometry edge between the nodes $\bx^\mathrm{s}_t$ and
1048$\bx^\mathrm{s}_{t+1}$ consists of these two entities:
1049\begin{itemize}
1050\item $\bz^\mathrm{s}_{t,t+1} \in SE(2)$ that represents the motion
1051  between the nodes and
1052\item $\bOmega^\mathrm{s}_{t,t+1} \in \Re^{3 \times 3}$ that represents the
1053  inverse covariance of the measurement, and thus is symmetric and
1054  positive definite.
1055\end{itemize}
1056
1057If the robot senses a landmark $\bx^\mathrm{l}_i$ from the location
1058$\bx^\mathrm{s}_t$, the corresponding measurement will be modeled by
1059an edge going from the robot pose to the landmark. A measurement about
1060the landmark consists in a point in the $x$-$y$ plane, perceived in the
1061robot frame. Thus a landmark measurement lives in $\Re^2$ as the
1062landmarks do. Again, under white Gaussian noise assumption, the
1063noise can be modeled by its inverse covariance. Accordingly, an edge between
1064a robot pose and a landmark is parametrized in this way:
1065\begin{itemize}
1066\item $\bz^\mathrm{l}_{t,i} \in \Re^2$ that represents position of the landmark
1067  in the frame expressed by $\bx^\mathrm{s}_t$ and
1068\item $\bOmega^\mathrm{l}_{t,i} \in \Re^{2 \times 2}$ that represents the inverse
1069  covariance of the measurement and is SPD.
1070\end{itemize}
1071
1072\subsection{Choice of the Parameterization for the Increments}
1073So far, we defined most of the elements necessary to implement a 2D
1074SLAM algorithm with \gopt{}.  Namely we characterized the domains of
1075the variables and the domains of the measurements.  What remains to do
1076is to define the error functions for the two kinds of edges in our
1077system and to determine a (possibly smart) parameterization for the
1078increments.
1079
1080
1081The landmark positions are parameterized in $\Re^2$, which is already
1082an Euclidean space. Thus the increments $\bTDeltax^\mathrm{l}_i$ can live in the same space and the
1083 $\boxplus$ operator can be safely chosen as the vector sum:
1084\begin{eqnarray}
1085  \bx^\mathrm{l}_i \boxplus \bTDeltax^\mathrm{l}_i   & \doteq & \bx^\mathrm{l}_i + \bTDeltax^\mathrm{l}_i
1086\end{eqnarray}
1087
1088The poses, conversely, live in the non-Euclidean space $SE(2)$.
1089This space admits many parameterizations. Examples include:
1090rotation matrix $\bR(\theta)$ and translation vector $(x \; y)^T$ or
1091angle $\theta$ and translation vector $(x \; y)^T$.
1092
1093As a parameterization for the increments, we choose a minimal one,
1094that is translation vector and angle. Having chosen this
1095parameterization, we need to define the $\boxplus$ operator between a
1096pose and a pose increment.  One possible choice would be to treat the
1097three scalar parameters $x$, $y$ and $\theta$ of a pose as if they
1098were a vector, and define the $\boxplus$ as the vector sum.  There are
1099many reasons why this is a poor choice. One of them is that the angles
1100are not Euclidean, and one would need to re-normalize them after every
1101addition.
1102
1103A better choice is to define the $\boxplus$ between a pose and a pose
1104increment as the motion composition operator. Namely, given a robot
1105pose $\bx^{s}_t = (x\; y\; \theta)^T$ and an increment $\bTDeltax^{s}_t
1106= (\Delta x\; \Delta y\; \Delta \theta)^T$, where $\Delta x$ is the
1107longitudinal displacement (i.e. in direction of the heading of the robot),
1108$\Delta y$ the lateral displacement and $\Delta \theta$ the rotational
1109change, the operator can be defined as follows:
1110\begin{eqnarray}
1111    \bx^\mathrm{s}_t \boxplus \bTDeltax^\mathrm{s}_t   & \doteq &
1112    \left(
1113    \begin{array}{c}
1114      x + \Delta x \cos \theta - \Delta y \sin \theta \\
1115      y + \Delta x \sin \theta + \Delta y \cos \theta \\
1116      \mathrm{normAngle}(\theta + \Delta \theta)
1117    \end{array}
1118    \right)\\
1119    = \bx^\mathrm{s}_t \oplus \bTDeltax^\mathrm{s}_t.
1120\end{eqnarray}
1121
1122In  the previous equation we introduced the motion composition operator $\oplus$
1123Similarly to $\oplus$ there is the $\ominus$ operator that performs the opposite operation
1124and is defined as follows:
1125\begin{eqnarray}
1126    \bx^\mathrm{s}_a \ominus \bx^\mathrm{s}_b   & \doteq &
1127    \left(
1128    \begin{array}{c}
1129        (x_a - x_b)  \cos \theta_b + (y_a - y_b) \sin \theta_b \\
1130      - (x_a - x_b)  \sin \theta_b + (y_a - y_b) \cos \theta_b \\
1131      \mathrm{normAngle}(\theta_b - \theta_a)
1132    \end{array}
1133    \right)
1134\end{eqnarray}
1135
1136\subsection{Design of the Error Functions}
1137The last step in formalizing the problem is to design error functions
1138$\be(\bx_k)$ that are ``reasonable''. A common way to do this to define a
1139so-called \emph{measurement} function $\bh_k(\bx_k)$ that ``predicts''
1140a measurement $\hat \bz_k$, given the knowledge of the vertices in the
1141set $\bx_k$. Defining this function is usually rather easy, and can be
1142done by directly implementing the error model. Subsequently, the error
1143vector can be computed as the vector difference between the prediction
1144$\hat \bz_k$ and the real measurement.  This is a general approach to
1145construct error functions and it works when the space of the errors is
1146locally Euclidean around the origin of the measurement. If this is not
1147the case one might want to replace the vector difference with some
1148other operator which is more ``regular''.
1149
1150We will now construct the error functions for the edges connecting a
1151robot pose $\bx^\mathrm{s}_t$ and a landmark $\bx^\mathrm{l}_i$.  The
1152first step is to construct a measurement prediction
1153$\bh^\mathrm{l}_{t,i}(\bx^\mathrm{s}_t, \bx^\mathrm{l}_i)$ that computes
1154a ``virtual measurement''. This virtual measurement is the position of
1155the landmark $\bx^\mathrm{l}_i$, seen from the robot position
1156$\bx^\mathrm{s}_t$. The equation for $\bh^\mathrm{l}_{t,i}(\cdot)$ is the
1157following:
1158\begin{eqnarray}
1159    \bh^\mathrm{l}_{t,i}(\bx^\mathrm{s}_t, \bx^\mathrm{l}_i)   & \doteq &
1160    \left(
1161    \begin{array}{c}
1162        (x^\mathrm{s}_t - x_i)  \cos \theta^\mathrm{s}_t + (y^\mathrm{s}_t - y_i) \sin \theta^\mathrm{s}_t \\
1163      - (x^\mathrm{s}_t - x_i)  \sin \theta^\mathrm{s}_t + (y^\mathrm{s}_t - y_i) \cos \theta^\mathrm{s}_t
1164    \end{array}
1165    \right)
1166\end{eqnarray}
1167which converts the position of the landmark into the coordinate system of
1168the robot.
1169
1170Since the landmarks live in an Euclidean space, it is reasonable to
1171compute the error function as the normal vector difference.  This
1172leads to the following definition for the error functions of the
1173landmarks.
1174\begin{eqnarray}
1175    \be^\mathrm{l}_{t,i}(\bx^\mathrm{s}_t, \bx^\mathrm{l}_i)   & \doteq & \bz_{t,i} - \bh^\mathrm{l}_{t,i}(\bx^\mathrm{s}_t, \bx^\mathrm{l}_i).
1176    \label{eq:landmarkError}
1177\end{eqnarray}
1178
1179In a similar way, we can define the error functions of an odometry
1180edge connecting two robot poses $\bx^\mathrm{s}_t$ and
1181$\bx^\mathrm{s}_{t+1}$. As stated before, an odometry measurement lives in $SE(2)$.
1182By using the $\oplus$ operator we can write a synthetic measurement function:
1183\begin{eqnarray}
1184    \bh^\mathrm{s}_{t,t+1}(\bx^\mathrm{s}_t, \bx^\mathrm{s}_{t+1})   & \doteq & \bx^\mathrm{s}_{t+1} \ominus \bx^\mathrm{s}_{t}.
1185\end{eqnarray}
1186In short this function returns the motion that brings the robot from
1187$\bx^\mathrm{s}_t$ to $\bx^\mathrm{s}_{t+1}$, that is the ``ideal''
1188odometry.  Once again the error can be obtained as a difference
1189between the measurement and the prediction. However, since our measurements
1190do not live in an Euclidean space we can use $\ominus$ instead of the vector difference.
1191
1192\begin{eqnarray}
1193    \be^\mathrm{s}_{t,t+1}(\bx^\mathrm{s}_t, \bx^\mathrm{s}_{t+1})   & \doteq & \bz_{t,t+1} \ominus \bh^\mathrm{s}_{t,t+1}(\bx^\mathrm{s}_t, \bx^\mathrm{s}_{t+1}).
1194    \label{eq:odometryError}
1195\end{eqnarray}
1196
1197\subsection{Putting things together}
1198Here we summarize the relevant parts of the previous problem definition,
1199and we get ready for the implementation.\\[.3em]
1200{\small
1201\begin{tabular}{|c|c|c|c|c|c|}
1202\hline
1203\textbf{Variable} & \textbf{Symbol}  & \textbf{Domain} & \textbf{Dimension} & \textbf{Parameterization of }$\bDeltax$   & $\boxplus$ \textbf{operator} \\
1204\hline
1205Robot    pose     & $\bx^\mathrm{s}_t$ & $SE(2)$         & 3                  & $(\Delta x \; \Delta y \; \Delta\theta)$  & $\bx^\mathrm{s}_t \oplus \bDeltax^\mathrm{s}_t$ \\
1206\hline
1207Landmark pose     & $\bx^\mathrm{l}_i$ & $\Re^2$         & 2                  & $(\Delta x \; \Delta y )$                 & $\bx^\mathrm{l}_i + \bDeltax^\mathrm{l}_i$\\
1208\hline
1209\end{tabular}\\[.3em]
1210\begin{tabular}{|c|c|c|c|c|c|}
1211\hline
1212\textbf{Measurement} & \textbf{Symbol}         & \textbf{Domain} & \textbf{Dimension} & \textbf{Set $\bx_k$ of variables involved}             & \textbf{error function}\\
1213\hline
1214Odometry             & = $\bz^\mathrm{s}_{t,t+1}$ & $SE(2)$         & 3                  & $\left\{ \bx^\mathrm{s}_t, \bx^\mathrm{s}_{t+1} \right\}$ & Eq.~\ref{eq:odometryError} \\
1215\hline
1216Landmark             & = $\bz^\mathrm{l}_{t,i}$   & $\Re^2$         & 2                  & $\left\{ \bx^\mathrm{s}_t, \bx^\mathrm{l}_{i} \right\}$  & Eq.~\ref{eq:landmarkError} \\
1217\hline
1218\end{tabular}}\\[.3em]
1219
1220The first thing we are going to do is to implement a class that
1221represents elements of the $SE(2)$ group. We represent these elements
1222internally by using the rotation matrix and the translation vector
1223representation, via the types defined in \verb+Eigen::Geometry+.  Thus
1224we define an \verb+operator*(...)+ that implements the motion composition
1225operator $\oplus$, and an \verb+inverse()+ function that returns the inverse of
1226a transformation. For convenience we also implement an
1227\verb+operator*+ that transforms 2D points. To convert the elements
1228from and to a minimal representation that utilizes an \verb+Eigen::Vector3d+
1229we define the methods \verb+fromVector(...)+ and \verb+toVector(...)+.
1230The constructor initializes this class as a point in the origin
1231oriented at 0 degrees.  Note that having a separate class for a group
1232is not mandatory in \gopt, but makes the code much more readable and
1233reusable. The corresponding C++ class is reported in
1234Listing~\ref{lst:se2}.
1235\begin{lstlisting}[float,label=lst:se2,caption=\text{Helper class to represent $SE(2)$}.]
1236class SE2 {
1237  public:
1238    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
1239    SE2():_R(0),_t(0,0){}
1240
1241    SE2(double x, double y, double theta):_R(theta),_t(x,y){}
1242
1243    SE2 operator * (const SE2& tr2) const{
1244      SE2 result(*this);
1245      result._t += _R*tr2._t;
1246      result._R.angle()+= tr2._R.angle();
1247      result._R.angle()=normalize_theta(result._R.angle());
1248      return result;
1249    }
1250
1251    Vector2d operator * (const Vector2d& v2) const{
1252      Vector2d result(*this);
1253      result._t = _t + _R*tr2._t;
1254      return result;
1255    }
1256
1257    SE2 inverse() const{
1258      SE2 ret;
1259      ret._R=_R.inverse();
1260      ret._R.angle()=normalize_theta(ret._R.angle());
1261      ret._t=ret._R*(_t*-1.);
1262      return ret;
1263    }
1264
1265    void fromVector (const Vector3d& v){
1266      *this=SE2(v[0], v[1], v[2]);
1267    }
1268
1269    Vector3d toVector() const {
1270      Vector3d ret;
1271      for (int i=0; i<3; i++){
1272        ret(i)=(*this)[i];
1273      }
1274      return ret;
1275    }
1276
1277  protected:
1278    Rotation2Dd _R;
1279    Vector2d _t;
1280};
1281\end{lstlisting}
1282
1283Once we defined our nice $SE(2)$ group we are ready to implement the
1284vertices.  To this end we extend the \verb+BaseVertex<>+ class, and we
1285derive the classes \verb+VertexSE2+ to represent a robot pose and
1286\verb+VertexPointXY+ to represent a point landmark in the plane.
1287The class definition for robot pose vertices is reported in
1288Listing~\ref{lst:vertexse2}.
1289The pose-vertex extends a template specialization of
1290\verb+BaseVertex<>+.  We should say to \gopt{} that the internal type
1291has dimension 3 and that the estimate is of type \verb+SE2+. This
1292means that the member \verb+_estimate+ of a \verb+VertexSE2+ is of
1293type \verb+SE2+.  Then all we need to do is to redefine the methods
1294\verb+setToOriginImpl()+ that resets the estimate to a known
1295configuration, and the method \verb+\oplusImpl(double*)+.  The method
1296should apply an increment, expressed in the increment parameterization
1297(that is a vector $(\Delta x \; \Delta y \; \Delta \theta )^t$ to the
1298current estimate.  To do this, we first convert the vector passed as
1299argument into an \verb+SE(2)+, then we multiply this increment at the
1300right of the previous estimate.  After that we should implement the
1301read and write functions to a stream, but this is straight-forward and
1302you can look it up yourself in the code.
1303
1304\begin{lstlisting}[float,label=lst:vertexse2,caption=Vertex representing
1305  a 2D robot pose]
1306class VertexSE2 : public BaseVertex<3, SE2>
1307{
1308  public:
1309    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
1310    VertexSE2();
1311
1312    virtual void setToOriginImpl() {
1313      _estimate=SE2();
1314    }
1315
1316    virtual void oplusImpl(double* update)
1317    {
1318      SE2 up(update[0], update[1], update[2]);
1319      _estimate = _estimate * up;
1320    }
1321
1322    virtual bool read(std::istream& is);
1323    virtual bool write(std::ostream& os) const;
1324
1325};
1326\end{lstlisting}
1327The next step is to implement a vertex to describe a landmark
1328position.  Since the landmarks are parameterized in $\Re^2$, we do not
1329need to define any group for that, and we use directly the vector
1330classes defined in Eigen. This class is reported in Listing~\ref{lst:vertexxy}.
1331
1332\begin{lstlisting}[float,label=lst:vertexxy,caption=Vertex representing
1333  a 2D landmark]
1334class VertexPointXY : public BaseVertex<2, Eigen::Vector2d>
1335{
1336  public:
1337    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
1338      VertexPointXY();
1339
1340    virtual void setToOriginImpl() {
1341      _estimate.setZero();
1342    }
1343
1344    virtual void oplusImpl(double* update)
1345    {
1346      _estimate[0] += update[0];
1347      _estimate[1] += update[1];
1348    }
1349
1350    virtual bool read(std::istream& is);
1351    virtual bool write(std::ostream& os) const;
1352};
1353\end{lstlisting}
1354
1355Now we are done with the vertices. We should go for the edges.  Since
1356both edges are binary edges we, extend the class
1357\verb+BaseBinaryEdge<>+.
1358To represent an odometry edge (see Listing~\ref{lst:edgese2}) that
1359connects two \verb+VertexSE2+, we need to extend
1360\verb+BaseBinaryEdge<>+,  specialized with the types of the
1361connected vertices (the order matters), where the measurement itself
1362is represented by an \verb+SE2+ that has dimension 3. The second
1363template parameter is the one used for the member variable
1364\verb+_measurement+.  The second step is to construct an error
1365function, by redefining the \verb+computeError()+ method.  The
1366\verb+computeError()+ should put the error vector in a member variable
1367\verb+error+ that has type \verb+Eigen::Vector<double,3>+. Here the 3
1368comes from the template parameter that specifies the dimension.
1369Again, the read and write functions can be looked up in the code.
1370Now we are done with this edge. The Jacobians are computed numerically
1371by \gopt{}.  However, if you want to speed up the execution of your
1372code after everything works, you are warmly invited to redefine the
1373\verb+linearizeOplus+ method.
1374
1375\begin{lstlisting}[float,label=lst:edgese2,caption=\text{Edge connecting two
1376  robot poses, for example, the odometry of the robot.}]
1377class EdgeSE2 : public BaseBinaryEdge<3, SE2, VertexSE2, VertexSE2>
1378{
1379  public:
1380    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
1381      EdgeSE2();
1382
1383    void computeError()
1384    {
1385      const VertexSE2* v1 = static_cast<const VertexSE2*>(_vertices[0]);
1386      const VertexSE2* v2 = static_cast<const VertexSE2*>(_vertices[1]);
1387      SE2 delta = _measurement.inverse() * (v1->estimate().inverse()*v2->estimate());
1388      _error = delta.toVector();
1389    }
1390    virtual bool read(std::istream& is);
1391    virtual bool write(std::ostream& os) const;
1392};
1393\end{lstlisting}
1394
1395The last thing that remains to do is to define a class to represent a
1396landmark measurement.  This is shown in Listing~\ref{lst:edgese2xy}.
1397Again, we extend a specialization of \verb+BaseBinaryEdge<>+, and we
1398tell the system that it connects a \verb+VertexSE2+ with a
1399\verb+VertexPointXY+, that the measurement is represented by an
1400\verb+Eigen::Vector2d+ that has dimension 2.
1401
1402\begin{lstlisting}[float,label=lst:edgese2xy,caption=Edge connecting a
1403  robot poses and a landmark.]
1404class EdgeSE2PointXY : public BaseBinaryEdge<2, Eigen::Vector2d, VertexSE2, VertexPointXY>
1405{
1406  public:
1407    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
1408      EdgeSE2PointXY();
1409
1410    void computeError()
1411    {
1412      const VertexSE2* v1 = static_cast<const VertexSE2*>(_vertices[0]);
1413      const VertexPointXY* l2 = static_cast<const VertexPointXY*>(_vertices[1]);
1414      _error = (v1->estimate().inverse() * l2->estimate()) - _measurement;
1415    }
1416
1417    virtual bool read(std::istream& is);
1418    virtual bool write(std::ostream& os) const;
1419
1420};
1421\end{lstlisting}
1422The final step we should do to make our system operational, is to
1423register the types to let \gopt{} know that there are new types ready.
1424However, if you intend to manually construct your graph without doing
1425any i/o operation on disk, this step is not even necessary. Have fun!
1426
1427You may find the full code of this 2D SLAM example in the folder
1428\texttt{examples/tutorials\_slam2d}.
1429
1430{\small
1431\bibliographystyle{unsrt}
1432\bibliography{robots}}
1433
1434\end{document}
1435