Skip to content

gz_waves: FFT backend - Part 4/4#885

Open
caguero wants to merge 11 commits into
vrx4from
caguero/waves-04-fft
Open

gz_waves: FFT backend - Part 4/4#885
caguero wants to merge 11 commits into
vrx4from
caguero/waves-04-fft

Conversation

@caguero

@caguero caguero commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Fourth in the series. Adds the second wave engine, the FFT spectral backend built on the EncinoWaves library, and wires it into the visual and the demo world as a selectable alternative to Gerstner.

How to test it?

ros2 launch vrx_bringup simulation.launch.xml

What this PR adds

  • FFT engine (FFTWaveSimulation, libgz-waves-provider-fft) — a plain IWaveField backed by EncinoWaves (TMA/JONSWAP/PM spectra + directional spreading + dispersion). MakeFFTWaveField() is its factory.
  • Source system (gz-sim-waves-fft-system) — a WavesSystemBase subclass that builds the FFT engine and writes the Wavefield component. Its doc block documents the full parameter surface and the <sea_state> precedence rules.
  • GUI registrar (gz-sim-waves-fft-gui) — registers the "fft" factory in the GUI so WaterVisual can rebuild a private FFT engine; same bare, no-ISystem, static-init form as the Gerstner registrar.
  • Unit tests (test/fft_test.cc, 18 tests).
  • Wiring: adds gz-sim-waves-fft-gui to the water_surface model (next to the Gerstner registrar) and offers FFT as a commented alternative in open_water.sdf. Gerstner stays the active default — flip by commenting the Gerstner <plugin> and uncommenting the FFT one.

@caguero
caguero force-pushed the caguero/waves-04-fft branch from 3253aa3 to c9ba107 Compare June 19, 2026 17:14
@caguero caguero changed the title gz_waves_provider_fft: FFT backend - Part 4/4 gz_waves: FFT backend - Part 4/4 Jun 19, 2026
@caguero
caguero force-pushed the caguero/waves-03-rendering branch from f4a163e to 4d50173 Compare June 19, 2026 17:33
@caguero
caguero force-pushed the caguero/waves-04-fft branch from c9ba107 to aa40214 Compare June 19, 2026 17:34
@caguero
caguero force-pushed the caguero/waves-03-rendering branch 2 times, most recently from 7df415e to 3a1f9b0 Compare June 22, 2026 17:20
@caguero
caguero force-pushed the caguero/waves-04-fft branch from aa40214 to c14f9f6 Compare June 22, 2026 18:16
@caguero
caguero force-pushed the caguero/waves-04-fft branch from 8025d64 to b5c9223 Compare June 30, 2026 22:02
@caguero
caguero force-pushed the caguero/waves-03-rendering branch from 509839f to 85be063 Compare June 30, 2026 22:02
@caguero
caguero force-pushed the caguero/waves-04-fft branch from b5c9223 to e88d3bb Compare June 30, 2026 22:12
@caguero
caguero force-pushed the caguero/waves-03-rendering branch from 85be063 to e1af261 Compare June 30, 2026 22:12
@srmainwaring

Copy link
Copy Markdown
Contributor

Hi @caguero, this looks good. I'm in the process of building this to test on macOS under Gazebo Jetty. May have an issue with missing metal shaders, but will let you know if I can get it running.

I see in the code you've benchmarked against asv_wave_sim in a few places and borrowed the approach using displacement maps.

// SaveToSystemRam (matches asv_wave_sim's Ogre2DisplacementMap): Ogre Next
// retains a CPU-side copy of the texture so it doesn't have to round-trip
// the GPU to validate residency state during streaming uploads.

Appreciate asv_wave_sims GPL license is a problem for vrx, but an acknowledgement would be nice.

@caguero
caguero force-pushed the caguero/waves-03-rendering branch from e1af261 to 8d6502e Compare July 3, 2026 13:33
@caguero
caguero force-pushed the caguero/waves-04-fft branch from e88d3bb to 6c48906 Compare July 3, 2026 13:33
@caguero
caguero force-pushed the caguero/waves-03-rendering branch from 8d6502e to e513a4d Compare July 3, 2026 13:40
@caguero
caguero force-pushed the caguero/waves-04-fft branch from 6c48906 to 09df94e Compare July 3, 2026 13:40
@bsb808

bsb808 commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

I was not able to run the example. I suspect that we have a build environment issue. @caguero - It would be good to provide the details of the build environment you expect folks to use. Here is what I pieced together from the jazzy release....

First, modify open_water.sdf to use SDF version 1.11 vice 1.12

Then....

rocker --pull --devices /dev/input/js0 --x11 --nvidia --user --home ghcr.io/osrf/vrx-devel:latest /bin/bash

Then in container...

sudo apt update

sudo apt install \
  ros-jazzy-gz-cmake-vendor ros-jazzy-gz-utils-vendor \
  ros-jazzy-gz-math-vendor ros-jazzy-gz-common-vendor \
  ros-jazzy-gz-msgs-vendor ros-jazzy-gz-transport-vendor \
  ros-jazzy-gz-fuel-tools-vendor ros-jazzy-gz-plugin-vendor \
  ros-jazzy-gz-physics-vendor ros-jazzy-gz-rendering-vendor \
  ros-jazzy-gz-sensors-vendor ros-jazzy-gz-gui-vendor \
  ros-jazzy-gz-sim-vendor ros-jazzy-gz-tools-vendor ros-jazzy-sdformat-vendor

source /opt/ros/jazzy/setup.bash

colcon build --merge-install

source ./install/setup.bash 

ros2 launch vrx_bringup simulation.launch.xml

This runs without error, but Gazebo hangs:
image

So, would appreciate instructions to reproduce the intended dev environment.

@caguero
caguero force-pushed the caguero/waves-04-fft branch from 09df94e to 11ca4b6 Compare July 8, 2026 14:28
caguero and others added 5 commits July 8, 2026 16:49
…I registrar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
… FFT the default in open_water.sdf

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
Make the FFT engine build on the rebased stack and match its conventions:
- ParticleVelocity/Normal return gz::math::Vector3d (the IWaveField interface
  is now gz::math); Eigen stays internal to EncinoWaves and the grid accessors.
- std::cerr/std::cout -> gzerr/gzmsg (wire gz-common into the engine target).
- Members switch to gz-style this->member (no trailing underscore).
- Wire gz-math/gz-common into CMake + package.xml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Carlos Agüero <caguero@honurobotics.com>
…ence)

ParticleVelocity was a zero stub, so buoyancy/drag saw still water under the
FFT backend. EncinoWaves exposes no velocity field or public spectral
coefficients, so compute the Eulerian water velocity as the time derivative of
the displacement field (∂Dx/∂t, ∂Dy/∂t, ∂η/∂t): each Update propagates a
scratch state at t+dt and finite-differences it into velocity grids that
ParticleVelocity bilinear-samples. Adds two tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Carlos Agüero <caguero@honurobotics.com>
Drop the retired "backend" / "wave model" terms in comments and Doxygen
in favour of "engine" / "wave field engine", matching the vocabulary
settled in the PR1 review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
@caguero
caguero force-pushed the caguero/waves-04-fft branch from 11ca4b6 to f26cb6d Compare July 8, 2026 14:51
@caguero
caguero changed the base branch from caguero/waves-03-rendering to vrx4 July 8, 2026 14:51
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
@caguero
caguero force-pushed the caguero/waves-04-fft branch from 27f20c0 to e5228b5 Compare July 8, 2026 14:58
@caguero

caguero commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

@srmainwaring thanks Rhys. Added an acknowledgement crediting asv_wave_sim's displacement-map approach at the top of Ogre2HeightMapBridge.cc in e5228b52. And please do flag any missing metal shaders on macOS/Jetty; happy to help track that down if we can help, although not my expertise.

@caguero

caguero commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

@bsb808, the supported platform is ROS Lyrical with Gazebo Jetty (our target, and currently the most modern stable ROS and Gazebo combination). I documented the local prerequisites in WAVES_DESIGN.md §10 in e5228b52 (Gazebo Jetty, EncinoWaves, GPU); container instructions are still TODO. Could you retry on Jetty and let me know if it still hangs?

@srmainwaring

srmainwaring commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@caguero I have a patched version of this PR running on macOS using Metal shaders. It is working fairly well. There are some hardcoded changes that need to be dynamic depending on the choice of platform and render engine.

02-metal-fft-waves

The patches to this PR for macOS are here: https://github.com/srmainwaring/vrx/tree/prs/waves-04-fft-metal

The encinowaves project is also built from source as there is a non-portable macro used in the Exception.h header of that project.


On macOS the vrx project is built in a Pixi workspace (target is osx-arm64). I now use these exclusively for ROS and Gazebo development on macOS. If you are interested the pixi.toml used is listed below. It includes a custom build of Gazebo Jetty which includes some improvements to the render engine and includes all latest patches to Jetty which are required to support shader parameters on macOS. The ROS distro is Jazzy.

Details
[workspace]
channels = [
  "https://prefix.dev/ardupilot",
  "https://prefix.dev/robostack-jazzy",
  "conda-forge"]
name = "gz-jetty-vrx"
platforms = ["osx-arm64"]
version = "0.1.0"

[activation]
scripts = [".pixi/envs/default/setup.sh"]

[activation.env]
GZ_IP = "127.0.0.1"
GZ_PARTITION = "jetty"
GZ_VERSION = "jetty"

# vrx
#GZ_SIM_RESOURCE_PATH = "$GZ_SIM_RESOURCE_PATH:$PIXI_PROJECT_ROOT/workspace/src/vrx"
#GZ_SIM_SYSTEM_PLUGIN_PATH = "$GZ_SIM_SYSTEM_PLUGIN_PATH:$PIXI_PROJECT_ROOT/workspace/src/vrx"

[dependencies]

# build essentials
cmake = "<4"
colcon-common-extensions = ">=0.3.0,<0.4"
compilers = ">=1.11.0,<2"
ninja = ">=1.13.2,<2"
python = ">=3.12.13,<3.13"

# utiltities
gh = ">=2.92.0,<3"
git = ">=2.53.0,<3"
tree = ">=2.3.2,<3"

# gz-* dependencies from conda-forge
#assimp = ">=6.0.5,<7"
bullet-cpp = ">=3.25,<4"
cli11 = ">=2.6.2,<3"
cppcheck = ">=2.20.1,<3"
cppzmq = ">=4.11.0,<5"
dartsim-cpp = ">=6.16.7,<7"
eigen = ">=3.4.0,<5"

ffmpeg = ">=8.0.1,<9" # jazzy
#ffmpeg = ">=8.1.1,<9" # kilted

freeimage = ">=3.18.0,<4"
jsoncpp = ">=1.9.6,<2"

libabseil = ">=20250512.1,<20250513" # jazzy
#libabseil = ">=20260107.1,<20260108" # kilted

libarchive = ">=3.8.7,<4"
libcurl = ">=8.20.0,<9"
libcxx = ">=22.1.4,<23"

libgdal = ">=3.12.2,<4" # jazzy
#libgdal = ">=3.12.4,<4" # kilted

libgdal-core = ">=3.12.2,<4" # jazzy
#libgdal-core = ">=3.12.4,<4" # kilted

libode = ">=0.16.6,<0.17"

# ros2-distro-mutex >=0.14.0,<0.15.0a0
libprotobuf = "==6.31.1" # jazzy
#libprotobuf = "==6.33.5" # kilted

libsqlite = ">=3.53.1,<4"
libzenohc = ">=1.9.0,<2"
libzip = ">=1.11.2,<2"
ogre = ">=1.10.12.1,<2"
ogre-next = ">=2.3.3,<3"
pkg-config = ">=0.29.2,<0.30"
pybind11 = ">=3.0.3,<4"
#qt6-main = ">=6.11.0,<7"
spdlog = ">=1.17.0,<2"
tinyxml2 = ">=11.0.0,<12"
zeromq = ">=4.3.5,<5"
#urdfdom = ">=5.1.2,<6"
#urdfdom_headers = ">=2.1.2,<3"
yaml = ">=0.2.5,<0.3"

ruby = ">=4.0.2,<5"
swig = ">=4.4.1,<5"

# ardupilot
blas = { version = "*", build = "accelerate" }
ros2-distro-mutex = { version = ">=0.14.0,<0.15", build = "jazzy_*" }
ros-jazzy-gz-sim-vendor = ">=0.4.4,<0.5"

# robostack-jazzy
ros-jazzy-ament-cmake-auto = ">=2.5.5,<3"
ros-jazzy-desktop = ">=0.11.0,<0.12"

ros-jazzy-sdformat-urdf = { version=">=1.0.2,<2", build="*jetty*" }
ros-jazzy-ros-gz-interfaces = { version=">=1.0.19,<2", build="*jetty*" }
ros-jazzy-ros-gz-bridge = { version=">=1.0.19,<2", build="*jetty*" }
ros-jazzy-ros-gz-image = { version=">=1.0.19,<2", build="*jetty*" }
ros-jazzy-ros-gz-sim = { version=">=1.0.19,<2", build="*jetty*" }
ros-jazzy-rviz-default-plugins = { version=">=14.1.19,<15", build="*jetty*" }

ros-jazzy-xacro = ">=2.1.1,<3"


# jazzy
# pixi add "protobuf>=6.31.1,<7"
# pixi add "gz-sim [version='10.*', build='*protobuf6311*']"
gz-sim = { version = "10.*", build = "*protobuf631*_2*" }
gz-sensors = ">=10.0.0,<11"
gz-gui = ">=10.0.0,<11"
gz-transport = ">=15.0.2,<16"
gz-rendering = ">=10.0.1,<11"
gz-physics = ">=9.2.0,<10"
gz-fuel-tools = ">=11.0.0,<12"
sdformat = ">=16.0.1,<17"
gz-msgs = ">=12.0.1,<13"
gz-common = ">=7.1.1,<8"
gz-plugin = ">=4.0.0,<5"
gz-math = ">=9.1.0,<10"
gz-utils = ">=4.0.0,<5"
gz-tools = ">=2.0.3,<3"
gz-cmake = ">=5.1.0,<6"

caguero and others added 5 commits July 13, 2026 17:01
…ajor grids directly

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
…Gerstner chop)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
…onfigure changes grid_size

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
…per-tick FFT cost

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
…ption safety)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Carlos Aguero <caguero@honurobotics.com>
@bsb808

bsb808 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

I was able to successfully run the example with the docker env from #889

I think it will be important to verify the model - HonuRobotics/encinowaves#4
When I run with FFT sea state 3 I get a very calm water surface, which, based on experience, does not match my expectations. Need to show it is numerically correct.
Screenshot from 2026-07-21 13-31-57

I did get a few run time warnings:

[component_container-1] Warning [Utils.cc:132] [/sdf/plugin[@name="gz::sim::systems::WaterVisual"]:<data-string>:L1]: XML Element[plugin], child of element[sdf], not defined in SDF. Copying[plugin] as children of [sdf].

and

[gz-2] Warning [Utils.cc:132] [/sdf/plugin[@name="gz::sim::systems::WaterVisual"]:<data-string>:L1]: XML Element[plugin], child of element[sdf], not defined in SDF. Copying[plugin] as children of [sdf].
[component_container-1] (2026-07-21 20:31:29.434) [debug] [EntityComponentManager.cc:1691] Updated state thread iterators: 8 threads processing around 1 entities each.
[gz-2] Warning [Utils.cc:132] [/sdf/plugin[@name="gz::sim::systems::WaterVisual"]:<data-string>:L1]: XML Element[plugin], child of element[sdf], not defined in SDF. Copying[plugin] as children of [sdf].

@bsb808

bsb808 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

CC says the warnings are cosmetic, do nott affect behavior, and the fix lives upstream in gz-sim's visual-plugin loader, not here. It's a well-known Gazebo quirk for any visual plugin that carries parameters.

Good enough for me!

@bsb808
bsb808 self-requested a review July 21, 2026 20:49

@bsb808 bsb808 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I left a few small comments/suggestions, but no showstoppers.

Merge when ready.

/// \brief Bilinear sample of `_grid` at the given world (x, y), wrapping
/// queries outside the tile to its periodic image.
/// \param[in] _grid The grid to sample.
/// \param[in] _x World x coordinate [m].

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Might be worth mentioning the coordinate convention. Is the world coordinate frame consistent with REP 103 - i.e., is it ENU?

@@ -0,0 +1,487 @@
/*

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I ran the test with....

 colcon test --packages-select gz_waves_provider_fft --merge-install
colcon test-result --verbose

Result

Summary: 31 tests, 0 errors, 0 failures, 5 skipped

Is this the expected result? Is the skipped tests expected?

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.

3 participants