Robosim: lightweight multi-player robotics simulator

logo

Robosim is a client-server simulator for multi-player robotics. The server holds the 3D scene (floor, walls, obstacles, controllable robot). Clients connect over TCP with a msgpack protocol and send JSON-shaped commands (move, entity_spawn, robot_get_state, ...). The server implements the physics and the actual simulation, while the client simply receive the world state as images. So, it is not a shared world, but rather the client operates only on top of rendered images. The server uses microecs as a data structure, where the World object is a Struct-of-Arrays representing the entitites' components (see ECS wikipedia).

Docsumentation. Se this link: open-visual-robotics.gitlab.io/robosim or open it locally (e.g Getting started.

Install & run

python -m venv .venv/ && source .venv/bin/activate     # Windows: .venv\Scripts\Activate.ps1
# NOTE: -e is mandatory, otherwise it will not behave ok for development
pip install -e .[client]                               # server + engine + client; `pip install -e .` for server only

# Checking installation by running tests
pytest test/ pkg/ # pkg is the 3rd party tests
bash test/e2e/run_all.sh

# Run the simulator
./cli/robosim.py -p 5555 [--map_path MAP.json] [--headless] [-i SCRIPT]   # server
./cli/client.py localhost -p 5555                                         # client (may be on another machine)

Install editable; the server and client have separate dependencies and can run on different machines. The client can control the UAV using: W/A/S/D, arrows, Q/E, and PageUp/PageDown. See the code for actual keybinds. For VSCode / Rider / static-analyzer setup see Integration with IDEs.

Command Line Interface tools

In cli/ we offer a few standalone runnable python programs. Most of them are symlinks to their implementation under src/<tool>/, with <tool> being one of the standalone programs this simulator is built of.

cli/
  robosim.py       -> ../src/robosim/robosim.py     # server: World + physics + TCP listener + renderer
  client.py        -> ../src/client/client.py       # interactive client: keyboard control, FPV, trajectory
  mapeditor.py     -> ../src/mapeditor/main.py      # map editor: select/move objects, collision view, F5/F6 save+load
  robosim-ncat.py                                   # netcat-like protocol tool (stdin commands -> replies)

Map editor

cli/mapeditor.py opens the same map JSONs as the server (default resources/maps/01-basic.json) to inspect and nudge objects before running the scene:

./cli/mapeditor.py [--map_path MAP.json] [--headless]
  • Select: hovering highlights the object under the cursor; a left click makes it the selection, and Ctrl+left-click toggles it in/out. Clicking empty space leaves the selection alone. O toggles collision rendering.
  • Move the selection: Shift+left-drag moves the whole selection; Left/Right → X, Up/Down → Z, PageUp/PageDown → Y nudge at 2 m/s.
  • Camera: WASD to move, right-drag to rotate, scroll to move along the view, Shift for fast.
  • F5 (or Ctrl+S) saves the world + editor state to resources/state.json (gitignored); F6 reloads it; R reloads the map the editor was started with. The server's own F6 reads the same file, so an editor save can be loaded straight into a running scene.
  • TCP CLI (--port, default 42068): plain ASCII lines, one command per line (ncat localhost 42068) — help, me_save_state (the F5 twin), me_load_state (the F6 twin: swaps the live scene for whatever is in resources/state.json).

Saving writes state.json, not the map JSON — map export is not implemented yet.

The state file is the handoff between the two apps, in both directions. Both read ["world"] out of it and both write it with the same serializer, so an editor save boots the server and a server save (sim_save_state / F5) boots the editor:

./cli/mapeditor.py --map_path resources/maps/01-basic.json   # edit, F5 -> resources/state.json
./cli/robosim.py   --map_path resources/state.json           # run the scene the editor saved
./cli/mapeditor.py --map_path resources/state.json           # ...and back, after the server's F5

Both directions are covered end to end by test/e2e/editor-to-server/.

Interactive CLI

cli/robosim-ncat.py is a netcat-like client for the protocol with a short human shorthand:

./cli/robosim-ncat.py localhost -p 5555
> {'status': 'connected', 'id': 0, 'timestamp': '1900-01-01T00:00:00.123456'}
move 0 0 1 2 0 0.5      # X Y Z pitch yaw roll → {"cmd": "move", "control_input": [0, 0, 1, 2, 0, 0.5]}
> {'status': 'Applied control_input=(0, 0, 1, 2, 0, 0.5). Current pose: (...)', ...}

The wire format and the full command set are on Protocol & Wire Format.

Repository layout

cli/        # executables: robosim.py + client.py + mapeditor.py (symlinks into src/), robosim-ncat.py (protocol tool)
src/
  robolib/    # reusable engine: math/physics, collisions, netcode, ECS components, plugin base
  robosim/    # server process (run-in-place): builds the World, owns systems + main loop, maps/
  mapeditor/  # map editor: hover/select/move objects, collision view, F5/F6 state save/load
  plugins/    # app-level plugins: manual_move, race, trajectory_mission
  client/     # standalone client: keyboard control, FPV, trajectory generation
pkg/        # vendored libraries: microecs, microspec, micronetcode, loggez, ...
resources/  # models, textures, maps
test/       # integration/ (DST fixtures), e2e/, manual diagnostics, lint
docs/       # prose pages + the shared pdoc builder

Naming: robosim is the product, the pip distribution, and the server process (src/robosim/); robolib is the reusable engine library it imports (src/robolib/). robolib and plugins install as importable packages; src/robosim/, src/mapeditor/ and src/client/ run in place (no __init__.py).

robolib is an ECS: all state lives in one microecs.World, an entity is an id plus components (data only), and behavior lives in systems (internal) and plugins (external, registered by name). Entities are spawned with plain functions like make_robot(...) — there are no entity OOP classes.

Documentation

The site is built from docs/source/ with pdoc (bash docs/build_docs.sh, output in public/). Highlights:

The sidebar is the docs/source/ tree, so a new markdown file appears there on the next build. Vendored packages that ship their own docs are built into public/vendored/<name>/ (microecs, microspec, micronetcode); the shared builder lives in docs/shared/.