Skip to content

staszewski/swarm-simulator

Repository files navigation

SwarmSim

Formation demo

SwarmSim is a TypeScript-first multi-drone coordination simulator focused on independent drone processes, real message-bus communication, network constraints, and live visualization. Includes simulators written in Typescript and Rust.

Packages

  • packages/shared: shared domain types, Redis message contracts, and channel helpers.
  • packages/kinematics: pure movement math shared by agents and future behaviors.
  • packages/behaviors: pure deterministic swarm behavior helpers, starting with formation data types and ENU leader-relative formation math.
  • packages/agent: drone process runtime, command handling, telemetry publishing, state broadcast publishing, inbox delivery handling, and inbox-derived neighbor caching.
  • packages/bus: Redis connection helpers for telemetry, broadcast, command, inbox, and subscriber flows.
  • packages/cli: CLI for operator commands.
  • packages/netsim: network simulation service that subscribes to drone telemetry, maintains latest-position state, and forwards swarm broadcasts to tracked drone inbox channels.
  • crates/netsim-rs: same network simulation as packages/netsim.
  • packages/viz-server: WebSocket bridge from Redis telemetry to browser clients.
  • packages/viz: React/Vite canvas visualization frontend.

Scripts

  • pnpm build: build all packages.
  • pnpm typecheck: typecheck all packages.
  • pnpm dev:agent: run the agent placeholder.
  • pnpm dev:cli: run the CLI placeholder.
  • pnpm dev:netsim: run the network simulation service telemetry cache and broadcast forwarder.
  • pnpm dev:viz-server: run the Redis-to-WebSocket visualization bridge.
  • pnpm dev:viz: run the React visualization frontend.
  • pnpm redis: start Redis with Docker Compose.

Docker Compose Demo

docker compose -f infra/docker-compose.yml up --build

Then open http://localhost:5173. Compose starts Redis, one drone-1 agent, the Redis-to-WebSocket visualization server on port 8080, and the Vite frontend on port 5173.

With the demo running, send a goto command from the host:

pnpm dev:cli -- send drone-1 goto 80 40

Start the Rust netsim Docker stack with Redis and two TypeScript agents:

docker compose -f infra/docker-compose.rust-netsim.yml up --build

This keeps the TypeScript netsim demos unchanged. The Rust service runs with REDIS_URL=redis://redis:6379 inside Compose. Optional netsim settings can be passed through from the host, for example:

NETSIM_LATENCY_MS=500 docker compose -f infra/docker-compose.rust-netsim.yml up --build

Start the formation demo with Redis, netsim, one leader, four followers, the visualization server, and the browser frontend:

pnpm demo:formation

Then open http://localhost:5173. The formation demo starts all drones stationary. Followers use FORMATION_SPEED_METERS_PER_SECOND=3 for faster recording; set the leader command speed before sending the waypoint:

pnpm dev:cli -- send drone-1 setSpeed 3
pnpm dev:cli -- send drone-1 goto 80 50

Start the range-limited formation variant to show that followers only react to messages netsim actually delivers:

pnpm demo:formation:range

To run the same range-limited formation demo with Rust netsim instead of the TypeScript netsim service:

docker compose -f infra/docker-compose.formation-range-rust-netsim.yml up --build

This uses NETSIM_BROADCAST_RANGE_METERS=25. drone-5 starts far away at (10, 90), outside leader broadcast range, so it should remain idle while the closer followers react to the leader. The demo sends its own command sequence after startup: setSpeed 3, goto 80 50, wait 3 seconds, then goto 0 50. To replay it manually against an already-running stack:

pnpm dev:cli -- send drone-1 setSpeed 3
pnpm dev:cli -- send drone-1 goto 80 50
sleep 3
pnpm dev:cli -- send drone-1 goto 0 50

Agent Configuration

pnpm dev:agent reads these optional environment variables:

  • REDIS_URL: Redis connection URL. Defaults to redis://localhost:6379.
  • DRONE_ID: drone ID in the drone-<number> format. Defaults to drone-1.
  • TICK_DURATION_MS: runtime tick duration. Defaults to 100.
  • WORLD_WIDTH / WORLD_HEIGHT: world bounds in meters. Defaults to 100 by 100.
  • INITIAL_X / INITIAL_Y: initial position in meters. Defaults to 10, 20.
  • INITIAL_VX / INITIAL_VY: initial velocity in meters per second. Defaults to 1, 0.
  • INITIAL_HEADING_RADIANS: initial heading. Defaults to 0.
  • FORMATION_ROLE: optional formation mode. Set to follower to enable local formation steering, leader to identify a command-driven leader, or leave unset/none to disable formation runtime behavior.
  • FORMATION_LEADER_ID: leader drone ID used by followers when deriving leader pose from received inbox broadcasts. Defaults to drone-1.
  • FORMATION_SLOT_OFFSET_X / FORMATION_SLOT_OFFSET_Y: optional leader-relative slot offset for this agent, in meters. Used when FORMATION_SLOT_MAP_JSON is unset.
  • FORMATION_SLOT_MAP_JSON: optional JSON object mapping drone IDs to slot offsets, for example {"drone-2":{"x":-10,"y":-5}}. Defaults to a small V-shaped map for drone-2 through drone-5.
  • FORMATION_SEPARATION_RADIUS_METERS: local crowding radius in meters. Active neighbors inside this radius push the follower away. Defaults to 6, which is below the built-in 10 m first-row slot spacing but leaves room to avoid obvious overlap.
  • FORMATION_SPEED_METERS_PER_SECOND: follower movement speed after the combined formation vector is normalized. Defaults to 1, matching the single-drone demo speed and keeping convergence easy to observe.
  • FORMATION_WEIGHT_SEPARATION: strength of local repulsion from nearby active neighbors. Defaults to 1.2, high enough to resist crowding without overpowering a valid slot target.
  • FORMATION_WEIGHT_ALIGNMENT: strength of steering toward the average active-neighbor heading. Defaults to 0 for the current leader-slot mode so followers settle at their assigned slots. Raise this only when intentionally experimenting with heading matching.
  • FORMATION_WEIGHT_COHESION: strength of steering toward the active-neighbor centroid. Defaults to 0 for the current leader-slot mode because cohesion can keep pulling a correctly placed follower away from its slot.
  • FORMATION_WEIGHT_LEADER_SLOT_ATTRACTION: strength of steering toward this follower's leader-relative slot. Defaults to 2, making slot convergence the dominant formation-control term.

Example:

DRONE_ID=drone-2 INITIAL_X=30 INITIAL_Y=40 INITIAL_VX=0 INITIAL_VY=0 pnpm dev:agent

Manual Formation Demo

The current formation mode is opt-in. pnpm demo:formation starts the same 5-drone setup in Docker. To run the pieces manually instead, use these commands in separate terminals from the repository root for a repeatable local 2-5 drone V-shape demo.

Start Redis:

pnpm redis

Start netsim so drone broadcasts are forwarded to inboxes:

pnpm dev:netsim

Start the leader:

DRONE_ID=drone-1 INITIAL_X=50 INITIAL_Y=50 INITIAL_VX=0 INITIAL_VY=0 FORMATION_ROLE=leader pnpm dev:agent

Start one or more followers. The default slot map supports drone-2 through drone-5:

DRONE_ID=drone-2 INITIAL_X=35 INITIAL_Y=45 INITIAL_VX=0 INITIAL_VY=0 FORMATION_ROLE=follower pnpm dev:agent
DRONE_ID=drone-3 INITIAL_X=35 INITIAL_Y=55 INITIAL_VX=0 INITIAL_VY=0 FORMATION_ROLE=follower pnpm dev:agent
DRONE_ID=drone-4 INITIAL_X=25 INITIAL_Y=40 INITIAL_VX=0 INITIAL_VY=0 FORMATION_ROLE=follower pnpm dev:agent
DRONE_ID=drone-5 INITIAL_X=25 INITIAL_Y=60 INITIAL_VX=0 INITIAL_VY=0 FORMATION_ROLE=follower pnpm dev:agent

Send a waypoint to the leader:

pnpm dev:cli -- send drone-1 goto 80 50

Expected behavior: the leader moves under the normal goto command path while followers use only forwarded broadcasts in their inbox caches to chase their assigned leader-relative slots. With the default heading convention, the initial east-facing V slots are behind the leader at (-10, -5), (-10, 5), (-20, -10), and (-20, 10) meters. Followers stop if they have no active leader broadcast or no configured slot.

Troubleshooting:

  • Followers that do not move usually are not receiving forwarded broadcasts. Confirm pnpm dev:netsim is running and Redis is shared by all terminals.
  • In the range-limited Docker demo, drone-5 is expected not to move because it starts outside the configured 25 m broadcast range.
  • Duplicate agent IDs are rejected by the Redis identity lease. Stop the old process or choose a different DRONE_ID.

Netsim Configuration

pnpm dev:netsim reads network simulation settings from built-in defaults, an optional JSON config file, and explicit environment variables. Precedence is: built-in defaults, then file values, then environment variables.

  • REDIS_URL: Redis connection URL. Defaults to redis://localhost:6379.
  • NETSIM_CONFIG_PATH: optional path to a netsim JSON config file. When absent, netsim uses netsim.config.json from the current working directory if that file exists; otherwise it runs from defaults and environment variables only. When set, the exact file must exist and contain valid JSON.
  • NETSIM_BROADCAST_RANGE_METERS: maximum Euclidean ENU distance in meters between sender and recipient latest telemetry positions for broadcast delivery. Defaults to unbounded range.
  • NETSIM_PACKET_LOSS_RATE: per-recipient probability from 0 through 1 that an otherwise eligible forwarded broadcast delivery is dropped. Defaults to 0.
  • NETSIM_LATENCY_MS: delay in milliseconds before publishing each eligible, non-dropped forwarded inbox delivery. Defaults to 0.
  • NETSIM_DEBUG_TELEMETRY: set to 1 to log a periodic count of tracked drones.
  • NETSIM_DEBUG_TELEMETRY_INTERVAL_MS: telemetry summary interval. Defaults to 5000 when debug telemetry logging is enabled.

Optional netsim.config.json shape:

{
  "broadcastRangeMeters": 50,
  "packetLossRate": 0.1,
  "latencyMs": 250
}

All fields are optional. broadcastRangeMeters and latencyMs must be finite numbers greater than or equal to 0; packetLossRate must be a finite number from 0 through 1; unknown fields are rejected. The network config file is watched while netsim is running. Valid changes affect future forwarded broadcasts without restarting Redis subscriptions. Invalid changes or deletion keep the last valid network config and log a warning. Already scheduled delayed deliveries keep the latency they were scheduled with.

Scenario Configuration

Scenario launch configuration is JSON and is validated by @swarm-sim/shared. It describes launch-time inputs only:

{
  "schemaVersion": 1,
  "format": "json",
  "world": { "width": 100, "height": 100 },
  "drones": [
    {
      "id": "drone-1",
      "initialPosition": { "x": 10, "y": 20 },
      "initialVelocity": { "x": 0, "y": 0 },
      "initialHeadingRadians": 0
    }
  ]
}

initialVelocity and initialHeadingRadians are optional. The ENU coordinate convention is unchanged: x is east/right, y is north/up, (0, 0) is bottom-left, heading 0 faces +x/east, and heading Math.PI / 2 faces +y/north.

CLI Commands

List drones seen in recent telemetry:

pnpm dev:cli -- list

Send a goto command to a running drone agent:

pnpm dev:cli -- send drone-1 goto 80 40

Stop or kill a running drone through the existing command channel:

pnpm dev:cli -- send drone-1 stop
pnpm dev:cli -- send drone-1 kill

The CLI publishes commands to drone.<id>.command using REDIS_URL, defaulting to redis://localhost:6379.

Spawn one local agent process with optional initial state:

pnpm dev:cli -- spawn drone-2 --x 30 --y 40 --vx 0 --vy 0 --heading 0

The spawn command preserves REDIS_URL and maps the options to the same agent environment variables documented above. Omitted options use agent defaults.

Launch one local agent process per drone from a scenario file:

pnpm redis
pnpm dev:cli -- launch scenarios/two-drones.json

The launcher validates the scenario with @swarm-sim/shared, preserves REDIS_URL, and maps each drone's initial state to the agent environment.

About

TypeScript/Rust multi-drone coordination simulator with independent drone processes, Redis message-bus communication, network simulation, formation behaviors, and live visualization. Built for experimenting with swarm behavior and distributed robotics systems.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages