Skip to content

Odometry Library

A templated C++-only library without any ROS2 dependencies. It contains all core functionalities of the localization and may be used for low-level developments. It also defines the built executables that may later be used in the ROS wrappers. Read the documentation to understand the software architecture.

Design

  • the folder base defines a base class and base types which all other modules are built on:
  • the base class holds a config and debug object (custom structs) which specify the values necessary for configuration of the module and the values that are to be logged for debugging
  • the base types holds the definition of the structs for each class (config and debug) and further specify custom, templated variables for interfaces between the modules (diagnostic status, point type)
  • the base types also hold the config with the template arguments for the construction of the single modules/pipelines/nodes
  • the folder modules implements the singles modules necessary for the whole odometry node
  • each module consists of a base module that specifies the config and debug variables of it and the interfaces of the modules with other modules (inherited of odometry_base)
  • implementations of specific modules (e.g. ICP) inherit from its base class (e.g. RegistationHandler) and implement overrides for the virtual interface functions of the module base class
  • each module is a header-only library
  • for each specific implementation, there is an exemplary executable in the folder example/module.cpp that can be used to see how to use it
  • for each specific implementation, there are unit tests to test correct functionality, leveraging GoogleTest
  • if you add a new implementation for a module, make sure to also add unit tests and and example
  • the folder pipeline combines all modules to a full odometry pipeline as a shared library and holds the single modules as member variables. It also holds an example and several unit tests

Develop

  • the library is a native C++ library without any ROS2 deps.
  • currently, it depends on the following libraries:
  • Eigen3 (sudo apt install libeigen3-dev)
  • Sophus
  • robinmap (MapHandler)
  • tbb (CPU Multithreading)
  • robinmap, tbb and Sophus are automatically fetched via CMake with a specific tag
  • generate the build files using the following commands:
cmake -S path/to/odometry_cpp -B path/to/build_folder -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release
  • then build the library:
cmake --build path/to/build_folder
  • execute the example executables to debug (exemplary):
path/to/build_folder/modules/registration_handler/icp_example
  • run the unit tests before creating merge requests:
ctest --test-dir path/to/build_folder
  • add the flag --verbose to see additional information if a test fails

CUDA

  • building the library with CUDA support involves the following additional dependencies:
  • CUDA (installation of nvcc compiler and cuda toolkit)
  • cuCollections (automatically fetched via Cmake with a specific tag)
  • the library support CUDA implementations for the following modules right now:
  • PolynomUndistortion (DistortionHandler)
  • LidarPreprocessing (PreprocessingHandler)
  • VoxelHashMap (MapHandler)
  • ICP, GICP (RegistrationHandler)
  • voxel_downsampling (VoxelTools)
  • to build the library with CUDA-capability, check the architecture of your target device in the NVCC GPU Feature List.
  • set the architecture of your GPU as a build-argument:
cmake -S path/to/odometry_cpp -B path/to/build_folder -DBUILD_TESTING=ON -DBUILD_CUDA=ON -DCUDA_ARCHITECTURES=<num>

Testing with Example Data

Example

See this example on how to use the test data for registration testing. To register a single point cloud to the map, call:

./icp_example <path/to/map.bin> <path/to/frame.bin> <path/to/init_guess.txt>

If using the test data, select one frame and the odometry file with the closest timestamp (from the timestamp). This will give you output on the iterations and the registration time. However, feel free to customize it.

Extensibility

Adding an Executable

To add an executable to the library, that can be used in the Examples or within the ROS2 node, follow these steps:

Adding a Module

To add a new implementation for one of the modules, follow these steps:

  • in the respective module, open its corresponding <module>_handler_base.hpp-file
  • identify the module's config- and debug-signals
  • following existing implementations, create a new .hpp-file with your implementation that inherits from the base-handler and overrides its virtual interface functions
  • add a unit test under test/ covering at least construction from both constructors (param manager & logger, and config & debug) and register it in the module's CMakeLists.txt by adding its name to the TESTS list
  • add an example under example/ and register it in the EXAMPLES list of the same CMakeLists.txt (both lists are iterated over, so no further CMake changes are required)

Visualization

For visualization, the cmake-argument VISUALIZATION is provided that installs rerun. It is fetched from github via cmake, but will take some time to build so it is highly recommended to use some additional cores when building the library with visualization:

cmake -S path/to/odometry_cpp -B path/to/build_folder -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release -DVISUALIZATION=ON
cmake --build path/to/build_folder -j <num_threads>

Rerun is based on a streamer-viewer concept: In your cpp-code, you can then stream data via TCP, when using Rerun. The streamed data can then be visualized in the viewer, which needs to installed via

pip3 install rerun-sdk

If you prefer a different installation method, refer to the documentation.

To use rerun within your code, the compiler definition USE_VISUALIZATION is provided (if DVISUALIZATION=ON). It provides the option to compile code in your example file, depending on the VISUALIZATION and may be used with the following makro:

#ifdef USE_VISUALIZATION
#include <rerun.hpp>
<your code>
#endif

For an example on the basic visualization of a point cloud, refer to the voxel_hash_map_example