Skip to content

Commit 963b086

Browse files
committed
- Implement posegraph update, Gauss newton is unstable
- Minor changes to Gauss newton and Sparse matrix. Residual still increases slightly over iterations
1 parent de7ba96 commit 963b086

File tree

5 files changed

+173
-73
lines changed

5 files changed

+173
-73
lines changed

modules/rgbd/src/large_kinfu.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ bool LargeKinfuImpl<MatType>::updateT(const MatType& _depth)
227227
std::cout << "Current frameID: " << frameCounter << "\n";
228228
for (const auto& it : submapMgr->activeSubmaps)
229229
{
230-
//! Iterate over map?
231230
int currTrackingId = it.first;
232231
auto submapData = it.second;
233232
Ptr<Submap<MatType>> currTrackingSubmap = submapMgr->getSubmap(currTrackingId);
@@ -250,6 +249,7 @@ bool LargeKinfuImpl<MatType>::updateT(const MatType& _depth)
250249
else
251250
{
252251
std::cout << "Tracking failed" << std::endl;
252+
continue;
253253
}
254254

255255
//2. Integrate
@@ -272,7 +272,17 @@ bool LargeKinfuImpl<MatType>::updateT(const MatType& _depth)
272272

273273
}
274274
//4. Update map
275-
submapMgr->updateMap(frameCounter, newPoints, newNormals);
275+
bool isMapUpdated = submapMgr->updateMap(frameCounter, newPoints, newNormals);
276+
277+
if(isMapUpdated)
278+
{
279+
// TODO: Convert constraints to posegraph
280+
PoseGraph poseGraph = submapMgr->createPoseGraph();
281+
std::cout << "Created posegraph\n";
282+
Optimizer::optimizeGaussNewton(Optimizer::Params(), poseGraph);
283+
/* submapMgr->adjustMap(poseGraph); */
284+
285+
}
276286
std::cout << "Number of submaps: " << submapMgr->submapList.size() << "\n";
277287

278288
frameCounter++;

modules/rgbd/src/pose_graph.cpp

+89-37
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ namespace cv
1515
{
1616
namespace kinfu
1717
{
18-
1918
bool PoseGraph::isValid() const
2019
{
2120
int numNodes = int(nodes.size());
@@ -34,7 +33,7 @@ bool PoseGraph::isValid() const
3433
{
3534
int currNodeId = nodesToVisit.back();
3635
nodesToVisit.pop_back();
37-
36+
std::cout << "Visiting node: " << currNodeId << "\n";
3837
// Since each node does not maintain its neighbor list
3938
for (int i = 0; i < numEdges; i++)
4039
{
@@ -49,20 +48,20 @@ bool PoseGraph::isValid() const
4948
{
5049
nextNodeId = potentialEdge.getSourceNodeId();
5150
}
52-
51+
std::cout << "Next node: " << nextNodeId << std::endl;
5352
if (nextNodeId != -1)
5453
{
55-
if(nodesVisited.count(currNodeId) == 0)
54+
if (nodesVisited.count(currNodeId) == 0)
5655
{
5756
nodesVisited.insert(currNodeId);
57+
nodesToVisit.push_back(nextNodeId);
5858
}
59-
nodesToVisit.push_back(nextNodeId);
6059
}
6160
}
6261
}
6362

6463
isGraphConnected = (int(nodesVisited.size()) == numNodes);
65-
64+
std::cout << "IsGraphConnected: " << isGraphConnected << std::endl;
6665
bool invalidEdgeNode = false;
6766
for (int i = 0; i < numEdges; i++)
6867
{
@@ -81,26 +80,37 @@ float PoseGraph::createLinearSystem(BlockSparseMat<float, 6, 6>& H, Mat& B)
8180
{
8281
int numEdges = int(edges.size());
8382

84-
float residual = 0;
83+
float residual = 0.0f;
8584

86-
//! Lmabda to calculate error jacobian for a particular constraint
85+
// Lambda to calculate the error Jacobians w.r.t source and target poses
8786
auto calcErrorJacobian = [](Matx66f& jacSource, Matx66f& jacTarget, const Affine3f& relativePoseMeas,
88-
const Affine3f& sourcePose, const Affine3f& targetPose) -> void {
87+
const Affine3f& sourcePose, const Affine3f& targetPose) -> void
88+
{
8989
const Affine3f relativePoseMeas_inv = relativePoseMeas.inv();
9090
const Affine3f targetPose_inv = targetPose.inv();
9191
for (int i = 0; i < 6; i++)
9292
{
9393
Affine3f dError_dSource = relativePoseMeas_inv * targetPose_inv * cv::Affine3f(generatorJacobian[i]) * sourcePose;
94-
Vec3f rot = dError_dSource.rvec();
95-
Vec3f trans = dError_dSource.translation();
96-
jacSource.col(i) = Vec6f(rot(0), rot(1), rot(2), trans(0), trans(1), trans(2));
94+
Vec3f rot = dError_dSource.rvec();
95+
Vec3f trans = dError_dSource.translation();
96+
jacSource.val[i + 6*0] = rot(0);
97+
jacSource.val[i + 6*1] = rot(1);
98+
jacSource.val[i + 6*2] = rot(2);
99+
jacSource.val[i + 6*3] = trans(0);
100+
jacSource.val[i + 6*4] = trans(1);
101+
jacSource.val[i + 6*5] = trans(2);
97102
}
98103
for (int i = 0; i < 6; i++)
99104
{
100105
Affine3f dError_dSource = relativePoseMeas_inv * targetPose_inv * cv::Affine3f(-generatorJacobian[i]) * sourcePose;
101-
Vec3f rot = dError_dSource.rvec();
102-
Vec3f trans = dError_dSource.translation();
103-
jacTarget.col(i) = Vec6f(rot(0), rot(1), rot(2), trans(0), trans(1), trans(2));
106+
Vec3f rot = dError_dSource.rvec();
107+
Vec3f trans = dError_dSource.translation();
108+
jacTarget.val[i + 6*0] = rot(0);
109+
jacTarget.val[i + 6*1] = rot(1);
110+
jacTarget.val[i + 6*2] = rot(2);
111+
jacTarget.val[i + 6*3] = trans(0);
112+
jacTarget.val[i + 6*4] = trans(1);
113+
jacTarget.val[i + 6*5] = trans(2);
104114
}
105115
};
106116

@@ -117,35 +127,51 @@ float PoseGraph::createLinearSystem(BlockSparseMat<float, 6, 6>& H, Mat& B)
117127
//! Edge transformation is source to target. (relativeConstraint should be identity if no error)
118128
Affine3f poseConstraint = currEdge.transformation.inv() * relativeSourceToTarget;
119129

120-
Vec6f error;
130+
std::cout << "RelativeSourceToTarget: \n" << relativeSourceToTarget.matrix << "\n";
131+
std::cout << "Edge estimated transformation: \n" << currEdge.transformation.matrix << "\n";
132+
std::cout << "PoseConstraint: \n" << poseConstraint.matrix << "\n";
133+
134+
Matx61f error;
121135
Vec3f rot = poseConstraint.rvec();
122136
Vec3f trans = poseConstraint.translation();
123137

124-
error[0] = rot[0];
125-
error[1] = rot[1];
126-
error[2] = rot[2];
127-
error[3] = trans[0];
128-
error[4] = trans[1];
129-
error[5] = trans[2];
138+
error.val[0] = rot[0];
139+
error.val[1] = rot[1];
140+
error.val[2] = rot[2];
141+
error.val[3] = trans[0];
142+
error.val[4] = trans[1];
143+
error.val[5] = trans[2];
144+
145+
std::cout << "Error vector: \n" << error << std::endl;
130146

131147
Matx66f jacSource, jacTarget;
132148
calcErrorJacobian(jacSource, jacTarget, currEdge.transformation, sourcePose, targetPose);
149+
std::cout << "Source Jacobian: \n" << jacSource << std::endl;
150+
std::cout << "Target Jacobian: \n" << jacTarget << std::endl;
133151

134152
Matx16f errorTransposeInfo = error.t() * currEdge.information;
135153
Matx66f jacSourceTransposeInfo = jacSource.t() * currEdge.information;
136154
Matx66f jacTargetTransposeInfo = jacTarget.t() * currEdge.information;
137155

138-
residual += (errorTransposeInfo * error)[0];
156+
/* std::cout << "errorTransposeInfo: " << errorTransposeInfo << "\n"; */
157+
/* std::cout << "jacSourceTransposeInfo: " << jacSourceTransposeInfo << "\n"; */
158+
/* std::cout << "jacTargetTransposeInfo: " << jacTargetTransposeInfo << "\n"; */
159+
160+
float res = std::sqrt((errorTransposeInfo * error).val[0]);
161+
residual += res;
139162

163+
std::cout << "sourceNodeId: " << sourceNodeId << " targetNodeId: " << targetNodeId << std::endl;
140164
H.refBlock(sourceNodeId, sourceNodeId) += jacSourceTransposeInfo * jacSource;
141165
H.refBlock(targetNodeId, targetNodeId) += jacTargetTransposeInfo * jacTarget;
142166
H.refBlock(sourceNodeId, targetNodeId) += jacSourceTransposeInfo * jacTarget;
143167
H.refBlock(targetNodeId, sourceNodeId) += jacTargetTransposeInfo * jacSource;
144168

145-
B.rowRange(6 * sourceNodeId, 6 * (sourceNodeId + 1)) += errorTransposeInfo * jacSource;
146-
B.rowRange(6 * targetNodeId, 6 * (targetNodeId + 1)) += errorTransposeInfo * jacTarget;
169+
170+
B.rowRange(6 * sourceNodeId, 6 * (sourceNodeId + 1)) += (errorTransposeInfo * jacSource).reshape<6, 1>();
171+
B.rowRange(6 * targetNodeId, 6 * (targetNodeId + 1)) += (errorTransposeInfo * jacTarget).reshape<6, 1>();
147172
}
148173

174+
std::cout << "Residual value: " << residual << std::endl;
149175
return residual;
150176
}
151177

@@ -161,6 +187,7 @@ PoseGraph PoseGraph::update(const Mat& deltaVec)
161187
Vec6f delta = deltaVec.rowRange(6 * currentNodeId, 6 * (currentNodeId + 1));
162188
Affine3f pose = nodes.at(currentNodeId).getPose();
163189
Affine3f currDelta = Affine3f(Vec3f(delta.val), Vec3f(delta.val + 3));
190+
std::cout << "Current Delta for node ID: " << currentNodeId << " \n" << currDelta.matrix << std::endl;
164191
Affine3f updatedPose = currDelta * pose;
165192

166193
updatedPoseGraph.nodes.at(currentNodeId).setPose(updatedPose);
@@ -169,6 +196,20 @@ PoseGraph PoseGraph::update(const Mat& deltaVec)
169196
return updatedPoseGraph;
170197
}
171198

199+
Mat PoseGraph::getVector()
200+
{
201+
int numNodes = int(nodes.size());
202+
Mat vector(6 * numNodes, 1, CV_32F, Scalar(0));
203+
for(int currentNodeId = 0; currentNodeId < numNodes; currentNodeId++)
204+
{
205+
Affine3f pose = nodes.at(currentNodeId).getPose();
206+
Vec3f rot = pose.rvec();
207+
Vec3f trans = pose.translation();
208+
vector.rowRange(6 * currentNodeId, 6 * (currentNodeId+1)) = Vec6f(rot.val[0], rot.val[1], rot.val[2], trans.val[0], trans.val[1], trans.val[2]);
209+
}
210+
return vector;
211+
}
212+
172213
//! NOTE: We follow left-composition for the infinitesimal pose update
173214
void Optimizer::optimizeGaussNewton(const Optimizer::Params& params, PoseGraph& poseGraph)
174215
{
@@ -187,38 +228,49 @@ void Optimizer::optimizeGaussNewton(const Optimizer::Params& params, PoseGraph&
187228
std::cout << "Optimizing posegraph with nodes: " << numNodes << " edges: " << numEdges << std::endl;
188229

189230
int iter = 0;
190-
//! converged is set to true when error/residual is within some limit
191-
bool converged = false;
192231
float prevResidual = std::numeric_limits<float>::max();
193232
do
194233
{
234+
std::cout << "Current iteration: " << iter << std::endl;
195235
//! ApproxH = Approximate Hessian = J^T * information * J
196236
BlockSparseMat<float, 6, 6> ApproxH(numNodes);
197-
Mat B(6 * numNodes, 1, CV_32F);
237+
Mat B = cv::Mat::zeros(6 * numNodes, 1, CV_32F);
198238

199239
float currentResidual = poseGraph.createLinearSystem(ApproxH, B);
200-
240+
std::cout << "Number of non-zero blocks in H: " << ApproxH.nonZeroBlocks() << "\n";
201241
Mat delta(6 * numNodes, 1, CV_32F);
242+
Mat X = poseGraph.getVector();
202243
bool success = sparseSolve(ApproxH, B, delta);
203244
if (!success)
204245
{
205246
CV_Error(Error::StsNoConv, "Sparse solve failed");
206247
return;
207248
}
249+
std::cout << "delta update: " << delta << "\n delta norm: " << cv::norm(delta) << " X norm: " << cv::norm(X) << std::endl;
250+
208251
//! Check delta
252+
if(cv::norm(delta) < 1e-6f * (cv::norm(X) + 1e-6f))
253+
{
254+
std::cout << "Delta norm[" << cv::norm(delta) << "] < 1e-6f * X norm[" << cv::norm(X) <<"] + 1e-6f\n";
255+
break;
256+
}
257+
std::cout << " Current residual: " << currentResidual << " Prev residual: " << prevResidual << "\n";
209258

210259
//! TODO: Improve criterion and allow small increments in residual
211-
if((currentResidual - prevResidual) > params.maxAcceptableResIncre)
260+
if ((currentResidual - prevResidual) > params.maxAcceptableResIncre)
261+
{
262+
std::cout << "Current residual increased from prevResidual by at least " << params.maxAcceptableResIncre << std::endl;
212263
break;
213-
if ((currentResidual - params.minResidual) < 0.00001f)
214-
converged = true;
215-
264+
}
265+
//! If updates don't improve a lot means loss function basin has been reached
266+
if ((currentResidual - params.minResidual) < 1e-5f)
267+
{
268+
std::cout << "Gauss newton converged \n";
269+
break;
270+
}
216271
poseGraph = poseGraph.update(delta);
217-
std::cout << " Current residual: " << currentResidual << " Prev residual: " << prevResidual << "\n";
218-
219272
prevResidual = currentResidual;
220-
221-
} while (iter < params.maxNumIters && !converged);
273+
} while (iter < params.maxNumIters);
222274
}
223275
} // namespace kinfu
224276
} // namespace cv

modules/rgbd/src/pose_graph.hpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ namespace kinfu
1818
struct PoseGraphNode
1919
{
2020
public:
21-
explicit PoseGraphNode(int _nodeId, const Affine3f& _pose) : nodeId(_nodeId), isFixed(false), pose(_pose) {}
22-
virtual ~PoseGraphNode();
21+
explicit PoseGraphNode(int _nodeId, const Affine3f& _pose) : nodeId(_nodeId), isFixed(false), pose(_pose) {}
22+
virtual ~PoseGraphNode() = default;
2323

2424
int getId() const { return nodeId; }
2525
Affine3f getPose() const { return pose; }
@@ -64,7 +64,8 @@ struct PoseGraphEdge
6464
//! Jose Luis Blanco
6565
//! Compactly represents the jacobian of the SE3 generator
6666
// clang-format off
67-
static const std::array<Matx44f, 6> generatorJacobian = { // alpha
67+
static const std::array<Matx44f, 6> generatorJacobian = {
68+
// alpha
6869
Matx44f(0, 0, 0, 0,
6970
0, 0, -1, 0,
7071
0, 1, 0, 0,
@@ -126,11 +127,12 @@ class PoseGraph
126127
int getNumNodes() const { return nodes.size(); }
127128
int getNumEdges() const { return edges.size(); }
128129

130+
Mat getVector();
131+
129132
//! @brief: Constructs a linear system and returns the residual of the current system
130133
float createLinearSystem(BlockSparseMat<float, 6, 6>& H, Mat& B);
131134

132135
private:
133-
134136
NodeVector nodes;
135137
EdgeVector edges;
136138
};
@@ -144,7 +146,7 @@ struct Params
144146
float maxAcceptableResIncre;
145147

146148
// TODO: Refine these constants
147-
Params() : maxNumIters(40), minResidual(1e-3f), maxAcceptableResIncre(1e-3f){};
149+
Params() : maxNumIters(40), minResidual(1e-3f), maxAcceptableResIncre(1e-2f){};
148150
virtual ~Params() = default;
149151
};
150152

0 commit comments

Comments
 (0)