Skip to content

Commit c711e54

Browse files
authored
Merge pull request #479 from RoboCupULaval/fix/goal_kick_outside
Fix/goal kick outside
2 parents 275e5f1 + 81248b4 commit c711e54

File tree

8 files changed

+68
-24
lines changed

8 files changed

+68
-24
lines changed

Engine/robot.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
MAX_LINEAR_SPEED = 4000 # mm/s
77
MAX_LINEAR_ACCELERATION = 4000 # mm/s^2
8-
MAX_ANGULAR_SPEED = 100 # rad/s
8+
MAX_ANGULAR_SPEED = 50 # rad/s
99
MAX_ANGULAR_ACC = 3 # rad/s^2
1010
MIN_LINEAR_SPEED = 200 # mm/s Speed near zero, but still move the robot
1111

Util/geometry.py

+4
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ def pad(cls, area, padding=0):
122122
def from_limits(cls, top, bottom, right, left):
123123
return cls(Position(left, top), Position(right, bottom))
124124

125+
@classmethod
126+
def flip_x(cls, area):
127+
return Area.from_limits(area.top, area.bottom, -area.left, -area.right)
128+
125129

126130
def find_bisector_of_triangle(c, a, b):
127131
"""

ai/GameDomainObjects/field.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,7 @@ def _update_field_const(self):
164164
self.our_goal_area = Area(self.field_lines["RightPenaltyStretch"].p2,
165165
self.field_lines["RightFieldLeftPenaltyStretch"].p1)
166166

167-
self.their_goal_area = Area(self.field_lines["RightPenaltyStretch"].p2.flip_x(),
168-
self.field_lines["RightFieldLeftPenaltyStretch"].p1.flip_x())
167+
self.their_goal_area = Area.flip_x(self.our_goal_area)
169168

170169
self.goal_line = Line(p1=Position(self.our_goal_x, +self.goal_width / 2),
171170
p2=Position(self.our_goal_x, -self.goal_width / 2))
@@ -174,6 +173,11 @@ def _update_field_const(self):
174173
p2=Position(self.our_goal_x, -self.goal_width / 2))
175174
self.their_goal_line = Line(p1=Position(self.their_goal_x, +self.goal_width / 2),
176175
p2=Position(self.their_goal_x, -self.goal_width / 2))
176+
self.behind_our_goal_line = Area.from_limits(self.our_goal_area.top,
177+
self.our_goal_area.bottom,
178+
self.our_goal_area.right + 50 * self.goal_depth,
179+
self.our_goal_area.left)
180+
self.behind_their_goal_line = Area.flip_x(self.behind_our_goal_line)
177181

178182
self.free_kick_avoid_area = Area.pad(self.their_goal_area,
179183
INDIRECT_KICK_OFFSET + KEEPOUT_DISTANCE_FROM_GOAL)

ai/STA/Strategy/smart_stop.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ def obstacles(self):
4848
def required_roles(cls):
4949
return [Role.GOALKEEPER,
5050
Role.FIRST_ATTACK,
51-
Role.FIRST_DEFENCE]
51+
Role.SECOND_ATTACK]
5252

5353
@classmethod
5454
def optional_roles(cls):
55-
return [Role.SECOND_ATTACK,
55+
return [Role.FIRST_DEFENCE,
5656
Role.MIDDLE,
5757
Role.SECOND_DEFENCE]
5858

ai/STA/Tactic/align_to_defense_wall.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ def go_kick(self):
134134
if self.go_kick_tactic is None:
135135
self.go_kick_tactic = GoKick(self.game_state, self.player, target=self.game_state.field.their_goal_pose)
136136

137-
if not self._should_ball_be_kick_by_wall() or self.game_state.field.is_ball_in_our_goal_area():
137+
if not self._should_ball_be_kick_by_wall() \
138+
or self.game_state.field.is_ball_in_our_goal_area() \
139+
or not self._is_closest_not_goaler(self.player):
138140
self.go_kick_tactic = None
139141
self.next_state = self.main_state
140142
return Idle

ai/STA/Tactic/go_to_position.py

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from typing import List
44

5+
from Debug.debug_command_factory import DebugCommandFactory, VIOLET
56
from Util import Pose, Position
67
from Util.ai_command import CmdBuilder
78
from Util.constant import POSITION_DEADZONE, ANGLE_TO_HALT
@@ -34,3 +35,4 @@ def check_success(self):
3435
distance = (self.player.pose - self.target.position).norm
3536
return (distance < POSITION_DEADZONE) and compare_angle(self.player.pose.orientation,
3637
self.target.orientation, abs_tol=ANGLE_TO_HALT)
38+

ai/STA/Tactic/goalkeeper.py

+29-12
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

66
from Debug.debug_command_factory import DebugCommandFactory
77
from ai.Algorithm.evaluation_module import player_with_ball, player_pointing_toward_point, \
8-
player_pointing_toward_segment
8+
player_pointing_toward_segment, closest_players_to_point
99

1010
__author__ = 'RoboCupULaval'
1111

1212
from typing import List
1313

1414
from Util import Pose, Position
1515
from Util.ai_command import MoveTo, Idle
16-
from Util.constant import ROBOT_RADIUS, KEEPOUT_DISTANCE_FROM_GOAL
16+
from Util.constant import ROBOT_RADIUS, KEEPOUT_DISTANCE_FROM_GOAL, ROBOT_DIAMETER
1717
from Util.geometry import intersection_line_and_circle, intersection_between_lines, \
1818
closest_point_on_segment, find_bisector_of_triangle, Area
1919
from ai.GameDomainObjects import Player
@@ -54,7 +54,7 @@ def defense_dumb(self):
5454

5555
def defense(self):
5656
# Prepare to block the ball
57-
if self.game_state.field.is_ball_in_our_goal_area() and self.game_state.ball.is_immobile():
57+
if self._is_ball_safe_to_kick() and self.game_state.ball.is_immobile():
5858
self.next_state = self.clear
5959

6060
if self._ball_going_toward_goal():
@@ -104,28 +104,38 @@ def intercept(self):
104104
end_speed=0,
105105
ball_collision=False)
106106

107-
def _ball_going_toward_goal(self):
108-
upper_angle = (self.game_state.ball.position - self.GOAL_LINE.p2).angle + 5 * np.pi / 180.0
109-
lower_angle = (self.game_state.ball.position - self.GOAL_LINE.p1).angle - 5 * np.pi / 180.0
110-
ball_speed = self.game_state.ball.velocity.norm
111-
return (ball_speed > self.DANGER_BALL_VELOCITY and self.game_state.ball.velocity.x > 0) or \
112-
(ball_speed > self.MOVING_BALL_VELOCITY and upper_angle <= self.game_state.ball.velocity.angle <= lower_angle)
113-
114107
def clear(self):
115108
# Move the ball to outside of the penality zone
116109
if self.go_kick_tactic is None:
117110
self.go_kick_tactic = GoKick(self.game_state,
118111
self.player,
119112
auto_update_target=True,
120113
go_behind_distance=1.2*GRAB_BALL_SPACING,
121-
forbidden_areas=self.forbidden_areas) # make it easier
122-
if not self.game_state.field.is_ball_in_our_goal_area():
114+
forbidden_areas=self.forbidden_areas) # make it easier
115+
if not self._is_ball_safe_to_kick():
123116
self.next_state = self.defense
124117
self.go_kick_tactic = None
125118
return Idle
126119
else:
127120
return self.go_kick_tactic.exec()
128121

122+
def _ball_going_toward_goal(self):
123+
upper_angle = (self.game_state.ball.position - self.GOAL_LINE.p2).angle + 5 * np.pi / 180.0
124+
lower_angle = (self.game_state.ball.position - self.GOAL_LINE.p1).angle - 5 * np.pi / 180.0
125+
ball_speed = self.game_state.ball.velocity.norm
126+
return (ball_speed > self.DANGER_BALL_VELOCITY and self.game_state.ball.velocity.x > 0) or \
127+
(ball_speed > self.MOVING_BALL_VELOCITY and upper_angle <= self.game_state.ball.velocity.angle <= lower_angle)
128+
129+
def _is_ball_safe_to_kick(self):
130+
# Since defender can not kick the ball while inside the goal there are position where the ball is unreachable
131+
# The goalee must leave the goal area and kick the ball
132+
goal_area = self.game_state.field.our_goal_area
133+
width = KEEPOUT_DISTANCE_FROM_GOAL + ROBOT_DIAMETER
134+
area_in_front_of_goal = Area.from_limits(goal_area.top, goal_area.bottom,
135+
goal_area.left, goal_area.left - width)
136+
return self.game_state.field.is_ball_in_our_goal_area() or \
137+
area_in_front_of_goal.point_inside(self.game_state.ball.position) and self._no_enemy_around_ball()
138+
129139
def _best_target_into_goal(self):
130140

131141
enemy_player_with_ball = player_with_ball(min_dist_from_ball=200, our_team=False)
@@ -159,3 +169,10 @@ def debug_cmd(self):
159169
else:
160170
return []
161171

172+
def _no_enemy_around_ball(self):
173+
closest = closest_players_to_point(self.game_state.ball_position, our_team=False)
174+
if len(closest) == 0:
175+
return True
176+
DANGEROUS_ENEMY_MIN_DISTANCE = 500
177+
return closest[0].distance > DANGEROUS_ENEMY_MIN_DISTANCE
178+

ai/STA/Tactic/tactic.py

+21-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import logging
55

6-
from Debug.debug_command_factory import DebugCommandFactory
6+
from Debug.debug_command_factory import DebugCommandFactory, VIOLET
77
from Util import Pose, Position
88
from Util.ai_command import AICommand
99
from Util.constant import ROBOT_RADIUS, KEEPOUT_DISTANCE_FROM_GOAL
@@ -56,11 +56,14 @@ def __init__(self, game_state: GameState, player: Player, target: Optional[Pose]
5656
field.bottom,
5757
field.left - field.boundary_width,
5858
field.left - 100 * field.boundary_width)
59+
# Those limits are for ulaval local only
5960
areas = [
6061
top_area,
6162
bottom_area,
6263
right_area,
63-
left_area]
64+
left_area,
65+
self.field.behind_our_goal_line,
66+
self.field.behind_their_goal_line]
6467
#areas = []
6568
self.forbidden_areas = [Area.pad(area, KEEPOUT_DISTANCE_FROM_GOAL) for area in areas]
6669
self.forbidden_areas += [self.game_state.field.their_goal_forbidden_area,
@@ -86,13 +89,19 @@ def exec(self) -> AICommand:
8689
def _check_for_forbidden_area(self, next_ai_command):
8790
old_target_position = next_ai_command.target.position
8891
target_to_position = Line(self.player.position, next_ai_command.target.position)
92+
closest_new_target = None
8993
for area in self.forbidden_areas:
9094
if old_target_position in area:
9195
target_position = self._find_best_next_target(area, old_target_position, self.player.position, target_to_position)
9296
new_target = Pose(target_position, next_ai_command.target.orientation)
9397

94-
# This trailing _ is not for protected access, it was add to avoid a name conflict with the function replace ;)
95-
return next_ai_command._replace(target=new_target)
98+
if closest_new_target is None \
99+
or (closest_new_target - self.player.position).norm > (new_target - self.player.position).norm:
100+
closest_new_target = new_target
101+
102+
if closest_new_target is not None:
103+
# This trailing _ is not for protected access, it was add to avoid a name conflict with the function replace
104+
return next_ai_command._replace(target=closest_new_target )
96105
return next_ai_command
97106

98107
def _find_best_next_target(self, area: Area, old_target_position, player_position: Position, target_to_position: Line):
@@ -109,12 +118,18 @@ def _find_best_next_target(self, area: Area, old_target_position, player_positio
109118
return intersections[1]
110119

111120
def debug_cmd(self):
112-
#return [DebugCommandFactory().area(area) for area in self.forbidden_areas]
113-
return []
121+
cmds = []
122+
[cmds.extend(DebugCommandFactory().area(area, color=VIOLET)) for area in self.forbidden_areas]
123+
return cmds
124+
#return []
114125

115126
@classmethod
116127
def name(cls):
117128
return cls.__name__
118129

130+
@property
131+
def field(self):
132+
return self.game_state.field
133+
119134
def __str__(self):
120135
return self.__class__.__name__

0 commit comments

Comments
 (0)