00001
00002
00003 import actionlib
00004 import rospy
00005 import threading
00006 import time
00007 import yaml
00008
00009 from bwi_planning_common.srv import PlannerInterface
00010 from bwi_planning_common.msg import PlannerAtom
00011 from bwi_tools import WallRate
00012 from geometry_msgs.msg import PoseWithCovarianceStamped
00013 from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal
00014 from segbot_gui.srv import QuestionDialog, QuestionDialogRequest
00015 from segbot_simulation_apps.srv import DoorHandlerInterface
00016 from sound_play.msg import SoundRequest
00017
00018 from .atom import Atom
00019
00020 def point_in_polygon(point, polygon):
00021 nvert = len(polygon)
00022 c = False
00023 j = nvert - 1
00024 for i in range(nvert):
00025 if (((polygon[i][1] >= point[1]) != (polygon[j][1] >= point[1])) and
00026 (point[0] <= ((polygon[j][0] - polygon[i][0]) *
00027 (point[1] - polygon[i][1]) /
00028 (polygon[j][1] - polygon[i][1])) + polygon[i][0])):
00029 c = not c
00030 j = i
00031 return c
00032
00033 class ActionExecutor(object):
00034
00035 def __init__(self, dry_run=False, initial_file=None, atom_class=Atom):
00036
00037 self.dry_run = dry_run
00038 if self.dry_run:
00039 rospy.loginfo("This is a dry run. The expected next state will " +
00040 "be used to generate observations.")
00041 self.auto_open_door = rospy.get_param("~auto_open_door", False)
00042 self.initial_file = initial_file
00043
00044
00045 self.artificial_delays = None
00046 self.artificial_delay_file = \
00047 rospy.get_param("~artificial_delay_file", None)
00048 if self.artificial_delay_file:
00049 rospy.loginfo("Using artificial delays read from " +
00050 self.artificial_delay_file)
00051 adf = open(self.artificial_delay_file)
00052 self.artificial_delays = yaml.load(adf)
00053 adf.close()
00054 self.pose_subscriber = rospy.Subscriber("amcl_pose",
00055 PoseWithCovarianceStamped,
00056 self.pose_handler)
00057 self.move_base_client = actionlib.SimpleActionClient(
00058 'move_base', MoveBaseAction)
00059 self.move_base_client.wait_for_server()
00060
00061
00062 rospy.loginfo("Waiting for GUI to come up...")
00063 rospy.wait_for_service('question_dialog')
00064 self._gui = rospy.ServiceProxy('question_dialog', QuestionDialog)
00065 rospy.loginfo(" Found GUI")
00066 self.use_speech = rospy.get_param("~use_speech", False)
00067
00068 if self.use_speech:
00069 rospy.loginfo(" Will Speak")
00070 self.sound_publisher = rospy.Publisher('robotsound', SoundRequest)
00071
00072 if not self.dry_run:
00073
00074
00075 rospy.wait_for_service('execute_logical_goal')
00076 self.nav_executor = rospy.ServiceProxy('execute_logical_goal',
00077 PlannerInterface)
00078
00079
00080 if self.auto_open_door:
00081 self.update_doors = rospy.ServiceProxy('update_doors',
00082 DoorHandlerInterface)
00083 self.atom_class = atom_class
00084
00085 def gui(self, displayType, text, choices, timeout):
00086 if self.use_speech:
00087 req = SoundRequest()
00088 req.sound = SoundRequest.SAY
00089 req.command = SoundRequest.PLAY_ONCE
00090 req.arg = text
00091 self.sound_publisher.publish(req)
00092 return self._gui(displayType, text, choices, timeout)
00093
00094 def clear_gui(self):
00095 self._gui(QuestionDialogRequest.DISPLAY, "", [], 0.0)
00096
00097 def pose_handler(self, msg):
00098 self.position_frame_id = msg.header.frame_id
00099 self.pose = msg.pose.pose
00100 self.position = msg.pose.pose.position
00101
00102 def stop_robot(self):
00103 goal = MoveBaseGoal()
00104 goal.target_pose.header.stamp = rospy.get_rostime()
00105 goal.target_pose.header.frame_id = self.position_frame_id
00106 goal.target_pose.pose = self.pose
00107 self.move_base_client.send_goal(goal)
00108 self.move_base_client.wait_for_result()
00109
00110 def artificial_delay_handler(self):
00111 self.expect_artificial_delays = True
00112 delays_encountered = []
00113 r = rospy.Rate(1)
00114 while self.expect_artificial_delays:
00115 for i,delay in enumerate(self.artificial_delays):
00116 if i in delays_encountered:
00117 continue
00118 if point_in_polygon([self.position.x, self.position.y],
00119 delay['location']):
00120 print "in delay region"
00121 delays_encountered.append(i)
00122 self.artificial_delay_time = delay['time']
00123 self.stopping_for_artificial_delay = True
00124 self.stop_robot()
00125 r.sleep()
00126
00127 def sense_initial_state(self):
00128
00129 if self.dry_run:
00130
00131 return
00132
00133 if self.auto_open_door:
00134 self.update_doors("", False, True)
00135
00136 result = self.nav_executor(PlannerAtom("noop", []))
00137
00138 initial_file = open(self.initial_file,"w")
00139 display_message = "Initial state: "
00140 for fluent in result.observations:
00141 atom = self.atom_class(fluent.name, ",".join(fluent.value), time=0)
00142 initial_file.write(str(atom) + ".\n")
00143 display_message += str(atom) + " "
00144 initial_file.close()
00145 rospy.loginfo(display_message)
00146 rospy.loginfo("Sensed initial state stored in: " + self.initial_file)
00147
00148 def execute_action(self, action, next_state, next_step):
00149
00150 rospy.loginfo("Executing action: " + str(action))
00151
00152 if self.dry_run and action.name != "askploc" and action.name != "greet":
00153 time.sleep(1)
00154 rospy.loginfo(" Observations: " + str(next_state))
00155 return True, next_state
00156
00157 success = False
00158 if (action.name == "approach" or action.name == "gothrough" or
00159 action.name == "goto"):
00160
00161 self.stopping_for_artificial_delay = False
00162 if self.artificial_delays:
00163 self.artificial_delay_thread = \
00164 threading.Thread(target=self.artificial_delay_handler)
00165 self.artificial_delay_thread.start()
00166
00167 while True:
00168 response = self.nav_executor(PlannerAtom(action.name,
00169 [str(action.value)]))
00170 if response.success or not self.stopping_for_artificial_delay:
00171 break
00172 rospy.sleep(self.artificial_delay_time)
00173 self.stopping_for_artificial_delay = False
00174
00175 if self.artificial_delays:
00176 self.expect_artificial_delays = False
00177 self.artificial_delay_thread.join()
00178
00179 if self.auto_open_door:
00180
00181 self.update_doors(str(action.value), False, False)
00182
00183 success = response.success
00184 result = response.observations
00185
00186
00187 if action.name == "opendoor":
00188 if self.auto_open_door:
00189 self.update_doors(str(action.value), True, False)
00190 else:
00191 self.gui(QuestionDialogRequest.DISPLAY,
00192 "Can you open door " + str(action.value) + "?",
00193 [], 0.0)
00194 rate = WallRate(0.5)
00195 door_opened = False
00196 for i in range(60):
00197 response = self.nav_executor(PlannerAtom("sensedoor",
00198 [str(action.value)]))
00199 result = response.observations
00200 for fluent in result:
00201 if (fluent.name == "open" and
00202 fluent.value[0] == str(action.value)):
00203 if not self.auto_open_door:
00204 self.gui(QuestionDialogRequest.DISPLAY,
00205 "Thanks!!", [], 0.0)
00206 door_opened = True
00207 break
00208 if door_opened:
00209 break
00210 rate.sleep()
00211 if door_opened:
00212 success = True
00213
00214 if action.name == "askploc":
00215 response = self.gui(QuestionDialogRequest.TEXT_QUESTION,
00216 "Can you tell me where " + str(action.value) +
00217 " is?",
00218 [], 30.0)
00219 if response.index == QuestionDialogRequest.TEXT_RESPONSE:
00220 result = [PlannerAtom("inside",
00221 [str(action.value), response.text])]
00222 success = True
00223 else:
00224
00225 result = []
00226
00227 if action.name == "greet":
00228 self.gui(QuestionDialogRequest.DISPLAY,
00229 "Hello " + str(action.value) + "!!",
00230 [], 0.0)
00231 time.sleep(2.0)
00232 result = [PlannerAtom("visiting",[str(action.value)])]
00233 success = True
00234
00235 observations = []
00236 for fluent in result:
00237 observations.append(self.atom_class(fluent.name,
00238 ",".join(fluent.value),
00239 time=next_step))
00240 rospy.loginfo(" Observations: " + str(observations))
00241 self.clear_gui()
00242 return success, observations
00243
00244