You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
4.6 KiB
Python

from math_ops.Math_Ops import Math_Ops as M
from world.World import World
import numpy as np
class Head():
FIELD_FLAGS = World.FLAGS_CORNERS_POS + World.FLAGS_POSTS_POS
HEAD_PITCH = -35
def __init__(self, world : World) -> None:
self.world = world
self.look_left = True
self.state = 0
def execute(self):
'''
Try to compute best head orientation if possible, otherwise look around
state:
0 - Adjust position - ball is in FOV and robot can self-locate
1..TIMEOUT-1 - Guided search - attempt to use recent visual/radio information to guide the search
TIMEOUT - Random search - look around (default mode after guided search fails by timeout)
'''
TIMEOUT = 30
w = self.world
r = w.robot
can_self_locate = r.loc_last_update > w.time_local_ms - w.VISUALSTEP_MS
#--------------------------------------- A. Ball is in FOV and robot can self-locate
if w.ball_last_seen > w.time_local_ms - w.VISUALSTEP_MS: # ball is in FOV
if can_self_locate:
best_dir = self.compute_best_direction(can_self_locate, use_ball_from_vision=True)
self.state = 0
elif self.state < TIMEOUT:
#--------------------------------------- B. Ball is in FOV but robot cannot currently self-locate
best_dir = self.compute_best_direction(can_self_locate, use_ball_from_vision=True)
self.state += 1 # change to guided search and increment time
elif self.state < TIMEOUT:
#--------------------------------------- C. Ball is not in FOV
best_dir = self.compute_best_direction(can_self_locate)
self.state += 1 # change to guided search and increment time
if self.state == TIMEOUT: # Random search
if w.ball_last_seen > w.time_local_ms - w.VISUALSTEP_MS: # Ball is in FOV (search 45 deg to both sides of the ball)
ball_dir = M.vector_angle(w.ball_rel_torso_cart_pos[:2])
targ = np.clip(ball_dir + (45 if self.look_left else -45), -119, 119)
else: # Ball is not in FOV (search 119 deg to both sides)
targ = 119 if self.look_left else -119
if r.set_joints_target_position_direct([0,1], np.array([targ,Head.HEAD_PITCH]), False) <= 0:
self.look_left = not self.look_left
else: # Adjust position or guided search
r.set_joints_target_position_direct([0,1], np.array([best_dir,Head.HEAD_PITCH]), False)
def compute_best_direction(self, can_self_locate, use_ball_from_vision=False):
FOV_MARGIN = 15 # safety margin, avoid margin horizontally
SAFE_RANGE = 120 - FOV_MARGIN*2
HALF_RANGE = SAFE_RANGE / 2
w = self.world
r = w.robot
if use_ball_from_vision:
ball_2d_dist = np.linalg.norm(w.ball_rel_torso_cart_pos[:2])
else:
ball_2d_dist = np.linalg.norm(w.ball_abs_pos[:2]-r.loc_head_position[:2])
if ball_2d_dist > 0.12:
if use_ball_from_vision:
ball_dir = M.vector_angle(w.ball_rel_torso_cart_pos[:2])
else:
ball_dir = M.target_rel_angle(r.loc_head_position, r.imu_torso_orientation, w.ball_abs_pos)
else: # ball is very close to robot
ball_dir = 0
flags_diff = dict()
# iterate flags
for f in Head.FIELD_FLAGS:
flag_dir = M.target_rel_angle(r.loc_head_position, r.imu_torso_orientation, f)
diff = M.normalize_deg( flag_dir - ball_dir )
if abs(diff) < HALF_RANGE and can_self_locate:
return ball_dir # return ball direction if robot can self-locate
flags_diff[f] = diff
closest_flag = min( flags_diff, key=lambda k: abs(flags_diff[k]) )
closest_diff = flags_diff[closest_flag]
if can_self_locate: # at this point, if it can self-locate, then abs(closest_diff) > HALF_RANGE
# return position that centers the ball as much as possible in the FOV, including the nearest flag if possible
final_diff = min( abs(closest_diff) - HALF_RANGE, SAFE_RANGE ) * np.sign(closest_diff)
else:
# position that centers the flag as much as possible, until it is seen, while keeping the ball in the FOV
final_diff = np.clip( closest_diff, -SAFE_RANGE, SAFE_RANGE )
# saturate instead of normalizing angle to avoid a complete neck rotation
return np.clip(ball_dir + final_diff, -119, 119)
return M.normalize_deg( ball_dir + final_diff )