00001
00002
00003 import csv
00004 import time
00005 import rospy
00006 import shutil
00007
00008 from bwi_planning.srv import CostLearnerInterface, CostLearnerInterfaceRequest
00009 from std_srvs.srv import Empty
00010
00011 from .action_executor_icaps2014 import ActionExecutorICAPS2014
00012 from .aton_icaps2014 import AtomICAPS2014
00013 from bwi_planning import ClingoWrapper
00014
00015 def get_adjacent_door(atoms):
00016 for atom in atoms:
00017 if atom.name == "beside" and not atom.negated:
00018 return str(atom.value)
00019 return None
00020
00021 def get_location(atoms):
00022 for atom in atoms:
00023 if atom.name == "at":
00024 return str(atom.value)
00025 return None
00026
00027 def cast_boolean(s):
00028 return s in ['1','t','true','T','True','TRUE','y','Y','yes','Yes','YES']
00029
00030 def read_table_from_file(file):
00031 t = {}
00032 csvfile = open(file, 'rb')
00033 csvreader = csv.reader(csvfile)
00034 first_row = True
00035 for row in csvreader:
00036 if first_row:
00037 column_headers = row
00038 first_row = False
00039 continue
00040 first_column = True
00041 for i,column_val in enumerate(row):
00042 if first_column:
00043 t[column_val] = {}
00044 row = t[column_val]
00045 first_column = False
00046 continue
00047 row[column_headers[i-1]] = cast_boolean(column_val)
00048
00049 csvfile.close()
00050 return t
00051
00052 def write_table_to_file(fluent_name, table, file_name, time=None):
00053 for row_name, row in table.iteritems():
00054 for column_name, value in row.iteritems():
00055 AtomICAPS2014
00056 atoms = [AtomICAPS2014(fluent_name, row_name+','+column_name, time, value)]
00057 table_file = open(file_name, 'w')
00058 for atom in atoms:
00059 table_file.write(str(atom) + '\n')
00060 table_file.close()
00061
00062 class PlannerICAPS2014(object):
00063
00064 def __init__(self):
00065 self.initialized = False
00066
00067 self.dry_run = rospy.get_param("~dry_run", False)
00068 self.clingo_interface = ClingoWrapper(AtomICAPS2014)
00069 initial_file = rospy.get_param("~initial_file", None)
00070 self.query_file = rospy.get_param("~query_file")
00071 self.passto_file = rospy.get_param("~passto_file")
00072 self.knowinside_file = rospy.get_param("~knowinside_file")
00073 self.knows_file = rospy.get_param("~knows_file")
00074 self.domain_costs_file = rospy.get_param("~domain_costs_file", None)
00075 self.costs_file = rospy.get_param("~costs_file", None)
00076 self.enable_learning = rospy.get_param("~enable_learning", False)
00077
00078 if self.dry_run and not initial_file:
00079 rospy.logfatal("Dry run requested, but no initial state provided." +
00080 " Please set the ~initial_file param.")
00081 return
00082 self.initial_file = "/tmp/initial"
00083 if self.dry_run:
00084 rospy.loginfo("Supplied initial state written to: " +
00085 self.initial_file)
00086 shutil.copyfile(initial_file, self.initial_file)
00087
00088 self.executor = ActionExecutorICAPS2014(self.dry_run, self.initial_file)
00089
00090 if self.enable_learning:
00091 rospy.wait_for_service('cost_learner/increment_episode')
00092 rospy.wait_for_service('cost_learner/add_sample')
00093 self.finalize_episode = \
00094 rospy.ServiceProxy('cost_learner/increment_episode', Empty)
00095 self.add_sample = \
00096 rospy.ServiceProxy('cost_learner/add_sample',
00097 CostLearnerInterface)
00098 self.planning_times_file = rospy.get_param("~planning_times_file",
00099 None)
00100
00101 self.passto_table = read_table_from_file(self.passto_file)
00102 self.knowinside_table = read_table_from_file(self.knowinside_file)
00103 self.knows_table = read_table_from_file(self.knows_file)
00104 self.initialized = True
00105
00106 def _construct_initial_state(self, previous_state, observations):
00107 """
00108 This is a poor man's belief merge function between the previous_state
00109 and observations. This needs to be replaced with proper belief merge
00110 algorithms as it impossible to cover every possible situation here
00111 using if/else statements
00112 """
00113
00114 new_state = []
00115 removed_atoms = []
00116 for atom in previous_state:
00117 atom.time = 0
00118 add_atom = True
00119 for observation in observations:
00120 if observation.conflicts_with(atom):
00121 add_atom = False
00122 if add_atom:
00123 new_state.append(atom)
00124 else:
00125 removed_atoms.append(atom)
00126
00127 if len(removed_atoms) != 0:
00128 display_message = " Incomaptible members in previous state: "
00129 for atom in removed_atoms:
00130 display_message += str(atom) + " "
00131 rospy.logwarn(display_message)
00132
00133 for observation in observations:
00134 observation.time = 0
00135 new_state.append(observation)
00136
00137 initial_file = open(self.initial_file,"w")
00138 display_message = " Constructed state: "
00139 for atom in new_state:
00140 initial_file.write(str(atom) + ".\n")
00141 display_message += str(atom) + " "
00142 initial_file.close()
00143 rospy.loginfo(display_message)
00144 rospy.loginfo("Constructed initial state written to: " +
00145 self.initial_file)
00146
00147 def _send_finish(self):
00148 if self.enable_learning:
00149 self.finalize_episode()
00150
00151 def start(self):
00152 if not self.initialized:
00153 return
00154 self.executor.sense_initial_state()
00155
00156
00157 passto_file = '/tmp/passto'
00158 write_table_to_file('passto', self.passto_table, passto_file)
00159 knows_file = '/tmp/knows'
00160 write_table_to_file('knows', self.knows_table, knows_file)
00161 knowinside_file = '/tmp/knowinside'
00162 write_table_to_file('knowinside', self.knowinside_table,
00163 knowinside_file, 0)
00164 first_run = True
00165 while True:
00166 additional_files = []
00167 if first_run:
00168 additional_files = [passto_file,
00169 knowinside_file,
00170 knows_file]
00171 first_run = False
00172 if self.domain_costs_file != None:
00173 rospy.loginfo("Planning with cost optimization!!")
00174 start_time = time.time()
00175 plan_available, optimization, plan, states = \
00176 self.clingo_interface.get_plan_costs([self.initial_file,
00177 self.query_file,
00178 self.costs_file,
00179 self.domain_costs_file].extend(additional_files)
00180 )
00181 duration = time.time() - start_time
00182 if self.enable_learning and self.planning_times_file:
00183 ptf = open(self.planning_times_file, "a")
00184 ptf.write(str(duration) + "\n")
00185 ptf.close()
00186 else:
00187 plan_available, optimization, plan, states = \
00188 self.clingo_interface.get_plan([self.initial_file,
00189 self.query_file].extend(additional_files))
00190 if not plan_available:
00191 rospy.logfatal("No plan found to complete task!")
00192 break
00193 rospy.loginfo("Found plan (optimization = %i): %s"%(optimization,
00194 plan))
00195
00196 need_replan = False
00197 for action in plan:
00198 current_state = [state for state in states
00199 if state.time == action.time]
00200 next_state = [state for state in states
00201 if state.time == action.time+1]
00202 start_time = rospy.get_time()
00203 success, observations = \
00204 self.executor.execute_action(action, next_state,
00205 action.time+1)
00206 duration = rospy.get_time() - start_time
00207 if self.enable_learning and action.name == "approach":
00208 if success:
00209
00210
00211 req = CostLearnerInterfaceRequest()
00212 req.location = get_location(current_state)
00213 req.door_from = get_adjacent_door(current_state)
00214 if req.door_from:
00215 req.door_to = str(action.value)
00216 req.cost = duration
00217 self.add_sample(req)
00218 else:
00219 rospy.loginfo("Executed unsuccessful approach action.")
00220
00221 for observation in observations:
00222 if observation not in next_state:
00223 rospy.logwarn(" Unexpected observation: " +
00224 str(observation))
00225 need_replan = True
00226 break
00227 if need_replan:
00228 break
00229
00230 if need_replan:
00231 self._construct_initial_state(current_state, observations)
00232 continue
00233
00234 rospy.loginfo("Plan successful!")
00235 self._send_finish()
00236 break
00237
00238