00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 import rospy
00035 import yaml
00036 import sys
00037 import actionlib
00038 import joint_states_settler.msg
00039 import time
00040 import threading
00041 import os
00042 import string
00043 
00044 import capture_executive
00045 from capture_executive.config_manager import ConfigManager
00046 from capture_executive.sensor_managers import *
00047 from capture_executive.robot_measurement_cache import RobotMeasurementCache
00048 
00049 from calibration_msgs.msg import RobotMeasurement
00050 
00051 from urdf_parser_py.urdf import URDF
00052 
00053 class CaptureExecutive:
00054     def __init__(self, config_dir, system, robot_desc, output_debug=False):
00055         
00056         self.cam_config        = yaml.load(open(config_dir + "/cam_config.yaml"))
00057         self.chain_config      = yaml.load(open(config_dir + "/chain_config.yaml"))
00058         self.laser_config      = yaml.load(open(config_dir + "/laser_config.yaml"))
00059         self.controller_config = yaml.load(open(config_dir + "/controller_config.yaml"))
00060         
00061         if self.laser_config == None:
00062             self.laser_config = dict()
00063         
00064         self.output_debug = output_debug
00065 
00066         
00067         self.message = None
00068 
00069         
00070         self.interval_status = None
00071 
00072         
00073         links = URDF().parse(robot_desc).link_map.keys()
00074 
00075         
00076         system = yaml.load(open(system))
00077         
00078         
00079         for cam in self.cam_config.keys():
00080             try:
00081                 link = system['sensors']['rectified_cams'][cam]['frame_id']
00082                 if not link in links:
00083                     print 'URDF does not contain link [%s]. Removing camera [%s]' % (link, cam)
00084                     del self.cam_config[cam]
00085             except:
00086                 print 'System description does not contain camera [%s]' % cam
00087                 del self.cam_config[cam]
00088 
00089         
00090         for chain in self.chain_config.keys():
00091             try:
00092                 link = system['sensors']['chains'][chain]['tip']
00093                 if not link in links:
00094                     print 'URDF does not contain link [%s]. Removing chain [%s]' % (link, chain)
00095                     del self.chain_config[chain]
00096             except:
00097                 print 'System description does not contain chain [%s]' % chain
00098                 del self.chain_config[chain]
00099 
00100         self.cache = RobotMeasurementCache()
00101         self.lock = threading.Lock()
00102 
00103         
00104         self.active = False
00105 
00106         
00107         self.config_manager = ConfigManager(self.cam_config,
00108                                             self.chain_config,
00109                                             self.laser_config,
00110                                             self.controller_config)
00111 
00112         
00113         self.cam_managers  = [ (cam_id,   CamManager(  cam_id,   self.add_cam_measurement) )   for cam_id   in self.cam_config.keys() ]
00114         self.chain_managers = [ (chain_id, ChainManager(chain_id, self.add_chain_measurement) ) for chain_id in self.chain_config.keys() ]
00115         self.laser_managers = [ (laser_id, LaserManager(laser_id, self.add_laser_measurement) ) for laser_id in self.laser_config.keys() ]
00116 
00117         
00118         self.request_interval_sub = rospy.Subscriber("intersected_interval", calibration_msgs.msg.Interval, self.request_callback)
00119 
00120         
00121         self.interval_status_sub = rospy.Subscriber(
00122               "intersected_interval_status",
00123               calibration_msgs.msg.IntervalStatus, self.status_callback)
00124 
00125         
00126         
00127 
00128     def capture(self, next_configuration, timeout):
00129         done = False
00130         self.m_robot = None
00131         self.message = None
00132         self.interval_status = None
00133 
00134         timeout_time = rospy.Time().now() + timeout
00135 
00136         self.lock.acquire()
00137         if self.active:
00138             rospy.logerr("Can't capture since we're already active")
00139             done = True
00140         self.lock.release()
00141 
00142         camera_measurements = next_configuration["camera_measurements"]
00143         next_configuration["camera_measurements"] = []
00144 
00145         for (i, cam) in enumerate(camera_measurements):
00146             if self.cam_config.has_key(cam["cam_id"]):
00147                 next_configuration["camera_measurements"].append(cam)
00148             else:
00149                 rospy.logdebug("Not capturing measurement for camera: %s"%(cam["cam_id"]))
00150 
00151         
00152         self.config_manager.reconfigure(next_configuration)
00153 
00154         time.sleep(2.0)
00155 
00156         
00157         cam_ids   = [x["cam_id"]   for x in next_configuration["camera_measurements"]]
00158         chain_ids = [x["chain_id"] for x in next_configuration["joint_measurements"]]
00159         try:
00160             laser_ids = [x["laser_id"] for x in next_configuration["laser_measurements"]]
00161         except:
00162             laser_ids = list()
00163         self.cache.reconfigure(cam_ids, chain_ids, laser_ids)
00164 
00165         rospy.logdebug("Setting up sensor managers")
00166         enable_list = []
00167         disable_list = []
00168         
00169         for cam_id, cam_manager in self.cam_managers:
00170             if cam_id in [x["cam_id"] for x in next_configuration["camera_measurements"]]:
00171                 enable_list.append(cam_id)
00172                 cam_manager.enable(self.output_debug)
00173             else:
00174                 disable_list.append(cam_id)
00175                 cam_manager.disable()
00176 
00177         for chain_id, chain_manager in self.chain_managers:
00178             if chain_id in [x["chain_id"] for x in next_configuration["joint_measurements"]]:
00179                 enable_list.append(chain_id)
00180                 chain_manager.enable()
00181             else:
00182                 disable_list.append(chain_id)
00183                 chain_manager.disable()
00184 
00185         for laser_id, laser_manager in self.laser_managers:
00186             enabled_lasers = [x["laser_id"] for x in next_configuration["laser_measurements"]]
00187             if laser_id in enabled_lasers:
00188                 enable_list.append(laser_id)
00189                 laser_manager.enable(self.output_debug)
00190             else:
00191                 disable_list.append(laser_id)
00192                 laser_manager.disable()
00193 
00194         print "Enabling:"
00195         for cur_enabled in enable_list:
00196             print " + %s" % cur_enabled
00197         print "Disabling:"
00198         for cur_disabled in disable_list:
00199             print " - %s" % cur_disabled
00200 
00201         self.lock.acquire()
00202         self.active = True
00203         self.lock.release()
00204 
00205         print "\nCollecting sensor data.."
00206         
00207         while (not rospy.is_shutdown()) and (not done) and (rospy.Time().now() < timeout_time):
00208             time.sleep(.1)
00209             self.lock.acquire()
00210             if self.m_robot is not None:
00211                 done = True
00212             self.lock.release()
00213 
00214         print "Stopping sensor streams..\n"
00215         
00216         for cam_id, cam_manager in self.cam_managers:
00217             cam_manager.disable()
00218         for chain_id, chain_manager in self.chain_managers:
00219             chain_manager.disable()
00220         for laser_id, laser_manager in self.laser_managers:
00221             laser_manager.disable()
00222 
00223         
00224         self.config_manager.stop()
00225 
00226         
00227         if self.m_robot is not None:
00228             self.m_robot.sample_id = next_configuration["sample_id"]
00229             self.m_robot.target_id = next_configuration["target"]["target_id"]
00230             self.m_robot.chain_id  = next_configuration["target"]["chain_id"]
00231         elif self.interval_status is not None:
00232             total = 0
00233             bad_sensors = []
00234             l = len(self.interval_status.names)
00235             if l != len(self.interval_status.yeild_rates):
00236                rospy.logerr("Invalid interval status message; names and yeild rates have different lengths")
00237                l = min(l, len(self.interval_status.yeild_rates))
00238             
00239             for i in range(l):
00240                if self.interval_status.yeild_rates[i] == 0.0:
00241                   bad_sensors.append(self.interval_status.names[i])
00242                total += self.interval_status.yeild_rates[i]
00243 
00244             
00245             if len(bad_sensors) > 0:
00246                print "Didn't get good data from %s"%(', '.join(bad_sensors))
00247             else:
00248                
00249                
00250                avg = total / l
00251                for i in range(l):
00252                   if self.interval_status.yeild_rates[i] <= avg:
00253                      bad_sensors.append(self.interval_status.names[i])
00254                print "%s may be performing poorly"%(", ".join(bad_sensors))
00255         elif self.message is not None:
00256             print self.message
00257 
00258         self.lock.acquire()
00259         self.active = False
00260         self.lock.release()
00261 
00262         return self.m_robot
00263 
00264     def request_callback(self, msg):
00265         
00266         if (msg.end - msg.start) < rospy.Duration(1,0):
00267             return
00268 
00269         self.lock.acquire()
00270         if self.active:
00271             
00272             m = self.cache.request_robot_measurement(msg.start, msg.end)
00273             if isinstance(m, basestring):
00274                self.message = m
00275             else:
00276                self.m_robot = m
00277             
00278             if self.m_robot is not None:
00279                 self.active = False
00280         self.lock.release()
00281 
00282     def status_callback(self, msg):
00283         self.lock.acquire()
00284         if self.active:
00285             self.interval_status = msg
00286         self.lock.release()
00287 
00288     def add_cam_measurement(self, cam_id, msg):
00289         if len(msg.image_points) > 0:
00290             self.lock.acquire()
00291             self.cache.add_cam_measurement(cam_id, msg)
00292             self.lock.release()
00293 
00294     def add_chain_measurement(self, chain_id, msg):
00295         self.lock.acquire()
00296         self.cache.add_chain_measurement(chain_id, msg)
00297         self.lock.release()
00298 
00299     def add_laser_measurement(self, laser_id, msg, interval_start, interval_end):
00300         self.lock.acquire()
00301         self.cache.add_laser_measurement(laser_id, msg, interval_start, interval_end)
00302         self.lock.release()
00303 
00304 if __name__=='__main__':
00305     rospy.init_node("capture_executive_node")
00306 
00307     rospy.logdebug("Starting executive...")
00308     rospy.sleep(5.0)
00309 
00310     
00311     samples_dir = rospy.myargv()[1]
00312     config_dir = rospy.myargv()[2]
00313     system = rospy.myargv()[3]
00314 
00315     try:
00316         robot_description = rospy.get_param('robot_description')
00317     except:
00318         rospy.logfatal('robot_description not set, exiting')
00319         sys.exit(-1)
00320 
00321     executive = CaptureExecutive(config_dir, system, robot_description)
00322     time.sleep(1.0)
00323 
00324     
00325     sample_steps = list()
00326     sample_names = dict()
00327     sample_options = dict()
00328     sample_success = dict()
00329     sample_failure = dict()
00330     for directory in os.listdir(samples_dir):
00331         try:
00332             sample_options[directory] = yaml.load(open(samples_dir + '/' + directory + '/config.yaml'))
00333             sample_steps.append(directory)
00334         except IOError:
00335             continue
00336         sample_names[directory] = [x for x in os.listdir(samples_dir + '/' + directory + '/') if '.yaml' in x and x != 'config.yaml']
00337         sample_names[directory].sort()
00338         sample_success[directory] = 0
00339         sample_failure[directory] = 0
00340     sample_steps.sort()
00341 
00342     for step in sample_steps:
00343         rospy.logdebug("%s Samples: \n - %s" % (sample_options[step]['group'], "\n - ".join(sample_names[step])))
00344 
00345     pub = rospy.Publisher("robot_measurement", RobotMeasurement)
00346 
00347     try:
00348         for step in sample_steps:
00349             keep_collecting = True
00350             full_paths = [samples_dir + '/' + step + '/' + x for x in sample_names[step] ]
00351             if len(full_paths) == 0:
00352                 rospy.logfatal(samples_dir + '/' + step + '/' + ' is not found' )
00353                 rospy.logfatal('please run make_samples.py' )
00354                 sys.exit(-1)
00355             cur_config = yaml.load(open(full_paths[0]))
00356             m_robot = executive.capture(cur_config, rospy.Duration(0.01))
00357             while not rospy.is_shutdown() and keep_collecting:
00358                 print
00359                 print sample_options[step]["prompt"]
00360                 print "Press <enter> to continue, type N to exit this step"
00361                 resp = raw_input(">>>")
00362                 if string.upper(resp) == "N":
00363                     print sample_options[step]["finish"]
00364                     keep_collecting = False
00365                 else:
00366                     for cur_sample_path in full_paths:
00367                         print "On %s sample [%s]" % (sample_options[step]["group"], cur_sample_path)
00368                         cur_config = yaml.load(open(cur_sample_path))
00369                         m_robot = executive.capture(cur_config, rospy.Duration(40))
00370                         if m_robot is None:
00371                             print "--------------- Failed To Capture a %s Sample -----------------" % sample_options[step]["group"]
00372                             if not sample_options[step]["repeat"]:
00373                                 sample_failure[step] += 1
00374                         else:
00375                             print "++++++++++++++ Successfully Captured a %s Sample ++++++++++++++" % sample_options[step]["group"]
00376                             sample_success[step] += 1
00377                             pub.publish(m_robot)
00378                         print "Succeeded on %u %s samples" % (sample_success[step], sample_options[step]["group"])
00379                         if rospy.is_shutdown():
00380                             break
00381                     keep_collecting = sample_options[step]["repeat"]
00382     except EOFError:
00383         print "Exiting"    
00384 
00385     time.sleep(1)
00386 
00387     print "Calibration data collection has completed!"
00388     for step in sample_steps:
00389         if sample_options[step]["repeat"]:
00390             print "%s Samples: %u" % (sample_options[step]["group"], sample_success[step])
00391         else:
00392             print "%s Samples: %u/%u" % (sample_options[step]["group"], sample_success[step], sample_success[step] + sample_failure[step])
00393     print ""
00394     print "You can now kill this node, along with any other calibration nodes that are running."
00395 
00396