g2o
Loading...
Searching...
No Matches
polynomial_fit.cpp
Go to the documentation of this file.
1// This example illustrates how to use a dynamic vertex in a graph.
2
3// The goal is to fit a polynomial y(x)=p(x) to a set of data. The degree of the
4// polynomial is user-defined. The amount of samples is user defined as well.
5
6// Each observation consists of the pair Z_i=(x_i,z_i) where
7// z_i=y(x_i)+w_i, where w_i is additive white noise with information
8// matrix Omega.
9
10#include <unsupported/Eigen/Polynomials>
11
19#include "g2o/stuff/sampler.h"
20
21// Declare the custom types used in the graph
22
23// This vertex stores the coefficients of the polynomial. It is dynamic because
24// we can change it at runtime.
25
27 : public g2o::BaseDynamicVertex<Eigen::VectorXd> {
28 public:
30
31 // Create the vertex
33
34 // Read the vertex
35 virtual bool read(std::istream& is) {
36 // Read the dimension
37 int dimension;
38 is >> dimension;
39 if (is.good() == false) {
40 return false;
41 }
42
43 // Set the dimension; we call the method here to ensure stuff like
44 // cache and the workspace is setup
46
47 // Read the state
49 }
50
51 // Write the vertex
52 virtual bool write(std::ostream& os) const {
53 os << _estimate.size() << " ";
55 }
56
57 // Reset to zero
58 virtual void setToOriginImpl() { _estimate.setZero(); }
59
60 // Direct linear add
61 virtual void oplusImpl(const double* update) {
62 Eigen::VectorXd::ConstMapType v(update, _dimension);
63 _estimate += v;
64 }
65
66 // Resize the vertex state. In this case, we want to preserve as much of the
67 // state as we can. Therefore, we use conservativeResize and pad with zeros
68 // at the end if the state dimension has increased.
69 virtual bool setDimensionImpl(int newDimension) {
70 int oldDimension = dimension();
71
72 // Handle the special case this is the first time
73 if (oldDimension == Eigen::Dynamic) {
74 _estimate.resize(newDimension);
75 _estimate.setZero();
76 return true;
77 }
78
79 _estimate.conservativeResize(newDimension);
80
81 // If the state has expanded, pad with zeros
82 if (oldDimension < newDimension)
83 _estimate.tail(newDimension - oldDimension).setZero();
84
85 return true;
86 }
87};
88
89// This edge provides an observation of the polynomial at a single value.
90// The assumed model is z = p(x) + w,
91// where w is the additive noise with covariance equal to inv(Omega).
92
93// Note that x is not a measurement so it has to be stored separately.
94
96 : public g2o::BaseUnaryEdge<1, double, PolynomialCoefficientVertex> {
97 public:
99
101 double x, double z,
103 : _x(x) {
104 _x = x;
106 setInformation(omega);
107 }
108
109 virtual bool read(std::istream& is) {
110 double z;
111 is >> _x >> z;
113 return readInformationMatrix(is);
114 }
115
116 virtual bool write(std::ostream& os) const {
117 os << _x << " " << _measurement;
118 return writeInformationMatrix(os);
119 }
120
121 // Compute the measurement from the eigen polynomial module
122 virtual void computeError() {
124 static_cast<const PolynomialCoefficientVertex*>(_vertices[0]);
125 _error[0] = _measurement - Eigen::poly_eval(vertex->estimate(), _x);
126 }
127
128 private:
129 // The point that the polynomial is computed at
130 double _x;
131};
132
133int main(int argc, const char* argv[]) {
134 // Number of dimensions of the polynomial; the default is 4
135 int polynomialDimension = 4;
136 if (argc > 1) {
137 polynomialDimension = atoi(argv[1]);
138 }
139
140 // Create the coefficients for the polynomial (all drawn randomly)
141 Eigen::VectorXd p(polynomialDimension);
142 for (int i = 0; i < polynomialDimension; ++i) {
143 p[i] = g2o::sampleUniform(-1, 1);
144 }
145
146 std::cout << "Ground truth vector=" << p.transpose() << std::endl;
147
148 // The number of observations in the polynomial; the default is 6
149 int obs = 6;
150 if (argc > 2) {
151 obs = atoi(argv[2]);
152 }
153
154 // Sample the observations; we don't do anything with them here, but
155 // they could be plotted
156 double sigmaZ = 0.1;
157 Eigen::VectorXd x(obs);
158 Eigen::VectorXd z(obs);
159
160 for (int i = 0; i < obs; ++i) {
161 x[i] = g2o::sampleUniform(-5, 5);
162 z[i] = Eigen::poly_eval(p, x[i]) + sigmaZ * g2o::sampleGaussian();
163 }
164
165 // Construct the graph and set up the solver and optimiser
166 std::unique_ptr<g2o::BlockSolverX::LinearSolverType> linearSolver =
167 std::make_unique<
169
170 // Set up the solver
171 std::unique_ptr<g2o::BlockSolverX> blockSolver =
172 std::make_unique<g2o::BlockSolverX>(std::move(linearSolver));
173
174 // Set up the optimisation algorithm
175 g2o::OptimizationAlgorithm* optimisationAlgorithm =
176 new g2o::OptimizationAlgorithmLevenberg(std::move(blockSolver));
177
178 // Create the graph and configure it
179 std::unique_ptr<g2o::SparseOptimizer> optimizer =
180 std::make_unique<g2o::SparseOptimizer>();
181 optimizer->setVerbose(true);
182 optimizer->setAlgorithm(optimisationAlgorithm);
183
184 // Create the vertex; note its dimension is currently is undefined
186 pv->setId(0);
187 optimizer->addVertex(pv);
188
189 // Create the information matrix
191 PolynomialSingleValueEdge::InformationType::Zero();
192 omega(0, 0) = 1 / (sigmaZ * sigmaZ);
193
194 // Create the observations and the edges
195 for (int i = 0; i < obs; ++i) {
197 new PolynomialSingleValueEdge(x[i], z[i], omega);
198 pe->setVertex(0, pv);
199 optimizer->addEdge(pe);
200 }
201
202 // Now run the same optimization problem for different choices of
203 // dimension of the polynomial vertex. This shows how we can
204 // dynamically change the vertex dimensions in an alreacy
205 // constructed graph. Note that you must call initializeOptimization
206 // before you can optimize after a state dimension has changed.
207 for (int testDimension = 1; testDimension <= polynomialDimension;
208 ++testDimension) {
209 pv->setDimension(testDimension);
210 optimizer->initializeOptimization();
211 optimizer->optimize(10);
212 std::cout << "Computed parameters = " << pv->estimate().transpose()
213 << std::endl;
214 }
215 for (int testDimension = polynomialDimension - 1; testDimension >= 1;
216 --testDimension) {
217 pv->setDimension(testDimension);
218 optimizer->initializeOptimization();
219 optimizer->optimize(10);
220 std::cout << "Computed parameters = " << pv->estimate().transpose()
221 << std::endl;
222 }
223}
virtual void setToOriginImpl()
sets the node to the origin (used in the multilevel stuff)
virtual void oplusImpl(const double *update)
virtual bool read(std::istream &is)
read the vertex from a stream, i.e., the internal state of the vertex
virtual bool setDimensionImpl(int newDimension)
virtual bool write(std::ostream &os) const
write the vertex to a stream
PolynomialSingleValueEdge(double x, double z, const PolynomialSingleValueEdge::InformationType &omega)
virtual bool write(std::ostream &os) const
write the vertex to a stream
virtual bool read(std::istream &is)
read the vertex from a stream, i.e., the internal state of the vertex
virtual bool setDimension(int newDimension)
bool writeInformationMatrix(std::ostream &os) const
write the upper trinagular part of the information matrix into the stream
Definition base_edge.h:165
bool readInformationMatrix(std::istream &is)
Definition base_edge.h:173
virtual void setMeasurement(const Measurement &m)
Definition base_edge.h:122
void setInformation(const InformationType &information)
Definition base_edge.h:111
internal::BaseEdgeTraits< D >::InformationType InformationType
Definition base_edge.h:91
Measurement _measurement
the measurement of the edge
Definition base_edge.h:146
ErrorVector _error
Definition base_edge.h:149
const EstimateType & estimate() const
return the current estimate of the vertex
EstimateType _estimate
void setVertex(size_t i, Vertex *v)
VertexContainer _vertices
const Vertex * vertex(size_t i) const
linear solver which uses the sparse Cholesky solver from Eigen
int dimension() const
dimension of the estimated state belonging to this node
Implementation of the Levenberg Algorithm.
Generic interface for a non-linear solver operating on a graph.
int main()
Definition gicp_demo.cpp:44
bool writeVector(std::ostream &os, const Eigen::DenseBase< Derived > &b)
Definition io_helper.h:36
bool readVector(std::istream &is, Eigen::DenseBase< Derived > &b)
Definition io_helper.h:42
double sampleUniform(double min, double max, std::mt19937 *generator)
Definition sampler.cpp:35
double sampleGaussian(std::mt19937 *generator)
Definition sampler.cpp:40