Skip to content

Commit 51c441b

Browse files
szeyoong-lowfred-huang122energy-in-joles
authored
Feature/kalman (#101)
* Added extracted vision data with varying degrees of vanishing and noise * Attempt to store past raw game frames; starting work on test cases * added Andrew's filter * Integrated filters into position refiner * bad example test * Refined filter weights, added vision data for testing * Updated test data * Noise now added manually, updated test data * Amended test cases to address CI failure due to changes to PositionRefiner's constructor * Refinements to filters based on empirical data * Added some references for building tests * First pass unit tests * Some issues with unit tests. Working on fix * Added live testing utilities, conducted analytics for filters, refactored classes, added new datasets * Finalised analysis of filters, unit tests are working, added more live testing utilities * Deleted redundant files * Minor refinements to the live testing utilities * fixed formatting issues * Commented out utilities for testing and exporting data * Analysed vanishing problem, created sample datasets * Position refiner now has access to the last game frame and command map. Imputing feature is coming along , but there are unexplained spikes that are preventing it from working properly * Fixed issues, now imputes correctly based on last gameframe * Completed work on vanishing * Incorporated some suggestions from copilot regardign style * Addressed comments on PR * Removed redundant utility function * Factored out static method into global utility * cleanup * Added back error handling for imports when run from Jupyter * Renamed datasets to include the control mode, filtering only turned on during run mode together with vanishing * Fixed bug in tests due to renaming of files * Created working Kalman filter. Working on analytics * Working on analytics * Implemented a flag to add Gaussian noise in rsim. Added another more hackish implementation for comparison. * Analysing Kalman filter performance; need to merge new rsim utilities first * Cleaned up implementation, removed hackish alternative * Working on adding noise to each component independently * Completed implementation of noise addition * Addressed comments on PR, got orientation to work * Performed benchmarking on kalman filters * Performed benchmarking on kalman filters * More benchmarking * Kalman filter performance validated * Kalman filters now handle vanishing * Removed non-performing implementations * Rejigged analytics for Kalman filter * Addressed comments in PR, added vanishing, added support for ball * Minor linting * Merge branch 'feature/rsim_noise' into feature/kalman * Added filter for ball, added support for orientation * Now able to run analytics for orientation * Moved utility functions for adding Gaussian noise from classes representing balls/robots to the SSLStdenv class. Learned about this best practice in this week's lecture * Turned off a flag used for testing * Minor bug fix * Analysing data for orientation, plus some minor fixes * Completed work with Kalman filters, will now work on PR * Removed some redundant files * Renamed folder to ease merging with testing * Renamed folder to ease merging with testing * Cleanup, getting ready for PR * Small fix to satisfy linter * Final cleanup after reviewing PR diff * Improved filter docs * add docstring to math_utils.py * remove unused get_displacement_vector * remove unused import * [chore] formatting * chore: formatting * fix matrix inversion numerical instability and formatting of class name * remove improper static method use * improper use of tuple[float] type hint and mix bug referencing self.covariance_mat_xy instead of self.covariance_mat in KalmanFilterBall * removed references of .cmd_map not used position.py * fix test failure * Made non-global constant variable lower-cased * Using matrix transpose attribute instead of function call * update test cases * Removed redundant last_game_frame attribute * separate my and opp refiners * update running attribute for opposite refiner too * Minor formatting and naming changes to Kalman filter * fix incorrect assumption about robot id numbering in position refiner tests * corrected direct imputation of None values into vision data. Fix bug causing test fail * improve reset functionaity of position refiner * Fixed bugs introduced when removing assumption about numbering * fix dictionary logic in position refiner * update optional typing for KalmanFilterBall filter * fix typing in KalmanFilterBall * fix point cycle strat * Fixed issues in test due to filters no longer having ids, and now taking Robot instead of Dict[Int, Robot] * Kalman filter now has id attribute * label vision data using kalman id instead of last frame id * add comment about treating substitution as vanishing * remove unncessary imports * add authors --------- Co-authored-by: Fred Huang <fredhuang122106@gmail.com> Co-authored-by: Joel <nzmjoel@gmail.com>
1 parent c0228ae commit 51c441b

File tree

13 files changed

+2521
-1394
lines changed

13 files changed

+2521
-1394
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ target/
113113

114114
# Jupyter Notebook
115115
.ipynb_checkpoints
116+
.jupyter/
116117

117118
# IPython
118119
profile_default/

main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from utama_core.strategy.examples import (
66
DefenceStrategy,
77
GoToBallExampleStrategy,
8+
PointCycleStrategy,
89
RobotPlacementStrategy,
910
StartupStrategy,
1011
TwoRobotPlacementStrategy,
@@ -17,15 +18,15 @@ def main():
1718
custom_bounds = FieldBounds(top_left=(2.25, 1.5), bottom_right=(4.5, -1.5))
1819

1920
runner = StrategyRunner(
20-
strategy=TwoRobotPlacementStrategy(first_robot_id=0, second_robot_id=1, field_bounds=custom_bounds),
21+
strategy=PointCycleStrategy(n_robots=2, field_bounds=custom_bounds, endpoint_tolerance=0.1, seed=42),
2122
my_team_is_yellow=True,
2223
my_team_is_right=True,
2324
mode="rsim",
2425
exp_friendly=2,
2526
exp_enemy=0,
2627
replay_writer_config=ReplayWriterConfig(replay_name="test_replay", overwrite_existing=True),
2728
print_real_fps=True,
28-
profiler_name=None
29+
profiler_name=None,
2930
)
3031
runner.my_strategy.render()
3132
runner.run()

pixi.lock

Lines changed: 980 additions & 1082 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ authors = [
1010
{ name = "Hamish Starling", email = "hamish.starling22@imperial.ac.uk" },
1111
{ name = "Luke Moran", email = "luke.moran22@imperial.ac.uk" },
1212
{ name = "Sadig Sadikhzada", email = "sadig.sadikhzada23@imperial.ac.uk" },
13-
{ name = "Li Han", email = "han.li124@imperial.ac.uk" }
13+
{ name = "Li Han", email = "han.li124@imperial.ac.uk" },
14+
{ name = "Valentin Bruhl", email = "valentin.bruhl24@imperial.ac.uk" },
15+
{ name = "Sarthak Agarwal", email = "sarthak.agarwal25@imperial.ac.uk" },
16+
{ name = "Low Sze Yoong", email = "szeyoong.low25@imperial.ac.uk" },
17+
{ name = "Jiaming Liu", email = "jiaming.liu25@imperial.ac.uk" },
1418
]
1519
requires-python = ">=3.11"
1620

utama_core/global_utils/math_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def normalise_heading_deg(angle):
5656
The normalized angle in the range [-180, 180] degrees.
5757
"""
5858
half_rev = 180
59-
59+
6060
return (angle + half_rev) % (2 * half_rev) - half_rev
6161

6262

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

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import logging
2-
from numpy.random import normal
32
import random
43
from typing import List, Tuple
54

5+
from numpy.random import normal
6+
67
from utama_core.config.formations import LEFT_START_ONE, RIGHT_START_ONE
78
from utama_core.config.robot_params import RSIM_PARAMS
89
from utama_core.config.settings import (
@@ -13,11 +14,15 @@
1314
)
1415
from utama_core.entities.data.command import RobotResponse
1516
from utama_core.entities.data.raw_vision import RawBallData, RawRobotData, RawVisionData
16-
from utama_core.global_utils.math_utils import deg_to_rad, rad_to_deg, normalise_heading_deg
17+
from utama_core.global_utils.math_utils import (
18+
deg_to_rad,
19+
normalise_heading_deg,
20+
rad_to_deg,
21+
)
1722
from utama_core.rsoccer_simulator.src.Entities import Ball, Frame, Robot
1823
from utama_core.rsoccer_simulator.src.ssl.ssl_gym_base import SSLBaseEnv
19-
from utama_core.rsoccer_simulator.src.Utils.gaussian_noise import RsimGaussianNoise
2024
from utama_core.rsoccer_simulator.src.Utils import KDTree
25+
from utama_core.rsoccer_simulator.src.Utils.gaussian_noise import RsimGaussianNoise
2126

2227
logger = logging.getLogger(__name__)
2328

@@ -38,19 +43,19 @@ class SSLStandardEnv(SSLBaseEnv):
3843
Description:
3944
list of (x, y, theta) coords for each robot to spawn in (in meters and radians).
4045
See the default BLUE_START_ONE/YELLOW_START_ONE for reference.
41-
46+
4247
gaussian_noise
4348
Type: RsimGaussianNoise
4449
Description:
4550
When running in rsim, add Gaussian noise to ball and robots with the given standard deviations.
4651
Mutates the Robot object in place. The 3 parameters are for x (in m), y (in m), and orientation (in degrees)
4752
respectively. Defaults to 0 for each.
48-
53+
4954
vanishing
5055
Type: float
5156
Description:
5257
When running in rsim, cause robots and ball to vanish with the given probability. Defaults to 0.
53-
58+
5459
Observation:
5560
Type: Tuple[FrameData, List[RobotInfo], List[RobotInfo]]
5661
Num Item
@@ -78,7 +83,7 @@ def __init__(
7883
blue_starting_formation: list[tuple] = None,
7984
yellow_starting_formation: list[tuple] = None,
8085
gaussian_noise: RsimGaussianNoise = RsimGaussianNoise(),
81-
vanishing: float = 0
86+
vanishing: float = 0,
8287
):
8388
super().__init__(
8489
field_type=field_type,
@@ -108,11 +113,11 @@ def __init__(
108113
self.latest_observation = (-1, None)
109114

110115
logger.info(f"{n_robots_blue}v{n_robots_yellow} SSL Environment Initialized")
111-
116+
112117
# Adding Gaussian noise and vanishing. Refer to StrategyRunner
113118
self.gaussian_noise = gaussian_noise
114-
115-
assert vanishing >= 0
119+
120+
assert vanishing >= 0, "Negative vanishing probability not allowed"
116121
self.vanishing = vanishing
117122

118123
def reset(self, *, seed=None, options=None):
@@ -172,7 +177,7 @@ def _frame_to_observations(
172177
yellow_robots_info: feedback from individual yellow robots that returns a List[RobotInfo]
173178
blue_robots_info: feedback from individual blue robots that returns a List[RobotInfo]
174179
"""
175-
180+
176181
if self.latest_observation[0] == self.steps:
177182
return self.latest_observation[1]
178183

@@ -189,7 +194,7 @@ def _frame_to_observations(
189194
for i in range(len(self.frame.robots_blue)):
190195
if self._vanishing():
191196
continue
192-
197+
193198
robot = self.frame.robots_blue[i]
194199
robot_pos, robot_info = self._get_robot_observation(robot)
195200
blue_obs.append(robot_pos)
@@ -200,7 +205,7 @@ def _frame_to_observations(
200205
for i in range(len(self.frame.robots_yellow)):
201206
if self._vanishing():
202207
continue
203-
208+
204209
robot = self.frame.robots_yellow[i]
205210
robot_pos, robot_info = self._get_robot_observation(robot)
206211
yellow_obs.append(robot_pos)
@@ -221,7 +226,7 @@ def _frame_to_observations(
221226

222227
def _get_robot_observation(self, robot):
223228
SSLStandardEnv._add_gaussian_noise_robot(robot, self.gaussian_noise)
224-
229+
225230
robot_pos = RawRobotData(robot.id, robot.x, -robot.y, -float(deg_to_rad(robot.theta)), 1)
226231
robot_info = RobotResponse(robot.id, robot.infrared)
227232
return robot_pos, robot_info
@@ -415,47 +420,45 @@ def in_gk_area(obj):
415420

416421
return pos_frame
417422

418-
419423
def _vanishing(self) -> bool:
420424
"""Determines whether a frame should vanish. Only runs after Game Gater is passed"""
421425
return self.steps > 0 and self.vanishing and (random.random() < self.vanishing)
422426

423-
424427
@staticmethod
425428
def _add_gaussian_noise_ball(ball: Ball, noise: RsimGaussianNoise):
426429
"""
427430
When running in rsim, add Gaussian noise to ball with the given standard deviations.
428431
Mutates the Robot object in place.
429-
432+
430433
Args:
431434
noise (RsimGaussianNoise): The 3 parameters are for x (in m), y (in m), and orientation (in degrees) respectively.
432435
Defaults to 0 for each.
433436
"""
434-
437+
435438
if noise.x_stddev:
436439
ball.x += normal(scale=noise.x_stddev)
437-
440+
438441
if noise.y_stddev:
439442
ball.y += normal(scale=noise.y_stddev)
440-
443+
441444
# No noise addition for z, since rSim is 2-D
442-
445+
443446
@staticmethod
444447
def _add_gaussian_noise_robot(robot: Robot, noise: RsimGaussianNoise):
445448
"""
446449
When running in rsim, add Gaussian noise to robot with the given standard deviations.
447450
Mutates the Robot object in place.
448-
451+
449452
Args:
450453
noise (RsimGaussianNoise): The 3 parameters are for x (in m), y (in m), and orientation (in degrees) respectively.
451454
Defaults to 0 for each.
452455
"""
453-
456+
454457
if noise.x_stddev:
455458
robot.x += normal(scale=noise.x_stddev)
456-
459+
457460
if noise.y_stddev:
458461
robot.y += normal(scale=noise.y_stddev)
459-
462+
460463
if noise.th_stddev_deg:
461-
robot.theta = normalise_heading_deg(robot.theta + normal(scale=noise.th_stddev_deg))
464+
robot.theta = normalise_heading_deg(robot.theta + normal(scale=noise.th_stddev_deg))

0 commit comments

Comments
 (0)