41 #include <Eigen/Cholesky> 43 using namespace Eigen;
55 gettimeofday(&tv,NULL);
56 long long ts = tv.tv_sec;
66 w2n(0,0) = w2n(1,1) = cos(arot);
70 w2n.col(2) = -w2n*trans;
79 dRdx(0,0) = dRdx(1,1) = w2n(1,0);
81 dRdx(1,0) = -w2n(0,0);
91 Matrix<double,3,1> &tr = nr.
trans;
93 Matrix<double,3,1> &t1 = n1.
trans;
96 Eigen::Matrix<double,2,1> pc = nr.
w2n * t1;
102 J0.block<2,2>(0,0) = -nr.
w2n.block<2,2>(0,0);
107 Eigen::Matrix<double,2,1> pwt;
108 pwt = (t1-tr).head(2);
111 Eigen::Matrix<double,2,1> dp = nr.
dRdx * pwt;
112 J0.block<2,1>(0,2) = dp;
120 J0t = J0.transpose();
127 J1.block<2,2>(0,0) = nr.
w2n.block<2,2>(0,0);
139 J1t = J1.transpose();
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;
159 return err.dot(prec * err);
167 return derr.dot(derr);
181 for(
size_t i=0; i<p2cons.size(); i++)
192 for(
size_t i=0; i<p2cons.size(); i++)
214 nd.
trans.head(2) = pos.head(2);
220 int ndi = nodes.size();
232 const Matrix3d &prec)
234 int ni0 = -1, ni1 = -1;
235 for (
int i=0; i<(int)nodes.size(); i++)
237 if (nodes[i].nodeId == ndi0)
239 if (nodes[i].nodeId == ndi1)
242 if (ni0 < 0 || ni1 < 0)
return false;
248 con.
tmean = mean.head(2);
251 p2cons.push_back(con);
263 int nFree = nodes.size() - nFixed;
264 A.setZero(3*nFree,3*nFree);
266 VectorXi dcnt(nFree);
270 double lam = 1.0 + sLambda;
273 for(
size_t pi=0; pi<p2cons.size(); pi++)
279 int i0 = 3*(con.
ndr-nFixed);
280 int i1 = 3*(con.
nd1-nFixed);
284 A.block<3,3>(i0,i0) += con.
J0t * con.
prec * con.
J0;
285 dcnt(con.
ndr - nFixed)++;
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;
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;
301 B.block<3,1>(i0,0) -= con.
J0t * con.
prec * con.
err;
303 B.block<3,1>(i1,0) -= con.
J1t * con.
prec * con.
err;
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; }
315 for (
int j=0; j<3*nFree; j++)
316 if (isnan(B[j]) ) { printf(
"[SetupSys] NaN in B\n"); *(
int *)0x0 = 0; }
319 for (
int i=0; i<nFree; i++)
320 if (dcnt(i) == 0) ndc++;
323 cout <<
"[SetupSys] " << ndc <<
" disconnected nodes" << endl;
333 int nFree = nodes.size() - nFixed;
334 if(nFree < 0) nFree = 0;
336 long long t0, t1, t2, t3;
341 csp.setupBlockStructure(nFree);
348 VectorXi dcnt(nFree);
352 double lam = 1.0 + sLambda;
355 for(
size_t pi=0; pi<p2cons.size(); pi++)
361 int i0 = con.
ndr-nFixed;
362 int i1 = con.
nd1-nFixed;
366 Matrix<double,3,3> m = con.
J0t*con.
prec*con.
J0;
367 csp.addDiagBlock(m,i0);
368 dcnt(con.
ndr - nFixed)++;
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);
378 Matrix<double,3,3> m2 = con.
J0t * tp;
382 csp.addOffdiagBlock(m,i1,i0);
386 csp.addOffdiagBlock(m2,i0,i1);
394 csp.B.block<3,1>(i0*3,0) -= con.
J0t * con.
prec * con.
err;
399 csp.B.block<3,1>(i1*3,0) -= con.
J1t * con.
prec * con.
err;
407 csp.incDiagBlocks(lam);
409 csp.setupCSstructure(lam,iter==0);
413 printf(
"\n[SetupSparseSys] Block: %0.1f Cons: %0.1f CS: %0.1f\n",
414 (t1-t0)*.001, (t2-t1)*.001, (t3-t2)*.001);
417 for (
int i=0; i<nFree; i++)
418 if (dcnt(i) == 0) ndc++;
421 cout <<
"[SetupSparseSys] " << ndc <<
" disconnected nodes" << endl;
438 int ncams = nodes.size();
441 int ncons = p2cons.size();
443 if(ncams <= 0 || ncons <= 0)
451 cout <<
"[doSPA2d] No fixed frames" << endl;
454 for (
int i=0; i<ncams; i++)
472 sqMinDelta = 1e-8 * 1e-8;
473 double cost = calcCost();
475 cout << iter <<
" Initial squared cost: " << cost <<
" which is " 476 << sqrt(cost/ncons) <<
" rms error" << endl;
480 for (; iter<niter; iter++)
484 printf(
" --- Begin of iteration %d ---\n", iter);
490 long long t0, t1, t2, t3;
494 setupSparseSys(lambda,iter,useCSparse);
515 else if (useCSparse == 3)
517 int res = csp.doPCG(maxCGiters);
519 cout <<
"[DoSPA] Sparse PCG failed with error " << res << endl;
522 else if (useCSparse > 0)
524 if (csp.B.rows() != 0)
526 bool ok = csp.doChol();
528 cout <<
"[DoSBA] Sparse Cholesky failed!" << endl;
534 A.ldlt().solveInPlace(B);
540 VectorXd &BB = useCSparse ? csp.B : B;
544 double sqDiff = BB.squaredNorm();
545 if (sqDiff < sqMinDelta)
548 cout <<
"Converged with delta: " << sqrt(sqDiff) << endl;
554 for(
int i=0; i < ncams; i++)
560 nd.
trans.head<2>() += BB.segment<2>(ci);
570 double newcost = calcCost();
572 cout << iter <<
" Updated squared cost: " << newcost <<
" which is " 573 << sqrt(newcost/ncons) <<
" rms error" << endl;
589 for(
int i=0; i<ncams; i++)
601 cout << iter <<
" Downdated cost: " << cost << endl;
606 if (iter == 0 && verbose)
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));
614 double dt=1e-6*(double)(t3-t0);
616 if (print_iros_stats){
617 cerr <<
"iteration= " << iter
618 <<
"\t chi2= " << cost
620 <<
"\t cumTime= " << cumTime
621 <<
"\t kurtChi2= " << cost
640 static inline int getind(std::map<int,int> &m,
int ind)
642 std::map<int,int>::iterator it;
653 int nnodes = nodes.size();
654 if (nnodes < 2)
return 0;
656 int nlow = nnodes - window;
657 if (nlow < 1) nlow = 1;
660 cout <<
"[SPA Window] From " << nlow <<
" to " << nnodes << endl;
663 int ncons = p2cons.size();
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;
676 for (
int i=0; i<ncons; i++)
679 if (con.
ndr >= nlow || con.
nd1 >= nlow)
680 wp2cons.push_back(con);
682 if (con.
ndr >= nlow && con.
nd1 < nlow)
687 inds.insert(std::pair<int,int>(con.
nd1,wnodes.size()));
688 wnodes.push_back(nodes[con.
nd1]);
690 rinds.push_back(con.
nd1);
692 else if (con.
nd1 >= nlow && con.
ndr < nlow)
697 inds.insert(std::pair<int,int>(con.
ndr,wnodes.size()));
698 wnodes.push_back(nodes[con.
ndr]);
700 rinds.push_back(con.
ndr);
704 spa.
nFixed = wnodes.size();
706 cout <<
"[SPA Window] Fixed node count: " << spa.
nFixed << endl;
709 for (
int i=0; i<(int)wp2cons.size(); i++)
712 if (con.
nd1 >= nlow && con.
ndr >= nlow)
717 inds.insert(std::pair<int,int>(con.
ndr,wnodes.size()));
718 wnodes.push_back(nodes[con.
ndr]);
719 rinds.push_back(con.
ndr);
724 inds.insert(std::pair<int,int>(con.
nd1,wnodes.size()));
725 wnodes.push_back(nodes[con.
nd1]);
726 rinds.push_back(con.
nd1);
733 cout <<
"[SPA Window] Variable node count: " << spa.
nodes.size() - spa.
nFixed << endl;
734 cout <<
"[SPA Window] Constraint count: " << spa.
p2cons.size() << endl;
738 for (
int i=0; i<(int)wp2cons.size(); i++)
746 niter = spa.
doSPA(niter,sLambda,useCSparse);
749 for (
int i=0; i<(int)wp2cons.size(); i++)
765 void SysSPA2d::setupSparseDSIF(
int newnode)
769 int nFree = nodes.size() - nFixed;
775 csp.setupBlockStructure(nFree,
false);
780 for(
size_t pi=0; pi<p2cons.size(); pi++)
785 if (con.
ndr < newnode && con.
nd1 < newnode)
791 int i0 = con.
ndr-nFixed;
792 int i1 = con.
nd1-nFixed;
797 if (i0 != i1-1) fact = 0.99;
801 Matrix<double,3,3> m = con.
J0t*con.
prec*con.
J0;
802 csp.addDiagBlock(m,i0);
806 Matrix<double,3,3> m = con.
J1t*con.
prec*con.
J1;
807 csp.addDiagBlock(m,i1);
811 Matrix<double,3,3> m2 = con.
J0t*con.
prec*con.
J1;
812 m2 = m2 * fact * fact;
816 csp.addOffdiagBlock(m,i1,i0);
820 csp.addOffdiagBlock(m2,i0,i1);
827 csp.B.block<3,1>(i0*3,0) -= con.
J0t * con.
prec * con.
err;
829 csp.B.block<3,1>(i1*3,0) -= con.
J1t * con.
prec * con.
err;
838 csp.setupCSstructure(1.0,
true);
850 void SysSPA2d::doDSIF(
int newnode)
853 int nnodes = nodes.size();
856 int ncons = p2cons.size();
861 cout <<
"[doDSIF] No fixed frames" << endl;
866 if (newnode >= nnodes)
868 cout <<
"[doDSIF] no new nodes to add" << endl;
873 for (
int i=newnode; i<nnodes; i++)
875 nodes[i].oldtrans = nodes[i].trans;
876 nodes[i].oldarot = nodes[i].arot;
880 for (
int i=0; i<nnodes; i++)
892 double cost = calcCost();
894 cout <<
" Initial squared cost: " << cost <<
" which is " 895 << sqrt(cost/ncons) <<
" rms error" << endl;
898 long long t0, t1, t2, t3;
900 setupSparseDSIF(newnode);
903 cout <<
"[doDSIF] B = " << csp.B.transpose() << endl;
905 cout <<
"[doDSIF] A = " << endl << A << endl;
910 bool ok = csp.doChol();
912 cout <<
"[doDSIF] Sparse Cholesky failed!" << endl;
917 VectorXd &BB = csp.B;
923 for(
int i=0; i < nnodes; i++)
936 double newcost = calcCost();
938 cout <<
" Updated squared cost: " << newcost <<
" which is " 939 << sqrt(newcost/ncons) <<
" rms error" << endl;
952 cout <<
"Can't open file " << fname << endl;
959 setupSparseSys(0.0,0);
963 double *Ax = csp.A->x;
965 for (
int i=0; i<csp.csize; i++)
966 for (
int j=Ap[i]; j<Ap[i+1]; j++)
968 ofs << Ai[j] <<
" " << i << setprecision(16) <<
" " << Ax[j] << endl;
972 Eigen::IOFormat pfmt(16);
974 int nrows = A.rows();
975 int ncols = A.cols();
977 for (
int i=0; i<nrows; i++)
978 for (
int j=i; j<ncols; j++)
982 ofs << i <<
" " << j << setprecision(16) <<
" " << a << endl;
995 for (
int i=0; i<(int)p2cons.size(); i++)
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));
int nFixed
Number of fixed nodes.
static int getind(std::map< int, int > &m, int ind)
Eigen::Matrix< double, 3, 3 > J0t
Eigen::Matrix< double, 3, 1 > trans
6DOF pose as a unit quaternion and translation vector
double calcErrDist(const Node2d &nd0, const Node2d &nd1)
calculate error in distance only, no weighting
Eigen::Matrix< double, 2, 3 > w2n
Resultant transform from world to node coordinates;.
void writeSparseA(char *fname, bool useCSparse=false)
bool verbose
if we want statistics
Eigen::Matrix< double, 3, 3 > J0
jacobian with respect to frames; uses dR'/dq from Node2d calculation
EIGEN_MAKE_ALIGNED_OPERATOR_NEW int ndr
Reference pose index.
SysSPA2d holds a set of nodes and constraints for sparse pose adjustment.
void normArot()
Normalize to [-pi,+pi].
int doSPAwindowed(int window, int niter, double sLambda, int useCSparse)
EIGEN_MAKE_ALIGNED_OPERATOR_NEW int nodeId
node id - somewhat redundant, but can be useful, e.g., in KARTO links
double calcErr(const Node2d &nd0, const Node2d &nd1)
calculates projection error and stores it in <err>
bool addConstraint(int nd0, int nd1, const Vector3d &mean, const Matrix3d &prec)
Eigen::Matrix< double, 3, 3 > prec
Eigen::Matrix< double, 3, 3 > J1t
void setupSparseSys(double sLambda, int iter, int sparseType)
int nd1
Node2d index for the second node.
Eigen::Matrix< double, 3, 1 > err
error
std::vector< Node2d, Eigen::aligned_allocator< Node2d > > nodes
set of nodes (camera frames) for SPA system, indexed by position;
std::vector< Con2dP2, Eigen::aligned_allocator< Con2dP2 > > p2cons
Set of P2 constraints.
Vector3< kt_double > Vector3d
int addNode(const Vector3d &pos, int id)
Vector2< kt_double > Vector2d
bool isFixed
For SPA, is this camera fixed or free?
#define SBA_BLOCK_JACOBIAN_PCG
int doSPA(int niter, double sLambda=1.0e-4, int useCSparse=SBA_SPARSE_CHOLESKY, double initTol=1.0e-8, int CGiters=50)
void setJacobians(std::vector< Node2d, Eigen::aligned_allocator< Node2d > > &nodes)
Eigen::Matrix< double, 3, 1 > oldtrans
Eigen::Vector2d tmean
Mean vector, quaternion (inverse) and precision matrix for this constraint.
void setupSys(double sLambda)
Eigen::Matrix< double, 3, 3 > J1
void getGraph(std::vector< float > &graph)
double calcCost(bool tcost=false)