Go to the documentation of this file.00001 import numpy as np
00002 from sklearn.decomposition import PCA
00003
00004 import rospy
00005 import rosbag
00006
00007 """
00008 This is the GraspSynergy class. It does the book-keeping of the low-dimensional
00009 grasp representations (the eigen-grasps).
00010
00011 For the ROS functionality, see synergy_node.py.
00012
00013 Author: Felix Duvallet <felixd@gmail.com>
00014 """
00015
00016
00017 class GraspSynergy(object):
00018 """
00019 A grasp synergy (aka eigengrasp) is a lower-dimensional representation of
00020 the hand configuration useful to simplify all manners of grasping things.
00021
00022 Essentially this is just a utilitarian wrapper around PCA.
00023 """
00024
00025 def __init__(self):
00026
00027 self._pca = PCA(n_components=None)
00028 self._N = 0
00029 self._D = 0
00030
00031
00032
00033
00034 self._transformed_joint_angles = None
00035
00036 @property
00037 def trained(self):
00038 """
00039 :return True if the synergy space has been computed.
00040 """
00041 return hasattr(self._pca, 'components_')
00042
00043 def fit_joint_values(self, joint_values):
00044 """
00045 Fit the principal components of the given joint values to compute the
00046 grasp synergies (aka eigengrasps).
00047
00048 We compute and store *all* D principal components here. The
00049 dimensionality of the lower-dimensional subspace is determined at grasp
00050 computation time.
00051
00052 :param joint_values A numpy array (N by D) with N datapoints of D
00053 dimensions.
00054
00055 :return True if the synergies were properly fit.
00056
00057 """
00058
00059 assert isinstance(joint_values, np.ndarray), 'Must have np.array().'
00060
00061 if joint_values.size <= 0:
00062 return False
00063
00064 self._N = joint_values.shape[0]
00065 self._D = joint_values.shape[1]
00066 self._transformed_joint_angles = self._pca.fit_transform(joint_values)
00067
00068 rospy.loginfo('Learned synergy space (from {}-dims) with {} points.'.
00069 format(self._D, self._N))
00070 rospy.loginfo(' Explained variance ratio: {}\b... ]'.format(
00071 self._pca.explained_variance_ratio_[0:4]))
00072
00073 return True
00074
00075 def fit_joint_state_messages(self, joint_state_messages):
00076 """
00077 Extract joint state values from a list of ROS messages, then
00078 compute the grasp synergies.
00079
00080 :param joint_state_messages A list of ROS sensor_msgs/JointState.
00081 """
00082
00083 joint_values = []
00084 for (idx, msg) in enumerate(joint_state_messages):
00085 try:
00086 joint_values.append(msg.position)
00087 except AttributeError:
00088 rospy.logwarn('Message is not a JointState, skipping.')
00089 pass
00090
00091 joint_values = np.array(joint_values)
00092
00093 return self.fit_joint_values(joint_values)
00094
00095 def fit_bag_file(self, bag_filepath):
00096 """
00097 Extract *all* joint state messages from the given bag file, then
00098 compute the hand synergies.
00099
00100 :param bag_filepath: Fully-qualified filepath of the bagfile.
00101 """
00102
00103 try:
00104 bag = rosbag.Bag(bag_filepath)
00105 except IOError:
00106 rospy.logerr('Could not open file: {}'.format(bag_filepath))
00107 return False
00108
00109
00110 joint_messages = []
00111 for (topic, msg, time) in bag.read_messages():
00112 if 'JointState' in str(type(msg)):
00113 joint_messages.append(msg)
00114
00115 return self.fit_joint_state_messages(joint_messages)
00116
00117 def compute_grasp(self, alphas):
00118 """
00119 Reconstruct a grasp given a combination of (low-dimensional) synergy
00120 coefficients to get a (full-dimensional) grasp configuration.
00121
00122 The coefficients are a weighting vector of the various (ordered)
00123 principal grasp components.
00124
00125 :return mean + sum_i alpha_i * coeff_i. If the synergy is not already
00126 computed this returns None.
00127
00128 """
00129
00130 if not hasattr(self._pca, 'components_'):
00131 rospy.logwarn('No grasp synergies, did you call fit_joint_*?')
00132 return None
00133
00134 num_components = len(self._pca.components_)
00135 num_alpha = len(alphas)
00136
00137 rospy.logdebug('Computing a grasp using {} principal components'.
00138 format(min(num_alpha, num_components)))
00139
00140
00141
00142
00143 ret = self._pca.mean_ + np.dot(alphas[0:num_components],
00144 self._pca.components_[0:num_alpha])
00145
00146 return ret
00147
00148 def synergy_range(self, component_num):
00149 """
00150 Compute the range of values for the i'th component of the synergy,
00151 using the transformed original values used to train the PCA.
00152
00153 If there are no synergies or the component number is invalid, returns
00154 (0, 0).
00155
00156 :return (min, max) for transformed values
00157 """
00158
00159 transformed_min, transformed_max = 0.0, 0.0
00160
00161
00162 if 0 <= component_num < self._D:
00163 transformed_min = \
00164 np.min(self._transformed_joint_angles, axis=0)[component_num]
00165 transformed_max = \
00166 np.max(self._transformed_joint_angles, axis=0)[component_num]
00167 return transformed_min, transformed_max