Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
9720dc1
feat: referee support in rsim
isaac0804 Nov 26, 2025
4cff935
hotfix: self.name bug
isaac0804 Nov 26, 2025
ac7ffb0
Merge branch 'main' into referee_integration
isaac0804 Feb 18, 2026
3f6b0d9
feat: receive complete referee data and use it in strategy runner
isaac0804 Feb 18, 2026
27850b1
feat: referee command nodes and test
isaac0804 Feb 18, 2026
ec2558b
fix: make RefereeData.__eq__ actually override tuple equality; fix te…
isaac0804 Mar 13, 2026
08af954
merge: resolve conflicts from main into referee_integration
isaac0804 Mar 13, 2026
769a8a6
Add custom referee integration and profile rename
isaac0804 Mar 31, 2026
5c0b05a
Fix referee runner startup and keep-out behavior
isaac0804 Mar 31, 2026
626ef22
Scale referee actions with field geometry
isaac0804 Mar 31, 2026
d4adaeb
Fix referee restart progression and clearance
isaac0804 Mar 31, 2026
a0b120d
Fix custom referee demo wiring and status messages
isaac0804 Mar 31, 2026
2250a92
Show referee source details in live status
isaac0804 Mar 31, 2026
5d757c1
Add referee behaviour integration tests and unit test coverage
isaac0804 Apr 3, 2026
b683f39
Use public Field class properties instead of private constants
isaac0804 Apr 3, 2026
f6f1297
Default headless=True, skip ball teleport when placement pending, doc…
isaac0804 Apr 3, 2026
aa4974a
Document end-to-end ball placement test as future work
isaac0804 Apr 3, 2026
15aa391
Remove advanced controls (penalty & ball placement) from referee GUI
isaac0804 Apr 3, 2026
c148b34
Add three future work items to referee integration docs
isaac0804 Apr 3, 2026
cb6b176
Fix three bugs identified in Copilot review
isaac0804 Apr 7, 2026
5282cc2
Update utama_core/custom_referee/state_machine.py
isaac0804 Apr 7, 2026
cc1b187
Address second round of Copilot review comments
isaac0804 Apr 7, 2026
9d6b8ea
fix: stage_time_left now updates every frame in RefereeRefiner
isaac0804 Apr 14, 2026
6c265ab
fix: increase BALL_KEEP_OUT_DISTANCE to 0.8 m to prevent restart viol…
isaac0804 Apr 14, 2026
3125309
Merge origin/main into referee_integration
isaac0804 Apr 14, 2026
2040685
fix: adapt referee code to FieldDimensions API and arbitrary field sizes
isaac0804 Apr 14, 2026
3d170b8
fix: use own-half fallback direction when robot is coincident with ball
isaac0804 Apr 14, 2026
04288e5
feat: make StrategyRunner the single source of truth for referee geom…
isaac0804 Apr 14, 2026
fe1c490
refactor: remove RefereeGeometry.from_standard_div_b()
isaac0804 Apr 14, 2026
82051b9
refactor: rename half_defense_length → half_defense_depth in RefereeG…
isaac0804 Apr 14, 2026
4869ca9
fix: remove dead CLEARANCE_FALLBACK_DIRECTION and fix _clear_to_legal…
isaac0804 Apr 15, 2026
d6f4f11
fix: exclude PREPARE_KICKOFF/PENALTY from keep-out rule to prevent se…
isaac0804 Apr 15, 2026
b3a02bd
Revert "fix: exclude PREPARE_KICKOFF/PENALTY from keep-out rule to pr…
isaac0804 Apr 15, 2026
3bb31bc
fix: clear encroaching robots from current position, not formation ta…
isaac0804 Apr 15, 2026
ea33066
fix: use BALL_KEEP_OUT_DISTANCE in _ensure_outside_center_circle
isaac0804 Apr 15, 2026
386dd94
fix: address Copilot review issues in referee code and docs
isaac0804 Apr 16, 2026
ea8514b
fix: address Copilot review issues in referee actions and tests
isaac0804 Apr 16, 2026
530cd0b
fix: keep RefereeRefiner properties current when deduplication skips …
isaac0804 Apr 16, 2026
7707973
docs: rename half_defense_length → half_defense_depth in all docs
isaac0804 Apr 16, 2026
e5f381b
docs: remove non-existent exhibition.yaml from file structure
isaac0804 Apr 16, 2026
aceeb8e
Merge branch 'main' into referee_integration
energy-in-joles May 10, 2026
0c65a12
bug fix for pytest and add referee example in main
energy-in-joles May 10, 2026
82e80a2
fix: use game.ts as referee timebase instead of wall clock
isaac0804 May 15, 2026
69b96e0
fix(#107): suppress Kalman ghost tracking during robot substitution
isaac0804 May 15, 2026
3cc2ee8
Revert "fix(#107): suppress Kalman ghost tracking during robot substi…
isaac0804 May 15, 2026
d89a3af
fix: address Copilot review issues in main.py and referee tree dispat…
isaac0804 May 15, 2026
2e65361
fix: make DefenseAreaRule color-aware for symmetric enforcement
isaac0804 May 15, 2026
6db9d49
fix: cache last RefereeData so GameFrame.referee stays populated betw…
isaac0804 May 15, 2026
0a1e912
fix: address Copilot review issues in referee tree dispatchers and pe…
isaac0804 May 16, 2026
60b7c52
fix: prevent keep-out violations and defense-area infinite loop
isaac0804 May 17, 2026
b317811
fix: seed _prepare_entered_time in set_command() for direct PREPARE_*…
isaac0804 May 17, 2026
56b20a9
refactor: replace referee_system+custom_referee args with typed Refer…
isaac0804 May 18, 2026
4104b95
feat: add center_circle_radius to FieldDimensions
isaac0804 May 18, 2026
58fd7e1
refactor: replace hardcoded geometry fallbacks with STANDARD_FIELD_DI…
isaac0804 May 18, 2026
bdbe130
refactor: remove dead initial_command and initial_time params from Ga…
isaac0804 May 18, 2026
7502472
refactor: remove GameTracer from referee_integration branch
isaac0804 May 18, 2026
7b95ae8
fix: address Copilot review issues — timebase mismatch and center_cir…
isaac0804 May 18, 2026
d689b5d
fix: snapshot TeamInfo per packet to prevent retroactive history muta…
isaac0804 May 18, 2026
180d00a
update center circle render for rsim
energy-in-joles May 19, 2026
d0fb462
fix crash when rsim not included for rendering fpp
energy-in-joles May 19, 2026
9193f38
refactor: make StrategyRunner.full_field_dims the single source of fi…
isaac0804 May 19, 2026
eac4eb8
feat: add Exhibition Road festival demo (2v2, compact field, human-op…
isaac0804 May 19, 2026
3799bae
refactor: use WanderingStrategy instead of external Strategy_2v2 import
isaac0804 May 19, 2026
b8a259d
feat: scale WanderingStrategy waypoints to field_dims; pass exhibitio…
isaac0804 May 19, 2026
7b19c92
fix: propagate geometry mismatch error from vision thread to main thread
isaac0804 May 19, 2026
a4107af
fix: initialise _stop_event before _setup_vision_and_referee in __init__
isaac0804 May 19, 2026
2a9237b
fix: guard draw_line calls when env is None (grsim/real mode)
isaac0804 May 19, 2026
f5f30f0
fix: re-raise parked thread exception after stop_event exits the run …
isaac0804 May 19, 2026
a0f2bc6
feat: redesign GUI to 2x2 quadrant layout with field filling top-left
isaac0804 May 19, 2026
a35829c
feat: muted colour palette and grouped ghost-style buttons in GUI
isaac0804 May 19, 2026
788c637
Revert "feat: muted colour palette and grouped ghost-style buttons in…
isaac0804 May 19, 2026
0395d80
Revert "feat: redesign GUI to 2x2 quadrant layout with field filling …
isaac0804 May 19, 2026
7d16076
feat: restore 2x2 quadrant layout with original colour scheme
isaac0804 May 19, 2026
d380365
fix: pass waypoints directly to WanderingStep instead of via blackboard
isaac0804 May 19, 2026
a427963
fix: set _waypoints before super().__init__() in WanderingStrategy
isaac0804 May 19, 2026
3c5a653
config: disable defense area and keep-out rules for exhibition road
isaac0804 May 19, 2026
7d2c565
feat: add goal_depth to FieldDimensions and propagate to RefereeGeome…
isaac0804 May 19, 2026
a885932
docs: fix stale referee_gui.py references and outdated StrategyRunner…
isaac0804 May 19, 2026
b73ba24
fix: add _vision_receiver=None to mock_runner fixture to match Strate…
isaac0804 May 19, 2026
6fa9b74
feat: add ball placement entry point, strategy skeleton, and tests
isaac0804 May 20, 2026
6e3dc05
fix: finish the ball_placement
NingchuanIC May 24, 2026
4fabeea
init demp
NingchuanIC May 25, 2026
f328708
take the free kick
NingchuanIC May 26, 2026
5a16fbe
success sow the demo
NingchuanIC May 26, 2026
893cbb6
finish the ball placement
NingchuanIC May 26, 2026
1fa6e67
test: tighten ball placement rsim tests and add placer selection cove…
isaac0804 May 30, 2026
a1f4c50
feat: add god mode to referee GUI for manual ball placement testing
isaac0804 May 31, 2026
ba7b581
fix: update direct free kick tests broken by ball-placement-first rou…
isaac0804 May 31, 2026
ba22677
feat: pivot around ball using turn_on_spot during ball placement carry
isaac0804 May 31, 2026
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
2 changes: 1 addition & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def pytest_addoption(parser):
parser.addoption(
"--headless",
action="store_true",
default=False,
default=True,
help="Don't display any graphics (runs faster)",
)
parser.addoption(
Expand Down
146 changes: 146 additions & 0 deletions demo_ball_placement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
"""demo_ball_placement.py — Entry point for the ball placement feature.

Run:
pixi run python demo_ball_placement.py
# RSim window opens; open http://localhost:8080 in a browser

What this sets up
-----------------
- Exhibition Road field (4 m × 3 m, ``GREAT_EXHIBITION_FIELD_DIMS``)
- 2v2 format: two yellow robots (your team) vs two blue robots that
deliberately push the ball out once per side, then attack the yellow goal
- CustomReferee pre-configured with:
- Goal detection enabled (issues PREPARE_KICKOFF → NORMAL_START cycle)
- Out-of-bounds enabled (issues STOP → BALL_PLACEMENT_YELLOW → DIRECT_FREE)
- Defense area and keep-out rules OFF (less noise during development)
- Auto-advance fully enabled so state transitions fire automatically —
you can watch the full placement → free-kick → restart cycle without
touching the GUI
- Browser GUI at http://localhost:8080 — use "Manual Commands" to fire
BALL_PLACEMENT_YELLOW at any time and set the target position

Workflow
--------
1. Run this script and open http://localhost:8080.
2. Let the blue robots push the ball out of bounds, or use the GUI "Manual
Commands" panel to issue BALL_PLACEMENT_YELLOW.
3. Watch one of your robots (yellow) drive to the ball, capture it with the
dribbler, and carry it to the target circle shown in the GUI.
4. After the referee advances to NORMAL_START, yellow kicks once toward the
configured blue robot target.
"""

from utama_core.config.field_params import GREAT_EXHIBITION_FIELD_DIMS
from utama_core.custom_referee import CustomReferee
from utama_core.custom_referee.profiles.profile_loader import (
AutoAdvanceConfig,
DefenseAreaConfig,
GameConfig,
GoalDetectionConfig,
KeepOutConfig,
OutOfBoundsConfig,
RefereeProfile,
RulesConfig,
)
from utama_core.run import StrategyRunner
from utama_core.strategy.examples.ball_placement_and_kick_strategy import (
BallPlacementAndKickStrategy,
)
from utama_core.strategy.examples.deliberate_out_of_bounds_strategy import (
DeliberateOutOfBoundsStrategy,
)

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------

GUI_PORT = 8080
N_ROBOTS = 2
MY_TEAM_IS_YELLOW = True
MY_TEAM_IS_RIGHT = True


# ---------------------------------------------------------------------------
# Referee profile
#
# Out-of-bounds is the primary trigger for ball placement in a real game.
# Defense area and keep-out rules are disabled to reduce noise while you're
# developing the placement skill itself.
# ---------------------------------------------------------------------------

_BALL_PLACEMENT_PROFILE = RefereeProfile(
profile_name="ball_placement_dev",
rules=RulesConfig(
goal_detection=GoalDetectionConfig(
enabled=True,
cooldown_seconds=1.0,
),
out_of_bounds=OutOfBoundsConfig(
enabled=True,
free_kick_assigner="last_touch",
),
defense_area=DefenseAreaConfig(
enabled=False,
max_defenders=1,
attacker_infringement=False,
),
keep_out=KeepOutConfig(
enabled=False,
radius_meters=0.3,
violation_persistence_frames=30,
),
),
game=GameConfig(
half_duration_seconds=300.0,
kickoff_team="yellow",
force_start_after_goal=False,
stop_duration_seconds=2.0,
prepare_duration_seconds=3.0,
kickoff_timeout_seconds=10.0,
auto_advance=AutoAdvanceConfig(
# All auto-advance enabled: state machine drives itself so you can
# observe the full placement → free-kick → normal-start cycle.
stop_to_next_command=True,
prepare_kickoff_to_normal=True,
prepare_penalty_to_normal=True,
direct_free_to_normal=True,
ball_placement_to_next=True,
normal_start_to_force=True,
),
),
)

# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------


def main() -> None:
referee = CustomReferee(
_BALL_PLACEMENT_PROFILE,
n_robots_yellow=N_ROBOTS,
n_robots_blue=N_ROBOTS,
enable_gui=True,
gui_port=GUI_PORT,
)

runner = StrategyRunner(
strategy=BallPlacementAndKickStrategy(),
# Opponents create one out-of-bounds event per side, then attack yellow's goal.
opp_strategy=DeliberateOutOfBoundsStrategy(field_dims=GREAT_EXHIBITION_FIELD_DIMS),
my_team_is_yellow=MY_TEAM_IS_YELLOW,
my_team_is_right=MY_TEAM_IS_RIGHT,
mode="rsim",
control_scheme="pid",
exp_friendly=N_ROBOTS,
exp_enemy=N_ROBOTS,
full_field_dims=GREAT_EXHIBITION_FIELD_DIMS,
referee=referee,
show_live_status=True,
)

runner.run()


if __name__ == "__main__":
main()
133 changes: 133 additions & 0 deletions demo_ball_placement_real.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"""demo_ball_placement_real.py — Test ball placement with a single real robot.

Run:
pixi run python demo_ball_placement_real.py
# Then open http://localhost:8080 in a browser

What this sets up
-----------------
- Exhibition Road field (4 m x 3 m)
- 1 yellow robot, no opponents
- CustomReferee starts in HALT — the robot will not move until you issue a
command from the GUI
- All automatic rule detection (out-of-bounds, goal, keep-out) is disabled so
the only commands come from the operator via the GUI

Workflow
--------
1. Run this script and open http://localhost:8080.
2. Place the ball somewhere on the field by hand.
3. In the GUI "Manual Commands" panel, set a target position and issue
BALL_PLACEMENT_YELLOW.
4. The robot drives to the ball with its dribbler on and attempts to carry it
to the target.
5. Issue HALT from the GUI at any time to stop the robot immediately.

Safety notes
------------
- The robot starts in HALT and will not move until you issue a command.
- Keep the target position within the physical field bounds.
- Issue HALT from the GUI before approaching the field.
"""

from utama_core.config.field_params import GREAT_EXHIBITION_FIELD_DIMS
from utama_core.custom_referee import CustomReferee
from utama_core.custom_referee.profiles.profile_loader import (
AutoAdvanceConfig,
DefenseAreaConfig,
GameConfig,
GoalDetectionConfig,
KeepOutConfig,
OutOfBoundsConfig,
RefereeProfile,
RulesConfig,
)
from utama_core.run import StrategyRunner
from utama_core.strategy.examples.ball_placement_strategy import BallPlacementStrategy

# ---------------------------------------------------------------------------
# Configuration -- edit these to match your setup
# ---------------------------------------------------------------------------

GUI_PORT = 8080
MY_TEAM_IS_YELLOW = True
MY_TEAM_IS_RIGHT = True

# ---------------------------------------------------------------------------
# Referee profile
#
# All automatic rules are OFF. Commands come from the operator only.
# auto_advance is also OFF so the state never transitions without you.
# ---------------------------------------------------------------------------

_REAL_TEST_PROFILE = RefereeProfile(
profile_name="ball_placement_real_test",
rules=RulesConfig(
goal_detection=GoalDetectionConfig(
enabled=False,
cooldown_seconds=1.0,
),
out_of_bounds=OutOfBoundsConfig(
enabled=False,
free_kick_assigner="last_touch",
),
defense_area=DefenseAreaConfig(
enabled=False,
max_defenders=1,
attacker_infringement=False,
),
keep_out=KeepOutConfig(
enabled=False,
radius_meters=0.3,
violation_persistence_frames=30,
),
),
game=GameConfig(
half_duration_seconds=300.0,
kickoff_team="yellow",
force_start_after_goal=False,
stop_duration_seconds=2.0,
prepare_duration_seconds=3.0,
kickoff_timeout_seconds=10.0,
auto_advance=AutoAdvanceConfig(
stop_to_next_command=False,
prepare_kickoff_to_normal=False,
prepare_penalty_to_normal=False,
direct_free_to_normal=False,
ball_placement_to_next=False,
normal_start_to_force=False,
),
),
)

# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------


def main() -> None:
referee = CustomReferee(
_REAL_TEST_PROFILE,
n_robots_yellow=1,
n_robots_blue=0,
enable_gui=True,
gui_port=GUI_PORT,
)

runner = StrategyRunner(
strategy=BallPlacementStrategy(),
my_team_is_yellow=MY_TEAM_IS_YELLOW,
my_team_is_right=MY_TEAM_IS_RIGHT,
mode="rsim",
exp_friendly=1,
exp_enemy=0,
full_field_dims=GREAT_EXHIBITION_FIELD_DIMS,
referee=referee,
show_live_status=True,
)

runner.run()


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