# Spatio-Temporal Voxel Layer: a 3D costmap with expiry

> The Spatio-Temporal Voxel Layer is a drop-in replacement for the ROS voxel_grid layer that stores the environment in an OpenVDB sparse grid and lets each voxel expire over time, so a robot can plan against a world where obstacles disappear. The README is unusually specific about when not to use it, on planar lidars and in the global costmap.

**SteveMacenski/spatio_temporal_voxel_layer** — A new voxel layer leveraging modern 3D graphics tools to modernize navigation environmental representations

- Repository: https://github.com/SteveMacenski/spatio_temporal_voxel_layer
- Website: http://wiki.ros.org/spatio_temporal_voxel_layer
- Stars: 871 · Forks: 232
- Language: C++
- License: LGPL-2.1
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/stevemacenski-spatio-temporal-voxel-layer

## A drop-in replacement for the voxel_grid layer

The Spatio-Temporal Voxel Layer is described as a drop-in replacement for the voxel_grid voxel representation of the environment, a package that does a number of things to improve on the voxel grid package and extend the capabilities offered to users, under an LGPL v2.1 licence.

The ROS context matters for understanding the scope. The package wraps a voxel representation with ROS tools and interfaces to the navigation stack, so it drops into standard ROS configurations without rewriting the planner around it. The README also says it is certainly possible to utilize the package without ROS and Navigation, and invites other competing methodologies to develop there and create interfaces, which is an invitation to build alternative front ends on the same storage layer.

It is developed and maintained by Steven Macenski at Simbe Robotics, and the project is documented on the ROS wiki at wiki.ros.org/spatio_temporal_voxel_layer. The default branch is ros2, and the release history shows 2.6.1 on 2025-11-20, 2.6.2 on 2026-01-06 and 2.7.0 on 2026-08-05, with a last push on 2026-10-15.

The name splits into the two ideas the package is built on, and the README devotes a section to each. Spatio is how the environment is represented. Temporal is how that representation forgets.

## OpenVDB does the storage, and the film industry built it

The package sits on top of OpenVDB, an open-source C++ library built by Dreamworks, described in the README as comprising a novel hierarchical data structure and a suite of tools for the efficient storage and manipulation of sparse volumetric data discretized on three-dimensional grids. It is developed and maintained by DreamWorks Animation for use in volumetric applications typically encountered in feature film production.

That provenance is the point of the choice. A sparse volumetric data structure with a hierarchical representation is exactly what a feature film pipeline needs for effects volumes, and the people who built it solved the hard part of storing sparse data at scale. A robotics costmap is a smaller version of the same problem, and using a structure that has been stressed by rendering farms is a better bet than rolling a hash grid for the hundredth time.

The practical effect is stated plainly: leveraging OpenVDB, the project can efficiently maintain a 3 dimensional voxel-representative world space. The repository layout shows how the dependency is handled, with an openvdb_vendor/ directory alongside the package directory itself, so the library is built and shipped inside the repository rather than being a system package you have to arrange.

The other half of the design is that ROS is an accessory. The core is a C++ library with a storage model; the navigation stack integration is a wrapper. That is why the author can say the package works without ROS, and it is the reason to look at this repository if your platform is not ROS but your problem is the same.

## The performance claim, and the conditions attached to it

The README gives numbers, and it gives them with their conditions, which is more useful than most robotics README files.

The first claim is about hardware load. Sample videos are shown of a robot using 7 depth cameras with less than 50% of a core, and another robot using a VLP-16. The second is more specific: in experimental trials with 6 7hz dense stereo RGBD cameras, the major move_base process ran at 20-50% nominal, from 80-110% on a 5th gen i7 CPU, in the global costmap updating using the existing voxel_layer.

Read that carefully. The comparison is between the new layer and the existing voxel_grid layer doing the same global costmap update, on a 5th generation i7, with six dense stereo cameras at seven hertz each. It is a project maintainer's own trial, not an independent benchmark, and the numbers come from an experimental setup rather than from a reproducible suite, so treat them as an indication of the order of magnitude of the gain rather than as a result to plan around.

What is interesting is why the gain exists. A voxel grid that stores a static snapshot of the environment has to keep every cell it has ever seen, or clear it wholesale, and with dense range data the sensor sees far more of the world than the robot needs to plan around. The temporal mechanism below is what makes the map cheap: cells expire, so the map only holds what is currently relevant.

The deployment environments the author lists are also a signal about the use case: retail, warehouses, factories, libraries, hospitals, hospitality, RoboCup@Home, and oil and gas. These are indoor human environments with clutter and people, not a controlled test course.

## Spatio: a configurable grid, and a filter you can turn on

The Spatio half is the representation of the environment in a configurable voxel_size voxel grid, stored and searched by OpenVDB. The resolution is a parameter, which matters because the whole cost of the layer is proportional to the number of cells a scene occupies.

There is a second, optional piece in this half, and it is the more immediately useful of the two. Buffered measurement readings have the option to run an approximate voxel grid filter, parameterizable at runtime in the configuration yamls. The author describes it as incredibly useful to reduce spikes in move_base cpu due to dense measurement readings when getting close to objects, that is, when there are more points. The cost is that it increases the overhead very slightly, 1-2% for nominal operations, and his advice is explicit: it is a trade off, and he recommends using it.

The spikes he describes are the real problem with dense sensing. A camera close to a wall returns far more points than a camera in open space, and a naive layer pays for that in one long processing step, which shows up as a frame-rate drop exactly when the robot is doing something difficult.

The scale example gives a feel for the storage budget. A 60,000 square foot retail store is described as trivial for the layer to maintain and render, accounting for 710,765 voxels at a 0.05m resolution, with a size in memory of a mere 6.45MB. Half a centimetre resolution on a shop floor, and the whole thing fits in a rounding error of memory. That number is the clearest argument for the OpenVDB choice, because a dense array at 0.05 metres over that area would be a different order of magnitude.

## Temporal: voxels that expire, and frusta that hurry them along

The Temporal half is the part the author calls the novel concept, and it is the reason to prefer this package over a plain voxel grid.

The mechanism is voxel_decay. There are configurable functions that expire voxels and their occupation over time, and the infrastructure was created to store times in each voxel after which the voxel will disappear from the map. So a cell is not a boolean, it is a cell with an expiry.

On its own, that is a timer. The interesting part is the combination with the sensor frustums. Voxels are checked for inclusion in the current measurement frustums, which accelerates the decay of those voxels that do not have measurements but should if they are still in the scene and remain marked. The README is explicit that this is done rather than simply clearing them naively or via costly raytracing. The time it takes to clear depends on the configured functions and acceleration factors.

The geometry is modelled per sensor type. Voxel acceleration uses the given FOV to compute the frustum geometry, depth cameras such as the Intel Realsense are modeled with traditional 6-planed cubical frustums, and 3D lidars such as the Velodyne VLP 16 are modeled with their hourglass-shaped FOV. Although many 3D lidars have 360 degree horizontal FOV, it is possible to use a narrower angle for the clearing frustum by setting the hFOV parameter accordingly, which is a useful lever, because a clearing frustum that is too wide will keep stale cells alive.

The decay models are tunable, with the provided models able to do the accelerated frustum behaviour, or persist through linear or exponential equations. The second of the two demonstration videos shows the accelerated frustum with tuned decay times and acceleration factors in navigation mode.

One extension is described as future work rather than as shipped behaviour: querying a static map and determining which connected components belong to the map, which are not in the map, and which are moving, with configurable models to control how long each class persists and whether these things are reported to the user. So the classification of static against moving is on the roadmap, not in the release.

## The local costmap, and the case where the author says not to use this

Two sections of the README are about how to configure the layer, and one of them contains the most useful sentence in the document for anyone deciding whether to adopt it.

The local costmap uses all of the information coming in from the robot before the decay time. Rather than having a defined, discrete spatial barrier for the local planner to operate in, it relies on the user configuration of the layer to have a short decay time of voxels, 1-30 seconds, so that you only plan in relevant space. The author describes this as a conscious design requirement, on the grounds that the local planner should often operate with more information than at other times when the speed is greater or slower. In his phrasing, this natively implements dynamic costmap scaling for speed.

The configuration advice follows: it is the user's responsibility to choose a decay time that makes sense for their local planner, and 5-15 seconds is what he has found to be nominally good for most open-sourced local planner plugins.

And then the boundary. He does not recommend using this for planar lidars, because 2D raytracing for professional grade lidars is sufficiently efficient and effective. That is a clear statement of the wrong-tool case, from the author of the tool, and it saves anyone the experiment.

The global costmap follows the same logic with a different answer. The amount of information you want to store there depends on your use case, and it is certainly possible to not decay the voxels in the global map at all, which is the right setting for the static parts of a building and wrong for the parts of it that move.

## The paper, the talk, and what the repository actually ships

The work has a formal citation, which is a good sign for a package that has been in production for years rather than being a weekend experiment:

```
@article{doi:10.1177/1729881420910530,
    author = {Steve Macenski and David Tsai and Max Feinberg},
    title ={Spatio-temporal voxel layer: A view on robot perception for the dynamic world},
    journal = {International Journal of Advanced Robotic Systems},
    volume = {17},
    number = {2},
    year = {2020},
    doi = {10.1177/1729881420910530},
    URL = {https://doi.org/10.1177/1729881420910530}
}
```

The title is a fair description of the idea, a view on robot perception for the dynamic world. The venue is the International Journal of Advanced Robotic Systems, volume 17, number 2, in 2020. The author also spoke about it at ROSCon 2018, and the talk is linked from the README.

The repository itself is small: a LICENSE, a README, an openvdb_vendor/ directory and the spatio_temporal_voxel_layer/ package directory. The package is C++ under LGPL v2.1, and the fact that OpenVDB is vendored rather than depended on means the build of the sparse grid library is part of your build, which is a cost to plan for and the reason the container images and the CI of ROS packages tend to carry it.

The remaining judgement is about age and stability rather than activity. The releases run to 2.7.0 in August 2026 with a push in October, and the underlying ideas have not changed since 2018. For a navigation component, a design that has been in production for years and is still receiving releases is exactly the profile most teams want, and the 2020 paper gives you a citable description of what the code does that you can put in a design document without inventing it.

## Conclusion

Adopt the Spatio-Temporal Voxel Layer if your robot navigates in a dynamic 3D environment with depth cameras and the voxel_grid layer is the bottleneck, because the OpenVDB storage and the per-voxel decay are what make dense sensing affordable, and the author reports the global costmap update dropping from 80-110 percent of a core to 20-50 percent in his trials. Do not adopt it for a planar lidar robot, because the author says he does not recommend it there and that 2D raytracing is sufficient for professional grade lidars, and think hard about the decay time, since every second is a tradeoff between a local planner that knows about a chair and one that plans through it. Before you deploy it, pick a decay window in the 5 to 15 second range the author calls nominally good, decide whether the global costmap should decay at all, and check the vendored OpenVDB build against your own toolchain.

## FAQ

### What does the Spatio-Temporal Voxel Layer replace?

It is a drop-in replacement for the voxel_grid voxel representation of the environment in ROS, wrapping a sparse OpenVDB grid with ROS tools and interfaces to the navigation stack. The README also says the package can be used without ROS and Navigation, and invites other approaches to build interfaces on it.

### What is voxel_decay and how does it work?

It is the mechanism behind the Temporal half of the package. Configurable functions expire voxels and their occupation over time, with times stored in each voxel, and the expiry is accelerated for voxels that fall inside the current measurement frustums. Depth cameras are modelled with 6-planed cubical frustums and 3D lidars with their hourglass-shaped field of view.

### How long should the voxel decay time be?

The README gives 5-15 seconds as nominally good for most open-sourced local planner plugins, and describes the local costmap as relying on a short decay time of 1-30 seconds so that the planner only plans in relevant space. The author notes it is the user's responsibility to choose a decay time that suits their local planner.

### Is the Spatio-Temporal Voxel Layer suitable for a planar lidar robot?

The author says he does not recommend it for planar lidars, on the grounds that 2D raytracing for professional grade lidars is sufficiently efficient and effective. The package is aimed at dense 3D sensing in dynamic indoor environments.

## Sources

- [License: LGPL-2.1](https://github.com/SteveMacenski/spatio_temporal_voxel_layer/blob/ros2/LICENSE)
- [Project website](http://wiki.ros.org/spatio_temporal_voxel_layer)
- [README](https://github.com/SteveMacenski/spatio_temporal_voxel_layer/blob/ros2/README.md)
- [Releases](https://github.com/SteveMacenski/spatio_temporal_voxel_layer/releases)
- [SteveMacenski/spatio_temporal_voxel_layer on GitHub](https://github.com/SteveMacenski/spatio_temporal_voxel_layer)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/stevemacenski-spatio-temporal-voxel-layer
