Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 17 additions & 18 deletions dotbot/examples/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
# DotBot Simulator Experiments
# DotBot Examples

This directory contains **experimental control scripts** for the DotBot simulator.
The goal is to prototype, test, and iterate on the testbed without needing to deploy anything,
with the same API that will run on a real testbed. **without touching the controller internals**.
This directory contains example scenarios for DotBots.
Examples can run against either real robots or the simulator, using the same controller APIs.
The simulator setup is documented as the default path because it is the most common way to reproduce experiments.
Each scenario has its own folder with dedicated instructions, initial states, and run commands.

All interaction with the simulator is done **via HTTP**, exactly like a real deployment.
## Available scenarios

---
- `minimum_naming_game/`: naming game examples (with and without motion)
- `work_and_charge/`: work/charge alternation scenario
- `charging_station/`: queue-and-charge scenario

## 1. Start the simulator
We also provide a stop.py helper script to halt the simulator (without needing to stop robots via SwarmIT).

First, start the DotBot controller in **simulator mode** with the correct configuration:
## Common usage pattern (default: simulator)

```bash
dotbot-controller \
--config-path config_sample.toml \
-a dotbot-simulator
```

## 2. Run the experiments

For example, if you want to run the charging station proof-of-concept
1. Pick a scenario and read its local `README.md`.
2. Set `simulator_init_state_path` in `config_sample.toml` as described by that scenario.
3. Start the controller in simulator mode:

```bash
python3 dotbot/examples/charging_station.py
python -m dotbot.controller_app --config-path config_sample.toml -a dotbot-simulator
```

4. Run the selected example using its documented command.
30 changes: 30 additions & 0 deletions dotbot/examples/charging_station/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Charging Station

This demo runs a charging-station scenario:
robots first form a queue, then move through charging and parking phases.
It works with real robots or with the simulator via the same controller API.
The simulator setup below is the default path for reproducibility.

## How to run (default: simulator)

### 1. Specify the initial state

Set `simulator_init_state_path` in `config_sample.toml` to:

```toml
simulator_init_state_path = "dotbot/examples/charging_station/charging_station_init_state.toml"
```

### 2. Start the controller in simulator mode

```bash
python -m dotbot.controller_app --config-path config_sample.toml -a dotbot-simulator
```

### 3. Run the charging-station scenario

From the `PyDotBot/` root in a new terminal:

```bash
python -m dotbot.examples.charging_station.charging_station
```
21 changes: 21 additions & 0 deletions dotbot/examples/charging_station/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from dotbot.examples.charging_station.charging_station import DT as DT
Copy link
Contributor

Choose a reason for hiding this comment

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

How about keeping this module empty and modify the import in the corresponding test module? I would find it more convenient as it's only a one liner, even if you have to do import from dotbot.examples.charging_station.charging_station import ....

from dotbot.examples.charging_station.charging_station import (
PARK_SPACING as PARK_SPACING,
)
from dotbot.examples.charging_station.charging_station import PARK_X as PARK_X
from dotbot.examples.charging_station.charging_station import PARK_Y as PARK_Y
from dotbot.examples.charging_station.charging_station import (
QUEUE_HEAD_X as QUEUE_HEAD_X,
)
from dotbot.examples.charging_station.charging_station import (
QUEUE_HEAD_Y as QUEUE_HEAD_Y,
)
from dotbot.examples.charging_station.charging_station import (
QUEUE_SPACING as QUEUE_SPACING,
)
from dotbot.examples.charging_station.charging_station import (
charge_robots as charge_robots,
)
from dotbot.examples.charging_station.charging_station import (
queue_robots as queue_robots,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import os
from typing import Dict, List

from dotbot.examples.orca import (
from dotbot.examples.common.orca import (
Agent,
OrcaParams,
compute_orca_velocity_for_agent,
)
from dotbot.examples.vec2 import Vec2
from dotbot.examples.common.vec2 import Vec2
from dotbot.models import (
DotBotLH2Position,
DotBotModel,
Expand Down Expand Up @@ -300,7 +300,7 @@ def preferred_vel(dotbot: DotBotModel, goal: Vec2 | None) -> Vec2:

def direction_to_rad(direction: float) -> float:
rad = (direction + 90) * math.pi / 180.0
return math.atan2(math.sin(rad), math.cos(rad)) # normalize to [-π, π]
return math.atan2(math.sin(rad), math.cos(rad)) # normalize to [-π, +π]


async def compute_orca_velocity(
Expand Down
1 change: 1 addition & 0 deletions dotbot/examples/common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Shared helpers for DotBot examples."""
2 changes: 1 addition & 1 deletion dotbot/examples/orca.py → dotbot/examples/common/orca.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import math
from dataclasses import dataclass

from dotbot.examples.vec2 import (
from dotbot.examples.common.vec2 import (
Vec2,
add,
dot,
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion dotbot/examples/minimum_naming_game/controller.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import random

from dotbot.examples.sct import SCT
from dotbot.examples.common.sct import SCT
from dotbot.models import (
DotBotLH2Position,
DotBotModel,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import math
import random

from dotbot.examples.common.sct import SCT
from dotbot.examples.minimum_naming_game.walk_avoid import walk_avoid
from dotbot.examples.sct import SCT
from dotbot.models import (
DotBotLH2Position,
DotBotModel,
Expand Down
44 changes: 44 additions & 0 deletions dotbot/examples/stop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import asyncio
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain the use case of this example?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes: when we run any of the examples in a real robot, then when (1) when we stop the example with ctrl+c or (2) the example script crashes, the robots just keep running like crazy. This stop commands make them stop, without having to call swamit stop (and then swarmit start again). Technically we could just use swarmit stop, but right now it takes like 5 seconds or more, which is annoying from a development experience PoV.

Copy link
Contributor

Choose a reason for hiding this comment

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

but could it be done in the example script directly?
in a "catch KeyboardInterrup" block, send the empty waypoint and return

import os

from dotbot.models import DotBotQueryModel, DotBotStatus, DotBotWaypoints, WSWaypoints
from dotbot.rest import rest_client
from dotbot.websocket import DotBotWsClient

"""
This example shows how to stop a dotbot swarm. Run it with:
python -m dotbot.examples.stop
"""


async def main() -> None:
url = os.getenv("DOTBOT_CONTROLLER_URL", "localhost")
port = os.getenv("DOTBOT_CONTROLLER_PORT", "8000")
use_https = os.getenv("DOTBOT_CONTROLLER_USE_HTTPS", False)

async with rest_client(url, port, use_https) as client:
dotbots = await client.fetch_dotbots(
query=DotBotQueryModel(status=DotBotStatus.ACTIVE)
)

ws = DotBotWsClient(url, port)
await ws.connect()
try:
for dotbot in dotbots:
await ws.send(
WSWaypoints(
cmd="waypoints",
address=dotbot.address,
application=dotbot.application,
data=DotBotWaypoints(
threshold=0,
waypoints=[],
),
)
)
finally:
await ws.close()


if __name__ == "__main__":
asyncio.run(main())
2 changes: 1 addition & 1 deletion dotbot/examples/work_and_charge/controller.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import math

from dotbot.examples.sct import SCT
from dotbot.examples.common.sct import SCT
from dotbot.models import (
DotBotLH2Position,
)
Expand Down
4 changes: 2 additions & 2 deletions dotbot/examples/work_and_charge/work_and_charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
import numpy as np
from scipy.spatial import cKDTree

from dotbot.examples.orca import (
from dotbot.examples.common.orca import (
Agent,
OrcaParams,
compute_orca_velocity_for_agent,
)
from dotbot.examples.vec2 import Vec2
from dotbot.examples.common.vec2 import Vec2
from dotbot.examples.work_and_charge.controller import Controller
from dotbot.models import (
DotBotLH2Position,
Expand Down
2 changes: 1 addition & 1 deletion dotbot/tests/test_experiment_charging_station.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
charge_robots,
queue_robots,
)
from dotbot.examples.orca import OrcaParams
from dotbot.examples.common.orca import OrcaParams
from dotbot.models import (
DotBotLH2Position,
DotBotModel,
Expand Down