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
00035
00036
00037
00038
00039
00040 from __future__ import with_statement
00041 PKG = 'pr2_hardware_test_monitor'
00042
00043 import roslib
00044 roslib.load_manifest(PKG)
00045
00046 from pr2_mechanism_msgs.msg import MechanismStatistics
00047 from diagnostic_msgs.msg import DiagnosticArray, DiagnosticStatus, KeyValue
00048 from std_srvs.srv import *
00049 from std_msgs.msg import Bool
00050
00051 import rospy
00052
00053 import threading
00054
00055 from pr2_hw_listener import PR2HWListenerBase
00056
00057 GRACE_HITS = 5
00058
00059 TURRET_NAME = 'fl_caster_rotation_joint'
00060 L_WHEEL_NAME = 'fl_caster_l_wheel_joint'
00061 R_WHEEL_NAME = 'fl_caster_r_wheel_joint'
00062
00063
00064 WHEEL_RADIUS = 0.074792
00065 WHEEL_OFFSET = 0.049
00066
00067 ALLOWED_SLIP = 0.020
00068 UPDATE_INTERVAL = 0.25
00069
00070 class CasterPosition(object):
00071 def __init__(self, msg):
00072 self.turret = None
00073 self.l_wheel = None
00074 self.r_wheel = None
00075 self.turret_cal = False
00076
00077 if msg is None:
00078 return
00079
00080 for js in msg:
00081 if js.name == TURRET_NAME:
00082 self.turret_cal = js.is_calibrated
00083 self.turret = js.position
00084 if js.name == L_WHEEL_NAME:
00085 self.l_wheel = js.position
00086 if js.name == R_WHEEL_NAME:
00087 self.r_wheel = js.position
00088
00089 def has_data(self):
00090 return self.turret_cal and self.turret is not None and self.l_wheel is not None and self.r_wheel is not None
00091
00092 def check_position(new, old):
00093 if not new.has_data() or not old.has_data():
00094 return True, None, None
00095
00096
00097 turret = new.turret - old.turret
00098
00099
00100 wheel_dx = turret * WHEEL_OFFSET
00101
00102
00103 r_dx = (new.r_wheel - old.r_wheel) * WHEEL_RADIUS
00104 l_dx = -1 * (new.l_wheel - old.l_wheel) * WHEEL_RADIUS
00105
00106
00107 r_err = r_dx - wheel_dx
00108 l_err = l_dx - wheel_dx
00109
00110 if (abs(r_err) > ALLOWED_SLIP) or (abs(l_err) > ALLOWED_SLIP):
00111 return False, r_err, l_err
00112 return True, r_err, l_err
00113
00114
00115 class CasterSlipListener(object):
00116 def __init__(self):
00117 self._ok = True
00118 self._update_time = 0
00119
00120 self.last_position = CasterPosition(None)
00121
00122 self._max_l_err_pos = None
00123 self._max_r_err_pos = None
00124
00125 self._max_l_err_neg = None
00126 self._max_r_err_neg = None
00127
00128 self._max_l_err_pos_reset = None
00129 self._max_r_err_pos_reset = None
00130
00131 self._max_l_err_neg_reset = None
00132 self._max_r_err_neg_reset = None
00133
00134 self._num_errors = 0
00135 self._num_errors_since_reset = 0
00136
00137 self.diag = DiagnosticStatus()
00138 self.stat = 0
00139 self.msg = ''
00140
00141 def create(self, params):
00142 return True
00143
00144 def reset(self):
00145 self._ok = True
00146 self._num_errors_since_reset = 0
00147
00148 self._max_l_err_pos_reset = None
00149 self._max_r_err_pos_reset = None
00150
00151 self._max_l_err_neg_reset = None
00152 self._max_r_err_neg_reset = None
00153
00154 def update(self, msg):
00155 if rospy.get_time() - self._update_time < UPDATE_INTERVAL:
00156 return self._ok
00157
00158 self._update_time = rospy.get_time()
00159
00160 position = CasterPosition(msg.joint_statistics)
00161
00162 ok, r_err, l_err = check_position(position, self.last_position)
00163 if not ok:
00164 self._ok = False
00165 self._num_errors += 1
00166 self._num_errors_since_reset += 1
00167
00168 if r_err is None:
00169 pass
00170 elif r_err > 0:
00171 self._max_r_err_pos = max(self._max_r_err_pos, abs(r_err))
00172 self._max_r_err_pos_reset = max(self._max_r_err_pos_reset, abs(r_err))
00173 else:
00174 self._max_r_err_neg = max(self._max_r_err_neg, abs(r_err))
00175 self._max_r_err_neg_reset = max(self._max_r_err_neg_reset, abs(r_err))
00176
00177 if l_err is None:
00178 pass
00179 elif l_err > 0:
00180 self._max_l_err_pos = max(self._max_l_err_pos, abs(l_err))
00181 self._max_l_err_pos_reset = max(self._max_l_err_pos_reset, abs(l_err))
00182 else:
00183 self._max_l_err_neg = max(self._max_l_err_neg, abs(l_err))
00184 self._max_l_err_neg_reset = max(self._max_l_err_neg_reset, abs(l_err))
00185
00186 self.last_position = position
00187
00188 return self._ok
00189
00190 def get_status(self):
00191 stat = 0
00192 if not self._ok:
00193 stat = 2
00194
00195 if rospy.get_time() - self._update_time > 3:
00196 stat = 3
00197
00198 diag = DiagnosticStatus()
00199 diag.level = stat
00200 diag.name = 'Caster Slip Listener'
00201 diag.message = 'OK'
00202 if diag.level == 2:
00203 diag.message = 'Caster Slipping'
00204 if diag.level == 3:
00205 diag.message = 'Caster Stale'
00206 diag.level = 2
00207
00208 diag.values.append(KeyValue("Turret", str(TURRET_NAME)))
00209 diag.values.append(KeyValue("R Wheel", str(R_WHEEL_NAME)))
00210 diag.values.append(KeyValue("L Wheel", str(L_WHEEL_NAME)))
00211 diag.values.append(KeyValue("Turret Position", str(self.last_position.turret)))
00212 diag.values.append(KeyValue("R Wheel Position", str(self.last_position.r_wheel)))
00213 diag.values.append(KeyValue("L Wheel Position", str(self.last_position.l_wheel)))
00214 diag.values.append(KeyValue("Max Pos. Left Slip", str(self._max_l_err_pos)))
00215 diag.values.append(KeyValue("Max Neg. Left Slip", str(self._max_l_err_neg)))
00216 diag.values.append(KeyValue("Max Pos. Right Slip", str(self._max_r_err_pos)))
00217 diag.values.append(KeyValue("Max Neg. Right Slip", str(self._max_r_err_neg)))
00218 diag.values.append(KeyValue("Max Pos. Left Slip (Reset)", str(self._max_l_err_pos_reset)))
00219 diag.values.append(KeyValue("Max Neg. Left Slip (Reset)", str(self._max_l_err_neg_reset)))
00220 diag.values.append(KeyValue("Max Pos. Right Slip (Reset)", str(self._max_r_err_pos_reset)))
00221 diag.values.append(KeyValue("Max Neg. Right Slip (Reset)", str(self._max_r_err_neg_reset)))
00222 diag.values.append(KeyValue("Wheel Offset", str(WHEEL_OFFSET)))
00223 diag.values.append(KeyValue("Wheel Diameter", str(WHEEL_RADIUS)))
00224 diag.values.append(KeyValue("Allowed Slip", str(ALLOWED_SLIP)))
00225 diag.values.append(KeyValue("Update Interval", str(UPDATE_INTERVAL)))
00226 diag.values.append(KeyValue("Total Errors", str(self._num_errors)))
00227 diag.values.append(KeyValue("Errors Since Reset", str(self._num_errors_since_reset)))
00228
00229 return diag
00230
00231
00232
00233 class TransmissionListener(PR2HWListenerBase):
00234 def __init__(self):
00235 self._joint_monitors = []
00236 self._mech_sub = None
00237 self._halt_motors = rospy.ServiceProxy('pr2_etherCAT/halt_motors', Empty)
00238
00239 self._trans_sub = rospy.Subscriber('pr2_transmission_check/transmission_status', Bool, self._trans_cb)
00240 self._reset_trans = rospy.ServiceProxy('pr2_transmission_check/reset_transmission_check', Empty)
00241
00242 self._mutex = threading.Lock()
00243 self._ok = True
00244 self._jms_ok = True
00245 self._last_msg_time = 0
00246
00247 def create(self, params):
00248 for joint, joint_param in params.iteritems():
00249
00250 if joint == 'type' or joint == 'file':
00251 continue
00252
00253
00254
00255 if joint == 'caster_slip':
00256 joint_mon = CasterSlipListener()
00257 if not joint_mon.create(joint_param):
00258 rospy.logerr('Unable to create CasterSlipListener')
00259 return False
00260 self._joint_monitors.append(joint_mon)
00261
00262 if self._joint_monitors:
00263 self._mech_sub = rospy.Subscriber('mechanism_statistics', MechanismStatistics, self._callback)
00264
00265 return True
00266
00267 def _trans_cb(self, msg):
00268 with self._mutex:
00269 self._last_msg_time = rospy.get_time()
00270
00271 was_ok = self._ok
00272 self._ok = msg.data
00273
00274 if not self._ok and was_ok:
00275 rospy.logerr('Halting motors, broken transmission.')
00276 try:
00277 self._halt_motors()
00278 except Exception, e:
00279 import traceback
00280 rospy.logerr('Caught exception trying to halt motors: %s', traceback.format_exc())
00281
00282
00283
00284 def _callback(self, msg):
00285 with self._mutex:
00286 self._last_msg_time = rospy.get_time()
00287
00288 was_ok = self._jms_ok
00289
00290 for joint_mon in self._joint_monitors:
00291 ok = joint_mon.update(msg)
00292 self._jms_ok = ok and self._jms_ok
00293
00294
00295
00296 if not self._jms_ok and was_ok:
00297 rospy.logerr('Halting motors, caster slipping')
00298 try:
00299 self._halt_motors()
00300 except Exception, e:
00301 import traceback
00302 rospy.logerr('Caught exception trying to halt motors: %s', traceback.format_exc())
00303
00304 def reset(self):
00305 with self._mutex:
00306 self._ok = True
00307 self._jms_ok = True
00308 for joint_mon in self._joint_monitors:
00309 joint_mon.reset()
00310
00311 try:
00312 self._reset_trans()
00313 except Exception, e:
00314 import traceback
00315 rospy.logerr("Unable to reset tranmission checker\n%s" % traceback.format_exc())
00316
00317 def check_ok(self):
00318 with self._mutex:
00319 if self._last_msg_time == 0:
00320 return 3, "No trans status", None
00321 if rospy.get_time() - self._last_msg_time > 3:
00322 return 3, "Trans status stale", None
00323
00324 if self._ok and self._jms_ok:
00325 status = DiagnosticStatus.OK
00326 msg = ''
00327 elif self._ok:
00328 status = DiagnosticStatus.ERROR
00329 msg = 'Caster Slipping'
00330 else:
00331 status = DiagnosticStatus.ERROR
00332 msg = 'Transmission Broken'
00333
00334 diag_stats = []
00335 for joint_mon in self._joint_monitors:
00336 diag_stats.append(joint_mon.get_status())
00337
00338 return status, msg, diag_stats