Skip to content

Commit 779f1e8

Browse files
authored
Handle the case when normal is close to y axis (#917)
* fix EdgeGICP::makeRot functions for case when y is close to 0 * improve numerical stability if a normal is close to [0,1,0] * add unit test
1 parent f690637 commit 779f1e8

4 files changed

Lines changed: 104 additions & 9 deletions

File tree

g2o/types/icp/types_icp.h

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,19 @@ class G2O_TYPES_ICP_API EdgeGICP {
8484
y << 0, 1, 0;
8585
R0.row(2) = normal0;
8686
y = y - normal0(1) * normal0;
87-
y.normalize(); // need to check if y is close to 0
88-
R0.row(1) = y;
89-
R0.row(0) = normal0.cross(R0.row(1));
90-
// cout << normal.transpose() << endl;
91-
// cout << R0 << endl << endl;
92-
// cout << R0*R0.transpose() << endl << endl;
87+
double ysquarednorm = y.squaredNorm();
88+
if (ysquarednorm >= ySquaredBnd) {
89+
y /= std::sqrt(ysquarednorm);
90+
R0.row(1) = y;
91+
R0.row(0) = normal0.cross(R0.row(1));
92+
} else {
93+
Vector3 x;
94+
x << -1, 0, 0;
95+
x = x + normal0(0) * normal0;
96+
x.normalize();
97+
R0.row(0) = x;
98+
R0.row(1) = -normal0.cross(R0.row(0));
99+
}
93100
}
94101

95102
// set up rotation matrix for pos1
@@ -98,9 +105,19 @@ class G2O_TYPES_ICP_API EdgeGICP {
98105
y << 0, 1, 0;
99106
R1.row(2) = normal1;
100107
y = y - normal1(1) * normal1;
101-
y.normalize(); // need to check if y is close to 0
102-
R1.row(1) = y;
103-
R1.row(0) = normal1.cross(R1.row(1));
108+
double ysquarednorm = y.squaredNorm();
109+
if (y.squaredNorm() >= ySquaredBnd) {
110+
y /= std::sqrt(ysquarednorm);
111+
R1.row(1) = y;
112+
R1.row(0) = normal1.cross(R1.row(1));
113+
} else {
114+
Vector3 x;
115+
x << -1, 0, 0;
116+
x = x + normal1(0) * normal1;
117+
x.normalize();
118+
R1.row(0) = x;
119+
R1.row(1) = -normal1.cross(R1.row(0));
120+
}
104121
}
105122

106123
// returns a precision matrix for point-plane
@@ -134,6 +151,9 @@ class G2O_TYPES_ICP_API EdgeGICP {
134151
cov << 1, 0, 0, 0, 1, 0, 0, 0, e;
135152
return R1.transpose() * cov * R1;
136153
}
154+
155+
// this parameter is used by makeRot0 and makeRot1
156+
static constexpr double ySquaredBnd{0.1};
137157
};
138158

139159
// 3D rigid constraint

unit_test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ add_subdirectory(slam3d_addons)
2626
add_subdirectory(sim3)
2727
add_subdirectory(sba)
2828
add_subdirectory(solver)
29+
add_subdirectory(icp)

unit_test/icp/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
add_executable(unittest_icp
2+
icp_rotation.cpp
3+
)
4+
target_link_libraries(unittest_icp types_icp)
5+
create_test(unittest_icp)

unit_test/icp/icp_rotation.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// g2o - General Graph Optimization
2+
// Copyright (C) 2014 R. Kuemmerle, G. Grisetti, W. Burgard
3+
// All rights reserved.
4+
//
5+
// Redistribution and use in source and binary forms, with or without
6+
// modification, are permitted provided that the following conditions are
7+
// met:
8+
//
9+
// * Redistributions of source code must retain the above copyright notice,
10+
// this list of conditions and the following disclaimer.
11+
// * Redistributions in binary form must reproduce the above copyright
12+
// notice, this list of conditions and the following disclaimer in the
13+
// documentation and/or other materials provided with the distribution.
14+
//
15+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16+
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17+
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18+
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19+
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21+
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22+
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23+
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24+
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+
#include "g2o/types/icp/types_icp.h"
28+
#include "gtest/gtest.h"
29+
30+
using namespace g2o;
31+
32+
void checkRotationMatrix(const Matrix3& R) {
33+
constexpr double tol = 1e-14;
34+
// check norms of basis vectors
35+
EXPECT_NEAR(R.row(0).norm(), 1, tol);
36+
EXPECT_NEAR(R.row(1).norm(), 1, tol);
37+
EXPECT_NEAR(R.row(2).norm(), 1, tol);
38+
// check orthogonality
39+
EXPECT_NEAR(R.row(0).dot(R.row(1)), 0, tol);
40+
EXPECT_NEAR(R.row(1).dot(R.row(2)), 0, tol);
41+
EXPECT_NEAR(R.row(2).dot(R.row(0)), 0, tol);
42+
// check that basis is left-handed
43+
EXPECT_NEAR(R.determinant(), -1.0, tol);
44+
}
45+
46+
/*
47+
* ROTATION MATRIX Tests
48+
*/
49+
TEST(IcpRotation, RotationMatrix) {
50+
constexpr size_t thetaPoints = 100;
51+
constexpr size_t phiPoints = 100;
52+
EdgeGICP edge;
53+
for (size_t n = 0; n < thetaPoints; n++) {
54+
const double theta = M_PI * n / thetaPoints;
55+
for (size_t k = 0; k < phiPoints; k++) {
56+
const double phi = 2.0L * M_PI * k / phiPoints;
57+
const Vector3 normal(std::sin(theta) * std::cos(phi), std::cos(theta),
58+
std::sin(theta) * std::sin(phi));
59+
edge.normal0 = normal;
60+
edge.normal1 = normal;
61+
62+
edge.makeRot0();
63+
edge.makeRot1();
64+
65+
checkRotationMatrix(edge.R0);
66+
checkRotationMatrix(edge.R1);
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)