Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1,375 changes: 735 additions & 640 deletions pixi.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ authors = ["Florian Vahl <7vahl@informatik.uni-hamburg.de>"]
channels = [
"https://data.bit-bots.de/conda/", # For self hosted ROS / robostack packages
"https://data.bit-bots.de/conda-misc/output/", # For self hosted misc (non-ROS, neural network weights, ...) packages
"robostack-jazzy", # Robostack jazzy channel for ROS packages
"robostack-jazzy", # Robostack jazzy channel for ROS packages
"conda-forge" # General conda-forge channel
]
name = "bitbots_main"
Expand Down Expand Up @@ -208,11 +208,12 @@ webots = ">=2022b,<2023a"
# of pre-commit hooks running after commits from the git-subrepo cli
git-subrepo = ">=0.4.9,<0.5"
clang-format = ">=21.1.0,<22"
cppcheck = ">=2.18.3,<3"
cppcheck = ">=2.18.3,<2.20"
pre-commit = ">=4.4.0,<5"

[feature.ros.pypi-dependencies]
# These are are pypi dependencies needed for our ROS 2 packages
construct = ">=2.10.56, <3"
syrupy = ">=5.0.0, <6"
exhale = ">=0.3.7, <0.4"
mycroft-mimic3-tts = ">=0.2.4, <0.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from typing import Optional

from bitbots_utils.utils import get_parameters_from_other_node
from game_controller_hl_interfaces.msg import GameState
from game_controller_hsl_interfaces.msg import GameState
from std_msgs.msg import Bool

from bitbots_blackboard.capsules import AbstractBlackboardCapsule

Expand All @@ -12,33 +13,44 @@ class GameStatusCapsule(AbstractBlackboardCapsule):
def __init__(self, node, blackboard=None):
super().__init__(node, blackboard)
self.team_id: int = get_parameters_from_other_node(self._node, "parameter_blackboard", ["team_id"])["team_id"]
self.own_id: int = get_parameters_from_other_node(self._node, "parameter_blackboard", ["bot_id"])["bot_id"]
self.gamestate = GameState()
self.last_update: float = 0.0
self.unpenalized_time: float = 0.0
self.last_goal_from_us_time = -86400.0
self.last_goal_time = -86400.0
self.free_kick_kickoff_team: Optional[bool] = None
self.game_controller_stop: bool = False
# publish stopped msg for hcm
self.stop_pub = node.create_publisher(Bool, "game_controller/stop_msg", 1)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is sent to the HCM. I think in the HCM we also subscribe to the Gamestate, so there we could handle it ourselves without needing an extra publisher.


def get_gamestate(self) -> int:
return self.gamestate.game_state
def get_game_state(self) -> int:
# Init, ready, set, playing, finished
return self.gamestate.main_state

def get_secondary_state(self) -> int:
return self.gamestate.secondary_state
def get_game_phase(self) -> int:
# Timeout, Normal, Extratime, Penaltyshoot
return self.gamestate.game_phase

def get_secondary_state_mode(self) -> int:
return self.gamestate.secondary_state_mode
def get_set_play(self) -> int:
# None, Direct Freekick, Indirect Freekick, Penalty, Throw in, Goalkick, Cornerkick,
return self.gamestate.set_play

def get_secondary_team(self) -> int:
return self.gamestate.secondary_state_team
# Team ID, wer in set Play den Ball hat
return self.gamestate.kicking_team

def has_kickoff(self) -> bool:
return self.gamestate.has_kick_off
# vegelcih mit eigener Teamnummer
return self.gamestate.kicking_team == self.team_id

def is_stopped(self) -> bool:
return self.gamestate.stopped

def has_penalty_kick(self) -> bool:
return (
self.gamestate.secondary_state == GameState.STATE_PENALTYKICK
or self.gamestate.secondary_state == GameState.STATE_PENALTYSHOOT
) and self.gamestate._secondary_state_team == self.team_id
self.gamestate.set_play == GameState.SET_PLAY_PENALTY_KICK and self.gamestate.kicking_team == self.team_id
)

def get_our_goals(self) -> int:
return self.gamestate.own_score
Expand All @@ -55,26 +67,17 @@ def get_seconds_since_any_goal(self) -> float:
def get_seconds_remaining(self) -> float:
# Time from the message minus time passed since receiving it
return max(
self.gamestate.seconds_remaining - (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update), 0.0
self.gamestate.secs_remaining - (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update), 0.0
)

def get_secondary_seconds_remaining(self) -> float:
"""Seconds remaining for things like kickoff"""
# Time from the message minus time passed since receiving it
return max(
self.gamestate.secondary_seconds_remaining
- (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update),
self.gamestate.secondary_time - (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update),
0.0,
)

def get_seconds_since_last_drop_ball(self) -> Optional[float]:
"""Returns the seconds since the last drop in"""
if self.gamestate.drop_in_time == -1:
return None
else:
# Time from the message plus seconds passed since receiving it
return self.gamestate.drop_in_time + (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update)

def get_seconds_since_unpenalized(self) -> float:
return self._node.get_clock().now().nanoseconds / 1e9 - self.unpenalized_time

Expand All @@ -87,9 +90,6 @@ def received_gamestate(self) -> bool:
def get_team_id(self) -> int:
return self.team_id

def get_red_cards(self) -> int:
return self.gamestate.team_mates_with_red_card

def gamestate_callback(self, gamestate_msg: GameState) -> None:
if self.gamestate.penalized and not gamestate_msg.penalized:
self.unpenalized_time = self._node.get_clock().now().nanoseconds / 1e9
Expand All @@ -101,21 +101,29 @@ def gamestate_callback(self, gamestate_msg: GameState) -> None:
if gamestate_msg.rival_score > self.gamestate.rival_score:
self.last_goal_time = self._node.get_clock().now().nanoseconds / 1e9

self.game_controller_stop = gamestate_msg.stopped

self.stop_pub.publish(Bool(data=self.game_controller_stop))

"""Anstoß im Falle von Overtime jetzt erstmal nicht genauer geregelt
if (
gamestate_msg.secondary_state_mode == 2
and self.gamestate.secondary_state_mode != 2
and gamestate_msg.game_state == GameState.GAMESTATE_PLAYING
gamestate_msg.main_state == GameState.STATE_SET
and self.gamestate.setPlay != 2
and gamestate_msg.state == GameState.STATE_PLAYING
):
# secondary action is now executed but we will not see this in the new messages.
# it will look like a normal kick off, but we need to remember that this is some sort of free kick
# we set the kickoff value accordingly, then we will not be allowed to move if it is a kick for the others
self.free_kick_kickoff_team = gamestate_msg.secondary_state_team
self.free_kick_kickoff_team = gamestate_msg.kicking_team

if gamestate_msg.secondary_state_mode != 2 and gamestate_msg.secondary_seconds_remaining == 0:
if gamestate_msg.set_play != 2 and gamestate_msg.secondary_time == 0:
self.free_kick_kickoff_team = gamestate_msg.kicking_team

if gamestate_msg.set_play != 2 and gamestate_msg.secondary_time == 0:
self.free_kick_kickoff_team = None

if self.free_kick_kickoff_team is not None:
gamestate_msg.has_kick_off = self.free_kick_kickoff_team == self.team_id

"""
self.last_update = self._node.get_clock().now().nanoseconds / 1e9
self.gamestate = gamestate_msg
2 changes: 1 addition & 1 deletion src/bitbots_behavior/bitbots_blackboard/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<build_depend>bitbots_docs</build_depend>
<depend>bitbots_tf_buffer</depend>
<depend>bitbots_utils</depend>
<depend>game_controller_hl_interfaces</depend>
<depend>game_controller_hsl_interfaces</depend>
<depend>python3-numpy</depend>
<depend>rclpy</depend>
<depend>ros2_numpy</depend>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from bitbots_blackboard.body_blackboard import BodyBlackboard
from dynamic_stack_decider.abstract_decision_element import AbstractDecisionElement
from game_controller_hl_interfaces.msg import GameState
from game_controller_hsl_interfaces.msg import GameState


class GameStateDecider(AbstractDecisionElement):
Expand All @@ -16,18 +16,28 @@ def perform(self, reevaluate=False):
:return:
"""

game_state_number = self.blackboard.gamestate.get_gamestate()
game_state_number = self.blackboard.gamestate.get_game_state()
is_stopped = self.blackboard.gamestate.is_stopped()
# todo this is a temporary hack to make GUI work
if game_state_number == GameState.GAMESTATE_INITIAL:
if is_stopped:
return "STOPPED"
elif game_state_number == GameState.STATE_INITIAL:
return "INITIAL"
elif game_state_number == GameState.GAMESTATE_READY:
elif game_state_number == GameState.STATE_READY:
return "READY"
elif game_state_number == GameState.GAMESTATE_SET:
elif game_state_number == GameState.STATE_SET:
return "SET"
elif game_state_number == GameState.GAMESTATE_PLAYING:
elif game_state_number == GameState.STATE_PLAYING:
return "PLAYING"
elif game_state_number == GameState.GAMESTATE_FINISHED:
elif game_state_number == GameState.STATE_FINISHED:
return "FINISHED"
elif game_state_number == GameState.STATE_STANDBY:
return "STANDBY"
else:
# This should never happen, but all cases required string response
# as we do not get any stack trace otherwise
self.blackboard.node.get_logger().error(f"Received unknown game state number: {game_state_number}")
return "UNKNOWN"

def get_reevaluate(self):
"""
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from bitbots_blackboard.body_blackboard import BodyBlackboard
from dynamic_stack_decider.abstract_decision_element import AbstractDecisionElement
from game_controller_hl_interfaces.msg import GameState
from game_controller_hsl_interfaces.msg import GameState


class SecondaryStateDecider(AbstractDecisionElement):
Expand All @@ -15,28 +15,35 @@ def __init__(self, blackboard, dsd, parameters):
super().__init__(blackboard, dsd, parameters)

def perform(self, reevaluate=False):
state_number = self.blackboard.gamestate.get_secondary_state()
set_play_number = self.blackboard.gamestate.get_set_play()
game_phase_number = self.blackboard.gamestate.get_game_phase()

# todo this is a temporary hack to make GUI work
if state_number == GameState.STATE_NORMAL:
if game_phase_number == GameState.GAME_PHASE_NORMAL and set_play_number == GameState.SET_PLAY_NONE:
return "NORMAL"
elif state_number == GameState.STATE_PENALTYSHOOT:
elif game_phase_number == GameState.GAME_PHASE_PENALTY_SHOOT_OUT and set_play_number == GameState.SET_PLAY_NONE:
return "PENALTYSHOOT"
elif state_number == GameState.STATE_OVERTIME:
elif game_phase_number == GameState.GAME_PHASE_EXTRA_TIME and set_play_number == GameState.SET_PLAY_NONE:
return "OVERTIME"
elif state_number == GameState.STATE_TIMEOUT:
elif game_phase_number == GameState.GAME_PHASE_TIMEOUT and set_play_number == GameState.SET_PLAY_NONE:
return "TIMEOUT"
elif state_number == GameState.STATE_DIRECT_FREEKICK:
elif set_play_number == GameState.SET_PLAY_DIRECT_FREE_KICK:
return "DIRECT_FREEKICK"
elif state_number == GameState.STATE_INDIRECT_FREEKICK:
elif set_play_number == GameState.SET_PLAY_INDIRECT_FREE_KICK:
return "INDIRECT_FREEKICK"
elif state_number == GameState.STATE_PENALTYKICK:
elif set_play_number == GameState.SET_PLAY_PENALTY_KICK:
return "PENALTYKICK"
elif state_number == GameState.STATE_CORNER_KICK:
elif set_play_number == GameState.SET_PLAY_CORNER_KICK:
return "CORNER_KICK"
elif state_number == GameState.STATE_GOAL_KICK:
elif set_play_number == GameState.SET_PLAY_GOAL_KICK:
return "GOAL_KICK"
elif state_number == GameState.STATE_THROW_IN:
elif set_play_number == GameState.SET_PLAY_THROW_IN:
return "THROW_IN"
else:
self.blackboard.node.get_logger().error(
f"Unknown secondary state with game phase {game_phase_number} and set play {set_play_number}"
)
return "UNKNOWN"

def get_reevaluate(self):
"""
Expand All @@ -57,9 +64,9 @@ def __init__(self, blackboard, dsd, parameters):
self.team_id = self.blackboard.gamestate.get_team_id()

def perform(self, reevaluate=False):
state_number = self.blackboard.gamestate.get_secondary_state()
game_phase_number = self.blackboard.gamestate.get_game_phase()
# we have to handle penalty shoot differently because the message is strange
if state_number == GameState.STATE_PENALTYSHOOT:
if game_phase_number == GameState.GAME_PHASE_PENALTY_SHOOT_OUT:
if self.blackboard.gamestate.has_kickoff():
return "OUR"
return "OTHER"
Expand All @@ -73,26 +80,3 @@ def get_reevaluate(self):
Secondary state Team can change during the game
"""
return True


class SecondaryStateModeDecider(AbstractDecisionElement):
"""
Decides which mode in the secondary state we are.
"""

blackboard: BodyBlackboard

def __init__(self, blackboard, dsd, parameters):
super().__init__(blackboard, dsd, parameters)

def perform(self, reevaluate=False):
state_mode = self.blackboard.gamestate.get_secondary_state_mode()
if state_mode == GameState.MODE_PREPARATION:
return "PREPARATION"
elif state_mode == GameState.MODE_PLACING:
return "PLACING"
elif state_mode == GameState.MODE_END:
return "END"

def get_reevaluate(self):
return True
Loading
Loading