Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,10 @@ cython_debug/
#.idea/

# Webots
.arena.wbproj
.*.wbproj
.arena.jpg

/simulator/controllers/usercode_runner/runtime.ini
/simulator/controllers/competition_supervisor/runtime.ini
/zone_*
/dist
55 changes: 1 addition & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
# sbot_simulator
A simulator built around Webots to use the sbot library virtually.

![sbot_simulator](assets/arena_overview.jpg)

### This is a work in progress
![sbot_simulator](assets/arena_overview.png)

## Installation

Expand Down Expand Up @@ -101,54 +99,3 @@ In order to keep the released simulator tidy and easy to use, the project is spl
Alongside these folders that are placed in the releases, the `assets` folder contains images and other resources that are used in the documentation. These are used to render an HTML page user facing readme that is included in the release archive.

Of the the folders that are not included in the release, the `tests` folder contains the unit and integration tests for the simulator that don't require running webots and the `test_simulator` folder contains a separate webots world that is used to test the simulator.

## Project Status

1. ~~device spinup~~
2. ~~debug logs~~
3. ~~test devices~~
4. ~~webots devices~~
5. ~~usercode runner~~
6. ~~vision~~
7. ~~arena~~
- ~~box~~
- ~~deck~~
- ~~triangle deck~~
- ~~floor texture~~
- ~~line~~
- ~~scoring lines~~
- ~~starting zones~~
8. ~~robot~~
9. ~~device jitter~~
- ~~in Webots~~
- ~~Ultrasound noise~~
- ~~Reflectance sensor noise~~
- ~~in python~~
- ~~motor noise~~
- ~~servo noise~~
10. sbot updates
1. ~~simulator discovery~~
2. ~~vision~~
3. ~~leds~~
4. ~~sleep & time~~
5. Windows startup performance
11. ~~keyboard robot~~
12. ~~setup script~~
13. ~~releases~~
14. documentation
1. ~~dev setup~~
2. user usage
3. ~~how it works~~
15. simulator tests
- vision position
- vision orientation
- distance sensor
- reflectance sensor
- bump sensor
- motor
- servo
16. ~~linting~~
17. ~~CI~~
18. report currents
19. supervisor
20. comp match running
Binary file removed assets/arena_overview.jpg
Binary file not shown.
Binary file added assets/arena_overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 10 additions & 1 deletion assets/user_readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ The API for the simulator is the same as the API for the physical robot, so you


As well as the logs being displayed in the console, they are also saved to a file.
This file is saved in the `zone_0` folder and has a name in the format `log-<date>.log`.
This file is saved in the `zone_0` folder and has a name in the format `log-zone-<zone>-<date>.log`.
The date is when that simulation was run.

### Simulation of Time
Expand Down Expand Up @@ -252,6 +252,15 @@ If you see a message saying that Python cannot be found that looks similar to th
As well as the guidance above, there are a few other points to note when using the simulator.
These can help you to understand what is happening and how to get the most out of the simulator.

### Using Other Zones

If the arena has multiple starting zones, you can run multiple robots in the simulator.
To test how your robot behaves in each starting zone of the arena, you can copy your robot's code to run in each corner.

In the folder where you extracted the simulator, alongside the `zone_0` folder, you may have other `zone_<number>` folders.
Such as `zone_1`, `zone_2`, etc.
Each of these folders can contain a `robot.py` file that will be run in the corresponding starting zone of the arena.

### Performance Optimisations

The default settings work for most users however if you are using a less powerful computer or one without a dedicated graphics card (as is the case on many laptops), you may wish to adjust the graphics settings to enable the simulation to run faster.
Expand Down
20 changes: 10 additions & 10 deletions example_robots/basic_robot.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
from sbot import Robot
from sbot import arduino, motors, utils

robot = Robot()
# robot = Robot()

robot.motor_board.motors[0].power = 1
robot.motor_board.motors[1].power = 1
motors.set_power(0, 1)
motors.set_power(1, 1)

# measure the distance of the right ultrasound sensor
# pin 6 is the trigger pin, pin 7 is the echo pin
distance = robot.arduino.ultrasound_measure(6, 7)
distance = arduino.measure_ultrasound_distance(6, 7)
print(f"Right ultrasound distance: {distance / 1000} meters")

# motor board, channel 0 to half power forward
robot.motor_board.motors[0].power = 0.5
motors.set_power(0, 0.5)

# motor board, channel 1 to half power forward,
robot.motor_board.motors[1].power = 0.5
motors.set_power(1, 0.5)
# minimal time has passed at this point,
# so the robot will appear to move forward instead of turning

# sleep for 2 second
robot.sleep(2)
utils.sleep(2)

# stop both motors
robot.motor_board.motors[0].power = 0
robot.motor_board.motors[1].power = 0
motors.set_power(0, 0)
motors.set_power(1, 0)
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
sbot==2024.0.1
sbot==2025.1.0
april_vision==2.2.0
opencv-python-headless >=4,<5
opencv-python-headless >=4.8.0.76,<5
4 changes: 4 additions & 0 deletions scripts/generate_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,12 @@
logger.info("Copying helper scripts to temp directory")
shutil.copy(project_root / "scripts/setup.py", temp_dir / "setup.py")
for script in project_root.glob("scripts/run_*.py"):
if "run_comp_" in str(script):
continue
shutil.copy(script, temp_dir)

script_dir = temp_dir / "scripts"
script_dir.mkdir()
logger.info("Copying example code to temp directory")
shutil.copytree(project_root / "example_robots", temp_dir / "example_robots")

Expand Down
116 changes: 75 additions & 41 deletions scripts/run_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,98 @@
A script to run the project in Webots.

Largely just a shortcut to running the arena world in Webots.
Only functional in releases.
"""
# ruff: noqa: E501
from __future__ import annotations

import sys
import traceback
from os.path import expandvars
from pathlib import Path
from shutil import which
from subprocess import Popen

if sys.platform == "win32":
from subprocess import CREATE_NEW_PROCESS_GROUP, DETACHED_PROCESS

try:
if not (Path(__file__).parent / 'simulator/VERSION').exists():
print("This script is only functional in releases.")
raise RuntimeError
if (Path(__file__).parent / 'simulator/VERSION').exists():
print("Running in release mode")
SIM_BASE = Path(__file__).parent.resolve()
else:
print("Running in development mode")
# Assume the script is in the scripts directory
SIM_BASE = Path(__file__).parents[1].resolve()

POSSIBLE_WEBOTS_PATHS = [
("darwin", "/Applications/Webots.app/Contents/MacOS/webots"),
("win32", "C:\\Program Files\\Webots\\msys64\\mingw64\\bin\\webotsw.exe"),
("win32", expandvars("%LOCALAPPDATA%\\Programs\\Webots\\msys64\\mingw64\\bin\\webotsw.exe")),
# Attempt to use the start menu shortcut
("win32", expandvars("%ProgramData%\\Microsoft\\Windows\\Start Menu\\Programs\\Cyberbotics\\Webots.lnk")),
("win32", expandvars("%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Cyberbotics\\Webots.lnk")),
("linux", "/usr/local/bin/webots"),
("linux", "/usr/bin/webots"),
]


def get_webots_parameters() -> tuple[Path, Path]:
"""
Get the paths to the Webots executable and the arena world file.

world_file = Path(__file__).parent / "simulator/worlds/arena.wbt"
:return: The paths to the Webots executable and the arena world file
"""
world_file = SIM_BASE / "simulator/worlds/arena.wbt"

if not world_file.exists():
raise RuntimeError("World file not found.")

if not (SIM_BASE / "venv").exists():
raise RuntimeError("Please run the setup.py script before running the simulator.")

# Check if Webots is in the PATH
webots = which("webots")

# Find the webots executable, if it is not in the PATH
if webots is None:
if sys.platform == "darwin":
webots = "/Applications/Webots.app/Contents/MacOS/webots"
elif sys.platform == "win32":
webots = "C:\\Program Files\\Webots\\msys64\\mingw64\\bin\\webotsw.exe"
elif sys.platform.startswith("linux"):
possible_paths = ["/usr/local/bin/webots", "/usr/bin/webots"]
for path in possible_paths:
for system_filter, path in POSSIBLE_WEBOTS_PATHS:
if sys.platform.startswith(system_filter):
print(f"Checking {path}")
if Path(path).exists():
webots = path
break
else:
print("Webots executable not found.")
raise RuntimeError

if webots is None or not Path(webots).exists():
raise RuntimeError("Webots executable not found.")

return Path(webots), world_file


def main() -> None:
"""Run the project in Webots."""
try:
webots, world_file = get_webots_parameters()

# Run the world file in Webots,
# detaching the process so it does not close when this script does
if sys.platform == "win32":
Popen(
[str(webots), str(world_file)],
creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
# shell=True is needed to run from shortcuts
shell=(webots.suffix == ".lnk"),
)
else:
print("Unsupported platform.")
raise RuntimeError

if not Path(webots).exists():
print("Webots executable not found.")
raise RuntimeError

if not (Path(__file__).parent / "venv").exists():
print("Please run the setup.py script before running the simulator.")
raise RuntimeError

# Run the world file in Webots,
# detaching the process so it does not close when this script does
if sys.platform == "win32":
Popen([webots, world_file], creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
else:
Popen([webots, world_file], start_new_session=True)
except RuntimeError:
input("Press enter to continue...")
exit(1)
except Exception as e:
print(f"An error occurred: {e}")
print(traceback.format_exc())
input("Press enter to continue...")
exit(1)
Popen([str(webots), str(world_file)], start_new_session=True)
except RuntimeError as e:
print(f"An error occurred: {e}")
input("Press enter to continue...")
exit(1)
except Exception as e:
print(f"An error occurred: {e}")
print(traceback.format_exc())
input("Press enter to continue...")
exit(1)


if __name__ == "__main__":
main()
Loading
Loading