-
Notifications
You must be signed in to change notification settings - Fork 15
Getting Started
Get LOTUSim installed and running a live example in about 10 minutes!
There are two ways to run LOTUSim:
- With a container (Podman). The quickest way to try it: one command, and no Nix needed.
- With Nix, a package manager that downloads everything the simulator needs (ROS 2, Gazebo, and all their dependencies) into its own isolated location on your computer. It won't conflict with or change anything else you have installed, and you don't need to know Nix to use it. This is the way to install LOTUSim permanently or to develop it.
LOTUSim runs natively on Linux and macOS. On Windows, the Nix paths run inside WSL2; see the Windows tab under One-time setup.
- Pick your path
- Run with a container (no Nix)
- Run with Nix
- Try your first scenario
- Optional: Set up the 3D rendering
- Developing for LOTUSim
| I want to... | Use this path | Needs Nix? |
|---|---|---|
| Try it out quickly, with nothing to set up | Run with a container | No |
| Try it out with Nix, without installing lotusim | Path A - Run without installing | Yes |
Have lotusim available any time, like a normal app |
Path B - Install LOTUSim | Yes |
| Change or contribute to LOTUSim's code | Path C - Set up your dev environment | Yes |
You'll need Podman version 6 or later installed, check with podman --version, and upgrade if it's older. Then:
podman run --rm ghcr.io/naval-group/lotusimFor NVIDIA cards, you will need the NVIDIA Container Toolkit installed and configured to use Podman, or the 3D window won't render.
Once it's running, jump to Try your first scenario.
Paths A, B and C all run LOTUSim through Nix. Do the one-time setup first, then pick one.
Do this once per machine. Pick your OS:
Linux / macOS
Open a terminal and run:
curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install | sh -s -- --daemonOnce it finishes, close your terminal window and open a new one so the changes take effect. For more options, check their official website: Nix website.
LOTUSim depends on Gazebo/ROS packages, and on Naval Group's own builds, that aren't part of Nix's normal download cache. Without this step, the very first time you build or run LOTUSim, Nix will quietly compile those packages from scratch, that can take about an hour, with no progress message explaining why. It needs admin rights, only once per machine:
sudo tee -a /etc/nix/nix.conf <<'EOF'
experimental-features = nix-command flakes
extra-substituters = https://ros.cachix.org https://naval-group.cachix.org
extra-trusted-public-keys = ros.cachix.org-1:dSyZxI8geDCJrwgvCOHDoAfOm5sV1wCPjBkKL+38Rvo= naval-group.cachix.org-1:ytTEzFEeuzQrC9IRYLzHGa5OnM65G95M6/sbPd0fy28=
EOF
sudo systemctl restart nix-daemon # macOS: sudo launchctl kickstart -k system/org.nixos.nix-daemon
/etc/nix/nix.confis the system-wide file, so the keys are trusted for every user and the signatures verify.
Programs installed through Nix can't see your system's graphics drivers on their own, so LOTUSim's 3D Gazebo window may fail to open or fall back to slow software rendering. nixGL bridges that gap by pointing LOTUSim at the driver already on your machine. It's optional: pick the bridge that matches your graphics card, or skip it and come back here if lotusim run --gui doesn't open a window. macOS users can skip this step.
Not sure which graphics card you have? Run lspci | grep -iE 'vga|3d'.
Intel or AMD (both use the open-source Mesa drivers, so they share one bridge despite the Intel in its name):
nix profile add github:nix-community/nixGL#nixGLIntelNVIDIA, including hybrid/Optimus laptops (for hybrid laptops, install the Intel/AMD bridge above too). It reads your installed driver's exact version, so it needs --impure, and NVIDIA's driver is unfree, so it needs NIXPKGS_ALLOW_UNFREE=1:
NIXPKGS_ALLOW_UNFREE=1 nix profile add --impure github:nix-community/nixGL#nixGLNvidiaWindows (WSL2)
Nix doesn't run natively on Windows, so you'll run LOTUSim inside NixOS-WSL, a full NixOS system running under WSL2.
Open PowerShell as Administrator and run:
wsl --install --no-distribution
Restart Windows if prompted.
Download nixos.wsl from the latest NixOS-WSL release. If you have WSL 2.4.4 or later, you can double-click the file to install it. Or install it from PowerShell:
wsl --install --from-file nixos.wsl
On older WSL versions, use:
wsl --import NixOS $env:USERPROFILE\NixOS nixos.wsl --version 2
Then open it:
wsl -d NixOS
Optionally, make it your default distro with wsl -s NixOS.
This lets ROS 2's device discovery work without extra configuration, and lets your Windows browser reach the LOTUSim UI on localhost. In PowerShell, open:
notepad $env:USERPROFILE\.wslconfig
and add:
[wsl2]
networkingMode=mirrored
memory=16GB
swap=16GB
This step sets nix binary caches and graphics bridge. Inside the NixOS shell, open the system config:
sudo nano /etc/nixos/configuration.nix
Add these lines inside the main { ... } block. Keep what's already there, especially the imports and system.stateVersion lines.
# Flakes + binary caches (replaces Step 2)
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
extra-substituters = [ "https://ros.cachix.org" "https://naval-group.cachix.org" ];
extra-trusted-public-keys = [
"ros.cachix.org-1:dSyZxI8geDCJrwgvCOHDoAfOm5sV1wCPjBkKL+38Rvo="
"naval-group.cachix.org-1:ytTEzFEeuzQrC9IRYLzHGa5OnM65G95M6/sbPd0fy28="
];
};
# GPU access through the Windows driver (replaces Step 3)
hardware.graphics.enable = true;
wsl.useWindowsDriver = true;
# Needed to clone the repo and for flakes to see your files
environment.systemPackages = with pkgs; [ git ];Apply it:
sudo nixos-rebuild switch
Without the trusted keys, Nix will quietly build the ROS/Gazebo packages from source the first time, which takes about an hour. If a build seems stuck, check that this rebuild succeeded.
In PowerShell:
wsl --shutdown
Then reopen NixOS with wsl -d NixOS and continue with Path A, B or C.
Troubleshooting: If ROS nodes can't find each other, check the Windows Firewall first, since mirrored networking is sometimes blocked by default. NixOS also has its own firewall; for local development you can add
networking.firewall.enable = false;toconfiguration.nixand rebuild. If--guifails to open a window, try the Intel/AMD nixGL bridge from Step 3 (in the Linux / macOS section) as a fallback.
You're now ready to run LOTUSim!
Good for a first try. Nothing is added to your system permanently (aside from Nix itself).
nix run github:naval-group/LOTUSim -- runIn a second terminal, start the web UI:
nix run github:naval-group/LOTUSim#uiThen open http://localhost:8080 in your browser and head to Try your first scenario.
This puts lotusim and lotusim-ui permanently on your computer, like installing a normal application.
nix profile add github:naval-group/LOTUSim github:naval-group/LOTUSim#uilotusim runIn a second terminal:
lotusim-uiThen open http://localhost:8080 in your browser.
To update later, see Upgrading an installed build.
Now head to Try your first scenario.
If you're going to modify LOTUSim's code, you want the developer workflow:
git clone https://github.com/naval-group/LOTUSim.git
cd LOTUSim
nix develop # drops you into a shell with ROS 2, Gazebo, and build tools
mise run build # builds the workspace
mise run sim # runs it. You can add the param --guiThis gets the core simulator running. If you also want to work on the web UI or the physics engine (xdyn), or you want the full task reference, see Developing for LOTUSim below.
- With LOTUSim and the web UI both running, open the UI in your browser:
- Container, Path A or Path B: http://localhost:8080
- Dev setup (Path C,
nix run .#ui): http://localhost:8080; if running the frontend directly withnpm run dev, it's http://localhost:5173
- In the left panel, under "Launch Scenario", select
demo.yaml. - Click Launch Scenario. You should see an arrow representing an LRAUV (an underwater vehicle) appear and start moving. 🎉
Want to see more? Check the Tutorial page for further examples, or run lotusim --help to see every scenario/world your build includes.
This is only needed if you want photorealistic rendering through Unity. The demo scenario above works without it.
- Install Unity
- Download the Unity Hub and follow the on-screen installation guide.
- Create or sign in to your Unity account (UDN), choose a license type, and then install the Unity Editor through Unity Hub.
- Use Unity version 2022.3.18f1 (required for HDRP water system).
- Clone the Unity project
git clone --recurse-submodules https://github.com/naval-group/LOTUSim-Unity-modules
cd LOTUSim-Unity-modules
git submodule update --remote --merge- Open the project
- In Unity Hub -> Projects, add the "LOTUSim-Unity-modules" folder and open it.
- Once the project opens, go to the Project tab (bottom panel), open the Scenes folder, and load one of the scenes.
- Launch a scene
In the Unity Editor, open a scene from the imported project (for example, the defense scenario).
If the scene appears black, check that your graphics drivers are properly installed.
This section covers building LOTUSim from source, working on the web UI, developing the physics engine (xdyn), and how the pieces fit together internally. It assumes you've already done the one-time setup above. Path C above is the short version.
mise run <task>. The devShell provides mise, so no separate install.
| Task | What it does |
|---|---|
build |
Build the colcon workspace |
rebuild |
Clean, then build |
clean |
Remove build/, install/ and log/
|
sim |
Run a simulation world |
check |
Headless smoke test: vessels spawn, sensors render, gz logs no error |
test |
Run the gtest suite (advisory — does not gate the build) |
test:assets |
Check that --assets-path composes assets roots instead of replacing them |
lint:tidy |
Run clang-tidy over the workspace sources |
doc |
Generate the doxygen documentation |
image |
Build the container image and load it into docker or podman |
mise tasks prints this list.
Every session
nix develop # once per shell
mise run build
mise run sim --guiBefore a PR
mise run check # headless smoke test
mise run test:assets # --assets-path compositionBefore publishing — rare, and the only thing that exercises the packaged artifact:
nix run .#lotusim -- run --guimise run sim runs install/, which the flake never sees. The wrapper, the state seeding, and the assets derivation live only in flake.nix, so nothing in the everyday loop covers them.
Use --gui, not headless. The GPU-driver bridge is fatal only for --gui; a headless run warns and carries on, so it passes whether or not the bridge survived into the packaged environment. mise run check does not close that gap either, it calls gz from the devShell and never goes through the flake's wrapper.
What users run - not part of the dev loop, but worth knowing what you are shipping:
nix profile add github:naval-group/LOTUSim github:naval-group/LOTUSim#ui
lotusim run --gui
lotusim-ui
nix profile upgrade LOTUSim ui # move both to the current mainlotusim is not on PATH inside nix develop; it is a flake package, not a devShell tool. Use mise run sim there.
| Code it runs | Worlds resolved from | |
|---|---|---|
mise run sim |
install/ — your last mise run build
|
repo assets/worlds/
|
nix run .#lotusim |
store build of the git-tracked tree |
~/.local/share/lotusim, then store |
lotusim run |
store build, frozen at install time | same |
Anything reached through the flake sees git-tracked files only. Modifications to tracked files are picked up; a brand-new file needs git add first or it is silently absent.
Path C above gets the core simulator running. LOTUSim also has a web UI (two repos) and a physics engine (a separate project, xdyn) — here's how to develop each.
The UI lives in two repositories, each packaged as its own flake:
Frontend, with the packaged backend:
git clone https://github.com/naval-group/LOTUSim-UI-frontend.git
cd LOTUSim-UI-frontend
nix develop # nodejs
npm ci && npm run dev # vite, http://localhost:5173
nix run ./path/to/LOTUSim#ui-backend # the other halfBackend, with the packaged frontend:
git clone https://github.com/naval-group/LOTUSim-UI-backend.git
cd LOTUSim-UI-backend
nix develop ./path/to/LOTUSim#ui-backend # node, ROS, and lotusim_msgs on AMENT_PREFIX_PATH
npm run setup # rclnodejs ships no prebuilt for this Node, so it compiles
npx generate-ros-messages
npm run dev # go to http://localhost:8080
nix run ./path/to/LOTUSim#ui-frontend # the other halfThe backend's shell has to come from the main LOTUSim repository, because only LOTUSim generates its messages. Editing interfaces/ there is enough to change what the UI can talk about: packages.messages builds from your working tree, so the next nix run .#ui carries the new types.
Before pushing, check the change still works in the packaged build, which is what users actually get:
nix run .#ui --override-input lotusim-ui-frontend git+file:///path/to/LOTUSim-UI-frontendRunning the whole packaged UI from the main repo:
nix run .#ui # open http://localhost:8080; the backend serves the API on :5000packages.ui provides a binary named lotusim-ui, so nix profile add github:naval-group/LOTUSim#ui puts it on PATH permanently. #ui-backend and #ui-frontend are lotusim-ui-backend and lotusim-ui-frontend, and run either half alone.
The UI is a separate package on purpose, not a lotusim subcommand: its closure carries no gz- paths, against the full Gazebo stack for lotusim. Folding them together would put Gazebo behind the UI and the whole Node stack behind the simulator.
The devShell puts a pinned xdyn-for-cs on PATH, taken from lxdyn's published image. To use a local build instead:
cd /path/to/lxdyn
zig build
./zig-out/bin/xdyn-for-cs # bind it to the same host:port the scenario's `uri:` namesThere is no config pointing LOTUSim at one build or the other, whichever process holds that port when the vessel activates is the one it talks to. Run your build by explicit path, or from outside nix develop, since a bare xdyn-for-cs inside this repo's devShell resolves to the pinned one. nix run does not reach a local checkout: lxdyn's flake declares no apps.
There is no reconnect. activateInterface retries three times at spawn and then gives up, with no handler that re-dials a dropped connection. Restarting a rebuilt xdyn-for-cs leaves an already-spawned vessel's connection dead -> respawn the vessel, not the whole simulation.
New versions don't appear automatically, run this whenever you want to update:
nix profile upgrade LOTUSim ui # by name, as `nix profile list` shows them
nix profile upgrade --all # every entry, nixGL includedAd-hoc runs sit behind a second cache. Nix remembers a flake reference's resolution for tarball-ttl - one hour by default - so a nix run inside that window can resolve the previous revision:
nix run github:naval-group/LOTUSim --refresh -- run --gui--refresh forces the lookup, and nix profile upgrade resolves through the same cache, so it wants the flag too when you are chasing a revision pushed minutes ago.
nix profile list prints the locked flake URL and store path behind each entry, which is what to read when an installed build behaves differently from mise run sim: they are different artifacts, frozen at different times.
--assets-path adds assets roots to the core one rather than replacing it, so a consumer project composes its own models and worlds with the core catalogue. It is colon-separated and repeatable, and both forms append:
mise run sim --assets-path /path/to/mine other.world
mise run sim --assets-path /a:/b --assets-path /c other.worldRoots are searched in order. A world is taken from the first root that holds it; model:// URIs resolve across all of them. mise run test:assets checks this.
Worlds and models are bundled, so a bare world name resolves without a path - aerialWorld.world, circling_ship_example.world, defenseScenario.world, gz_sensor_example.world, lotusim.world and xdyn_multithread_test.world. lotusim --help lists whatever a given build carries.
lotusim run takes --gui and --debug. Anything starting with a dash goes straight to gz sim instead, so lotusim -s -r worlds/lotusim.world and every other gz option still work.
On first run the packaged lotusim seeds ~/.local/share/lotusim, which is also where scenarios you create and models you upload are written — the store copy is read-only, so it cannot be that place. LOTUSIM_STATE_HOME moves the whole directory; GZ_SIM_RESOURCE_PATH, LOTUSIM_MODELS_PATH and LOTUSIM_SCENARIOS_PATH override one at a time, and the wrapper defers to each when it is already set.
mise run sim does not use the state directory at all, it reads the repo's assets/, so a developer edits models in-tree.
physics_engine_interface is a websocket client, not a launcher: it dials the uri named in the scenario YAML, once per vessel per domain, and never invokes an xdyn binary itself. mise run sim and mise run check start Gazebo only. Without an xdyn-for-cs listening, a vessel spawns and renders but does not move.
mise run check needs no xdyn: gz exits 0 whether or not a model resolved, so the log is the only signal, and the check reads the log.
The devShell evaluates mise.toml on entry and exports what colcon build installs, so LOTUSIM_*, xdyn-for-cs and the workspace's own Python and ROS packages are in scope without sourcing anything. The examples under examples/ rely on that.
See the Troubleshooting & FAQ page.
↑ Back to Top ↑ | 🏠 Home | ❓ Support | Licensed under Eclipse Public License 2.0 | © 2025 Naval Group