Skip to content

Commit 6000835

Browse files
authored
Feature/ef plane derivative from 3x3 eigen decomposition to homogeneous 4x4 (#67)
* addint EF dense homog (num unstable) and modyfying Dense for plane orientation and depth * EF hessian plane centered. WIP * center gradient on 3x3 inverse minus plan. Does not work. WIP * homogeneous gradient by the 3x3 eigen decomposiotion. Hessian is accurate to numerical approximoation * adding 4 EF python bindings dense, dense homog, alternating and with plane. Methods have been tested numerically successfully * removing comment
1 parent eaf2e44 commit 6000835

10 files changed

Lines changed: 345 additions & 56 deletions

src/plane-surfaces/CMakeLists.txt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ SET(sources
1010
utils_lie_differentiation.cpp
1111
factors/nodePlane4d.cpp
1212
factors/factor1Pose1Plane4d.cpp
13-
factors/EigenFactorPlaneCenter.cpp
14-
factors/EigenFactorPoint.cpp
1513
factors/PiFactorPlane.cpp
14+
factors/BaregEFPlane.cpp
1615
factors/EigenFactorPlaneBase.cpp
16+
factors/EigenFactorPlaneCenter.cpp
17+
factors/EigenFactorPoint.cpp
1718
factors/EigenFactorPlaneDense.cpp
18-
factors/BaregEFPlane.cpp
19+
factors/EigenFactorPlaneDenseHomog.cpp
1920
factors/EigenFactorPlaneCenter2.cpp
2021
)
2122

@@ -29,12 +30,13 @@ SET(headers
2930
mrob/utils_lie_differentiation.hpp
3031
mrob/factors/nodePlane4d.hpp
3132
mrob/factors/factor1Pose1plane4d.hpp
32-
mrob/factors/EigenFactorPlaneCenter.hpp
33-
mrob/factors/EigenFactorPoint.hpp
3433
mrob/factors/PiFactorPlane.hpp
34+
mrob/factors/BaregEFPlane.hpp
3535
mrob/factors/EigenFactorBase.hpp
36+
mrob/factors/EigenFactorPlaneCenter.hpp
37+
mrob/factors/EigenFactorPoint.hpp
3638
mrob/factors/EigenFactorDense.hpp
37-
mrob/factors/BaregEFPlane.hpp
39+
mrob/factors/EigenFactorDenseHomog.hpp
3840
mrob/factors/EigenFactorPlaneCenter2.hpp
3941
)
4042

src/plane-surfaces/create_points.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ CreatePoints::CreatePoints(uint_t numberPoints, uint_t numberPlanes, uint_t numb
121121
initialPose_(initial_pose),
122122
numberPoses_(numberPoses)
123123
{
124-
std::cout << "samples bias = " << noiseBias_ << "\n and point noise = " << noisePerPoint_ << std::endl;
124+
//std::cout << "samples bias = " << noiseBias_ << "\n and point noise = " << noisePerPoint_ << std::endl;
125125
// 0) initialize vectors and variables
126126
X_.reserve(numberPoses_);
127127
pointId_.reserve(numberPoses_);

src/plane-surfaces/factors/EigenFactorPlaneCenter2.cpp

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ void EigenFactorPlaneCenter2::evaluate_jacobians()
6565
// Cross term dpi * dQ*pi, where dpi/dxi_i = Q^-1 dQ/dxi_i pi.
6666
// This does not do anything? we should test this variant as well
6767
Mat6 grad_pi_time_Q_grad;
68-
grad_pi_time_Q_grad.triangularView<Eigen::Upper>() = 2.0*grad.transpose()*Q_inv_no_kernel_*grad;
68+
grad_pi_time_Q_grad.triangularView<Eigen::Upper>() = 2.0*grad.transpose()*Q_inv_minus_plane_*grad;
6969

7070
// sum of all terms
7171
hessian.triangularView<Eigen::Upper>() =
@@ -117,30 +117,29 @@ void EigenFactorPlaneCenter2::estimate_plane()
117117
// Only needs Lower View from Q (https://eigen.tuxfamily.org/dox/classEigen_1_1SelfAdjointEigenSolver.html)
118118
Eigen::SelfAdjointEigenSolver<Mat3> es;
119119
es.computeDirect(accumulatedCenterQ_.topLeftCorner<3,3>());
120-
planeEstimationUnit_.head<3>() = es.eigenvectors().col(0);
121-
planeEstimationUnit_(3) = 0.0;
122120

123-
planeEstimation_ = SE3(Tcenter_).inv().transform_plane(planeEstimationUnit_);
121+
Mat41 planeEstimationCenter;
122+
planeEstimationCenter.head<3>() = es.eigenvectors().col(0);
123+
planeEstimationCenter(3) = 0.0;
124+
planeEstimation_ = Tcenter_.transpose() * planeEstimationCenter;
124125

125126
//std::cout << "\n and solution plane = \n" << planeEstimationUnit_ << std::endl;
126127
//std::cout << "plane estimation error (0): " << es.eigenvalues() << std::endl;
127128

128-
// Solution for the inverse without kernel from pi solution. This is an overkill, but it is for comparisons.
129-
Eigen::SelfAdjointEigenSolver<Mat4> es4;
130-
es4.compute(accumulatedQ_);
131-
matData_t lambda_plane = es4.eigenvalues()(0);
132-
Mat4 other_eigenvectors, other_eigenvectors_multiplied;
133-
other_eigenvectors = es4.eigenvectors();
129+
// Calculate almost inverse for the 3x3
130+
matData_t lambda_plane = es.eigenvalues()(0);
131+
Mat3 other_eigenvectors, other_eigenvectors_multiplied;
132+
other_eigenvectors = es.eigenvectors();
134133
other_eigenvectors.col(0) *= 0.0;
135134
//std::cout << "other eignevect \n" << other_eigenvectors <<std::endl;
136135
other_eigenvectors_multiplied.col(0) = 0.0 * other_eigenvectors.col(0);
137-
other_eigenvectors_multiplied.col(1) = 1.0/(lambda_plane - es4.eigenvalues()(1) ) * other_eigenvectors.col(1);
138-
other_eigenvectors_multiplied.col(2) = 1.0/(lambda_plane - es4.eigenvalues()(2) ) * other_eigenvectors.col(2);
139-
other_eigenvectors_multiplied.col(3) = 1.0/(lambda_plane - es4.eigenvalues()(3) ) * other_eigenvectors.col(3);
136+
other_eigenvectors_multiplied.col(1) = 1.0/(lambda_plane - es.eigenvalues()(1)) * other_eigenvectors.col(1);
137+
other_eigenvectors_multiplied.col(2) = 1.0/(lambda_plane - es.eigenvalues()(2)) * other_eigenvectors.col(2);
140138
//std::cout << "other eignevect mult \n" << other_eigenvectors_multiplied <<std::endl;
141-
//std::cout << "Q_inv_ 4x4 inverse =\n" << other_eigenvectors * other_eigenvectors_multiplied.transpose() <<std::endl;
142-
Q_inv_no_kernel_ = other_eigenvectors * other_eigenvectors_multiplied.transpose();
143-
139+
Q_inv_minus_plane_.setZero();
140+
Q_inv_minus_plane_.topLeftCorner<3,3>() = other_eigenvectors * other_eigenvectors_multiplied.transpose();
141+
Q_inv_minus_plane_(3,3)= -1.0/accumulatedQ_(3,3);
142+
Q_inv_minus_plane_ = Tcenter_.transpose() * Q_inv_minus_plane_ * Tcenter_;
144143
}
145144

146145

src/plane-surfaces/factors/EigenFactorPlaneDense.cpp

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,18 @@ void EigenFactorPlaneDense::evaluate_jacobians()
5858
gradQ_xi_times_pi_.push_back(grad);
5959
jacobian = grad.transpose() * planeEstimation_;
6060

61+
62+
6163
// calculate hessian ONLY Upper Trianlar view
6264
//tested: pi_t_x_hessian_Q_x_pi(),coincident with EFcenter-element-by-element implementation
6365
Mat6 pi_t_G_time_Q_grad;
6466
pi_t_G_time_Q_grad.triangularView<Eigen::Upper>() = 2.0*pi_t_times_lie_generatives(planeEstimation_)*grad;
6567

6668
// Cross term dpi * dQ*pi, where dpi/dxi_i = Q^-1 dQ/dxi_i pi. Due to symetry, both temrs are equal and sum (-> 2.0*)
6769
Mat6 grad_pi_time_Q_grad;
68-
grad_pi_time_Q_grad.triangularView<Eigen::Upper>() = 2.0*grad.transpose()*Q_inv_no_kernel_*grad;
70+
71+
// symetric
72+
grad_pi_time_Q_grad.triangularView<Eigen::Upper>() = 2.0*grad.transpose()*Q_inv_minus_plane_*grad;
6973

7074
// sum of all terms
7175
hessian.triangularView<Eigen::Upper>() =
@@ -76,8 +80,6 @@ void EigenFactorPlaneDense::evaluate_jacobians()
7680

7781
J_.push_back(jacobian);
7882
H_.push_back(hessian);
79-
// need to store
80-
//std::cout << "Jacobian =\n" << hessian <<std::endl;
8183
}
8284
}
8385

@@ -97,7 +99,7 @@ void EigenFactorPlaneDense::estimate_plane()
9799
if (accumulatedQ_.sum()< 1e-4)
98100
{
99101
planeEstimation_.setZero();
100-
Q_inv_no_kernel_.setZero();
102+
Q_inv_minus_plane_.setZero();
101103
return;
102104
}
103105

@@ -106,13 +108,13 @@ void EigenFactorPlaneDense::estimate_plane()
106108
// pi_centered = [n, 0], such that T^{-\top} * pi_centered = pi,
107109
// This only hold for when T^{-\top} = [I, -n d].
108110
// and n d = - sum{p} / N = -E{x} from the centered calculation of a plane
109-
Mat4 Tcenter = Mat4::Identity();
110-
Tcenter.topRightCorner<3,1>() = -accumulatedQ_.topRightCorner<3,1>()/accumulatedQ_(3,3);
111+
Tcenter_ = Mat4::Identity();
112+
Tcenter_.topRightCorner<3,1>() = -accumulatedQ_.topRightCorner<3,1>()/accumulatedQ_(3,3);
111113
//std::cout << "T center = " << Tcenter << std::endl;
112114

113115

114116
Mat4 accumulatedCenterQ;
115-
accumulatedCenterQ = Tcenter * accumulatedQ_ * Tcenter.transpose();
117+
accumulatedCenterQ = Tcenter_ * accumulatedQ_ * Tcenter_.transpose();
116118

117119

118120
// Only needs Lower View from Q (https://eigen.tuxfamily.org/dox/classEigen_1_1SelfAdjointEigenSolver.html)
@@ -127,30 +129,22 @@ void EigenFactorPlaneDense::estimate_plane()
127129
planeEstimationCenter(3) = 0.0;
128130

129131

130-
planeEstimation_ = Tcenter.transpose() * planeEstimationCenter;
131-
132-
// Calcualte almost inverse of Q for later derivatives:
133-
// option 1, full inverse: slightly inacurate solution.
134-
//Q_inv_no_kernel_ = accumulatedQ_.inverse();
132+
planeEstimation_ = Tcenter_.transpose() * planeEstimationCenter;
135133

136-
// Option 2. Direct eig decomposition and inverse
137-
Eigen::SelfAdjointEigenSolver<Mat4> es4;
138-
es4.compute(accumulatedQ_);
139-
matData_t lambda_plane = es4.eigenvalues()(0);
140-
Mat4 other_eigenvectors, other_eigenvectors_multiplied;
141-
other_eigenvectors = es4.eigenvectors();
134+
// Calculate almost inverse for the 3x3
135+
matData_t lambda_plane = es.eigenvalues()(0);
136+
Mat3 other_eigenvectors, other_eigenvectors_multiplied;
137+
other_eigenvectors = es.eigenvectors();
142138
other_eigenvectors.col(0) *= 0.0;
143139
//std::cout << "other eignevect \n" << other_eigenvectors <<std::endl;
144140
other_eigenvectors_multiplied.col(0) = 0.0 * other_eigenvectors.col(0);
145-
other_eigenvectors_multiplied.col(1) = 1.0/(lambda_plane - es4.eigenvalues()(1)) * other_eigenvectors.col(1);
146-
other_eigenvectors_multiplied.col(2) = 1.0/(lambda_plane - es4.eigenvalues()(2)) * other_eigenvectors.col(2);
147-
other_eigenvectors_multiplied.col(3) = 1.0/(lambda_plane - es4.eigenvalues()(3)) * other_eigenvectors.col(3);
141+
other_eigenvectors_multiplied.col(1) = 1.0/(lambda_plane - es.eigenvalues()(1)) * other_eigenvectors.col(1);
142+
other_eigenvectors_multiplied.col(2) = 1.0/(lambda_plane - es.eigenvalues()(2)) * other_eigenvectors.col(2);
148143
//std::cout << "other eignevect mult \n" << other_eigenvectors_multiplied <<std::endl;
149-
//std::cout << "Q_inv_ 4x4 inverse =\n" << other_eigenvectors * other_eigenvectors_multiplied.transpose() <<std::endl;
150-
Q_inv_no_kernel_ = other_eigenvectors * other_eigenvectors_multiplied.transpose();
151-
//std::cout << "Difference =\n" << (other_eigenvectors * other_eigenvectors_multiplied.transpose()-Q_inv_no_kernel_).squaredNorm() <<std::endl;
152-
153-
144+
Q_inv_minus_plane_.setZero();
145+
Q_inv_minus_plane_.topLeftCorner<3,3>() = other_eigenvectors * other_eigenvectors_multiplied.transpose();
146+
Q_inv_minus_plane_(3,3)= -1.0/accumulatedQ_(3,3);
147+
Q_inv_minus_plane_ = Tcenter_.transpose() * Q_inv_minus_plane_ * Tcenter_;
154148

155149
}
156150

@@ -169,8 +163,9 @@ bool EigenFactorPlaneDense::get_hessian(MatRef H, mrob::factor_id_t id_i, mrob::
169163
}
170164
else
171165
{
172-
// cross terms as
173-
H = 2.0*gradQ_xi_times_pi_.at(localId1).transpose() * Q_inv_no_kernel_* gradQ_xi_times_pi_.at(localId2);
166+
// cross terms as:
167+
H = 2.0* gradQ_xi_times_pi_.at(localId1).transpose() * Q_inv_minus_plane_ * gradQ_xi_times_pi_.at(localId2);
168+
//std::cout << "Testing symetry =\n" << std::endl;
174169
return true;
175170
}
176171
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/* Copyright (c) 2022, Gonzalo Ferrer
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*
15+
*
16+
* EigenFactorPlaneDenseHomog.cpp
17+
*
18+
* Created on: Aug 23, 2023
19+
* Author: Gonzalo Ferrer
20+
* g.ferrer@skoltech.ru
21+
* Mobile Robotics Lab.
22+
*/
23+
24+
25+
#include "mrob/factors/EigenFactorPlaneDenseHomog.hpp"
26+
27+
#include <iostream>
28+
#include <Eigen/Eigenvalues>
29+
#include "mrob/SE3.hpp"
30+
#include "mrob/utils_lie_differentiation.hpp"
31+
32+
using namespace mrob;
33+
34+
EigenFactorPlaneDenseHomog::EigenFactorPlaneDenseHomog(Factor::robustFactorType robust_type):
35+
EigenFactorPlaneBase(robust_type)
36+
{
37+
}
38+
39+
void EigenFactorPlaneDenseHomog::evaluate_residuals()
40+
{
41+
this->estimate_plane();
42+
}
43+
44+
void EigenFactorPlaneDenseHomog::evaluate_jacobians()
45+
{
46+
// Assumes residuals evaluated beforehand
47+
J_.clear();
48+
H_.clear();
49+
gradQ_xi_times_pi_.clear();
50+
for (auto &Qt: Q_)
51+
{
52+
Mat61 jacobian = Mat61::Zero();
53+
Mat6 hessian = Mat6::Zero();
54+
55+
// calculate gradient
56+
Mat<4,6> grad;
57+
grad = gradient_Q_x_pi(Qt,planeEstimation_);
58+
gradQ_xi_times_pi_.push_back(grad);
59+
jacobian = grad.transpose() * planeEstimation_;
60+
61+
// calculate hessian ONLY Upper Trianlar view
62+
//tested: pi_t_x_hessian_Q_x_pi(),coincident with EFcenter-element-by-element implementation
63+
Mat6 pi_t_G_time_Q_grad;
64+
pi_t_G_time_Q_grad.triangularView<Eigen::Upper>() = 2.0*pi_t_times_lie_generatives(planeEstimation_)*grad;
65+
66+
// Cross term dpi * dQ*pi, where dpi/dxi_i = Q^-1 dQ/dxi_i pi. Due to symetry, both temrs are equal and sum (-> 2.0*)
67+
Mat6 grad_pi_time_Q_grad;
68+
grad_pi_time_Q_grad.triangularView<Eigen::Upper>() = 2.0*grad.transpose()*Q_inv_no_kernel_*grad;
69+
70+
// sum of all terms
71+
hessian.triangularView<Eigen::Upper>() =
72+
pi_t_x_hessian_Q_x_pi(Qt,planeEstimation_) +
73+
grad_pi_time_Q_grad + // crosterm due to plane x dQ
74+
pi_t_G_time_Q_grad; //slihglty better than EFcenter
75+
76+
77+
J_.push_back(jacobian);
78+
H_.push_back(hessian);
79+
// need to store
80+
//std::cout << "Jacobian =\n" << hessian <<std::endl;
81+
}
82+
}
83+
84+
void EigenFactorPlaneDenseHomog::evaluate_chi2()
85+
{
86+
// Point 2 plane exact error requires chi2 = pi' Q pi
87+
chi2_ = planeEstimation_.dot( accumulatedQ_ * planeEstimation_ );
88+
//std::cout << "plane = " << planeEstimation_ << "\n Q = \n" << accumulatedQ_ << std::endl;
89+
}
90+
91+
void EigenFactorPlaneDenseHomog::estimate_plane()
92+
{
93+
calculate_all_matrices_S();
94+
calculate_all_matrices_Q();
95+
96+
// Check for empty EF (no points)
97+
if (accumulatedQ_.sum()< 1e-4)
98+
{
99+
planeEstimation_.setZero();
100+
Q_inv_no_kernel_.setZero();
101+
return;
102+
}
103+
104+
105+
// Center the plane requires a transformation (translation) such that
106+
// pi_centered = [n, 0], such that T^{-\top} * pi_centered = pi,
107+
// This only hold for when T^{-\top} = [I, -n d].
108+
// and n d = - sum{p} / N = -E{x} from the centered calculation of a plane
109+
Mat4 Tcenter = Mat4::Identity();
110+
Tcenter.topRightCorner<3,1>() = -accumulatedQ_.topRightCorner<3,1>()/accumulatedQ_(3,3);
111+
//std::cout << "T center = " << Tcenter << std::endl;
112+
113+
114+
Mat4 accumulatedCenterQ;
115+
accumulatedCenterQ = Tcenter * accumulatedQ_ * Tcenter.transpose();
116+
117+
118+
// Only needs Lower View from Q (https://eigen.tuxfamily.org/dox/classEigen_1_1SelfAdjointEigenSolver.html)
119+
Eigen::SelfAdjointEigenSolver<Mat3> es;
120+
es.computeDirect(accumulatedCenterQ.topLeftCorner<3,3>());
121+
// This plane center is more stable when calcualating a solution than its counterpart in 4d
122+
// Most likely it is how the error leaks from the distance componet and breaks the normal component,
123+
// that needs to be projected (regenerated) on the 4x4 case. this way, we make sure it is a unit vector by construction.
124+
// Right after the calcuation, we convert back to the correct reference frame.
125+
Mat41 planeEstimationCenter;
126+
planeEstimationCenter.head<3>() = es.eigenvectors().col(0);
127+
planeEstimationCenter(3) = 0.0;
128+
129+
130+
planeEstimation_ = Tcenter.transpose() * planeEstimationCenter;
131+
132+
// Calcualte almost inverse of Q for later derivatives:
133+
// option 1, full inverse: slightly inacurate solution.
134+
//Q_inv_no_kernel_ = accumulatedQ_.inverse();
135+
136+
// Option 2. Direct eig decomposition and inverse
137+
Eigen::SelfAdjointEigenSolver<Mat4> es4;
138+
es4.compute(accumulatedQ_);
139+
matData_t lambda_plane = es4.eigenvalues()(0);
140+
Mat4 other_eigenvectors, other_eigenvectors_multiplied;
141+
other_eigenvectors = es4.eigenvectors();
142+
other_eigenvectors.col(0) *= 0.0;
143+
//std::cout << "other eignevect \n" << other_eigenvectors <<std::endl;
144+
other_eigenvectors_multiplied.col(0) = 0.0 * other_eigenvectors.col(0);
145+
other_eigenvectors_multiplied.col(1) = 1.0/(lambda_plane - es4.eigenvalues()(1)) * other_eigenvectors.col(1);
146+
other_eigenvectors_multiplied.col(2) = 1.0/(lambda_plane - es4.eigenvalues()(2)) * other_eigenvectors.col(2);
147+
other_eigenvectors_multiplied.col(3) = 1.0/(lambda_plane - es4.eigenvalues()(3)) * other_eigenvectors.col(3);
148+
//std::cout << "other eignevect mult \n" << other_eigenvectors_multiplied <<std::endl;
149+
//std::cout << "Q_inv_ 4x4 inverse =\n" << other_eigenvectors * other_eigenvectors_multiplied.transpose() <<std::endl;
150+
Q_inv_no_kernel_ = other_eigenvectors * other_eigenvectors_multiplied.transpose();
151+
//std::cout << "Difference =\n" << (other_eigenvectors * other_eigenvectors_multiplied.transpose()-Q_inv_no_kernel_).squaredNorm() <<std::endl;
152+
153+
154+
155+
}
156+
157+
bool EigenFactorPlaneDenseHomog::get_hessian(MatRef H, mrob::factor_id_t id_i, mrob::factor_id_t id_j) const
158+
{
159+
// this condition should always hold since ids are taken from neubouring nodes, but in case useage changes.
160+
if (reverseNodeIds_.count(id_i) == 0 || reverseNodeIds_.count(id_j) == 0)
161+
return false;
162+
163+
uint_t localId1 = reverseNodeIds_.at(id_i);
164+
uint_t localId2 = reverseNodeIds_.at(id_j);
165+
if(id_i == id_j)
166+
{
167+
H = H_.at(localId1);
168+
return true;
169+
}
170+
else
171+
{
172+
// cross terms as
173+
H = 2.0*gradQ_xi_times_pi_.at(localId1).transpose() * Q_inv_no_kernel_* gradQ_xi_times_pi_.at(localId2);
174+
return true;
175+
}
176+
}

src/plane-surfaces/mrob/factors/EigenFactorPlaneCenter2.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class EigenFactorPlaneCenter2: public EigenFactorPlaneBase{
8787
Mat4 Tcenter_;
8888

8989
// This matrix is calculated when estiamting the plane, as a byproduct of the eigiendecompsition
90-
Mat4 Q_inv_no_kernel_;
90+
Mat4 Q_inv_minus_plane_;
9191

9292
};
9393

0 commit comments

Comments
 (0)