spa2d.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2009, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 //
36 // Sparse Pose Adjustment classes and functions, 2D version
37 //
38 #include <nav2d_karto/spa2d.h>
39 
40 #include <stdio.h>
41 #include <Eigen/Cholesky>
42 
43 using namespace Eigen;
44 using namespace std;
45 
46 #include <iostream>
47 #include <iomanip>
48 #include <fstream>
49 #include <sys/time.h>
50 
51 // elapsed time in microseconds
52 static long long utime()
53 {
54  timeval tv;
55  gettimeofday(&tv,NULL);
56  long long ts = tv.tv_sec;
57  ts *= 1000000;
58  ts += tv.tv_usec;
59  return ts;
60 }
61 
62  // R = [[c -s][s c]]
63  // [R' | R't]
65  {
66  w2n(0,0) = w2n(1,1) = cos(arot);
67  w2n(0,1) = sin(arot);
68  w2n(1,0) = -w2n(0,1);
69  w2n.col(2).setZero();
70  w2n.col(2) = -w2n*trans;
71  }
72 
73 
74  //
75  // sets angle derivatives dR'/dth
76  //
78  {
79  dRdx(0,0) = dRdx(1,1) = w2n(1,0); // -sin(th)
80  dRdx(0,1) = w2n(0,0); // cos(th)
81  dRdx(1,0) = -w2n(0,0); // -cos(th)
82  }
83 
84 
85  // set up Jacobians
86 
87  void Con2dP2::setJacobians(std::vector<Node2d,Eigen::aligned_allocator<Node2d> > &nodes)
88  {
89  // node references
90  Node2d &nr = nodes[ndr];
91  Matrix<double,3,1> &tr = nr.trans;
92  Node2d &n1 = nodes[nd1];
93  Matrix<double,3,1> &t1 = n1.trans;
94 
95  // first get the second frame in first frame coords
96  Eigen::Matrix<double,2,1> pc = nr.w2n * t1;
97 
98  // Jacobians wrt first frame parameters
99 
100  // translational part of 0p1 wrt translational vars of p0
101  // this is just -R0' [from 0t1 = R0'(t1 - t0)]
102  J0.block<2,2>(0,0) = -nr.w2n.block<2,2>(0,0);
103 
104 
105  // translational part of 0p1 wrt rotational vars of p0
106  // dR'/dq * [pw - t]
107  Eigen::Matrix<double,2,1> pwt;
108  pwt = (t1-tr).head(2); // transform translations
109 
110  // dx
111  Eigen::Matrix<double,2,1> dp = nr.dRdx * pwt; // dR'/dq * [pw - t]
112  J0.block<2,1>(0,2) = dp;
113 
114  // rotational part of 0p1 wrt translation vars of p0 => zero
115  J0.block<1,2>(2,0).setZero();
116 
117  // rotational part of 0p1 wrt rotational vars of p0
118  // from 0q1 = (q1-q0)
119  J0(2,2) = -1.0;
120  J0t = J0.transpose();
121 
122  // cout << endl << "J0 " << ndr << endl << J0 << endl;
123 
124  // Jacobians wrt second frame parameters
125  // translational part of 0p1 wrt translational vars of p1
126  // this is just R0' [from 0t1 = R0'(t1 - t0)]
127  J1.block<2,2>(0,0) = nr.w2n.block<2,2>(0,0);
128 
129  // translational part of 0p1 wrt rotational vars of p1: zero
130  J1.block<2,1>(0,2).setZero();
131 
132  // rotational part of 0p1 wrt translation vars of p0 => zero
133  J1.block<1,2>(2,0).setZero();
134 
135 
136  // rotational part of 0p1 wrt rotational vars of p0
137  // from 0q1 = (q1-q0)
138  J1(2,2) = 1.0;
139  J1t = J1.transpose();
140 
141  // cout << endl << "J1 " << nd1 << endl << J1 << endl;
142 
143  };
144 
145 
146 
147  // error function
148  // NOTE: this is h(x) - z, not z - h(x)
149  inline double Con2dP2::calcErr(const Node2d &nd0, const Node2d &nd1)
150  {
151  err.block<2,1>(0,0) = nd0.w2n * nd1.trans - tmean;
152  double aerr = (nd1.arot - nd0.arot) - amean;
153  if (aerr > M_PI) aerr -= 2.0*M_PI;
154  if (aerr < -M_PI) aerr += 2.0*M_PI;
155  err(2) = aerr;
156 
157  // cout << err.transpose() << endl;
158 
159  return err.dot(prec * err);
160  }
161 
162 
163  // error function for distance cost
164  double Con2dP2::calcErrDist(const Node2d &nd0, const Node2d &nd1)
165  {
166  Vector2d derr = nd0.w2n * nd1.trans - tmean;
167  return derr.dot(derr);
168  }
169 
170 
171  // error measure, squared
172  // assumes node transforms have already been calculated
173  // <tcost> is true if we just want the distance offsets
174  double SysSPA2d::calcCost(bool tcost)
175  {
176  double cost = 0.0;
177 
178  // do distance offset
179  if (tcost)
180  {
181  for(size_t i=0; i<p2cons.size(); i++)
182  {
183  Con2dP2 &con = p2cons[i];
184  double err = con.calcErrDist(nodes[con.ndr],nodes[con.nd1]);
185  cost += err;
186  }
187  }
188 
189  // full cost
190  else
191  {
192  for(size_t i=0; i<p2cons.size(); i++)
193  {
194  Con2dP2 &con = p2cons[i];
195  double err = con.calcErr(nodes[con.ndr],nodes[con.nd1]);
196  cost += err;
197  }
198  errcost = cost;
199  }
200 
201  return cost;
202  }
203 
204 
205  // add a node at a pose
206  // <pos> is x,y,th, with th in radians
207  // returns node position
208  int SysSPA2d::addNode(const Vector3d &pos, int id)
209  {
210  Node2d nd;
211  nd.nodeId = id;
212 
213  nd.arot = pos(2);
214  nd.trans.head(2) = pos.head(2);
215  nd.trans(2) = 1.0;
216 
217  // add in to system
218  nd.setTransform(); // set up world2node transform
219  nd.setDr();
220  int ndi = nodes.size();
221  nodes.push_back(nd);
222  return ndi;
223  }
224 
225  // add a constraint
226  // <nd0>, <nd1> are node id's
227  // <mean> is x,y,th, with th in radians
228  // <prec> is a 3x3 precision matrix (inverse covariance
229  // returns true if nodes are found
230  // TODO: make node lookup more efficient
231  bool SysSPA2d::addConstraint(int ndi0, int ndi1, const Vector3d &mean,
232  const Matrix3d &prec)
233  {
234  int ni0 = -1, ni1 = -1;
235  for (int i=0; i<(int)nodes.size(); i++)
236  {
237  if (nodes[i].nodeId == ndi0)
238  ni0 = i;
239  if (nodes[i].nodeId == ndi1)
240  ni1 = i;
241  }
242  if (ni0 < 0 || ni1 < 0) return false;
243 
244  Con2dP2 con;
245  con.ndr = ni0;
246  con.nd1 = ni1;
247 
248  con.tmean = mean.head(2);
249  con.amean = mean(2);
250  con.prec = prec;
251  p2cons.push_back(con);
252  return true;
253  }
254 
255 
256  // Set up linear system
257  // Use dense matrices
258 
259  void SysSPA2d::setupSys(double sLambda)
260  {
261  // set matrix sizes and clear
262  // assumes scales vars are all free
263  int nFree = nodes.size() - nFixed;
264  A.setZero(3*nFree,3*nFree);
265  B.setZero(3*nFree);
266  VectorXi dcnt(nFree);
267  dcnt.setZero(nFree);
268 
269  // lambda augmentation
270  double lam = 1.0 + sLambda;
271 
272  // loop over P2 constraints
273  for(size_t pi=0; pi<p2cons.size(); pi++)
274  {
275  Con2dP2 &con = p2cons[pi];
276  con.setJacobians(nodes);
277 
278  // add in 4 blocks of A
279  int i0 = 3*(con.ndr-nFixed); // will be negative if fixed
280  int i1 = 3*(con.nd1-nFixed); // will be negative if fixed
281 
282  if (i0>=0)
283  {
284  A.block<3,3>(i0,i0) += con.J0t * con.prec * con.J0;
285  dcnt(con.ndr - nFixed)++;
286  }
287  if (i1>=0)
288  {
289  dcnt(con.nd1 - nFixed)++;
290  Matrix<double,3,3> tp = con.prec * con.J1;
291  A.block<3,3>(i1,i1) += con.J1t * tp;
292  if (i0>=0)
293  {
294  A.block<3,3>(i0,i1) += con.J0t * con.prec * con.J1;
295  A.block<3,3>(i1,i0) += con.J1t * con.prec * con.J0;
296  }
297  }
298 
299  // add in 2 blocks of B
300  if (i0>=0)
301  B.block<3,1>(i0,0) -= con.J0t * con.prec * con.err;
302  if (i1>=0)
303  B.block<3,1>(i1,0) -= con.J1t * con.prec * con.err;
304  } // finish P2 constraints
305 
306 
307  // augment diagonal
308  A.diagonal() *= lam;
309 
310  // check the matrix and vector
311  for (int i=0; i<3*nFree; i++)
312  for (int j=0; j<3*nFree; j++)
313  if (isnan(A(i,j)) ) { printf("[SetupSys] NaN in A\n"); *(int *)0x0 = 0; }
314 
315  for (int j=0; j<3*nFree; j++)
316  if (isnan(B[j]) ) { printf("[SetupSys] NaN in B\n"); *(int *)0x0 = 0; }
317 
318  int ndc = 0;
319  for (int i=0; i<nFree; i++)
320  if (dcnt(i) == 0) ndc++;
321 
322  if (ndc > 0)
323  cout << "[SetupSys] " << ndc << " disconnected nodes" << endl;
324  }
325 
326 
327  // Set up sparse linear system; see setupSys for algorithm.
328  // Currently doesn't work with scale variables
329  void SysSPA2d::setupSparseSys(double sLambda, int iter, int sparseType)
330  {
331  // set matrix sizes and clear
332  // assumes scales vars are all free
333  int nFree = nodes.size() - nFixed;
334  if(nFree < 0) nFree = 0;
335 
336  long long t0, t1, t2, t3;
337  t0 = utime();
338 
339  if (iter == 0)
340  {
341  csp.setupBlockStructure(nFree); // initialize CSparse structures
342  }
343 // else
344 // csp.setupBlockStructure(0); // zero out CSparse structures
345 
346  t1 = utime();
347 
348  VectorXi dcnt(nFree);
349  dcnt.setZero(nFree);
350 
351  // lambda augmentation
352  double lam = 1.0 + sLambda;
353 
354  // loop over P2 constraints
355  for(size_t pi=0; pi<p2cons.size(); pi++)
356  {
357  Con2dP2 &con = p2cons[pi];
358  con.setJacobians(nodes);
359 
360  // add in 4 blocks of A; actually just need upper triangular
361  int i0 = con.ndr-nFixed; // will be negative if fixed
362  int i1 = con.nd1-nFixed; // will be negative if fixed
363 
364  if (i0>=0)
365  {
366  Matrix<double,3,3> m = con.J0t*con.prec*con.J0;
367  csp.addDiagBlock(m,i0);
368  dcnt(con.ndr - nFixed)++;
369  }
370  if (i1>=0)
371  {
372  dcnt(con.nd1 - nFixed)++;
373  Matrix<double,3,3> tp = con.prec * con.J1;
374  Matrix<double,3,3> m = con.J1t * tp;
375  csp.addDiagBlock(m,i1);
376  if (i0>=0)
377  {
378  Matrix<double,3,3> m2 = con.J0t * tp;
379  if (i1 < i0)
380  {
381  m = m2.transpose();
382  csp.addOffdiagBlock(m,i1,i0);
383  }
384  else
385  {
386  csp.addOffdiagBlock(m2,i0,i1);
387  }
388  }
389  }
390 
391  // add in 2 blocks of B
392  if (i0>=0)
393  {
394  csp.B.block<3,1>(i0*3,0) -= con.J0t * con.prec * con.err;
395 
396  }
397  if (i1>=0)
398  {
399  csp.B.block<3,1>(i1*3,0) -= con.J1t * con.prec * con.err;
400  }
401 
402  } // finish P2 constraints
403  t2 = utime();
404 
405  // set up sparse matrix structure from blocks
406  if (sparseType == SBA_BLOCK_JACOBIAN_PCG)
407  csp.incDiagBlocks(lam); // increment diagonal block
408  else
409  csp.setupCSstructure(lam,iter==0);
410  t3 = utime();
411 
412  if (verbose)
413  printf("\n[SetupSparseSys] Block: %0.1f Cons: %0.1f CS: %0.1f\n",
414  (t1-t0)*.001, (t2-t1)*.001, (t3-t2)*.001);
415 
416  int ndc = 0;
417  for (int i=0; i<nFree; i++)
418  if (dcnt(i) == 0) ndc++;
419 
420  if (ndc > 0)
421  cout << "[SetupSparseSys] " << ndc << " disconnected nodes" << endl;
422  }
423 
424 
433 
434  int SysSPA2d::doSPA(int niter, double sLambda, int useCSparse, double initTol,
435  int maxCGiters)
436  {
437  // number of nodes
438  int ncams = nodes.size();
439 
440  // set number of constraints
441  int ncons = p2cons.size();
442 
443  if(ncams <= 0 || ncons <= 0)
444  {
445  return 0;
446  }
447 
448  // check for fixed frames
449  if (nFixed <= 0)
450  {
451  cout << "[doSPA2d] No fixed frames" << endl;
452  return 0;
453  }
454  for (int i=0; i<ncams; i++)
455  {
456  Node2d &nd = nodes[i];
457  if (i >= nFixed)
458  nd.isFixed = false;
459  else
460  nd.isFixed = true;
461  nd.setTransform(); // set up world-to-node transform for cost calculation
462  nd.setDr(); // always use local angles
463  }
464 
465  // initialize vars
466  if (sLambda > 0.0) // do we initialize lambda?
467  lambda = sLambda;
468 
469  double laminc = 2.0; // how much to increment lambda if we fail
470  double lamdec = 0.5; // how much to decrement lambda if we succeed
471  int iter = 0; // iterations
472  sqMinDelta = 1e-8 * 1e-8;
473  double cost = calcCost();
474  if (verbose)
475  cout << iter << " Initial squared cost: " << cost << " which is "
476  << sqrt(cost/ncons) << " rms error" << endl;
477 
478  int good_iter = 0;
479  double cumTime = 0;
480  for (; iter<niter; iter++) // loop at most <niter> times
481  {
482  if(verbose)
483  {
484  printf(" --- Begin of iteration %d ---\n", iter);
485  }
486  // set up and solve linear system
487  // NOTE: shouldn't need to redo all calcs in setupSys if we
488  // got here from a bad update
489 
490  long long t0, t1, t2, t3;
491  t0 = utime();
492  if (useCSparse)
493  {
494  setupSparseSys(lambda,iter,useCSparse); // set up sparse linear system
495  }
496  else
497  {
498  setupSys(lambda); // set up linear system
499  }
500  // cout << "[SPA] Solving...";
501  t1 = utime();
502 
503  // use appropriate linear solver
504  if (useCSparse == SBA_BLOCK_JACOBIAN_PCG)
505  {/*
506  if (csp.B.rows() != 0)
507  {
508  int iters = csp.doBPCG(maxCGiters,initTol,iter);
509  if (verbose)
510  cout << "[Block PCG] " << iters << " iterations" << endl;
511  } */
512  }
513 #ifdef SBA_DSIF
514  // PCG with incomplete Cholesky
515  else if (useCSparse == 3)
516  {
517  int res = csp.doPCG(maxCGiters);
518  if (res > 1)
519  cout << "[DoSPA] Sparse PCG failed with error " << res << endl;
520  }
521 #endif
522  else if (useCSparse > 0)
523  {
524  if (csp.B.rows() != 0)
525  {
526  bool ok = csp.doChol();
527  if (!ok)
528  cout << "[DoSBA] Sparse Cholesky failed!" << endl;
529  }
530  }
531 
532  // Dense direct Cholesky
533  else
534  A.ldlt().solveInPlace(B); // Cholesky decomposition and solution
535 
536  t2 = utime();
537  // cout << "solved" << endl;
538 
539  // get correct result vector
540  VectorXd &BB = useCSparse ? csp.B : B;
541 
542  // check for convergence
543  // this is a pretty crummy convergence measure...
544  double sqDiff = BB.squaredNorm();
545  if (sqDiff < sqMinDelta) // converged, done...
546  {
547  if (verbose)
548  cout << "Converged with delta: " << sqrt(sqDiff) << endl;
549  break;
550  }
551 
552  // update the frames
553  int ci = 0;
554  for(int i=0; i < ncams; i++)
555  {
556  Node2d &nd = nodes[i];
557  if (nd.isFixed) continue; // not to be updated
558  nd.oldtrans = nd.trans; // save in case we don't improve the cost
559  nd.oldarot = nd.arot;
560  nd.trans.head<2>() += BB.segment<2>(ci);
561 
562  nd.arot += BB(ci+2);
563  nd.normArot();
564  nd.setTransform(); // set up projection matrix for cost calculation
565  nd.setDr(); // set rotational derivatives
566  ci += 3; // advance B index
567  }
568 
569  // new cost
570  double newcost = calcCost();
571  if (verbose)
572  cout << iter << " Updated squared cost: " << newcost << " which is "
573  << sqrt(newcost/ncons) << " rms error" << endl;
574 
575  // check if we did good
576  if (newcost < cost) // && iter != 0) // NOTE: iter==0 case is for checking
577  {
578  cost = newcost;
579  lambda *= lamdec; // decrease lambda
580  // laminc = 2.0; // reset bad lambda factor; not sure if this is a good idea...
581  good_iter++;
582  }
583  else
584  {
585  lambda *= laminc; // increase lambda
586  laminc *= 2.0; // increase the increment
587 
588  // reset nodes
589  for(int i=0; i<ncams; i++)
590  {
591  Node2d &nd = nodes[i];
592  if (nd.isFixed) continue; // not to be updated
593  nd.trans = nd.oldtrans;
594  nd.arot = nd.oldarot;
595  nd.setTransform(); // set up projection matrix for cost calculation
596  nd.setDr();
597  }
598 
599  cost = calcCost(); // need to reset errors
600  if (verbose)
601  cout << iter << " Downdated cost: " << cost << endl;
602  // NOTE: shouldn't need to redo all calcs in setupSys
603  }
604 
605  t3 = utime();
606  if (iter == 0 && verbose)
607  {
608  printf("[SPA] Setup: %0.2f ms Solve: %0.2f ms Update: %0.2f ms\n",
609  0.001*(double)(t1-t0),
610  0.001*(double)(t2-t1),
611  0.001*(double)(t3-t2));
612  }
613 
614  double dt=1e-6*(double)(t3-t0);
615  cumTime+=dt;
616  if (print_iros_stats){
617  cerr << "iteration= " << iter
618  << "\t chi2= " << cost
619  << "\t time= " << dt
620  << "\t cumTime= " << cumTime
621  << "\t kurtChi2= " << cost
622  << endl;
623  }
624 
625  }
626 
627  // return number of iterations performed
628  return good_iter;
629 
630  }
631 
632 
639 
640  static inline int getind(std::map<int,int> &m, int ind)
641  {
642  std::map<int,int>::iterator it;
643  it = m.find(ind);
644  if (it == m.end())
645  return -1;
646  else
647  return it->second;
648  }
649 
650  int SysSPA2d::doSPAwindowed(int window, int niter, double sLambda, int useCSparse)
651  {
652  // number of nodes
653  int nnodes = nodes.size();
654  if (nnodes < 2) return 0;
655 
656  int nlow = nnodes - window;
657  if (nlow < 1) nlow = 1; // always have one fixed node
658 
659  if (verbose)
660  cout << "[SPA Window] From " << nlow << " to " << nnodes << endl;
661 
662  // number of constraints
663  int ncons = p2cons.size();
664 
665  // set up SPA
666  SysSPA2d spa;
667  spa.verbose = verbose;
668 
669  // node, constraint vectors and index mapping
670  std::vector<Node2d,Eigen::aligned_allocator<Node2d> > &wnodes = spa.nodes;
671  std::vector<Con2dP2,Eigen::aligned_allocator<Con2dP2> > &wp2cons = spa.p2cons;
672  std::map<int,int> inds;
673  std::vector<int> rinds; // reverse indices
674 
675  // loop through all constraints and set up fixed nodes and constraints
676  for (int i=0; i<ncons; i++)
677  {
678  Con2dP2 &con = p2cons[i];
679  if (con.ndr >= nlow || con.nd1 >= nlow)
680  wp2cons.push_back(con);
681 
682  if (con.ndr >= nlow && con.nd1 < nlow) // have a winner
683  {
684  int j = getind(inds,con.nd1); // corresponding index
685  if (j < 0) // not present, add it in
686  {
687  inds.insert(std::pair<int,int>(con.nd1,wnodes.size()));
688  wnodes.push_back(nodes[con.nd1]);
689  }
690  rinds.push_back(con.nd1);
691  }
692  else if (con.nd1 >= nlow && con.ndr < nlow)
693  {
694  int j = getind(inds,con.ndr); // corresponding index
695  if (j < 0) // not present, add it in
696  {
697  inds.insert(std::pair<int,int>(con.ndr,wnodes.size()));
698  wnodes.push_back(nodes[con.ndr]);
699  }
700  rinds.push_back(con.ndr);
701  }
702  }
703 
704  spa.nFixed = wnodes.size();
705  if (verbose)
706  cout << "[SPA Window] Fixed node count: " << spa.nFixed << endl;
707 
708  // add in variable nodes
709  for (int i=0; i<(int)wp2cons.size(); i++)
710  {
711  Con2dP2 &con = wp2cons[i];
712  if (con.nd1 >= nlow && con.ndr >= nlow) // have a winner
713  {
714  int n0 = getind(inds,con.ndr);
715  if (n0 < 0)
716  {
717  inds.insert(std::pair<int,int>(con.ndr,wnodes.size()));
718  wnodes.push_back(nodes[con.ndr]);
719  rinds.push_back(con.ndr);
720  }
721  int n1 = getind(inds,con.nd1);
722  if (n1 < 0)
723  {
724  inds.insert(std::pair<int,int>(con.nd1,wnodes.size()));
725  wnodes.push_back(nodes[con.nd1]);
726  rinds.push_back(con.nd1);
727  }
728  }
729  }
730 
731  if (verbose)
732  {
733  cout << "[SPA Window] Variable node count: " << spa.nodes.size() - spa.nFixed << endl;
734  cout << "[SPA Window] Constraint count: " << spa.p2cons.size() << endl;
735  }
736 
737  // new constraint indices
738  for (int i=0; i<(int)wp2cons.size(); i++)
739  {
740  Con2dP2 &con = wp2cons[i];
741  con.ndr = getind(inds,con.ndr);
742  con.nd1 = getind(inds,con.nd1);
743  }
744 
745  // run spa
746  niter = spa.doSPA(niter,sLambda,useCSparse);
747 
748  // reset constraint indices
749  for (int i=0; i<(int)wp2cons.size(); i++)
750  {
751  Con2dP2 &con = wp2cons[i];
752  con.ndr = rinds[con.ndr];
753  con.nd1 = rinds[con.nd1];
754  }
755  return niter;
756  }
757 
758 
759 #ifdef SBA_DSIF
760 
764  // Set up sparse linear system for Delayed Sparse Information Filter
765  void SysSPA2d::setupSparseDSIF(int newnode)
766  {
767  // set matrix sizes and clear
768  // assumes scales vars are all free
769  int nFree = nodes.size() - nFixed;
770 
771  // long long t0, t1, t2, t3;
772  // t0 = utime();
773 
774  // don't erase old stuff here, the delayed filter just grows in size
775  csp.setupBlockStructure(nFree,false); // initialize CSparse structures
776 
777  // t1 = utime();
778 
779  // loop over P2 constraints
780  for(size_t pi=0; pi<p2cons.size(); pi++)
781  {
782  Con2dP2 &con = p2cons[pi];
783 
784  // don't consider old constraints
785  if (con.ndr < newnode && con.nd1 < newnode)
786  continue;
787 
788  con.setJacobians(nodes);
789 
790  // add in 4 blocks of A; actually just need upper triangular
791  int i0 = con.ndr-nFixed; // will be negative if fixed
792  int i1 = con.nd1-nFixed; // will be negative if fixed
793 
794  // DSIF will not diverge on standard datasets unless
795  // we reduce the precision of the constraints
796  double fact = 1.0;
797  if (i0 != i1-1) fact = 0.99; // what should we use????
798 
799  if (i0>=0)
800  {
801  Matrix<double,3,3> m = con.J0t*con.prec*con.J0;
802  csp.addDiagBlock(m,i0);
803  }
804  if (i1>=0)
805  {
806  Matrix<double,3,3> m = con.J1t*con.prec*con.J1;
807  csp.addDiagBlock(m,i1);
808 
809  if (i0>=0)
810  {
811  Matrix<double,3,3> m2 = con.J0t*con.prec*con.J1;
812  m2 = m2 * fact * fact;
813  if (i1 < i0)
814  {
815  m = m2.transpose();
816  csp.addOffdiagBlock(m,i1,i0);
817  }
818  else
819  {
820  csp.addOffdiagBlock(m2,i0,i1);
821  }
822  }
823  }
824 
825  // add in 2 blocks of B
826  if (i0>=0)
827  csp.B.block<3,1>(i0*3,0) -= con.J0t * con.prec * con.err;
828  if (i1>=0)
829  csp.B.block<3,1>(i1*3,0) -= con.J1t * con.prec * con.err;
830 
831  } // finish P2 constraints
832 
833  // t2 = utime();
834 
835  csp.Bprev = csp.B; // save for next iteration
836 
837  // set up sparse matrix structure from blocks
838  csp.setupCSstructure(1.0,true);
839 
840  // t3 = utime();
841 
842  // printf("\n[SetupSparseSys] Block: %0.1f Cons: %0.1f CS: %0.1f\n",
843  // (t1-t0)*.001, (t2-t1)*.001, (t3-t2)*.001);
844 
845  }
846 
847 
850  void SysSPA2d::doDSIF(int newnode)
851  {
852  // number of nodes
853  int nnodes = nodes.size();
854 
855  // set number of constraints
856  int ncons = p2cons.size();
857 
858  // check for fixed frames
859  if (nFixed <= 0)
860  {
861  cout << "[doDSIF] No fixed frames" << endl;
862  return;
863  }
864 
865  // check for newnode being ok
866  if (newnode >= nnodes)
867  {
868  cout << "[doDSIF] no new nodes to add" << endl;
869  return;
870  }
871  else // set up saved mean value of pose
872  {
873  for (int i=newnode; i<nnodes; i++)
874  {
875  nodes[i].oldtrans = nodes[i].trans;
876  nodes[i].oldarot = nodes[i].arot;
877  }
878  }
879 
880  for (int i=0; i<nnodes; i++)
881  {
882  Node2d &nd = nodes[i];
883  if (i >= nFixed)
884  nd.isFixed = false;
885  else
886  nd.isFixed = true;
887  nd.setTransform(); // set up world-to-node transform for cost calculation
888  nd.setDr(); // always use local angles
889  }
890 
891  // initialize vars
892  double cost = calcCost();
893  if (verbose)
894  cout << " Initial squared cost: " << cost << " which is "
895  << sqrt(cost/ncons) << " rms error" << endl;
896 
897  // set up and solve linear system
898  long long t0, t1, t2, t3;
899  t0 = utime();
900  setupSparseDSIF(newnode); // set up sparse linear system
901 
902 #if 0
903  cout << "[doDSIF] B = " << csp.B.transpose() << endl;
904  csp.uncompress(A);
905  cout << "[doDSIF] A = " << endl << A << endl;
906 #endif
907 
908  // cout << "[SPA] Solving...";
909  t1 = utime();
910  bool ok = csp.doChol();
911  if (!ok)
912  cout << "[doDSIF] Sparse Cholesky failed!" << endl;
913  t2 = utime();
914  // cout << "solved" << endl;
915 
916  // get correct result vector
917  VectorXd &BB = csp.B;
918 
919  // cout << "[doDSIF] RES = " << BB.transpose() << endl;
920 
921  // update the frames
922  int ci = 0;
923  for(int i=0; i < nnodes; i++)
924  {
925  Node2d &nd = nodes[i];
926  if (nd.isFixed) continue; // not to be updated
927  nd.trans.head(2) = nd.oldtrans.head(2)+BB.segment<2>(ci);
928  nd.arot = nd.oldarot+BB(ci+2);
929  nd.normArot();
930  nd.setTransform(); // set up projection matrix for cost calculation
931  nd.setDr(); // set rotational derivatives
932  ci += 3; // advance B index
933  }
934 
935  // new cost
936  double newcost = calcCost();
937  if (verbose)
938  cout << " Updated squared cost: " << newcost << " which is "
939  << sqrt(newcost/ncons) << " rms error" << endl;
940 
941  t3 = utime();
942  }
943 
944 
945 
946  // write out the precision matrix for CSparse
947  void SysSPA2d::writeSparseA(char *fname, bool useCSparse)
948  {
949  ofstream ofs(fname);
950  if (ofs == NULL)
951  {
952  cout << "Can't open file " << fname << endl;
953  return;
954  }
955 
956  // cameras
957  if (useCSparse)
958  {
959  setupSparseSys(0.0,0);
960 
961  int *Ai = csp.A->i;
962  int *Ap = csp.A->p;
963  double *Ax = csp.A->x;
964 
965  for (int i=0; i<csp.csize; i++)
966  for (int j=Ap[i]; j<Ap[i+1]; j++)
967  if (Ai[j] <= i)
968  ofs << Ai[j] << " " << i << setprecision(16) << " " << Ax[j] << endl;
969  }
970  else
971  {
972  Eigen::IOFormat pfmt(16);
973 
974  int nrows = A.rows();
975  int ncols = A.cols();
976 
977  for (int i=0; i<nrows; i++)
978  for (int j=i; j<ncols; j++)
979  {
980  double a = A(i,j);
981  if (A(i,j) != 0.0)
982  ofs << i << " " << j << setprecision(16) << " " << a << endl;
983  }
984  }
985 
986  ofs.close();
987  }
988 #endif
989 
990 
991  // return vector of connections
992  // 4 entries for each connection: x,y,x',y'
993  void SysSPA2d::getGraph(std::vector<float> &graph)
994  {
995  for (int i=0; i<(int)p2cons.size(); i++)
996  {
997  Con2dP2 &con = p2cons[i];
998  Node2d &nd0 = nodes[con.ndr];
999  Node2d &nd1 = nodes[con.nd1];
1000  graph.push_back(nd0.trans(0));
1001  graph.push_back(nd0.trans(1));
1002  graph.push_back(nd1.trans(0));
1003  graph.push_back(nd1.trans(1));
1004  }
1005  }
int nFixed
Number of fixed nodes.
Definition: spa2d.h:217
Definition: spa2d.h:89
static int getind(std::map< int, int > &m, int ind)
Definition: spa2d.cpp:640
Eigen::Matrix< double, 3, 3 > J0t
Definition: spa2d.h:162
Eigen::Matrix< double, 3, 1 > trans
6DOF pose as a unit quaternion and translation vector
Definition: spa2d.h:98
double calcErrDist(const Node2d &nd0, const Node2d &nd1)
calculate error in distance only, no weighting
Definition: spa2d.cpp:164
Eigen::Matrix< double, 2, 3 > w2n
Resultant transform from world to node coordinates;.
Definition: spa2d.h:108
void writeSparseA(char *fname, bool useCSparse=false)
bool verbose
if we want statistics
Definition: spa2d.h:269
Eigen::Matrix< double, 3, 3 > J0
jacobian with respect to frames; uses dR&#39;/dq from Node2d calculation
Definition: spa2d.h:162
double arot
Definition: spa2d.h:99
void setZero()
EIGEN_MAKE_ALIGNED_OPERATOR_NEW int ndr
Reference pose index.
Definition: spa2d.h:142
void setTransform()
Definition: spa2d.cpp:64
void setDr()
Definition: spa2d.cpp:77
SysSPA2d holds a set of nodes and constraints for sparse pose adjustment.
Definition: spa2d.h:187
void normArot()
Normalize to [-pi,+pi].
Definition: spa2d.h:101
int doSPAwindowed(int window, int niter, double sLambda, int useCSparse)
Definition: spa2d.cpp:650
EIGEN_MAKE_ALIGNED_OPERATOR_NEW int nodeId
node id - somewhat redundant, but can be useful, e.g., in KARTO links
Definition: spa2d.h:95
double calcErr(const Node2d &nd0, const Node2d &nd1)
calculates projection error and stores it in <err>
Definition: spa2d.cpp:149
bool addConstraint(int nd0, int nd1, const Vector3d &mean, const Matrix3d &prec)
Definition: spa2d.cpp:231
Definition: spa2d.h:136
static long long utime()
Definition: spa2d.cpp:52
Eigen::Matrix< double, 3, 3 > prec
Definition: spa2d.h:150
Eigen::Matrix< double, 3, 3 > J1t
Definition: spa2d.h:162
void setupSparseSys(double sLambda, int iter, int sparseType)
Definition: spa2d.cpp:329
double amean
Definition: spa2d.h:149
int nd1
Node2d index for the second node.
Definition: spa2d.h:145
Eigen::Matrix< double, 3, 1 > err
error
Definition: spa2d.h:153
std::vector< Node2d, Eigen::aligned_allocator< Node2d > > nodes
set of nodes (camera frames) for SPA system, indexed by position;
Definition: spa2d.h:209
std::vector< Con2dP2, Eigen::aligned_allocator< Con2dP2 > > p2cons
Set of P2 constraints.
Definition: spa2d.h:220
Vector3< kt_double > Vector3d
Definition: Geometry.h:1046
double oldarot
Definition: spa2d.h:128
int addNode(const Vector3d &pos, int id)
Definition: spa2d.cpp:208
Vector2< kt_double > Vector2d
Definition: Geometry.h:610
bool isFixed
For SPA, is this camera fixed or free?
Definition: spa2d.h:123
#define SBA_BLOCK_JACOBIAN_PCG
Definition: spa2d.h:65
int doSPA(int niter, double sLambda=1.0e-4, int useCSparse=SBA_SPARSE_CHOLESKY, double initTol=1.0e-8, int CGiters=50)
Definition: spa2d.cpp:434
void setJacobians(std::vector< Node2d, Eigen::aligned_allocator< Node2d > > &nodes)
Definition: spa2d.cpp:87
Eigen::Matrix< double, 3, 1 > oldtrans
Definition: spa2d.h:127
Eigen::Vector2d tmean
Mean vector, quaternion (inverse) and precision matrix for this constraint.
Definition: spa2d.h:148
void setupSys(double sLambda)
Definition: spa2d.cpp:259
Eigen::Matrix< double, 3, 3 > J1
Definition: spa2d.h:162
void getGraph(std::vector< float > &graph)
Definition: spa2d.cpp:993
double calcCost(bool tcost=false)
Definition: spa2d.cpp:174
Eigen::Matrix2d dRdx
Definition: spa2d.h:118


nav2d_karto
Author(s): Sebastian Kasperski
autogenerated on Tue Nov 7 2017 06:02:36