Skip to content

Commit 6781439

Browse files
committed
Merge branch 'feature/analytical_ik' of github.com:bit-bots/bitbots_main into feature/analytical_ik
2 parents d978e60 + 20dd8e2 commit 6781439

182 files changed

Lines changed: 19938 additions & 2962 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
exclude: |
2+
(?x)(
3+
^src/lib/|
4+
^src/bitbots_team_communication/bitbots_team_communication/bitbots_team_communication/RobocupProtocol/
5+
)
6+
17
repos:
28
- repo: https://github.com/astral-sh/ruff-pre-commit
39
rev: v0.9.6
@@ -14,7 +20,6 @@ repos:
1420
args:
1521
- "-i"
1622
- id: cppcheck
17-
exclude: &exclude_robocup_protocol ^src/bitbots_team_communication/bitbots_team_communication/bitbots_team_communication/RobocupProtocol/
1823
args:
1924
- "--inline-suppr"
2025
- "--suppress=missingIncludeSystem"
@@ -28,13 +33,11 @@ repos:
2833
hooks:
2934
- id: cmake-format
3035
- id: cmake-lint
31-
exclude: *exclude_robocup_protocol
3236
- repo: https://github.com/pre-commit/pre-commit-hooks
3337
rev: v5.0.0
3438
hooks:
3539
- id: check-merge-conflict
3640
- id: check-toml
3741
- id: check-xml
3842
- id: check-yaml
39-
exclude: *exclude_robocup_protocol
4043
- id: detect-private-key

pixi.lock

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

pixi.toml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ authors = ["Florian Vahl <7vahl@informatik.uni-hamburg.de>"]
33
channels = [
44
"https://data.bit-bots.de/conda/", # For self hosted ROS / robostack packages
55
"https://data.bit-bots.de/conda-misc/output/", # For self hosted misc (non-ROS, neural network weights, ...) packages
6-
"robostack-jazzy", # Robostack jazzy channel for ROS packages
6+
"robostack-jazzy", # Robostack jazzy channel for ROS packages
77
"conda-forge" # General conda-forge channel
88
]
99
name = "bitbots_main"
@@ -37,6 +37,7 @@ readline = ">=8.2.999" # This is needed because we want to install an empty rea
3737

3838
[feature.ros.dependencies]
3939
# Misc dependencies for our ROS 2 packages
40+
alsa-plugins = ">=1.2.12,<2"
4041
beartype = ">=0.22.6,<0.23"
4142
breathe = ">=4.36.0,<5"
4243
cmake = "<3.30" # Constraint to avoid issues with deprecated features in newer cmake versions
@@ -51,6 +52,7 @@ ipython = ">=9.7.0,<10"
5152
jaxtyping = ">=0.3.2,<0.4"
5253
jinja2 = ">=3.1.6,<4"
5354
libboost-devel = ">=1.86.0,<2"
55+
libflac = "==1.4.3"
5456
libnuma = ">=2.0.18,<3"
5557
libopencv = ">=4.11.0,<5"
5658
libprotobuf = ">=6.31.1,<7"
@@ -62,6 +64,7 @@ opencv = ">=4.11.0,<5"
6264
paramiko = ">=4.0.0,<5"
6365
pkg-config = ">=0.29.2,<0.30"
6466
playsound = ">=1.3.0,<2"
67+
portaudio = ">=19.7.0,<20"
6568
protobuf = ">=6.31.1,<7"
6669
psutil = ">=7.1.3,<8"
6770
pthread-stubs = ">=0.4,<0.5"
@@ -84,6 +87,7 @@ colcon-common-extensions = ">=0.3.0,<0.4"
8487
colcon-notification = ">=0.3.0,<0.4"
8588
ros-jazzy-action-msgs = ">=2.0.3,<3"
8689
ros-jazzy-ament-cmake = ">=2.5.4,<3"
90+
ros-jazzy-ament-cmake-clang-format = ">=0.17.3,<0.18"
8791
ros-jazzy-ament-cmake-mypy = ">=0.17.3,<0.18"
8892
ros-jazzy-ament-copyright = ">=0.17.3,<0.18"
8993
ros-jazzy-ament-flake8 = ">=0.17.3,<0.18"
@@ -131,6 +135,7 @@ ros-jazzy-rosbridge-suite = ">=2.3.0,<3"
131135
ros-jazzy-rosgraph-msgs = ">=2.0.3,<3"
132136
ros-jazzy-rosidl-default-generators = ">=1.6.0,<2"
133137
ros-jazzy-rosidl-default-runtime = ">=1.6.0,<2"
138+
ros-jazzy-rosidl-runtime-c = ">=4.6.7,<5"
134139
ros-jazzy-rqt-gui = ">=1.6.0,<2"
135140
ros-jazzy-rqt-gui-py = ">=1.6.0,<2"
136141
ros-jazzy-sensor-msgs = ">=5.3.6,<6"
@@ -160,12 +165,11 @@ ros-jazzy-soccer-vision-3d-rviz-markers = ">=1.0.0,<2"
160165
ros-jazzy-soccer-vision-3d-msgs = ">=1.0.0,<2"
161166
ros-jazzy-soccer-vision-attribute-msgs = ">=1.0.0,<2"
162167
ros-jazzy-rot-conv = ">=1.1.0,<2"
163-
ros-jazzy-bio-ik = ">=2.0.0,<3"
168+
# ros-jazzy-bio-ik = ">=2.0.0,<3"
164169
ros-jazzy-bio-ik-msgs = ">=0.0.0,<0.0.1"
165170
ros-jazzy-biped-interfaces = ">=0.0.0,<0.0.1"
166171
ros-jazzy-bitbots-tf-buffer = ">=1.0.0,<2"
167172
ros-jazzy-ros2-python-extension = ">=1.0.0,<2"
168-
ros-jazzy-audio-common = ">=0.3.15,<0.4"
169173
ros-jazzy-dynamic-stack-decider = ">=0.5.3,<0.6"
170174
ros-jazzy-dynamic-stack-decider-visualization = ">=0.2.1,<0.3"
171175
ros-jazzy-game-controller-hl = ">=1.1.0,<2"
@@ -203,12 +207,17 @@ make = ">=4.4.1,<5" # To avoid issues in the CI
203207
webots = ">=2022b,<2023a"
204208

205209
[feature.format.dependencies]
210+
# Add git-subrepo to format dependencies as currently there are
211+
# sqlite conflicts in the default/ros environment preventing execution
212+
# of pre-commit hooks running after commits from the git-subrepo cli
213+
git-subrepo = ">=0.4.9,<0.5"
206214
clang-format = ">=21.1.0,<22"
207-
cppcheck = ">=2.18.3,<3"
215+
cppcheck = ">=2.18.3,<2.20"
208216
pre-commit = ">=4.4.0,<5"
209217

210218
[feature.ros.pypi-dependencies]
211219
# These are are pypi dependencies needed for our ROS 2 packages
220+
construct = ">=2.10.56, <3"
212221
syrupy = ">=5.0.0, <6"
213222
exhale = ">=0.3.7, <0.4"
214223
mycroft-mimic3-tts = ">=0.2.4, <0.3"
@@ -233,6 +242,7 @@ RMW_IMPLEMENTATION = "rmw_cyclonedds_cpp"
233242
COLCON_LOG_LEVEL = "30"
234243
PYTHONWARNINGS="ignore:::setuptools.command.install,ignore:::setuptools.command.easy_install,ignore:::pkg_resources,ignore:easy_install command is deprecated,ignore:setup.py install is deprecated"
235244
WEBOTS_HOME = "$CONDA_PREFIX/share/webots"
245+
ALSA_PLUGIN_DIR = "/usr/lib/x86_64-linux-gnu/alsa-lib"
236246

237247
[feature.ros.activation]
238248
scripts = [
@@ -244,3 +254,4 @@ scripts = [
244254
default = ["ros", "format"] # Full development environment (excluding robot-only deps)
245255
format = ["format"] # Format only environment
246256
robot = ["ros", "format", "robot"] # Robot environment with additional robot-only dependencies
257+

scripts/deploy/tasks/build.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def _clean(self, connections: Group) -> GroupResult:
4242
:return: The results of the task.
4343
"""
4444
print_debug(f"Cleaning the following packages before building: {self._package}")
45-
cmd_clean = f"cd {self._remote_workspace} && pixi run clean {self._package}"
45+
cmd_clean = f"cd {self._remote_workspace} && pixi run --environment robot clean {self._package}"
4646

4747
print_debug(f"Calling '{cmd_clean}'")
4848
try:
@@ -62,7 +62,7 @@ def _build(self, connections: Group) -> GroupResult:
6262
:return: The results of the task.
6363
"""
6464
print_debug("Building packages")
65-
cmd = f"cd {self._remote_workspace} && pixi run build {self._package}"
65+
cmd = f"cd {self._remote_workspace} && pixi run --environment robot build {self._package}"
6666

6767
print_debug(f"Calling '{cmd}'")
6868
try:

scripts/deploy/tasks/launch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def _check_nodes_already_running(self, connections: Group) -> GroupResult:
5555
:return: Results, with success if ROS 2 nodes are not already running
5656
"""
5757
print_debug("Checking if ROS 2 nodes are already running")
58-
cmd = f"cd {self._remote_workspace} && pixi run ros2 node list -c"
58+
cmd = f"cd {self._remote_workspace} && pixi run --environment robot ros2 node list -c"
5959

6060
print_debug(f"Calling '{cmd}'")
6161
try:
@@ -152,7 +152,7 @@ def _check_tmux_session_already_running(self, connections: Group) -> GroupResult
152152
def _launch_teamplayer(self, connections: Group) -> GroupResult:
153153
print_debug("Launching teamplayer")
154154
# Create tmux session
155-
cmd = f"tmux new-session -d -s {self._tmux_session_name} && tmux send-keys -t {self._tmux_session_name} 'cd {self._remote_workspace} && pixi run ros2 launch bitbots_bringup teamplayer.launch record:=true tts:=false' Enter"
155+
cmd = f"tmux new-session -d -s {self._tmux_session_name} && tmux send-keys -t {self._tmux_session_name} 'cd {self._remote_workspace} && pixi run --environment robot ros2 launch bitbots_bringup teamplayer.launch record:=true tts:=false' Enter"
156156

157157
print_debug(f"Calling '{cmd}'")
158158
try:

scripts/deploy/tasks/sync.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ def _sync_single(connection: Connection) -> Result | None:
6767
print_error(f"Rsync command failed to execute on host {connection.host}")
6868
return
6969

70-
# Verify syncing succeeded and system architectures matches by running "pixi install"
71-
cmd = f"cd {self._remote_workspace} && pixi install"
70+
# Verify syncing succeeded and system architectures matches by running "pixi install --environment robot"
71+
cmd = f"cd {self._remote_workspace} && pixi install --environment robot"
7272
print_debug("Installing dependencies on remote host to verify synchronization and architecture match.")
7373
print_debug(f"Calling '{cmd}' on: {connection.host}")
7474
verify_result = connection.run(cmd, hide=hide_output())
Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from typing import Optional
2-
31
from bitbots_utils.utils import get_parameters_from_other_node
4-
from game_controller_hl_interfaces.msg import GameState
2+
from builtin_interfaces.msg import Time as TimeMsg
3+
from game_controller_hsl_interfaces.msg import GameState
4+
from rclpy.time import Time
5+
from std_msgs.msg import Bool
56

67
from bitbots_blackboard.capsules import AbstractBlackboardCapsule
78

@@ -12,33 +13,45 @@ class GameStatusCapsule(AbstractBlackboardCapsule):
1213
def __init__(self, node, blackboard=None):
1314
super().__init__(node, blackboard)
1415
self.team_id: int = get_parameters_from_other_node(self._node, "parameter_blackboard", ["team_id"])["team_id"]
16+
self.own_id: int = get_parameters_from_other_node(self._node, "parameter_blackboard", ["bot_id"])["bot_id"]
1517
self.gamestate = GameState()
1618
self.last_update: float = 0.0
1719
self.unpenalized_time: float = 0.0
1820
self.last_goal_from_us_time = -86400.0
1921
self.last_goal_time = -86400.0
20-
self.free_kick_kickoff_team: Optional[bool] = None
22+
self.free_kick_kickoff_team: bool | None = None
23+
self.game_controller_stop: bool = False
24+
self.last_timestep_whistle_detected: Time | None = None
25+
# publish stopped msg for hcm
26+
self.stop_pub = node.create_publisher(Bool, "game_controller/stop_msg", 1)
2127

22-
def get_gamestate(self) -> int:
23-
return self.gamestate.game_state
28+
def get_game_state(self) -> int:
29+
# Init, ready, set, playing, finished
30+
return self.gamestate.main_state
2431

25-
def get_secondary_state(self) -> int:
26-
return self.gamestate.secondary_state
32+
def get_game_phase(self) -> int:
33+
# Timeout, Normal, Extratime, Penaltyshoot
34+
return self.gamestate.game_phase
2735

28-
def get_secondary_state_mode(self) -> int:
29-
return self.gamestate.secondary_state_mode
36+
def get_set_play(self) -> int:
37+
# None, Direct Freekick, Indirect Freekick, Penalty, Throw in, Goalkick, Cornerkick,
38+
return self.gamestate.set_play
3039

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

3444
def has_kickoff(self) -> bool:
35-
return self.gamestate.has_kick_off
45+
# vegelcih mit eigener Teamnummer
46+
return self.gamestate.kicking_team == self.team_id
47+
48+
def is_stopped(self) -> bool:
49+
return self.gamestate.stopped
3650

3751
def has_penalty_kick(self) -> bool:
3852
return (
39-
self.gamestate.secondary_state == GameState.STATE_PENALTYKICK
40-
or self.gamestate.secondary_state == GameState.STATE_PENALTYSHOOT
41-
) and self.gamestate._secondary_state_team == self.team_id
53+
self.gamestate.set_play == GameState.SET_PLAY_PENALTY_KICK and self.gamestate.kicking_team == self.team_id
54+
)
4255

4356
def get_our_goals(self) -> int:
4457
return self.gamestate.own_score
@@ -55,26 +68,17 @@ def get_seconds_since_any_goal(self) -> float:
5568
def get_seconds_remaining(self) -> float:
5669
# Time from the message minus time passed since receiving it
5770
return max(
58-
self.gamestate.seconds_remaining - (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update), 0.0
71+
self.gamestate.secs_remaining - (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update), 0.0
5972
)
6073

6174
def get_secondary_seconds_remaining(self) -> float:
6275
"""Seconds remaining for things like kickoff"""
6376
# Time from the message minus time passed since receiving it
6477
return max(
65-
self.gamestate.secondary_seconds_remaining
66-
- (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update),
78+
self.gamestate.secondary_time - (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update),
6779
0.0,
6880
)
6981

70-
def get_seconds_since_last_drop_ball(self) -> Optional[float]:
71-
"""Returns the seconds since the last drop in"""
72-
if self.gamestate.drop_in_time == -1:
73-
return None
74-
else:
75-
# Time from the message plus seconds passed since receiving it
76-
return self.gamestate.drop_in_time + (self._node.get_clock().now().nanoseconds / 1e9 - self.last_update)
77-
7882
def get_seconds_since_unpenalized(self) -> float:
7983
return self._node.get_clock().now().nanoseconds / 1e9 - self.unpenalized_time
8084

@@ -87,9 +91,6 @@ def received_gamestate(self) -> bool:
8791
def get_team_id(self) -> int:
8892
return self.team_id
8993

90-
def get_red_cards(self) -> int:
91-
return self.gamestate.team_mates_with_red_card
92-
9394
def gamestate_callback(self, gamestate_msg: GameState) -> None:
9495
if self.gamestate.penalized and not gamestate_msg.penalized:
9596
self.unpenalized_time = self._node.get_clock().now().nanoseconds / 1e9
@@ -101,21 +102,32 @@ def gamestate_callback(self, gamestate_msg: GameState) -> None:
101102
if gamestate_msg.rival_score > self.gamestate.rival_score:
102103
self.last_goal_time = self._node.get_clock().now().nanoseconds / 1e9
103104

105+
self.game_controller_stop = gamestate_msg.stopped
106+
107+
self.stop_pub.publish(Bool(data=self.game_controller_stop))
108+
109+
"""Anstoß im Falle von Overtime jetzt erstmal nicht genauer geregelt
104110
if (
105-
gamestate_msg.secondary_state_mode == 2
106-
and self.gamestate.secondary_state_mode != 2
107-
and gamestate_msg.game_state == GameState.GAMESTATE_PLAYING
111+
gamestate_msg.main_state == GameState.STATE_SET
112+
and self.gamestate.setPlay != 2
113+
and gamestate_msg.state == GameState.STATE_PLAYING
108114
):
109115
# secondary action is now executed but we will not see this in the new messages.
110116
# it will look like a normal kick off, but we need to remember that this is some sort of free kick
111117
# we set the kickoff value accordingly, then we will not be allowed to move if it is a kick for the others
112-
self.free_kick_kickoff_team = gamestate_msg.secondary_state_team
118+
self.free_kick_kickoff_team = gamestate_msg.kicking_team
119+
120+
if gamestate_msg.set_play != 2 and gamestate_msg.secondary_time == 0:
121+
self.free_kick_kickoff_team = gamestate_msg.kicking_team
113122
114-
if gamestate_msg.secondary_state_mode != 2 and gamestate_msg.secondary_seconds_remaining == 0:
123+
if gamestate_msg.set_play != 2 and gamestate_msg.secondary_time == 0:
115124
self.free_kick_kickoff_team = None
116125
117126
if self.free_kick_kickoff_team is not None:
118127
gamestate_msg.has_kick_off = self.free_kick_kickoff_team == self.team_id
119-
128+
"""
120129
self.last_update = self._node.get_clock().now().nanoseconds / 1e9
121130
self.gamestate = gamestate_msg
131+
132+
def whistle_detection_callback(self, msg: TimeMsg) -> None:
133+
self.last_timestep_whistle_detected = Time.from_msg(msg)

src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/world_model_capsule.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,10 @@ def __init__(self, node, blackboard):
5858
self.map_margin: float = self._node.get_parameter("map_margin").value
5959

6060
# Ball state
61+
# The ball in the map frame (default to the center of the field if ball is not seen yet)
6162
self._ball: PointStamped = PointStamped(
62-
header=Header(stamp=Time(clock_type=ClockType.ROS_TIME), frame_id=self.map_frame)
63-
) # The ball in the map frame (default to the center of the field if ball is not seen yet)
63+
header=Header(stamp=Time(clock_type=ClockType.ROS_TIME).to_msg(), frame_id=self.map_frame)
64+
)
6465
self._ball_covariance: np.ndarray = np.zeros((2, 2)) # Covariance of the ball
6566

6667
# Publisher for visualization in RViZ

src/bitbots_behavior/bitbots_blackboard/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<build_depend>bitbots_docs</build_depend>
2424
<depend>bitbots_tf_buffer</depend>
2525
<depend>bitbots_utils</depend>
26-
<depend>game_controller_hl_interfaces</depend>
26+
<depend>game_controller_hsl_interfaces</depend>
2727
<depend>python3-numpy</depend>
2828
<depend>rclpy</depend>
2929
<depend>ros2_numpy</depend>

src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/game_state_decider.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from bitbots_blackboard.body_blackboard import BodyBlackboard
22
from dynamic_stack_decider.abstract_decision_element import AbstractDecisionElement
3-
from game_controller_hl_interfaces.msg import GameState
3+
from game_controller_hsl_interfaces.msg import GameState
44

55

66
class GameStateDecider(AbstractDecisionElement):
@@ -16,18 +16,28 @@ def perform(self, reevaluate=False):
1616
:return:
1717
"""
1818

19-
game_state_number = self.blackboard.gamestate.get_gamestate()
19+
game_state_number = self.blackboard.gamestate.get_game_state()
20+
is_stopped = self.blackboard.gamestate.is_stopped()
2021
# todo this is a temporary hack to make GUI work
21-
if game_state_number == GameState.GAMESTATE_INITIAL:
22+
if is_stopped:
23+
return "STOPPED"
24+
elif game_state_number == GameState.STATE_INITIAL:
2225
return "INITIAL"
23-
elif game_state_number == GameState.GAMESTATE_READY:
26+
elif game_state_number == GameState.STATE_READY:
2427
return "READY"
25-
elif game_state_number == GameState.GAMESTATE_SET:
28+
elif game_state_number == GameState.STATE_SET:
2629
return "SET"
27-
elif game_state_number == GameState.GAMESTATE_PLAYING:
30+
elif game_state_number == GameState.STATE_PLAYING:
2831
return "PLAYING"
29-
elif game_state_number == GameState.GAMESTATE_FINISHED:
32+
elif game_state_number == GameState.STATE_FINISHED:
3033
return "FINISHED"
34+
elif game_state_number == GameState.STATE_STANDBY:
35+
return "STANDBY"
36+
else:
37+
# This should never happen, but all cases required string response
38+
# as we do not get any stack trace otherwise
39+
self.blackboard.node.get_logger().error(f"Received unknown game state number: {game_state_number}")
40+
return "UNKNOWN"
3141

3242
def get_reevaluate(self):
3343
"""

0 commit comments

Comments
 (0)