Skip to content

Commit 58021dc

Browse files
committed
update 2d tests and add 3d tests
1 parent 4106364 commit 58021dc

File tree

5 files changed

+284
-2
lines changed

5 files changed

+284
-2
lines changed

CMakeLists.txt

+15
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,21 @@ target_include_directories(test_suite
7474
)
7575
target_compile_definitions(test_suite PRIVATE TEST)
7676

77+
set(TEST_SOURCES_3D tests/test3d.cpp)
78+
add_executable(test_suite3d ${TEST_SOURCES_3D})
79+
target_link_libraries(test_suite3d
80+
cevicp
81+
)
82+
target_include_directories(test_suite3d
83+
PRIVATE
84+
${CMAKE_CURRENT_SOURCE_DIR}/include
85+
/usr/local/include/simple_test
86+
)
87+
target_compile_definitions(test_suite3d PRIVATE TEST)
88+
89+
# add_executable(test3d_suite tests/test3d.cpp)
90+
# target_link_libraries(test3d_suite PRIVATE icp)
91+
7792
set(BENCH_SOURCES bench/bench.cpp common/parse_scan.cpp)
7893
add_executable(bench_suite ${BENCH_SOURCES})
7994
target_link_libraries(bench_suite

Makefile

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ LIB_TARGET := cevicp
44
MAIN_TARGET := main
55
TEST_TARGET := test_suite
66
BENCH_TARGET := bench_suite
7+
TEST3D_TARGET := test_suite3d
78

89
N := 1
910
METHOD := vanilla
@@ -39,6 +40,14 @@ $(TEST_TARGET): configure
3940
.PHONY: $(BENCH_TARGET)
4041
$(BENCH_TARGET): configure
4142
cmake --build $(BUILD_DIR) --target $(BENCH_TARGET) -- $(MAKE_FLAGS)
43+
44+
.PHONY: $(TEST3D_TARGET)
45+
$(TEST3D_TARGET): configure
46+
cmake --build $(BUILD_DIR) --target $(TEST3D_TARGET) -- $(MAKE_FLAGS)
47+
48+
.PHONY: test3d
49+
test3d: $(TEST3D_TARGET)
50+
./$(BUILD_DIR)/$(TEST3D_TARGET)
4251

4352
.PHONY: test
4453
test: $(TEST_TARGET)

lib/icp/driver.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ namespace icp {
7676
return true;
7777
}
7878
// 3d rotation decompose in 3 param
79+
//used Axis_angle to decompose it now: avoiding gimbal lock for 90 degree rotation that happens in euler angles
7980
if (angle_tolerance_rad_ && translation_tolerance_) {
8081
icp::Matrix rotation_step = current_state.transform.rotation
8182
* last_state.value().transform.rotation.transpose();

tests/test.cpp

+13-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ void test_icp_generic(const std::string& method, const icp::ICP::Config& config)
6262
// Check translation
6363
assert_true(std::abs(result.transform.translation.x() - 100) <= TRANS_EPS);
6464
assert_true(std::abs(result.transform.translation.y() - 0) <= TRANS_EPS);
65+
assert_true(result.transform.rotation.isApprox(icp::Matrix::Identity(2, 2)));
6566
}
6667

6768
// Test case 2: Identity test
@@ -80,10 +81,11 @@ void test_icp_generic(const std::string& method, const icp::ICP::Config& config)
8081

8182
assert_true(std::abs(result.transform.translation.x() - 0) <= TRANS_EPS);
8283
assert_true(std::abs(result.transform.translation.y() - 0) <= TRANS_EPS);
84+
assert_true(result.transform.rotation.isApprox(icp::Matrix::Identity(2, 2)));
8385
}
8486

8587
// Test case 3: Rotation at different angles
86-
for (int deg = 0; deg < 20; deg++) {
88+
for (int deg = 170; deg < 180; deg++) {
8789
std::vector<icp::Vector> a = {
8890
icp::Vector(Eigen::Vector2d(-100, -100)), icp::Vector(Eigen::Vector2d(100, 100))};
8991
std::vector<icp::Vector> b = {};
@@ -98,11 +100,16 @@ void test_icp_generic(const std::string& method, const icp::ICP::Config& config)
98100
}
99101

100102
std::cout << "testing angle: " << deg << '\n';
103+
std::cout << "the result for the matrix " << rotation_matrix << '\n';
101104

102105
auto result = driver.converge(a, b, icp::RBTransform(2));
103106

104107
assert_true(std::abs(result.transform.translation.x() - 0) <= TRANS_EPS);
105108
assert_true(std::abs(result.transform.translation.y() - 0) <= TRANS_EPS);
109+
110+
std::cout << "the result for the matrix " << result.transform.rotation << '\n';
111+
assert_true(result.transform.rotation.isApprox(rotation_matrix));
112+
//having problem when the angle is 90, 180
106113
}
107114

108115
{
@@ -122,6 +129,7 @@ void test_icp_generic(const std::string& method, const icp::ICP::Config& config)
122129

123130
assert_true(std::abs(result.transform.translation.x() - 100) <= TRANS_EPS);
124131
assert_true(std::abs(result.transform.translation.y() - 0) <= TRANS_EPS);
132+
assert_true(result.transform.rotation.isApprox(icp::Matrix::Identity(2, 2)));
125133
}
126134

127135
// need more test case that works for trimmed and feature aware(more points? since it filtered
@@ -152,7 +160,7 @@ void test_icp_generic(const std::string& method, const icp::ICP::Config& config)
152160

153161
assert_true(std::abs(result.transform.translation.x() - 50) <= TRANS_EPS);
154162
assert_true(std::abs(result.transform.translation.y() - 50) <= TRANS_EPS);
155-
// assert_true(std::abs(result.transform.rotation.angle() - angle) <= RAD_EPS);
163+
assert_true(result.transform.rotation.isApprox(rotation_matrix));
156164
}
157165

158166
{
@@ -183,6 +191,9 @@ void test_icp_generic(const std::string& method, const icp::ICP::Config& config)
183191

184192
assert_true(std::abs(result.transform.translation.x() - 20) <= TRANS_EPS + 1.0);
185193
assert_true(std::abs(result.transform.translation.y() - 10) <= TRANS_EPS + 1.0);
194+
double angle_compute = std::atan2(result.transform.rotation(1,0), result.transform.rotation(0,0));
195+
assert_true(std::abs(angle_compute - angle) <= RAD_EPS);
196+
//need to find a way to test the rotation
186197
}
187198
}
188199

tests/test3d.cpp

+246
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
#include <string>
2+
#include "icp/geo.h"
3+
#include "icp/icp.h"
4+
#include "icp/driver.h"
5+
#include <iostream>
6+
#include <random>
7+
8+
extern "C" {
9+
#include <simple_test/simple_test.h>
10+
}
11+
12+
#define BURN_IN 0 // Minimum required iterations for the algorithm
13+
#define TRANS_EPS 0.5 // Translation tolerance in units
14+
#define RAD_EPS ((double)(0.01)) // Rotation tolerance in radians
15+
16+
void test_icp_3d(const std::string& method, const icp::ICP::Config& config) {
17+
18+
std::unique_ptr<icp::ICP> icp = icp::ICP::from_method(method, config).value();
19+
icp::ICPDriver driver(std::move(icp));
20+
driver.set_min_iterations(BURN_IN);
21+
driver.set_max_iterations(100);
22+
driver.set_transform_tolerance(0.1 * M_PI / 180, 0.1);
23+
24+
// Test case 1: Single point translation
25+
{
26+
std::vector<icp::Vector> a = {icp::Vector(Eigen::Vector3d(0, 0, 0))};
27+
std::vector<icp::Vector> b = {icp::Vector(Eigen::Vector3d(100, 0, 0))};
28+
auto result = driver.converge(a, b, icp::RBTransform(3));
29+
30+
// Debug: Print transformation results
31+
std::cout << "[1]Result Transform Translation X: " << result.transform.translation.x()
32+
<< std::endl;
33+
std::cout << "[1]Result Transform Translation Y: " << result.transform.translation.y()
34+
<< std::endl;
35+
std::cout << "[1]Result Iteration Count: " << result.iteration_count << std::endl;
36+
37+
// Check iteration count
38+
assert_true(result.iteration_count <= BURN_IN + 10);
39+
40+
// Check translation
41+
assert_true(std::abs(result.transform.translation.x() - 100) <= TRANS_EPS);
42+
assert_true(std::abs(result.transform.translation.y() - 0) <= TRANS_EPS);
43+
assert_true(result.transform.rotation.isApprox(icp::Matrix::Identity(3, 3)));
44+
}
45+
46+
// Test case 2: Identity test
47+
{
48+
std::vector<icp::Vector> a = {
49+
icp::Vector(Eigen::Vector3d(0, 0, 0)), icp::Vector(Eigen::Vector3d(100, 100, 100))};
50+
std::vector<icp::Vector> b = {
51+
icp::Vector(Eigen::Vector3d(0, 0, 0)), icp::Vector(Eigen::Vector3d(100, 100, 100))};
52+
auto result = driver.converge(a, b, icp::RBTransform(3));
53+
54+
std::cout << "[2]Result Transform Translation X: " << result.transform.translation.x()
55+
<< std::endl;
56+
std::cout << "[2]Result Transform Translation Y: " << result.transform.translation.y()
57+
<< std::endl;
58+
std::cout << "[2]Result Iteration Count: " << result.iteration_count << std::endl;
59+
60+
assert_true(std::abs(result.transform.translation.x() - 0) <= TRANS_EPS);
61+
assert_true(std::abs(result.transform.translation.y() - 0) <= TRANS_EPS);
62+
assert_true(result.transform.rotation.isApprox(icp::Matrix::Identity(3, 3)));
63+
}
64+
65+
// Test case 3: Rotation about z-axis
66+
{
67+
std::vector<icp::Vector> a = {icp::Vector(Eigen::Vector3d(1, 0, 0))};
68+
std::vector<icp::Vector> b = {icp::Vector(Eigen::Vector3d(0, 1, 0))};
69+
auto result = driver.converge(a, b, icp::RBTransform(3));
70+
71+
Eigen::Matrix3d rotation_matrix;
72+
rotation_matrix << 0, -1, 0, 1, 0, 0, 0, 0, 1; // 90 degree rotation about z-axis
73+
74+
// Check iteration count
75+
assert_true(result.iteration_count <= BURN_IN + 10);
76+
77+
// Check translation
78+
assert_true(std::abs(result.transform.translation.x() - 100) <= TRANS_EPS);
79+
assert_true(std::abs(result.transform.translation.y() - 0) <= TRANS_EPS);
80+
assert_true(result.transform.rotation.isApprox(rotation_matrix));
81+
}
82+
// Test case 3: Rotation about one of the axis
83+
for (int deg = 0; deg < 180; deg++) {
84+
std::vector<icp::Vector> a = {
85+
icp::Vector(Eigen::Vector3d(1, 0, 0)), icp::Vector(Eigen::Vector3d(0, 1, 0)),icp::Vector(Eigen::Vector3d(0, 0, 1))};
86+
std::vector<icp::Vector> b = {};
87+
88+
double angle = (double)deg * M_PI / 180.0;
89+
icp::Vector center = icp::get_centroid(a);
90+
icp::Matrix rotation_matrix(3, 3);
91+
rotation_matrix << 1, 0, 0,
92+
0, std::cos(angle), -std::sin(angle),
93+
0, std::sin(angle), std::cos(angle);
94+
95+
for (const auto& point: a) {
96+
b.push_back(rotation_matrix * (point - center) + center);
97+
}
98+
99+
std::cout << "testing angle: " << deg << '\n';
100+
101+
auto result = driver.converge(a, b, icp::RBTransform(3));
102+
103+
assert_true(std::abs(result.transform.translation.x() - 0) <= TRANS_EPS);
104+
assert_true(std::abs(result.transform.translation.y() - 0) <= TRANS_EPS);
105+
assert_true(result.transform.rotation.isApprox(rotation_matrix));
106+
}
107+
108+
// Test case 3: Rotation about multiple the axis
109+
{
110+
std::vector<icp::Vector> a = {
111+
icp::Vector(Eigen::Vector3d(1, 0, 0)), icp::Vector(Eigen::Vector3d(0, 1, 0)),icp::Vector(Eigen::Vector3d(0, 0, 1))};
112+
std::vector<icp::Vector> b = {};
113+
114+
int deg_1 = rand()%360;
115+
int deg_2 = rand()%360;
116+
int deg_3 = rand()%360;
117+
118+
double angle_1 = (double)deg_1 * M_PI / 180.0;
119+
double angle_2 = (double)deg_2 * M_PI / 180.0;
120+
double angle_3 = (double)deg_3 * M_PI / 180.0;
121+
icp::Vector center = icp::get_centroid(a);
122+
icp::Matrix rotation_matrix(3, 3);
123+
rotation_matrix << std::cos(angle_2)*std::cos(angle_3), std::sin(angle_1)*std::sin(angle_2)*std::cos(angle_3)-std::cos(angle_1)*std::sin(angle_3), std::cos(angle_1)*std::sin(angle_2)*std::cos(angle_3)+std::sin(angle_1)*std::sin(angle_3),
124+
std::cos(angle_2)*std::sin(angle_3), std::sin(angle_1)*std::sin(angle_2)*std::sin(angle_3)+std::cos(angle_1)*std::cos(angle_3), std::cos(angle_1)*std::sin(angle_2)*std::sin(angle_3)-std::sin(angle_1)*std::cos(angle_3),
125+
-std::sin(angle_2), std::sin(angle_1)*std::cos(angle_2), std::cos(angle_1)*std::cos(angle_2);
126+
127+
for (const auto& point: a) {
128+
b.push_back(rotation_matrix * (point - center) + center);
129+
}
130+
131+
auto result = driver.converge(a, b, icp::RBTransform(3));
132+
133+
assert_true(std::abs(result.transform.translation.x() - 0) <= TRANS_EPS);
134+
assert_true(std::abs(result.transform.translation.y() - 0) <= TRANS_EPS);
135+
assert_true(result.transform.rotation.isApprox(rotation_matrix));
136+
}
137+
138+
{
139+
// Test case 4: Pure translation multiple axis
140+
std::vector<icp::Vector> a = {
141+
icp::Vector(Eigen::Vector3d(1, 0, 0)), icp::Vector(Eigen::Vector3d(0, 1, 0)),icp::Vector(Eigen::Vector3d(0, 0, 1))};
142+
std::vector<icp::Vector> b = {
143+
icp::Vector(Eigen::Vector3d(51, 73, 2)), icp::Vector(Eigen::Vector3d(50, 74, 2)),icp::Vector(Eigen::Vector3d(50, 73, 3))};
144+
145+
auto result = driver.converge(a, b, icp::RBTransform(3));
146+
147+
std::cout << "[3]Result Transform Translation X: " << result.transform.translation.x()
148+
<< std::endl;
149+
std::cout << "[3]Result Transform Translation Y: " << result.transform.translation.y()
150+
<< std::endl;
151+
std::cout << "[3]Result Transform Translation Y: " << result.transform.translation.z()
152+
<< std::endl;
153+
std::cout << "[3]Result Iteration Count: " << result.iteration_count << std::endl;
154+
155+
assert_true(std::abs(result.transform.translation.x() - 50) <= TRANS_EPS);
156+
assert_true(std::abs(result.transform.translation.y() - 73) <= TRANS_EPS);
157+
assert_true(std::abs(result.transform.translation.y() - 2) <= TRANS_EPS);
158+
assert_true(result.transform.rotation.isApprox(icp::Matrix::Identity(3, 3)));
159+
160+
}
161+
162+
// // need more test case that works for trimmed and feature aware(more points? since it filtered
163+
// // out points)
164+
{
165+
// Translation + rotation
166+
std::vector<icp::Vector> a = {
167+
icp::Vector(Eigen::Vector3d(1, 0, 0)), icp::Vector(Eigen::Vector3d(0, 1, 0)),icp::Vector(Eigen::Vector3d(0, 0, 1))};
168+
std::vector<icp::Vector> b;
169+
170+
double angle_1 = 45 * M_PI / 180.0;
171+
double angle_2 = 45 * M_PI / 180.0;
172+
double angle_3 = 45 * M_PI / 180.0;
173+
icp::Vector translation(Eigen::Vector3d(50, 50, 50));
174+
175+
icp::Matrix rotation_matrix(3, 3);
176+
rotation_matrix << std::cos(angle_2)*std::cos(angle_3), std::sin(angle_1)*std::sin(angle_2)*std::cos(angle_3)-std::cos(angle_1)*std::sin(angle_3), std::cos(angle_1)*std::sin(angle_2)*std::cos(angle_3)+std::sin(angle_1)*std::sin(angle_3),
177+
std::cos(angle_2)*std::sin(angle_3), std::sin(angle_1)*std::sin(angle_2)*std::sin(angle_3)+std::cos(angle_1)*std::cos(angle_3), std::cos(angle_1)*std::sin(angle_2)*std::sin(angle_3)-std::sin(angle_1)*std::cos(angle_3),
178+
-std::sin(angle_2), std::sin(angle_1)*std::cos(angle_2), std::cos(angle_1)*std::cos(angle_2);
179+
180+
for (const auto& point: a) {
181+
b.push_back(rotation_matrix * point + translation);
182+
}
183+
184+
auto result = driver.converge(a, b, icp::RBTransform(3));
185+
186+
std::cout << "[4]Result Transform Translation X: " << result.transform.translation.x()
187+
<< std::endl;
188+
std::cout << "[4]Result Transform Translation Y: " << result.transform.translation.y()
189+
<< std::endl;
190+
std::cout << "[4]Result Transform Translation Z: " << result.transform.translation.z()
191+
<< std::endl;
192+
std::cout << "[4]Result Iteration Count: " << result.iteration_count << std::endl;
193+
194+
assert_true(std::abs(result.transform.translation.x() - 50) <= TRANS_EPS);
195+
assert_true(std::abs(result.transform.translation.y() - 50) <= TRANS_EPS);
196+
assert_true(std::abs(result.transform.translation.z() - 50) <= TRANS_EPS);
197+
assert_true(result.transform.rotation.isApprox(rotation_matrix));
198+
}
199+
200+
{
201+
// Add noise
202+
std::vector<icp::Vector> a = {
203+
icp::Vector(Eigen::Vector3d(1, 0, 0)), icp::Vector(Eigen::Vector3d(0, 1, 0)),icp::Vector(Eigen::Vector3d(0, 0, 1))};
204+
std::vector<icp::Vector> b;
205+
206+
double angle_1 = 30 * M_PI / 180.0;
207+
double angle_2 = 40 * M_PI / 180.0;
208+
double angle_3 = 50 * M_PI / 180.0;
209+
icp::Vector translation(Eigen::Vector3d(20, 10, 30)); // Translate by (20, 10)
210+
211+
icp::Matrix rotation_matrix(3, 3);
212+
rotation_matrix << std::cos(angle_2)*std::cos(angle_3), std::sin(angle_1)*std::sin(angle_2)*std::cos(angle_3)-std::cos(angle_1)*std::sin(angle_3), std::cos(angle_1)*std::sin(angle_2)*std::cos(angle_3)+std::sin(angle_1)*std::sin(angle_3),
213+
std::cos(angle_2)*std::sin(angle_3), std::sin(angle_1)*std::sin(angle_2)*std::sin(angle_3)+std::cos(angle_1)*std::cos(angle_3), std::cos(angle_1)*std::sin(angle_2)*std::sin(angle_3)-std::sin(angle_1)*std::cos(angle_3),
214+
-std::sin(angle_2), std::sin(angle_1)*std::cos(angle_2), std::cos(angle_1)*std::cos(angle_2);
215+
216+
std::default_random_engine generator;
217+
std::normal_distribution<double> noise_dist(0.0,
218+
1.0); // Noise with standard deviation of 1.0
219+
220+
for (const auto& point: a) {
221+
Eigen::Vector3d noisy_point;
222+
noisy_point = rotation_matrix * point + translation;
223+
noisy_point.x() += noise_dist(generator);
224+
noisy_point.y() += noise_dist(generator);
225+
noisy_point.z() += noise_dist(generator);
226+
b.push_back(noisy_point);
227+
}
228+
229+
auto result = driver.converge(a, b, icp::RBTransform());
230+
231+
assert_true(std::abs(result.transform.translation.x() - 20) <= TRANS_EPS + 1.0);
232+
assert_true(std::abs(result.transform.translation.y() - 10) <= TRANS_EPS + 1.0);
233+
assert_true(std::abs(result.transform.translation.z() - 10) <= TRANS_EPS + 1.0);
234+
235+
Eigen::Matrix3d rotation_matrix_2 = result.transform.rotation.block<3,3>(0,0);
236+
Eigen::Vector3d euler_angles = rotation_matrix_2.eulerAngles(0, 1, 2);
237+
// assert_true(std::abs(euler_angles[0] - angle_1) <= RAD_EPS);
238+
// assert_true(std::abs(euler_angles[1] - angle_2) <= RAD_EPS);
239+
// assert_true(std::abs(euler_angles[2] - angle_3) <= RAD_EPS);
240+
}
241+
//for testing the rotation, should we decompose the rotation matrix and compare the angles seperately?
242+
}
243+
244+
void test_main() {
245+
test_icp_3d("vanilla_3d", icp::ICP::Config());
246+
}

0 commit comments

Comments
 (0)