Repository navigation
Representation improvements: unknown space, incremental updates, descriptors, links, compact maps - #35
Merged
Conversation
navmap_core/Iterators.hpp: box (every surface, via the BVH), disc, ring, polygon, distance order and segment walk over the NavCels. Regions around a point grow from the NavCel under it through the mesh adjacency, so they never jump to another floor. Distances are Euclidean (to the centroid) or geodesic (Lazy Theta*: exact on planes, bends around borders). A reusable TraversalScratch avoids allocations. Tests against brute force and a table in navmap_bench. Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
- Each layer can declare the value meaning "not observed" (NaN allowed): NavMap::set_layer_unknown() / is_unknown(). The unknown value, description and unit now travel in NavMapLayer and in the file; from_occupancy_grid declares NO_INFORMATION as the occupancy layer's unknown value. - int16 layers (LayerType::I16, NavMapLayer::I16 / data_i16). - navmap_core/Occupancy.hpp: log-odds occupancy with OctoMap's clamped update, in fixed point over an int16 layer, and a free / occupied / unknown state. - .navmap file format version 2. Version 1 files (and files without header) load through frozen NavMapV1 / NavMapLayerV1 messages, with a deprecation warning, and are rewritten in place when writable. resample.py reads them too. - RViz2: int16 layers (log-odds as occupancy greys), and cells holding a layer's unknown value in translucent grey; U8 layers unchanged. - Fixes: add_layer() ignored the default value of a new layer, and integer conversions clamped negative values to 0. - Example 04_unknown_and_occupancy with its launch file and RViz2 config. Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
The copy constructor and the full-copy assignment copied the layer registry, which holds shared pointers, so a copy shared every layer buffer with the original: a write on either side reached the other (EasyNav filters copy the map in and out of NavState every cycle). LayerRegistry copies are now deep, through LayerViewBase::clone(). Moves are unchanged. Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
- Layers record the NavCels changed since the last clear_changes(): single writes (layer_set, OccupancyLayer, mark_changed) once each, bulk writes (set_data, layer_clear, copies, messages, a new layer) as the whole layer. - navmap_core/DistanceField.hpp: distance to seed cells and open edges, kept up to date from the seeds added and removed. With a finite max_distance it is exact (each seed paints the NavCels it reaches through adjacency within that distance; removing one repaints the seeds within twice the distance, found through a spatial index), and an update gives the same field as a full recomputation. changed() lists the NavCels whose distance changed. - point_segment_distance moved to Geometry.hpp; resize_all handles int16. - navmap_bench: full and incremental distance field timings. Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
navmap_core/Descriptors.hpp: compute_descriptors() adds F32 layers slope (deg), roughness (RMS distance to the plane fitted in a disc, m), step (height span in a small disc, m) and clearance (free height to the next surface of the map above, by an upward ray through the BVH; +inf if none). Discs grow over the mesh adjacency, so floors never mix. They describe the surface only; a planner decides how to weigh them. Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
SurfaceLink (from, to, type, length, bidirectional) records connections such as stairs, elevators or doors between NavCels that do not share an edge: NavMap::add_link / remove_link / clear_links / links() / links_from(), with a lazily rebuilt index. The map only records them; a planner decides whether a robot can take one. Copies and assignments carry them. They travel in the new NavMapLink[] links field of NavMap (file format version 2; version 1 files have none), invalid ones are dropped on load, and RViz2 draws them coloured by type (Draw Links). Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
- NavMap::geometry_id(): the geometry fingerprint, now also recomputed after add_vertex(), add_navcel() and rebuild_geometry_accels() (it went stale after a first query). - NavMap and NavMapLayer messages carry geometry_id; NavMapLayer can carry indices for a sparse update (values for those NavCels only). - navmap_ros::to_layer_update_msg(): sparse from the layer's change log when few NavCels changed, the full layer otherwise. from_msg rejects layer messages for another geometry and malformed sparse updates; RViz2 ignores the former and patches its cached layer with the latter. Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
The version 2 envelope records the codec and both sizes. Files are zlib-compressed by default (SaveOptions::compression, level 1-9); playground maps shrink to 20-41 %. zlib is available on every ROS distro and platform, and in the Python standard library, which resample.py uses to read and write the same format. Corrupt or truncated files are rejected. Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
combine_layer() composes a source layer into a destination with the costmap_2d operators (overwrite, true overwrite, max, max keeping the unknown, min, saturating add). Each layer's declared unknown value is respected, types may differ, the work can be limited to the changed cells, and an optional U8 layer records which source set each cell. The cost values move to navmap_core/Costs.hpp (navmap_ros keeps the names). README: combining layers, and the save/load section now shows the real API. Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
The barycentric tolerance of ray_triangle_intersect was absolute (1e-8), below float rounding for NavCels of a few decimetres or more. A vertical ray through the edge between two NavCels could miss both, and locate_navcel then fell back to the floor above. The tolerance is now relative (1e-5). Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
simplify() merges NavCels by half-edge collapses, cheapest quadric error first. A vertex goes only if all its NavCels share surface and layer values, so every layer keeps its value at every point. Open edges keep their shape, the topology is checked (link condition, no folds), link ends survive, and quality, normal change and edge length are bounded. Layers, surfaces, links and colors are carried over. navmap_simplify simplifies a .navmap file. Playground maps drop to 21-23 % of their NavCels (occupancy grids) and 4-11 % (parking garage), staying within millimetres of the original surface. Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
This was referenced Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #34
One commit per improvement:
.navmapmoves to format version 2; version 1 files load through a migration, with a deprecation warning, and are rewritten in place. RViz2 draws unknown cells translucent grey.DistanceField. Layers record which cells changed.DistanceFieldupdates distances exactly when seeds are added or removed.SurfaceLink: links between NavCels that do not share an edge. They travel in messages and files, and RViz2 draws them.NavMap/NavMapLayermessage carries the geometry id. Updates for another geometry are rejected. Layer updates send only the changed cells when they are few. Also fixes a stale geometry fingerprint..navmap(zlib is available on every ROS distro and in the Python standard library). Playground maps shrink to 20–41 %.combine_layer: the costmap_2d operators (overwrite, true overwrite, max, max keeping unknown, min, saturating add). It respects each layer's unknown value and can record provenance in a U8 layer. Cost values move tonavmap_core/Costs.hpp.locate_navcelcould then return the floor above.simplifyandnavmap_simplify: a mesh simplification that keeps every layer value, the borders and the topology. Measured results:.pgm;Tests cover each feature, including corrupt files, migration of version 1 files and the edge cases of each operator.
Merge order: EasyNavigation/easynav_plugins#122 must be merged first. With the edge fix, one obstacle-filter test (a point exactly on a grid vertex) marks a neighbouring NavCel.