grasp_synergy.py
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         # Fit *all* components. Select lower-dimensional subspace later.
00027         self._pca = PCA(n_components=None)
00028         self._N = 0
00029         self._D = 0
00030 
00031         # Store the transformed joint angles for all principal components. This
00032         # is used to compute the min/max values of the transformed
00033         # representation.
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:  # Catch any exceptions due to a wrong message type.
00086                 joint_values.append(msg.position)
00087             except AttributeError:
00088                 rospy.logwarn('Message is not a JointState, skipping.')
00089                 pass
00090         # Create a numpy array of size NxD.
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         # Collect *all* messages of type JointState.
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         # Compute mean + dot<alphas, components>. Truncate both alphas and
00141         # components to ensure we have correct shapes. The resulting vector
00142         # is a D-dimensional vector.
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         # If there are no synergies, D = 0 so this does not get called.
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


grasp-synergy
Author(s): Felix Duvallet
autogenerated on Sat Jun 8 2019 20:11:26