Skip to content

Referee integration - #111

Merged
energy-in-joles merged 85 commits into
mainfrom
referee_integration
May 20, 2026
Merged

energy-in-joles merged 85 commits into
mainfrom
referee_integration

Conversation

@isaac0804

Copy link
Copy Markdown
Contributor

Referee data pipeline

  • Receive and parse complete SSL referee protobuf messages into RefereeData
  • RefereeRefiner records referee state changes
  • RefereeData converted from NamedTuple to @dataclass to support a custom eq that ignores timestamps and game events (preventing spurious re-records)

Custom referee & state machine

  • GameStateMachine tracks referee commands, stages, and transitions
  • 5 configurable auto-advance transitions (HALT→STOP, STOP→kickoff, kickoff→NORMAL, direct free→NORMAL, ball placement→next command)
  • 2-second sustained-readiness delay on game-starting advances for safety
  • All auto-advances disabled by default in physical environment profiles

Compliant action nodes (actions.py)

  • BallPlacementTheirsStep: active radial clearance — robots push out of the 0.55 m keep-out zone instead of freezing
  • PrepareKickoffTheirsStep: robots clamped to own half before kickoff
  • BallPlacementOursStep: two-phase placer motion (drive to ball → carry to target) with non-placer radial clearance and completion detection
  • PreparePenaltyOursStep / PreparePenaltyTheirsStep: non-kicker/non-keeper robots placed at touch-line boundary (SSL rule approximation)

Operator GUI (gui.py)

  • Browser-based panel served over HTTP + SSE at ~30 Hz
  • Displays live referee state, game score, stage, and command history
  • Config panel exposes all 5 auto-advance flags as toggleable ON/OFF pills
  • Integrates with the custom referee via attach_gui(referee, profile, port)

Profiles

  • arcade.yaml and strict_ai.yaml with explicit auto_advance blocks
  • AutoAdvanceConfig dataclass parsed from YAML game.auto_advance section

isaac0804 and others added 7 commits November 26, 2025 15:50
…st bugs

- Convert RefereeData from NamedTuple to @DataClass(eq=False) so the
  custom __eq__ is respected (NamedTuple.__eq__ cannot be overridden —
  tuple equality always wins)
- Use TYPE_CHECKING guard for TeamInfo import to avoid circular import
  (game/__init__ → Game → GameFrame → RefereeData → TeamInfo → game/__init__)
- __eq__ compares TeamInfo by .score and .goalkeeper (the mutable game-state
  fields) since TeamInfo has no structural __eq__ of its own
- Add __hash__ consistent with the subset of fields used in __eq__
- RefereeRefiner.add_new_referee_data: replace tuple slicing [1:] with ==
  (now correctly uses the custom __eq__)
- test_referee_unit.py: fix GameHistory() → GameHistory(10) (max_history
  is a required positional argument)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adopted main's SideRuntime refactor (my/opp sides) in strategy_runner.py
while preserving referee integration. Fixed imports to new data_processing
module paths. Resolved standard_ssl.py conflicts keeping RefereeData usage
from our branch with main's improved assertion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

utama_core/rsoccer_simulator/src/ssl/envs/standard_ssl.py:231

  • _frame_to_observations() docstring says it returns a 4-tuple including referee_data, and StrategyRunner now branches on len(obs) == 4, but the function still returns only a 3-tuple. This means embedded referee data is never provided to StrategyRunner and RSIM mode will always see referee_data=None unless externally injected. Update the return value (and type annotation) to actually include the current RefereeData from the embedded referee state machine, or revert the docstring/StrategyRunner logic if referee data is not available here.
    def _frame_to_observations(
        self,
    ) -> Tuple[RawVisionData, RobotResponse, RobotResponse]:
        """Return observation data that aligns with grSim. There may be Gaussian noise and vanishing added.

        Returns (vision_observation, yellow_robot_feedback, blue_robot_feedback, referee_data)
        vision_observation: closely aligned to SSLVision that returns a FramData object
        yellow_robots_info: feedback from individual yellow robots that returns a List[RobotInfo]
        blue_robots_info: feedback from individual blue robots that returns a List[RobotInfo]
        referee_data: current referee state from embedded referee state machine
        """

        if self.latest_observation[0] == self.steps:
            return self.latest_observation[1]

        # Ball observation shared by all robots
        if self._vanishing():
            ball_obs = []
        else:
            SSLStandardEnv._add_gaussian_noise_ball(self.frame.ball, self.gaussian_noise)
            ball_obs = [RawBallData(self.frame.ball.x, -self.frame.ball.y, self.frame.ball.z, 1.0)]

        # Robots observation (Blue + Yellow)
        blue_obs = []
        blue_robots_info = []
        for i in range(len(self.frame.robots_blue)):
            if self._vanishing():
                continue

            robot = self.frame.robots_blue[i]
            robot_pos, robot_info = self._get_robot_observation(robot)
            blue_obs.append(robot_pos)
            blue_robots_info.append(robot_info)

        yellow_obs = []
        yellow_robots_info = []
        for i in range(len(self.frame.robots_yellow)):
            if self._vanishing():
                continue

            robot = self.frame.robots_yellow[i]
            robot_pos, robot_info = self._get_robot_observation(robot)
            yellow_obs.append(robot_pos)
            yellow_robots_info.append(robot_info)

        # Return the complete shared observation
        # note that ball_obs stored in list to standardise with SSLVision
        # As there is sometimes multiple possible positions for the ball

        # Get referee data
        # current_time = self.time_step * self.steps

        # Camera id as 0, only one camera for RSim
        result = (
            RawVisionData(self.time_step * self.steps, yellow_obs, blue_obs, ball_obs, 0),
            yellow_robots_info,
            blue_robots_info,
        )
        self.latest_observation = (self.steps, result)
        return result

Comment thread utama_core/entities/data/referee.py Outdated
Comment thread utama_core/strategy/referee/actions.py Outdated
Comment thread utama_core/rsoccer_simulator/src/ssl/referee_state_machine.py Outdated
@isaac0804

Copy link
Copy Markdown
Contributor Author

Referee integration

  • Added full referee data plumbing into the game pipeline: complete SSL referee protobuf messages are parsed into RefereeData, recorded by RefereeRefiner, and exposed to strategies through the game state
  • RefereeData was converted to a @dataclass with custom equality so timestamps and non-behavioral fields do not cause noisy re-recording

Custom referee

  • Added an in-process CustomReferee with a GameStateMachine that works across rsim, grsim, and real
  • Added explicit referee source selection in StrategyRunner: none, official, or custom
  • Ported built-in profiles and renamed them to:
    • simulation for auto-progressing simulator / RL workflows
    • human for operator-controlled physical / testing workflows
  • Added configurable auto-advance behavior for queued restarts, including kickoff, direct free, penalty, and ball placement

Referee override behavior

  • Added the referee override tree and action nodes so strategies comply with referee commands like HALT, STOP, kickoff, penalty, direct free, and ball placement
  • Fixed key legality behaviors:
    • active clearance during STOP and opponent restarts
    • two-phase ball placement for our team
    • teammate clearance during our ball placement
    • clearance from both ball and designated target during opponent ball placement
    • kickoff and penalty positioning now scale correctly with field size and side assignment

Operator UX

  • Added a browser-based custom-referee GUI over HTTP/SSE for command control and state inspection
  • Live terminal status now shows richer referee context, including command, next command, referee source, and custom-referee profile
  • Custom-referee status messages are now preserved and shown to the operator so stoppages include a visible reason

Geometry / configurability

  • Removed hardcoded referee-action geometry where practical and moved shared distances into constants or live field-derived helpers
  • Kickoff and penalty formations now work on variable field sizes instead of assuming the standard field

Tests

  • Added focused unit/integration coverage for:
    • referee source selection in StrategyRunner
    • custom-referee profile loading and restart progression
    • referee action-node legality and field-scaling behavior
    • status-message propagation through the custom-referee path

isaac0804 and others added 6 commits April 3, 2026 11:43
- Add rsim integration tests for ball placement, direct free kick (ours
  and theirs), and kickoff positioning in test_referee_rsim.py
- Add 15 unit tests covering PrepareKickoffTheirsStep, DirectFreeOursStep,
  and DirectFreeTheirsStep action nodes in test_referee_unit.py
- Switch demo script control_scheme from dwa to pid

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace Field._UNDERSCORE_CONSTANTS with their public ClassProperty
equivalents in math_utils.py and geometry.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ument future work

- conftest.py: default --headless to True so tests don't open rsim window
- strategy_runner.py: skip ball teleport on STOP when next_command is
  BALL_PLACEMENT so the robot must physically carry the ball
- test_referee_rsim.py: replace broken full-sequence test with a comment
  documenting why it is deferred (ball placement carry mechanics not yet
  reliable in rsim)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Penalty and ball placement buttons are not in use; removing them keeps
the operator panel focused on the commands we actually use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ball placement phase before free kick per SSL rulebook
- BallPlacementOursStep carry mechanics investigation (two-robot kissing)
- GUI suggested next action to reduce operator cognitive load

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 48 out of 50 changed files in this pull request and generated 3 comments.

Comment thread utama_core/rsoccer_simulator/src/ssl/referee_state_machine.py Outdated
Comment thread utama_core/rsoccer_simulator/src/ssl/envs/standard_ssl.py Outdated
Comment thread utama_core/entities/data/referee.py
- Delete dead RefereeStateMachine (never wired up, duplicated GameStateMachine
  with a broken _replace() call on a mutable class)
- Remove dead len(obs)==4 branch in _run_step; RSim always reads from ref_buffer
- Snapshot TeamInfo via copy.copy() in _generate_referee_data() to prevent
  score mutations retroactively corrupting stored RefereeRefiner records
- Update docs and stale docstrings accordingly
- Document TeamInfo frozen dataclass refactor as deferred follow-up work

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 19, 2026 23:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 60 out of 62 changed files in this pull request and generated 4 comments.

Comment thread utama_core/config/field_params.py Outdated
Comment thread utama_core/run/strategy_runner.py
Comment thread utama_core/custom_referee/custom_referee.py
Comment thread utama_core/tests/referee/referee_sim.py
Copilot AI review requested due to automatic review settings May 20, 2026 11:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 60 out of 62 changed files in this pull request and generated 3 comments.

Comment thread utama_core/custom_referee/profiles/profile_loader.py
Comment thread docs/custom_referee.md Outdated
Comment thread docs/custom_referee.md Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 20, 2026 11:12
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 60 out of 62 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (2)

utama_core/strategy/referee/actions.py:469

  • kicker_id = ... else robot_ids[0] will raise IndexError if friendly_robots is empty. Add a guard before indexing so penalty setup does not crash on frames with no robot detections.
    utama_core/strategy/referee/actions.py:561
  • kicker_id = min(game.friendly_robots, ...) will raise ValueError when friendly_robots is empty. Add an early return/stop guard so DIRECT_FREE handling is robust to intermittent vision dropouts.

Comment thread utama_core/strategy/referee/actions.py
Comment thread utama_core/strategy/referee/actions.py
Make __hash__ consistent with __eq__ by including all fields that __eq__
compares (score, goalkeeper for both teams, current_action_time_remaining).

Fix _ScriptedReferee to use self._start instead of time.time() for
referee_command_timestamp so the value stays stable across frames while
the same command is active, allowing RefereeRefiner deduplication to work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@energy-in-joles
energy-in-joles merged commit b5fcd3e into main May 20, 2026
2 checks passed
@energy-in-joles
energy-in-joles deleted the referee_integration branch May 20, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants