Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
b7ac2b8
added schema
sauravbanna Mar 28, 2026
e42bf21
refactored possession tracker
sauravbanna Mar 28, 2026
debcd45
fixed BUILD
sauravbanna Mar 28, 2026
cf1839d
Merge branch 'sauravbanna/verbose_log_schema' of github.com:UBC-Thund…
sauravbanna Mar 30, 2026
ee18c64
added from team
sauravbanna Mar 30, 2026
ecdee53
added from team
sauravbanna Mar 30, 2026
9eb22be
format
sauravbanna Mar 30, 2026
9e40188
Merge branch 'sauravbanna/verbose_log_schema' of github.com:UBC-Thund…
sauravbanna Mar 30, 2026
201c1b7
refactored all trackers to new api
sauravbanna Mar 30, 2026
67b49bc
updated schema
sauravbanna Mar 30, 2026
855ee5b
Merge branch 'sauravbanna/verbose_log_schema' of github.com:UBC-Thund…
sauravbanna Mar 30, 2026
44aca4e
made list of robots have constant len in csv
sauravbanna Mar 30, 2026
e0aa9d0
minor type hint fix
sauravbanna Mar 30, 2026
be01270
add robot states based on id instead of order
sauravbanna Mar 30, 2026
b0bde2e
use protobufs in tracked event
Thunderbots Mar 30, 2026
fb949b9
Merge branch 'sauravbanna/verbose_log_schema' of https://github.com/U…
Thunderbots Mar 30, 2026
681ccaa
docs:
Thunderbots Mar 30, 2026
c252877
docs
Thunderbots Mar 30, 2026
11ad662
Merge branch 'sauravbanna/verbose_log_schema' of https://github.com/U…
Thunderbots Mar 30, 2026
cdd7724
fixed bugs, stats csv works now
Thunderbots Mar 30, 2026
d682a99
fixed bugs
Thunderbots Mar 30, 2026
05e03ec
Merge branch 'sauravbanna/verbose_log_schema' of https://github.com/U…
Thunderbots Mar 30, 2026
5387f96
docs update
Thunderbots Mar 30, 2026
1fee2ef
fix build
Thunderbots Mar 31, 2026
1937952
Merge branch 'sauravbanna/verbose_log_schema' into sauravbanna/refact…
Thunderbots Mar 31, 2026
8621606
Merge branch 'master' of https://github.com/UBC-Thunderbots/Software …
Thunderbots Mar 31, 2026
bdc5e18
Merge branch 'master' of github.com:UBC-Thunderbots/Software into sau…
nycrat Mar 31, 2026
5f9dd00
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Mar 31, 2026
5069913
Merge branch 'master' of github.com:UBC-Thunderbots/Software into sau…
sauravbanna Mar 31, 2026
146f94e
Merge branch 'sauravbanna/verbose_log_schema' of github.com:UBC-Thund…
sauravbanna Mar 31, 2026
2b8ac00
Merge branch 'sauravbanna/verbose_log_schema' of github.com:UBC-Thund…
sauravbanna Mar 31, 2026
2a35535
format
sauravbanna Mar 31, 2026
061dc16
testing
sauravbanna Apr 2, 2026
6ce1c07
lot of refactoring
sauravbanna Apr 4, 2026
96c12f1
Merge branch 'master' of github.com:UBC-Thunderbots/Software into sau…
sauravbanna Apr 4, 2026
be09413
fix
sauravbanna Apr 4, 2026
d41c399
add files
sauravbanna Apr 4, 2026
06d6fed
fuixed bugs
Thunderbots Apr 5, 2026
daaefd3
BUILD files + format
sauravbanna Apr 5, 2026
751f534
format
sauravbanna Apr 5, 2026
f47e37f
test
Thunderbots Apr 5, 2026
4b1f396
fix
sauravbanna Apr 5, 2026
5dd256c
Merge branch 'sauravbanna/ml_testing' of https://github.com/UBC-Thund…
Thunderbots Apr 5, 2026
e580201
added BUILD + requirements to bazel
Thunderbots Apr 5, 2026
4a204b8
format
sauravbanna Apr 5, 2026
844fc95
update req lock
Thunderbots Apr 7, 2026
4d73bc9
fixed bugs w model, changed labelling to predict all interval probabi…
Thunderbots Apr 7, 2026
2779b31
fix pass log tracker
Thunderbots Apr 25, 2026
b3c342c
removed log statement
Thunderbots Apr 25, 2026
8c47855
fix bugs with timestamp reading + ordering
Thunderbots Apr 25, 2026
f0bbb3d
add sklearn + replace bit accuracy with f1 score + undersample passes
Thunderbots Apr 25, 2026
9efef7a
Update labelled_passes.py to use difference between intervals instead…
sauravbanna Apr 28, 2026
dc66924
Merge branch 'sauravbanna/ml_testing' of github.com:UBC-Thunderbots/S…
sauravbanna Apr 28, 2026
649c6b7
fixed syntax + added label weights function
sauravbanna Apr 28, 2026
a1830d7
format + requirements
Thunderbots Apr 28, 2026
207ce4e
fix python req in BUILD
Thunderbots Apr 29, 2026
89b1763
added scaled weights for priority events
Thunderbots Apr 29, 2026
551a527
format
Thunderbots Apr 29, 2026
f873bc4
seperate labelling and training
Thunderbots Apr 29, 2026
024b47f
refactored train passing to only train from pre-labelled passes
sauravbanna Apr 29, 2026
70ff89f
refactored train passing to only train from pre-labelled passes
sauravbanna Apr 29, 2026
995dd88
added options to specify pass results and game events file names
sauravbanna Apr 29, 2026
c9ee7a7
update pass logger to use custom file
sauravbanna Apr 29, 2026
03d7d0a
format
sauravbanna Apr 29, 2026
52db2d2
update yellow stats file name
sauravbanna Apr 29, 2026
dfcf853
fixed BUILD + bugs in generate labelled passes
Thunderbots May 1, 2026
3eae010
format
Thunderbots May 1, 2026
0f986a2
added reqs + updated training to use more hidden dims etc
Thunderbots May 2, 2026
06efc7a
add scaling of weights
sauravbanna May 2, 2026
994337b
replace w constants
sauravbanna May 2, 2026
ac74634
use scaling
sauravbanna May 2, 2026
f939b30
use interval scaled weights instead
sauravbanna May 2, 2026
959ca49
reduce interval scales
sauravbanna May 2, 2026
f6eb940
back to normal weights
sauravbanna May 2, 2026
71d662e
event scaled weights
sauravbanna May 9, 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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ src/external/external

# Misc. Log Files
*.log
*.csv


### Node ###

Expand Down Expand Up @@ -162,3 +162,4 @@ src/external
# macOS folder info
.DS_Store

*_venv/
1 change: 1 addition & 0 deletions scripts/compile_pip_requirements.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ bazel run //software/embedded/ansible:requirements.update
bazel run //software/simulated_tests:requirements.update
bazel run //software/embedded/robot_diagnostics_cli:requirements.update
bazel run //starlark/nanopb:requirements.update
bazel run //software/ml:requirements.update
7 changes: 7 additions & 0 deletions src/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ pip.parse(
requirements_lock = "//starlark/nanopb:requirements_lock.txt",
)
use_repo(pip, "nanopb_deps")
pip.parse(
hub_name = "ml_deps",
python_interpreter = "/opt/tbotspython/bin/python",
python_version = "3.12",
requirements_lock = "//software/ml:requirements_lock.txt",
)
use_repo(pip, "ml_deps")

##############################################
# Configure Dependencies
Expand Down
17 changes: 17 additions & 0 deletions src/software/evaluation/loggers/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package(default_visibility = ["//visibility:public"])

py_library(
name = "stats_logger",
srcs = ["stats_logger.py"],
deps = [
"//software/evaluation/trackers:tracker",
],
)

py_library(
name = "pass_logger",
srcs = ["pass_logger.py"],
deps = [
"//software/evaluation/trackers:tracker",
],
)
123 changes: 123 additions & 0 deletions src/software/evaluation/loggers/pass_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
from software.thunderscope.proto_unix_io import ProtoUnixIO
from software.thunderscope.constants import PassResultsConstants
import os
from proto.import_all_protos import *
from software.evaluation.logs.event_log import (
Team as TeamEnum,
)
from software.evaluation.trackers.pass_log_tracker import (
PassLogTracker,
)
from software.evaluation.logs.pass_log import (
PassLog,
)
import queue


class PassLogger:
"""Class to track passes.

i.e When a pass happens, we want to log the game state and the pass itself
As well as the game state at certain intervals after the pass
to see the outcomes of the pass
"""

EVENT_BUFFER_SIZE = 100

def __init__(
self,
proto_unix_io: ProtoUnixIO,
friendly_colour_yellow: bool,
out_file_name: str | None = None,
buffer_size: int = 5,
):
"""Initializes the pass results tracker

:param proto_unix_io: the proto unix io to use
:param friendly_colour_yellow: if the friendly color is yellow or not
:param out_file_name: name of file to write pass results to.
If None, uses the value from constants
:param buffer_size: buffer size to use
"""
self.friendly_colour_yellow = friendly_colour_yellow

# track pass results to this queue
self.pass_result_queue = queue.Queue(self.EVENT_BUFFER_SIZE)

self.pass_tracker = PassLogTracker(
proto_unix_io=proto_unix_io,
from_team=(
TeamEnum.YELLOW if self.friendly_colour_yellow else TeamEnum.BLUE
),
event_queue=self.pass_result_queue,
buffer_size=buffer_size,
)

self.events_file_path = os.path.join(
PassResultsConstants.PASS_RESULTS_DIRECTORY_PATH,
PassResultsConstants.PASS_RESULTS_FILE_NAME
if out_file_name is None
else out_file_name,
)
self.events_file_handle = None

def _get_team(self, is_friendly: bool) -> TeamEnum:
"""Gets the correct Team enum value for either the current friendly or enemy team

:param is_friendly: whether to return the team for friendly or enemy
:return: the corresponding Team Enum value
"""
return (
TeamEnum.YELLOW
if (self.friendly_colour_yellow == is_friendly)
else TeamEnum.BLUE
)

def __enter__(self):
"""Creates any missing directories and opens an append file handle"""
# create temp stats directory if it doesn't exist
os.makedirs(os.path.dirname(self.events_file_path), exist_ok=True)

self.events_file_handle = open(self.events_file_path, "a")

return self

def __exit__(self, exc_type, exc_value, traceback):
"""Flushes content and closes the log file"""
if self.events_file_handle:
self.events_file_handle.flush()
self.events_file_handle.close()

def refresh(self) -> None:
"""Refreshes the tracker so we stay up to date on new passes
and checks to see if any passes are older than their interval
"""
self.pass_tracker.refresh()

if not self.events_file_handle:
return

while not self.pass_result_queue.empty():
try:
# Get item without blocking
pass_log = self.pass_result_queue.get_nowait()

self._log_pass(pass_log)
except queue.Empty:
return

def _log_pass(self, pass_log: PassLog):
"""Logs a single pass to file

:param result: the result to log
"""
if not self.events_file_handle:
return

try:
csv_row = pass_log.to_csv_row()
self.events_file_handle.write(csv_row + "\n")
self.events_file_handle.flush()

except (IOError, FileNotFoundError, PermissionError):
pass
166 changes: 166 additions & 0 deletions src/software/evaluation/loggers/stats_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import os

from software.evaluation.trackers import (
PossessionTracker,
ShotTracker,
PassTracker,
TrackerBuilder,
RefereeTracker,
GoalieTracker,
)
from dataclasses import dataclass
from software.thunderscope.proto_unix_io import ProtoUnixIO
from software.thunderscope.constants import RuntimeManagerConstants
from software.evaluation.logs.event_log import Team as TeamEnum, EventLog
import logging
from proto.import_all_protos import *
import queue


@dataclass
class FSStats:
"""Stats for how well a FullSystem is performing"""

num_yellow_cards: int = 0
num_red_cards: int = 0
num_scores: int = 0

num_shots_on_net: int = 0
num_enemy_shots_blocked: int = 0


class StatsLogger:
# From GoalieTacticConfig
INCOMING_SHOT_MIN_VELOCITY = 0.2

EVENT_BUFFER_SIZE = 100

def __init__(
self,
proto_unix_io: ProtoUnixIO,
friendly_colour_yellow: bool,
out_file_name: str | None = None,
buffer_size: int = 5,
record_enemy_stats: bool = False,
):
"""Initializes the FullSystem Stats Tracker

:param friendly_colour_yellow: if the friendly colour is yellow
:param out_file_name: name of file to write stats to.
If None, uses the value from constants
:param buffer_size: the buffer size for protocol buffers
:param record_enemy_stats: if this should record both friendly and enemy stats or just friendly
"""
self.friendly_colour_yellow = friendly_colour_yellow

self.events_file_path = os.path.join(
RuntimeManagerConstants.RUNTIME_EVENTS_DIRECTORY_PATH,
RuntimeManagerConstants.RUNTIME_EVENTS_FILE
if out_file_name is None
else out_file_name,
)
# initialized in setup()
self.events_file_handle = None

self.event_queue = queue.Queue(self.EVENT_BUFFER_SIZE)

# flag to turn off logging stats if needed
self.logging_enabled = True

self.tracker = (
TrackerBuilder(
proto_unix_io=proto_unix_io,
from_team=(
TeamEnum.YELLOW if self.friendly_colour_yellow else TeamEnum.BLUE
),
event_queue=self.event_queue,
buffer_size=buffer_size,
)
.add_tracker(PassTracker)
.add_tracker(ShotTracker)
.add_tracker(PossessionTracker)
.add_tracker(
RefereeTracker,
friendly_color_yellow=self.friendly_colour_yellow,
toggle_logging=self._toggle_logging,
)
.add_tracker(GoalieTracker, for_friendly=True)
)

self.record_enemy_stats = record_enemy_stats
if self.record_enemy_stats:
self.enemy_tracker = (
TrackerBuilder(
proto_unix_io=proto_unix_io,
from_team=(
TeamEnum.YELLOW
if self.friendly_colour_yellow
else TeamEnum.BLUE
),
for_team=(
TeamEnum.BLUE
if self.friendly_colour_yellow
else TeamEnum.YELLOW
),
event_queue=self.event_queue,
buffer_size=buffer_size,
)
.add_tracker(
RefereeTracker,
friendly_color_yellow=(not self.friendly_colour_yellow),
toggle_logging=self._toggle_logging,
)
.add_tracker(GoalieTracker, for_friendly=False)
)

def refresh(self) -> None:
"""Refreshes the events for the game so far"""
self.tracker.refresh()

if not self.events_file_handle:
return

while not self.event_queue.empty():
try:
# Get item without blocking
event = self.event_queue.get_nowait()

self._write_event_to_file(event)
except queue.Empty:
return

def __enter__(self):
"""Sets up the file resources for logging
Creates any missing directories and stores the file handle
"""
# create temp stats directory if it doesn't exist
os.makedirs(os.path.dirname(self.events_file_path), exist_ok=True)

self.events_file_handle = open(self.events_file_path, "a")

return self

def __exit__(self, exc_type, exc_value, traceback):
"""Writes all logs back to file, and cleans up any created file resources after logging"""
if self.events_file_handle:
self.events_file_handle.flush()
self.events_file_handle.close()

def _toggle_logging(self, should_log: bool) -> None:
self.logging_enabled = should_log

def _write_event_to_file(self, event: EventLog) -> None:
"""Write the given stats to the given file

:param event: the event to write
"""
if not self.events_file_handle:
return

try:
csv_row = event.to_csv_row()
self.events_file_handle.write(csv_row + "\n")
self.events_file_handle.flush()

except (IOError, FileNotFoundError, PermissionError):
logging.warning("Failed to write event to file")
49 changes: 49 additions & 0 deletions src/software/evaluation/logs/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package(default_visibility = ["//visibility:public"])

py_library(
name = "log_interface",
srcs = ["log_interface.py"],
deps = [
"//proto:import_all_protos",
"//software/thunderscope:time_provider",
],
)

py_library(
name = "world_state_log",
srcs = ["world_state_log.py"],
data = [
"//software:py_constants.so",
],
deps = [
":log_interface",
],
)

py_library(
name = "event_log",
srcs = ["event_log.py"],
deps = [
":log_interface",
":world_state_log",
],
)

py_library(
name = "pass_log",
srcs = ["pass_log.py"],
deps = [
":event_log",
":log_interface",
],
)

py_library(
name = "logs",
deps = [
":event_log",
":log_interface",
":pass_log",
":world_state_log",
],
)
Loading
Loading