class Navigation(): """ AUV 2017 Version Controls thrusters to move or point AUV to a certain direction given power and direction or rotational values """ def __init__(self, wp=None): self.is_killswitch_on = False self.pub_h_nav = rospy.Publisher('height_control', HControl, queue_size=100) self.pub_r_nav = rospy.Publisher('rotation_control', RControl, queue_size=100) self.pub_m_nav = rospy.Publisher('movement_control', MControl, queue_size=100) # rospy.init_node('navigation_node', anonymous=True) rospy.Subscriber('rotation_control_status', RControl, self.r_status_callback, queue_size=100) rospy.Subscriber('movement_control_status', MControl, self.m_status_callback, queue_size=100) rospy.Subscriber('height_control_status', HControl, self.h_status_callback, queue_size=100) self.h_control = HControl() self.r_control = RControl() self.m_control = MControl() # used for HControl (int state, float depth, int power) ####################################### self.hStates = { 'down': 0, 'staying': 1, 'up': 2, 'unlock': 4, 'lock': 5 } self.hState = None # state self.depth = None # depth (nonstop moving: -1, moving distance: x) self.hPower = 100 # power # used for RControl (int state, float rotation, int power) #################################### self.rStates = { 'left': 0, # rotate left 'staying': 1, 'right': 2, # rotate right 'rotate_front_cam_dist': 3, # rotate with fcd 'keep_rotate_front_cam_dist': 4 # keeping rotating with fcd } self.rState = None # state self.rotation = None # rotation (nonstop rotating: -1, rotate degree: x) self.rPower = 90 # power # used for MControl (int state, int mDirection, float power, float distance) ####### self.mStates = { 'off': 0, 'power': 1, # adjust with power 'distance': 2, # ajust with distance 'front_cam_center': 3, # centered with front camera 'bot_cam_center': 4, # centered with bottom camera 'motor_time': 5 # turn on motor with specific time } self.mState = None # state self.directions = { 'none': 0, 'forward': 1, 'right': 2, 'backward': 3, 'left': 4 } self.mDirection = None # mDirection self.mPower = None # power (none: 0, motor power: x) self.distance = None # distance (distance away from the object: x) self.runningTime = None # runningTime (time for the motor to turn on) #waypoint variables if wp: self.waypoint = wp else: self.waypoint = Waypoint() self.is_running_waypoint_rotation = False self.is_running_waypoint_movement = False self.is_busy_waypoint = False self.w_distance_m = 0 self.w_power_m = 140 self.r_power = 90 self.h_power = 100 self.m_power = 140 self.waypoint_state = 0 self.thread_w = None self.exit_waypoints = False #vars dealing with movement break time self.waypoint_m_time = 0 # self.waypoint_m_time_max = 2.14 self.waypoint_m_time_max = 1.0 #vars dealing with height checking self.depth_threshold = 0.3 self.depth_assignment = 0.5 self.current_waypoint_x = 0 self.current_waypoint_y = 0 self.depth_cap = 3.5 #var for saved heading self.saved_heading = 0 def set_h_nav(self, hState, depth, hPower): """ hState -- 'down': 0, 'staying': 1, 'up': 2 depth -- nonstop moving: -1, moving distance: x hPower -- int """ if hState.isdigit(): self.hState = hState else: self.hState = self.hStates[hState] self.depth = depth self.hPower = hPower def set_r_nav(self, rState, rotation, rPower): """ rState -- 'left': 0, 'staying': 1, 'right': 2, 'rotate_front_cam_dist': 3, 'keep_rotate_front_cam_dist': 4 rotation -- nonstop rotating: -1, rotate degree: x rPower -- int """ if rState.isdigit(): self.rState = rState else: self.rState = self.rStates[rState] self.rotation = rotation self.rPower = rPower def set_m_nav(self, mState, mDirection, power, value=0.0): """ mState -- 'off': 0, 'power': 1, 'distance': 2, 'front_cam_center': 3, 'bot_cam_center': 4, 'motor_time': 5 mDirection -- 'none': 0, 'forward': 1, 'right': 2, 'backward': 3, 'left': 4 power -- none: 0, motor power: x value -- based on mState (2)distance: distance away from the object: x (5)runningTime: time for the motor to turn on """ if mState.isdigit(): self.mState = mState else: self.mState = self.mStates[mState] if mDirection.isdigit(): self.mDirection = mDirection else: self.mDirection = self.directions[mDirection] self.mPower = power self.distance = 0.0 self.runningTime = 0.0 if self.mState == self.mStates['distance']: self.distance = value elif self.mState == self.mStates['motor_time']: self.runningTime = value def cancel_m_nav(self, power=None): if not power: power = 140 self.m_nav('off', 'none', power) def cancel_h_nav(self, power=None): if not power: power = 100 self.h_nav('staying', 0, power) def cancel_r_nav(self, power=None): if not power: power = 90 self.r_nav('staying', 0, power) def cancel_all_nav(self, power=None): self.cancel_m_nav(power) self.cancel_r_nav(power) self.cancel_h_nav(power) def cancel_and_h_nav(self, hState=None, depth=None, hPower=None): self.cancel_h_nav(hPower) self.h_nav(hState, depth, hPower) def cancel_and_r_nav(self, rState=None, rotation=None, rPower=None): self.cancel_r_nav(rPower) self.r_nav(rState, rotation, rPower) def cancel_and_m_nav(self, mState=None, mDirection=None, power=None, value=None): self.cancel_m_nav(power) self.m_nav(mState, mDirection, power, value) def h_nav(self, hState=None, depth=None, hPower=None): """ Start horizontal navigation given hState and depth when killswitch is on. hState -- 'down': 0, 'staying': 1, 'up': 2 depth -- nonstop moving: -1, moving distance: x hPower -- int """ if self.is_killswitch_on: if hState is not None or depth is not None or hPower is not None: self.set_h_nav(hState, depth, hPower) self.h_control.state = self.hState self.h_control.depth = self.depth self.h_control.power = self.hPower self.pub_h_nav.publish(self.h_control) # self.ros_sleep() # rospy.sleep(.1) # print('state: %d depth: %.2f power: %d' % (self.hState, self.depth, self.hPower)) def r_nav(self, rState=None, rotation=None, rPower=None): """ Start rotational navigation given rState and rotation when killswitch is on. rState -- 'left': 0, 'staying': 1, 'right': 2, 'rotate_front_cam_dist': 3, 'keep_rotate_front_cam_dist': 4 rotation -- nonstop rotating: -1, rotate degree: x rPower -- int """ if self.is_killswitch_on: if rState is not None or rotation is not None or rPower is not None: self.set_r_nav(rState, rotation, rPower) self.r_control.state = self.rState self.r_control.rotation = self.rotation self.r_control.power = self.rPower self.pub_r_nav.publish(self.r_control) # self.ros_sleep() # rospy.sleep(.1) # print('state: %d rotation: %.2f power: %d' % (self.rState, self.rotation, self.rPower)) def m_nav(self, mState=None, mDirection=None, power=None, value=None): """ Start movement navigation given mState, mDirection, and power/distance/runningTime when killswitch is on. mState -- 'off': 0, 'power': 1, 'distance': 2, 'front_cam_center': 3, 'bot_cam_center': 4, 'motor_time': 5 mDirection -- 'none': 0, 'forward': 1, 'right': 2, 'backward': 3, 'left': 4 power -- none: 0, motor power: x value -- based on mState (2)distance: distance away from the object: x (5)runningTime: time for the motor to turn on """ if self.is_killswitch_on: if mState is not None or mDirection is not None or power is not None: self.set_m_nav(mState, mDirection, power, value) self.m_control.state = self.mState self.m_control.mDirection = self.mDirection self.m_control.power = self.mPower self.m_control.distance = self.distance self.m_control.runningTime = self.runningTime self.pub_m_nav.publish(self.m_control) # self.ros_sleep() # rospy.sleep(.1) # print( # 'state: %d direction: %d power: %.2f distance: %.2f runningTime: %.2f' # % (self.mState, self.mDirection, self.mPower, self.distance, self.runningTime) # ) def start(self): """Starts navigation with set preferences when killswitch is plugged in""" self.is_killswitch_on = True def stop(self): """Stops navigation when killswitch is unplugged""" self.is_killswitch_on = False def ros_sleep(self, time=0.05): if time: rospy.sleep(time) else: rospy.sleep() def ros_rate(self, hz=100): rospy.Rate(hz) ############################### Waypoint Functions ###################################################################################### #rotaton control status callback def r_status_callback(self, rotation_status): # print(rotation_status) if self.is_running_waypoint_rotation and self.is_busy_waypoint: if rotation_status.state == 1: # print('waypoint rotation r_status_callback') self.is_running_waypoint_movement = True self.w_distance_m = self.waypoint.get_distance( self.current_waypoint_x, self.current_waypoint_y) self.m_nav('distance', 'forward', self.w_power_m, self.w_distance_m) self.waypoint_m_time = time.time() print("foward wp state 1") self.is_running_waypoint_rotation = False self.waypoint_state = 1 # print(self.w_distance_m) #movement control status callback def m_status_callback(self, movement_status): # print(rotation_status) if self.is_running_waypoint_movement and not self.is_running_waypoint_rotation and self.is_busy_waypoint: # print('movement_status: ') # print(self.waypoint_state) # print(movement_status.distance) # print(self.w_distance_m) if movement_status.state == 0 and self.waypoint_state == 1 and abs( movement_status.distance - self.w_distance_m) < 0.001: # print('in state 1') final_waypoint_m_time = time.time() - self.waypoint_m_time if final_waypoint_m_time > self.waypoint_m_time_max: final_waypoint_m_time = self.waypoint_m_time_max self.m_nav('motor_time', 'backward', self.w_power_m, final_waypoint_m_time) self.waypoint_state = 2 # print("backward wp state 2") elif movement_status.state == 0 and self.waypoint_state == 2: # print('in state 2') # print('waypoint rotation r_status_callback') self.is_running_waypoint_movement = False self.is_busy_waypoint = False self.waypoint_state = 0 # print("time fin wp state reset") #height control status callback def h_status_callback(self, height_status): if height_status.state == 0 or height_status.state == 2: self.depth_assignment = height_status.depth #go to waypoint from current position def go_waypoint(self, direction_r, degree_r, power_r, direction_h, distance_h, power_h, distance_m, power_m): # if not direction or not degree or not distance or not depth or not power or not h_power: # return if self.is_busy_waypoint: return self.is_busy_waypoint = True self.cancel_r_nav() # self.cancel_h_nav() self.cancel_m_nav() self.waypoint_state = 0 rospy.sleep(2) # print('going to waypoint') self.is_running_waypoint_rotation = True self.r_nav(direction_r, degree_r, power_r) self.h_nav(direction_h, distance_h, power_h) self.w_distance_m = distance_m self.w_power_m = power_m def is_running_waypoint(self): return self.is_busy_waypoint def clear_waypoints(self): self.waypoint.clear_all() #add current position to stack/queue def push_current_waypoint(self): self.waypoint.push_current_position() def enqueue_current_waypoint(self): self.waypoint.enqueue_current_position() #travel to current front/top of list def run_top_stack_waypoint(self, r_power=None, h_power=None, m_power=None): if not r_power: r_power = self.r_power if not h_power: h_power = self.h_power if not m_power: m_power = self.m_power #travel to waypoint at top of stack if not self.waypoint.is_empty(): last_x, last_y, last_depth = self.waypoint.pop() self.current_waypoint_x = last_x self.current_waypoint_y = last_y direction_r, degree_r, distance_m = self.waypoint.get_directions( last_x, last_y) direction_h, distance_h = self.waypoint.get_depth_directions( last_depth) self.go_waypoint(direction_r, degree_r, r_power, direction_h, distance_h, h_power, distance_m, m_power) def run_front_queue_waypoint(self, r_power=None, h_power=None, m_power=None): if not r_power: r_power = self.r_power if not h_power: h_power = self.h_power if not m_power: m_power = self.m_power #travel to waypoint at front of queue if not self.waypoint.is_empty(): last_x, last_y, last_depth = self.waypoint.dequeue() self.current_waypoint_x = last_x self.current_waypoint_y = last_y direction_r, degree_r, distance_m = self.waypoint.get_directions( last_x, last_y) direction_h, distance_h = self.waypoint.get_depth_directions( last_depth) self.go_waypoint(direction_r, degree_r, r_power, direction_h, distance_h, h_power, distance_m, m_power) #check if sub is within depth threshold def is_at_assigned_depth(self): if abs(self.depth_assignment - self.waypoint.get_depth()) <= self.depth_threshold: return True return False #run through all waypoints in stack/queue def run_stack_waypoints(self, r_power=None, h_power=None, m_power=None): if not r_power: r_power = self.r_power if not h_power: h_power = self.h_power if not m_power: m_power = self.m_power # print('waiting 4 seconds') # self.ros_sleep(4) # self.set_exit_waypoints(False) self.reset_wp_vals() print('running all stack waypoints...') while not self.waypoint.is_empty() and not self.exit_waypoints: if not self.is_busy_waypoint and self.is_at_assigned_depth(): self.run_top_stack_waypoint(r_power, h_power, m_power) print('finished running all waypoints') def run_queue_waypoints(self, r_power=None, h_power=None, m_power=None): if not r_power: r_power = self.r_power if not h_power: h_power = self.h_power if not m_power: m_power = self.m_power # print('waiting 4 seconds') # self.ros_sleep(4) # self.set_exit_waypoints(False) self.reset_wp_vals() print('running all queue waypoints...') while not self.waypoint.is_empty() and not self.exit_waypoints: if not self.is_busy_waypoint and self.is_at_assigned_depth(): self.run_front_queue_waypoint(r_power, h_power, m_power) print('finished running all waypoints') #options to run waypoints on new thread async def run_stack_waypoints_async(self, r_power=None, h_power=None, m_power=None): if not r_power: r_power = self.r_power if not h_power: h_power = self.h_power if not m_power: m_power = self.m_power self.reset_thread() self.thread_w = Thread(target=self.run_stack_waypoints, args=(r_power, h_power, m_power)) self.thread_w.start() def run_queue_waypoints_async(self, r_power=None, h_power=None, m_power=None): if not r_power: r_power = self.r_power if not h_power: h_power = self.h_power if not m_power: m_power = self.m_power self.reset_thread() self.thread_w = Thread(target=self.run_queue_waypoints, args=(r_power, h_power, m_power)) self.thread_w.start() def set_exit_waypoints(self, exit=False): self.exit_waypoints = exit def reset_thread(self): if self.thread_w: self.thread_w = None def reset_wp_vals(self): self.is_running_waypoint_rotation = False self.is_running_waypoint_movement = False self.is_busy_waypoint = False self.waypoint_state = 0 #vars dealing with movement break time self.waypoint_m_time = 0 #vars dealing with height checking #reset initial depth to current self.depth_assignment = self.waypoint.get_depth() self.current_waypoint_x = 0 self.current_waypoint_y = 0 self.exit_waypoints = False def save_current_heading(self): self.saved_heading = self.waypoint.get_dvl_yaw() print('saved heading: {}'.format(str(self.saved_heading))) def do_depth_cap(self, h_power): depth = self.waypoint.get_depth() if depth < self.depth_cap: self.cancel_and_h_nav('down', 0.2, h_power)