Referee integration#111
Open
isaac0804 wants to merge 48 commits into
Open
Conversation
…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>
Contributor
There was a problem hiding this comment.
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 includingreferee_data, and StrategyRunner now branches onlen(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 seereferee_data=Noneunless externally injected. Update the return value (and type annotation) to actually include the currentRefereeDatafrom 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
Contributor
Author
|
Referee integration
Custom referee
Referee override behavior
Operator UX
Geometry / configurability
Tests
|
- 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>
- 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>
…a record Properties like last_command, blue_team, status_message, etc. were all reading from _referee_records[-1], which is not updated when the new RefereeData compares equal (e.g. only status_message or time_sent changes). This could leave live properties stale between meaningful state changes. Add _latest_referee_data, updated unconditionally on every refine() call (alongside the existing _latest_stage_time_left), and point all live properties at it. _referee_records still grows only on meaningful state changes, preserving its role as a deduplicated history. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The field was renamed in code (RefereeGeometry, YAML profiles) but three doc files still used the old name in YAML examples and the field table. Updated docs/custom_referee.md, docs/referee_integration.md, and docs/custom_referee_gui.md to match the actual API. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Only simulation.yaml and human.yaml exist under profiles/. exhibition.yaml was listed in the directory tree but was never created. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GameStateMachine.stage_start_time is now seeded lazily from the first current_time passed to step(), so the timebase matches the caller's clock. StrategyRunner now passes game_frame.ts instead of time.time(), making referee cooldowns and stage durations operate in sim-time for RSIM (which starts near 0) rather than wall-clock time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GameStateMachine now sets bot_substitution_allowed=False by default and flips it to True only when the stage transitions to a halftime/break stage (where SSL rules permit substitution). PositionRefiner reads this flag from game_frame.referee and skips adding None vanishing entries for robots that disappear during an open substitution window, preventing the Kalman filter from ghost-predicting their positions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tution" This reverts commit 69b96e0.
…chers - Move CustomReferee construction inside main() to eliminate import-time side effects - Store _is_opp_strat on AbstractBehaviour so setup_() can propagate it to inner nodes - Fix dispatcher setup_() calls in tree.py to forward is_opp_strat instead of hardcoding False Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the rule only checked the "friendly" team's defense area, missing violations in the opponent's box. Now derives yellow/blue sides from my_team_is_right XOR my_team_is_yellow and checks both areas in one pass. Also fix two stale doc issues: - Remove STOP from KeepOutRule active-commands table (it was always excluded) - Update design-decisions section 4 to reflect that reset() is already called on command transitions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 49 out of 51 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
conftest.py:23
- With
--headlessusingaction='store_true'anddefault=True, the--headlessflag itself becomes a no-op (it can only ever set True). Since--no-headlessexists to flip it back, consider either removing--headlessentirely or switching to a single--headless/--headfulstyle option to make the CLI intent clearer.
parser.addoption(
"--headless",
action="store_true",
default=True,
help="Don't display any graphics (runs faster)",
)
parser.addoption(
"--no-headless",
action="store_false",
dest="headless",
help="Run with graphics",
)
…een packets When using referee_system='official', the GC sends packets at ~10 Hz while the main loop runs at 60 Hz. popleft() returned None on most ticks, causing GameFrame.referee to drop out intermittently and disabling the referee layer. Now the last successfully received RefereeData is cached and reused on ticks where the buffer is empty. The RSIM/custom path is unaffected (data is always produced on the same tick it is consumed). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 49 out of 51 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
conftest.py:22
--headlessis defined withaction='store_true'but also hasdefault=True, so the flag itself can never change behavior (only--no-headlesscan). If the intent is to default to headless while still allowing an explicit opt-in, consider dropping--headlessentirely (keeping only--no-headless), or switch to a single boolean option pair so both flags are meaningful.
parser.addoption(
"--headless",
action="store_true",
default=True,
help="Don't display any graphics (runs faster)",
)
parser.addoption(
"--no-headless",
action="store_false",
dest="headless",
help="Run with graphics",
Comment on lines
+193
to
+203
| def setup_(self): | ||
| self._ours.setup(is_opp_strat=self._is_opp_strat) | ||
| self._theirs.setup(is_opp_strat=self._is_opp_strat) | ||
|
|
||
| def update(self) -> py_trees.common.Status: | ||
| if self._is_yellow_command == self.blackboard.game.my_team_is_yellow: | ||
| self._ours.blackboard = self.blackboard | ||
| return self._ours.update() | ||
| else: | ||
| self._theirs.blackboard = self.blackboard | ||
| return self._theirs.update() |
Comment on lines
+415
to
+424
| behind_idx = 0 | ||
| behind_y_step = PENALTY_LINE_Y_STEP_RATIO * _field_half_width(game) | ||
| for robot_id in robot_ids: | ||
| if robot_id == kicker_id: | ||
| self.blackboard.cmd_map[robot_id] = move(game, motion_controller, robot_id, penalty_mark, goal_oren) | ||
| else: | ||
| # Place behind the line, spread in y | ||
| offset = (behind_idx - (len(robot_ids) - 1) / 2.0) * behind_y_step | ||
| pos = Vector2D(behind_line_x, offset) | ||
| self.blackboard.cmd_map[robot_id] = move(game, motion_controller, robot_id, pos, 0.0) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Referee data pipeline
@dataclassto support a custom eq that ignores timestamps and game events (preventing spurious re-records)Custom referee & state machine
Compliant action nodes (actions.py)
Operator GUI (gui.py)
Profiles