Skip to content

Automated Mesh Generation Toolchain & New SDF WorldsMaster#25

Open
thippeswammy wants to merge 28 commits intonaturerobots:mainfrom
thippeswammy:master
Open

Automated Mesh Generation Toolchain & New SDF WorldsMaster#25
thippeswammy wants to merge 28 commits intonaturerobots:mainfrom
thippeswammy:master

Conversation

@thippeswammy
Copy link

Summary

This PR introduces a comprehensive toolchain to automate the generation of navigation meshes from Gazebo SDF world files. It streamlines the workflow for creating complex terrains for mesh_navigation, allowing for direct conversion of SDF geometries (including primitives and includes) into high-quality PLY and H5 map files.

Key Changes

1. New Tool: generate_mesh_env.py

A new Python script located in mesh_navigation_tutorials/launch/ that handles the entire pipeline:

  • SDF Parsing: Recursively parses SDF files, including nested <include> models and geometric primitives (Box, Cylinder, Sphere, Plane, Polyline).
  • Mesh Generation: Converts primitives to high-resolution meshes and extracts existing meshes.
  • Optimization: Includes a --single-layer flag that optimizes meshes for navigation by:
    • Aligning and flattening ground surfaces.
    • Filtering steep faces (navigable surfaces).
    • Iteratively cleaning mesh topology (removing duplicates, degenerate faces).
  • Attribute Injection: Automatically injects vertex/face attributes (roughness, quality) into the final .h5 files using lvr2 tools if available.
  • Validation: Includes methods to compare generated meshes against reference files.

2. World Files

  • New SDFs: Added uneven_terrain.sdf and uneven_terrain_big.sdf to test and run the pipeline.

3. Documentation

  • Updated README.md with instructions on how to use the generation script.

How to Test

Once you have cloned the repository, you can directly build and launch the simulation.

1. Build the Workspace

Rebuild the packages to ensure the new maps and models are installed correctly.

colcon build --packages-select mesh_navigation_tutorials_sim mesh_navigation_tutorials --allow-overriding mesh_navigation_tutorials mesh_navigation_tutorials_sim

2. Launch the Simulation

Launch the tutorial to see the result.

ros2 launch mesh_navigation_tutorials mesh_navigation_tutorials_launch.py world_name:=uneven_terrain_big

Note: Initializing the mesh environment in the simulation might take a few seconds. If the robot spawns in the air or the map isn't visible immediately, wait for ~10 seconds for the systems to synchronize and the mesh to load.

Optional: Test Mesh Generation Tool

If you want to regenerate the mesh or test the tool manually:

python3 src/mesh_navigation_tutorials/mesh_navigation_tutorials/launch/generate_mesh_env.py src/mesh_navigation_tutorials/mesh_navigation_tutorials_sim/worlds/uneven_terrain.sdf --single-layer

… path, need to fix the gap, addin all the cmd and files required for running.
…cript

- Added `--single-layer` flag to automatically configure density, flattening, and filtering for high-quality navigation meshes.
- Implemented `clean_mesh_iterative` for robust topological repair (removing duplicates, degenerate faces, and non-manifold edges).
- Added `--exclude` argument to filter out specific models (e.g., ceilings) by name.
- Added `Stopwatch` class for precise timing of generation stages.
- Improved memory management with explicit garbage collection and smart subdivision limits to prevent OOM errors.
- Enhanced `find_lvr2_tool` to search more common workspace paths.
- Exposed new parameters as ROS 2 launch arguments.
…h files, and refactor the mesh generation script for handeling the polyline geometry type.
…, updating related launch configurations and documentation.
- Removed redundant scripts: sdf_to_ply_converter.py, generate_world_mesh.py
- Updated generate_mesh_env.py:
  - Added --single-layer flag support
  - Implemented iterative mesh cleaning
  - Code cleanup and documentation
- Updated SDF worlds (cleaned up models)
- Updated README.md
@amock
Copy link
Collaborator

amock commented Jan 21, 2026

Hi! First of all, thank you for contributing to MeshNav! I have a few questions to better understand your tool. I’m sure you can answer them more quickly than I can by reading the code.

  1. Is a single connected mesh generated for one Gazebo world file (SDF), which may consist of many objects? In other words, does the tool generate new meshes rather than sticking together existing mesh buffers when they are already present in the SDF file? Or is a complete scene generated consisting of many objects/meshes?
  2. Is there a parameter to define the triangle density? If not, would that be possible to add?
  3. Why do you think this tool would be valuable to include in the tutorials? Who is it intended to help?

@thippeswammy
Copy link
Author

Hi @amock ,

thank you for taking the time to review this and for the clear questions - happy to clarify.

  1. Mesh generation & connectivity
    Yes, the tool generates a single global mesh for an entire Gazebo world (SDF), even if it consists of many models and objects.
    It iterates through all models, links, and geometries in the SDF, resolves their absolute transforms, and aggregates them into one combined mesh.
  • If a geometry already references an existing mesh, that mesh is loaded.
  • If a geometry is a primitive (box, cylinder, plane, polyline, etc.), a mesh is generated for it.
  • All meshes are concatenated into a single mesh and then optionally processed with vertex welding (via --weld-threshold) to merge nearby vertices.

This allows adjacent surfaces (e.g., ramps and ground) to become connected where possible, instead of remaining as isolated mesh buffers.

  1. Triangle density control
    Yes, triangle density can already be controlled through existing parameters:
  • --max-edge: specifies the maximum allowed edge length and drives mesh subdivision.
  • --target-density: allows defining a target vertex density (internally mapped to max-edge constraints).
  • --primitive-resolution: controls tessellation resolution for generated primitives such as cylinders and spheres.

Internally, the tool uses trimesh.remesh.subdivide_to_size to enforce these constraints and ensure consistent mesh density across the scene.

  1. Value for inclusion in the tutorials
    The tool is intended for users who want to run mesh_navigation in Gazebo-based simulations but currently face friction when converting SDF worlds into valid navigation maps.

Without such a tool, users typically need to rely on external 3D engines such as Blender or Unity, which requires them to:

  • Model the geometry in Blender/Unity
  • Export a navigation mesh for mesh_navigation
  • Separately build the corresponding Gazebo SDF world
  • Manually ensure that the exported mesh and the SDF remain perfectly aligned

This workflow is time-consuming and error-prone, especially when the simulation environment changes frequently.

This tool automates that synchronization by generating navigable PLY/HDF5 maps directly from the SDF world itself, ensuring that the simulation geometry and navigation map always stay consistent.

Its value lies in:

  • Bridging Gazebo SDF worlds directly to MeshNav-compatible PLY/HDF5 maps
  • Eliminating manual mesh extraction, cleanup, and alignment steps
  • Enabling rapid iteration on complex simulation environments (ramps, uneven terrain, multi-object scenes)

In short, it lowers the barrier to entry for new users and provides a reproducible, end-to-end workflow for generating navigable meshes directly from Gazebo worlds.

Happy to clarify or adapt the tool further based on feedback.

@amock
Copy link
Collaborator

amock commented Jan 21, 2026

Alright, thanks for the clarification. I checked the functionality, but a few things are still a bit confusing to me.

Parking Garage Missing

It looks like the parking_garage world is no longer available, which makes the README.md invalid. It is unclear to me why this world is important for the feature you want to have merged.

Reproduce:

Follow the instructions in naturerobots/mesh_navigation_tutorials
Run: ros2 launch mesh_navigation_tutorials mesh_navigation_tutorials_launch.py world_name:=parking_garage
Replace the repository with your version and check whether the result is the same (it is not, at least for me)

Gazebo GUI Dark

The worlds that are already included seem to be rendered differently in the Gazebo GUI. Why is this relevant for your changes? More generally, why is it necessary to modify files that already exist at all?

How to Execute Your Script

I wanted to run your script to generate some meshes from sdf files I have, so I ran:

python3 src/mesh_navigation_tutorials/mesh_navigation_tutorials/launch/generate_mesh_env.py src/mesh_navigation_tutorials/mesh_navigation_tutorials_sim/worlds/uneven_terrain.sdf --single-layer

This caused some errors. First, I had to manually install some Python dependencies. After that, I ran into an error that is likely caused by a different trimesh version (mine is 4.11.1)

amock@amock-dell:~/meshnav_ws$ python3 src/mesh_navigation_tutorials/mesh_navigation_tutorials/launch/generate_mesh_env.py src/mesh_navigation_tutorials/mesh_navigation_tutorials_sim/worlds/uneven_terrain.sdf --single-layer
Auto-derived world_name: uneven_terrain
  Input SDF: /home/amock/meshnav_ws/src/mesh_navigation_tutorials/mesh_navigation_tutorials_sim/worlds/uneven_terrain.sdf
  Maps output:   /home/amock/meshnav_ws/src/mesh_navigation_tutorials/mesh_navigation_tutorials/maps
  Models output: /home/amock/meshnav_ws/src/mesh_navigation_tutorials/mesh_navigation_tutorials_sim/models/uneven_terrain

=== Stage 1: Extraction & Validation ===
[Stage 1: Extraction] START
Found 1 model(s) in SDF.
  Processing Model: uneven_terrain_complete
/home/amock/meshnav_ws/src/mesh_navigation_tutorials/mesh_navigation_tutorials/launch/generate_mesh_env.py:349: DeprecationWarning: Testing an element's truth value will raise an exception in future versions.  Use specific 'len(elem)' or 'elem is not None' test instead.
  if geom:
[Stage 1: Extraction] END - Duration: 0.0016s
Traceback (most recent call last):
  File "/home/amock/meshnav_ws/src/mesh_navigation_tutorials/mesh_navigation_tutorials/launch/generate_mesh_env.py", line 1427, in <module>
    main()
  File "/home/amock/meshnav_ws/src/mesh_navigation_tutorials/mesh_navigation_tutorials/launch/generate_mesh_env.py", line 730, in main
    mesh_data_list = extract_meshes_from_sdf(input_sdf, os.path.dirname(input_sdf), resolution=args.primitive_resolution, exclude_list=args.exclude)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/amock/meshnav_ws/src/mesh_navigation_tutorials/mesh_navigation_tutorials/launch/generate_mesh_env.py", line 414, in extract_meshes_from_sdf
    m = create_high_res_primitive(geom, resolution=resolution)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/amock/meshnav_ws/src/mesh_navigation_tutorials/mesh_navigation_tutorials/launch/generate_mesh_env.py", line 241, in create_high_res_primitive
    poly = trimesh.path.polygons.Polygon(points)
           ^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'trimesh.path' has no attribute 'polygons'

Which system and setup are you using? I am using a pretty standard Ubuntu 24/Jazzy environment.

Idea: Make a Standalone Package From Your Tool

Before trying to restore the previous state of the tutorials, I think it might not be a good idea to integrate this tool into mesh_navigation_tutorials at all. The tool seems very general, and I would not tie it specifically to the tutorials. Instead, it could live as a standalone project. For example, it could be useful in a future experiments repository where we gradually increase the gap between an ideal world and the navigation mesh until mesh navigation fails.

My proposal would be:

  • Keep the tool as a standalone public repository under your own account, for example named sdf_to_nav_mesh
  • Make it a proper ROS package, or check whether it even needs a ROS dependency at all
  • Make sure that input and output paths are easily configurable, for example: python3 sdf_to_nav_mesh PATH/TO/INPUT.sdf PATH/TO/OUTPUT.ply (or another mesh format)
  • Write a short guide explaining how to use the tool, ideally with some images, so that we can publish it here: https://naturerobots.github.io/mesh_navigation_docs/guides/

If this works out and the tool becomes easy to use, I would be happy to add a reference in the documentation so that people can find it more easily.

@thippeswammy
Copy link
Author

Thank you again for your guidance and detailed feedback.

I wanted to let you know that I have now completely moved the tool into a standalone repository, as you suggested:
https://github.com/thippeswammy/sdf_to_nav_mesh

I’ve also added a user guide that explains the purpose of the tool, basic usage, and input/output configuration. The aim was to keep it generic and easy to use, without tightly coupling it to mesh_navigation_tutorials.

I’ve also taken your technical questions and the errors you encountered into account for the initial setup of this new package, particularly around dependencies and execution.

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