Skip to content
This repository was archived by the owner on Oct 8, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
22 changes: 11 additions & 11 deletions controllers/example_controller/keyboard_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,20 @@ def print_sensors(robot: Robot) -> None:
boost = True

if key_ascii == key_forward:
left_power += 50
right_power += 50
left_power += 0.5
right_power += 0.5

elif key_ascii == key_reverse:
left_power += -50
right_power += -50
left_power += -0.5
right_power += -0.5

elif key_ascii == key_left:
left_power -= 25
right_power += 25
left_power -= 0.25
right_power += 0.25

elif key_ascii == key_right:
left_power += 25
right_power -= 25
left_power += 0.25
right_power -= 0.25

elif key_ascii == key_sense:
print_sensors(R)
Expand All @@ -111,9 +111,9 @@ def print_sensors(robot: Robot) -> None:
key = keyboard.getKey()

if boost:
# double power values but constrain to [-100, 100]
left_power = max(min(left_power * 2, 100), -100)
right_power = max(min(right_power * 2, 100), -100)
# double power values but constrain to [-1, 1]
left_power = max(min(left_power * 2, 1), -1)
right_power = max(min(right_power * 2, 1), -1)

R.motor_board.motors[0].power = left_power
R.motor_board.motors[1].power = right_power
Expand Down
16 changes: 13 additions & 3 deletions modules/sr/robot3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
MARKER_TOKEN_GOLD,
MARKER_TOKEN_SILVER,
)
from sr.robot3.ruggeduino import INPUT, OUTPUT, AnaloguePin, INPUT_PULLUP
from sr.robot3.metadata import RobotMode
from sr.robot3.ruggeduino import AnaloguePin, GPIOPinMode

OUT_H0 = Outputs.OUT_H0
OUT_H1 = Outputs.OUT_H1
OUT_L0 = Outputs.OUT_L0
OUT_L1 = Outputs.OUT_L1
OUT_L2 = Outputs.OUT_L2
OUT_L3 = Outputs.OUT_L3
OUT_FIVE_VOLT = Outputs.OUT_FIVE_VOLT

A0 = AnaloguePin.A0
A1 = AnaloguePin.A1
Expand All @@ -24,13 +25,22 @@
A4 = AnaloguePin.A4
A5 = AnaloguePin.A5

COMP = RobotMode.COMP
DEV = RobotMode.DEV

OUTPUT = GPIOPinMode.DIGITAL_OUTPUT
INPUT = GPIOPinMode.DIGITAL_INPUT
INPUT_PULLUP = GPIOPinMode.DIGITAL_INPUT_PULLUP


__all__ = (
'OUTPUT',
'INPUT',
'INPUT_PULLUP',
'COAST',
'BRAKE',
'COMP',
'DEV',
'Robot',
'Note',
'A0',
Expand All @@ -43,8 +53,8 @@
'OUT_H1',
'OUT_L0',
'OUT_L1',
'OUT_L2',
'OUT_L3',
'OUT_FIVE_VOLT',
'MarkerType',
'MARKER_ARENA',
'MARKER_TOKEN_GOLD',
Expand Down
2 changes: 1 addition & 1 deletion modules/sr/robot3/_version_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# Provide a nicer check that the user has the right version of Python than
# them getting a `SyntaxError`
assert sys.version_info >= (3, 7), (
assert sys.version_info >= (3, 8), (
"Sorry, you must be using a recent version of Python 3. "
"Please see the SR docs for how to switch your Python version."
)
26 changes: 22 additions & 4 deletions modules/sr/robot3/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
from enum import Enum
from typing import NamedTuple

from controller import Robot
from controller import Robot, Camera as WebotCamera
from sr.robot3.vision import Face, Orientation, tokens_from_objects
from sr.robot3.coordinates import Point

from .utils import maybe_get_robot_device

MARKER_MODEL_RE = re.compile(r"^[AGS]\d{0,2}$")


Expand Down Expand Up @@ -129,17 +131,17 @@ def orientation(self) -> Orientation:


class Camera:
def __init__(self, webot: Robot, lock: threading.Lock) -> None:
def __init__(self, webot: Robot, camera: WebotCamera, lock: threading.Lock) -> None:
self._webot = webot
self._timestep = int(webot.getBasicTimeStep())

self.camera = webot.getCamera("camera")
self.camera = camera
self.camera.enable(self._timestep)
self.camera.recognitionEnable(self._timestep)

self._lock = lock

def see(self) -> list[Marker]:
def see(self, *, eager: bool = True) -> list[Marker]:
"""
Identify items which the camera can see and return a list of `Marker`
instances describing them.
Expand Down Expand Up @@ -181,3 +183,19 @@ def _see(self) -> list[Marker]:
markers.append(Marker(face, marker_info, when))

return markers

def see_ids(self) -> list[int]:
# While in theory this method ought to be the "fast" method, processing
# speed doesn't matter much in the simulator and with the locking we
# need to do it's much easier to let this be a shallow wrapper around
# the full implementation.
return [x.info.code for x in self.see()]

# The simulator does not emulate the `capture` or `save` methods.


def init_cameras(webot: Robot, lock: threading.Lock) -> list[Camera]:
camera = maybe_get_robot_device(webot, 'camera', WebotCamera)
if camera is None:
return []
return [Camera(webot, camera, lock)]
66 changes: 66 additions & 0 deletions modules/sr/robot3/metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from __future__ import annotations

import os
import enum
import dataclasses
from pathlib import Path

_ETC_OS_RELEASE = Path('/etc/os-release')


class RobotMode(enum.Enum):
# Note: The simulator internally continues to use the historical (lower
# case) spelling of these modes, however for the competitor-facing API we
# provide uppercased spellings to match sr.robot3.

COMP = 'COMP'
DEV = 'DEV'


@dataclasses.dataclass(frozen=True)
class Metadata:
"""Minimal version of robot metadata."""

arena: str = 'A'
zone: int = 0
mode: RobotMode = RobotMode.DEV
marker_offset: int = 0
game_timeout: int | None = None
wifi_enabled: bool = True

# From Software
astoria_version: str = ''
kernel_version: str = ''
arch: str = ''
python_version: str = ''
libc_ver: str = ''
os_name: str | None = None
os_pretty_name: str | None = None
os_version: str | None = None

# From robot settings file
usercode_entrypoint: str = ''
wifi_ssid: str | None = None
wifi_psk: str | None = None
wifi_region: str | None = None

def is_wifi_valid(self) -> bool:
return True

@classmethod
def get_os_version_info(cls, os_release_path: Path = _ETC_OS_RELEASE) -> dict[str, str]:
return {}


def init_metadata() -> tuple[Metadata, Path]:
mode_str = os.environ.get('SR_ROBOT_MODE', 'dev').upper()

zone_str = os.environ.get('SR_ROBOT_ZONE', '0')
zone = int(zone_str)
if zone not in range(4):
raise ValueError(f"Zone must be in range 0-3 inclusive. {zone_str!r} is invalid")

return (
Metadata(mode=RobotMode(mode_str), zone=zone),
Path(os.environ['SR_ROBOT_FILE']).parent.absolute(),
)
17 changes: 8 additions & 9 deletions modules/sr/robot3/motor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
from sr.robot3.motor_devices import Wheel, Gripper, LinearMotor

# The maximum value that the motor board will accept
SPEED_MAX = 1.0
SPEED_MAX = 1

COAST = 0
BRAKE = 0


def init_motor_array(webot: Robot) -> dict[str, Motor]:
def init_motor_array(webot: Robot) -> dict[str, MotorBoard]:
return {
'srABC1': Motor(
'srABC1': MotorBoard(
Wheel(webot, 'left wheel'),
Wheel(webot, 'right wheel'),
),
Expand All @@ -36,7 +36,7 @@ def translate(sr_speed_val: float, sr_motor: Gripper | Wheel | LinearMotor) -> f
)


class Motor:
class MotorBoard:
"""Represents a motor board."""

def __init__(
Expand Down Expand Up @@ -77,11 +77,10 @@ def power(self, value: float) -> None:
"target setter function"
self._power = value

# Limit the value to within the valid range
if value > SPEED_MAX:
value = SPEED_MAX
elif value < -SPEED_MAX:
value = -SPEED_MAX
if value > SPEED_MAX or value < -SPEED_MAX:
raise ValueError(
f"Motor power must be between {SPEED_MAX} and -{SPEED_MAX}.",
)

if self.sr_motor:
self.sr_motor.set_speed(translate(value, self.sr_motor))
38 changes: 28 additions & 10 deletions modules/sr/robot3/power.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
from __future__ import annotations

import datetime
from enum import Enum
from typing import Union, Iterator
from typing import Union, Iterator, TYPE_CHECKING

from controller import Robot
if TYPE_CHECKING:
from .robot import Robot


class Outputs(Enum):
OUT_H0 = 'H0'
OUT_H1 = 'H1'
OUT_L0 = 'L0'
OUT_L1 = 'L1'
OUT_L2 = 'L2'
# OUT_L2 = 'L2' # Brain board runs from L2
OUT_L3 = 'L3'
OUT_FIVE_VOLT = 'FIVE_VOLT'


def init_power_board(webot: Robot) -> Power:
return Power()
def init_power_board(robot: Robot) -> PowerBoard:
return PowerBoard(robot)


class Power:
def __init__(self) -> None:
class PowerBoard:
def __init__(self, robot: Robot) -> None:
self.outputs = OutputGroup()
self.battery_sensor = BatterySensor()
self.piezo = Piezo()
self.piezo = Piezo(robot)


class OutputGroup:
Expand Down Expand Up @@ -81,8 +84,23 @@ def current(self) -> float:


class Piezo:
def buzz(self, duration: float, note: Pitch) -> None:
pass
def __init__(self, robot: Robot) -> None:
self.robot = robot

def buzz(
self,
duration: int | float | datetime.timedelta,
pitch: Pitch,
*,
blocking: bool | None = None,
) -> None:
if not blocking:
return

if isinstance(duration, datetime.timedelta):
duration = duration.total_seconds()

self.robot.sleep(duration)


class Note(float, Enum):
Expand Down
Loading