From fa4c0c3f932561e1c6e87a2d1a63f3f3b913231a Mon Sep 17 00:00:00 2001 From: WillB97 Date: Mon, 18 Nov 2024 23:04:31 +0000 Subject: [PATCH 01/13] Log errors in subprocesses --- scripts/setup.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/setup.py b/scripts/setup.py index bbf1d30..b5776a0 100755 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -15,7 +15,7 @@ import shutil import sys from pathlib import Path -from subprocess import run +from subprocess import SubprocessError, check_call from venv import create logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(levelname)s: %(message)s") @@ -86,11 +86,11 @@ def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: else: pip = venv_dir / "bin/pip" venv_python = venv_dir / "bin/python" - run( + check_call( [str(venv_python), "-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel"], cwd=venv_dir, ) - run([str(pip), "install", "-r", str(requirements)], cwd=venv_dir) + check_call([str(pip), "install", "-r", str(requirements)], cwd=venv_dir) logger.info("Setting up Webots Python location") @@ -106,6 +106,9 @@ def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: logger.info("Repopulating zone 0 with example code") zone_0.mkdir(exist_ok=True) shutil.copy(project_root / "example_robots/basic_robot.py", zone_0 / "robot.py") +except SubprocessError: + logger.error("Setup failed due to an error.") + input("An error occurred, press enter to close.") except Exception: logger.exception("Setup failed due to an error.") input("An error occurred, press enter to close.") From 55b964ac2cb0cae875b4a0ef66ec38ac616c3322 Mon Sep 17 00:00:00 2001 From: WillB97 Date: Tue, 2 Sep 2025 20:22:53 +0100 Subject: [PATCH 02/13] Correct servo status return --- simulator/modules/sbot_interface/boards/servo_board.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simulator/modules/sbot_interface/boards/servo_board.py b/simulator/modules/sbot_interface/boards/servo_board.py index 536f623..0aa922e 100644 --- a/simulator/modules/sbot_interface/boards/servo_board.py +++ b/simulator/modules/sbot_interface/boards/servo_board.py @@ -44,7 +44,7 @@ def handle_command(self, command: str) -> str: if args[0] == '*IDN?': return f'Student Robotics:SBv4B:{self.asset_tag}:{self.software_version}' elif args[0] == '*STATUS?': - return f"{self.watchdog_fail}:{self.pgood}" + return f"{self.watchdog_fail:d}:{self.pgood:d}" elif args[0] == '*RESET': LOGGER.info(f'Resetting servo board {self.asset_tag}') for servo in self.servos: From 12e107e3a2ce0481985e4ea1221d63709f524bbb Mon Sep 17 00:00:00 2001 From: WillB97 Date: Tue, 2 Sep 2025 20:23:17 +0100 Subject: [PATCH 03/13] Fix custom pulse support --- simulator/modules/sbot_interface/devices/servo.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/simulator/modules/sbot_interface/devices/servo.py b/simulator/modules/sbot_interface/devices/servo.py index 92e7e07..a71f040 100644 --- a/simulator/modules/sbot_interface/devices/servo.py +++ b/simulator/modules/sbot_interface/devices/servo.py @@ -20,8 +20,10 @@ if TYPE_CHECKING: from controller import PositionSensor -MAX_POSITION = 2000 -MIN_POSITION = 1000 +MAX_POSITION = 4000 +MIN_POSITION = 500 +SERVO_MAX = 2000 +SERVO_MIN = 1000 class BaseServo(ABC): @@ -98,7 +100,7 @@ class Servo(BaseServo): """A servo connected to the Servo board.""" def __init__(self, device_name: str) -> None: - self.position = (MAX_POSITION + MIN_POSITION) // 2 + self.position = (SERVO_MAX + SERVO_MIN) // 2 # TODO use setAvailableForce to simulate disabled self._enabled = False g = get_globals() @@ -121,11 +123,11 @@ def set_position(self, value: int) -> None: """ # Apply a small amount of variation to the power setting to simulate # inaccuracies in the servo - value = int(add_jitter(value, (MIN_POSITION, MAX_POSITION), std_dev_percent=0.5)) + value = int(add_jitter(value, (SERVO_MIN, SERVO_MAX), std_dev_percent=0.5)) self._device.setPosition(map_to_range( value, - (MIN_POSITION, MAX_POSITION), + (SERVO_MIN, SERVO_MAX), (self._min_position + 0.001, self._max_position - 0.001), )) self.position = value From 7fa43f0e0ac0c972363be959a7724134f0d09d60 Mon Sep 17 00:00:00 2001 From: WillB97 Date: Tue, 2 Sep 2025 20:33:57 +0100 Subject: [PATCH 04/13] Remove unused robots in dev mode --- .../competition_supervisor/competition_supervisor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/simulator/controllers/competition_supervisor/competition_supervisor.py b/simulator/controllers/competition_supervisor/competition_supervisor.py index 6eafd23..08a7f26 100644 --- a/simulator/controllers/competition_supervisor/competition_supervisor.py +++ b/simulator/controllers/competition_supervisor/competition_supervisor.py @@ -226,6 +226,8 @@ def run_match( def main() -> None: """Run the competition supervisor.""" if is_dev_mode(): + robots = Robots() + robots.remove_unoccupied_robots() exit() match_data = get_match_data() From 7811b0955818ee73dd834eeab8a55a7e7c8103af Mon Sep 17 00:00:00 2001 From: WillB97 Date: Tue, 2 Sep 2025 20:48:33 +0100 Subject: [PATCH 05/13] Fail setup where wheels are not available --- scripts/setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/setup.py b/scripts/setup.py index b5776a0..847157f 100755 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -90,7 +90,10 @@ def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: [str(venv_python), "-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel"], cwd=venv_dir, ) - check_call([str(pip), "install", "-r", str(requirements)], cwd=venv_dir) + check_call( + [str(pip), "install", "--only-binary=:all:", "-r", str(requirements)], + cwd=venv_dir, + ) logger.info("Setting up Webots Python location") From 533d0705a3d7b29a045820631c9961c98af91f06 Mon Sep 17 00:00:00 2001 From: WillB97 Date: Wed, 3 Sep 2025 21:38:57 +0100 Subject: [PATCH 06/13] New dependencies for Python 3.13 --- requirements.txt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/requirements.txt b/requirements.txt index ff61e2b..39ec17c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,15 @@ -sr-robot3==2025.0.1 -april_vision==2.2.0 -opencv-python-headless >=4.8.0.76,<5 +sr-robot3 @ git+https://github.com/srobo/sr-robot3@sbot_simulator # TODO update +april_vision==3.0.0 +opencv-python-headless >=4.10,<5 -# Library versions are selected to provide support for Python 3.9-3.12 +# Library versions are selected to provide support for Python 3.10-3.13 flask==2.3.3 -matplotlib==3.9.2 +matplotlib==3.10.6 networkx==3.1 -numpy==1.26.4 -pandas==2.2.3 -pillow==11.0.0 -scikit-learn==1.3.1 -scipy==1.11.2 -shapely==2.0.6 +numpy>=2.2.6,<=2.3.2 +pandas==2.3.2 # no 3.14 support +pillow==11.3.0 +scikit-learn==1.7.1 # no 3.14 support +scipy>=1.15.3,<=1.16.1 +shapely==2.1.1 # no 3.14 support From a8777321616f69b7ea5292b399b3cefd9fe30277 Mon Sep 17 00:00:00 2001 From: WillB97 Date: Wed, 3 Sep 2025 21:49:45 +0100 Subject: [PATCH 07/13] Colour setup text --- scripts/setup.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/setup.py b/scripts/setup.py index 847157f..e595bea 100755 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -21,6 +21,10 @@ logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(levelname)s: %(message)s") logger = logging.getLogger(__name__) +BOLD_RED = '\x1b[31;1m' +GREEN = '\x1b[32;20m' +RESET_COLOUR = '\x1b[0m' + def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: """ @@ -110,10 +114,12 @@ def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: zone_0.mkdir(exist_ok=True) shutil.copy(project_root / "example_robots/basic_robot.py", zone_0 / "robot.py") except SubprocessError: + print(BOLD_RED) logger.error("Setup failed due to an error.") - input("An error occurred, press enter to close.") + input(f"An error occurred, press enter to close.{RESET_COLOUR}") except Exception: + print(BOLD_RED) logger.exception("Setup failed due to an error.") - input("An error occurred, press enter to close.") + input(f"An error occurred, press enter to close.{RESET_COLOUR}") else: - input("Setup complete, press enter to close.") + input(f"{GREEN}Setup complete, press enter to close.{RESET_COLOUR}") From 7884e42c4eda4dbb94ecfc11c071a97b1567a2e7 Mon Sep 17 00:00:00 2001 From: WillB97 Date: Wed, 3 Sep 2025 21:54:35 +0100 Subject: [PATCH 08/13] Preload opencv at setup Avoids a long hang when first running the simulator --- scripts/setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/setup.py b/scripts/setup.py index e595bea..d79e0f6 100755 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -99,6 +99,9 @@ def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: cwd=venv_dir, ) + logger.info("Preloading OpenCV") + check_call([str(venv_python), "-c", "import cv2"], cwd=venv_dir) + logger.info("Setting up Webots Python location") controllers_dir = project_root / "simulator/controllers" From fb1fde590ed14ba57309a7b8f0d71a7817634c9f Mon Sep 17 00:00:00 2001 From: WillB97 Date: Wed, 3 Sep 2025 22:11:21 +0100 Subject: [PATCH 09/13] Validate sr-robot3 is installed before loading webots --- scripts/run_simulator.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/scripts/run_simulator.py b/scripts/run_simulator.py index e4a6a7d..862291e 100755 --- a/scripts/run_simulator.py +++ b/scripts/run_simulator.py @@ -12,7 +12,11 @@ from os.path import expandvars from pathlib import Path from shutil import which -from subprocess import Popen +from subprocess import Popen, check_output + +BOLD_RED = '\x1b[31;1m' +RESET_COLOUR = '\x1b[0m' + if sys.platform == "win32": from subprocess import CREATE_NEW_PROCESS_GROUP, DETACHED_PROCESS @@ -51,6 +55,16 @@ def get_webots_parameters() -> tuple[Path, Path]: if not (SIM_BASE / "venv").exists(): raise RuntimeError("Please run the setup.py script before running the simulator.") + # Check the venv contains sr-robot3 + venv_dir = SIM_BASE / "venv" + if sys.platform == "win32": + pip = venv_dir / "Scripts/pip.exe" + else: + pip = venv_dir / "bin/pip" + packages = check_output([pip, 'freeze'], text=True) + if 'sr-robot3' not in packages: + raise RuntimeError("sr-robot3 is not installed. Please re-run the setup.py script.") + # Check if Webots is in the PATH webots = which("webots") @@ -86,13 +100,15 @@ def main() -> None: else: 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...") + print(BOLD_RED) + print(f"An error occurred: \n{e}") + input(f"Press enter to continue...{RESET_COLOUR}") exit(1) except Exception as e: + print(BOLD_RED) print(f"An error occurred: {e}") print(traceback.format_exc()) - input("Press enter to continue...") + input(f"Press enter to continue...{RESET_COLOUR}") exit(1) From 001b193887f23268e96c44ced30beace625d1522 Mon Sep 17 00:00:00 2001 From: WillB97 Date: Fri, 5 Sep 2025 20:30:05 +0100 Subject: [PATCH 10/13] Bump sr-robot3 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 39ec17c..68d87cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -sr-robot3 @ git+https://github.com/srobo/sr-robot3@sbot_simulator # TODO update +sr-robot3==2026.0.1 april_vision==3.0.0 opencv-python-headless >=4.10,<5 From 4d8f2a489c4af74dfbee2a70195d3e17f981aa45 Mon Sep 17 00:00:00 2001 From: WillB97 Date: Sat, 6 Sep 2025 09:59:24 +0100 Subject: [PATCH 11/13] Bump webots test version Update python versions --- .github/workflows/check.yml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index bdfda36..ff3070e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -4,22 +4,25 @@ on: push: workflow_dispatch: +env: + WEBOTS_VER: "2025a" + jobs: test: strategy: fail-fast: false matrix: os: [ubuntu-latest] - py_version: ["3.8", "3.9", "3.10", "3.11"] + py_version: ["3.10", "3.11", "3.12", "3.13"] include: - os: windows-latest - py_version: "3.8" + py_version: "3.10" - os: windows-latest - py_version: "3.11" + py_version: "3.13" - os: macos-latest - py_version: "3.8" + py_version: "3.10" - os: macos-latest - py_version: "3.11" + py_version: "3.13" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -36,8 +39,9 @@ jobs: - name: Download & install Webots (Linux) if: runner.os == 'Linux' run: | + sudo apt-get update wget -O ./webots.deb \ - https://github.com/cyberbotics/webots/releases/download/R2023b/webots_2023b_amd64.deb + https://github.com/cyberbotics/webots/releases/download/R$WEBOTS_VER/webots_${WEBOTS_VER}_amd64.deb sudo apt-get install --yes ./webots.deb - name: Download & extract Webots (Windows) # Windows install was problematic so we just extract the python library for typehints @@ -46,14 +50,14 @@ jobs: run: | mkdir stubs C:\\msys64\\usr\\bin\\wget.exe -O ./webots.tar.bz2 \ - https://github.com/cyberbotics/webots/releases/download/R2023b/webots-R2023b-x86-64.tar.bz2 + https://github.com/cyberbotics/webots/releases/download/R$WEBOTS_VER/webots-R$WEBOTS_VER-x86-64.tar.bz2 C:\\msys64\\usr\\bin\\tar.exe xvf webots.tar.bz2 --strip-components=4 \ --directory=stubs webots/lib/controller/python/controller - name: Download & install Webots (macOS) if: runner.os == 'macOS' run: | wget -O ./webots.dmg \ - https://github.com/cyberbotics/webots/releases/download/R2023b/webots-R2023b.dmg + https://github.com/cyberbotics/webots/releases/download/R$WEBOTS_VER/webots-R$WEBOTS_VER.dmg hdiutil mount ./webots.dmg sudo cp -R /Volumes/Webots/Webots.app /Applications hdiutil unmount /Volumes/Webots From 215a2ae21e7c87ced01f978d9258d50f4d67dce8 Mon Sep 17 00:00:00 2001 From: WillB97 Date: Sat, 6 Sep 2025 10:37:43 +0100 Subject: [PATCH 12/13] Rework detecting the venv has the libraries --- scripts/run_simulator.py | 14 ++++---------- scripts/setup.py | 6 ++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/run_simulator.py b/scripts/run_simulator.py index 862291e..fe311fc 100755 --- a/scripts/run_simulator.py +++ b/scripts/run_simulator.py @@ -12,7 +12,7 @@ from os.path import expandvars from pathlib import Path from shutil import which -from subprocess import Popen, check_output +from subprocess import Popen BOLD_RED = '\x1b[31;1m' RESET_COLOUR = '\x1b[0m' @@ -55,15 +55,9 @@ def get_webots_parameters() -> tuple[Path, Path]: if not (SIM_BASE / "venv").exists(): raise RuntimeError("Please run the setup.py script before running the simulator.") - # Check the venv contains sr-robot3 - venv_dir = SIM_BASE / "venv" - if sys.platform == "win32": - pip = venv_dir / "Scripts/pip.exe" - else: - pip = venv_dir / "bin/pip" - packages = check_output([pip, 'freeze'], text=True) - if 'sr-robot3' not in packages: - raise RuntimeError("sr-robot3 is not installed. Please re-run the setup.py script.") + # Check setup finish successfully + if not (SIM_BASE / "venv/setup_success").exists(): + raise RuntimeError("Setup has not completed successfully. Please re-run the setup.py script.") # Check if Webots is in the PATH webots = which("webots") diff --git a/scripts/setup.py b/scripts/setup.py index d79e0f6..4d69eff 100755 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -80,6 +80,9 @@ def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: venv_dir = project_root / "venv" + # Reset success flag + (venv_dir / "setup_success").unlink(missing_ok=True) + logger.info(f"Creating virtual environment in {venv_dir.absolute()}") create(venv_dir, with_pip=True) @@ -110,6 +113,9 @@ def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: populate_python_config(usercode_ini, venv_python) populate_python_config(supervisor_ini, venv_python) + # Mark that we succeeded + (venv_dir / "setup_success").touch() + # repopulate zone 0 with example code if robot.py is missing zone_0 = project_root / "zone_0" if not (zone_0 / "robot.py").exists(): From c9a57d8187d7e614d5c2a429f88eb433801584e7 Mon Sep 17 00:00:00 2001 From: WillB97 Date: Sat, 6 Sep 2025 10:38:18 +0100 Subject: [PATCH 13/13] Also preload sr-robot3 --- scripts/setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/setup.py b/scripts/setup.py index 4d69eff..db75d0a 100755 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -102,8 +102,8 @@ def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: cwd=venv_dir, ) - logger.info("Preloading OpenCV") - check_call([str(venv_python), "-c", "import cv2"], cwd=venv_dir) + logger.info("Preloading OpenCV & sr.robot3") + check_call([str(venv_python), "-c", "import cv2;import sr.robot3"], cwd=venv_dir) logger.info("Setting up Webots Python location")