Skip to content

Panav/feat/osm graph loader#6219

Open
Pana1v wants to merge 5 commits into
ros-navigation:mainfrom
Pana1v:panav/feat/osm-graph-loader
Open

Panav/feat/osm graph loader#6219
Pana1v wants to merge 5 commits into
ros-navigation:mainfrom
Pana1v:panav/feat/osm-graph-loader

Conversation

@Pana1v

@Pana1v Pana1v commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Basic Info

Info Please fill out this column
Ticket(s) this addresses #6025
Primary OS tested on Ubuntu
Robotic platform tested on Simulation
Does this PR contain AI generated software? No
Was this PR description generated by AI software? No

Description of contribution in a few bullet points

Added OSM map loader along the lines of GeoJSON loading, added appropriate unit test for the same.

Description of documentation updates required from your changes

  • New Route Server parameters (osm_graph_file_loader.highway_filter,
    from_ll_service, from_ll_service_timeout) should be added to the configuration page on
    docs.nav2.org. (The in-package README.md is updated in this PR.)
  • The new OsmGraphFileLoader should be added to the plugins page on docs.nav2.org.
  • The Route Server docs should note that loading an OSM graph requires navsat_transform_node
    (the FromLLArray service) to be running, since the load blocks on it during lifecycle configure.

Description of how this change was tested

  • Added test/test_osm_graph_file_loader.cpp
  • colcon test passes, including cpplint, uncrustify, and xmllint.
  • The parser/topology stages were validated against a real Overpass extract.

Future work that may be required in bullet points

  • Populate node/edge metadata from OSM tags (speed limits from maxspeed, surface, access),
    enabling the existing SemanticScorer / AdjustSpeedLimit plugins — the planned follow-on PR.
  • Richer edge cost: true polyline length (and/or curvature-preserving vertices) instead of relying on
    straight-line scoring between junctions.
  • De-duplicate parallel edges between the same pair of junctions (currently both are kept).
  • .pbf support; <relation>-based turn restrictions; oneway:conditional and access tags.
  • Optionally preserve/expose the original OSM node ids (currently sequential ids are assigned because
    OSM int64 ids don't fit the 32-bit nodeid)

For Maintainers:

  • Check that any new parameters added are updated in docs.nav2.org
  • Check that any significant change is added to the migration guide
  • Check that any new features OR changes to existing behaviors are reflected in the tuning guide
  • Check that any new functions have Doxygen added
  • Check that any new features have test coverage
  • Check that any new plugins is added to the plugins page
  • If BT Node, Additionally: add to BT's XML index of nodes for groot, BT package's readme table, and BT library lists
  • Should this be backported to current distributions? If so, tag with backport-*.

@Pana1v Pana1v left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing OsmGraphFileLoader (nav2_route)

Build

colcon build --packages-select nav2_route --symlink-install
source install/setup.bash

Offline tests

./build/nav2_route/test/test_osm_graph_file_loader
# expect: [ PASSED ] 23 tests.

colcon test --packages-select nav2_route --ctest-args -R "cpplint|uncrustify|xmllint"
# expect: 0 failures

End-to-end

Datum: lat 40.711, lon -74.007

1. navsat_transform

ros2 run robot_localization navsat_transform_node --ros-args \
  -p use_local_cartesian:=true -p wait_for_datum:=true -p datum:="[40.711,-74.007,0.0]"

2. Feed odom + GPS, set datum

ros2 topic pub -r5 /odometry/filtered nav_msgs/msg/Odometry \
  "{header: {frame_id: odom}, pose: {pose: {orientation: {w: 1.0}}}}" &
ros2 topic pub -r5 /gps/fix sensor_msgs/msg/NavSatFix \
  "{status: {status: 0}, latitude: 40.711, longitude: -74.007}" &
ros2 service call /datum robot_localization/srv/SetDatum \
  "{geo_pose: {position: {latitude: 40.711, longitude: -74.007}, orientation: {w: 1.0}}}"

3. Route server

GRAPH=$(ros2 pkg prefix nav2_route)/share/nav2_route/graphs/sample_graph.osm
cat > /tmp/osm_params.yaml <<EOF
route_server:
  ros__parameters:
    graph_filepath: "$GRAPH"
    graph_file_loader: "OsmGraphFileLoader"
    OsmGraphFileLoader: {plugin: "nav2_route::OsmGraphFileLoader"}
    route_frame: "map"
    edge_cost_functions: ["DistanceScorer"]
    DistanceScorer: {plugin: "nav2_route::DistanceScorer"}
    operations: ["AdjustSpeedLimit"]   # non-empty required
    AdjustSpeedLimit: {plugin: "nav2_route::AdjustSpeedLimit"}
EOF
ros2 run nav2_route route_server --ros-args --params-file /tmp/osm_params.yaml

4. Activate

ros2 lifecycle set /route_server configure
ros2 lifecycle set /route_server activate
# expect log: Loaded OSM graph: 5 vertices from 4 sections

Visualize

ros2 topic echo --once /route_graph   # 13 markers

RViz2: MarkerArray on /route_graph, Fixed Frame map, Durability Transient Local.

@Pana1v

Pana1v commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

@SteveMacenski Can you take a look :D

@SteveMacenski

Copy link
Copy Markdown
Member

Please fix the compilation, precommit, and DCO issues before I take a detailed look :-)

Comment thread nav2_route/README.md Outdated

| Parameter | Type | Default | Description |
| --------- | ---- | ------- | ----------- |
| `highway_filter` | string[] | `[]` (keep all `highway=*` ways) | Allowlist of OSM `highway` values to keep, e.g. `["track", "path", "service", "unclassified", "residential"]` |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not all in general?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I jumped the gun here given what was being discussed on the issue such as filtering routes.
In hindsight, I should revert this change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed this filtering every tag is retained now, the type of 'way' is also retained to be used later.

Comment thread nav2_route/README.md

A small example is provided in `graphs/sample_graph.osm`.

Note: OSM node ids are 64-bit and cannot be the Route Server's 32-bit node id, so unlike the GeoJSON

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a problem. The solution is either to truncate the OSM IDs or to increase the node id to 64 bits.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be a nice idea to encode it, something like

std::string osm_id_str = std::to_string(osm_id); graph[idx].metadata.setValue("osm_id", osm_id_str);

Changing nav2_msgs woudn't be desirable, truncation would be a one way process and could still cause corruption.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should increase node_ids to 64 bits

Comment thread nav2_route/README.md
Coordinates are converted from WGS84 latitude/longitude into the map frame using
robot_localization's `FromLLArray` service, so the graph shares a single datum with the robot's
localization rather than introducing a second one. **This requires `navsat_transform_node` to be
running when the graph is loaded** — the graph is loaded during the Route Server's lifecycle

@SteveMacenski SteveMacenski Jun 29, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The - tells me this is AI generated. Can you please write documentation in your own words so its more clear to users?

The loader resolves this implicit topology by treating shared nodes (and way endpoints) as junctions, splitting each way at its junctions, and emitting one directed edge per inter-junction section. The intermediate shape nodes are not turned into graph vertices, keeping the graph sparse

This is particularly obtuse. Is this is a standard way to do this in OSM? Seems like defining connected nodes shouldn't be that hard and seems like you may have invented some way of shoehorning the data format into what we need (which makes me think perhaps there's a better way or I'm misunderstanding)

Pana1v added 4 commits July 13, 2026 17:44
Signed-off-by: Pana1v <praajarpit@gmail.com>
…e conversion

Signed-off-by: Pana1v <praajarpit@gmail.com>
- export robot_localization (the installed header exposes the FromLLArray
  service type) and move the geographic_msgs include into the .cpp (IWYU)
- parse OSM node/nd attributes with Query*Attribute, skipping malformed
  elements instead of silently storing id/coords 0
- warn (and document) when a self-loop spur section is dropped
- make the FromLLArray service name and timeout parameters, and check the
  wait_for_service result before invoking
- README: note OSM ids are not preserved and that loading blocks on
  navsat_transform during the lifecycle configure transition
- tests: oneway value table, highway-filter allowlist, <2-ref ways, missing
  coordinates, self-loop edge skip, edge destinations + ids, parallel edges

Signed-off-by: Pana1v <praajarpit@gmail.com>
Signed-off-by: Pana1v <praajarpit@gmail.com>
@Pana1v
Pana1v force-pushed the panav/feat/osm-graph-loader branch from 85bb3f3 to cc0c381 Compare July 13, 2026 17:46
@Pana1v

Pana1v commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

While I am adding a sample osm which is a simplified implementation, should we also include documentation for someone to import an OSM map and edit it using a tool such as JOSM ?

@SteveMacenski What are your thoughts..

@SteveMacenski

Copy link
Copy Markdown
Member

should we also include documentation for someone to import an OSM map and edit it using a tool such as JOSM

That would be a good follow up PR! First, I think we should focus on getting this to build so we can review and chat more 😉

A .osm fed to nav2_route is a purpose-built route graph, so the loader
should load every way faithfully rather than second-guess which ways are
"real" via highway=*. Which edges are preferable is a cost decision that
belongs to the edge scoring plugins, which can read each way's tags.

- Drop the highway_filter parameter and the implicit highway=* requirement
- Delete shouldKeepWay(); every parsed way is now kept
- Remove the now-dead unordered_set include and member, and the two filter
  unit tests; add a test asserting an untagged way is kept
- Clarify README topology and keep-all-ways prose; drop highway_filter row

Signed-off-by: Pana1v <praajarpit@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants