├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── Thirdparty
└── ndt_omp_ros2
│ ├── .clang-format
│ ├── CMakeLists.txt
│ ├── LICENSE
│ ├── README.md
│ ├── apps
│ └── align.cpp
│ ├── data
│ ├── 251370668.pcd
│ ├── 251371071.pcd
│ └── screenshot.png
│ ├── include
│ └── pclomp
│ │ ├── gicp_omp.h
│ │ ├── gicp_omp_impl.hpp
│ │ ├── ndt_omp.h
│ │ ├── ndt_omp_impl.hpp
│ │ ├── voxel_grid_covariance_omp.h
│ │ └── voxel_grid_covariance_omp_impl.hpp
│ ├── package.xml
│ └── src
│ └── pclomp
│ ├── gicp_omp.cpp
│ ├── ndt_omp.cpp
│ └── voxel_grid_covariance_omp.cpp
├── graph_based_slam
├── CMakeLists.txt
├── include
│ └── graph_based_slam
│ │ └── graph_based_slam_component.h
├── launch
│ └── graphbasedslam.launch.py
├── package.xml
├── param
│ └── graphbasedslam.yaml
└── src
│ ├── graph_based_slam_component.cpp
│ └── graph_based_slam_node.cpp
├── lidarslam_msgs
├── CMakeLists.txt
├── msg
│ ├── MapArray.msg
│ └── SubMap.msg
└── package.xml
└── scanmatcher
├── CMakeLists.txt
├── images
├── big_loop_with_lo.png
├── big_loop_without_lo.png
├── li_slam.gif
├── li_slam.png
├── liosam_thesis.png
└── rosgraph.png
├── include
└── scanmatcher
│ ├── scanmatcher_component.h
│ └── utility.h
├── launch
├── lio.launch.py
└── lio_bigloop.launch.py
├── package.xml
├── param
├── lio.yaml
└── lio_bigloop.yaml
├── rviz
├── lio.rviz
├── lio_bigloop.rviz
└── mapping.rviz
└── src
├── image_projection.cpp
├── imu_preintegration.cpp
├── scanmatcher_component.cpp
└── scanmatcher_node.cpp
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - humble
7 | - develop
8 |
9 | jobs:
10 | job:
11 | name: Build
12 | runs-on: ubuntu-22.04
13 | container: ros:humble-ros-core
14 | steps:
15 | - name: Install Git and software-properties-common
16 | run: |
17 | apt-get update
18 | apt-get install -y git software-properties-common
19 | shell: bash
20 | - uses: actions/checkout@v2
21 | with:
22 | submodules: true
23 | - name: Copy repository
24 | run: |
25 | mkdir -p ~/ros2_ws/src/li_slam_ros2
26 | cp -rf . ~/ros2_ws/src/li_slam_ros2
27 | - name: Install gtsam
28 | run: |
29 | add-apt-repository ppa:borglab/gtsam-release-4.1
30 | apt update
31 | apt install -y libgtsam-dev libgtsam-unstable-dev
32 | - name: Install dependencies
33 | run: |
34 | source /opt/ros/humble/setup.bash
35 | apt install -y python3-rosdep
36 | rosdep init
37 | rosdep update
38 | cd ~/ros2_ws/src
39 | rosdep install -r -y --from-paths . --ignore-src
40 | shell: bash
41 | - name: Build packages
42 | run: |
43 | source /opt/ros/humble/setup.bash
44 | # Install colcon
45 | # Ref: https://index.ros.org/doc/ros2/Tutorials/Colcon-Tutorial/
46 | sudo apt install -y python3-colcon-common-extensions
47 | cd ~/ros2_ws
48 | colcon build
49 | source ~/ros2_ws/install/setup.bash
50 | shell: bash
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | scanmatcher/launch/test.launch.py
3 | scanmatcher/param/test.yaml
4 | scanmatcher/rviz/test.rviz
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Thirdparty/ndt_omp_ros2"]
2 | path = Thirdparty/ndt_omp_ros2
3 | url = https://github.com/rsasaki0109/ndt_omp_ros2
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2020, Ryohei Sasaki
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | li_slam_ros2
2 | ====
3 |
4 | This package is a combination of [lidarslam_ros2](https://github.com/rsasaki0109/lidarslam_ros2) and the [LIO-SAM](https://github.com/TixiaoShan/LIO-SAM) IMU composite method.
5 |
6 | See LIO-SAM for IMU composites, otherwise see lidarslam_ros2.
7 |
8 | - Walking dataset(casual_walk.bag)
9 |
10 |
11 | Yellow path: path without loop closure, Green path: modified path, Red: map
12 |
13 | Reference(From the LIO-SAM paper)
14 | https://github.com/TixiaoShan/LIO-SAM/blob/master/config/doc/paper.pdf
15 |
16 |
17 | - Campus dataset (large) demo(big_loop.bag)
18 |
19 |
20 |
21 | Yellow path: path without loop closure, Red: map
22 | (the 10x10 grids in size of 10m × 10m)
23 |
24 |
25 |
26 | Green path: modified path with loop closure, Red: map
27 |
28 |
29 |
30 | ## requirement to build
31 | You need [ndt_omp_ros2](https://github.com/rsasaki0109/ndt_omp_ros2) and gtsam for scan-matcher
32 |
33 | clone
34 | ```
35 | cd ~/ros2_ws/src
36 | git clone --recursive https://github.com/rsasaki0109/li_slam_ros2
37 | ```
38 | gtsam install
39 | ```
40 | sudo add-apt-repository ppa:borglab/gtsam-release-4.1
41 | sudo apt update
42 | sudo apt install libgtsam-dev libgtsam-unstable-dev
43 | ```
44 | build
45 | ```
46 | cd ~/ros2_ws
47 | rosdep update
48 | rosdep install --from-paths src --ignore-src -yr
49 | colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
50 | ```
51 |
52 |
53 | ## Walking dataset demo(casual_walk.bag)
54 |
55 | The optimization pipeline in Lidar Inertial SLAM were taken from [LIO-SAM](https://github.com/TixiaoShan/LIO-SAM).
56 |
57 | (Note: See the LIO-SAM repository for detailed settings regarding IMU.
58 | The other thing to note is that the speed will diverge if the voxel_grid_size is large.
59 |
60 | demo data(ROS1) in LIO-SAM
61 | https://github.com/TixiaoShan/LIO-SAM
62 | To use ros1 rosbag , use [rosbags](https://pypi.org/project/rosbags/).
63 | The Velodyne VLP-16 was used in this data.
64 |
65 | ```
66 | rviz2 -d src/li_slam_ros2/scanmatcher/rviz/lio.rviz
67 | ```
68 |
69 | ```
70 | ros2 launch scanmatcher lio.launch.py
71 | ```
72 |
73 | ```
74 | ros2 bag play walking_dataset/
75 | ```
76 |
77 |
78 |
79 | Green arrow: pose, Yellow path: path, Green path: path by imu
80 |
81 |
82 |
83 | Yellow path: path without loop closure, Green path: modified path, Red: map
84 |
85 | rosgraph
86 |
87 |
88 |
89 | `pose_graph.g2o` and `map.pcd` are saved in loop closing or using the following service call.
90 |
91 | ```
92 | ros2 service call /map_save std_srvs/Empty
93 | ```
94 |
95 | ## Campus dataset (large) demo(big_loop.bag)
96 |
97 | ```
98 | rviz2 -d src/li_slam_ros2/scanmatcher/rviz/lio_bigloop.rviz
99 | ```
100 |
101 | ```
102 | ros2 launch scanmatcher lio_bigloop.launch.py
103 | ```
104 |
105 | ```
106 | ros2 bag play rosbag_v2 big_loop/
107 | ```
108 |
109 |
110 |
111 |
112 | Yellow path: path without loop closure, Red: map
113 | (the 10x10 grids in size of 10m × 10m)
114 |
115 |
116 |
117 | Green path: modified path with loop closure, Red: map
118 |
119 |
120 |
121 | ## Used Libraries
122 |
123 | - Eigen
124 | - PCL(BSD3)
125 | - g2o(BSD2 except a part)
126 | - [ndt_omp](https://github.com/koide3/ndt_omp) (BSD2)
127 | - gtsam(BSD2)
128 |
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | Language: Cpp
3 | # BasedOnStyle: Google
4 | AccessModifierOffset: -2
5 | AlignAfterOpenBracket: Align
6 | AlignConsecutiveAssignments: false
7 | AlignConsecutiveDeclarations: false
8 | AlignEscapedNewlines: Left
9 | AlignOperands: true
10 | AlignTrailingComments: true
11 | AllowAllParametersOfDeclarationOnNextLine: true
12 | AllowShortBlocksOnASingleLine: false
13 | AllowShortCaseLabelsOnASingleLine: false
14 | AllowShortFunctionsOnASingleLine: Empty
15 | AllowShortIfStatementsOnASingleLine: true
16 | AllowShortLoopsOnASingleLine: true
17 | AlwaysBreakAfterDefinitionReturnType: None
18 | AlwaysBreakAfterReturnType: None
19 | AlwaysBreakBeforeMultilineStrings: true
20 | AlwaysBreakTemplateDeclarations: true
21 | BinPackArguments: true
22 | BinPackParameters: false
23 | BraceWrapping:
24 | AfterClass: false
25 | AfterControlStatement: false
26 | AfterEnum: false
27 | AfterFunction: false
28 | AfterNamespace: false
29 | AfterObjCDeclaration: false
30 | AfterStruct: false
31 | AfterUnion: false
32 | AfterExternBlock: false
33 | BeforeCatch: false
34 | BeforeElse: false
35 | IndentBraces: false
36 | SplitEmptyFunction: true
37 | SplitEmptyRecord: true
38 | SplitEmptyNamespace: true
39 | BreakBeforeBinaryOperators: None
40 | BreakBeforeBraces: Attach
41 | BreakBeforeInheritanceComma: false
42 | BreakBeforeTernaryOperators: true
43 | BreakConstructorInitializersBeforeComma: false
44 | BreakConstructorInitializers: BeforeColon
45 | BreakAfterJavaFieldAnnotations: false
46 | BreakStringLiterals: false
47 | ColumnLimit: 256
48 | CommentPragmas: '^ IWYU pragma:'
49 | CompactNamespaces: false
50 | ConstructorInitializerAllOnOneLineOrOnePerLine: true
51 | ConstructorInitializerIndentWidth: 4
52 | ContinuationIndentWidth: 4
53 | Cpp11BracedListStyle: true
54 | DerivePointerAlignment: true
55 | DisableFormat: false
56 | ExperimentalAutoDetectBinPacking: false
57 | FixNamespaceComments: true
58 | ForEachMacros:
59 | - foreach
60 | - Q_FOREACH
61 | - BOOST_FOREACH
62 | IncludeBlocks: Preserve
63 | IncludeCategories:
64 | - Regex: '^'
65 | Priority: 2
66 | - Regex: '^<.*\.h>'
67 | Priority: 1
68 | - Regex: '^<.*'
69 | Priority: 2
70 | - Regex: '.*'
71 | Priority: 3
72 | IncludeIsMainRegex: '([-_](test|unittest))?$'
73 | IndentCaseLabels: true
74 | IndentPPDirectives: None
75 | IndentWidth: 2
76 | IndentWrappedFunctionNames: false
77 | JavaScriptQuotes: Leave
78 | JavaScriptWrapImports: true
79 | KeepEmptyLinesAtTheStartOfBlocks: false
80 | MacroBlockBegin: ''
81 | MacroBlockEnd: ''
82 | MaxEmptyLinesToKeep: 1
83 | NamespaceIndentation: None
84 | ObjCBlockIndentWidth: 2
85 | ObjCSpaceAfterProperty: false
86 | ObjCSpaceBeforeProtocolList: false
87 | PenaltyBreakAssignment: 2
88 | PenaltyBreakBeforeFirstCallParameter: 1
89 | PenaltyBreakComment: 300
90 | PenaltyBreakFirstLessLess: 120
91 | PenaltyBreakString: 1000
92 | PenaltyExcessCharacter: 0
93 | PenaltyReturnTypeOnItsOwnLine: 200
94 | PointerAlignment: Left
95 | RawStringFormats:
96 | - Delimiter: pb
97 | Language: TextProto
98 | BasedOnStyle: google
99 | ReflowComments: true
100 | SortIncludes: false
101 | SortUsingDeclarations: false
102 | SpaceAfterCStyleCast: false
103 | SpaceAfterTemplateKeyword: false
104 | SpaceBeforeAssignmentOperators: true
105 | SpaceBeforeParens: Never
106 | SpaceInEmptyParentheses: false
107 | SpacesBeforeTrailingComments: 2
108 | SpacesInAngles: false
109 | SpacesInContainerLiterals: true
110 | SpacesInCStyleCastParentheses: false
111 | SpacesInParentheses: false
112 | SpacesInSquareBrackets: false
113 | Standard: Auto
114 | TabWidth: 8
115 | UseTab: Never
116 | ...
117 |
118 |
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 | project(ndt_omp_ros2)
3 |
4 | # -mavx causes a lot of errors!!
5 | #add_definitions(-std=c++14 -msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2)
6 | #set(CMAKE_CXX_FLAGS "-std=c++14 -msse -msse2 -msse3 -msse4 -msse4.1 -msse4.2")
7 |
8 | if(NOT CMAKE_CXX_STANDARD)
9 | set(CMAKE_CXX_STANDARD 14)
10 | endif()
11 |
12 | # pcl 1.7 causes a segfault when it is built with debug mode
13 | set(CMAKE_BUILD_TYPE "RELEASE")
14 |
15 | #find_package(catkin REQUIRED COMPONENTS
16 | # roscpp
17 | # pcl_ros
18 | #)
19 | find_package(ament_cmake REQUIRED)
20 | find_package(rclcpp REQUIRED)
21 | find_package(std_msgs REQUIRED)
22 |
23 | find_package(PCL 1.12 REQUIRED)
24 | include_directories(${PCL_INCLUDE_DIRS})
25 | link_directories(${PCL_LIBRARY_DIRS})
26 | add_definitions(${PCL_DEFINITIONS})
27 |
28 | message(STATUS "PCL_INCLUDE_DIRS:" ${PCL_INCLUDE_DIRS})
29 | message(STATUS "PCL_LIBRARY_DIRS:" ${PCL_LIBRARY_DIRS})
30 | message(STATUS "PCL_DEFINITIONS:" ${PCL_DEFINITIONS})
31 |
32 | find_package(OpenMP)
33 | if (OPENMP_FOUND)
34 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
35 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
36 | endif()
37 |
38 | ###################################
39 | ## catkin specific configuration ##
40 | ###################################
41 | #catkin_package(
42 | # INCLUDE_DIRS include
43 | # LIBRARIES ndt_omp
44 | #)
45 |
46 | ###########
47 | ## Build ##
48 | ###########
49 | include_directories(
50 | include
51 | )
52 |
53 | add_library(ndt_omp
54 | src/pclomp/voxel_grid_covariance_omp.cpp
55 | src/pclomp/ndt_omp.cpp
56 | src/pclomp/gicp_omp.cpp
57 | )
58 |
59 | add_executable(align
60 | apps/align.cpp
61 | )
62 | add_dependencies(align
63 | ndt_omp
64 | )
65 | target_link_libraries(align
66 | ${PCL_LIBRARIES}
67 | ndt_omp
68 | )
69 |
70 | ament_target_dependencies(align
71 | rclcpp
72 | std_msgs
73 | )
74 |
75 | ############
76 | ## INSTAL ##
77 | ############
78 |
79 | install(TARGETS
80 | align
81 | DESTINATION lib/${PROJECT_NAME}
82 | )
83 |
84 | install(
85 | TARGETS
86 | align
87 | ARCHIVE DESTINATION lib
88 | LIBRARY DESTINATION lib
89 | RUNTIME DESTINATION bin
90 | )
91 |
92 | # install headers
93 | install(
94 | DIRECTORY "include/"
95 | DESTINATION include
96 | )
97 |
98 | ament_export_dependencies(rclcpp)
99 | ament_export_include_directories(include)
100 |
101 | ament_package()
102 |
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2019, k.koide
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/README.md:
--------------------------------------------------------------------------------
1 | # ndt_omp_ros2
2 | This package provides an OpenMP-boosted Normal Distributions Transform (and GICP) algorithm derived from pcl. The NDT algorithm is modified to be SSE-friendly and multi-threaded. It can run up to 10 times faster than its original version in pcl.
3 |
4 | ### Benchmark
5 | ```
6 | $ cd ~/ros2_ws/src/ndt_omp_ros2/data
7 | $ ros2 run ndt_omp_ros2 align 251370668.pcd 251371071.pcd
8 |
9 | --- pcl::GICP ---
10 | single : 267.385[msec]
11 | 10times: 1151.76[msec]
12 | fitness: 0.220382
13 |
14 | --- pclomp::GICP ---
15 | single : 173.152[msec]
16 | 10times: 1299.14[msec]
17 | fitness: 0.220388
18 |
19 | --- pcl::NDT ---
20 | single : 425.142[msec]
21 | 10times: 3638.77[msec]
22 | fitness: 0.213937
23 |
24 | --- pclomp::NDT (KDTREE, 1 threads) ---
25 | single : 308.935[msec]
26 | 10times: 3095.53[msec]
27 | fitness: 0.213937
28 |
29 | --- pclomp::NDT (DIRECT7, 1 threads) ---
30 | single : 188.942[msec]
31 | 10times: 1373.47[msec]
32 | fitness: 0.214205
33 |
34 | --- pclomp::NDT (DIRECT1, 1 threads) ---
35 | single : 41.3584[msec]
36 | 10times: 347.261[msec]
37 | fitness: 0.208511
38 |
39 | --- pclomp::NDT (KDTREE, 8 threads) ---
40 | single : 108.68[msec]
41 | 10times: 1046.16[msec]
42 | fitness: 0.213937
43 |
44 | --- pclomp::NDT (DIRECT7, 8 threads) ---
45 | single : 56.9189[msec]
46 | 10times: 545.279[msec]
47 | fitness: 0.214205
48 |
49 | --- pclomp::NDT (DIRECT1, 8 threads) ---
50 | single : 16.7266[msec]
51 | 10times: 169.097[msec]
52 | fitness: 0.208511
53 | ```
54 |
55 | Several methods for neighbor voxel search are implemented. If you select pclomp::KDTREE, results will be completely same as the original pcl::NDT. We recommend to use pclomp::DIRECT7 which is faster and stable. If you need extremely fast registration, choose pclomp::DIRECT1, but it might be a bit unstable.
56 |
57 | 
58 | Red: target, Green: source, Blue: aligned
59 |
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/apps/align.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | //#include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include
13 | #include
14 |
15 | #include
16 |
17 | // align point clouds and measure processing time
18 | pcl::PointCloud::Ptr align(boost::shared_ptr> registration, const pcl::PointCloud::Ptr& target_cloud, const pcl::PointCloud::Ptr& source_cloud ) {
19 | registration->setInputTarget(target_cloud);
20 | registration->setInputSource(source_cloud);
21 | pcl::PointCloud::Ptr aligned(new pcl::PointCloud());
22 |
23 | rclcpp::Clock system_clock;
24 |
25 | ///auto t1 = ros::WallTime::now();
26 | auto t1 = system_clock.now();
27 | registration->align(*aligned);
28 | auto t2 = system_clock.now();
29 | //std::cout << "single : " << (t2 - t1).toSec() * 1000 << "[msec]" << std::endl;
30 | std::cout << "single : " << (t2 - t1).seconds()* 1000 << "[msec]" << std::endl;
31 |
32 | for(int i=0; i<10; i++) {
33 | registration->align(*aligned);
34 | }
35 | auto t3 = system_clock.now();
36 | std::cout << "10times: " << (t3 - t2).seconds() * 1000 << "[msec]" << std::endl;
37 | std::cout << "fitness: " << registration->getFitnessScore() << std::endl << std::endl;
38 |
39 | return aligned;
40 | }
41 |
42 | int main(int argc, char** argv) {
43 | if(argc != 3) {
44 | std::cout << "usage: align target.pcd source.pcd" << std::endl;
45 | return 0;
46 | }
47 |
48 | std::string target_pcd = argv[1];
49 | std::string source_pcd = argv[2];
50 |
51 | pcl::PointCloud::Ptr target_cloud(new pcl::PointCloud());
52 | pcl::PointCloud::Ptr source_cloud(new pcl::PointCloud());
53 |
54 | if(pcl::io::loadPCDFile(target_pcd, *target_cloud)) {
55 | std::cerr << "failed to load " << target_pcd << std::endl;
56 | return 0;
57 | }
58 | if(pcl::io::loadPCDFile(source_pcd, *source_cloud)) {
59 | std::cerr << "failed to load " << source_pcd << std::endl;
60 | return 0;
61 | }
62 |
63 | // downsampling
64 | pcl::PointCloud::Ptr downsampled(new pcl::PointCloud());
65 |
66 | pcl::VoxelGrid voxelgrid;
67 | voxelgrid.setLeafSize(0.1f, 0.1f, 0.1f);
68 |
69 | voxelgrid.setInputCloud(target_cloud);
70 | voxelgrid.filter(*downsampled);
71 | *target_cloud = *downsampled;
72 |
73 | voxelgrid.setInputCloud(source_cloud);
74 | voxelgrid.filter(*downsampled);
75 | source_cloud = downsampled;
76 |
77 | //ros::Time::init();
78 |
79 | // benchmark
80 | std::cout << "--- pcl::GICP ---" << std::endl;
81 | boost::shared_ptr> gicp(new pcl::GeneralizedIterativeClosestPoint());
82 | pcl::PointCloud::Ptr aligned = align(gicp, target_cloud, source_cloud);
83 |
84 | //TODO:The problem of uninitialized member variables
85 | std::cout << "--- pclomp::GICP ---" << std::endl;
86 | boost::shared_ptr> gicp_omp(new pclomp::GeneralizedIterativeClosestPoint());
87 | aligned = align(gicp_omp, target_cloud, source_cloud);
88 |
89 |
90 | std::cout << "--- pcl::NDT ---" << std::endl;
91 | boost::shared_ptr> ndt(new pcl::NormalDistributionsTransform());
92 | ndt->setResolution(1.0);
93 | aligned = align(ndt, target_cloud, source_cloud);
94 |
95 | std::vector num_threads = {1, omp_get_max_threads()};
96 | std::vector> search_methods = {
97 | {"KDTREE", pclomp::KDTREE},
98 | {"DIRECT7", pclomp::DIRECT7},
99 | {"DIRECT1", pclomp::DIRECT1}
100 | };
101 |
102 | pclomp::NormalDistributionsTransform::Ptr ndt_omp(new pclomp::NormalDistributionsTransform());
103 | ndt_omp->setResolution(1.0);
104 |
105 |
106 | for(int n : num_threads) {
107 | for(const auto& search_method : search_methods) {
108 | std::cout << "--- pclomp::NDT (" << search_method.first << ", " << n << " threads) ---" << std::endl;
109 | ndt_omp->setNumThreads(n);
110 | ndt_omp->setNeighborhoodSearchMethod(search_method.second);
111 | aligned = align(ndt_omp, target_cloud, source_cloud);
112 | }
113 | }
114 |
115 |
116 | // visulization
117 | pcl::visualization::PCLVisualizer vis("vis");
118 | pcl::visualization::PointCloudColorHandlerCustom target_handler(target_cloud, 255.0, 0.0, 0.0);
119 | pcl::visualization::PointCloudColorHandlerCustom source_handler(source_cloud, 0.0, 255.0, 0.0);
120 | pcl::visualization::PointCloudColorHandlerCustom aligned_handler(aligned, 0.0, 0.0, 255.0);
121 | vis.addPointCloud(target_cloud, target_handler, "target");
122 | vis.addPointCloud(source_cloud, source_handler, "source");
123 | vis.addPointCloud(aligned, aligned_handler, "aligned");
124 | vis.spin();
125 |
126 | return 0;
127 | }
128 |
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/data/251370668.pcd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsasaki0109/li_slam_ros2/a684c7af7ba1f08bff957c3a0690c77476b7c077/Thirdparty/ndt_omp_ros2/data/251370668.pcd
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/data/251371071.pcd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsasaki0109/li_slam_ros2/a684c7af7ba1f08bff957c3a0690c77476b7c077/Thirdparty/ndt_omp_ros2/data/251371071.pcd
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/data/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsasaki0109/li_slam_ros2/a684c7af7ba1f08bff957c3a0690c77476b7c077/Thirdparty/ndt_omp_ros2/data/screenshot.png
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/include/pclomp/gicp_omp.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD License)
3 | *
4 | * Point Cloud Library (PCL) - www.pointclouds.org
5 | * Copyright (c) 2010, Willow Garage, Inc.
6 | * Copyright (c) 2012-, Open Perception, Inc.
7 | *
8 | * All rights reserved.
9 | *
10 | * Redistribution and use in source and binary forms, with or without
11 | * modification, are permitted provided that the following conditions
12 | * are met:
13 | *
14 | * * Redistributions of source code must retain the above copyright
15 | * notice, this list of conditions and the following disclaimer.
16 | * * Redistributions in binary form must reproduce the above
17 | * copyright notice, this list of conditions and the following
18 | * disclaimer in the documentation and/or other materials provided
19 | * with the distribution.
20 | * * Neither the name of the copyright holder(s) nor the names of its
21 | * contributors may be used to endorse or promote products derived
22 | * from this software without specific prior written permission.
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 | * POSSIBILITY OF SUCH DAMAGE.
36 | *
37 | * $Id$
38 | *
39 | */
40 |
41 | #ifndef PCL_GICP_OMP_H_
42 | #define PCL_GICP_OMP_H_
43 |
44 | #include
45 | #include
46 | #include
47 |
48 | namespace pclomp
49 | {
50 | /** \brief GeneralizedIterativeClosestPoint is an ICP variant that implements the
51 | * generalized iterative closest point algorithm as described by Alex Segal et al. in
52 | * http://www.robots.ox.ac.uk/~avsegal/resources/papers/Generalized_ICP.pdf
53 | * The approach is based on using anistropic cost functions to optimize the alignment
54 | * after closest point assignments have been made.
55 | * The original code uses GSL and ANN while in ours we use an eigen mapped BFGS and
56 | * FLANN.
57 | * \author Nizar Sallem
58 | * \ingroup registration
59 | */
60 | template
61 | class GeneralizedIterativeClosestPoint : public pcl::IterativeClosestPoint
62 | {
63 | public:
64 | using pcl::IterativeClosestPoint::reg_name_;
65 | using pcl::IterativeClosestPoint::getClassName;
66 | using pcl::IterativeClosestPoint::indices_;
67 | using pcl::IterativeClosestPoint::target_;
68 | using pcl::IterativeClosestPoint::input_;
69 | using pcl::IterativeClosestPoint::tree_;
70 | using pcl::IterativeClosestPoint::tree_reciprocal_;
71 | using pcl::IterativeClosestPoint::nr_iterations_;
72 | using pcl::IterativeClosestPoint::max_iterations_;
73 | using pcl::IterativeClosestPoint::previous_transformation_;
74 | using pcl::IterativeClosestPoint::final_transformation_;
75 | using pcl::IterativeClosestPoint::transformation_;
76 | using pcl::IterativeClosestPoint::transformation_epsilon_;
77 | using pcl::IterativeClosestPoint::converged_;
78 | using pcl::IterativeClosestPoint::corr_dist_threshold_;
79 | using pcl::IterativeClosestPoint::inlier_threshold_;
80 | using pcl::IterativeClosestPoint::min_number_correspondences_;
81 | using pcl::IterativeClosestPoint::update_visualizer_;
82 |
83 | using PointCloudSource = pcl::PointCloud;
84 | using PointCloudSourcePtr = typename PointCloudSource::Ptr;
85 | using PointCloudSourceConstPtr = typename PointCloudSource::ConstPtr;
86 |
87 | using PointCloudTarget = pcl::PointCloud;
88 | using PointCloudTargetPtr = typename PointCloudTarget::Ptr;
89 | using PointCloudTargetConstPtr = typename PointCloudTarget::ConstPtr;
90 |
91 | using PointIndicesPtr = pcl::PointIndices::Ptr;
92 | using PointIndicesConstPtr = pcl::PointIndices::ConstPtr;
93 |
94 | using MatricesVector = std::vector >;
95 | using MatricesVectorPtr = boost::shared_ptr;
96 | using MatricesVectorConstPtr = boost::shared_ptr;
97 |
98 | using InputKdTree = typename pcl::Registration::KdTree;
99 | using InputKdTreePtr = typename pcl::Registration::KdTreePtr;
100 |
101 | using Ptr = boost::shared_ptr >;
102 | using ConstPtr = boost::shared_ptr >;
103 |
104 |
105 | using Vector6d = Eigen::Matrix;
106 |
107 | /** \brief Empty constructor. */
108 | GeneralizedIterativeClosestPoint ()
109 | : k_correspondences_(20)
110 | , gicp_epsilon_(0.001)
111 | , rotation_epsilon_(2e-3)
112 | , mahalanobis_(0)
113 | , max_inner_iterations_(20)
114 | {
115 | min_number_correspondences_ = 4;
116 | reg_name_ = "GeneralizedIterativeClosestPoint";
117 | max_iterations_ = 200;
118 | transformation_epsilon_ = 5e-4;
119 | corr_dist_threshold_ = 5.;
120 | rigid_transformation_estimation_ = [this] (const PointCloudSource& cloud_src,
121 | const std::vector& indices_src,
122 | const PointCloudTarget& cloud_tgt,
123 | const std::vector& indices_tgt,
124 | Eigen::Matrix4f& transformation_matrix)
125 | {
126 | estimateRigidTransformationBFGS (cloud_src, indices_src, cloud_tgt, indices_tgt, transformation_matrix);
127 | };
128 | }
129 |
130 | /** \brief Provide a pointer to the input dataset
131 | * \param cloud the const boost shared pointer to a PointCloud message
132 | */
133 | inline void
134 | setInputSource (const PointCloudSourceConstPtr &cloud) override
135 | {
136 |
137 | if (cloud->points.empty ())
138 | {
139 | PCL_ERROR ("[pcl::%s::setInputSource] Invalid or empty point cloud dataset given!\n", getClassName ().c_str ());
140 | return;
141 | }
142 | PointCloudSource input = *cloud;
143 | // Set all the point.data[3] values to 1 to aid the rigid transformation
144 | for (size_t i = 0; i < input.size (); ++i)
145 | input[i].data[3] = 1.0;
146 |
147 | pcl::IterativeClosestPoint::setInputSource (cloud);
148 | input_covariances_.reset ();
149 | }
150 |
151 | /** \brief Provide a pointer to the covariances of the input source (if computed externally!).
152 | * If not set, GeneralizedIterativeClosestPoint will compute the covariances itself.
153 | * Make sure to set the covariances AFTER setting the input source point cloud (setting the input source point cloud will reset the covariances).
154 | * \param[in] target the input point cloud target
155 | */
156 | inline void
157 | setSourceCovariances (const MatricesVectorPtr& covariances)
158 | {
159 | input_covariances_ = covariances;
160 | }
161 |
162 | /** \brief Provide a pointer to the input target (e.g., the point cloud that we want to align the input source to)
163 | * \param[in] target the input point cloud target
164 | */
165 | inline void
166 | setInputTarget (const PointCloudTargetConstPtr &target) override
167 | {
168 | pcl::IterativeClosestPoint::setInputTarget(target);
169 | target_covariances_.reset ();
170 | }
171 |
172 | /** \brief Provide a pointer to the covariances of the input target (if computed externally!).
173 | * If not set, GeneralizedIterativeClosestPoint will compute the covariances itself.
174 | * Make sure to set the covariances AFTER setting the input source point cloud (setting the input source point cloud will reset the covariances).
175 | * \param[in] target the input point cloud target
176 | */
177 | inline void
178 | setTargetCovariances (const MatricesVectorPtr& covariances)
179 | {
180 | target_covariances_ = covariances;
181 | }
182 |
183 | /** \brief Estimate a rigid rotation transformation between a source and a target point cloud using an iterative
184 | * non-linear Levenberg-Marquardt approach.
185 | * \param[in] cloud_src the source point cloud dataset
186 | * \param[in] indices_src the vector of indices describing the points of interest in \a cloud_src
187 | * \param[in] cloud_tgt the target point cloud dataset
188 | * \param[in] indices_tgt the vector of indices describing the correspondences of the interest points from \a indices_src
189 | * \param[out] transformation_matrix the resultant transformation matrix
190 | */
191 | void
192 | estimateRigidTransformationBFGS (const PointCloudSource &cloud_src,
193 | const std::vector &indices_src,
194 | const PointCloudTarget &cloud_tgt,
195 | const std::vector &indices_tgt,
196 | Eigen::Matrix4f &transformation_matrix);
197 |
198 | /** \brief \return Mahalanobis distance matrix for the given point index */
199 | inline const Eigen::Matrix4f& mahalanobis(size_t index) const
200 | {
201 | assert(index < mahalanobis_.size());
202 | return mahalanobis_[index];
203 | }
204 |
205 | /** \brief Computes rotation matrix derivative.
206 | * rotation matrix is obtainded from rotation angles x[3], x[4] and x[5]
207 | * \return d/d_rx, d/d_ry and d/d_rz respectively in g[3], g[4] and g[5]
208 | * param x array representing 3D transformation
209 | * param R rotation matrix
210 | * param g gradient vector
211 | */
212 | void
213 | computeRDerivative(const Vector6d &x, const Eigen::Matrix3d &R, Vector6d &g) const;
214 |
215 | /** \brief Set the rotation epsilon (maximum allowable difference between two
216 | * consecutive rotations) in order for an optimization to be considered as having
217 | * converged to the final solution.
218 | * \param epsilon the rotation epsilon
219 | */
220 | inline void
221 | setRotationEpsilon (double epsilon) { rotation_epsilon_ = epsilon; }
222 |
223 | /** \brief Get the rotation epsilon (maximum allowable difference between two
224 | * consecutive rotations) as set by the user.
225 | */
226 | inline double
227 | getRotationEpsilon () { return (rotation_epsilon_); }
228 |
229 | /** \brief Set the number of neighbors used when selecting a point neighbourhood
230 | * to compute covariances.
231 | * A higher value will bring more accurate covariance matrix but will make
232 | * covariances computation slower.
233 | * \param k the number of neighbors to use when computing covariances
234 | */
235 | void
236 | setCorrespondenceRandomness (int k) { k_correspondences_ = k; }
237 |
238 | /** \brief Get the number of neighbors used when computing covariances as set by
239 | * the user
240 | */
241 | int
242 | getCorrespondenceRandomness () { return (k_correspondences_); }
243 |
244 | /** set maximum number of iterations at the optimization step
245 | * \param[in] max maximum number of iterations for the optimizer
246 | */
247 | void
248 | setMaximumOptimizerIterations (int max) { max_inner_iterations_ = max; }
249 |
250 | ///\return maximum number of iterations at the optimization step
251 | int
252 | getMaximumOptimizerIterations () { return (max_inner_iterations_); }
253 |
254 | protected:
255 |
256 | /** \brief The number of neighbors used for covariances computation.
257 | * default: 20
258 | */
259 | int k_correspondences_;
260 |
261 | /** \brief The epsilon constant for gicp paper; this is NOT the convergence
262 | * tolerance
263 | * default: 0.001
264 | */
265 | double gicp_epsilon_;
266 |
267 | /** The epsilon constant for rotation error. (In GICP the transformation epsilon
268 | * is split in rotation part and translation part).
269 | * default: 2e-3
270 | */
271 | double rotation_epsilon_;
272 |
273 | /** \brief base transformation */
274 | Eigen::Matrix4f base_transformation_;
275 |
276 | /** \brief Temporary pointer to the source dataset. */
277 | const PointCloudSource *tmp_src_;
278 |
279 | /** \brief Temporary pointer to the target dataset. */
280 | const PointCloudTarget *tmp_tgt_;
281 |
282 | /** \brief Temporary pointer to the source dataset indices. */
283 | const std::vector *tmp_idx_src_;
284 |
285 | /** \brief Temporary pointer to the target dataset indices. */
286 | const std::vector *tmp_idx_tgt_;
287 |
288 |
289 | /** \brief Input cloud points covariances. */
290 | MatricesVectorPtr input_covariances_;
291 |
292 | /** \brief Target cloud points covariances. */
293 | MatricesVectorPtr target_covariances_;
294 |
295 | /** \brief Mahalanobis matrices holder. */
296 | std::vector mahalanobis_;
297 |
298 | /** \brief maximum number of optimizations */
299 | int max_inner_iterations_;
300 |
301 | /** \brief compute points covariances matrices according to the K nearest
302 | * neighbors. K is set via setCorrespondenceRandomness() method.
303 | * \param cloud pointer to point cloud
304 | * \param tree KD tree performer for nearest neighbors search
305 | * \param[out] cloud_covariances covariances matrices for each point in the cloud
306 | */
307 | template
308 | void computeCovariances(typename pcl::PointCloud::ConstPtr cloud,
309 | const typename pcl::search::KdTree::ConstPtr tree,
310 | MatricesVector& cloud_covariances);
311 |
312 | /** \return trace of mat1^t . mat2
313 | * \param mat1 matrix of dimension nxm
314 | * \param mat2 matrix of dimension nxp
315 | */
316 | inline double
317 | matricesInnerProd(const Eigen::MatrixXd& mat1, const Eigen::MatrixXd& mat2) const
318 | {
319 | double r = 0.;
320 | size_t n = mat1.rows();
321 | // tr(mat1^t.mat2)
322 | for(size_t i = 0; i < n; i++)
323 | for(size_t j = 0; j < n; j++)
324 | r += mat1 (j, i) * mat2 (i,j);
325 | return r;
326 | }
327 |
328 | /** \brief Rigid transformation computation method with initial guess.
329 | * \param output the transformed input point cloud dataset using the rigid transformation found
330 | * \param guess the initial guess of the transformation to compute
331 | */
332 | void
333 | computeTransformation (PointCloudSource &output, const Eigen::Matrix4f &guess) override;
334 |
335 | /** \brief Search for the closest nearest neighbor of a given point.
336 | * \param query the point to search a nearest neighbour for
337 | * \param index vector of size 1 to store the index of the nearest neighbour found
338 | * \param distance vector of size 1 to store the distance to nearest neighbour found
339 | */
340 | inline bool
341 | searchForNeighbors (const PointSource &query, std::vector& index, std::vector& distance) const
342 | {
343 | int k = tree_->nearestKSearch (query, 1, index, distance);
344 | if (k == 0)
345 | return (false);
346 | return (true);
347 | }
348 |
349 | /// \brief compute transformation matrix from transformation matrix
350 | void applyState(Eigen::Matrix4f &t, const Vector6d& x) const;
351 |
352 | /// \brief optimization functor structure
353 | struct OptimizationFunctorWithIndices : public BFGSDummyFunctor
354 | {
355 | OptimizationFunctorWithIndices (const GeneralizedIterativeClosestPoint* gicp)
356 | : BFGSDummyFunctor (), gicp_(gicp) {}
357 | double operator() (const Vector6d& x) override;
358 | void df(const Vector6d &x, Vector6d &df) override;
359 | void fdf(const Vector6d &x, double &f, Vector6d &df) override;
360 |
361 | const GeneralizedIterativeClosestPoint *gicp_;
362 | };
363 |
364 | std::function &cloud_src,
365 | const std::vector &src_indices,
366 | const pcl::PointCloud &cloud_tgt,
367 | const std::vector &tgt_indices,
368 | Eigen::Matrix4f &transformation_matrix)> rigid_transformation_estimation_;
369 | };
370 | }
371 |
372 | #endif //#ifndef PCL_GICP_H_
373 |
--------------------------------------------------------------------------------
/Thirdparty/ndt_omp_ros2/include/pclomp/voxel_grid_covariance_omp.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Software License Agreement (BSD License)
3 | *
4 | * Point Cloud Library (PCL) - www.pointclouds.org
5 | * Copyright (c) 2010-2011, Willow Garage, Inc.
6 | *
7 | * All rights reserved.
8 | *
9 | * Redistribution and use in source and binary forms, with or without
10 | * modification, are permitted provided that the following conditions
11 | * are met:
12 | *
13 | * * Redistributions of source code must retain the above copyright
14 | * notice, this list of conditions and the following disclaimer.
15 | * * Redistributions in binary form must reproduce the above
16 | * copyright notice, this list of conditions and the following
17 | * disclaimer in the documentation and/or other materials provided
18 | * with the distribution.
19 | * * Neither the name of the copyright holder(s) nor the names of its
20 | * contributors may be used to endorse or promote products derived
21 | * from this software without specific prior written permission.
22 | *
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | * POSSIBILITY OF SUCH DAMAGE.
35 | *
36 | */
37 |
38 | #ifndef PCL_VOXEL_GRID_COVARIANCE_OMP_H_
39 | #define PCL_VOXEL_GRID_COVARIANCE_OMP_H_
40 |
41 | #include
42 | #include
43 | #include