00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include <iostream>
00018 #include <map>
00019 #include <vector>
00020
00021 #include "rand.h"
00022 #include "vertex_se2.h"
00023 #include "vertex_point_xy.h"
00024 #include "edge_se2.h"
00025 #include "edge_se2_pointxy.h"
00026 #include "se2.h"
00027
00028 #include "g2o/core/graph_optimizer_sparse.h"
00029 #include "g2o/core/block_solver.h"
00030 #include "g2o/solvers/csparse/linear_solver_csparse.h"
00031 using namespace std;
00032 using namespace g2o;
00033 using namespace g2o::tutorial;
00034
00035 struct Landmark
00036 {
00037 int id;
00038 Vector2d truePose;
00039 Vector2d simulatedPose;
00040 vector<int> seenBy;
00041 Landmark() : id(-1) {}
00042 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
00043 };
00044 typedef vector<Landmark*> LandmarkVector;
00045 typedef map<int, map<int, LandmarkVector> > LandmarkGrid;
00046
00050 struct GridPose
00051 {
00052 int id;
00053 SE2 truePose;
00054 SE2 simulatorPose;
00055 LandmarkVector landmarks;
00056 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
00057 };
00058
00059 struct GridEdge
00060 {
00061 int from;
00062 int to;
00063 SE2 trueTransf;
00064 SE2 simulatorTransf;
00065 EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
00066 };
00067
00068 enum MotionType {
00069 MO_LEFT, MO_RIGHT,
00070 MO_NUM_ELEMS
00071 };
00072
00073 typedef vector<GridPose, Eigen::aligned_allocator<GridPose> > PosesVector;
00074 typedef vector<const GridPose*> PosesPtrVector;
00075 typedef map<int, map<int, PosesPtrVector> > PosesGrid;
00076
00077 SE2 sampleTransformation(const SE2& trueMotion_, const Vector2d& transNoise, double rotNoise)
00078 {
00079 Vector3d trueMotion = trueMotion_.toVector();
00080 SE2 noiseMotion(
00081 trueMotion[0] + Rand::gauss_rand(0.0, transNoise[0]),
00082 trueMotion[1] + Rand::gauss_rand(0.0, transNoise[1]),
00083 trueMotion[2] + Rand::gauss_rand(0.0, rotNoise));
00084 return noiseMotion;
00085 }
00086
00087 GridPose generateNewPose(const GridPose& prev, const SE2& trueMotion, const Vector2d& transNoise, double rotNoise)
00088 {
00089 GridPose nextPose;
00090 nextPose.id = prev.id + 1;
00091 nextPose.truePose = prev.truePose * trueMotion;
00092 SE2 noiseMotion = sampleTransformation(trueMotion, transNoise, rotNoise);
00093 nextPose.simulatorPose = prev.simulatorPose * noiseMotion;
00094 return nextPose;
00095 }
00096
00097 SE2 getMotion(int motionDirection, double stepLen)
00098 {
00099 switch (motionDirection) {
00100 case MO_LEFT:
00101 return SE2(stepLen, 0, 0.5*M_PI);
00102 case MO_RIGHT:
00103 return SE2(stepLen, 0, -0.5*M_PI);
00104 default:
00105 cerr << "Unknown motion direction" << endl;
00106 return SE2(stepLen, 0, -0.5*M_PI);
00107 }
00108 }
00109
00110 int main()
00111 {
00112
00113 int steps = 5;
00114 double stepLen = 1.0;
00115 int numNodes = 300;
00116 int boundArea = 50;
00117 int seed = time(0);
00118
00119 double maxSensorRangeLandmarks = 2.5 * stepLen;
00120
00121 int landMarksPerSquareMeter = 1;
00122 double observationProb = 0.8;
00123
00124 int landmarksRange=2;
00125
00126 Vector2d transNoise(0.05, 0.01);
00127 double rotNoise = DEG2RAD(2.);
00128 Vector2d landmarkNoise(0.05, 0.05);
00129
00130 Vector2d bound(boundArea, boundArea);
00131 Rand::seed_rand(seed);
00132
00133 VectorXd probLimits(MO_NUM_ELEMS);
00134 for (int i = 0; i < probLimits.size(); ++i)
00135 probLimits[i] = (i + 1) / (double) MO_NUM_ELEMS;
00136
00137
00138 Matrix3d covariance;
00139 covariance.fill(0.);
00140 covariance(0, 0) = transNoise[0]*transNoise[0];
00141 covariance(1, 1) = transNoise[1]*transNoise[1];
00142 covariance(2, 2) = rotNoise*rotNoise;
00143 Matrix3d information = covariance.inverse();
00144
00145 SE2 maxStepTransf(stepLen * steps, 0, 0);
00146 PosesVector poses;
00147 LandmarkVector landmarks;
00148 GridPose firstPose;
00149 firstPose.id = 0;
00150 firstPose.truePose = SE2(0,0,0);
00151 firstPose.simulatorPose = SE2(0,0,0);
00152 poses.push_back(firstPose);
00153 cerr << "sampling nodes ...";
00154 while ((int)poses.size() < numNodes) {
00155
00156 for (int i = 1; i < steps && (int)poses.size() < numNodes; ++i) {
00157 GridPose nextGridPose = generateNewPose(poses.back(), SE2(stepLen,0,0), transNoise, rotNoise);
00158 poses.push_back(nextGridPose);
00159 }
00160 if ((int)poses.size() == numNodes)
00161 break;
00162
00163
00164 double sampleMove = Rand::uniform_rand(0., 1.);
00165 int motionDirection = 0;
00166 while (probLimits[motionDirection] < sampleMove && motionDirection+1 < MO_NUM_ELEMS) {
00167 motionDirection++;
00168 }
00169
00170 SE2 nextMotionStep = getMotion(motionDirection, stepLen);
00171 GridPose nextGridPose = generateNewPose(poses.back(), nextMotionStep, transNoise, rotNoise);
00172
00173
00174 SE2 nextStepFinalPose = nextGridPose.truePose * maxStepTransf;
00175 if (fabs(nextStepFinalPose.translation().x()) >= bound[0] || fabs(nextStepFinalPose.translation().y()) >= bound[1]) {
00176
00177
00178 for (int i = 0; i < MO_NUM_ELEMS; ++i) {
00179 nextMotionStep = getMotion(i, stepLen);
00180 nextGridPose = generateNewPose(poses.back(), nextMotionStep, transNoise, rotNoise);
00181 nextStepFinalPose = nextGridPose.truePose * maxStepTransf;
00182 if (fabs(nextStepFinalPose.translation().x()) < bound[0] && fabs(nextStepFinalPose.translation().y()) < bound[1])
00183 break;
00184 }
00185 }
00186
00187 poses.push_back(nextGridPose);
00188 }
00189 cerr << "done." << endl;
00190
00191
00192 cerr << "Creating landmarks ... ";
00193 LandmarkGrid grid;
00194 for (PosesVector::const_iterator it = poses.begin(); it != poses.end(); ++it) {
00195 int ccx = lrint(it->truePose.translation().x());
00196 int ccy = lrint(it->truePose.translation().y());
00197 for (int a=-landmarksRange; a<=landmarksRange; a++)
00198 for (int b=-landmarksRange; b<=landmarksRange; b++){
00199 int cx=ccx+a;
00200 int cy=ccy+b;
00201 LandmarkVector& landmarksForCell = grid[cx][cy];
00202 if (landmarksForCell.size() == 0) {
00203 for (int i = 0; i < landMarksPerSquareMeter; ++i) {
00204 Landmark* l = new Landmark();
00205 double offx, offy;
00206 do {
00207 offx = Rand::uniform_rand(-0.5*stepLen, 0.5*stepLen);
00208 offy = Rand::uniform_rand(-0.5*stepLen, 0.5*stepLen);
00209 } while (hypot_sqr(offx, offy) < 0.25*0.25);
00210 l->truePose[0] = cx + offx;
00211 l->truePose[1] = cy + offy;
00212 landmarksForCell.push_back(l);
00213 landmarks.push_back(l);
00214 }
00215 }
00216 }
00217 }
00218 cerr << "done." << endl;
00219
00220 cerr << "Creating landmark observations for the simulated poses ... ";
00221 double maxSensorSqr = maxSensorRangeLandmarks * maxSensorRangeLandmarks;
00222 int globalId = 0;
00223 for (PosesVector::iterator it = poses.begin(); it != poses.end(); ++it) {
00224 GridPose& pv = *it;
00225 int cx = lrint(it->truePose.translation().x());
00226 int cy = lrint(it->truePose.translation().y());
00227 int numGridCells = (int)(maxSensorRangeLandmarks) + 1;
00228
00229 pv.id = globalId++;
00230 SE2 trueInv = pv.truePose.inverse();
00231
00232 for (int xx = cx - numGridCells; xx <= cx + numGridCells; ++xx)
00233 for (int yy = cy - numGridCells; yy <= cy + numGridCells; ++yy) {
00234 LandmarkVector& landmarksForCell = grid[xx][yy];
00235 if (landmarksForCell.size() == 0)
00236 continue;
00237 for (size_t i = 0; i < landmarksForCell.size(); ++i) {
00238 Landmark* l = landmarksForCell[i];
00239 double dSqr = hypot_sqr(pv.truePose.translation().x() - l->truePose.x(), pv.truePose.translation().y() - l->truePose.y());
00240 if (dSqr > maxSensorSqr)
00241 continue;
00242 double obs = Rand::uniform_rand(0.0, 1.0);
00243 if (obs > observationProb)
00244 continue;
00245 if (l->id < 0)
00246 l->id = globalId++;
00247 if (l->seenBy.size() == 0) {
00248 Vector2d trueObservation = trueInv * l->truePose;
00249 Vector2d observation = trueObservation;
00250 observation[0] += Rand::gauss_rand(0., landmarkNoise[0]);
00251 observation[1] += Rand::gauss_rand(0., landmarkNoise[1]);
00252 l->simulatedPose = pv.simulatorPose * observation;
00253 }
00254 l->seenBy.push_back(pv.id);
00255 pv.landmarks.push_back(l);
00256 }
00257 }
00258
00259 }
00260 cerr << "done." << endl;
00261
00262
00263
00264
00265
00266
00267 typedef BlockSolver< BlockSolverTraits<-1, -1> > SlamBlockSolver;
00268 typedef LinearSolverCSparse<SlamBlockSolver::PoseMatrixType> SlamLinearSolver;
00269
00270
00271 SparseOptimizer optimizer;
00272 SlamLinearSolver* linearSolver = new SlamLinearSolver();
00273 linearSolver->setBlockOrdering(false);
00274 SlamBlockSolver* solver = new SlamBlockSolver(&optimizer, linearSolver);
00275 optimizer.setSolver(solver);
00276
00277
00278
00279 cerr << "Adding robot poses ... ";
00280 for (size_t i = 0; i < poses.size(); ++i) {
00281 const GridPose& p = poses[i];
00282 const SE2& t = p.simulatorPose;
00283 VertexSE2* robot = new VertexSE2;
00284 robot->setId(p.id);
00285 robot->setEstimate(t);
00286 optimizer.addVertex(robot);
00287 }
00288 cerr << "done." << endl;
00289
00290
00291 cerr << "Adding odometry measurements ... ";
00292 for (size_t i = 1; i < poses.size(); ++i) {
00293 const GridPose& prev = poses[i-1];
00294 const GridPose& p = poses[i];
00295
00296 SE2 transf = prev.simulatorPose.inverse() * p.simulatorPose;
00297
00298 EdgeSE2* odometry = new EdgeSE2;
00299 odometry->vertices()[0] = optimizer.vertex(prev.id);
00300 odometry->vertices()[1] = optimizer.vertex(p.id);
00301 odometry->setMeasurement(transf);
00302 odometry->setInverseMeasurement(transf.inverse());
00303 odometry->setInformation(information);
00304 optimizer.addEdge(odometry);
00305 }
00306 cerr << "done." << endl;
00307
00308
00309 {
00310 Matrix2d covariance; covariance.fill(0.);
00311 covariance(0, 0) = landmarkNoise[0]*landmarkNoise[0];
00312 covariance(1, 1) = landmarkNoise[1]*landmarkNoise[1];
00313 Matrix2d information = covariance.inverse();
00314
00315 cerr << "add landmark vertices ... ";
00316 for (size_t i = 0; i < poses.size(); ++i) {
00317 const GridPose& p = poses[i];
00318 for (size_t j = 0; j < p.landmarks.size(); ++j) {
00319 Landmark* l = p.landmarks[j];
00320 if (l->seenBy.size() > 0 && l->seenBy[0] == p.id) {
00321 VertexPointXY* landmark = new VertexPointXY;
00322 landmark->setId(l->id);
00323 landmark->setEstimate(l->simulatedPose);
00324 optimizer.addVertex(landmark);
00325 }
00326 }
00327 }
00328 cerr << "done." << endl;
00329
00330 cerr << "add landmark observations ... ";
00331 for (size_t i = 0; i < poses.size(); ++i) {
00332 const GridPose& p = poses[i];
00333 SE2 trueInv = p.truePose.inverse();
00334 for (size_t j = 0; j < p.landmarks.size(); ++j) {
00335 Landmark* l = p.landmarks[j];
00336 Vector2d observation;
00337 if (l->seenBy.size() > 0 && l->seenBy[0] == p.id) {
00338 observation = p.simulatorPose.inverse() * l->simulatedPose;
00339 } else {
00340
00341 Vector2d trueObservation = trueInv * l->truePose;
00342 observation = trueObservation;
00343 observation[0] += Rand::gauss_rand(0., landmarkNoise[0]);
00344 observation[1] += Rand::gauss_rand(0., landmarkNoise[1]);
00345 }
00346
00347 EdgeSE2PointXY* landmarkObservation = new EdgeSE2PointXY;
00348 landmarkObservation->vertices()[0] = optimizer.vertex(p.id);
00349 landmarkObservation->vertices()[1] = optimizer.vertex(l->id);
00350 landmarkObservation->setMeasurement(observation);
00351 landmarkObservation->setInverseMeasurement(-1.*observation);
00352 landmarkObservation->setInformation(information);
00353 optimizer.addEdge(landmarkObservation);
00354 }
00355 }
00356 cerr << "done." << endl;
00357 }
00358
00359
00360
00361
00362
00363
00364
00365
00366 VertexSE2* firstRobotPose = dynamic_cast<VertexSE2*>(optimizer.vertex(0));
00367 firstRobotPose->setFixed(true);
00368 optimizer.setVerbose(true);
00369
00370 cerr << "Optimizing" << endl;
00371 optimizer.initializeOptimization();
00372 optimizer.optimize(10);
00373 cerr << "done." << endl;
00374
00375
00376 return 0;
00377 }