41 #include <Eigen/Cholesky> 43 using namespace Eigen;
55 gettimeofday(&tv,NULL);
56 long long ts = tv.tv_sec;
67 void Node2d::setTransform()
69 w2n(0,0) = w2n(1,1) = cos(arot);
73 w2n.col(2) = -w2n*trans;
82 dRdx(0,0) = dRdx(1,1) = w2n(1,0);
84 dRdx(1,0) = -w2n(0,0);
90 void Con2dP2::setJacobians(std::vector<
Node2d,Eigen::aligned_allocator<Node2d> > &nodes)
94 Matrix<double,3,1> &tr = nr.
trans;
96 Matrix<double,3,1> &t1 = n1.
trans;
99 Eigen::Matrix<double,2,1> pc = nr.
w2n * t1;
105 J0.block<2,2>(0,0) = -nr.
w2n.block<2,2>(0,0);
110 Eigen::Matrix<double,2,1> pwt;
111 pwt = (t1-tr).head(2);
114 Eigen::Matrix<double,2,1> dp = nr.
dRdx * pwt;
115 J0.block<2,1>(0,2) = dp;
118 J0.block<1,2>(2,0).setZero();
123 J0t = J0.transpose();
130 J1.block<2,2>(0,0) = nr.
w2n.block<2,2>(0,0);
133 J1.block<2,1>(0,2).setZero();
136 J1.block<1,2>(2,0).setZero();
142 J1t = J1.transpose();
154 err.block<2,1>(0,0) = nd0.
w2n * nd1.
trans - tmean;
155 double aerr = (nd1.
arot - nd0.
arot) - amean;
156 if (aerr > M_PI) aerr -= 2.0*M_PI;
157 if (aerr < -M_PI) aerr += 2.0*M_PI;
162 return err.dot(prec * err);
169 Vector2d derr = nd0.
w2n * nd1.
trans - tmean;
170 return derr.dot(derr);
177 double SysSPA2d::calcCost(
bool tcost)
184 for(
size_t i=0; i<p2cons.size(); i++)
195 for(
size_t i=0; i<p2cons.size(); i++)
211 int SysSPA2d::addNode(
const Vector3d &pos,
int id)
217 nd.
trans.head(2) = pos.head(2);
223 int ndi = nodes.size();
234 bool SysSPA2d::addConstraint(
int ndi0,
int ndi1,
const Vector3d &mean,
235 const Matrix3d &prec)
237 int ni0 = -1, ni1 = -1;
238 for (
int i=0; i<(int)nodes.size(); i++)
240 if (nodes[i].nodeId == ndi0)
242 if (nodes[i].nodeId == ndi1)
245 if (ni0 < 0 || ni1 < 0)
return false;
251 con.
tmean = mean.head(2);
254 p2cons.push_back(con);
262 void SysSPA2d::setupSys(
double sLambda)
266 int nFree = nodes.size() - nFixed;
267 A.setZero(3*nFree,3*nFree);
269 VectorXi dcnt(nFree);
273 double lam = 1.0 + sLambda;
276 for(
size_t pi=0; pi<p2cons.size(); pi++)
282 int i0 = 3*(con.
ndr-nFixed);
283 int i1 = 3*(con.
nd1-nFixed);
287 A.block<3,3>(i0,i0) += con.
J0t * con.
prec * con.
J0;
288 dcnt(con.
ndr - nFixed)++;
292 dcnt(con.
nd1 - nFixed)++;
293 Matrix<double,3,3> tp = con.
prec * con.
J1;
294 A.block<3,3>(i1,i1) += con.
J1t * tp;
297 A.block<3,3>(i0,i1) += con.
J0t * con.
prec * con.
J1;
298 A.block<3,3>(i1,i0) += con.
J1t * con.
prec * con.
J0;
304 B.block<3,1>(i0,0) -= con.
J0t * con.
prec * con.
err;
306 B.block<3,1>(i1,0) -= con.
J1t * con.
prec * con.
err;
314 for (
int i=0; i<3*nFree; i++)
315 for (
int j=0; j<3*nFree; j++)
316 if (isnan(A(i,j)) ) { printf(
"[SetupSys] NaN in A\n"); *(
int *)0x0 = 0; }
318 for (
int j=0; j<3*nFree; j++)
319 if (isnan(B[j]) ) { printf(
"[SetupSys] NaN in B\n"); *(
int *)0x0 = 0; }
322 for (
int i=0; i<nFree; i++)
323 if (dcnt(i) == 0) ndc++;
326 cout <<
"[SetupSys] " << ndc <<
" disconnected nodes" << endl;
332 void SysSPA2d::setupSparseSys(
double sLambda,
int iter,
int sparseType)
336 int nFree = nodes.size() - nFixed;
338 long long t0, t1, t2, t3;
342 csp.setupBlockStructure(nFree);
344 csp.setupBlockStructure(0);
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);
393 csp.B.block<3,1>(i0*3,0) -= con.
J0t * con.
prec * con.
err;
395 csp.B.block<3,1>(i1*3,0) -= con.
J1t * con.
prec * con.
err;
402 csp.incDiagBlocks(lam);
404 csp.setupCSstructure(lam,iter==0);
408 printf(
"\n[SetupSparseSys] Block: %0.1f Cons: %0.1f CS: %0.1f\n",
409 (t1-t0)*.001, (t2-t1)*.001, (t3-t2)*.001);
412 for (
int i=0; i<nFree; i++)
413 if (dcnt(i) == 0) ndc++;
416 cout <<
"[SetupSparseSys] " << ndc <<
" disconnected nodes" << endl;
429 int SysSPA2d::doSPA(
int niter,
double sLambda,
int useCSparse,
double initTol,
433 int ncams = nodes.size();
436 int ncons = p2cons.size();
441 cout <<
"[doSPA2d] No fixed frames" << endl;
444 for (
int i=0; i<ncams; i++)
462 sqMinDelta = 1e-8 * 1e-8;
463 double cost = calcCost();
465 cout << iter <<
" Initial squared cost: " << cost <<
" which is " 466 << sqrt(cost/ncons) <<
" rms error" << endl;
470 for (; iter<niter; iter++)
476 long long t0, t1, t2, t3;
479 setupSparseSys(lambda,iter,useCSparse);
489 if (csp.B.rows() != 0)
491 int iters = csp.doBPCG(maxCGiters,initTol,iter);
493 cout <<
"[Block PCG] " << iters <<
" iterations" << endl;
498 else if (useCSparse == 3)
500 int res = csp.doPCG(maxCGiters);
502 cout <<
"[DoSPA] Sparse PCG failed with error " << res << endl;
505 else if (useCSparse > 0)
507 if (csp.B.rows() != 0)
509 bool ok = csp.doChol();
511 cout <<
"[DoSBA] Sparse Cholesky failed!" << endl;
517 A.ldlt().solveInPlace(B);
523 VectorXd &BB = useCSparse ? csp.B : B;
527 double sqDiff = BB.squaredNorm();
528 if (sqDiff < sqMinDelta)
531 cout <<
"Converged with delta: " << sqrt(sqDiff) << endl;
537 for(
int i=0; i < ncams; i++)
543 nd.
trans.head<2>() += BB.segment<2>(ci);
553 double newcost = calcCost();
555 cout << iter <<
" Updated squared cost: " << newcost <<
" which is " 556 << sqrt(newcost/ncons) <<
" rms error" << endl;
572 for(
int i=0; i<ncams; i++)
584 cout << iter <<
" Downdated cost: " << cost << endl;
589 if (iter == 0 && verbose)
591 printf(
"[SPA] Setup: %0.2f ms Solve: %0.2f ms Update: %0.2f ms\n",
592 0.001*(
double)(t1-t0),
593 0.001*(
double)(t2-t1),
594 0.001*(
double)(t3-t2));
597 double dt=1e-6*(double)(t3-t0);
599 if (print_iros_stats){
600 cerr <<
"iteration= " << iter
601 <<
"\t chi2= " << cost
603 <<
"\t cumTime= " << cumTime
604 <<
"\t kurtChi2= " << cost
623 static inline int getind(std::map<int,int> &m,
int ind)
625 std::map<int,int>::iterator it;
633 int SysSPA2d::doSPAwindowed(
int window,
int niter,
double sLambda,
int useCSparse)
636 int nnodes = nodes.size();
637 if (nnodes < 2)
return 0;
639 int nlow = nnodes - window;
640 if (nlow < 1) nlow = 1;
643 cout <<
"[SPA Window] From " << nlow <<
" to " << nnodes << endl;
646 int ncons = p2cons.size();
653 std::vector<Node2d,Eigen::aligned_allocator<Node2d> > &wnodes = spa.
nodes;
654 std::vector<Con2dP2,Eigen::aligned_allocator<Con2dP2> > &wp2cons = spa.
p2cons;
655 std::map<int,int> inds;
656 std::vector<int> rinds;
659 for (
int i=0; i<ncons; i++)
662 if (con.
ndr >= nlow || con.
nd1 >= nlow)
663 wp2cons.push_back(con);
665 if (con.
ndr >= nlow && con.
nd1 < nlow)
670 inds.insert(std::pair<int,int>(con.
nd1,wnodes.size()));
671 wnodes.push_back(nodes[con.
nd1]);
673 rinds.push_back(con.
nd1);
675 else if (con.
nd1 >= nlow && con.
ndr < nlow)
680 inds.insert(std::pair<int,int>(con.
ndr,wnodes.size()));
681 wnodes.push_back(nodes[con.
ndr]);
683 rinds.push_back(con.
ndr);
687 spa.
nFixed = wnodes.size();
689 cout <<
"[SPA Window] Fixed node count: " << spa.
nFixed << endl;
692 for (
int i=0; i<(int)wp2cons.size(); i++)
695 if (con.
nd1 >= nlow && con.
ndr >= nlow)
700 inds.insert(std::pair<int,int>(con.
ndr,wnodes.size()));
701 wnodes.push_back(nodes[con.
ndr]);
702 rinds.push_back(con.
ndr);
707 inds.insert(std::pair<int,int>(con.
nd1,wnodes.size()));
708 wnodes.push_back(nodes[con.
nd1]);
709 rinds.push_back(con.
nd1);
716 cout <<
"[SPA Window] Variable node count: " << spa.
nodes.size() - spa.
nFixed << endl;
717 cout <<
"[SPA Window] Constraint count: " << spa.
p2cons.size() << endl;
721 for (
int i=0; i<(int)wp2cons.size(); i++)
729 niter = spa.
doSPA(niter,sLambda,useCSparse);
732 for (
int i=0; i<(int)wp2cons.size(); i++)
748 void SysSPA2d::setupSparseDSIF(
int newnode)
752 int nFree = nodes.size() - nFixed;
758 csp.setupBlockStructure(nFree,
false);
763 for(
size_t pi=0; pi<p2cons.size(); pi++)
768 if (con.
ndr < newnode && con.
nd1 < newnode)
774 int i0 = con.
ndr-nFixed;
775 int i1 = con.
nd1-nFixed;
780 if (i0 != i1-1) fact = 0.99;
784 Matrix<double,3,3> m = con.
J0t*con.
prec*con.
J0;
785 csp.addDiagBlock(m,i0);
789 Matrix<double,3,3> m = con.
J1t*con.
prec*con.
J1;
790 csp.addDiagBlock(m,i1);
794 Matrix<double,3,3> m2 = con.
J0t*con.
prec*con.
J1;
795 m2 = m2 * fact * fact;
799 csp.addOffdiagBlock(m,i1,i0);
803 csp.addOffdiagBlock(m2,i0,i1);
810 csp.B.block<3,1>(i0*3,0) -= con.
J0t * con.
prec * con.
err;
812 csp.B.block<3,1>(i1*3,0) -= con.
J1t * con.
prec * con.
err;
821 csp.setupCSstructure(1.0,
true);
833 void SysSPA2d::doDSIF(
int newnode)
836 int nnodes = nodes.size();
839 int ncons = p2cons.size();
844 cout <<
"[doDSIF] No fixed frames" << endl;
849 if (newnode >= nnodes)
851 cout <<
"[doDSIF] no new nodes to add" << endl;
856 for (
int i=newnode; i<nnodes; i++)
858 nodes[i].oldtrans = nodes[i].trans;
859 nodes[i].oldarot = nodes[i].arot;
863 for (
int i=0; i<nnodes; i++)
875 double cost = calcCost();
877 cout <<
" Initial squared cost: " << cost <<
" which is " 878 << sqrt(cost/ncons) <<
" rms error" << endl;
881 long long t0, t1, t2, t3;
883 setupSparseDSIF(newnode);
886 cout <<
"[doDSIF] B = " << csp.B.transpose() << endl;
888 cout <<
"[doDSIF] A = " << endl << A << endl;
893 bool ok = csp.doChol();
895 cout <<
"[doDSIF] Sparse Cholesky failed!" << endl;
900 VectorXd &BB = csp.B;
906 for(
int i=0; i < nnodes; i++)
919 double newcost = calcCost();
921 cout <<
" Updated squared cost: " << newcost <<
" which is " 922 << sqrt(newcost/ncons) <<
" rms error" << endl;
935 cout <<
"Can't open file " << fname << endl;
942 setupSparseSys(0.0,0);
946 double *Ax = csp.A->x;
948 for (
int i=0; i<csp.csize; i++)
949 for (
int j=Ap[i]; j<Ap[i+1]; j++)
951 ofs << Ai[j] <<
" " << i << setprecision(16) <<
" " << Ax[j] << endl;
955 Eigen::IOFormat pfmt(16);
957 int nrows = A.rows();
958 int ncols = A.cols();
960 for (
int i=0; i<nrows; i++)
961 for (
int j=i; j<ncols; j++)
965 ofs << i <<
" " << j << setprecision(16) <<
" " << a << endl;
976 void SysSPA2d::getGraph(std::vector<float> &graph)
978 for (
int i=0; i<(int)p2cons.size(); i++)
983 graph.push_back(nd0.
trans(0));
984 graph.push_back(nd0.
trans(1));
985 graph.push_back(nd1.
trans(0));
986 graph.push_back(nd1.
trans(1));
1006 void SysSPA2d::setupSparseDSIF(
int newnode)
1008 cout <<
"[SetupDSIF] at " << newnode << endl;
1012 int nFree = nodes.size() - nFixed;
1018 csp.setupBlockStructure(nFree,
false);
1023 for(
size_t pi=0; pi<p2cons.size(); pi++)
1028 if (con.
ndr < newnode && con.
nd1 < newnode)
1034 Vector2d dRdth = nodes[con.
ndr].dRdx.transpose() * con.
tmean;
1037 Matrix3d Jt = J.transpose();
1039 u.head(2) = nodes[con.
ndr].trans.head(2);
1040 u(2) = nodes[con.
ndr].arot;
1042 f.head(2) = u.head(2) + nodes[con.
ndr].w2n.transpose().block<2,2>(0,0) * con.
tmean;
1043 f(2) = u(2) + con.
amean;
1044 if (f(2) > M_PI) f(2) -= 2.0*M_PI;
1045 if (f(2) < M_PI) f(2) += 2.0*M_PI;
1048 cout <<
"[SetupDSIF] u = " << u.transpose() << endl;
1049 cout <<
"[SetupDSIF] f = " << f.transpose() << endl;
1050 cout <<
"[SetupDSIF] fo = " << nodes[con.
nd1].trans.transpose() << endl;
1054 int i0 = con.
ndr-nFixed;
1055 int i1 = con.
nd1-nFixed;
1057 if (i0 != i1-1)
continue;
1061 Matrix<double,3,3> m = Jt*con.
prec*J;
1062 csp.addDiagBlock(m,i0);
1066 Matrix<double,3,3> m = con.
prec;
1067 csp.addDiagBlock(m,i1);
1071 Matrix<double,3,3> m2 = -con.
prec * J;
1077 csp.addOffdiagBlock(m,i1,i0);
1081 csp.addOffdiagBlock(m2,i0,i1);
1088 Vector3d Juf = J*u - f;
1089 if (Juf(2) > M_PI) Juf(2) -= 2.0*M_PI;
1090 if (Juf(2) < M_PI) Juf(2) += 2.0*M_PI;
1093 csp.B.block<3,1>(i0*3,0) += Jt * con.
prec * Juf;
1097 csp.B.block<3,1>(i1*3,0) -= con.
prec * Juf;
1107 csp.setupCSstructure(1.0,
true);
1120 void SysSPA2d::doDSIF(
int newnode,
bool useCSparse)
1123 int nnodes = nodes.size();
1126 int ncons = p2cons.size();
1131 cout <<
"[doDSIF] No fixed frames" << endl;
1136 if (newnode >= nnodes)
1138 cout <<
"[doDSIF] no new nodes to add" << endl;
1144 for (
int i=0; i<nnodes; i++)
1156 double cost = calcCost();
1158 cout <<
" Initial squared cost: " << cost <<
" which is " 1159 << sqrt(cost/ncons) <<
" rms error" << endl;
1164 csp.setupBlockStructure(1,
false);
1168 csp.addDiagBlock(m,0);
1170 u.head(2) = nodes[0].trans.head(2);
1171 u(2) = nodes[0].arot;
1172 csp.B.head(3) = u*1000000;
1174 cout <<
"[doDSIF] B = " << csp.B.transpose() << endl;
1181 long long t0, t1, t2, t3;
1184 setupSparseDSIF(newnode);
1189 cout <<
"[doDSIF] B = " << csp.B.transpose() << endl;
1191 cout <<
"[doDSIF] A = " << endl << A << endl;
1198 bool ok = csp.doChol();
1200 cout <<
"[doDSIF] Sparse Cholesky failed!" << endl;
1203 A.ldlt().solveInPlace(B);
1208 VectorXd &BB = useCSparse ? csp.B : B;
1210 cout <<
"[doDSIF] RES = " << BB.transpose() << endl;
1214 for(
int i=0; i < nnodes; i++)
1218 nd.
trans.head<2>() = BB.segment<2>(ci);
1227 double newcost = calcCost();
1229 cout <<
" Updated squared cost: " << newcost <<
" which is " 1230 << sqrt(newcost/ncons) <<
" rms error" << endl;
1241 void SysSPA2d::setupSparseDSIF(
int newnode)
1243 cout <<
"[SetupDSIF] at " << newnode << endl;
1247 int nFree = nodes.size() - nFixed;
1253 csp.setupBlockStructure(nFree,
false);
1258 for(
size_t pi=0; pi<p2cons.size(); pi++)
1263 if (con.
ndr < newnode && con.
nd1 < newnode)
1269 int i0 = con.
ndr-nFixed;
1270 int i1 = con.
nd1-nFixed;
1272 if (i0 != i1-1)
continue;
1276 Matrix<double,3,3> m = con.
J0t*con.
prec*con.
J0;
1277 csp.addDiagBlock(m,i0);
1281 Matrix<double,3,3> m = con.
J1t*con.
prec*con.
J1;
1282 csp.addDiagBlock(m,i1);
1286 Matrix<double,3,3> m2 = con.
J0t*con.
prec*con.
J1;
1290 csp.addOffdiagBlock(m,i1,i0);
1294 csp.addOffdiagBlock(m2,i0,i1);
1305 u.head(2) = nodes[con.
ndr].trans.head(2);
1306 u(2) = nodes[con.
ndr].arot;
1307 Vector3d bm = con.
err + con.
J0 * u;
1308 csp.B.block<3,1>(i0*3,0) += (bm.transpose() * con.
prec * con.
J0).transpose();
1313 u.head(2) = nodes[con.
nd1].trans.head(2);
1314 u(2) = nodes[con.
nd1].arot;
1315 Vector3d bm = con.
err + con.
J1 * u;
1316 csp.B.block<3,1>(i1*3,0) += (bm.transpose() * con.
prec * con.
J1).transpose();
1326 csp.setupCSstructure(1.0,
true);
1339 void SysSPA2d::doDSIF(
int newnode,
bool useCSparse)
1342 int nnodes = nodes.size();
1345 int ncons = p2cons.size();
1350 cout <<
"[doDSIF] No fixed frames" << endl;
1355 if (newnode >= nnodes)
1357 cout <<
"[doDSIF] no new nodes to add" << endl;
1361 for (
int i=0; i<nnodes; i++)
1373 double cost = calcCost();
1375 cout <<
" Initial squared cost: " << cost <<
" which is " 1376 << sqrt(cost/ncons) <<
" rms error" << endl;
1382 long long t0, t1, t2, t3;
1385 setupSparseDSIF(newnode);
1390 cout <<
"[doDSIF] B = " << csp.B.transpose() << endl;
1392 cout <<
"[doDSIF] A = " << endl << A << endl;
1399 bool ok = csp.doChol();
1401 cout <<
"[doDSIF] Sparse Cholesky failed!" << endl;
1404 A.ldlt().solveInPlace(B);
1409 VectorXd &BB = useCSparse ? csp.B : B;
1411 cout <<
"[doDSIF] RES = " << BB.transpose() << endl;
1415 for(
int i=0; i < nnodes; i++)
1419 nd.
trans.head<2>() = BB.segment<2>(ci);
1428 double newcost = calcCost();
1430 cout <<
" Updated squared cost: " << newcost <<
" which is " 1431 << sqrt(newcost/ncons) <<
" rms error" << endl;
1440 void SysSPA2d::removeNode(
int id)
1443 for (
int i=0; i<(int)nodes.size(); i++)
1445 if (nodes[i].nodeId == ind)
1448 if (ind < 0)
return;
1451 nodes.erase(nodes.begin() + ind);
1457 while (i <(
int)p2cons.size())
1459 std::vector<Con2dP2,Eigen::aligned_allocator<Con2dP2> >::iterator iter = p2cons.begin() + i;
1460 if (iter->ndr == ind || iter->nd1 == ind)
1466 if (iter->ndr > ind) iter->ndr--;
1467 if (iter->nd1 > ind) iter->nd1--;
1475 bool SysSPA2d::removeConstraint(
int ndi0,
int ndi1)
1477 int ni0 = -1, ni1 = -1;
1478 for (
int i=0; i<(int)nodes.size(); i++)
1480 if (nodes[i].nodeId == ndi0)
1482 if (nodes[i].nodeId == ndi1)
1485 if (ni0 < 0 || ni1 < 0)
return false;
1488 while (i <(
int)p2cons.size())
1490 std::vector<Con2dP2,Eigen::aligned_allocator<Con2dP2> >::iterator iter = p2cons.begin() + i;
1491 if (iter->ndr == ni0 && iter->nd1 == ni1)
int doSPA(int niter, double sLambda=1.0e-4, int useCSparse=SBA_SPARSE_CHOLESKY, double initTol=1.0e-8, int CGiters=50)
Eigen::Vector2d tmean
Mean vector, quaternion (inverse) and precision matrix for this constraint.
Eigen::Matrix< double, 3, 3 > J1
Eigen::Matrix< double, 3, 3 > J0
jacobian with respect to frames; uses dR'/dq from Node2d calculation
void setJacobians(std::vector< Node2d, Eigen::aligned_allocator< Node2d > > &nodes)
bool isFixed
For SPA, is this camera fixed or free?
int nd1
Node2d index for the second node.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW int nodeId
node id - somewhat redundant, but can be useful, e.g., in KARTO links
Eigen::Matrix< double, 3, 3 > J1t
Eigen::Matrix< double, 3, 3 > J0t
Eigen::Matrix< double, 3, 3 > prec
double calcErr(const Node2d &nd0, const Node2d &nd1)
calculates projection error and stores it in <err>
Eigen::Matrix< double, 3, 1 > trans
6DOF pose as a unit quaternion and translation vector
Eigen::Matrix< double, 3, 1 > err
error
void writeSparseA(const char *fname, SysSBA &sba)
void normArot()
Normalize to [-pi,+pi].
bool verbose
if we want statistics
Eigen::Matrix< double, 3, 1 > oldtrans
Eigen::Matrix< double, 2, 3 > w2n
Resultant transform from world to node coordinates;.
std::vector< Con2dP2, Eigen::aligned_allocator< Con2dP2 > > p2cons
Set of P2 constraints.
std::vector< Node2d, Eigen::aligned_allocator< Node2d > > nodes
set of nodes (camera frames) for SPA system, indexed by position;
static int getind(std::map< int, int > &m, int ind)
EIGEN_MAKE_ALIGNED_OPERATOR_NEW int ndr
Reference pose index.
#define SBA_BLOCK_JACOBIAN_PCG
SysSPA2d holds a set of nodes and constraints for sparse pose adjustment.
int nFixed
Number of fixed nodes.
double calcErrDist(const Node2d &nd0, const Node2d &nd1)
calculate error in distance only, no weighting