analyse_logs.py
Go to the documentation of this file.
00001 
00002 import roslib; roslib.load_manifest('modeling_forces')
00003 import rospy
00004 
00005 import hrl_lib.util as ut
00006 import hrl_lib.transforms as tr
00007 import matplotlib_util.util as mpu
00008 import hrl_tilting_hokuyo.display_3d_mayavi as d3m
00009 
00010 import modeling_forces.smooth as mfs
00011 import kinematics_estimation as ke
00012 
00013 import glob
00014 import math, numpy as np
00015 import sys
00016 
00017 ##
00018 # plot to ensure that the time stamps in the different logs are
00019 # reasonable.
00020 # TODO - check for the rates too.
00021 def check_time_sync(ft_time_list, mechanism_time_list, hand_time_list):
00022     mpu.plot_yx(np.zeros(len(ft_time_list))+1, ft_time_list,
00023                 color = mpu.random_color(), label='ft\_time\_list',
00024                 axis=None, linewidth=0.5, scatter_size=10)
00025     mpu.plot_yx(np.zeros(len(mechanism_time_list))+2, mechanism_time_list,
00026                 color = mpu.random_color(), label='mechanism\_time\_list',
00027                 axis=None, linewidth=0.5, scatter_size=10)
00028     mpu.plot_yx(np.zeros(len(hand_time_list))+3, hand_time_list,
00029                 color = mpu.random_color(), label='hand\_time\_list',
00030                 axis=None, linewidth=0.5, scatter_size=10)
00031     mpu.legend()
00032 #    mpu.show()
00033 
00034 
00035 ##
00036 #
00037 # @return single dict with ft_list, mech_pose_lists, hand_pose_lists
00038 # and ONE time_list
00039 def synchronize(ft_dict, mechanism_dict, hand_dict):
00040     ft_time_arr = np.array(ft_dict['time_list'])
00041     mech_time_arr = np.array(mechanism_dict['time_list'])
00042     hand_time_arr = np.array(hand_dict['time_list'])
00043     
00044     print 'ft_time_arr.shape:', ft_time_arr.shape
00045     print 'mech_time_arr.shape:', mech_time_arr.shape
00046     print 'hand_time_arr.shape:', hand_time_arr.shape
00047 
00048     start_time  = max(ft_time_arr[0], mech_time_arr[0],
00049                       hand_time_arr[0])
00050     end_time  = min(ft_time_arr[-1], mech_time_arr[-1],
00051                     hand_time_arr[-1])
00052 
00053     t1_arr = mech_time_arr[np.where(np.logical_and(mech_time_arr >= start_time,
00054                                           mech_time_arr <= end_time))]
00055     t2_arr = hand_time_arr[np.where(np.logical_and(hand_time_arr >= start_time,
00056                                           hand_time_arr <= end_time))]
00057 
00058     #time_arr = np.arange(start_time, end_time, 0.03) # 30ms
00059     n_times = min(len(t1_arr), len(t2_arr))
00060     time_arr_list = []
00061     i, j = 0, 0
00062     while True:
00063         if t1_arr[i] == t2_arr[j]:
00064             time_arr_list.append(t1_arr[i])
00065             i += 1
00066             j += 1
00067         elif t1_arr[i] > t2_arr[j]:
00068             j += 1
00069         else:
00070             i += 1
00071         if j == n_times or i == n_times:
00072             break
00073     time_arr = np.array(time_arr_list)
00074     tstep_size = .03333333333
00075     uniform_time = np.cumsum(np.round((time_arr[1:] - time_arr[:-1]) / tstep_size) * tstep_size)
00076     uniform_time = np.concatenate((np.array([0]), uniform_time))
00077     uniform_time = uniform_time + time_arr_list[0]
00078     time_arr = uniform_time
00079 
00080     # adding a 50ms bias. see modeling_forces/image_ft_sync_test
00081     ft_time_arr = ft_time_arr + 0.05
00082     raw_ft_arr = np.array(ft_dict['ft_list']).T
00083     window_len = 3
00084 
00085     sm_ft_l = []
00086     for i in range(raw_ft_arr.shape[0]):
00087         s = mfs.smooth(raw_ft_arr[i,:], window_len,'blackman')
00088         sm_ft_l.append(s.tolist())
00089     # smooth truncates the array
00090     if window_len != 1:
00091         ft_time_arr = ft_time_arr[window_len-1:-window_len+1]
00092     raw_ft_arr = (np.array(sm_ft_l).T).tolist()
00093 
00094     raw_mech_pos_arr = mechanism_dict['pos_list']
00095     raw_mech_rot_arr = mechanism_dict['rot_list']
00096 
00097     raw_hand_pos_arr = hand_dict['pos_list']
00098     raw_hand_rot_arr = hand_dict['rot_list']
00099 
00100     raw_arr_list = [raw_ft_arr, raw_mech_pos_arr, raw_mech_rot_arr,
00101                     raw_hand_pos_arr, raw_hand_rot_arr]
00102     time_arr_list = [ft_time_arr, mech_time_arr, mech_time_arr,
00103                      hand_time_arr, hand_time_arr]
00104     n_arr = len(raw_arr_list)
00105 
00106     ft_list = []
00107     mech_pos_list, mech_rot_list = [], []
00108     hand_pos_list, hand_rot_list = [], []
00109     acc_list = [ft_list, mech_pos_list, mech_rot_list, hand_pos_list,
00110                 hand_rot_list]
00111     key_list = ['ft_list', 'mech_pos_list', 'mech_rot_list',
00112                 'hand_pos_list', 'hand_rot_list']
00113 
00114     for i in range(time_arr.shape[0]):
00115         t = time_arr[i]
00116         for j in range(n_arr):
00117             # nearest neighbor interpolation
00118             min_idx = np.argmin(np.abs(time_arr_list[j] - t))
00119             acc_list[j].append(raw_arr_list[j][min_idx])
00120 
00121     d = {}
00122     d['time_list'] = time_arr.tolist()
00123     for i in range(n_arr):
00124         d[key_list[i]] = acc_list[i]
00125 
00126     return d
00127 
00128 
00129 #--------------- functions that operate on combined pkl ----------------------
00130 
00131 ##
00132 # transform forces to camera coord frame.
00133 # @param hand_rot_matrix - rotation matrix for camera to hand checker.
00134 # @param hand_pos_matrix - position of hand checkerboard in camera coord frame.
00135 # @param mech_pos_matrix - position of mechanism checkerboard in camera coord frame.
00136 # @param number - checkerboard number (1, 2, 3 or 4)
00137 def ft_to_camera(force_tool, hand_rot_matrix, hand_pos_matrix, mech_pos_matrix, number):
00138     # hc == hand checkerboard
00139     hc_rot_tool = tr.Rx(math.radians(90)) * tr.Ry(math.radians(180.)) * tr.Rz(math.radians(30.))
00140 
00141     while number != 1:
00142         hc_rot_tool = tr.Ry(math.radians(90.)) * hc_rot_tool
00143         number = number-1
00144 
00145     force_hc = hc_rot_tool * force_tool
00146     p_hc_ft = np.matrix([0.04, 0.01, 0.09]).T # vector from hook checkerboard origin to the base of the FT sensor in hook checker coordinates.
00147 
00148     # vec from FT sensor to mechanism checker origin in camera coordinates.
00149     p_ft_mech = -hand_pos_matrix + mech_pos_matrix - hand_rot_matrix * p_hc_ft
00150     
00151     force_cam = hand_rot_matrix * force_hc # force at hook base in camera coordinates
00152     moment_cam = hand_rot_matrix * moment_hc
00153     force_at_mech_origin = force_cam
00154     moment_at_mech_origin = moment_cam + np.matrix(np.cross(-p_ft_mech.A1, force_cam.A1)).T
00155 
00156     return hand_rot_matrix * force_hc
00157 
00158 ##
00159 # transform force to camera coord frame.
00160 def ft_to_camera_3(force_tool, moment_tool, hand_rot_matrix, number,
00161                    return_moment_cam = False):
00162     # hc == hand checkerboard
00163     hc_rot_tool = tr.Rx(math.radians(90)) * tr.Ry(math.radians(180.)) * tr.Rz(math.radians(30.))
00164 
00165     while number != 1:
00166         hc_rot_tool = tr.Ry(math.radians(90.)) * hc_rot_tool
00167         number = number-1
00168 
00169     force_hc = hc_rot_tool * force_tool
00170     moment_hc = hc_rot_tool * moment_tool
00171     p_ft_hooktip = np.matrix([0.0, -0.08, 0.00]).T # vector from FT sensor to the tip of the hook in hook checker coordinates.
00172 #    p_ft_hooktip = np.matrix([0.0, -0.08 - 0.034, 0.00]).T # vector from FT sensor to the tip of the hook in hook checker coordinates.
00173 
00174     p_ft_hooktip = hand_rot_matrix * p_ft_hooktip
00175     force_cam = hand_rot_matrix * force_hc # force at hook base in camera coordinates
00176     moment_cam = hand_rot_matrix * moment_hc
00177     force_at_hook_tip = force_cam
00178     moment_at_hook_tip = moment_cam + np.matrix(np.cross(-p_ft_hooktip.A1, force_cam.A1)).T
00179 
00180 #    if np.linalg.norm(moment_at_hook_tip) > 0.7:
00181 #        import pdb; pdb.set_trace()
00182 
00183     if return_moment_cam:
00184         return force_at_hook_tip, moment_at_hook_tip, moment_cam
00185     else:
00186         return force_at_hook_tip, moment_at_hook_tip
00187 
00188 def plot(combined_dict, savefig):
00189     plot_trajectories(combined_dict)
00190 
00191 #    tup = ke.init_ros_node()
00192 #    mech_rot_list = compute_mech_rot_list(combined_dict, tup)
00193 #    combined_dict['mech_rot_list'] = mech_rot_list
00194 #    plot_trajectories(combined_dict)
00195 
00196     cd = combined_dict
00197     ft_mat = np.matrix(cd['ft_list']).T
00198     force_mat = ft_mat[0:3, :]
00199     mpu.plot_yx(ut.norm(force_mat).A1, cd['time_list'])
00200     mpu.show()
00201 
00202 #    plot_forces(combined_dict)
00203 #    if savefig:
00204 #        d3m.savefig(opt.dir+'/trajectory_check.png', size=(900, 800))
00205 #    else:
00206 #        d3m.show()
00207 
00208 
00209 ##
00210 # @param pts - 3xN np matrix
00211 def project_points_plane(pts):
00212 #    import mdp
00213 #    p = mdp.nodes.PCANode(svd=True)
00214 #    p.train((pts-np.mean(pts, 1)).T.A)
00215 #    print 'min mdp:', p.get_projmatrix()
00216 #
00217 #    U, s , _ = np.linalg.svd(np.cov(pts))
00218 #    print 'min svd:', U[:,2]
00219     eval, evec = np.linalg.eig(np.cov(pts))
00220     min_idx = np.argmin(eval)
00221     min_evec = np.matrix(evec[:,min_idx]).T
00222     if min_evec[1,0] > 0:
00223         min_evec = min_evec * -1
00224     print 'min evec:', min_evec
00225     pts_proj = pts - np.multiply((min_evec.T * pts), min_evec)
00226     return pts_proj, min_evec
00227 
00228 ##
00229 # use method 1 to compute the mechanism angle from combined dict.
00230 # method 1 - angle between the x axis of checkerboard coord frame.
00231 # @return list of mechanism angles.
00232 def compute_mech_angle_1(cd, axis_direc=None):
00233     mech_rot = cd['mech_rot_list']
00234     directions_x = (np.row_stack(mech_rot)[:,0]).T.reshape(len(mech_rot), 3).T
00235     if axis_direc != None:
00236         directions_x = directions_x - np.multiply(axis_direc.T * directions_x,
00237                                                   axis_direc)
00238         directions_x = directions_x/ut.norm(directions_x)
00239     start_normal = directions_x[:,0]
00240     mech_angle = np.arccos(start_normal.T * directions_x).A1.tolist()
00241     return mech_angle
00242 
00243 ##
00244 # use method 2 to compute the mechanism angle from combined dict.
00245 # method 2 - fit a circle to estimate location of axis of rotation and
00246 # radius. Then use that to compute the angle of the mechanism.
00247 # @return list of mechanism angles.
00248 def compute_mech_angle_2(cd, tup, project_plane=False):
00249     pos_mat = np.column_stack(cd['mech_pos_list'])
00250     if project_plane:
00251         pts_proj, min_evec = project_points_plane(pos_arr)
00252         pos_arr = pts_proj
00253 
00254     kin_dict = ke.fit(pos_mat, tup)
00255     center = np.matrix((kin_dict['cx'], kin_dict['cy'],
00256                        kin_dict['cz'])).T
00257     directions_x = pos_mat - center
00258     directions_x = directions_x / ut.norm(directions_x)
00259     start_normal = directions_x[:,0]
00260     #mech_angle = np.arccos(start_normal.T * directions_x).A1.tolist()
00261 
00262     ct = (start_normal.T * directions_x).A1
00263     st = ut.norm(np.matrix(np.cross(start_normal.A1, directions_x.T.A)).T).A1
00264     mech_angle = np.arctan2(st, ct).tolist()
00265     return mech_angle
00266 
00267 def compute_mech_rot_list(cd, tup, project_plane=False):
00268     pos_arr = np.column_stack(cd['mech_pos_list'])
00269     rot_list = cd['mech_rot_list']
00270     directions_y = (np.row_stack(rot_list)[:,1]).T.reshape(len(rot_list), 3).T
00271     start_y = directions_y[:,0]
00272 
00273     pts_proj, y_mech = project_points_plane(pos_arr)
00274     if np.dot(y_mech.T, start_y.A1) < 0:
00275         print 'Negative hai boss'
00276         y_mech = -1 * y_mech
00277 
00278     if project_plane:
00279         pos_arr = pts_proj
00280 
00281     kin_dict = ke.fit(np.matrix(pos_arr), tup)
00282     center = np.array((kin_dict['cx'], kin_dict['cy'],
00283                        kin_dict['cz'])).T
00284 
00285     print 'pos_arr[:,0]', pos_arr[:,0]
00286     print 'center:', center
00287 
00288     directions_x = (np.row_stack(rot_list)[:,0]).T.reshape(len(rot_list), 3).T
00289     start_x = directions_x[:,0]
00290     directions_x = np.matrix(pos_arr) - np.matrix(center).T.A
00291     directions_x = directions_x / ut.norm(directions_x)
00292     if np.dot(directions_x[:,0].A1, start_x.A1) < 0:
00293         print 'Negative hai boss'
00294         directions_x = -1 * directions_x
00295 
00296     mech_rot_list = []
00297     for i in range(len(rot_list)):
00298         x = -directions_x[:, i]
00299         y = np.matrix(y_mech)
00300         z = np.matrix(np.cross(x.A1, y.A1)).T
00301         rot_mat = np.column_stack((x, y, z))
00302         mech_rot_list.append(rot_mat)
00303 
00304 #    return mech_rot_list
00305     return rot_list
00306 
00307 ##
00308 # @param tup - if None then use method 1 else use method 2 to
00309 # compute mech angle.
00310 # @return 1d array (radial force), 1d array (tangential force), list of mechanism angles, type ('rotary' or 'prismatic')
00311 def compute_mechanism_properties(combined_dict, bias_ft = False,
00312                                  tup = None, cd_pkl_name = None):
00313     cd = combined_dict
00314     force_cam, moment_cam, _ = fts_to_camera(combined_dict)
00315     moment_contact_l = None
00316     if bias_ft:
00317         print 'Biasing magnitude:', np.linalg.norm(force_cam[:,0])
00318         force_cam = force_cam - force_cam[:,0]
00319         moment_cam = moment_cam - moment_cam[:,0]
00320 
00321     if cd['radius'] != -1:
00322         if tup == None:
00323             mech_angle = compute_mech_angle_1(cd)
00324         else:
00325             mech_angle = compute_mech_angle_2(cd, tup)
00326             # compute new mech_rot_list. used for factoring the forces.
00327             mech_rot_list = compute_mech_rot_list(combined_dict, tup)
00328             combined_dict['mech_rot_list'] = mech_rot_list
00329 
00330             hook_tip_l = compute_hook_tip_trajectory(cd)
00331             hand_mat = np.column_stack(hook_tip_l)
00332             for i,f in enumerate(force_cam.T):
00333                 fmag = np.linalg.norm(f)
00334                 if fmag > 1.0:
00335                     break
00336             end_idx = np.argmax(mech_angle)
00337             hand_mat_short = hand_mat[:,i:end_idx]
00338 
00339             kin_dict = ke.fit(hand_mat_short, tup)
00340             center_hand = np.matrix((kin_dict['cx'], kin_dict['cy'],
00341                                      kin_dict['cz'])).T
00342             radius_hand = kin_dict['r']
00343             
00344             center_mech_coord = mech_rot_list[0].T * center_hand
00345             start_mech_coord = mech_rot_list[0].T * hand_mat_short[:,0]
00346             opens_right = False
00347             if start_mech_coord[0,0] < center_mech_coord[0,0]:
00348                 print 'Opens Right'
00349                 opens_right = True
00350 
00351             # radial vectors.
00352             radial_mat = hand_mat - center_hand
00353             radial_mat = radial_mat / ut.norm(radial_mat)
00354             _, nrm_hand = project_points_plane(hand_mat_short)
00355             if np.dot(nrm_hand.A1, mech_rot_list[0][:,1].A1) < 0:
00356                 nrm_hand = -1 * nrm_hand
00357             if opens_right == False:
00358                 nrm_hand = -1 * nrm_hand
00359 
00360             frad_l = []
00361             ftan_l = []
00362             moment_contact_l = []
00363             for i, f in enumerate(force_cam.T):
00364                 f = f.T
00365                 m = (moment_cam[:,i].T * nrm_hand)[0,0]
00366                 moment_contact_l.append(m)
00367                 tvec = np.matrix(np.cross(nrm_hand.A1, radial_mat[:,i].A1)).T
00368                 ftan = (f.T * tvec)[0,0]
00369                 ftan_l.append(ftan)
00370 
00371                 frad = np.linalg.norm(f - tvec * ftan)
00372                 #frad = (f_cam.T*radial_mat[:,i])[0,0]
00373                 frad_l.append(abs(frad))
00374 
00375         typ = 'rotary'
00376 
00377     else:
00378         pos_mat = np.column_stack(cd['mech_pos_list'])
00379         mech_angle = ut.norm(pos_mat-pos_mat[:,0]).A1.tolist()
00380         #print 'mech_angle:', mech_angle
00381         typ = 'prismatic'
00382         moment_axis_list = None
00383 
00384         frad_l = []
00385         ftan_l = []
00386         moment_contact_l = []
00387         rot_list = cd['mech_rot_list']
00388         directions_z = (np.row_stack(rot_list)[:,2]).T.reshape(len(rot_list), 3).T
00389         for i, f in enumerate(force_cam.T):
00390             f = f.T
00391             tvec = np.matrix(directions_z[:,i])
00392             ftan = (f.T * tvec)[0,0]
00393             ftan_l.append(ftan)
00394 
00395             frad = np.linalg.norm(f - tvec * ftan)
00396             #frad = (f_cam.T*radial_mat[:,i])[0,0]
00397             frad_l.append(abs(frad))
00398             radius_hand = 10.
00399 
00400     ut.save_pickle(combined_dict, cd_pkl_name)
00401 
00402     return np.array(frad_l), np.array(ftan_l), mech_angle, typ, \
00403            np.array(ftan_l)*radius_hand, np.array(moment_contact_l)
00404 
00405 def plot_radial_tangential(mech_dict, savefig, fig_name=''):
00406     radial_mech = mech_dict['force_rad_list']
00407     tangential_mech = mech_dict['force_tan_list']
00408     typ = mech_dict['mech_type']
00409     mech_x = mech_dict['mechanism_x']
00410     if typ == 'rotary':
00411         mech_x_degrees = np.degrees(mech_x)
00412         xlabel = 'angle (degrees)'
00413     else:
00414         mech_x_degrees = mech_x
00415         xlabel = 'distance (meters)'
00416     mpu.pl.clf()
00417     mpu.plot_yx(radial_mech, mech_x_degrees, axis=None, label='radial force',
00418                 xlabel=xlabel, ylabel='N', color='r')
00419     mpu.plot_yx(tangential_mech, mech_x_degrees, axis=None, label='tangential force',
00420                 xlabel=xlabel, ylabel='N', color='g')
00421     mpu.legend()
00422     
00423     if typ == 'rotary':
00424         mpu.figure()
00425         rad = mech_dict['radius']
00426         torques_1 = rad * np.array(tangential_mech)
00427         torques_3 = np.array(mech_dict['moment_tip_list']) + torques_1
00428         mpu.plot_yx(torques_1, mech_x_degrees, axis=None,
00429                     label='torque from tangential',
00430                     xlabel=xlabel, ylabel='moment', color='r')
00431         mpu.plot_yx(torques_3, mech_x_degrees, axis=None,
00432                     label='total torque',
00433                     xlabel=xlabel, ylabel='moment', color='y')
00434         mpu.legend()
00435 
00436 
00437     if savefig:
00438         mpu.savefig(opt.dir+'/%s_force_components.png'%fig_name)
00439     else:
00440         mpu.show()
00441 
00442 ##
00443 # returns force and moment at the tip of the hook in camera
00444 # coordinates.
00445 def fts_to_camera(combined_dict):
00446     cd = combined_dict
00447     number = cd['hook_checker_number']
00448     hand_rot = cd['hand_rot_list']
00449     hand_pos = cd['hand_pos_list']
00450     ft_mat = np.matrix(cd['ft_list']).T # 6xN np matrix
00451     force_mat = ft_mat[0:3, :]
00452     moment_mat = ft_mat[3:6, :]
00453 
00454     n_forces = force_mat.shape[1]
00455     force_cam_list = []
00456     moment_cam_list = []
00457     moment_base_list = []
00458     for i in range(n_forces):
00459         f,m,m_base = ft_to_camera_3(force_mat[:,i], moment_mat[:,i], hand_rot[i],
00460                                     number, return_moment_cam = True)
00461         force_cam_list.append(f)
00462         moment_cam_list.append(m)
00463         moment_base_list.append(m_base)
00464     force_cam = np.column_stack(force_cam_list)
00465     moment_cam = np.column_stack(moment_cam_list)
00466     moment_base = np.column_stack(moment_base_list)
00467     return force_cam, moment_cam, moment_base
00468 
00469 def plot_forces(combined_dict):
00470     cd = combined_dict
00471     hand_mat = np.column_stack(cd['hand_pos_list'])
00472     hand_rot = cd['hand_rot_list']
00473 
00474     mech_mat = np.column_stack(cd['mech_pos_list'])
00475     mech_rot = cd['mech_rot_list']
00476     directions_x = (np.row_stack(mech_rot)[:,0]).T.reshape(len(mech_rot), 3).T
00477     force_cam, moment_cam, _ = fts_to_camera(combined_dict)
00478 
00479     d3m.plot_points(hand_mat, color = (1.,0.,0.), mode='sphere')
00480     d3m.plot_points(mech_mat, color = (0.,0.,1.), mode='sphere')
00481     d3m.plot_normals(mech_mat, directions_x, color=(1.,0,0.))
00482 #    d3m.plot_normals(mech_mat, force_mat, color=(0.,1,0.))
00483     d3m.plot_normals(mech_mat, force_cam, color=(0.,0,1.))
00484 
00485 def plot_trajectories(combined_dict):
00486     cd = combined_dict
00487     hand_mat = np.column_stack(cd['hand_pos_list'])
00488     hand_rot = cd['hand_rot_list']
00489     directions_x = (np.row_stack(hand_rot)[:,0]).T.reshape(len(hand_rot), 3).T
00490     directions_y = (np.row_stack(hand_rot)[:,1]).T.reshape(len(hand_rot), 3).T
00491     directions_z = (np.row_stack(hand_rot)[:,2]).T.reshape(len(hand_rot), 3).T
00492 
00493     #d3m.white_bg()
00494 
00495     d3m.plot_points(hand_mat, color = (1.,0.,0.), mode='sphere',
00496                     scale_factor = 0.005)
00497     d3m.plot_normals(hand_mat, directions_x, color=(1.,0,0.),
00498                      scale_factor = 0.02)
00499     d3m.plot_normals(hand_mat, directions_y, color=(0.,1,0.),
00500                      scale_factor = 0.02)
00501     d3m.plot_normals(hand_mat, directions_z, color=(0.,0,1.),
00502                      scale_factor = 0.02)
00503 
00504     mech_mat = np.column_stack(cd['mech_pos_list'])
00505     mech_rot = cd['mech_rot_list']
00506     directions_x = (np.row_stack(mech_rot)[:,0]).T.reshape(len(mech_rot), 3).T
00507     directions_y = (np.row_stack(mech_rot)[:,1]).T.reshape(len(hand_rot), 3).T
00508     directions_z = (np.row_stack(mech_rot)[:,2]).T.reshape(len(mech_rot), 3).T
00509 
00510     d3m.plot_points(mech_mat[:,0:1], color = (0.,0.,0.), mode='sphere',
00511                     scale_factor = 0.01)
00512     d3m.plot_points(mech_mat, color = (0.,0.,1.), mode='sphere',
00513                     scale_factor = 0.005)
00514     d3m.plot_normals(mech_mat, directions_x, color=(1.,0,0.),
00515                      scale_factor = 0.02)
00516     d3m.plot_normals(mech_mat, directions_y, color=(0.,1,0.),
00517                      scale_factor = 0.02)
00518     d3m.plot_normals(mech_mat, directions_z, color=(0.,0,1.),
00519                      scale_factor = 0.02)
00520 
00521     m = np.mean(mech_mat, 1)
00522     d3m.mlab.view(azimuth=-120, elevation=60, distance=1.60,
00523                   focalpoint=(m[0,0], m[1,0], m[2,0]))
00524 
00525 ##
00526 # @return list of hook tip coodinates in camera coordinate frame.
00527 def compute_hook_tip_trajectory(combined_dict):
00528     cd = combined_dict
00529     hand_mat = np.column_stack(cd['hand_pos_list'])
00530     hand_rot_l = cd['hand_rot_list']
00531     directions_x = (np.row_stack(hand_rot_l)[:,0]).T.reshape(len(hand_rot_l), 3).T
00532     directions_y = (np.row_stack(hand_rot_l)[:,1]).T.reshape(len(hand_rot_l), 3).T
00533     directions_z = (np.row_stack(hand_rot_l)[:,2]).T.reshape(len(hand_rot_l), 3).T
00534 
00535     hand_pos_list = cd['hand_pos_list']
00536     hook_tip_l = []
00537     for i,p in enumerate(hand_pos_list): # p - hook checker origin in camera coordinates.
00538         hc_P_hc_hooktip = np.matrix([0.035, -0.0864, 0.09]).T # vector from hook checkerboard origin to the tip of the hook in hook checker coordinates.
00539         cam_P_hc_hooktip = hand_rot_l[i] * hc_P_hc_hooktip
00540         hook_tip_l.append(p + cam_P_hc_hooktip)
00541     
00542     return hook_tip_l
00543 
00544 ##
00545 # take the open + close trajectory and split it into two separate
00546 # trajectories and save them as pkls.
00547 def split_open_close(rad, tan, ang, typ, mech_radius, time_list,
00548                      moment_axis, moment_tip):
00549     ang = np.array(ang)
00550     incr = ang[1:] - ang[:-1]
00551     n_pts = ang.shape[0] - 2 #ignoring first and last readings.
00552     rad_l, tan_l, ang_l = [], [], []
00553     for i in range(n_pts):
00554         if typ == 'rotary':
00555             sgn = incr[i] * incr[i+1]
00556             mag = abs(incr[i] - incr[i+1])
00557             if sgn < 0 and mag > math.radians(10):
00558                 continue
00559             rad_l.append(rad[i+1])
00560             tan_l.append(tan[i+1])
00561             ang_l.append(ang[i+1])
00562         else:
00563             # no cleanup for prismatic joints, for now
00564             rad_l.append(rad[i+1])
00565             tan_l.append(tan[i+1])
00566             ang_l.append(ang[i+1])
00567 
00568     rad, tan, ang = rad_l, tan_l, ang_l
00569     max_idx = np.argmax(ang)
00570     d_open = {'force_rad_list': rad[:max_idx+1],
00571               'force_tan_list': tan[:max_idx+1],
00572               'mechanism_x': ang[:max_idx+1], 'mech_type': typ,
00573               'radius': mech_radius,
00574               'time_list': time_list[:max_idx+1]}
00575     if moment_tip != None:
00576         d_open['moment_tip_list'] = moment_tip[:max_idx+1]
00577         d_open['moment_list'] = moment_axis[:max_idx+1]
00578     ut.save_pickle(d_open, opt.dir + '/open_mechanism_trajectories_handhook.pkl')
00579 
00580     d_close = {'force_rad_list': rad[max_idx+1:],
00581                'force_tan_list': tan[max_idx+1:],
00582                'mechanism_x': ang[max_idx+1:], 'mech_type': typ,
00583                'radius': mech_radius,
00584                'time_list': time_list[max_idx+1:]}
00585     if moment_tip != None:
00586         d_open['moment_tip_list'] = moment_tip[max_idx+1:]
00587         d_open['moment_list'] = moment_axis[max_idx+1:]
00588     ut.save_pickle(d_close, opt.dir + '/close_mechanism_trajectories_handhook.pkl')
00589 
00590 
00591 def plot_hooktip_trajectory_and_force(cd):
00592     hook_tip_l = compute_hook_tip_trajectory(cd)
00593 
00594     # plot trajectory in 3D.
00595     d3m.white_bg()
00596     d3m.plot_points(np.column_stack(hook_tip_l), color = (1.,0.,0.), mode='sphere',
00597                     scale_factor = 0.005)
00598 #    d3m.plot_points(mech_proj[:,0:1], color = (0.,0.,0.), mode='sphere',
00599 #                    scale_factor = 0.01)
00600 #    d3m.plot_points(mech_proj, color = (0.,0.,1.), mode='sphere',
00601 #                    scale_factor = 0.005)
00602 #    d3m.plot(np.column_stack((mech_proj[:,-1],center_mech, mech_proj[:,0])),
00603 #             color = (0.,0.,1.))
00604 #    d3m.plot(np.column_stack((hand_proj[:,-1],center_hand, hand_proj[:,0])),
00605 #             color = (1.,0.,0.))
00606     d3m.show()
00607 
00608 ##
00609 # sanity check - fitting circle to mechanism and hook tip
00610 # trajectories, computing the angle between the initial radial
00611 # direction of the mechanism and the radial directions for the hook
00612 # tip. This angle starts out at a slightly positive angle. I'm
00613 # assuming that this corresponds to the fact that the handle sticks
00614 # out from the cabinet door. What makes me nervous is that I am still
00615 # fitting two different circles to the mechanism and hook
00616 # trajectories.
00617 def compare_tip_mechanism_trajectories(mech_mat, hand_mat):
00618 #    hand_proj, nrm_hand = project_points_plane(hand_mat)
00619 #    mech_proj, nrm_mech = project_points_plane(mech_mat)
00620     hand_proj = hand_mat
00621     mech_proj = mech_mat
00622 
00623     kin_dict = ke.fit(hand_proj, tup)
00624     print 'kin_dict from hook tip:', kin_dict
00625     print 'measured radius:', cd['radius']
00626     center_hand = np.matrix((kin_dict['cx'], kin_dict['cy'],
00627                              kin_dict['cz'])).T
00628 
00629     kin_dict = ke.fit(mech_proj, tup)
00630     print 'kin_dict from mechanism:', kin_dict
00631     center_mech = np.matrix((kin_dict['cx'], kin_dict['cy'],
00632                              kin_dict['cz'])).T
00633 
00634     # working with the projected coordinates.
00635     directions_hand = hand_proj - center_hand
00636     directions_hand = directions_hand / ut.norm(directions_hand)
00637     directions_mech = mech_proj - center_mech
00638     directions_mech = directions_mech / ut.norm(directions_mech)
00639 
00640     start_normal = directions_mech[:,0]
00641     print 'directions_mech[:,0]', directions_mech[:,0].A1
00642     print 'directions_hand[:,0]', directions_hand[:,0].A1
00643     ct = (start_normal.T * directions_hand).A1
00644     st = ut.norm(np.matrix(np.cross(start_normal.A1, directions_hand.T.A)).T).A1
00645     
00646     mech_angle = np.arctan2(st, ct).tolist()
00647     #mech_angle = np.arccos(start_normal.T * directions_hand).A1.tolist()
00648 
00649     mpu.plot_yx(np.degrees(mech_angle))
00650     mpu.show()
00651 
00652     # plot trajectory in 3D.
00653     d3m.white_bg()
00654     d3m.plot_points(hand_proj, color = (1.,0.,0.), mode='sphere',
00655                     scale_factor = 0.005)
00656     d3m.plot_points(mech_proj[:,0:1], color = (0.,0.,0.), mode='sphere',
00657                     scale_factor = 0.01)
00658     d3m.plot_points(mech_proj, color = (0.,0.,1.), mode='sphere',
00659                     scale_factor = 0.005)
00660     d3m.plot(np.column_stack((mech_proj[:,-1],center_mech, mech_proj[:,0])),
00661              color = (0.,0.,1.))
00662     d3m.plot(np.column_stack((hand_proj[:,-1],center_hand, hand_proj[:,0])),
00663              color = (1.,0.,0.))
00664     d3m.show()
00665 
00666 def angle_between_hooktip_mechanism_radial_vectors(mech_mat, hand_mat):
00667     kin_dict = ke.fit(hand_mat, tup)
00668     print 'kin_dict from hook tip:', kin_dict
00669     print 'measured radius:', cd['radius']
00670     center_hand = np.matrix((kin_dict['cx'], kin_dict['cy'],
00671                              kin_dict['cz'])).T
00672 
00673     kin_dict = ke.fit(mech_mat, tup)
00674     print 'kin_dict from mechanism:', kin_dict
00675     center_mech = np.matrix((kin_dict['cx'], kin_dict['cy'],
00676                              kin_dict['cz'])).T
00677 
00678     # working with the projected coordinates.
00679     directions_hand = hand_mat - center_hand
00680     directions_hand = directions_hand / ut.norm(directions_hand)
00681     directions_mech = mech_mat - center_mech
00682     directions_mech = directions_mech / ut.norm(directions_mech)
00683 
00684     #import pdb; pdb.set_trace()
00685     ang = np.degrees(np.arccos(np.sum(np.multiply(directions_mech, directions_hand), 0))).A1
00686     mpu.plot_yx(ang, label = 'angle between hooktip-radial and mechanism radial')
00687     mpu.legend()
00688     mpu.show()
00689 
00690 def split_forces_hooktip_test(hand_mat):
00691     kin_dict = ke.fit(hand_mat, tup)
00692     center_hand = np.matrix((kin_dict['cx'], kin_dict['cy'],
00693                              kin_dict['cz'])).T
00694 
00695     print 'kin_dict:', kin_dict
00696     # radial vectors.
00697     radial_mat = hand_mat - center_hand
00698     radial_mat = radial_mat / ut.norm(radial_mat)
00699 
00700     # cannot use hook tip to compute mechanism angle because I
00701     # don't have a way of knowing when the hook starts opening the
00702     # mechanism. (Think hook makes contact with door, moves in
00703     # freespace and then makes contact with the handle.)
00704     #start_rad = radial_mat[:,0]
00705     #ct = (start_rad.T * radial_mat).A1
00706     #st = ut.norm(np.matrix(np.cross(start_rad.A1, radial_mat.T.A)).T).A1
00707     #mech_angle_l = np.arctan2(st, ct).tolist()
00708 
00709     _, nrm_hand = project_points_plane(hand_mat)
00710     print 'nrm_hand:', nrm_hand.A1
00711 
00712     f_cam_l = []
00713     m_cam_l = []
00714     m_base_l = []
00715     frad_l = []
00716     ftan_l = []
00717     hook_num = cd['hook_checker_number']
00718     print 'hook_num:', hook_num
00719     for i, f in enumerate(force_mat.T):
00720         f = f.T
00721         m = moment_mat[:,i]
00722         f_cam, m_cam, m_base = ft_to_camera_3(f, m, hook_rot_l[i], hook_num,
00723                                               return_moment_cam = True)
00724         f_cam_l.append(f_cam)
00725         m_cam_l.append(abs((m_cam.T*nrm_hand)[0,0]))
00726         m_base_l.append(abs((m_base.T*nrm_hand)[0,0]))
00727         #m_base_l.append(np.linalg.norm(f))
00728 
00729         tangential_vec = np.matrix(np.cross(radial_mat[:,i].A1, nrm_hand.A1)).T
00730         ftan = (f_cam.T * tangential_vec)[0,0]
00731         ftan_l.append(ftan)
00732 
00733         #frad = np.linalg.norm(f_cam - tangential_vec * ftan)
00734         frad = (f_cam.T*radial_mat[:,i])[0,0]
00735         frad_l.append(abs(frad))
00736 
00737 
00738     fig1 = mpu.figure()
00739     mech_ang_deg = np.degrees(mech_angle_l)
00740     mpu.plot_yx(ftan_l, mech_ang_deg, label='Tangential Force (hook tip)', color='b')
00741     mpu.plot_yx(frad_l, mech_ang_deg, label='Radial Force (hook tip)', color='y')
00742 
00743     mech_pkl_name = glob.glob(opt.dir + '/open_mechanism_trajectories_*.pkl')[0]
00744     md = ut.load_pickle(mech_pkl_name)
00745     radial_mech = md['force_rad_list']
00746     tangential_mech = md['force_tan_list']
00747     mech_x = np.degrees(md['mechanism_x'])
00748     mpu.plot_yx(tangential_mech, mech_x, label='Tangential Force (mechanism checker)', color='g')
00749     mpu.plot_yx(radial_mech, mech_x, label='Radial Force (mechanism checker)', color='r')
00750 
00751 
00752     mpu.legend()
00753 
00754     fig2 = mpu.figure()
00755     mpu.plot_yx(m_cam_l, mech_ang_deg, label='\huge{$m_{axis}$}')
00756     mpu.plot_yx(m_base_l, mech_ang_deg, label='\huge{$m^s$}',
00757                 color = 'r')
00758 
00759     mpu.legend()
00760     mpu.show()
00761 
00762 
00763 if __name__ == '__main__':
00764 
00765     import optparse
00766     p = optparse.OptionParser()
00767     p.add_option('-d', '--dir', action='store', default='',
00768                  type='string', dest='dir', help='directory with logged data')
00769     p.add_option('-t', '--time_check', action='store_true', dest='tc',
00770                  help='plot to check the consistency of time stamps')
00771     p.add_option('-s', '--sync', action='store_true', dest='sync',
00772                  help='time synchronize poses, forces etc.')
00773     p.add_option('--split', action='store_true', dest='split_forces',
00774                  help='split forces into radial and tangential and save in a pickle')
00775     p.add_option('--savefig', action='store_true', dest='savefig',
00776                  help='save the plot instead of showing it.')
00777     p.add_option('-c', '--cd', action='store_true', dest='cd',
00778                  help='work with the combined dict')
00779     p.add_option('-f', '--force', action='store_true', dest='force',
00780                  help='plot radial and tangential force')
00781     p.add_option('--mech_prop_ros', action='store_true',
00782                  dest='mech_prop_ros',
00783                  help='plot radial and tangential force')
00784     p.add_option('--moment_test', action='store_true',
00785                  dest='moment_test',
00786                  help='trying to compute moment about the joint axis.')
00787     p.add_option('--hook_tip_test', action='store_true',
00788                  dest='hook_tip_test',
00789                  help='plot trajectory of hook tip for debugging etc.')
00790 
00791     opt, args = p.parse_args()
00792 
00793     if opt.dir == '':
00794         raise RuntimeError('Need a directory to work with (-d or --dir)')
00795 
00796     if opt.force:
00797         mech_pkl_name = glob.glob(opt.dir + '/open_mechanism_trajectories_*.pkl')[0]
00798         md = ut.load_pickle(mech_pkl_name)
00799         plot_radial_tangential(md, opt.savefig, 'open')
00800 #        mech_pkl_name = glob.glob(opt.dir + '/close_mechanism_trajectories_handhook.pkl')[0]
00801 #        md = ut.load_pickle(mech_pkl_name)
00802 #        plot_radial_tangential(md, opt.savefig, 'close')
00803 
00804     ft_pkl = glob.glob(opt.dir + '/ft_log*.pkl')[0]
00805     poses_pkl = glob.glob(opt.dir + '/poses_dict*.pkl')[0]
00806     
00807     ft_dict = ut.load_pickle(ft_pkl)
00808     poses_dict = ut.load_pickle(poses_pkl)
00809     mechanism_dict = poses_dict['mechanism']
00810     hand_dict = poses_dict['hand']
00811 
00812 
00813     ft_time_list = ft_dict['time_list']
00814     mechanism_time_list = mechanism_dict['time_list']
00815     hand_time_list = hand_dict['time_list']
00816 
00817     if opt.tc:
00818         check_time_sync(ft_time_list, mechanism_time_list, hand_time_list)
00819         if opt.savefig:
00820             mpu.savefig(opt.dir+'/time_check.png')
00821         else:
00822             mpu.show()
00823 
00824     if opt.sync:
00825         print 'Begin synchronize'
00826         d = synchronize(ft_dict, mechanism_dict, hand_dict)
00827         print 'End synchronize'
00828         #ut.save_pickle(d, opt.dir+'/combined_log'+ut.formatted_time()+'.pkl')
00829         ut.save_pickle(d, opt.dir+'/combined_log.pkl')
00830         print 'Saved pickle'
00831 
00832     if opt.cd:
00833         cd = ut.load_pickle(glob.glob(opt.dir + '/combined_log*.pkl')[0])
00834         plot(cd, opt.savefig)
00835 
00836     if opt.mech_prop_ros:
00837         import mechanism_analyse as ma
00838 
00839         cd = ut.load_pickle(glob.glob(opt.dir + '/combined_log*.pkl')[0])
00840 
00841         tup = ke.init_ros_node()
00842 
00843         ma2 = compute_mech_angle_2(cd, tup, project_plane=False)
00844         ma1 = compute_mech_angle_1(cd)
00845         ma3 = compute_mech_angle_2(cd, tup, project_plane=True)
00846 #        ma4 = compute_mech_angle_1(cd, min_evec)
00847 
00848 
00849         lab1 = 'orientation only'
00850         lab2 = 'checker origin position + circle fit'
00851         lab3 = 'checker origin position + PCA projection + circle fit'
00852 #        lab4 = 'PCA projection + orientation'
00853 
00854         mpu.figure()
00855         mpu.plot_yx(np.degrees(ma3), color='r', label=lab3,
00856                     linewidth = 1, scatter_size = 5)
00857         mpu.plot_yx(np.degrees(ma2), color='b', label=lab2,
00858                     linewidth = 1, scatter_size = 5)
00859         mpu.plot_yx(np.degrees(ma1), color='y', label=lab1,
00860                     linewidth = 1, scatter_size = 5)
00861         mpu.legend()
00862 
00863         vel3 = ma.compute_velocity(ma3, cd['time_list'], 1)
00864         vel2 = ma.compute_velocity(ma2, cd['time_list'], 1)
00865         vel1 = ma.compute_velocity(ma1, cd['time_list'], 1)
00866         mpu.figure()
00867         mpu.plot_yx(np.degrees(vel3), np.degrees(ma3), color='r',
00868                     label=lab3, linewidth = 1, scatter_size = 5)
00869         mpu.plot_yx(np.degrees(vel2), np.degrees(ma2), color='b',
00870                     label=lab2, linewidth = 1, scatter_size = 5)
00871         mpu.plot_yx(np.degrees(vel1), np.degrees(ma1), color='y',
00872                     label=lab1, linewidth = 1, scatter_size = 5)
00873         mpu.legend()
00874 
00875 #        acc3 = ma.compute_velocity(vel3, cd['time_list'], 1)
00876 #        mpu.figure()
00877 #        mpu.plot_yx(np.degrees(acc3), np.degrees(ma3), color='r',
00878 #                    label=lab3, linewidth = 1, scatter_size = 5)
00879 #        mpu.legend()
00880 
00881         mpu.show()
00882 
00883     if opt.split_forces:
00884         tup = ke.init_ros_node()
00885         pkl_name = glob.glob(opt.dir + '/combined_log*.pkl')[0]
00886         mech_pkl_name = glob.glob(opt.dir + '/mechanism_info*.pkl')[0]
00887 
00888         md = ut.load_pickle(mech_pkl_name)
00889         cd = ut.load_pickle(pkl_name)
00890         cd['hook_checker_number'] = md['checkerboard_number']
00891         cd['radius'] = md['radius']
00892         rad, tan, ang, typ, moment_axis, moment_tip = compute_mechanism_properties(cd,
00893                                                 bias_ft=True, tup=tup,
00894                                                 cd_pkl_name = pkl_name)
00895         split_open_close(rad, tan, ang, typ, md['radius'],
00896                          cd['time_list'], moment_axis, moment_tip)
00897 
00898     if opt.moment_test:
00899         tup = ke.init_ros_node()
00900         pkl_name = glob.glob(opt.dir + '/combined_log*.pkl')[0]
00901         mech_pkl_name = glob.glob(opt.dir + '/mechanism_info*.pkl')[0]
00902 
00903         md = ut.load_pickle(mech_pkl_name)
00904         cd = ut.load_pickle(pkl_name)
00905         cd['hook_checker_number'] = md['checkerboard_number']
00906         cd['radius'] = md['radius']
00907         rad, tan, ang, typ, moment_axis, moment_tip = compute_mechanism_properties(cd,
00908                                                 bias_ft=True, tup=tup,
00909                                                 cd_pkl_name = pkl_name)
00910         ang = np.array(ang)
00911         incr = ang[1:] - ang[:-1]
00912         n_pts = ang.shape[0] - 2 #ignoring first and last readings.
00913         rad_l, tan_l, ang_l = [], [], []
00914         for i in range(n_pts):
00915             if typ == 'rotary':
00916                 sgn = incr[i] * incr[i+1]
00917                 mag = abs(incr[i] - incr[i+1])
00918                 if sgn < 0 and mag > math.radians(10):
00919                     continue
00920                 rad_l.append(rad[i+1])
00921                 tan_l.append(tan[i+1])
00922                 ang_l.append(ang[i+1])
00923             else:
00924                 # no cleanup for prismatic joints, for now
00925                 rad_l.append(rad[i+1])
00926                 tan_l.append(tan[i+1])
00927                 ang_l.append(ang[i+1])
00928 
00929         rad, tan, ang = rad_l, tan_l, ang_l
00930         max_idx = np.argmax(ang)
00931         rad = np.array(rad[:max_idx+1])
00932         tan = np.array(tan[:max_idx+1])
00933         ang = np.array(ang[:max_idx+1])
00934         moment_axis = np.array(moment_axis[:max_idx+1])
00935         moment_tip = np.array(moment_tip[:max_idx+1])
00936 
00937         fig1 = mpu.figure()
00938         mpu.plot_yx(tan * cd['radius'], np.degrees(ang), label = 'Moment from Tangential Force',
00939                     color = 'b')
00940         mpu.plot_yx(moment_axis, np.degrees(ang), label = 'Computed Moment',
00941                     color = 'g')
00942         mpu.plot_yx(moment_tip, np.degrees(ang), label = 'Computed Moment using tip model',
00943                     color = 'y')
00944         mpu.legend()
00945         mpu.show()
00946 
00947     if opt.hook_tip_test:
00948         tup = ke.init_ros_node()
00949         pkl_name = glob.glob(opt.dir + '/combined_log*.pkl')[0]
00950         mech_pkl_name = glob.glob(opt.dir + '/mechanism_info*.pkl')[0]
00951 
00952         md = ut.load_pickle(mech_pkl_name)
00953         cd = ut.load_pickle(pkl_name)
00954         cd['hook_checker_number'] = md['checkerboard_number']
00955         cd['radius'] = md['radius']
00956         
00957         hook_tip_l = compute_hook_tip_trajectory(cd)
00958         hook_rot_l = cd['hand_rot_list']
00959         
00960         mech_mat = np.column_stack(cd['mech_pos_list'])
00961         hand_mat = np.column_stack(hook_tip_l)
00962         
00963         ft_mat = np.matrix(cd['ft_list']).T # 6xN np matrix
00964         force_mat = ft_mat[0:3, :]
00965 #        force_mat = force_mat - force_mat[:,0]
00966 
00967         moment_mat = ft_mat[3:6, :]
00968 #        moment_mat = moment_mat - moment_mat[:,0]
00969         
00970         for i,f in enumerate(force_mat.T):
00971             fmag = np.linalg.norm(f)
00972             if fmag > 1.0:
00973                 print 'i:', i
00974                 break
00975         
00976         mech_angle_l = compute_mech_angle_2(cd, tup, project_plane=False)
00977         end_idx = np.argmax(mech_angle_l)
00978         
00979         hand_mat = hand_mat[:,i:end_idx]
00980         mech_mat = mech_mat[:,i:end_idx]
00981         force_mat = force_mat[:,i:end_idx]
00982         moment_mat = moment_mat[:,i:end_idx]
00983         hook_rot_l = hook_rot_l[i:end_idx]
00984         mech_angle_l = mech_angle_l[i:end_idx]
00985 
00986         compare_tip_mechanism_trajectories(mech_mat[:,i:], hand_mat[:,i:])
00987         #angle_between_hooktip_mechanism_radial_vectors(mech_mat, hand_mat)
00988 
00989         #plot moment at hook ti and base.
00990         #split_forces_hooktip_test(hand_mat)
00991 
00992 
00993 
00994 


2010_biorob_everyday_mechanics
Author(s): Advait Jain, Hai Nguyen, Charles C. Kemp (Healthcare Robotics Lab, Georgia Tech)
autogenerated on Wed Nov 27 2013 11:58:43