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..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 @@ -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.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) @@ -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.0 + self.closest_robot_behind_dist = 10000.0 # Iterate over all robots robot: Robot for robot in msg.robots: @@ -69,6 +73,25 @@ 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 = 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]) + ) + ) + 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 +455,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 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 deleted file mode 100644 index 45db9efe0..000000000 --- a/src/bitbots_behavior/bitbots_body_behavior/bitbots_body_behavior/behavior_dsd/decisions/dribble_or_kick.py +++ /dev/null @@ -1,59 +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"] - - 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()) - 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 - 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] - ) - 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) - - if oriented_to_goal and front_free and goal_far and ball_near: - return "DRIBBLE" - else: - return "KICK" - - def get_reevaluate(self): - return True 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..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,15 +10,26 @@ 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) 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 + # 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) + # Get actual set play situation + set_play_state = self.blackboard.gamestate.get_set_play() + + 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 05bf498ca..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 // should be same as below $GoToBall + target:map action +$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