ikd-Tree 简单测试

ikd-Tree 简单测试

因为在学习时手写的 KNN 搜索可以用多线程的方式搜索,搜索效率很高,就考虑 PCL 自带的函数是否能够多线程 KNN 搜索,貌似好像是不行的,不过没有深入研究

发现了 ikd-Tree 这个方法,但是阅读论文后发现其 KNN 搜索貌似依然是单线程的,只是在 re-build 部分采用了并行线程的方式

ikd-Tree 简单测试_第1张图片

对其性能进行简单测试,主要是 Nearest_Search 函数的使用

void KD_TREE<PointType>::Nearest_Search(PointType point, int k_nearest, PointVector& Nearest_Points, vector<float> & Point_Distance, double max_dist)

ikd-Tree 简单测试_第2张图片

注意 max_dist 参数可以控制在指定的范围内进行 KNN 搜索

inline bool compare_point(const sad::PointType& p1, const sad::PointType& p2){
    if(p1.x == p2.x && p1.y == p2.y && p1.z == p2.z){
        return true;
    }
    return false;
}

double max_distance = 3.0;

TEST(CH5_TEST, KDTREE_KNN) {
    sad::CloudPtr first(new sad::PointCloudType), second(new sad::PointCloudType);
    pcl::io::loadPCDFile(FLAGS_first_scan_path, *first);
    pcl::io::loadPCDFile(FLAGS_second_scan_path, *second);

    if (first->empty() || second->empty()) {
        LOG(ERROR) << "cannot load cloud";
        FAIL();
    }

    const int nearest_search_k = 5;

    // voxel grid 至 0.05
    sad::VoxelGrid(first);
    sad::VoxelGrid(second);

    sad::KdTree kdtree;
    sad::evaluate_and_call([&first, &kdtree]() { kdtree.BuildTree(first); }, "Kd Tree build", 1);

    kdtree.SetEnableANN(true, FLAGS_ANN_alpha);

    LOG(INFO) << "Kd tree leaves: " << kdtree.size() << ", points: " << first->size();

    // 比较 bfnn
    std::vector<std::pair<size_t, size_t>> true_matches;
    sad::bfnn_cloud_mt_k(first, second, true_matches);

    // 对第2个点云执行knn
    std::vector<std::pair<size_t, size_t>> matches;
    sad::evaluate_and_call([&first, &second, &kdtree, &matches]() { kdtree.GetClosestPointMT(second, matches, nearest_search_k); },
                           "Kd Tree 5NN multi-thread", 1);
    EvaluateMatches(true_matches, matches);

    LOG(INFO) << "building kdtree pcl";
    // 对比PCL
    pcl::search::KdTree<sad::PointType> kdtree_pcl;
    sad::evaluate_and_call([&first, &kdtree_pcl]() { kdtree_pcl.setInputCloud(first); }, "Kd Tree build", 1);

    LOG(INFO) << "searching pcl";
    matches.clear();
    std::vector<int> search_indices(second->size());
    for (int i = 0; i < second->points.size(); i++) {
        search_indices[i] = i;
    }

    std::vector<std::vector<int>> result_index;
    std::vector<std::vector<float>> result_distance;
    sad::evaluate_and_call(
        [&]() { kdtree_pcl.nearestKSearch(*second, search_indices, nearest_search_k, result_index, result_distance); },
        "Kd Tree 5NN in PCL", 1);
    for (int i = 0; i < second->points.size(); i++) {
        for (int j = 0; j < result_index[i].size(); ++j) {
            int m = result_index[i][j];
            double d = result_distance[i][j];
            matches.push_back({m, i});
        }
    }
    EvaluateMatches(true_matches, matches);

    LOG(INFO) << "ikd_Tree Test";
    KD_TREE<sad::PointType>::Ptr ikdtree_ptr = make_shared<KD_TREE<sad::PointType>>();
    KD_TREE<sad::PointType>& ikd_Tree = *ikdtree_ptr;
    sad::evaluate_and_call([&first, &ikd_Tree]() { ikd_Tree.Build(first->points); }, "iKd Tree build", 1);
    LOG(INFO) << "ikd-Tree valid points: " << ikd_Tree.validnum();

    LOG(INFO) << "searching ikd-Tree";
    LOG(INFO) << "ikdTree KNN search_distance:" << max_distance;
    vector<PointVector> nearest_points(second->points.size());
    vector<vector<float>> points_distance(second->points.size());
    sad::evaluate_and_call(
        [&]() { for(int i = 0; i < second->points.size(); ++i){
                ikd_Tree.Nearest_Search(second->points[i], nearest_search_k, nearest_points[i], points_distance[i], max_distance);}; },
        "ikd-Tree 5NN Search", 1);
    matches.clear();
    int m = 0;
    for(int i = 0; i < second->points.size(); ++i){
        for(int j = 0; j < nearest_points[i].size(); ++j){
            m = -1;
            for(int k = 0; k < first->points.size(); ++k){
                if(compare_point(first->points[k], nearest_points[i][j])){
                    m = k;
                    break;
                }
            }
            matches.push_back({m, i});
        }
    }
    EvaluateMatches(true_matches, matches);  
    
    LOG(INFO) << "done.";

    SUCCEED();
}

int main(int argc, char** argv) {
    google::InitGoogleLogging(argv[0]);
    FLAGS_stderrthreshold = google::INFO;
    FLAGS_colorlogtostderr = true;

    string envVarName = "ikdTree_search_distance";
    string max_distance_str = getenv(envVarName.data());
    max_distance = stod(max_distance_str);

    testing::InitGoogleTest(&argc, argv);
    google::ParseCommandLineFlags(&argc, &argv, true);
    return RUN_ALL_TESTS();
}

有几个需要注意的地方:

1、启动程序时从命令行参数中获取环境变量作为 max_dist 参数的值,这样可以避免每次变化max_dist 都要重新编译,编译还是很费时的

		string envVarName = "ikdTree_search_distance";
    string max_distance_str = getenv(envVarName.data());
    max_distance = stod(max_distance_str);

2、添加 ikd-Tree 的建树以及 KNN 搜索测试,对于 second 点云中的每个点找最近邻

		LOG(INFO) << "ikd_Tree Test";
    KD_TREE<sad::PointType>::Ptr ikdtree_ptr = make_shared<KD_TREE<sad::PointType>>();
    KD_TREE<sad::PointType>& ikd_Tree = *ikdtree_ptr;
    sad::evaluate_and_call([&first, &ikd_Tree]() { ikd_Tree.Build(first->points); }, "iKd Tree build", 1);
    LOG(INFO) << "ikd-Tree valid points: " << ikd_Tree.validnum();

    LOG(INFO) << "searching ikd-Tree";
    LOG(INFO) << "ikdTree KNN search_distance:" << max_distance;
    vector<PointVector> nearest_points(second->points.size());
    vector<vector<float>> points_distance(second->points.size());
    sad::evaluate_and_call(
        [&]() { for(int i = 0; i < second->points.size(); ++i){
                ikd_Tree.Nearest_Search(second->points[i], nearest_search_k, nearest_points[i], points_distance[i], max_distance);}; },
        "ikd-Tree 5NN Search", 1);

3、计算准确率和召回率,这里不太好实现的是 ikd-Tree 的 KNN 记录的是点信息而不是点在点云中的索引,而评价函数中用的是索引,因此还需要遍历找点对应的索引,这里的时间复杂度很高,应该进行优化

		matches.clear();
    int m = 0;
    for(int i = 0; i < second->points.size(); ++i){
        for(int j = 0; j < nearest_points[i].size(); ++j){
            m = -1;
            for(int k = 0; k < first->points.size(); ++k){
                if(compare_point(first->points[k], nearest_points[i][j])){
                    m = k;
                    break;
                }
            }
            matches.push_back({m, i});
        }
    }
    EvaluateMatches(true_matches, matches); 

简单进行了几组实验

1、max_dist = 0.5

lei@lei:~/slam_in_autonomous_driving$ ikdTree_search_distance=0.5 ./bin/test_nn --gtest_filter=CH5_TEST.KDTREE_KNN
Note: Google Test filter = CH5_TEST.KDTREE_KNN
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from CH5_TEST
[ RUN      ] CH5_TEST.KDTREE_KNN
Failed to find match for field 'intensity'.
Failed to find match for field 'intensity'.
I0831 09:19:16.993024  3630 sys_utils.h:32] 方法 Kd Tree build 平均调用时间/次数: 6.07747/1 毫秒.
I0831 09:19:16.993263  3630 test_nn.cc:242] Kd tree leaves: 18869, points: 18869
I0831 09:19:19.454218  3630 sys_utils.h:32] 方法 Kd Tree 5NN multi-thread 平均调用时间/次数: 3.67089/1 毫秒.
I0831 09:19:19.454253  3630 test_nn.cc:69] truth: 93895, esti: 93895
I0831 09:19:22.478986  3630 test_nn.cc:95] precision: 1, recall: 1, fp: 0, fn: 0
I0831 09:19:22.479022  3630 test_nn.cc:254] building kdtree pcl
I0831 09:19:22.481360  3630 sys_utils.h:32] 方法 Kd Tree build 平均调用时间/次数: 2.31427/1 毫秒.
I0831 09:19:22.481376  3630 test_nn.cc:259] searching pcl
I0831 09:19:22.500319  3630 sys_utils.h:32] 方法 Kd Tree 5NN in PCL 平均调用时间/次数: 18.9071/1 毫秒.
I0831 09:19:22.500790  3630 test_nn.cc:69] truth: 93895, esti: 93895
I0831 09:19:25.495046  3630 test_nn.cc:95] precision: 1, recall: 1, fp: 0, fn: 0
I0831 09:19:25.495080  3630 test_nn.cc:280] ikd_Tree Test
Multi thread started 
I0831 09:19:25.516258  3630 sys_utils.h:32] 方法 iKd Tree build 平均调用时间/次数: 7.09316/1 毫秒.
I0831 09:19:25.516289  3630 test_nn.cc:284] ikd-Tree valid points: 18869
I0831 09:19:25.516294  3630 test_nn.cc:286] searching ikd-Tree
I0831 09:19:25.516294  3630 test_nn.cc:287] ikdTree KNN search_distance:0.5
I0831 09:19:25.550645  3630 sys_utils.h:32] 方法 ikd-Tree 5NN Search 平均调用时间/次数: 34.1594/1 毫秒.
I0831 09:19:26.320165  3630 test_nn.cc:69] truth: 93895, esti: 80229
I0831 09:19:29.010622  3630 test_nn.cc:95] precision: 1, recall: 0.854454, fp: 0, fn: 13666
I0831 09:19:29.010658  3630 test_nn.cc:310] done.
Rebuild thread terminated normally
[       OK ] CH5_TEST.KDTREE_KNN (12039 ms)
[----------] 1 test from CH5_TEST (12039 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (12040 ms total)
[  PASSED  ] 1 test.

2、max_dist = 1.0

lei@lei:~/slam_in_autonomous_driving$ ikdTree_search_distance=1.0 ./bin/test_nn --gtest_filter=CH5_TEST.KDTREE_KNN
Note: Google Test filter = CH5_TEST.KDTREE_KNN
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from CH5_TEST
[ RUN      ] CH5_TEST.KDTREE_KNN
Failed to find match for field 'intensity'.
Failed to find match for field 'intensity'.
I0831 09:19:44.656121  3637 sys_utils.h:32] 方法 Kd Tree build 平均调用时间/次数: 5.99205/1 毫秒.
I0831 09:19:44.656301  3637 test_nn.cc:242] Kd tree leaves: 18869, points: 18869
I0831 09:19:47.107566  3637 sys_utils.h:32] 方法 Kd Tree 5NN multi-thread 平均调用时间/次数: 4.01058/1 毫秒.
I0831 09:19:47.107602  3637 test_nn.cc:69] truth: 93895, esti: 93895
I0831 09:19:50.210033  3637 test_nn.cc:95] precision: 1, recall: 1, fp: 0, fn: 0
I0831 09:19:50.210068  3637 test_nn.cc:254] building kdtree pcl
I0831 09:19:50.212378  3637 sys_utils.h:32] 方法 Kd Tree build 平均调用时间/次数: 2.28724/1 毫秒.
I0831 09:19:50.212393  3637 test_nn.cc:259] searching pcl
I0831 09:19:50.231775  3637 sys_utils.h:32] 方法 Kd Tree 5NN in PCL 平均调用时间/次数: 19.3455/1 毫秒.
I0831 09:19:50.232239  3637 test_nn.cc:69] truth: 93895, esti: 93895
I0831 09:19:53.198168  3637 test_nn.cc:95] precision: 1, recall: 1, fp: 0, fn: 0
I0831 09:19:53.198204  3637 test_nn.cc:280] ikd_Tree Test
Multi thread started 
I0831 09:19:53.219951  3637 sys_utils.h:32] 方法 iKd Tree build 平均调用时间/次数: 6.79115/1 毫秒.
I0831 09:19:53.219980  3637 test_nn.cc:284] ikd-Tree valid points: 18869
I0831 09:19:53.219987  3637 test_nn.cc:286] searching ikd-Tree
I0831 09:19:53.219990  3637 test_nn.cc:287] ikdTree KNN search_distance:1
I0831 09:19:53.255913  3637 sys_utils.h:32] 方法 ikd-Tree 5NN Search 平均调用时间/次数: 35.78/1 毫秒.
I0831 09:19:54.156787  3637 test_nn.cc:69] truth: 93895, esti: 89684
I0831 09:19:56.939265  3637 test_nn.cc:95] precision: 1, recall: 0.955152, fp: 0, fn: 4211
I0831 09:19:56.939299  3637 test_nn.cc:310] done.
Rebuild thread terminated normally
[       OK ] CH5_TEST.KDTREE_KNN (12308 ms)
[----------] 1 test from CH5_TEST (12308 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (12308 ms total)
[  PASSED  ] 1 test.

3、max_dist = 1.5

lei@lei:~/slam_in_autonomous_driving$ ikdTree_search_distance=1.5 ./bin/test_nn --gtest_filter=CH5_TEST.KDTREE_KNN
Note: Google Test filter = CH5_TEST.KDTREE_KNN
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from CH5_TEST
[ RUN      ] CH5_TEST.KDTREE_KNN
Failed to find match for field 'intensity'.
Failed to find match for field 'intensity'.
I0831 09:43:12.231748  4167 sys_utils.h:32] 方法 Kd Tree build 平均调用时间/次数: 6.1671/1 毫秒.
I0831 09:43:12.251842  4167 test_nn.cc:242] Kd tree leaves: 18869, points: 18869
I0831 09:43:14.705106  4167 sys_utils.h:32] 方法 Kd Tree 5NN multi-thread 平均调用时间/次数: 4.15197/1 毫秒.
I0831 09:43:14.705140  4167 test_nn.cc:69] truth: 93895, esti: 93895
I0831 09:43:17.634130  4167 test_nn.cc:95] precision: 1, recall: 1, fp: 0, fn: 0
I0831 09:43:17.634166  4167 test_nn.cc:254] building kdtree pcl
I0831 09:43:17.636514  4167 sys_utils.h:32] 方法 Kd Tree build 平均调用时间/次数: 2.32538/1 毫秒.
I0831 09:43:17.636538  4167 test_nn.cc:259] searching pcl
I0831 09:43:17.655293  4167 sys_utils.h:32] 方法 Kd Tree 5NN in PCL 平均调用时间/次数: 18.7018/1 毫秒.
I0831 09:43:17.655751  4167 test_nn.cc:69] truth: 93895, esti: 93895
I0831 09:43:20.850251  4167 test_nn.cc:95] precision: 1, recall: 1, fp: 0, fn: 0
I0831 09:43:20.850288  4167 test_nn.cc:280] ikd_Tree Test
Multi thread started 
I0831 09:43:20.870407  4167 sys_utils.h:32] 方法 iKd Tree build 平均调用时间/次数: 5.79657/1 毫秒.
I0831 09:43:20.870435  4167 test_nn.cc:284] ikd-Tree valid points: 18869
I0831 09:43:20.870441  4167 test_nn.cc:286] searching ikd-Tree
I0831 09:43:20.870443  4167 test_nn.cc:287] ikdTree KNN search_distance:1.5
I0831 09:43:20.911399  4167 sys_utils.h:32] 方法 ikd-Tree 5NN Search 平均调用时间/次数: 40.8256/1 毫秒.
I0831 09:43:22.000274  4167 test_nn.cc:69] truth: 93895, esti: 91513
I0831 09:43:25.069787  4167 test_nn.cc:95] precision: 1, recall: 0.974631, fp: 0, fn: 2382
I0831 09:43:25.069824  4167 test_nn.cc:310] done.
Rebuild thread terminated normally
[       OK ] CH5_TEST.KDTREE_KNN (12858 ms)
[----------] 1 test from CH5_TEST (12858 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (12858 ms total)
[  PASSED  ] 1 test.

经观察可以发现随着 max_dist 的增大,KNN 搜索的时间在增大,但召回率(recall)也在增大,说明搜索效率和准确率确实是矛盾的

另外 ikd-Tree KNN 的时间确实要大于 PCL KNN 的时间,这和论文中是一致的,但总体时间短很多

你可能感兴趣的:(SLAM,算法)