Coder Social home page Coder Social logo

bonxai's Introduction

Bonxai

Bonxai is a library that implements a compact hierarchical data structure that can store and manipulate volumetric data, discretized on a three-dimensional grid (AKA, a "Voxel Grid").

Bonxai data structure is:

  • Sparse: it uses only a fraction of the memory that a dense 3D voxel grid would use.
  • Unbounded: you don't need to define the boundary of the 3D space (*).

(*) The dimension of the 3D space is virtually "infinite": since 32-bits indices are used, given a voxel size of 1 cm, the maximum range of the X, Y and Z coordinates would be about 40.000 Km. As a reference the diameter of planet Earth is 12.000 Km.

If you are familiar with Octomap and Octrees, you know that those data structures are also sparse and unbounded.

On the other hand, Bonxai is much faster and, in some cases, even more memory-efficient than an Octree.

This work is strongly influenced by OpenVDB and it can be considered an implementation of the original paper, with a couple of non-trivial changes:

K. Museth, 
“VDB: High-Resolution Sparse Volumes with Dynamic Topology”,
ACM Transactions on Graphics 32(3), 2013. Presented at SIGGRAPH 2013.

You can read the previous paper here.

There is also some overlap with this other paper, but their implementation is much** simpler, even if conceptually similar:

 Eurico Pedrosa, Artur Pereira, Nuno Lau 
 "A Sparse-Dense Approach for Efficient Grid Mapping"
 2018 IEEE International Conference on Autonomous Robot Systems and Competitions (ICARSC)

Bonxai is currently under development and I am building this mostly for fun and for educational purposes. Don't expect any API stability for the time being.

Benchmark (preliminary)

Take these numbers with a grain of salt, since they are preliminary and the benchmark is strongly influenced by the way the data is stored. Anyway, they gave you a fair idea of what you may expect, in terms of performance.

-------------------------------------------
Benchmark                     Time      
-------------------------------------------
Bonxai_Create              1165 us  
Octomap_Create            25522 us  

Bonxai_Update               851 us  
Octomap_Update             3824 us  

Bonxai_IterateAllCells      124 us
Octomap_IterateAllCells     698 us
  • Create refers to creating a new VoxelGrid from scratch
  • Update means modifying the value of an already allocated VoxelGrid.
  • IterateAllCells will get the value and the coordinates of all the existing cells.

How to use it

The core of Bonxai is a header-only library that you can simply copy into your project and include like this:

#include "bonxai/bonxai.hpp"

To create a VoxelGrid, where each cell contains an integer value and has size 0.05.

double voxel_resolution = 0.05;
Bonxai::VoxelGrid<int> grid( voxel_resolution );

Nothing prevents you from having more complex cell values, for instance:

Bonxai::VoxelGrid<Eigen::Vector4d> vector_grid( voxel_resolution );
// or
struct Foo {
 int a;
 double b;
};
Bonxai::VoxelGrid<Foo> foo_grid( voxel_resolution );

To insert values into a cell with coordinates x, y and z, use a VoxelGrid::Accessor object. In the next code sample, we will create a dense cube of cells with value 42:

// Each cell will contain a `float` and it will have size 0.05
double voxel_resolution = 0.05;
Bonxai::VoxelGrid<float> grid( voxel_resolution );

// Create this accessor once, and reuse it as much as possible.
auto accessor = grid.createAccessor();

// Create cells with value 42.0 in a 1x1x1 cube.
// Given voxel_resolution = 0.05, this will be equivalent
// to 20x20x20 cells in the grid.

for( double x = 0; x < 1.0; x += voxel_resolution ) {
  for( double y = 0; y < 1.0; y += voxel_resolution ) {
    for( double z = 0; z < 1.0; z += voxel_resolution ) {
      // discretize the position {x,y,z}
      Bonxai::CoordT coord = grid.posToCoord(x, y, z);
      accessor.setValue( coord, 42.0 );
    }
  }
}

// You can read (or update) the value of a cell as shown below.
// If the cell doesn't exist, `value_ptr` will be `nullptr`, 

Bonxai::CoordT coord = grid.posToCoord(x, y, z);
float* value_ptr = accessor.value( coord );

Note about multi-threading

Bonxai::VoxelGrid is not thread-safe, for write operations.

If you want to access the grid in read-only mode, you can use multi-threading, but each thread should have its own accessor.

Roadmap

  • serialization to/from file.
  • full implementation of the Octomap algorithm (ray casting + probability map).
  • integration with ROS 2.
  • RViz2 visualization messages.
  • integration with FCL for collision detection (?)

Frequently Asked Question

What is the point of reimplementing OpenVDB?

  • The number one reason is to have fun and to learn something new :)
  • I want this library to be small and easy to integrate into larger projects. The core data structure is less than 1000 lines of code.
  • It is not an "exact" rewrite, I modified a few important aspects of the algorithm to make it slightly faster, at least for my specific use cases.

How much memory does it use, compared with Octomap?

It is... complicated.

If you need to store very sparse point clouds, you should expect Bonxai to use more memory (20-40% more). If the point cloud is relatively dense, Bonxai might use less memory than Octomap (less than half).

bonxai's People

Contributors

davidcapek avatar eupedrosa avatar facontidavide avatar griswaldbrooks avatar ivrolan avatar jjd9 avatar jlblancoc avatar mikeferguson avatar sea-bass avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bonxai's Issues

vdbfusion

Hello there! Great work, I wonder how this might speed up the vdbfusion pipeline. Any interest in giving this a try?

To try the current implementation just pip install vdbfusion and play around, there is also a thin simple C++ API in case you don't want to use Python.

Best!

Memory usage handling of huge maps

Thank you for creating this wonderful project! I am just wondering how would your implementation handle building large maps for example a 300 m by 300 m by 5 m voxel grid. Assuming one grid only holds a float value.

Issue with the scale of Bonxai::ProbabilisticMap

First of all, thank you so much for your awesome work!

I've been trying to get the free space from a point cloud scan making use of the Bonxai::ProbabilisticMap class just like you would do it using Octomap, but i found out that the parameter that in the example code you call resolution, does not change the resolution of the map but its scale instead.

My code is looks like this:

pcl::PointCloud<pcl::PointXYZ>::Ptr raw_depth_cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::fromROSMsg(*msg, *raw_depth_cloud);

bonxai_ = std::make_unique<Bonxai::ProbabilisticMap>(resolution_);
Bonxai::ProbabilisticMap::Options opt = {
    bonxai_->logods(0.4), bonxai_->logods(0.7), bonxai_->logods(0.12), bonxai_->logods(0.97)};
bonxai_->setOptions(opt);

const pcl::PointXYZ sensor_og(0, 0, 0);
bonxai_->insertPointCloud(raw_depth_cloud->points, sensor_og, 20.0);

std::vector<Bonxai::CoordT> free_coords;
bonxai_->getFreeVoxels(free_coords);

std::vector<Bonxai::CoordT> occ_coords;
bonxai_->getOccupiedVoxels(occ_coords);

pcl::PointCloud<pcl::PointXYZ>::Ptr free_points(new pcl::PointCloud<pcl::PointXYZ>);

for (const auto& voxel : occ_coords) {
    const pcl::PointXYZ pt(voxel.x, voxel.y, voxel.z);
    free_points->points.push_back(pt);
}

free_points->width  = free_points->points.size();
free_points->height = 1;

sensor_msgs::PointCloud2 free_msg;
pcl::toROSMsg(*free_points, free_msg);

free_msg.header.frame_id = msg->header.frame_id;
free_msg.header.stamp    = ros::Time::now();
freespace_cloud_pub_.publish(free_msg);

Seems pretty straightforward. But with a resolution of 0.1 i get:
res_01

And using a resolution of 1.0:
res_1

Am I doing something wrong?

Thanks in advance!

ROS2 max_range parameter not affecting map

Hi,

I am currently using different parameters for the ROS2 node of the bonxai implementation. However, changing the sensor_model/max_range currently does not affect the map build process.

As described in the bonxai_ros section (https://github.com/facontidavide/Bonxai/tree/main/bonxai_ros) the max_range should be deactivated with -1.0, which is indeed the case. However, changing this value to something like 5.0 or 10.0 does not change the map building process.

Is this a bug or am I missing something?

Thanks in advance!

This is my current config:

bonxai_server_node:
  ros__parameters:
    resolution: 0.05
    frame_id: "world"
    base_frame_id: "livox_frame"

    occupancy_min_z: -10.0
    occupancy_max_z: 20.0

    sensor_model:
      max_range: 5.0
      hit: 0.85
      miss: 0.3
      min: 0.12
      max: 0.97

    latch: false

OpenVDB benchmark

I found that the OpenVDB benchmark for iterating over all cells was using a voxel-On iterator, which is user-friendly, safe, and fast, but not super fast. After running the benchmarks:

------------------------------------------------------------------
Benchmark                        Time             CPU   Iterations
------------------------------------------------------------------
Treexy_Create              2363567 ns      2347860 ns          293
Treexy_Update              2474811 ns      2472939 ns          279
Treexy_IterateAllCells       62399 ns        62399 ns        11633 ###
Octomap_Create            40142650 ns     40142345 ns           17
Octomap_Update            14739847 ns     14739871 ns           46
Octomap_IterateAllCells     425256 ns       425099 ns         1667
OpenVDB_Create             2785683 ns      2713275 ns          267
OpenVDB_Update             2024528 ns      2024314 ns          347
OpenVDB_IterateAllCells     202940 ns       202938 ns         3445 ###
Lama_Create               10595711 ns     10595341 ns           67
Lama_Update               10171020 ns     10170439 ns           62
Lama_IterateAllCells       3109528 ns      3109444 ns          218

Reading through this post from the OpenVDB mailing list: https://lists.aswf.io/g/openvdb-dev/topic/32212211?p=,,,20,0,0,0::,,,0,0,0,32212211

I found that there is a class called LeafManager that allows access leaping from leaf to leaf, which also grants the chance of iterating over the voxels below a given leaf (similar to what Bonxai is doing):

#include <openvdb/tree/LeafManager.h>
...
// To make a fair comparison, use Log2DIM values similar to Treexy
using TreeType = openvdb::tree::Tree4<int32_t, 2, 2, 3>::Type;
using GridType = openvdb::Grid<TreeType>;
...

static void OpenVDB_IterateAllCells(benchmark::State& state)
{
...
  openvdb::tree::LeafManager<TreeType> leafManager(grid->tree());

  long count = 0;
  for (auto _ : state)
  {
    auto visitor = [&](const TreeType::LeafNodeType& leaf, size_t /*idx*/) {
      for (auto nodeIter = leaf.beginValueOn(); nodeIter; ++nodeIter)
      {
        count++;
      }
    };
    leafManager.foreach(visitor, false);
  }

I ran the benchmarks with this modification and observed a solid improvement:

------------------------------------------------------------------
Benchmark                        Time             CPU   Iterations
------------------------------------------------------------------
...
Treexy_IterateAllCells       56818 ns        56818 ns        12411
...
OpenVDB_IterateAllCells      84142 ns        84134 ns         8238
...

Furthermore, by switching the tree configuration to openvdb::tree::Tree3<int32_t, 4, 2>::Type;, I was able to get results that almost match those of Bonxai:

------------------------------------------------------------------
Benchmark                        Time             CPU   Iterations
------------------------------------------------------------------
...
Treexy_IterateAllCells       57986 ns        57983 ns        11952
...
OpenVDB_IterateAllCells      64163 ns        64159 ns        10636

And when enabling multi-threaded access:

------------------------------------------------------------------
Benchmark                        Time             CPU   Iterations
------------------------------------------------------------------
...
Treexy_IterateAllCells       57688 ns        57686 ns        12146
...
OpenVDB_IterateAllCells      39098 ns        39097 ns        17763
...

Performance on moving robot

With the robot moving, the map will become larger and larger.

Will bonxai performance be affected? such as ray casting ...

question in querying neighbors

can bonxai query neighbors in a efficient way like flann?
I found both bonxai and nanovdb are much efficient in building the structure. but both of them do not offer a method for querying neighbors like k nearest neighbors or neighbors within a given radius.
are there any method to done it in a efficient way?

Bug causes by value() before setValue()

Thanks for your great work. I'm using your VoxelGrid to replace my own grid so that performance can be improved. However, I got a problem. Here's my pseudo code:
/* pesudor code
auto ptr = accessor.value(coord);
if(ptr){
DoSomething();
}
else{
accessor.setValue(coord, new_value);
}
*/
If ptr is nullptr(coord has not been setValue before), error happens!
"prev_leaf_ptr_ = getLeafGrid(coord, false)" in value() makes prev_leaf_ptr_ nullptr,
then "prev_leaf_ptr_->mask.setOn(index)" in setValue() caused segmentfault.
Is there anything I'm missing?
Thank you.

Not able to use move assignment on VoxelGrid?

TLDR:
Is there a way to use a move assignment operator with VoxelGrid?

Hey there!

First of all I'd like to say this project is incredible, it has spend up the 3d mapping systems of the robotics project I'm working on significantly.

Also I'm kind of new to c++ so please bear with me.

I wasn't able to find a visualisation tool for the project, so I threw together a janky one using openGL, and I ran into a minor issue/question while I was making it. Here's the github repo.

After making the initial visualiser, I wanted to see my serialized file update real time, so I tried adding a function that would deserialise the map file on a regular interval and assign the new deserialized map to the visualizer "global" map so that it could be drawn.

The issue I ran into was with the assignment/move operation.

When I tried compiling this:

void updateGridFromFile(std::string inputFileName){
    std::ifstream inputFile(inputFileName, std::ios::binary);
    if (!inputFile.is_open()) {
        std::cerr << "Error: Unable to open file " << inputFileName << std::endl;
    }

    // Read the header of the file to obtain information about the voxel grid
    char header[256];
    inputFile.getline(header, 256);
    Bonxai::HeaderInfo info = Bonxai::GetHeaderInfo(header);

    // Deserialize the voxel grid from the file
    auto g = Bonxai::Deserialize<float>(inputFile, info);
    inputFile.close();
    grid=std::move(g);
}

I would get the following error message:

/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/src/visualizer.cpp: In function ‘void updateGridFromFile(std::string)’:
/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/src/visualizer.cpp:240:21: error: use of deleted function ‘Bonxai::VoxelGrid<float>& Bonxai::VoxelGrid<float>::operator=(Bonxai::VoxelGrid<float>&&)’
  240 |     grid=std::move(g);
      |                     ^
In file included from /home/eryk/Desktop/Cascade/ros2_ws/src/mapping/src/visualizer.cpp:4:
/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/include/bonxai/bonxai.hpp:267:7: note: ‘Bonxai::VoxelGrid<float>& Bonxai::VoxelGrid<float>::operator=(Bonxai::VoxelGrid<float>&&)’ is implicitly deleted because the default definition would be ill-formed:
  267 | class VoxelGrid
      |       ^~~~~~~~~
/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/include/bonxai/bonxai.hpp:267:7: error: non-static const member ‘const uint32_t Bonxai::VoxelGrid<float>::INNER_BITS’, cannot use default assignment operator
/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/include/bonxai/bonxai.hpp:267:7: error: non-static const member ‘const uint32_t Bonxai::VoxelGrid<float>::LEAF_BITS’, cannot use default assignment operator
/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/include/bonxai/bonxai.hpp:267:7: error: non-static const member ‘const uint32_t Bonxai::VoxelGrid<float>::Log2N’, cannot use default assignment operator
/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/include/bonxai/bonxai.hpp:267:7: error: non-static const member ‘const double Bonxai::VoxelGrid<float>::resolution’, cannot use default assignment operator
/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/include/bonxai/bonxai.hpp:267:7: error: non-static const member ‘const double Bonxai::VoxelGrid<float>::inv_resolution’, cannot use default assignment operator
/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/include/bonxai/bonxai.hpp:267:7: error: non-static const member ‘const uint32_t Bonxai::VoxelGrid<float>::INNER_MASK’, cannot use default assignment operator
/home/eryk/Desktop/Cascade/ros2_ws/src/mapping/include/bonxai/bonxai.hpp:267:7: error: non-static const member ‘const uint32_t Bonxai::VoxelGrid<float>::LEAF_MASK’, cannot use default assignment operator
gmake[2]: *** [CMakeFiles/visualizer.dir/build.make:76: CMakeFiles/visualizer.dir/src/visualizer.cpp.o] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:167: CMakeFiles/visualizer.dir/all] Error 2
gmake: *** [Makefile:146: all] Error 2

So I just removed the const modifier from the listed variables and now its working fine.

So I was mainly wondering if there was a better way to move data from my deserialized grid to my global grid.

Also, is it a bad idea to remove the const modifier like I did? Is there a reason there is no move assignment operator defined for VoxelGrid?

Thanks in advance!!

Trversal

Do you have any code/benchmarks for tree traversal?
I am thinking of collision detection techniques where you need to traverse down 2 tree looking for intersections (narrowphase).
Thanks
Jim

Motivation

Hi,

Can you indicate the motivation of this over OpenVDB if this is more or less a reimplementation of it?

(also, love that logo, did you make that?!)

ROS support?

@facontidavide
Thanks for your great work and contribution for open source ecosystem!
I would like to know any progress on integration with ROS/ROS2.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.