Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
470aae7
use ci_cmd_with_delay instead of threading.Timer (#3952)
annieisawesome2 Sep 13, 2026
0d0b8e2
x
StarrryNight Sep 13, 2026
0e3a806
Refactor all play selection logic into PlaySelectionFSM (#3902)
Apeiros-46B Sep 14, 2026
323b750
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Sep 15, 2026
8ec3bfc
dynamicaly compute matrices after vision rollback instead of using ol…
StarrryNight Sep 13, 2026
a3d97c2
store post update/prediction state, instead of the state before for c…
StarrryNight Sep 13, 2026
4b99c94
fix rollback cnsistency
StarrryNight Sep 13, 2026
4fe2930
generateMeasurementModel for better clarity in code
StarrryNight Sep 13, 2026
46c5083
store enum instead of full matrix
StarrryNight Sep 13, 2026
c4e6ba6
reorder for clairy
StarrryNight Sep 13, 2026
38df81b
remove filter step structs
StarrryNight Sep 13, 2026
c8d1025
keep removing filter step structs
StarrryNight Sep 13, 2026
fb2ca0d
Use mandatory FilterStepType insted of optionals checking
StarrryNight Sep 13, 2026
2ecadf1
rename step to predict for discrete filters consistency
StarrryNight Sep 13, 2026
8c3d36a
add getters for pev target velocities
StarrryNight Sep 13, 2026
d138f5e
wire up prediction
StarrryNight Sep 13, 2026
f2e9dd6
rely on control input for velcoity and predicted position
StarrryNight Sep 13, 2026
17c7e9f
implmeent process model
Thunderbots Sep 13, 2026
1f03db1
jacobian and integration
StarrryNight Sep 13, 2026
c0224c7
x
StarrryNight Sep 13, 2026
0dc69d1
build
StarrryNight Sep 13, 2026
e10f9ac
rebase
Thunderbots Sep 15, 2026
6866019
add csv ogging
StarrryNight Sep 13, 2026
72c23aa
add testgin
StarrryNight Sep 13, 2026
07ba21e
merge and se real ocalzer for plot juggler
StarrryNight Sep 13, 2026
4871551
fi logign
StarrryNight Sep 13, 2026
1e9da21
x
StarrryNight Sep 13, 2026
71ddbfa
cleanup
StarrryNight Sep 15, 2026
b039079
q
StarrryNight Sep 15, 2026
07a8695
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Sep 15, 2026
4dd4098
Merge branch 'samuel/use_ekf_push' of https://github.com/UBC-Thunderb…
Thunderbots Sep 15, 2026
2c8abf3
fix
Thunderbots Sep 15, 2026
5f7ee4c
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Sep 15, 2026
e1ec566
move folder
Thunderbots Sep 15, 2026
edb5a17
localizer constants
Thunderbots Sep 15, 2026
9e205e1
use variables
Thunderbots Sep 15, 2026
2feaa75
use constants
Thunderbots Sep 15, 2026
ba79215
implemetnq
Thunderbots Sep 15, 2026
19bb605
update path
Thunderbots Sep 15, 2026
151e204
add more constants and fix build
Thunderbots Sep 15, 2026
91f0e6f
fix bug
Thunderbots Sep 15, 2026
ec3b4f6
merge
Thunderbots Sep 15, 2026
754876c
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Sep 15, 2026
3288ed4
merge
Thunderbots Sep 15, 2026
f8a616e
Merge branch 'samuel/refactor_robot_localizer' of https://github.com/…
Thunderbots Sep 15, 2026
3d20adf
merge
Thunderbots Sep 15, 2026
77182b5
i hate stack merging
Thunderbots Sep 15, 2026
7f7c54b
Merge branch 'samuel/use_ekf_push' into samuel/post_ekf_cleanup
Thunderbots Sep 15, 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
8 changes: 8 additions & 0 deletions docs/fsm-diagrams.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,24 @@ direction LR
Halt --> Stop : [gameStateStopped]\n<i>setupStopPlay</i>
Halt --> Playing : [gameStatePlaying]\n<i>setupOffensePlay</i>
Halt --> SetPlay : [gameStateSetupRestart]\n<i>setupSetPlay</i>
Halt --> Halt : <i>setupOverridePlay</i>
Halt --> Halt : <i>resetPlaySelection</i>
Stop --> Halt : [gameStateHalted]\n<i>setupHaltPlay</i>
Stop --> Playing : [gameStatePlaying]\n<i>setupOffensePlay</i>
Stop --> SetPlay : [gameStateSetupRestart]\n<i>setupSetPlay</i>
Stop --> Stop : <i>setupOverridePlay</i>
Stop --> Halt : <i>resetPlaySelection</i>
Playing --> Halt : [gameStateHalted]\n<i>setupHaltPlay</i>
Playing --> Stop : [gameStateStopped]\n<i>setupStopPlay</i>
Playing --> SetPlay : [gameStateSetupRestart]\n<i>setupSetPlay</i>
Playing --> Playing : <i>setupOverridePlay</i>
Playing --> Halt : <i>resetPlaySelection</i>
SetPlay --> Halt : [gameStateHalted]\n<i>resetSetPlay, setupHaltPlay</i>
SetPlay --> Stop : [gameStateStopped]\n<i>resetSetPlay, setupStopPlay</i>
SetPlay --> Playing : [gameStatePlaying]\n<i>resetSetPlay, setupOffensePlay</i>
SetPlay --> SetPlay : [gameStateSetupRestart]\n<i>setupSetPlay</i>
SetPlay --> SetPlay : <i>setupOverridePlay</i>
SetPlay --> Halt : <i>resetPlaySelection</i>
Terminate:::terminate --> Terminate:::terminate

```
Expand Down
3 changes: 1 addition & 2 deletions src/shared/robot_constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ RobotConstants createRobotConstants()
.kalman_process_noise_variance_rad_per_s_4 = 1.0f,
.kalman_vision_noise_variance_rad_2 = 0.0001f,
.kalman_motor_sensor_noise_variance_rad_per_s_2 = 0.5f,
.kalman_motor_sensor_noise_variance_m_per_s_2 = 0.05f
};
.kalman_motor_sensor_noise_variance_m_per_s_2 = 0.05f};
}
#elif CHECK_VERSION(2021)
constexpr RobotConstants createRobotConstants()
Expand Down
56 changes: 14 additions & 42 deletions src/software/ai/ai.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@

#include <Tracy.hpp>

#include "software/ai/hl/stp/play/halt_play/halt_play.h"
#include "software/ai/hl/stp/play/play_factory.h"
#include "software/tracy/tracy_constants.h"


Ai::Ai(std::shared_ptr<const TbotsProto::AiConfig> ai_config_ptr)
: ai_config_ptr(ai_config_ptr),
fsm(std::make_unique<FSM<PlaySelectionFSM>>(PlaySelectionFSM{ai_config_ptr})),
override_play(nullptr),
current_play(std::make_unique<HaltPlay>(ai_config_ptr)),
ai_config_changed(false)
{
auto current_override = ai_config_ptr->ai_control_config().override_ai_play();
Expand All @@ -26,12 +23,11 @@ Ai::Ai(std::shared_ptr<const TbotsProto::AiConfig> ai_config_ptr)

void Ai::overridePlay(std::unique_ptr<Play> play)
{
override_play = std::move(play);
fsm->process_event(PlaySelectionFSM::Override(std::move(play)));
}

void Ai::overridePlayFromProto(TbotsProto::Play play_proto)
{
current_override_play_proto = play_proto;
overridePlay(std::move(createPlay(play_proto, ai_config_ptr)));
}

Expand All @@ -44,23 +40,17 @@ void Ai::checkAiConfig()
{
if (ai_config_changed)
{
ai_config_changed = false;

fsm = std::make_unique<FSM<PlaySelectionFSM>>(PlaySelectionFSM{ai_config_ptr});

auto current_override = ai_config_ptr->ai_control_config().override_ai_play();
std::unique_ptr<Play> override_play;
if (current_override != TbotsProto::PlayName::UseAiSelection)
{
// Override to new play if we're not running Ai Selection
TbotsProto::Play play_proto;
play_proto.set_name(current_override);
overridePlayFromProto(play_proto);
}
else
{
// Clear play override if we're running Ai Selection
overridePlay(nullptr);
override_play = createPlay(play_proto, ai_config_ptr);
}

fsm->process_event(PlaySelectionFSM::Reset(std::move(override_play)));
ai_config_changed = false;
}
}

Expand All @@ -70,25 +60,12 @@ std::unique_ptr<TbotsProto::PrimitiveSet> Ai::getPrimitives(const WorldPtr& worl

checkAiConfig();

fsm->process_event(PlaySelectionFSM::Update([this](std::unique_ptr<Play> play)
{ current_play = std::move(play); },
world_ptr->gameState(), *ai_config_ptr));
fsm->process_event(PlaySelectionFSM::Update(world_ptr->gameState(), *ai_config_ptr));

std::unique_ptr<TbotsProto::PrimitiveSet> primitive_set;
if (static_cast<bool>(override_play))
{
primitive_set = override_play->get(world_ptr, inter_play_communication,
[this](InterPlayCommunication comm) {
inter_play_communication = std::move(comm);
});
}
else
{
primitive_set = current_play->get(world_ptr, inter_play_communication,
[this](InterPlayCommunication comm) {
inter_play_communication = std::move(comm);
});
}
auto primitive_set = static_cast<PlaySelectionFSM&>(*fsm).getSelectedPlay().get(
world_ptr, inter_play_communication,
[this](InterPlayCommunication comm)
{ inter_play_communication = std::move(comm); });

FrameMarkEnd(TracyConstants::AI_FRAME_MARKER);

Expand All @@ -97,14 +74,9 @@ std::unique_ptr<TbotsProto::PrimitiveSet> Ai::getPrimitives(const WorldPtr& worl

TbotsProto::PlayInfo Ai::getPlayInfo() const
{
std::vector<std::string> play_state = current_play->getState();
auto tactic_robot_id_assignment = current_play->getTacticRobotIdAssignment();

if (static_cast<bool>(override_play))
{
play_state = override_play->getState();
tactic_robot_id_assignment = override_play->getTacticRobotIdAssignment();
}
Play& selected_play = static_cast<const PlaySelectionFSM&>(*fsm).getSelectedPlay();
const std::vector<std::string> play_state = selected_play.getState();
auto tactic_robot_id_assignment = selected_play.getTacticRobotIdAssignment();

TbotsProto::PlayInfo info;

Expand Down
3 changes: 0 additions & 3 deletions src/software/ai/ai.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ class Ai final

std::shared_ptr<const TbotsProto::AiConfig> ai_config_ptr;
std::unique_ptr<FSM<PlaySelectionFSM>> fsm;
std::unique_ptr<Play> override_play;
std::unique_ptr<Play> current_play;
TbotsProto::Play current_override_play_proto;
bool ai_config_changed;

// inter play communication
Expand Down
15 changes: 5 additions & 10 deletions src/software/ai/hl/stp/play/kickoff_play_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
# +------------------+------------------+
# After ball leaves center: half/CC rules no longer enforced here.

import threading

import proto.import_all_protos as protos
import pytest
import software.python_bindings as tbots_cpp
Expand All @@ -33,6 +31,8 @@
RobotNeverEntersRegion,
)

NORMAL_START_DELAY_S = 4.0


@pytest.mark.parametrize("is_friendly_test", [True, False])
def test_kickoff_play(simulated_test_runner, is_friendly_test):
Expand Down Expand Up @@ -87,14 +87,6 @@ def setup(*args):
blue_play = protos.PlayName.KickoffEnemyPlay
yellow_play = protos.PlayName.KickoffFriendlyPlay

# Let robots get ready before starting kickoff
threading.Timer(
4.0,
lambda: simulated_test_runner.send_gamecontroller_command(
gc_command=protos.Command.Type.NORMAL_START, team=SslTeam.BLUE
),
).start()

simulated_test_runner.set_plays(blue_play=blue_play, yellow_play=yellow_play)

# Always Validation
Expand Down Expand Up @@ -160,6 +152,9 @@ def setup(*args):
setup=setup,
inv_eventually_validation_sequence_set=eventually_validation_sequence_set,
inv_always_validation_sequence_set=always_validation_sequence_set,
ci_cmd_with_delay=[
(NORMAL_START_DELAY_S, protos.Command.Type.NORMAL_START, SslTeam.BLUE),
],
test_timeout_s=10,
)

Expand Down
56 changes: 43 additions & 13 deletions src/software/ai/play_selection_fsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,25 @@

PlaySelectionFSM::PlaySelectionFSM(
std::shared_ptr<const TbotsProto::AiConfig> ai_config_ptr)
: ai_config_ptr(ai_config_ptr), current_set_play(std::nullopt)
: ai_config_ptr(ai_config_ptr),
current_set_play(std::nullopt),
current_play(std::make_shared<HaltPlay>(ai_config_ptr)),
override_play(nullptr)
{
}

Play& PlaySelectionFSM::getSelectedPlay() const
{
if (override_play)
{
return *override_play;
}
else
{
return *current_play;
}
}

bool PlaySelectionFSM::gameStateStopped(const Update& event)
{
return event.game_state.isStopped();
Expand All @@ -39,63 +54,73 @@ bool PlaySelectionFSM::gameStateSetupRestart(const Update& event)
return event.game_state.isSetupRestart();
}

void PlaySelectionFSM::setupOverridePlay(const Override& event)
{
override_play = event.play;
}

void PlaySelectionFSM::resetPlaySelection(const Reset& event)
{
current_set_play.reset();
setupOverridePlay(event);
}

void PlaySelectionFSM::setupSetPlay(const Update& event)
{
if (event.game_state.isOurBallPlacement())
{
if (current_set_play != TbotsProto::PlayName::BallPlacementPlay)
{
current_set_play = TbotsProto::PlayName::BallPlacementPlay;
event.set_current_play(std::make_unique<BallPlacementPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<BallPlacementPlay>(ai_config_ptr));
}
}
else if (event.game_state.isTheirBallPlacement())
{
if (current_set_play != TbotsProto::PlayName::EnemyBallPlacementPlay)
{
current_set_play = TbotsProto::PlayName::EnemyBallPlacementPlay;
event.set_current_play(
std::make_unique<EnemyBallPlacementPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<EnemyBallPlacementPlay>(ai_config_ptr));
}
}
else if (event.game_state.isOurKickoff())
{
if (current_set_play != TbotsProto::PlayName::KickoffFriendlyPlay)
{
current_set_play = TbotsProto::PlayName::KickoffFriendlyPlay;
event.set_current_play(std::make_unique<KickoffFriendlyPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<KickoffFriendlyPlay>(ai_config_ptr));
}
}
else if (event.game_state.isTheirKickoff())
{
if (current_set_play != TbotsProto::PlayName::KickoffEnemyPlay)
{
current_set_play = TbotsProto::PlayName::KickoffEnemyPlay;
event.set_current_play(std::make_unique<KickoffEnemyPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<KickoffEnemyPlay>(ai_config_ptr));
}
}
else if (event.game_state.isOurPenalty())
{
if (current_set_play != TbotsProto::PlayName::PenaltyKickPlay)
{
current_set_play = TbotsProto::PlayName::PenaltyKickPlay;
event.set_current_play(std::make_unique<PenaltyKickPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<PenaltyKickPlay>(ai_config_ptr));
}
}
else if (event.game_state.isTheirPenalty())
{
if (current_set_play != TbotsProto::PlayName::PenaltyKickEnemyPlay)
{
current_set_play = TbotsProto::PlayName::PenaltyKickEnemyPlay;
event.set_current_play(std::make_unique<PenaltyKickEnemyPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<PenaltyKickEnemyPlay>(ai_config_ptr));
}
}
else if (event.game_state.isOurDirectFree() || event.game_state.isOurIndirectFree())
{
if (current_set_play != TbotsProto::PlayName::FreeKickPlay)
{
current_set_play = TbotsProto::PlayName::FreeKickPlay;
event.set_current_play(std::make_unique<FreeKickPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<FreeKickPlay>(ai_config_ptr));
}
}
else if (event.game_state.isTheirDirectFree() ||
Expand All @@ -104,27 +129,32 @@ void PlaySelectionFSM::setupSetPlay(const Update& event)
if (current_set_play != TbotsProto::PlayName::EnemyFreeKickPlay)
{
current_set_play = TbotsProto::PlayName::EnemyFreeKickPlay;
event.set_current_play(std::make_unique<EnemyFreeKickPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<EnemyFreeKickPlay>(ai_config_ptr));
}
}
}

void PlaySelectionFSM::setupStopPlay(const Update& event)
{
event.set_current_play(std::make_unique<StopPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<StopPlay>(ai_config_ptr));
}

void PlaySelectionFSM::setupHaltPlay(const Update& event)
{
event.set_current_play(std::make_unique<HaltPlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<HaltPlay>(ai_config_ptr));
}

void PlaySelectionFSM::setupOffensePlay(const Update& event)
{
event.set_current_play(std::make_unique<OffensePlay>(ai_config_ptr));
setCurrentPlay(std::make_unique<OffensePlay>(ai_config_ptr));
}

void PlaySelectionFSM::resetSetPlay(const Update& event)
{
current_set_play.reset();
}

void PlaySelectionFSM::setCurrentPlay(std::unique_ptr<Play> play)
{
current_play = std::move(play);
}
Loading