-
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!
LOTUSim is installed and run through 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.
You don't need to know Nix to use LOTUSim. Follow the steps below in order and you'll have a working simulation in a few minutes.
LOTUSim runs natively on Linux and macOS.
On Windows? Nix doesn't run natively on Windows. Skip ahead to Windows users. You'll run everything inside WSL2, then the rest of this guide applies exactly as written.
- One-time setup
- Pick your path
- Path A - Run without installing
- Path B - Install LOTUSim
- Path C - Set up your dev environment
- Windows users
- Try your first scenario
- Optional: Set up the 3D rendering
- Developing for LOTUSim
Do this section once, no matter which path you choose.
LOTUSim is installed and run through 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. You don't need to know Nix to use LOTUSim. 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. Other install options (for special setups) are listed on Nix website.
LOTUSim depends on Gazebo/ROS packages 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. This step lets Nix download them pre-built instead, which takes seconds.
It needs admin rights, but only once per machine:
sudo tee -a /etc/nix/nix.conf <<'EOF'
experimental-features = nix-command flakes
extra-substituters = https://ros.cachix.org
extra-trusted-public-keys = ros.cachix.org-1:dSyZxI8geDCJrwgvCOHDoAfOm5sV1wCPjBkKL+38Rvo=
EOF
sudo systemctl restart nix-daemon # macOS: sudo launchctl kickstart -k system/org.nixos.nix-daemonWithout a trusted key the cache's signatures do not verify, so Nix builds from source anyway. That is why this step is not optional. A single-user install has no daemon and no
trusted-users, and can skip it. Ifsystemctlisn't found (e.g. macOS or a single-user Nix install), restart whichever service manager your install uses, or simply restart your computer./etc/nix/nix.confis the system-wide file, so the keys are trusted for every user and the signatures verify.
Skip this step you just want to use Podman (Path A, Option 2).
LOTUSim's 3D window needs to talk to your graphics card. On most Linux distributions, Nix programs can't see your machine's GPU by default. Without this bridge, the simulation window may not open.
Install the one matching your graphics card:
nix profile add github:nix-community/nixGL#nixGLIntel # Intel graphicsOn an NVIDIA or hybrid/Optimus machine, also add the NVIDIA bridge so rendering uses the discrete GPU instead of falling back to Intel — this reads your driver's exact version off the running machine, so it needs --impure, and NVIDIA's userspace driver is unfree:
NIXPKGS_ALLOW_UNFREE=1 nix profile add --impure github:nix-community/nixGL#nixGLNvidia
See the nixGL project if you're unsure.
You're now ready to run LOTUSim!
There are three ways to get LOTUSim, depending on what you want to do:
| I want to... | Use this path |
|---|---|
| Just try it out, nothing left behind afterwards | Path A - Run without installing |
Have lotusim available any time, like a normal app |
Path B - Install LOTUSim |
| Change or contribute to LOTUSim's code | Path C - Set up a dev environment |
Good for a first try. Nothing is added to your system permanently (aside from Nix itself).
Pick one of these two:
Option 1 - via Nix
nix run github:naval-group/LOTUSim -- runOption 2 - via a container (Podman)
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.
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.
Now head to Try your first scenario.
1 - 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.
Nix isn't natively supported on Windows, so you'll run it inside WSL2 (Windows Subsystem for Linux). It's a lightweight Linux environment that runs alongside Windows. Once it's set up, everything else in this guide (Paths A, B, and C) works exactly as written, from inside your WSL2 terminal.
- Open PowerShell as Administrator and run:
wsl --install -d Ubuntu-24.04- Enable mirrored networking, so ROS 2's device discovery works without extra configuration. Open:
notepad $env:USERPROFILE\.wslconfigand add:
[wsl2]
networkingMode=mirrored- Restart WSL, then open your Ubuntu shell and follow any of the paths above (A, B, or C) exactly as written.
If you hit networking issues after this, check your Windows Firewall, mirrored networking is sometimes blocked by default.
- With LOTUSim and the web UI both running, open the UI in your browser:
- Installed via 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 Unity Hub, 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. If you just want to run LOTUSim rather than modify it, Path C above already gives you 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 flakee:
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