From 6ceca54a7ba0fcf8bc2c155fccb507e761712893 Mon Sep 17 00:00:00 2001 From: Clemens Wulff Date: Sat, 4 Jul 2026 22:35:34 +0900 Subject: [PATCH 01/10] decide for kicking depending on distance of seen robots --- .../capsules/costmap_capsule.py | 22 +++++++++++++++++++ .../behavior_dsd/decisions/dribble_or_kick.py | 9 +++++++- .../behavior_dsd/main.dsd | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py index 35b038995..416f0027d 100644 --- a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py +++ b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py @@ -41,6 +41,8 @@ def __init__(self, node, blackboard): self.map_margin: float = self.body_config["map_margin"] self.obstacle_costmap_smoothing_sigma: float = self.body_config["obstacle_costmap_smoothing_sigma"] self.obstacle_cost: float = self.body_config["obstacle_cost"] + self.closest_robot_infront_dist: float = 10000 + self.closest_robot_behind_dist: float = 10000 # Publisher for visualization in RViZ self.costmap_publisher = self._node.create_publisher(OccupancyGrid, "debug/costmap", 1) @@ -61,6 +63,8 @@ def robot_callback(self, msg: RobotArray) -> None: """ # Init a new obstacle costmap obstacle_map = np.zeros_like(self.costmap) + self.closest_robot_infront_dist = 10000 + self.closest_robot_behind_dist = 10000 # Iterate over all robots robot: Robot for robot in msg.robots: @@ -69,6 +73,16 @@ def robot_callback(self, msg: RobotArray) -> None: # TODO inflate # Draw obstacle with smoothing independent weight on obstacle costmap obstacle_map[idx_x, idx_y] = self.obstacle_cost * self.obstacle_costmap_smoothing_sigma + + dist_to_robot = np.linalg.norm(np.array[self._blackboard.world_model.get_current_position()[0], self._blackboard.world_model.get_current_position()[1]]) - np.array([robot.bb.center.position.x,robot.bb.center.position.y]) + if robot.bb.center.position.x > self._blackboard.world_model.get_current_position()[0]: + if dist_to_robot < self.closest_robot_infront_dist: + self.closest_robot_infront_dist = dist_to_robot + else: + if dist_to_robot < self.closest_robot_behind_dist: + self.closest_robot_behind_dist = dist_to_robot + + # Smooth obstacle map obstacle_map = gaussian_filter(obstacle_map, self.obstacle_costmap_smoothing_sigma) # Get pass offsets @@ -432,3 +446,11 @@ def get_best_kick_direction( ) ] return kick_direction + + def is_other_robot_close(self, threshold_front: float, threshold_behind: float) -> bool: + if threshold_front > self.closest_robot_infront_dist: + return True + elif threshold_behind > self.closest_robot_behind_dist: + return True + else: + return False diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py index 45db9efe0..93013031d 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py @@ -17,6 +17,8 @@ def __init__(self, blackboard, dsd, parameters): self.num_kick_angles = self.blackboard.config["num_kick_angles"] self.dribble_kick_angle = self.blackboard.config["dribble_kick_angle"] + self.threshold_front = parameters.get("threshold_front", 10) + self.threshold_behind = parameters.get("threshold_behind", 10) def perform(self, reevaluate=False): """ @@ -50,7 +52,12 @@ def perform(self, reevaluate=False): ball_near = ball_distance < self.ball_distance_threshold self.publish_debug_data(f"Ball distance (needs <{self.ball_distance_threshold})", ball_distance) - if oriented_to_goal and front_free and goal_far and ball_near: + # no other robots to close + other_robots_close = self.blackboard.costmap.is_other_robot_close(self.threshold_front, self.threshold_behind) + # actual set play situation + set_play_state = self.blackboard.gamestate.get_set_play() + + if other_robots_close and set_play_state == 0: return "DRIBBLE" else: return "KICK" diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd index 7851fbf80..066ad49a6 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd @@ -31,7 +31,7 @@ $DoOnce NO --> @LookAtFieldFeatures, @GoToRolePosition #KickWithAvoidance -$KickOrDribble + map_goal_distance:%ball_approach_dist // should be same as below $GoToBall + target:map action +$KickOrDribble + map_goal_distance:%ball_approach_dist + threshold_front:1.5 + threshold_behind:1 // should be same as below $GoToBall + target:map action KICK --> $DoOnce NOT_DONE --> @ChangeAction + action:going_to_ball, @LookAtFieldFeatures, @GoToBall + target:rl_kick + blocking:false DONE --> $ReachedAndAlignedToPathPlanningGoalPosition + threshold:%rl_kick_approach_tolerance_pos + orientation_threshold:%rl_kick_approach_tolerance_deg + latch:true From 2945ded7035df7f1cf347c141722e30fb96dd67b Mon Sep 17 00:00:00 2001 From: Clemens Wulff Date: Sun, 5 Jul 2026 01:59:41 +0900 Subject: [PATCH 02/10] small fixes --- .../bitbots_blackboard/capsules/costmap_capsule.py | 8 ++++---- .../bitbots_body_behavior/behavior_dsd/main.dsd | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py index 416f0027d..c869142ab 100644 --- a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py +++ b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py @@ -41,8 +41,8 @@ def __init__(self, node, blackboard): self.map_margin: float = self.body_config["map_margin"] self.obstacle_costmap_smoothing_sigma: float = self.body_config["obstacle_costmap_smoothing_sigma"] self.obstacle_cost: float = self.body_config["obstacle_cost"] - self.closest_robot_infront_dist: float = 10000 - self.closest_robot_behind_dist: float = 10000 + self.closest_robot_infront_dist: float = 10000.0 + self.closest_robot_behind_dist: float = 10000.0 # Publisher for visualization in RViZ self.costmap_publisher = self._node.create_publisher(OccupancyGrid, "debug/costmap", 1) @@ -63,8 +63,8 @@ def robot_callback(self, msg: RobotArray) -> None: """ # Init a new obstacle costmap obstacle_map = np.zeros_like(self.costmap) - self.closest_robot_infront_dist = 10000 - self.closest_robot_behind_dist = 10000 + self.closest_robot_infront_dist = 10000.0 + self.closest_robot_behind_dist = 10000.0 # Iterate over all robots robot: Robot for robot in msg.robots: diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd index 066ad49a6..2f4097c37 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd @@ -31,7 +31,7 @@ $DoOnce NO --> @LookAtFieldFeatures, @GoToRolePosition #KickWithAvoidance -$KickOrDribble + map_goal_distance:%ball_approach_dist + threshold_front:1.5 + threshold_behind:1 // should be same as below $GoToBall + target:map action +$DribbleOrKick + map_goal_distance:%ball_approach_dist + threshold_front:2 + threshold_behind:0.5 // change back to KickorDribble to fix KICK --> $DoOnce NOT_DONE --> @ChangeAction + action:going_to_ball, @LookAtFieldFeatures, @GoToBall + target:rl_kick + blocking:false DONE --> $ReachedAndAlignedToPathPlanningGoalPosition + threshold:%rl_kick_approach_tolerance_pos + orientation_threshold:%rl_kick_approach_tolerance_deg + latch:true From 65338afa3aa8e79b5946d7e96be4dd3e74641d80 Mon Sep 17 00:00:00 2001 From: Clemens Wulff Date: Sun, 5 Jul 2026 02:13:27 +0900 Subject: [PATCH 03/10] change DribbleOrKick in KickOrDribble --- .../behavior_dsd/decisions/kick_or_dribble.py | 9 ++++++++- .../bitbots_body_behavior/behavior_dsd/main.dsd | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py index d25c2ae1d..1c3fa0107 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py @@ -11,13 +11,20 @@ def __init__(self, blackboard, dsd, parameters): super().__init__(blackboard, dsd, parameters) self.target_distance = parameters.get("map_goal_target_distance", 0.5) self.side_offset = parameters.get("side_offset", 0.0) + self.threshold_front = parameters.get("threshold_front", 10) + self.threshold_behind = parameters.get("threshold_behind", 10) def perform(self, reevaluate=False): """ Determines whether to kick or dribble based on the angle of the map goal """ map_goal = self.blackboard.pathfinding.get_map_goal(self.target_distance, self.side_offset) - if abs(map_goal[2]) > math.pi / 2: # point away from opponent goal, so we should dribble + # no other robots to close + other_robots_close = self.blackboard.costmap.is_other_robot_close(self.threshold_front, self.threshold_behind) + # actual set play situation + set_play_state = self.blackboard.gamestate.get_set_play() + + if abs(map_goal[2]) > math.pi / 2 or (other_robots_close and set_play_state == 0): # point away from opponent goal, so we should dribble return "DRIBBLE" else: return "KICK" diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd index 2f4097c37..54cff55e7 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd @@ -31,7 +31,7 @@ $DoOnce NO --> @LookAtFieldFeatures, @GoToRolePosition #KickWithAvoidance -$DribbleOrKick + map_goal_distance:%ball_approach_dist + threshold_front:2 + threshold_behind:0.5 // change back to KickorDribble to fix +$KickorDribble + map_goal_distance:%ball_approach_dist + threshold_front:2 + threshold_behind:0.5 // change back to KickorDribble to fix KICK --> $DoOnce NOT_DONE --> @ChangeAction + action:going_to_ball, @LookAtFieldFeatures, @GoToBall + target:rl_kick + blocking:false DONE --> $ReachedAndAlignedToPathPlanningGoalPosition + threshold:%rl_kick_approach_tolerance_pos + orientation_threshold:%rl_kick_approach_tolerance_deg + latch:true From 4784479c8a215cd56b5e6d75f357623358061eb5 Mon Sep 17 00:00:00 2001 From: Cornelius Krupp Date: Sun, 5 Jul 2026 06:18:09 +0800 Subject: [PATCH 04/10] Fix typos --- .../bitbots_blackboard/capsules/costmap_capsule.py | 2 +- .../bitbots_body_behavior/behavior_dsd/main.dsd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py index c869142ab..86ed25b83 100644 --- a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py +++ b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py @@ -74,7 +74,7 @@ def robot_callback(self, msg: RobotArray) -> None: # Draw obstacle with smoothing independent weight on obstacle costmap obstacle_map[idx_x, idx_y] = self.obstacle_cost * self.obstacle_costmap_smoothing_sigma - dist_to_robot = np.linalg.norm(np.array[self._blackboard.world_model.get_current_position()[0], self._blackboard.world_model.get_current_position()[1]]) - np.array([robot.bb.center.position.x,robot.bb.center.position.y]) + dist_to_robot = np.linalg.norm(np.array([self._blackboard.world_model.get_current_position()[0], self._blackboard.world_model.get_current_position()[1]]) - np.array([robot.bb.center.position.x,robot.bb.center.position.y])) if robot.bb.center.position.x > self._blackboard.world_model.get_current_position()[0]: if dist_to_robot < self.closest_robot_infront_dist: self.closest_robot_infront_dist = dist_to_robot diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd index 54cff55e7..010a193c2 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd @@ -31,7 +31,7 @@ $DoOnce NO --> @LookAtFieldFeatures, @GoToRolePosition #KickWithAvoidance -$KickorDribble + map_goal_distance:%ball_approach_dist + threshold_front:2 + threshold_behind:0.5 // change back to KickorDribble to fix +$KickOrDribble + map_goal_distance:%ball_approach_dist + threshold_front:2.0 + threshold_behind:0.5 KICK --> $DoOnce NOT_DONE --> @ChangeAction + action:going_to_ball, @LookAtFieldFeatures, @GoToBall + target:rl_kick + blocking:false DONE --> $ReachedAndAlignedToPathPlanningGoalPosition + threshold:%rl_kick_approach_tolerance_pos + orientation_threshold:%rl_kick_approach_tolerance_deg + latch:true From e1b8de821c503aba16eefffa40e0228293161443 Mon Sep 17 00:00:00 2001 From: Jan Gutsche <34797331+jaagut@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:45:54 +0900 Subject: [PATCH 05/10] Apply suggestions from code review Co-authored-by: Jan Gutsche <34797331+jaagut@users.noreply.github.com> --- .../bitbots_blackboard/capsules/costmap_capsule.py | 7 +------ .../behavior_dsd/decisions/kick_or_dribble.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py index 86ed25b83..6b95b8c2e 100644 --- a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py +++ b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py @@ -448,9 +448,4 @@ def get_best_kick_direction( return kick_direction def is_other_robot_close(self, threshold_front: float, threshold_behind: float) -> bool: - if threshold_front > self.closest_robot_infront_dist: - return True - elif threshold_behind > self.closest_robot_behind_dist: - return True - else: - return False + return threshold_front > self.closest_robot_infront_dist or if threshold_behind > self.closest_robot_behind_dist diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py index 1c3fa0107..ec83a70d4 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py @@ -19,7 +19,7 @@ def perform(self, reevaluate=False): Determines whether to kick or dribble based on the angle of the map goal """ map_goal = self.blackboard.pathfinding.get_map_goal(self.target_distance, self.side_offset) - # no other robots to close + # no other robots too close other_robots_close = self.blackboard.costmap.is_other_robot_close(self.threshold_front, self.threshold_behind) # actual set play situation set_play_state = self.blackboard.gamestate.get_set_play() From 9addee93964bf36e66f0e11a55979c5892041338 Mon Sep 17 00:00:00 2001 From: Jan Gutsche Date: Sun, 12 Jul 2026 14:19:32 +0900 Subject: [PATCH 06/10] Formatting --- .../capsules/costmap_capsule.py | 17 ++++++++++++----- .../behavior_dsd/decisions/dribble_or_kick.py | 3 --- .../behavior_dsd/decisions/kick_or_dribble.py | 4 +++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py index 6b95b8c2e..b79b63e90 100644 --- a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py +++ b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py @@ -73,8 +73,16 @@ def robot_callback(self, msg: RobotArray) -> None: # TODO inflate # Draw obstacle with smoothing independent weight on obstacle costmap obstacle_map[idx_x, idx_y] = self.obstacle_cost * self.obstacle_costmap_smoothing_sigma - - dist_to_robot = np.linalg.norm(np.array([self._blackboard.world_model.get_current_position()[0], self._blackboard.world_model.get_current_position()[1]]) - np.array([robot.bb.center.position.x,robot.bb.center.position.y])) + + dist_to_robot = np.linalg.norm( + np.array( + [ + self._blackboard.world_model.get_current_position()[0], + self._blackboard.world_model.get_current_position()[1], + ] + ) + - np.array([robot.bb.center.position.x, robot.bb.center.position.y]) + ) if robot.bb.center.position.x > self._blackboard.world_model.get_current_position()[0]: if dist_to_robot < self.closest_robot_infront_dist: self.closest_robot_infront_dist = dist_to_robot @@ -82,7 +90,6 @@ def robot_callback(self, msg: RobotArray) -> None: if dist_to_robot < self.closest_robot_behind_dist: self.closest_robot_behind_dist = dist_to_robot - # Smooth obstacle map obstacle_map = gaussian_filter(obstacle_map, self.obstacle_costmap_smoothing_sigma) # Get pass offsets @@ -446,6 +453,6 @@ def get_best_kick_direction( ) ] return kick_direction - + def is_other_robot_close(self, threshold_front: float, threshold_behind: float) -> bool: - return threshold_front > self.closest_robot_infront_dist or if threshold_behind > self.closest_robot_behind_dist + return threshold_front > self.closest_robot_infront_dist or threshold_behind > self.closest_robot_behind_dist diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py index 93013031d..4b9b21772 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py @@ -28,7 +28,6 @@ def perform(self, reevaluate=False): """ # robot needs to be correctly aligned to ball, so that opponent goal is in front goal_angle = abs(self.blackboard.world_model.get_map_based_opp_goal_angle()) - oriented_to_goal = goal_angle < self.orient_threshold self.publish_debug_data(f"Orientation to goal (needs <{self.orient_threshold})", goal_angle) # no other robots should be in front of the ball. this means the kick with angle 0 would be the best @@ -44,12 +43,10 @@ def perform(self, reevaluate=False): self.blackboard.world_model.get_map_based_opp_goal_center_xy()[0] - self.blackboard.world_model.get_current_position()[0] ) - goal_far = goal_distance > self.goal_distance_threshold self.publish_debug_data(f"Goal distance (needs >{self.goal_distance_threshold})", goal_distance) # ball needs to be close enough ball_distance = self.blackboard.world_model.get_ball_distance() - ball_near = ball_distance < self.ball_distance_threshold self.publish_debug_data(f"Ball distance (needs <{self.ball_distance_threshold})", ball_distance) # no other robots to close diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py index ec83a70d4..dde375fa5 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py @@ -24,7 +24,9 @@ def perform(self, reevaluate=False): # actual set play situation set_play_state = self.blackboard.gamestate.get_set_play() - if abs(map_goal[2]) > math.pi / 2 or (other_robots_close and set_play_state == 0): # point away from opponent goal, so we should dribble + if abs(map_goal[2]) > math.pi / 2 or ( + other_robots_close and set_play_state == 0 + ): # point away from opponent goal, so we should dribble return "DRIBBLE" else: return "KICK" From 9a1af863274eb05ec11f1d319d8e54b0b1925306 Mon Sep 17 00:00:00 2001 From: Jan Gutsche Date: Sun, 12 Jul 2026 16:13:06 +0900 Subject: [PATCH 07/10] Remove unused dribble_or_kick --- .../behavior_dsd/decisions/dribble_or_kick.py | 63 ------------------- 1 file changed, 63 deletions(-) delete mode 100644 src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py deleted file mode 100644 index 4b9b21772..000000000 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py +++ /dev/null @@ -1,63 +0,0 @@ -from bitbots_blackboard.body_blackboard import BodyBlackboard -from dynamic_stack_decider.abstract_decision_element import AbstractDecisionElement - - -class DribbleOrKick(AbstractDecisionElement): - blackboard: BodyBlackboard - - def __init__(self, blackboard, dsd, parameters): - super().__init__(blackboard, dsd, parameters) - self.orient_threshold = self.blackboard.config["dribble_orient_threshold"] - self.goal_distance_threshold = self.blackboard.config["dribble_goal_distance_threshold"] - self.ball_distance_threshold = self.blackboard.config["dribble_ball_distance_threshold"] - - self.kick_length = self.blackboard.config["kick_cost_kick_length"] - self.max_kick_angle = self.blackboard.config["max_kick_angle"] - self.angular_range = self.blackboard.config["kick_cost_angular_range"] - self.num_kick_angles = self.blackboard.config["num_kick_angles"] - - self.dribble_kick_angle = self.blackboard.config["dribble_kick_angle"] - self.threshold_front = parameters.get("threshold_front", 10) - self.threshold_behind = parameters.get("threshold_behind", 10) - - def perform(self, reevaluate=False): - """ - Determines whether we want to dribble if the area in front of us is clear, or to kick - :param reevaluate: - :return: - """ - # robot needs to be correctly aligned to ball, so that opponent goal is in front - goal_angle = abs(self.blackboard.world_model.get_map_based_opp_goal_angle()) - self.publish_debug_data(f"Orientation to goal (needs <{self.orient_threshold})", goal_angle) - - # no other robots should be in front of the ball. this means the kick with angle 0 would be the best - best_kick_direction = self.blackboard.costmap.get_best_kick_direction( - -self.max_kick_angle, self.max_kick_angle, self.num_kick_angles, self.kick_length, self.angular_range - ) - front_free = -self.dribble_kick_angle < best_kick_direction < self.dribble_kick_angle - self.publish_debug_data("best kick direction", best_kick_direction) - self.publish_debug_data("Front free", front_free) - - # we should be not to close to the goal, otherwise kicking makes more sense. only take x axis into account - goal_distance = abs( - self.blackboard.world_model.get_map_based_opp_goal_center_xy()[0] - - self.blackboard.world_model.get_current_position()[0] - ) - self.publish_debug_data(f"Goal distance (needs >{self.goal_distance_threshold})", goal_distance) - - # ball needs to be close enough - ball_distance = self.blackboard.world_model.get_ball_distance() - self.publish_debug_data(f"Ball distance (needs <{self.ball_distance_threshold})", ball_distance) - - # no other robots to close - other_robots_close = self.blackboard.costmap.is_other_robot_close(self.threshold_front, self.threshold_behind) - # actual set play situation - set_play_state = self.blackboard.gamestate.get_set_play() - - if other_robots_close and set_play_state == 0: - return "DRIBBLE" - else: - return "KICK" - - def get_reevaluate(self): - return True From b71e5c430c10f2e4b81bf82922acbe43b440f28b Mon Sep 17 00:00:00 2001 From: Jan Gutsche Date: Sun, 12 Jul 2026 16:13:46 +0900 Subject: [PATCH 08/10] Refactor KickOrDribble decision logic and update main behavior configuration --- .../behavior_dsd/decisions/kick_or_dribble.py | 19 +++++++++++-------- .../behavior_dsd/main.dsd | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py index dde375fa5..ca3a65bb5 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/kick_or_dribble.py @@ -2,6 +2,7 @@ from bitbots_blackboard.body_blackboard import BodyBlackboard from dynamic_stack_decider.abstract_decision_element import AbstractDecisionElement +from game_controller_hsl_interfaces.msg import GameState class KickOrDribble(AbstractDecisionElement): @@ -9,8 +10,6 @@ class KickOrDribble(AbstractDecisionElement): def __init__(self, blackboard, dsd, parameters): super().__init__(blackboard, dsd, parameters) - self.target_distance = parameters.get("map_goal_target_distance", 0.5) - self.side_offset = parameters.get("side_offset", 0.0) self.threshold_front = parameters.get("threshold_front", 10) self.threshold_behind = parameters.get("threshold_behind", 10) @@ -18,15 +17,19 @@ def perform(self, reevaluate=False): """ Determines whether to kick or dribble based on the angle of the map goal """ - map_goal = self.blackboard.pathfinding.get_map_goal(self.target_distance, self.side_offset) - # no other robots too close + # Get the map goal, so we can check the angle. + # distance or side offset are not relevant here, so we can just use 0.0 for both. + map_goal = self.blackboard.pathfinding.get_map_goal(distance=0.0, side_offset=0.0) + + # Are no other robots too close? other_robots_close = self.blackboard.costmap.is_other_robot_close(self.threshold_front, self.threshold_behind) - # actual set play situation + # Get actual set play situation set_play_state = self.blackboard.gamestate.get_set_play() - if abs(map_goal[2]) > math.pi / 2 or ( - other_robots_close and set_play_state == 0 - ): # point away from opponent goal, so we should dribble + map_goal_points_away_from_opp_goal = abs(map_goal[2]) > math.pi / 2 + regular_play_with_nearby_robot = other_robots_close and set_play_state == GameState.SET_PLAY_NONE + + if map_goal_points_away_from_opp_goal or regular_play_with_nearby_robot: return "DRIBBLE" else: return "KICK" diff --git a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd index 066eaecb9..ad541e7de 100644 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd +++ b/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/main.dsd @@ -31,7 +31,7 @@ $DoOnce NO --> @LookAtFieldFeatures, @GoToRolePosition #KickWithAvoidance -$KickOrDribble + map_goal_distance:%ball_approach_dist + threshold_front:2.0 + threshold_behind:0.5 +$KickOrDribble + threshold_front:2.0 + threshold_behind:0.5 KICK --> $DoOnce NOT_DONE --> @ChangeAction + action:going_to_ball, @LookAtFieldFeatures, @GoToBall + target:rl_kick + blocking:false DONE --> $ReachedAndAlignedToPathPlanningGoalPosition + threshold:%rl_kick_approach_tolerance_pos + orientation_threshold:%rl_kick_approach_tolerance_deg + latch:true From 9c9028d140eca5f588f4da56bf62975db2e3ef89 Mon Sep 17 00:00:00 2001 From: MegaIng Date: Mon, 13 Jul 2026 11:01:37 +0200 Subject: [PATCH 09/10] Fix type error by explictly converting to float --- .../bitbots_blackboard/capsules/costmap_capsule.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py index b79b63e90..b97a7c071 100644 --- a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py +++ b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py @@ -74,7 +74,7 @@ def robot_callback(self, msg: RobotArray) -> None: # Draw obstacle with smoothing independent weight on obstacle costmap obstacle_map[idx_x, idx_y] = self.obstacle_cost * self.obstacle_costmap_smoothing_sigma - dist_to_robot = np.linalg.norm( + dist_to_robot = float(np.linalg.norm( np.array( [ self._blackboard.world_model.get_current_position()[0], @@ -82,7 +82,7 @@ def robot_callback(self, msg: RobotArray) -> None: ] ) - np.array([robot.bb.center.position.x, robot.bb.center.position.y]) - ) + )) if robot.bb.center.position.x > self._blackboard.world_model.get_current_position()[0]: if dist_to_robot < self.closest_robot_infront_dist: self.closest_robot_infront_dist = dist_to_robot From afb4ff98df4bf70ca8e44c45fab7b8783f36aeaa Mon Sep 17 00:00:00 2001 From: Cornelius Krupp Date: Tue, 14 Jul 2026 10:16:03 +0200 Subject: [PATCH 10/10] pixi format --- .../capsules/costmap_capsule.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py index b97a7c071..c8a43b9d4 100644 --- a/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py +++ b/src/bitbots_behavior/bitbots_blackboard/bitbots_blackboard/capsules/costmap_capsule.py @@ -74,15 +74,17 @@ def robot_callback(self, msg: RobotArray) -> None: # Draw obstacle with smoothing independent weight on obstacle costmap obstacle_map[idx_x, idx_y] = self.obstacle_cost * self.obstacle_costmap_smoothing_sigma - dist_to_robot = float(np.linalg.norm( - np.array( - [ - self._blackboard.world_model.get_current_position()[0], - self._blackboard.world_model.get_current_position()[1], - ] + dist_to_robot = float( + np.linalg.norm( + np.array( + [ + self._blackboard.world_model.get_current_position()[0], + self._blackboard.world_model.get_current_position()[1], + ] + ) + - np.array([robot.bb.center.position.x, robot.bb.center.position.y]) ) - - np.array([robot.bb.center.position.x, robot.bb.center.position.y]) - )) + ) if robot.bb.center.position.x > self._blackboard.world_model.get_current_position()[0]: if dist_to_robot < self.closest_robot_infront_dist: self.closest_robot_infront_dist = dist_to_robot