g2o
circle_fit.cpp
Go to the documentation of this file.
1 // g2o - General Graph Optimization
2 // Copyright (C) 2012 R. Kümmerle
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18 // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 #include <Eigen/Core>
28 #include <Eigen/StdVector>
29 #include <Eigen/Geometry>
30 #include <iostream>
31 
32 #include "g2o/stuff/sampler.h"
33 #include "g2o/stuff/command_args.h"
35 #include "g2o/core/block_solver.h"
36 #include "g2o/core/solver.h"
39 #include "g2o/core/base_vertex.h"
42 
43 using namespace std;
44 
45 double errorOfSolution(int numPoints, Eigen::Vector2d* points, const Eigen::Vector3d& circle)
46 {
47  Eigen::Vector2d center = circle.head<2>();
48  double radius = circle(2);
49  double error = 0.;
50  for (int i = 0; i < numPoints; ++i) {
51  double d = (points[i] - center).norm() - radius;
52  error += d*d;
53  }
54  return error;
55 }
56 
60 class VertexCircle : public g2o::BaseVertex<3, Eigen::Vector3d>
61 {
62  public:
65  {
66  }
67 
68  virtual bool read(std::istream& /*is*/)
69  {
70  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
71  return false;
72  }
73 
74  virtual bool write(std::ostream& /*os*/) const
75  {
76  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
77  return false;
78  }
79 
80  virtual void setToOriginImpl()
81  {
82  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
83  }
84 
85  virtual void oplusImpl(const double* update)
86  {
87  Eigen::Vector3d::ConstMapType v(update);
88  _estimate += v;
89  }
90 };
91 
99 class EdgePointOnCircle : public g2o::BaseUnaryEdge<1, Eigen::Vector2d, VertexCircle>
100 {
101  public:
102  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
104  {
105  }
106  virtual bool read(std::istream& /*is*/)
107  {
108  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
109  return false;
110  }
111  virtual bool write(std::ostream& /*os*/) const
112  {
113  cerr << __PRETTY_FUNCTION__ << " not implemented yet" << endl;
114  return false;
115  }
116 
118  {
119  const VertexCircle* circle = static_cast<const VertexCircle*>(vertex(0));
120 
121  const Eigen::Vector2d& center = circle->estimate().head<2>();
122  const double& radius = circle->estimate()(2);
123 
124  _error(0) = (measurement() - center).norm() - radius;
125  }
126 };
127 
128 int main(int argc, char** argv)
129 {
130  int numPoints;
131  int maxIterations;
132  bool verbose;
133  std::vector<int> gaugeList;
134  g2o::CommandArgs arg;
135  arg.param("numPoints", numPoints, 100, "number of points sampled from the circle");
136  arg.param("i", maxIterations, 10, "perform n iterations");
137  arg.param("v", verbose, false, "verbose output of the optimization process");
138 
139  arg.parseArgs(argc, argv);
140 
141  // generate random data
142  Eigen::Vector2d center(4.0, 2.0);
143  double radius = 2.0;
144  Eigen::Vector2d* points = new Eigen::Vector2d[numPoints];
145 
147  for (int i = 0; i < numPoints; ++i) {
148  double r = g2o::Sampler::gaussRand(radius, 0.05);
149  double angle = g2o::Sampler::uniformRand(0.0, 2.0 * M_PI);
150  points[i].x() = center.x() + r * cos(angle);
151  points[i].y() = center.y() + r * sin(angle);
152  }
153 
154  // some handy typedefs
157 
158  // setup the solver
159  g2o::SparseOptimizer optimizer;
160  optimizer.setVerbose(false);
161  MyLinearSolver* linearSolver = new MyLinearSolver();
162  MyBlockSolver* solver_ptr = new MyBlockSolver(linearSolver);
164  //g2o::OptimizationAlgorithmGaussNewton* solver = new g2o::OptimizationAlgorithmGaussNewton(solver_ptr);
165  optimizer.setAlgorithm(solver);
166 
167  // build the optimization problem given the points
168  // 1. add the circle vertex
169  VertexCircle* circle = new VertexCircle();
170  circle->setId(0);
171  circle->setEstimate(Eigen::Vector3d(3.0, 3.0, 3.0)); // some initial value for the circle
172  optimizer.addVertex(circle);
173  // 2. add the points we measured
174  for (int i = 0; i < numPoints; ++i) {
176  e->setInformation(Eigen::Matrix<double, 1, 1>::Identity());
177  e->setVertex(0, circle);
178  e->setMeasurement(points[i]);
179  optimizer.addEdge(e);
180  }
181 
182  // perform the optimization
183  optimizer.initializeOptimization();
184  optimizer.setVerbose(verbose);
185  optimizer.optimize(maxIterations);
186 
187  if (verbose)
188  cout << endl;
189 
190  // print out the result
191  cout << "Iterative least squares solution" << endl;
192  cout << "center of the circle " << circle->estimate().head<2>().transpose() << endl;
193  cout << "radius of the cirlce " << circle->estimate()(2) << endl;
194  cout << "error " << errorOfSolution(numPoints, points, circle->estimate()) << endl;
195  cout << endl;
196 
197  // solve by linear least squares
198  // Let (a, b) be the center of the circle and r the radius of the circle.
199  // For a point (x, y) on the circle we have:
200  // (x - a)^2 + (y - b)^2 = r^2
201  // This leads to
202  // (-2x -2y 1)^T * (a b c) = -x^2 - y^2 (1)
203  // where c = a^2 + b^2 - r^2.
204  // Since we have a bunch of points, we accumulate Eqn (1) in a matrix and
205  // compute the normal equation to obtain a solution for (a b c).
206  // Afterwards the radius r is recovered.
207  Eigen::MatrixXd A(numPoints, 3);
208  Eigen::VectorXd b(numPoints);
209  for (int i = 0; i < numPoints; ++i) {
210  A(i, 0) = -2*points[i].x();
211  A(i, 1) = -2*points[i].y();
212  A(i, 2) = 1;
213  b(i) = -pow(points[i].x(), 2) - pow(points[i].y(), 2);
214  }
215  Eigen::Vector3d solution = (A.transpose()*A).ldlt().solve(A.transpose() * b);
216  // calculate the radius of the circle given the solution so far
217  solution(2) = sqrt(pow(solution(0), 2) + pow(solution(1), 2) - solution(2));
218  cout << "Linear least squares solution" << endl;
219  cout << "center of the circle " << solution.head<2>().transpose() << endl;
220  cout << "radius of the cirlce " << solution(2) << endl;
221  cout << "error " << errorOfSolution(numPoints, points, solution) << endl;
222 
223  // clean up
224  delete[] points;
225 
226  return 0;
227 }
virtual void oplusImpl(const double *update)
Definition: circle_fit.cpp:85
#define __PRETTY_FUNCTION__
Definition: macros.h:89
Command line parsing of argc and argv.
Definition: command_args.h:46
virtual void setToOriginImpl()
sets the node to the origin (used in the multilevel stuff)
Definition: circle_fit.cpp:80
static double gaussRand(double mean, double sigma)
Definition: sampler.h:86
Implementation of the Levenberg Algorithm.
static void seedRand()
Definition: sampler.h:107
int optimize(int iterations, bool online=false)
measurement for a point on the circle
Definition: circle_fit.cpp:99
Templatized BaseVertex.
Definition: base_vertex.h:50
virtual void setMeasurement(const Measurement &m)
Definition: base_edge.h:76
int main(int argc, char **argv)
Definition: circle_fit.cpp:128
void setVertex(size_t i, Vertex *v)
Definition: hyper_graph.h:194
virtual void setId(int id)
sets the id of the node in the graph be sure that the graph keeps consistent after changing the id ...
static double uniformRand(double lowerBndr, double upperBndr)
Definition: sampler.h:100
bool parseArgs(int argc, char **argv, bool exitOnError=true)
a circle located at x,y with radius r
Definition: circle_fit.cpp:60
void setVerbose(bool verbose)
linear solver which uses CSparse
void setAlgorithm(OptimizationAlgorithm *algorithm)
EIGEN_STRONG_INLINE void setInformation(const InformationType &information)
Definition: base_edge.h:69
virtual bool write(std::ostream &) const
write the vertex to a stream
Definition: circle_fit.cpp:111
void param(const std::string &name, bool &p, bool defValue, const std::string &desc)
void setEstimate(const EstimateType &et)
set the estimate for the vertex also calls updateCache()
Definition: base_vertex.h:101
virtual bool read(std::istream &)
read the vertex from a stream, i.e., the internal state of the vertex
Definition: circle_fit.cpp:106
const EstimateType & estimate() const
return the current estimate of the vertex
Definition: base_vertex.h:99
Implementation of a solver operating on the blocks of the Hessian.
Definition: block_solver.h:96
virtual bool initializeOptimization(HyperGraph::EdgeSet &eset)
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
Definition: circle_fit.cpp:63
double errorOfSolution(int numPoints, Eigen::Vector2d *points, const Eigen::Vector3d &circle)
Definition: circle_fit.cpp:45
EIGEN_MAKE_ALIGNED_OPERATOR_NEW EdgePointOnCircle()
Definition: circle_fit.cpp:103
virtual bool addEdge(HyperGraph::Edge *e)
virtual bool addVertex(HyperGraph::Vertex *v, Data *userData)
virtual bool write(std::ostream &) const
write the vertex to a stream
Definition: circle_fit.cpp:74
#define M_PI
Definition: misc.h:34
virtual bool read(std::istream &)
read the vertex from a stream, i.e., the internal state of the vertex
Definition: circle_fit.cpp:68