From b735f73840223f78a8ef214b80a3b3369d2a41fc Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Wed, 4 Mar 2026 16:45:26 +0100 Subject: [PATCH 1/2] examples: add stop example --- dotbot/examples/stop.py | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 dotbot/examples/stop.py diff --git a/dotbot/examples/stop.py b/dotbot/examples/stop.py new file mode 100644 index 0000000..61b2c38 --- /dev/null +++ b/dotbot/examples/stop.py @@ -0,0 +1,44 @@ +import asyncio +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()) From 5f4c06d2a0b43ec5fe552448642118274a4d9037 Mon Sep 17 00:00:00 2001 From: Geovane Fedrecheski Date: Wed, 4 Mar 2026 17:22:17 +0100 Subject: [PATCH 2/2] chore: organize examples folder --- dotbot/examples/README.md | 35 +++++++++---------- dotbot/examples/charging_station/README.md | 30 ++++++++++++++++ dotbot/examples/charging_station/__init__.py | 21 +++++++++++ .../charging_station.py | 6 ++-- .../charging_station_init_state.toml | 0 dotbot/examples/common/__init__.py | 1 + dotbot/examples/{ => common}/orca.py | 2 +- dotbot/examples/{ => common}/sct.py | 0 dotbot/examples/{ => common}/vec2.py | 0 .../minimum_naming_game/controller.py | 2 +- .../controller_with_motion.py | 2 +- dotbot/examples/stop.py | 2 +- dotbot/examples/work_and_charge/controller.py | 2 +- .../work_and_charge/work_and_charge.py | 4 +-- .../tests/test_experiment_charging_station.py | 2 +- 15 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 dotbot/examples/charging_station/README.md create mode 100644 dotbot/examples/charging_station/__init__.py rename dotbot/examples/{ => charging_station}/charging_station.py (99%) rename dotbot/examples/{ => charging_station}/charging_station_init_state.toml (100%) create mode 100644 dotbot/examples/common/__init__.py rename dotbot/examples/{ => common}/orca.py (99%) rename dotbot/examples/{ => common}/sct.py (100%) rename dotbot/examples/{ => common}/vec2.py (100%) diff --git a/dotbot/examples/README.md b/dotbot/examples/README.md index f3a3996..d83387f 100644 --- a/dotbot/examples/README.md +++ b/dotbot/examples/README.md @@ -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. diff --git a/dotbot/examples/charging_station/README.md b/dotbot/examples/charging_station/README.md new file mode 100644 index 0000000..e65fd73 --- /dev/null +++ b/dotbot/examples/charging_station/README.md @@ -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 +``` diff --git a/dotbot/examples/charging_station/__init__.py b/dotbot/examples/charging_station/__init__.py new file mode 100644 index 0000000..d6260d3 --- /dev/null +++ b/dotbot/examples/charging_station/__init__.py @@ -0,0 +1,21 @@ +from dotbot.examples.charging_station.charging_station import DT as DT +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, +) diff --git a/dotbot/examples/charging_station.py b/dotbot/examples/charging_station/charging_station.py similarity index 99% rename from dotbot/examples/charging_station.py rename to dotbot/examples/charging_station/charging_station.py index e5dcbae..90cbdfd 100644 --- a/dotbot/examples/charging_station.py +++ b/dotbot/examples/charging_station/charging_station.py @@ -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, @@ -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( diff --git a/dotbot/examples/charging_station_init_state.toml b/dotbot/examples/charging_station/charging_station_init_state.toml similarity index 100% rename from dotbot/examples/charging_station_init_state.toml rename to dotbot/examples/charging_station/charging_station_init_state.toml diff --git a/dotbot/examples/common/__init__.py b/dotbot/examples/common/__init__.py new file mode 100644 index 0000000..6d76345 --- /dev/null +++ b/dotbot/examples/common/__init__.py @@ -0,0 +1 @@ +"""Shared helpers for DotBot examples.""" diff --git a/dotbot/examples/orca.py b/dotbot/examples/common/orca.py similarity index 99% rename from dotbot/examples/orca.py rename to dotbot/examples/common/orca.py index 8c17652..437f4e6 100644 --- a/dotbot/examples/orca.py +++ b/dotbot/examples/common/orca.py @@ -5,7 +5,7 @@ import math from dataclasses import dataclass -from dotbot.examples.vec2 import ( +from dotbot.examples.common.vec2 import ( Vec2, add, dot, diff --git a/dotbot/examples/sct.py b/dotbot/examples/common/sct.py similarity index 100% rename from dotbot/examples/sct.py rename to dotbot/examples/common/sct.py diff --git a/dotbot/examples/vec2.py b/dotbot/examples/common/vec2.py similarity index 100% rename from dotbot/examples/vec2.py rename to dotbot/examples/common/vec2.py diff --git a/dotbot/examples/minimum_naming_game/controller.py b/dotbot/examples/minimum_naming_game/controller.py index 0095d67..241ed61 100644 --- a/dotbot/examples/minimum_naming_game/controller.py +++ b/dotbot/examples/minimum_naming_game/controller.py @@ -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, diff --git a/dotbot/examples/minimum_naming_game/controller_with_motion.py b/dotbot/examples/minimum_naming_game/controller_with_motion.py index edd590f..48b65bd 100644 --- a/dotbot/examples/minimum_naming_game/controller_with_motion.py +++ b/dotbot/examples/minimum_naming_game/controller_with_motion.py @@ -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, diff --git a/dotbot/examples/stop.py b/dotbot/examples/stop.py index 61b2c38..82cba73 100644 --- a/dotbot/examples/stop.py +++ b/dotbot/examples/stop.py @@ -5,12 +5,12 @@ 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") diff --git a/dotbot/examples/work_and_charge/controller.py b/dotbot/examples/work_and_charge/controller.py index 45cf4d1..65f08b2 100644 --- a/dotbot/examples/work_and_charge/controller.py +++ b/dotbot/examples/work_and_charge/controller.py @@ -1,6 +1,6 @@ import math -from dotbot.examples.sct import SCT +from dotbot.examples.common.sct import SCT from dotbot.models import ( DotBotLH2Position, ) diff --git a/dotbot/examples/work_and_charge/work_and_charge.py b/dotbot/examples/work_and_charge/work_and_charge.py index a056841..e8b0d57 100644 --- a/dotbot/examples/work_and_charge/work_and_charge.py +++ b/dotbot/examples/work_and_charge/work_and_charge.py @@ -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, diff --git a/dotbot/tests/test_experiment_charging_station.py b/dotbot/tests/test_experiment_charging_station.py index 197bc4f..f8a06e2 100644 --- a/dotbot/tests/test_experiment_charging_station.py +++ b/dotbot/tests/test_experiment_charging_station.py @@ -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,