g2o
simulator.cpp
Go to the documentation of this file.
1 // g2o - General Graph Optimization
2 // Copyright (C) 2011 R. Kuemmerle, G. Grisetti, W. Burgard
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 "simulator.h"
28 
29 #include "rand.h"
30 
31 #include <map>
32 #include <iostream>
33 #include <cmath>
34 using namespace std;
35 
36 namespace g2o {
37  namespace tutorial {
38 
39  using namespace Eigen;
40 
41 # ifdef _MSC_VER
42  inline double round(double number)
43  {
44  return number < 0.0 ? ceil(number - 0.5) : floor(number + 0.5);
45  }
46 # endif
47 
48  typedef std::map<int, std::map<int, Simulator::LandmarkPtrVector> > LandmarkGrid;
49 
51  {
52  time_t seed = time(0);
53  Rand::seed_rand(static_cast<unsigned int>(seed));
54  }
55 
56  Simulator::~Simulator()
57  {
58  }
59 
60  void Simulator::simulate(int numNodes, const SE2& sensorOffset)
61  {
62  // simulate a robot observing landmarks while travelling on a grid
63  int steps = 5;
64  double stepLen = 1.0;
65  int boundArea = 50;
66 
67  double maxSensorRangeLandmarks = 2.5 * stepLen;
68 
69  int landMarksPerSquareMeter = 1;
70  double observationProb = 0.8;
71 
72  int landmarksRange=2;
73 
74  Vector2d transNoise(0.05, 0.01);
75  double rotNoise = DEG2RAD(2.);
76  Vector2d landmarkNoise(0.05, 0.05);
77 
78  Vector2d bound(boundArea, boundArea);
79 
80  VectorXd probLimits;
81  probLimits.resize(MO_NUM_ELEMS);
82  for (int i = 0; i < probLimits.size(); ++i)
83  probLimits[i] = (i + 1) / (double) MO_NUM_ELEMS;
84 
85  Matrix3d covariance;
86  covariance.fill(0.);
87  covariance(0, 0) = transNoise[0]*transNoise[0];
88  covariance(1, 1) = transNoise[1]*transNoise[1];
89  covariance(2, 2) = rotNoise*rotNoise;
90  Matrix3d information = covariance.inverse();
91 
92  SE2 maxStepTransf(stepLen * steps, 0, 0);
93  Simulator::PosesVector& poses = _poses;
94  poses.clear();
95  LandmarkVector& landmarks = _landmarks;
96  landmarks.clear();
97  Simulator::GridPose firstPose;
98  firstPose.id = 0;
99  firstPose.truePose = SE2(0,0,0);
100  firstPose.simulatorPose = SE2(0,0,0);
101  poses.push_back(firstPose);
102  cerr << "Simulator: sampling nodes ...";
103  while ((int)poses.size() < numNodes) {
104  // add straight motions
105  for (int i = 1; i < steps && (int)poses.size() < numNodes; ++i) {
106  Simulator::GridPose nextGridPose = generateNewPose(poses.back(), SE2(stepLen,0,0), transNoise, rotNoise);
107  poses.push_back(nextGridPose);
108  }
109  if ((int)poses.size() == numNodes)
110  break;
111 
112  // sample a new motion direction
113  double sampleMove = Rand::uniform_rand(0., 1.);
114  int motionDirection = 0;
115  while (probLimits[motionDirection] < sampleMove && motionDirection+1 < MO_NUM_ELEMS) {
116  motionDirection++;
117  }
118 
119  SE2 nextMotionStep = getMotion(motionDirection, stepLen);
120  Simulator::GridPose nextGridPose = generateNewPose(poses.back(), nextMotionStep, transNoise, rotNoise);
121 
122  // check whether we will walk outside the boundaries in the next iteration
123  SE2 nextStepFinalPose = nextGridPose.truePose * maxStepTransf;
124  if (fabs(nextStepFinalPose.translation().x()) >= bound[0] || fabs(nextStepFinalPose.translation().y()) >= bound[1]) {
125  //cerr << "b";
126  // will be outside boundaries using this
127  for (int i = 0; i < MO_NUM_ELEMS; ++i) {
128  nextMotionStep = getMotion(i, stepLen);
129  nextGridPose = generateNewPose(poses.back(), nextMotionStep, transNoise, rotNoise);
130  nextStepFinalPose = nextGridPose.truePose * maxStepTransf;
131  if (fabs(nextStepFinalPose.translation().x()) < bound[0] && fabs(nextStepFinalPose.translation().y()) < bound[1])
132  break;
133  }
134  }
135 
136  poses.push_back(nextGridPose);
137  }
138  cerr << "done." << endl;
139 
140  // creating landmarks along the trajectory
141  cerr << "Simulator: Creating landmarks ... ";
142  LandmarkGrid grid;
143  for (PosesVector::const_iterator it = poses.begin(); it != poses.end(); ++it) {
144  int ccx = (int)round(it->truePose.translation().x());
145  int ccy = (int)round(it->truePose.translation().y());
146  for (int a=-landmarksRange; a<=landmarksRange; a++)
147  for (int b=-landmarksRange; b<=landmarksRange; b++){
148  int cx=ccx+a;
149  int cy=ccy+b;
150  LandmarkPtrVector& landmarksForCell = grid[cx][cy];
151  if (landmarksForCell.size() == 0) {
152  for (int i = 0; i < landMarksPerSquareMeter; ++i) {
153  Landmark* l = new Landmark();
154  double offx, offy;
155  do {
156  offx = Rand::uniform_rand(-0.5*stepLen, 0.5*stepLen);
157  offy = Rand::uniform_rand(-0.5*stepLen, 0.5*stepLen);
158  } while (hypot_sqr(offx, offy) < 0.25*0.25);
159  l->truePose[0] = cx + offx;
160  l->truePose[1] = cy + offy;
161  landmarksForCell.push_back(l);
162  }
163  }
164  }
165  }
166  cerr << "done." << endl;
167 
168  cerr << "Simulator: Simulating landmark observations for the poses ... ";
169  double maxSensorSqr = maxSensorRangeLandmarks * maxSensorRangeLandmarks;
170  int globalId = 0;
171  for (PosesVector::iterator it = poses.begin(); it != poses.end(); ++it) {
172  Simulator::GridPose& pv = *it;
173  int cx = (int)round(it->truePose.translation().x());
174  int cy = (int)round(it->truePose.translation().y());
175  int numGridCells = (int)(maxSensorRangeLandmarks) + 1;
176 
177  pv.id = globalId++;
178  SE2 trueInv = pv.truePose.inverse();
179 
180  for (int xx = cx - numGridCells; xx <= cx + numGridCells; ++xx)
181  for (int yy = cy - numGridCells; yy <= cy + numGridCells; ++yy) {
182  LandmarkPtrVector& landmarksForCell = grid[xx][yy];
183  if (landmarksForCell.size() == 0)
184  continue;
185  for (size_t i = 0; i < landmarksForCell.size(); ++i) {
186  Landmark* l = landmarksForCell[i];
187  double dSqr = hypot_sqr(pv.truePose.translation().x() - l->truePose.x(), pv.truePose.translation().y() - l->truePose.y());
188  if (dSqr > maxSensorSqr)
189  continue;
190  double obs = Rand::uniform_rand(0.0, 1.0);
191  if (obs > observationProb) // we do not see this one...
192  continue;
193  if (l->id < 0)
194  l->id = globalId++;
195  if (l->seenBy.size() == 0) {
196  Vector2d trueObservation = trueInv * l->truePose;
197  Vector2d observation = trueObservation;
198  observation[0] += Rand::gauss_rand(0., landmarkNoise[0]);
199  observation[1] += Rand::gauss_rand(0., landmarkNoise[1]);
200  l->simulatedPose = pv.simulatorPose * observation;
201  }
202  l->seenBy.push_back(pv.id);
203  pv.landmarks.push_back(l);
204  }
205  }
206 
207  }
208  cerr << "done." << endl;
209 
210  // add the odometry measurements
211  _odometry.clear();
212  cerr << "Simulator: Adding odometry measurements ... ";
213  for (size_t i = 1; i < poses.size(); ++i) {
214  const GridPose& prev = poses[i-1];
215  const GridPose& p = poses[i];
216 
217  _odometry.push_back(GridEdge());
218  GridEdge& edge = _odometry.back();
219 
220  edge.from = prev.id;
221  edge.to = p.id;
222  edge.trueTransf = prev.truePose.inverse() * p.truePose;
224  edge.information = information;
225  }
226  cerr << "done." << endl;
227 
228  _landmarks.clear();
229  _landmarkObservations.clear();
230  // add the landmark observations
231  {
232  cerr << "Simulator: add landmark observations ... ";
233  Matrix2d covariance; covariance.fill(0.);
234  covariance(0, 0) = landmarkNoise[0]*landmarkNoise[0];
235  covariance(1, 1) = landmarkNoise[1]*landmarkNoise[1];
236  Matrix2d information = covariance.inverse();
237 
238  for (size_t i = 0; i < poses.size(); ++i) {
239  const GridPose& p = poses[i];
240  for (size_t j = 0; j < p.landmarks.size(); ++j) {
241  Landmark* l = p.landmarks[j];
242  if (l->seenBy.size() > 0 && l->seenBy[0] == p.id) {
243  landmarks.push_back(*l);
244  }
245  }
246  }
247 
248  for (size_t i = 0; i < poses.size(); ++i) {
249  const GridPose& p = poses[i];
250  SE2 trueInv = (p.truePose * sensorOffset).inverse();
251  for (size_t j = 0; j < p.landmarks.size(); ++j) {
252  Landmark* l = p.landmarks[j];
253  Vector2d observation;
254  Vector2d trueObservation = trueInv * l->truePose;
255  observation = trueObservation;
256  if (l->seenBy.size() > 0 && l->seenBy[0] == p.id) { // write the initial position of the landmark
257  observation = (p.simulatorPose * sensorOffset).inverse() * l->simulatedPose;
258  } else {
259  // create observation for the LANDMARK using the true positions
260  observation[0] += Rand::gauss_rand(0., landmarkNoise[0]);
261  observation[1] += Rand::gauss_rand(0., landmarkNoise[1]);
262  }
263 
264  _landmarkObservations.push_back(LandmarkEdge());
265  LandmarkEdge& le = _landmarkObservations.back();
266 
267  le.from = p.id;
268  le.to = l->id;
269  le.trueMeas = trueObservation;
270  le.simulatorMeas = observation;
271  le.information = information;
272  }
273  }
274  cerr << "done." << endl;
275  }
276 
277 
278  // cleaning up
279  for (LandmarkGrid::iterator it = grid.begin(); it != grid.end(); ++it) {
280  for (std::map<int, Simulator::LandmarkPtrVector>::iterator itt = it->second.begin(); itt != it->second.end(); ++itt) {
281  Simulator::LandmarkPtrVector& landmarks = itt->second;
282  for (size_t i = 0; i < landmarks.size(); ++i)
283  delete landmarks[i];
284  }
285  }
286 
287  }
288 
289  Simulator::GridPose Simulator::generateNewPose(const Simulator::GridPose& prev, const SE2& trueMotion, const Eigen::Vector2d& transNoise, double rotNoise)
290  {
291  Simulator::GridPose nextPose;
292  nextPose.id = prev.id + 1;
293  nextPose.truePose = prev.truePose * trueMotion;
294  SE2 noiseMotion = sampleTransformation(trueMotion, transNoise, rotNoise);
295  nextPose.simulatorPose = prev.simulatorPose * noiseMotion;
296  return nextPose;
297  }
298 
299  SE2 Simulator::getMotion(int motionDirection, double stepLen)
300  {
301  switch (motionDirection) {
302  case MO_LEFT:
303  return SE2(stepLen, 0, 0.5*M_PI);
304  case MO_RIGHT:
305  return SE2(stepLen, 0, -0.5*M_PI);
306  default:
307  cerr << "Unknown motion direction" << endl;
308  return SE2(stepLen, 0, -0.5*M_PI);
309  }
310  }
311 
312  SE2 Simulator::sampleTransformation(const SE2& trueMotion_, const Eigen::Vector2d& transNoise, double rotNoise)
313  {
314  Vector3d trueMotion = trueMotion_.toVector();
315  SE2 noiseMotion(
316  trueMotion[0] + Rand::gauss_rand(0.0, transNoise[0]),
317  trueMotion[1] + Rand::gauss_rand(0.0, transNoise[1]),
318  trueMotion[2] + Rand::gauss_rand(0.0, rotNoise));
319  return noiseMotion;
320  }
321 
322  } // end namespace
323 } // end namespace
std::vector< GridPose, Eigen::aligned_allocator< GridPose > > PosesVector
Definition: simulator.h:75
T hypot_sqr(T x, T y)
Definition: misc.h:70
std::map< int, std::map< int, Simulator::LandmarkPtrVector > > LandmarkGrid
Definition: simulator.cpp:48
double uniform_rand(double lowerBndr, double upperBndr)
Eigen::Vector3d toVector() const
Definition: se2.h:102
SE2 inverse() const
Definition: se2.h:76
const Eigen::Vector2d & translation() const
Definition: se2.h:49
#define DEG2RAD(x)
Definition: macros.h:31
LandmarkPtrVector landmarks
the landmarks observed by this node
Definition: simulator.h:73
MO_LEFT
Definition: simulator.h:45
Simulator(OptimizableGraph *graph_)
MO_RIGHT
Definition: simulator.h:45
odometry constraint
Definition: simulator.h:80
#define M_PI
Definition: misc.h:34
double gauss_rand(double sigma)
std::vector< Landmark, Eigen::aligned_allocator< Landmark > > LandmarkVector
Definition: simulator.h:61
std::vector< Landmark * > LandmarkPtrVector
Definition: simulator.h:62