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 PKG = 'pr2_counterbalance_check'
00039 import roslib; roslib.load_manifest(PKG)
00040
00041 import numpy
00042 import math
00043 import sys
00044
00045
00046 import matplotlib
00047 matplotlib.use('Agg')
00048 import matplotlib.pyplot as plt
00049 from StringIO import StringIO
00050
00051 from pr2_self_test_msgs.msg import Plot, TestValue, TestParam
00052
00053 ok_dict = { False: 'FAIL', True: 'OK' }
00054
00055 def str_to_bytes(s):
00056 return map(lambda x: x if x < 128 else x-256, map(ord, s))
00057
00058 class JointPositionAnalysisData(object):
00059 def __init__(self, msg):
00060 self.time = numpy.array(msg.time)
00061 self.position = numpy.array(msg.position)
00062 self.velocity = numpy.array(msg.velocity)
00063 self.effort = numpy.array(msg.effort)
00064
00065 self.position_avg = numpy.average(self.position)
00066 self.position_sd = numpy.std(self.position)
00067 self.effort_avg = numpy.average(self.effort)
00068 self.effort_sd = numpy.std(self.effort)
00069
00070 class CBPositionAnalysisData(object):
00071 def __init__(self, msg):
00072 self.flex_position = msg.flex_position
00073 self.lift_hold = JointPositionAnalysisData(msg.lift_hold)
00074 self.flex_hold = JointPositionAnalysisData(msg.flex_hold)
00075
00076 class CBRunAnalysisData(object):
00077 def __init__(self, msg):
00078 self.lift_position = msg.lift_position
00079 self.flex_data = []
00080 for fd in msg.flex_data:
00081 self.flex_data.append(CBPositionAnalysisData(fd))
00082
00083 class CounterbalanceAnalysisData(object):
00084
00085 def __init__(self, msg):
00086 self.lift_data = []
00087 for ld in msg.lift_data:
00088 self.lift_data.append(CBRunAnalysisData(ld))
00089
00090
00091 class CounterbalanceAnalysisParams(object):
00092 def __init__(self, msg):
00093 self.lift_dither = msg.lift_amplitude
00094 self.flex_dither = msg.flex_amplitude
00095
00096 self.lift_joint = msg.lift_joint
00097 self.flex_joint = msg.flex_joint
00098
00099 self.timeout_hit = msg.timeout_hit
00100 self.flex_test = msg.flex_test
00101
00102 self.lift_mse = msg.arg_value[9]
00103 self.lift_avg_abs = msg.arg_value[10]
00104 self.lift_avg_eff = msg.arg_value[11]
00105 self.flex_mse = msg.arg_value[12]
00106 self.flex_avg_abs = msg.arg_value[13]
00107 self.flex_avg_eff = msg.arg_value[14]
00108
00109 self.lift_p = msg.arg_value[15]
00110 self.lift_i = msg.arg_value[16]
00111 self.lift_d = msg.arg_value[17]
00112 self.lift_i_clamp = msg.arg_value[18]
00113
00114 self.flex_p = msg.arg_value[19]
00115 self.flex_i = msg.arg_value[20]
00116 self.flex_d = msg.arg_value[21]
00117 self.flex_i_clamp = msg.arg_value[22]
00118
00119 if len(msg.arg_value) > 24:
00120 self.screw_tol = msg.arg_value[23]
00121 self.bar_tol = msg.arg_value[24]
00122 else:
00123
00124 self.screw_tol = 2.0
00125 self.bar_tol = 0.8
00126
00127 self.num_flexes = len(msg.lift_data[0].flex_data)
00128 self.num_lifts = len(msg.lift_data)
00129
00130 self.min_lift = msg.lift_data[0].lift_position
00131 self.max_lift = msg.lift_data[-1].lift_position
00132
00133 self.min_flex = msg.lift_data[0].flex_data[0].flex_position
00134 self.max_flex = msg.lift_data[0].flex_data[-1].flex_position
00135
00136 self.named_params = {}
00137 for i in range(0, 9):
00138 self.named_params[msg.arg_name[i]] = msg.arg_value[i]
00139
00140 def get_test_params(self):
00141 params = []
00142 params.append(TestParam(key='Lift Dither', value=str(self.lift_dither)))
00143 params.append(TestParam(key='Flex Dither', value=str(self.flex_dither)))
00144 params.append(TestParam(key='Lift Joint', value=self.lift_joint))
00145 params.append(TestParam(key='Flex Joint', value=self.flex_joint))
00146 params.append(TestParam(key='Timeout Hit', value=ok_dict[not self.timeout_hit]))
00147 params.append(TestParam(key='Flex Tested', value=str(self.flex_test)))
00148
00149 params.append(TestParam(key='Lift MSE', value=str(self.lift_mse)))
00150 params.append(TestParam(key='Lift Avg Abs', value=str(self.lift_avg_abs)))
00151 params.append(TestParam(key='Lift Avg Effort', value=str(self.lift_avg_eff)))
00152
00153
00154 params.append(TestParam(key='Lift P Gain', value=str(self.lift_p)))
00155 params.append(TestParam(key='Lift I Gain', value=str(self.lift_i)))
00156 params.append(TestParam(key='Lift D Gain', value=str(self.lift_d)))
00157 params.append(TestParam(key='Lift I Clamp', value=str(self.lift_i_clamp)))
00158
00159 params.append(TestParam(key='Num Lifts', value=str(self.num_lifts)))
00160 params.append(TestParam(key='Min Lift', value="%.2f" % self.min_lift))
00161 params.append(TestParam(key='Max Lift', value="%.2f" % self.max_lift))
00162
00163 if self.flex_test:
00164 params.append(TestParam(key='Flex MSE', value=str(self.flex_mse)))
00165 params.append(TestParam(key='Flex Avg Abs', value=str(self.flex_avg_abs)))
00166 params.append(TestParam(key='Flex Avg Effort', value=str(self.flex_avg_eff)))
00167 params.append(TestParam(key='Flex P Gain', value=str(self.flex_p)))
00168 params.append(TestParam(key='Flex I Gain', value=str(self.flex_i)))
00169 params.append(TestParam(key='Flex D Gain', value=str(self.flex_d)))
00170 params.append(TestParam(key='Flex I Clamp', value=str(self.flex_i_clamp)))
00171 params.append(TestParam(key='Num Flexes', value=str(self.num_flexes)))
00172 params.append(TestParam(key='Min Flex', value="%.2f" % self.min_flex))
00173 params.append(TestParam(key='Max Flex', value="%.2f" % self.max_flex))
00174
00175 for key, val in self.named_params.iteritems():
00176 params.append(TestParam(key=key, value=str(val)))
00177
00178
00179 return params
00180
00181 class CounterbalanceAnalysisResult:
00182 __slots__ = ['html', 'summary', 'result', 'values']
00183 def __init__(self):
00184 self.html = ''
00185 self.summary = ''
00186 self.result = False
00187 self.values = []
00188
00189
00190
00191
00192 def get_efforts(data, lift_calc):
00193 avg_effort_list = []
00194 for ld in data.lift_data:
00195 for fd in ld.flex_data:
00196 if lift_calc:
00197 avg_effort_list.append(fd.lift_hold.effort_avg)
00198 else:
00199 avg_effort_list.append(fd.flex_hold.effort_avg)
00200
00201 return avg_effort_list
00202
00203 def _get_mean_sq_effort(avg_effort_array):
00204 sq_array = avg_effort_array * avg_effort_array
00205 return numpy.average(sq_array)
00206
00207 def _get_mean_abs_effort(avg_effort_array):
00208 abs_array = abs(avg_effort_array)
00209 return numpy.average(abs_array)
00210
00211 def _get_mean_effort(avg_effort_array):
00212 return numpy.average(avg_effort_array)
00213
00214
00215 def _get_const_flex_effort(data, flex_index = 0, lift_calc = True):
00216 effort_list = []
00217 lift_list = []
00218 for ld in data.lift_data:
00219 fd = ld.flex_data[flex_index]
00220 lift_list.append(ld.lift_position)
00221 if lift_calc:
00222 effort_list.append(fd.lift_hold.effort_avg)
00223 else:
00224 effort_list.append(fd.flex_hold.effort_avg)
00225
00226 return lift_list, effort_list
00227
00228 def _get_const_lift_effort(data, lift_index = 0, lift_calc = True):
00229 ld = data.lift_data[lift_index]
00230
00231 effort_list = []
00232 flex_list = []
00233
00234 for fd in ld.flex_data:
00235 flex_list.append(fd.flex_position)
00236 if lift_calc:
00237 effort_list.append(fd.lift_hold.effort_avg)
00238 else:
00239 effort_list.append(fd.flex_hold.effort_avg)
00240
00241 return flex_list, effort_list
00242
00243
00244
00245 def _get_flex_positions(data):
00246 flex_list = []
00247 for fd in data.lift_data[0].flex_data:
00248 flex_list.append(fd.flex_position)
00249
00250 return flex_list
00251
00252 def _get_lift_positions(data):
00253 lifts = []
00254 for ld in data.lift_data:
00255 lifts.append(ld.lift_position)
00256 return lifts
00257
00258
00259
00260
00261
00262
00263
00264 def plot_effort_contour(params, data, lift_calc = True):
00265 effort_list = []
00266 for i in range(0, params.num_lifts):
00267 flexes, efforts = _get_const_lift_effort(data, i, lift_calc)
00268 effort_list.append(efforts)
00269 flexes = _get_flex_positions(data)
00270 lifts = _get_lift_positions(data)
00271
00272 flex_grid, lift_grid = numpy.meshgrid(numpy.array(flexes), numpy.array(lifts))
00273 effort_grid = numpy.array(effort_list)
00274
00275 CS = plt.contour(flex_grid, lift_grid, effort_grid)
00276 plt.clabel(CS, inline=0, fontsize=10)
00277
00278 plt.xlabel('Flex')
00279 plt.ylabel('Lift')
00280
00281 stream = StringIO()
00282 plt.savefig(stream, format = 'png')
00283 image = stream.getvalue()
00284 p = Plot()
00285 if lift_calc:
00286 p.title = 'lift_effort_contour'
00287 else:
00288 p.title = 'flex_effort_contour'
00289 p.image = str_to_bytes(image)
00290 p.image_format = 'png'
00291
00292 plt.close()
00293
00294 return p
00295
00296
00297
00298
00299
00300 def plot_efforts_by_lift_position(params, data, flex_index = -1, lift_calc = True):
00301 lift_position, effort = _get_const_flex_effort(data, flex_index, lift_calc)
00302
00303 flex_position = data.lift_data[0].flex_data[flex_index].flex_position
00304
00305 plt.plot(numpy.array(lift_position), numpy.array(effort))
00306 if lift_calc:
00307 plt.title('Shoulder Lift Effort at Flex Position %.2f' % (flex_position))
00308 else:
00309 plt.title('Shoulder Flex Effort at Flex Position %.2f' % (flex_position))
00310 plt.axes()
00311 plt.xlabel('Lift Position')
00312 plt.ylabel('Effort')
00313 plt.axhline(y = 0, color = 'r', label='_nolegend_')
00314
00315 stream = StringIO()
00316 plt.savefig(stream, format = 'png')
00317 image = stream.getvalue()
00318 p = Plot()
00319 if lift_calc:
00320 p.title = 'lift_effort_const_flex_%d' % flex_index
00321 else:
00322 p.title = 'flex_effort_const_flex_%d' % flex_index
00323 p.image = str_to_bytes(image)
00324 p.image_format = 'png'
00325
00326 plt.close()
00327
00328 return p
00329
00330
00331
00332
00333 def analyze_lift_efforts(params, data):
00334 result = CounterbalanceAnalysisResult()
00335
00336 avg_efforts = numpy.array(get_efforts(data, True))
00337 mse = _get_mean_sq_effort(avg_efforts)
00338 avg_abs = _get_mean_abs_effort(avg_efforts)
00339 avg_eff = _get_mean_effort(avg_efforts)
00340
00341 mse_ok = mse < params.lift_mse
00342 avg_abs_ok = avg_abs < params.lift_avg_abs
00343 avg_eff_ok = abs(avg_eff) < abs(params.lift_avg_eff)
00344
00345 if mse_ok and avg_abs_ok:
00346 result.summary = 'Lift efforts OK'
00347 elif avg_eff < 0:
00348 result.summary = 'Counterbalance is too weak. Requires adjustment'
00349 else:
00350 result.summary = 'Counterbalance is too strong. Requires adjustment'
00351
00352 html = ['<p>%s</p>' % result.summary]
00353
00354 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00355 html.append('<tr><td><b>Parameter</b></td><td><b>Value</b></td><td><b>Maximum</b></td><td><b>Status</b></td></tr>')
00356 html.append('<tr><td><b>Mean Sq. Effort</b></td><td>%.2f</td><td>%.2f</td><td>%s</td></tr>' % (mse, params.lift_mse, ok_dict[mse_ok]))
00357 html.append('<tr><td><b>Average Abs. Effort</b></td><td>%.2f</td><td>%.2f</td><td>%s</td></tr>' % (avg_abs, params.lift_avg_abs, ok_dict[avg_abs_ok]))
00358 html.append('<tr><td><b>Average Effort</b></td><td>%.2f</td><td>%.2f</td><td>%s</td></tr>' % (avg_eff, params.lift_avg_eff, ok_dict[avg_eff_ok]))
00359 html.append('</table>')
00360
00361 result.html = '\n'.join(html)
00362
00363 result.result = mse_ok and avg_abs_ok
00364
00365 result.values = []
00366 result.values.append(TestValue('Lift MSE', str(mse), '', str(params.lift_mse)))
00367 result.values.append(TestValue('Lift Avg. Abs. Effort', str(avg_abs), '', str(params.lift_avg_abs)))
00368 result.values.append(TestValue('Lift Avg. Effort', str(avg_eff), '', str(params.lift_avg_eff)))
00369
00370 return result
00371
00372
00373
00374
00375 def analyze_flex_efforts(params, data):
00376 result = CounterbalanceAnalysisResult()
00377
00378 avg_efforts = numpy.array(get_efforts(data, False))
00379 mse = _get_mean_sq_effort(avg_efforts)
00380 avg_abs = _get_mean_abs_effort(avg_efforts)
00381 avg_eff = _get_mean_effort(avg_efforts)
00382
00383 mse_ok = mse < params.flex_mse
00384 avg_abs_ok = avg_abs < params.flex_avg_abs
00385 avg_eff_ok = abs(avg_eff) < abs(params.flex_avg_eff)
00386
00387 if mse_ok and avg_abs_ok:
00388 result.summary = 'Flex efforts OK'
00389 else:
00390 result.summary = 'Flex MSE/Avg. Absolute effort too high'
00391
00392 html = ['<p>%s</p>' % result.summary]
00393
00394 html.append('<table border="1" cellpadding="2" cellspacing="0">')
00395 html.append('<tr><td><b>Parameter</b></td><td><b>Value</b></td><td><b>Maximum</b></td><td><b>Status</b></td></tr>')
00396 html.append('<tr><td><b>Mean Sq. Effort</b></td><td>%.2f</td><td>%.2f</td><td>%s</td></tr>' % (mse, params.flex_mse, ok_dict[mse_ok]))
00397 html.append('<tr><td><b>Average Abs. Effort</b></td><td>%.2f</td><td>%.2f</td><td>%s</td></tr>' % (avg_abs, params.flex_avg_abs, ok_dict[avg_abs_ok]))
00398 html.append('<tr><td><b>Average Effort</b></td><td>%.2f</td><td>%.2f</td><td>%s</td></tr>' % (avg_eff, params.flex_avg_eff, ok_dict[avg_eff_ok]))
00399 html.append('</table>')
00400
00401 result.html = '\n'.join(html)
00402
00403 result.result = mse_ok and avg_abs_ok
00404
00405 result.values = []
00406 result.values.append(TestValue('Flex MSE', str(mse), '', str(params.flex_mse)))
00407 result.values.append(TestValue('Flex Avg. Abs. Effort', str(avg_abs), '', str(params.flex_avg_abs)))
00408 result.values.append(TestValue('Flex Avg. Effort', str(avg_eff), '', str(params.flex_avg_eff)))
00409
00410 return result
00411
00412
00413
00414
00415 def calc_cb_adjust(data, model_file):
00416 try:
00417
00418
00419
00420
00421 A = numpy.load(model_file)[:-1].transpose()
00422 B = numpy.array(get_efforts(data, True) + get_efforts(data, False))
00423 X = numpy.linalg.lstsq(A,B)
00424 except:
00425 print >> sys.stderr, "Unable to calculate CB adjustment. May have incorrect model data"
00426 import traceback
00427 traceback.print_exc()
00428 return (100, 100)
00429
00430
00431 secondary = -X[0][0]
00432 cb_bar = X[0][1]
00433
00434 return (secondary, cb_bar)
00435
00436
00437
00438
00439
00440
00441
00442
00443 def check_cb_adjustment(params, data, model_file):
00444 result = CounterbalanceAnalysisResult()
00445
00446 (secondary, cb_bar) = calc_cb_adjust(data, model_file)
00447
00448 if (abs(secondary) > 25 or abs(cb_bar) > 25):
00449 result.result = False
00450 result.summary = 'Unable to calculate CB adjustment. Data may be corrupt or CB may be too far off'
00451 result.html = '<p>Unable to calculate CB adjustment.</p>'
00452 return result
00453
00454 secondary_dir = 'CW' if secondary > 0 else 'CCW'
00455 cb_bar_dir = 'CW' if cb_bar > 0 else 'CCW'
00456
00457 adjust_msg = '<table border="1" cellpadding="2" cellspacing="0">'
00458 adjust_msg += '<tr><td><b>Adjustment</b></td><td><b>Turns</b></td><td><b>Direction</b></td><td><b>Allowable Tolerance</b></td></tr>\n'
00459 adjust_msg += '<tr><td>Secondary Spring</td><td>%.1f</td><td>%s</td><td>%.1f</td></tr>\n' % (abs(secondary), secondary_dir, params.screw_tol)
00460 adjust_msg += '<tr><td>Arm Gimbal Shaft</td><td>%.1f</td><td>%s</td><td>%.1f</td></tr>\n' % (abs(cb_bar), cb_bar_dir, params.bar_tol)
00461 adjust_msg += '</table>\n'
00462
00463
00464 if abs(secondary) > params.screw_tol or abs(cb_bar) > params.bar_tol:
00465 result.result = False
00466 result.summary = 'CB adjustment recommended. Follow instructions below to tune CB. '
00467 result.html = '<p>CB adjustment recommended. Adjusting the counterbalance will increase performance of the arm. (Note: CW = "Clockwise")</p>\n<p>%s</p>\n' % adjust_msg
00468 else:
00469 result.result = True
00470 result.summary = ''
00471 result.html = '<p>No CB adjustment recommended. You may choose to adjust the CB using the following instructions, but this is within tolerance.</p>\n<p>%s</p>' % adjust_msg
00472
00473
00474 result.values = [TestValue('Secondary Spring Adjustment', str(secondary), '', str(params.screw_tol)),
00475 TestValue('CB Bar Adjustment', str(cb_bar), '', str(params.bar_tol))]
00476
00477 return result