haptic_mpc.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 #   Copyright 2013 Georgia Tech Research Corporation
00004 #
00005 #   Licensed under the Apache License, Version 2.0 (the "License");
00006 #   you may not use this file except in compliance with the License.
00007 #   You may obtain a copy of the License at
00008 #
00009 #     http://www.apache.org/licenses/LICENSE-2.0
00010 #
00011 #   Unless required by applicable law or agreed to in writing, software
00012 #   distributed under the License is distributed on an "AS IS" BASIS,
00013 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014 #   See the License for the specific language governing permissions and
00015 #   limitations under the License.
00016 #
00017 #  http://healthcare-robotics.com/
00018 
00019 ## @package hrl_haptic_mpc
00020 # @author Jeff Hawke 
00021 # @version 0.1
00022 # @copyright Apache 2.0
00023 
00024 import roslib
00025 roslib.load_manifest("hrl_haptic_mpc")
00026 import rospy
00027 import tf
00028 
00029 import hrl_lib.util as ut
00030 import hrl_lib.transforms as tr
00031 
00032 import epc_skin_math as esm # Core maths functions used by the MPC
00033 
00034 import hrl_haptic_manipulation_in_clutter_msgs.msg as haptic_msgs
00035 import hrl_haptic_manipulation_in_clutter_srvs.srv as haptic_srvs
00036 import geometry_msgs.msg
00037 import std_msgs.msg
00038 import hrl_msgs.msg
00039 
00040 # TODO - move to hrl_haptic_mpc package ie not just in src/
00041 import multiarray_to_matrix # Helper class with functions to convert a Float64MultiArray to a list of numpy matrices
00042 import haptic_mpc_util # Utility script with helper functions used across all haptic mpc nodes.
00043  
00044 import numpy as np
00045 import threading
00046 import copy
00047 import itertools as it
00048 import sys
00049 import math
00050 import signal
00051 
00052 ## Container class for the controller parameters. 
00053 #
00054 # Some of these are parameters rather than control data, but they're small cf. the data which changes at each timestep.
00055 class MPCData():
00056   def __init__(self, q, x_h, x_g, dist_g, goal_posture, 
00057            q_h_orient, q_g_orient, 
00058            position_weight, orient_weight, posture_weight, force_weight,
00059            force_reduction_goal,
00060            control_point_joint_num, 
00061            Kc_l, Rc_l, Jc_l, Je, 
00062            delta_f_min, delta_f_max, 
00063            phi_curr, K_j, 
00064            loc_l, n_l, f_l, f_n,
00065            jerk_opt_weight, max_force_mag,
00066            jep, time_step, stop):
00067   
00068     self.q = q            # Joint angles
00069     self.x_h = x_h        # end effector position
00070     self.x_g = x_g        # end effector goal
00071     self.dist_g = dist_g  # dist to goal
00072     self.goal_posture = goal_posture
00073     self.q_h_orient = q_h_orient # end effector orientation
00074     self.q_g_orient = q_g_orient
00075     self.position_weight = position_weight
00076     self.orient_weight = orient_weight
00077     self.posture_weight = posture_weight
00078     self.force_weight = force_weight
00079     self.force_reduction_goal = force_reduction_goal
00080     self.control_point_joint_num = control_point_joint_num
00081     self.Kc_l = Kc_l
00082     self.Rc_l = Rc_l
00083     self.Jc_l = Jc_l
00084     self.Je = Je
00085     self.delta_f_min = delta_f_min
00086     self.delta_f_max = delta_f_max
00087     self.phi_curr = phi_curr
00088     self.K_j = K_j
00089     self.loc_l = loc_l
00090     self.n_l = n_l 
00091     self.f_l = f_l
00092     self.f_n = f_n
00093     self.jerk_opt_weight = jerk_opt_weight
00094     self.max_force_mag = max_force_mag
00095     self.jep = jep
00096     self.time_step = time_step
00097     self.stop = stop
00098   
00099   ## String representation of the data structure. Useful for debugging.
00100   def __str__(self):
00101     string = ""
00102     string += "MPC Data Structure:"
00103     string += "\nq: \t\t%s" % str(self.q)
00104     string += "\nx_h: \t\t%s" % str(self.x_h)
00105     string += "\nx_g: \t\t%s" % str(self.x_g)
00106     string += "\ndist_g: \t\t%s" % str(self.dist_g)  # dist to goal
00107     string += "\nq_h_orient: \t\t%s" % str(self.q_h_orient) # end effector orientation
00108     string += "\nq_g_orient: \t\t%s" % str(self.q_g_orient)
00109     string += "\nposition_weight: \t\t%s" % str(self.position_weight)
00110     string += "\norient_weight: \t\t%s" % str(self.orient_weight)
00111     string += "\ncontrol_point_joint_num: \t\t%s" % str(self.control_point_joint_num)
00112     string += "\nKc_l: \t\t%s" % str(self.Kc_l)
00113     string += "\nRc_l: \t\t%s" % str(self.Rc_l)
00114     string += "\nJc_l: \t\t%s" % str(self.Jc_l)
00115     string += "\nJe: \t\t%s" % str(self.Je)
00116     string += "\ndelta_f_min: \t\t%s" % str(self.delta_f_min)
00117     string += "\ndelta_f_max: \t\t%s" % str(self.delta_f_max)
00118     string += "\nphi_curr: \t\t%s" % str(self.phi_curr)
00119     string += "\nK_j: \t\t%s" % str(self.K_j)
00120     string += "\nloc_l: \t\t%s" % str(self.loc_l)
00121     string += "\nn_l: \t\t%s" % str(self.n_l)
00122     string += "\nf_l: \t\t%s" % str(self.f_l)
00123     string += "\nf_n: \t\t%s" % str(self.f_n)
00124     string += "\njerk_opt_weight: \t\t%s" % str(self.jerk_opt_weight)
00125     string += "\nmax_force_mag: \t\t%s" % str(self.max_force_mag)
00126     string += "\njep: \t\t%s" % str(self.jep)
00127     string += "\ntime_step: \t\t%s" % str(self.time_step)
00128     string += "\nstop: \t\t%s" % str(self.stop)
00129     return string
00130     
00131 ## Haptic Model Predictive Controller class.
00132 class HapticMPC():
00133   ## Constructor
00134   # @param opt optparse options. Should be created using the helper utility script for reliability.
00135   # @param node_name Name used for the ROS node.
00136   def __init__(self, opt, node_name="haptic_mpc"):
00137     rospy.loginfo("Initialising Haptic MPC")
00138     self.opt = opt
00139 
00140     self.state_lock = threading.RLock() ## Haptic state lock
00141     self.goal_lock = threading.RLock() ## Goal state lock
00142     self.monitor_lock = threading.RLock() ## Monitor state lock
00143     self.gain_lock = threading.RLock() ## Controller gain state lock
00144     self.posture_lock = threading.RLock() ## Goal posture lock.
00145     self.mpc_data = None ## MPCData data structure. 
00146     self.msg = None ## Haptic state message
00147 
00148     # Haptic State parameters - from a RobotHapticState listener
00149     self.last_msg_time = None
00150     self.timeout = 0.50 # If the last time the MPC heard a state message was >50ms ago -> Stop!
00151     self.waiting_to_resume = False
00152     self.waiting_for_no_errors = False 
00153     self.mpc_state = None ## Current MPC state. Stored as a list of strings
00154     self.mpc_error = None ## Current MPC errors. Stored as a list of strings
00155     
00156     self.joint_names = []
00157     self.joint_angles = []
00158     self.desired_joint_angles = []
00159     self.joint_velocities = []
00160     self.joint_stiffness = []
00161     self.joint_damping = []
00162     
00163     self.end_effector_pos = None
00164     self.end_effector_orient_quat = None
00165     
00166     self.skin_data = []
00167     self.Jc = []
00168     self.Je = []
00169     
00170     self.time_step = 0.01 # seconds. NB: This is a default which is set by the "start" function.
00171     
00172     # Trajectory goal position - from a PoseStamped listener
00173     self.goal_pos = None
00174     self.goal_orient_quat = None
00175     self.goal_posture = None
00176     
00177     # Control parameters - read from parameter server
00178     self.orient_weight = 1.0
00179     self.pos_weight = 1.0
00180     self.posture_weight = 0.0
00181   
00182     self.position_step = 0.01 # metres
00183     self.orient_step = np.radians(0.4) # radians
00184     self.posture_step = np.radians(0.5) # radians
00185     
00186     self.deadzone_distance = 0.005 # 5mm 
00187     self.deadzone_angle = 10.0 # 10 degrees
00188     self.currently_in_deadzone = False
00189 
00190     self.mpc_enabled = True
00191     
00192     # Jacobian MultiArray to Matrix converter
00193     self.ma_to_m = multiarray_to_matrix.MultiArrayConverter()
00194     
00195   ## Read parameters from the ROS parameter server and store them.
00196   def initControlParametersFromServer(self):
00197     base_path = '/haptic_mpc'
00198     control_path = '/control_params'
00199     
00200     rospy.loginfo("HapticMPC: Initialising controller parameters from server. Path: %s" % (base_path+control_path))
00201     # controller parameters
00202     # Force limits for the controller.
00203     self.allowable_contact_force = rospy.get_param(base_path + control_path + '/allowable_contact_force') # Max force allowed by the controller
00204     self.max_delta_force_mag = rospy.get_param(base_path + control_path + '/max_delta_force_mag') # Max change in force allowed.
00205     self.stopping_force = rospy.get_param(base_path + control_path + '/stopping_force') # Completely shut down if this exceeded
00206     
00207     self.goal_velocity_for_hand = rospy.get_param(base_path + control_path + '/goal_velocity_for_hand')
00208     self.deadzone_distance = rospy.get_param(base_path + control_path + '/deadzone_distance')
00209     self.deadzone_angle = rospy.get_param(base_path + control_path + '/deadzone_angle')
00210     
00211     # stiffness parameters
00212     self.static_contact_stiffness_estimate = rospy.get_param(base_path + control_path + '/static_contact_stiffness_estimate')
00213     self.estimate_contact_stiffness = rospy.get_param(base_path + control_path + '/estimate_contact_stiffness')
00214     
00215     self.orient_weight = rospy.get_param(base_path + control_path + '/orientation_weight')
00216     self.pos_weight = rospy.get_param(base_path + control_path + '/position_weight')
00217     self.posture_weight = rospy.get_param(base_path + control_path + '/posture_weight')
00218     self.force_weight = rospy.get_param(base_path + control_path + '/force_reduction_weight')
00219     self.jerk_opt_weight = rospy.get_param(base_path + control_path + '/jerk_opt_weight')
00220     self.mpc_weights_pub.publish(std_msgs.msg.Header(), self.pos_weight, self.orient_weight, self.posture_weight)
00221     
00222     
00223     self.max_theta_step = rospy.get_param(base_path + control_path + '/posture_step_size')
00224     self.theta_step_scale = rospy.get_param(base_path + control_path + '/posture_step_scale')
00225     
00226     self.force_reduction_goal = rospy.get_param(base_path + control_path + '/force_reduction_goal')
00227 
00228     self.frequency = rospy.get_param(base_path + control_path + '/frequency')
00229   
00230   ## Initialise the robot joint limits.
00231   # This relies on something (usually the haptic state node) pushing the appropriate joint limits to the param server before execution.
00232   # NB: All joint limits are specified in degrees and converted to radians here (easier to understand)
00233   def initRobot(self):
00234     base_path = '/haptic_mpc'
00235     
00236     rospy.loginfo("Haptic MPC: Waiting for joint limit parameters to be set")
00237     while rospy.has_param(base_path + '/joint_limits/max') == False or rospy.has_param(base_path + '/joint_limits/min') == False:
00238       rospy.sleep(2.0)
00239     rospy.loginfo("Haptic MPC: Got joint limits, continuing")
00240     self.joint_limits_max = np.radians(rospy.get_param(base_path + '/joint_limits/max'))
00241     self.joint_limits_min = np.radians(rospy.get_param(base_path + '/joint_limits/min'))
00242 
00243     #if robot == "pr2":
00244     #  if self.opt.arm == 'r' or self.opt.arm == 'l':
00245     #    # Relies on something (usually the haptic state node) pushing the right joint limits to the parameter server before this starts
00246     #    base_path = '/haptic_mpc'
00247     #    self.joint_limits_max = rospy.get_param(base_path + '/joint_limits/max')
00248     #    self.joint_limits_min = rospy.get_param(base_path + '/joint_limits/min')
00249     #  else:
00250     #    rospy.logerr('Arm not specified for PR2 kinematics')
00251     #    sys.exit()
00252     #elif robot == "sim3":
00253     #  import gen_sim_arms as sim_robot
00254     #  import hrl_common_code_darpa_m3.robot_config.three_link_planar_capsule as sim_robot_config
00255     #  self.robot_kinematics = sim_robot.RobotSimulatorKDL(sim_robot_config) # KDL chain.
00256     #elif robot == "cody" or robot == "simcody":
00257     #  import hrl_cody_arms.cody_arm_kinematics as cody_robot
00258     #  if self.opt.arm == 'r' or self.opt.arm == 'l':
00259     #    self.robot_kinematics = cody_robot.CodyArmKinematics(self.opt.arm)
00260     #  else:
00261     #    rospy.logerr('Arm not specified for Cody kinematics')
00262     #    sys.exit()
00263     #else: # Unknown robot type
00264     #  rospy.logerr('Unknown robot type given. Valid options are: [\'pr2\', \'cody\', \'sim3\', \'simcody\']')
00265     #  sys.exit()
00266   
00267   ## getSkinData accessor function.
00268   # @return A copy of the skin_data dictionary, containing the latest taxel data. 
00269   def getSkinData(self):
00270     with self.state_lock:
00271       skin_data = copy.copy(self.skin_data)
00272     return skin_data
00273   
00274   ## getJointAngles accessor function.
00275   # @return A copy of the buffered joint angles list.
00276   def getJointAngles(self):
00277     with self.state_lock:
00278       joint_angles = copy.copy(self.joint_angles)
00279     return joint_angles
00280   
00281   ## getDesiredJointAngles accessor function.
00282   # @return A copy of the desired joint angles list, ie, the current arm controller setpoint.
00283   def getDesiredJointAngles(self):
00284     with self.state_lock:
00285       desired_joint_angles = copy.copy(self.desired_joint_angles)
00286     return desired_joint_angles
00287   
00288   ## getJointStiffness accessor function.
00289   # @return A copy of the joint stiffness parameters used by the controller.
00290   def getJointStiffness(self):
00291     with self.state_lock:
00292       joint_stiffness = copy.copy(self.joint_stiffness)
00293     return joint_stiffness
00294   
00295   ## getMPCData accessor function.
00296   # Returns a copy of the control data structure.
00297   def getMPCData(self):
00298     with lock:
00299       return copy.copy(self.mpc_data) 
00300 
00301   ## Builds the MPC data structure from the individual parameters
00302   # This is just a convenience data structure to amalgamate the data.
00303   # Also does data validation.
00304   def initMPCData(self):
00305     # Copy the latest data received from the robot haptic state.
00306     # To ensure the data is synchronised, all necessary data is copied with the lock rather than using the accessor functions.
00307     with self.state_lock:
00308       q = copy.copy(self.joint_angles)
00309       q_des = copy.copy(self.desired_joint_angles)
00310       Jc = copy.copy(self.Jc)  
00311       skin_data = copy.copy(self.skin_data)
00312       ee_pos = copy.copy(self.end_effector_pos)
00313       ee_orient_quat = copy.copy(self.end_effector_orient_quat)
00314       joint_stiffness = copy.copy(self.joint_stiffness)
00315       Je = copy.copy(self.Je)
00316 
00317     # Copy the goal parameters.
00318     with self.goal_lock:
00319       goal_pos = copy.copy(self.goal_pos)
00320       goal_orient_quat = copy.copy(self.goal_orient_quat)
00321       
00322     with self.posture_lock:
00323       goal_posture = copy.copy(self.goal_posture)
00324 
00325     n_l = haptic_mpc_util.getNormals(skin_data)
00326     f_l = haptic_mpc_util.getValues(skin_data)
00327     loc_l = haptic_mpc_util.getLocations(skin_data)
00328     
00329     # Control params
00330     k_default = self.static_contact_stiffness_estimate
00331     dist_g = self.time_step * self.goal_velocity_for_hand 
00332     
00333     # Compute various matrices.
00334     Kc_l = self.contactStiffnessMatrix(n_l, k_default) # Using default k_est_min and k_est_max
00335     Rc_l = self.contactForceTransformationMatrix(n_l)
00336     
00337     # calculate the normal components of the current contact
00338     # forces
00339     tmp_l = [R_ci * f_ci for R_ci, f_ci in it.izip(Rc_l, f_l)]
00340     f_n = np.matrix([tmp_i[0,0] for tmp_i in tmp_l]).T
00341     
00342     delta_f_min, delta_f_max = self.deltaForceBounds(f_n, 
00343                                                        max_pushing_force = self.allowable_contact_force,
00344                                                        max_pulling_force = self.allowable_contact_force,
00345                                                        max_pushing_force_increase = self.max_delta_force_mag, 
00346                                                        max_pushing_force_decrease = self.max_delta_force_mag, 
00347                                                        min_decrease_when_over_max_force = 0.,
00348                                                        max_decrease_when_over_max_force = 10.0)
00349       
00350     q_des_matrix = (np.matrix(q_des)[0:len(q_des)]).T
00351       
00352     K_j = np.diag(joint_stiffness)
00353    
00354     if len(Je) >= 1: # Je could potentially be a list of end effector jacobians. We only care about the first one in this implementation.
00355       Je = Je[0]
00356     
00357     # Calculate MPC orientation/position weight. This is a bad hack to essentially damp the response as it approaches the goal.
00358     with self.gain_lock:
00359         orient_weight = self.orient_weight
00360         position_weight = self.pos_weight
00361         posture_weight = self.posture_weight
00362     planar = False   
00363  
00364     # Evalute current goals to give the controller some meaningful input
00365     # If the goal is non-existant, use the current position/orientation/posture as the goal.
00366     # Position
00367     x_h = ee_pos # numpy array
00368     x_g = goal_pos
00369     if x_g == None:
00370       x_g = x_h
00371     
00372     # Orientation
00373     q_h_orient = ee_orient_quat
00374     q_g_orient = goal_orient_quat
00375     if q_g_orient == None:
00376       q_g_orient = q_h_orient
00377     
00378     # Posture
00379     if goal_posture == None:
00380       goal_posture = q  
00381     
00382     dist_goal_2D = np.linalg.norm((x_g - x_h)[0:2])
00383     dist_goal_3D = np.linalg.norm(x_g - x_h)
00384     #if planar:
00385     #  dist_goal = dist_goal_2D
00386     #else:
00387     dist_goal = dist_goal_3D
00388 
00389     angle_error = ut.quat_angle(q_h_orient, q_g_orient)
00390     
00391     jerk_opt_weight = self.jerk_opt_weight   
00392  
00393     if orient_weight != 0:
00394       proportional_ball_radius = 0.1
00395       proportional_ball_dist_slope = 1.0
00396       if dist_goal < proportional_ball_radius:
00397         position_weight = position_weight * dist_goal/ (proportional_ball_radius * proportional_ball_dist_slope)
00398 
00399       jerk_opt_weight = max(jerk_opt_weight * position_weight * 2, jerk_opt_weight)
00400 
00401       proportional_ball_angle = math.radians(30)
00402       proportional_ball_angle_slope = 1.
00403       if angle_error < proportional_ball_angle:
00404         orient_weight = orient_weight * angle_error/ (proportional_ball_angle * proportional_ball_angle_slope)
00405 
00406 
00407     # Initialise and return the mpc data structure.
00408     mpc_data = MPCData( q=q, 
00409                         x_h = x_h, # numpy array
00410                         x_g = x_g, # numpy array
00411                         dist_g = dist_g, 
00412                         goal_posture = goal_posture,
00413                         q_h_orient = q_h_orient, 
00414                         q_g_orient = q_g_orient, 
00415                         position_weight = position_weight, 
00416                         orient_weight = orient_weight, 
00417                         posture_weight = posture_weight,
00418                         force_weight = 0.0005, # default_force_weight. Is 0.005 for the position only version.
00419                         force_reduction_goal = self.force_reduction_goal, # default desired delta for force reduction. 
00420                         control_point_joint_num = len(q), # number of joints 
00421                         Kc_l = Kc_l, 
00422                         Rc_l = Rc_l, 
00423                         Jc_l = Jc, 
00424                         Je = Je, # NB: Je is a list of end effector jacobians (to use the same utils functions as Jc) 
00425                         delta_f_min = delta_f_min, 
00426                         delta_f_max = delta_f_max, 
00427                         phi_curr = q_des_matrix, 
00428                         K_j = K_j, 
00429                         loc_l = loc_l, # Contact locations from taxels. From skin client. 
00430                         n_l = n_l, # Normals for force locations
00431                         f_l = f_l, # Cartesian force components for contacts
00432                         f_n = f_n, # Normal force for contacts
00433                         jerk_opt_weight = jerk_opt_weight, # From control params
00434                         max_force_mag = self.allowable_contact_force, # From control params
00435                         jep = q_des, 
00436                         time_step = self.time_step, 
00437                         stop = False)
00438     return mpc_data
00439 
00440   ## Update the position/orientation weights used by the controller.
00441   # @param msg HapticMpcWeights message object
00442   def updateWeightsCallback(self, msg):   
00443     with self.gain_lock:
00444       rospy.loginfo("Updating MPC weights. Pos: %s, Orient: %s, Posture: %s" % (str(msg.position_weight), str(msg.orient_weight), str(msg.posture_weight)))
00445       self.pos_weight = msg.position_weight
00446       self.orient_weight = msg.orient_weight
00447       self.posture_weight = msg.posture_weight
00448       self.mpc_weights_pub.publish(msg) # Echo the currently used weights. Used as an ACK, basically.
00449 
00450   ## Store the current trajectory goal. The controller will always attempt a linear path to this.
00451   # @param msg A PoseStamped message
00452   def goalPoseCallback(self, msg):
00453     with self.goal_lock:
00454       self.goal_pos = np.matrix([[msg.pose.position.x], [msg.pose.position.y], [msg.pose.position.z]])
00455       self.goal_orient_quat = [msg.pose.orientation.x, msg.pose.orientation.y, msg.pose.orientation.z, msg.pose.orientation.w]
00456   
00457   ## Store the current posture goal. The controller attempts to achieve this posture.
00458   # TODO: Define message type. Ideally just joint angles. Float64 list?
00459   # @param msg An array of Float64s. Type hrl_msgs.FloatArray.
00460   def goalPostureCallback(self, msg):
00461     with self.posture_lock:
00462       self.goal_posture = msg.data # Store it as a python list of floats
00463   
00464   ## Store the state information from the monitor node. Allows the control to be somewhat stateful.
00465   # The state and error fields are lists of strings indicating some state.
00466   # @param msg HapticMpcState message object
00467   def mpcMonitorCallback(self, msg):
00468     with self.monitor_lock:
00469       self.mpc_state = msg.state
00470       self.mpc_error = msg.error
00471   
00472   ## Store the robot haptic state.
00473   # @param msg RobotHapticState message object
00474   def robotStateCallback(self, msg):
00475     with self.state_lock:
00476       self.last_msg_time = rospy.Time.now() # timeout for the controller
00477       
00478       self.msg = msg
00479       self.joint_names = msg.joint_names
00480       self.joint_angles = list(msg.joint_angles) 
00481       self.desired_joint_angles = list(msg.desired_joint_angles)
00482       self.joint_velocities= list(msg.joint_velocities)
00483       self.joint_stiffness = list(msg.joint_stiffness)
00484       self.joint_damping = list(msg.joint_damping)
00485       
00486       self.end_effector_pos = np.matrix([[msg.hand_pose.position.x], [msg.hand_pose.position.y], [msg.hand_pose.position.z]])
00487       self.end_effector_orient_quat = [msg.hand_pose.orientation.x, msg.hand_pose.orientation.y, msg.hand_pose.orientation.z, msg.hand_pose.orientation.w]
00488       
00489       self.skin_data = msg.skins
00490  
00491       self.Je = self.ma_to_m.multiArrayToMatrixList(msg.end_effector_jacobian)
00492       self.Jc = self.ma_to_m.multiArrayToMatrixList(msg.contact_jacobians)
00493 
00494   ## Interpolate a step towards the given goal orientation.
00495   # @param q_h_orient The current hand orientation as a quaternion in list form: [x,y,z,w]
00496   # @param q_g_orient The current goal orientation as a quaternion in list form: [x,y,z,w]
00497   # @return A desired change in orientation as a delta: 
00498   # TODO: clean this up so it doesn't use the "step" multiplier
00499   def goalOrientationInQuat(self, q_h_orient, q_g_orient, max_ang_step):
00500     # If the goal position is invalid, return the current end effector position 
00501     if not q_g_orient:
00502       q_g_orient = q_h_orient
00503     
00504     ang = ut.quat_angle(q_h_orient, q_g_orient)
00505     ang_mag = abs(ang)
00506     step_fraction = 0.1
00507     if step_fraction * ang_mag > max_ang_step:
00508       # this is pretty much always true, can clean up the code.
00509       step_fraction = max_ang_step / ang_mag
00510 
00511     interp_q_goal = tr.tft.quaternion_slerp(q_h_orient, q_g_orient, step_fraction)
00512     delta_q_des = tr.tft.quaternion_multiply(interp_q_goal, tr.tft.quaternion_inverse(q_h_orient))
00513 
00514     return np.matrix(delta_q_des[0:3]).T
00515   
00516   ## Interpolate a step towards the given goal position.
00517   # @return A goal delta in position as a numpy matrix.
00518   # @param x_h The current hand position as a numpy matrix: [x,y,z]
00519   # @param x_g The current goal position as a numpy matrix: [x,y,z]
00520   def goalMotionForHand(self, x_h, x_g, dist_g): 
00521     # Goal should be None (if no goal pose has been heard yet, or a numpy column vector.
00522     if x_g == None or x_g.size == 0:
00523       x_g = x_h
00524 
00525     err = x_g - x_h
00526     err_mag = np.linalg.norm(err)
00527 
00528     # Advait: reducing the step size as the arm reaches close to
00529     # the goal. scale is something that I picked empirically on
00530     # Cody (I printed out err_mag and dist_g)
00531     #scale = 4.
00532     scale = 15.0
00533     if err_mag > dist_g * scale:
00534       delta_x_g = dist_g * (err/err_mag)
00535     else:
00536       delta_x_g = err/scale
00537 
00538     return delta_x_g
00539 
00540   ## Process the goal posture input before sending it to the controller.
00541   # @param goal_posture List of desired joint angles. Should be
00542   # @param current_posture List of current joint angles
00543   # 
00544   def goalDeltaPosture(self, goal_posture, current_posture, max_theta_step, num_steps_scale):
00545     assert max_theta_step >= 0
00546     assert num_steps_scale >= 0
00547     # NB: max_theta_step, num_steps_scale must both be POSITIVE.
00548     delta_theta_des = np.matrix(goal_posture).T - np.matrix(current_posture).T # error term.
00549    # delta_theta_des = 0.1 * delta_theta_des # scale the absolute error
00550 #    print "goalDeltaPosture"
00551 #    print delta_theta_des
00552 
00553 # ONE SCALING METHOD
00554     for i in range(len(delta_theta_des)):
00555       theta = delta_theta_des[i] # theta is the error relative to the desired posture on each joint.
00556       # num_steps_scale is a parameter which represents how many steps out the delta will be scaled down.
00557       if abs(theta) > num_steps_scale * max_theta_step:
00558         theta = max_theta_step * theta/abs(theta) # If we're more than the num steps away, just cap the theta step.
00559       else:
00560         theta = theta/num_steps_scale 
00561         # Eg, if we have a step size of 1.0 deg, num_steps is 10.0
00562         # If we're 8 steps from the goal, we only command 0.8 deg rather than 1.0 (8/10)
00563       delta_theta_des[i] = theta
00564       
00565     return delta_theta_des
00566     
00567 
00568   
00569   ## Main control calculation.
00570   # @param mpc_dat An MPCData class, populated with relevant control parameters and current data
00571   # @return A list of joint equilibrium point deltas, ie, a desired increment to the current joint position(s).
00572   def deltaQpJepGen(self, mpc_dat):  
00573     # If we have invalid control data, do nothing
00574     if mpc_dat == None:
00575       return None
00576    
00577     # If we're within the deadzone band, do nothing
00578     dist_to_goal = np.linalg.norm(mpc_dat.x_h - mpc_dat.x_g)
00579     ang_to_goal = np.degrees(ut.quat_angle(mpc_dat.q_h_orient, mpc_dat.q_g_orient))
00580     dist_g = mpc_dat.dist_g # The computed step based on the controller frequency and desired cartesian velocity.
00581     ang_dist_g = self.orient_step
00582 
00583     # Deadzone on pose motion
00584     in_deadzone = False
00585     # If position and orientation, most common scenario
00586     if (mpc_dat.orient_weight > 0.0 and mpc_dat.position_weight > 0.0):
00587       if abs(dist_to_goal) < self.deadzone_distance and abs(ang_to_goal) < self.deadzone_angle:
00588         in_deadzone = True
00589     elif mpc_dat.position_weight > 0.0: # Position only 
00590       if abs(dist_to_goal) < self.deadzone_distance:
00591         in_deadzone = True
00592     elif mpc_dat.orient_weight > 0.0: # Orientation only
00593       if abs(ang_to_goal) < self.deadzone_angle:
00594         in_deadzone = True
00595       
00596     # Don't command any pose motion 
00597     if in_deadzone:
00598       dist_g = 0.0 
00599       ang_dist_g = 0.0   
00600       if self.currently_in_deadzone == False:
00601         rospy.loginfo("MPC is in deadzone: pos %s (%s); orient %s (%s)" % (str(dist_to_goal), str(self.deadzone_distance), str(ang_to_goal), str(self.deadzone_angle) ))   
00602       self.currently_in_deadzone = True
00603       
00604       # If we're in the deadzone and have no forces, return zeroes.
00605       if len(mpc_dat.loc_l) == 0:
00606         return [0.0] * len(self.joint_angles)
00607 
00608     else:
00609       self.currently_in_deadzone = False
00610   
00611     # Generate the position/orientation deltas. These are slewed to set step size.
00612     delta_pos_g  = self.goalMotionForHand(mpc_dat.x_h, 
00613                                                     mpc_dat.x_g, 
00614                                                     dist_g)
00615     
00616 
00617     delta_orient_g = self.goalOrientationInQuat(mpc_dat.q_h_orient, 
00618                                                    mpc_dat.q_g_orient, 
00619                                                    ang_dist_g)
00620     
00621     # Build the delta_x_g vector for the controller.
00622 #    if mpc_dat.position_weight == 0.:
00623 #      delta_x_g = delta_orient_g
00624 #      J_h = mpc_dat.Je[3:] 
00625 #      T_quat = 0.5 * (mpc_dat.q_h_orient[3] 
00626 #                      * np.matrix(np.eye(3)) 
00627 #                      - haptic_mpc_util.getSkewMatrix(mpc_dat.q_h_orient[0:3]))
00628 #      J_h = T_quat*J_h * np.sqrt(mpc_dat.orient_weight)
00629 #
00630 #    elif mpc_dat.orient_weight == 0.:
00631 #      delta_x_g = delta_pos_g
00632 #      J_h = mpc_dat.Je[0:3] * np.sqrt(mpc_dat.position_weight)
00633 #    else:
00634       
00635 #    delta_x_g = np.vstack((delta_pos_g*np.sqrt(mpc_dat.position_weight), 
00636 #                           delta_orient_g*np.sqrt(mpc_dat.orient_weight)))
00637 #    
00638 #    J_h = mpc_dat.Je
00639 #    T_quat = 0.5 * (mpc_dat.q_h_orient[3] 
00640 #                    * np.matrix(np.eye(3)) 
00641 #                    - haptic_mpc_util.getSkewMatrix(mpc_dat.q_h_orient[0:3]))
00642 #    
00643 #    J_h[3:] = T_quat*J_h[3:]
00644 #    J_h[0:3] = J_h[0:3] * np.sqrt(mpc_dat.position_weight)
00645 #    J_h[3:] = J_h[3:] * np.sqrt(mpc_dat.orient_weight)
00646 
00647 
00648 # TODO - NEW MATHS. Explicitly apply weights - it's much easier to understand.
00649     # combine position/orientation goals
00650     delta_x_g = np.vstack( (delta_pos_g, delta_orient_g) )
00651       
00652     J_h = mpc_dat.Je
00653     T_quat = 0.5 * (mpc_dat.q_h_orient[3] 
00654                       * np.matrix(np.eye(3)) 
00655                       - haptic_mpc_util.getSkewMatrix(mpc_dat.q_h_orient[0:3]))
00656       
00657     J_h[3:] = T_quat*J_h[3:]
00658     
00659     J_h[0:3] = J_h[0:3] #* np.sqrt(mpc_dat.position_weight)
00660     J_h[3:] = J_h[3:] #* np.sqrt(mpc_dat.orient_weight)
00661     
00662 
00663     # For some reason the force reduction term in the cost function changes weights... 
00664     # Jeff has no idea why Advait did this. This used to be buried in epc_skin_math and will be removed.
00665     # TODO: Push these to the param server, and work out why these exist to begin with.
00666     if delta_x_g.shape[0] == 3: # position only or orientation only
00667       mpc_dat.force_weight = 0.0005
00668     elif delta_x_g.shape[0] == 6: # both position and orientation at once.
00669        mpc_dat.force_weight = 0.005 
00670 
00671     n_joints = mpc_dat.K_j.shape[0]
00672     J_h[:, mpc_dat.control_point_joint_num:] = 0.
00673     J_h = np.matrix(J_h[:,0:n_joints]) # comes into play with Cody and the wrist cover
00674 
00675 
00676     # If torque constraints are violated - ie Q_des is far from Q - then reset Q_des to Q
00677     q_diff = np.array(mpc_dat.jep) - np.array(mpc_dat.q)
00678     max_q_diff = np.max(np.abs(q_diff))
00679     if max_q_diff > 15.0: # TODO: Hardcoded parameter used in the control function - move this elsewhere!
00680       # Reset JEP to Q.
00681       rospy.loginfo("JEPs too far from current position - resetting them to current position")
00682       self.publishDesiredJointAngles(self.getJointAngles()) 
00683       return None   
00684     
00685     # Postural term input
00686     delta_theta_des = self.goalDeltaPosture(mpc_dat.goal_posture, mpc_dat.q, self.max_theta_step, self.theta_step_scale)
00687     if len(delta_theta_des) > n_joints: # Trim to the number of DOFs being used for this - not necessarily the number of joints present, eg Cody 5DOF
00688       delta_theta_des = delta_theta_des[0:n_joints]
00689 
00690         
00691     
00692     
00693     cost_quadratic_matrices, cost_linear_matrices, \
00694     constraint_matrices, \
00695     constraint_vectors, lb, ub = esm.convert_to_qp_posture(J_h, # 6xDOF end effector jacobian
00696                                                    mpc_dat.Jc_l, # list of contacts
00697                                                    mpc_dat.K_j, # joint_stiffnesses (DOFxDOF)
00698                                                    mpc_dat.Kc_l, # list of 3x3s
00699                                                    mpc_dat.Rc_l, # list of 3x3s
00700                                                    mpc_dat.delta_f_min, # num contacts * 1
00701                                                    mpc_dat.delta_f_max,
00702                                                    mpc_dat.phi_curr, # DOF
00703                                                    delta_x_g, mpc_dat.f_n, mpc_dat.q,
00704                                                    self.joint_limits_min, # joint limits (used to be kinematics object)
00705                                                    self.joint_limits_max,
00706                                                    mpc_dat.jerk_opt_weight,     #between 0.000000001 and .0000000001, cool things happened
00707                                                    mpc_dat.max_force_mag,
00708                                                    delta_theta_des,
00709                                                    mpc_dat.posture_weight,
00710                                                    mpc_dat.position_weight,
00711                                                    mpc_dat.orient_weight,
00712                                                    mpc_dat.force_weight,
00713                                                    mpc_dat.force_reduction_goal)
00714 
00715     
00716 
00717     lb = lb[0:n_joints]
00718     ub = ub[0:n_joints] # Trim the lower/upper bounds to the number of joints being used (may be less than DOF - eg Cody).
00719 
00720     delta_phi_opt, opt_error, feasible = esm.solve_qp(cost_quadratic_matrices, 
00721                                                       cost_linear_matrices, 
00722                                                       constraint_matrices, 
00723                                                       constraint_vectors, 
00724                                                       lb, ub, 
00725                                                       debug_qp=False)
00726 
00727     # Updated joint positions.
00728     mpc_dat.jep[0:n_joints] = (mpc_dat.phi_curr[0:n_joints] + delta_phi_opt).A1
00729     delta_phi = np.zeros(len(self.joint_names))
00730     delta_phi[0:n_joints] = delta_phi_opt.T
00731 
00732     # warn if JEP goes beyond joint limits
00733     #if self.robot_kinematics.within_joint_limits(mpc_dat.jep) == False:
00734     #  rospy.logwarn('Outside joint limits. They will be clamped later...')
00735     #  rospy.logwarn("Limits: %s" % str(self.robot_kinematics.joint_lim_dict))
00736     #  rospy.logwarn("Current JEP: %s" % str(mpc_dat.jep))
00737 
00738     return delta_phi # Return a joint position delta
00739     #return mpc_dat.jep # Return the an absolute joint position - old implementation
00740 
00741   
00742   ## Computes the contact stiffness matrix, K_ci.
00743   # @param n_l List of normal forces
00744   # #param k_default Default stiffness, N/m. Default is typically 200.0-1000.0
00745   def contactStiffnessMatrix(self, n_l, k_default, k_est_min=100.0, k_est_max=100000.0):
00746     # This computes the contact stiffness matrices, K_ci
00747     #
00748     # K_ci are size 3 x 3
00749     # K_c is size 3n x 3n
00750     Kc_l = []
00751     for n_ci in it.izip(n_l):
00752       # n_ci is a unit vector located at the point of contact,
00753       # or at the center of the taxel, that is normal to the
00754       # surface of the robot and points away from the surface of
00755       # the robot.
00756       #
00757       # This should result in a stiffness matrix that has a
00758       # stiffness of k in the direction of the normal and a
00759       # stiffness of 0 in directions orthogonal to the normal.
00760       #
00761       # If the contact location moves away from the current
00762       # contact location along the arm's surface normal, then
00763       # the magnitude of the force applied by the robot to the
00764       # environment should increase proportionally to the
00765       # distance the contact location has moved along the
00766       # surface normal. Given our conventions, this motion
00767       # projected onto the surface normal, n_ci, would result in
00768       # a positive value. Moreover, since we represent forces as
00769       # the force applied by the robot to the environment, the
00770       # resulting force vector should point in the same
00771       # direction.
00772       #
00773       n_ci = np.nan_to_num(n_ci)
00774       K_ci = k_default * np.outer(n_ci, n_ci)
00775 
00776       Kc_l.append(np.matrix(K_ci))
00777     return Kc_l
00778 
00779   ## Calculates the force transformation matrix from a list of force normals
00780   # @param n_l List of force normals
00781   # @return List of transformation matrices (rotations)
00782   def contactForceTransformationMatrix(self, n_l):        
00783     # Compute R_c, which as originally conceived, would consist of
00784     # rotation matrices that transform contact forces to the local
00785     # contact frame R_c f_c_global = f_c_local
00786     #
00787     # For now, R_c instead ignores all forces other than the force
00788     # normal to the surface of the robot's arm at the point of
00789     # contact. R_c is only used for the constraint matrices. So,
00790     # R_ci recovers the component of the contact force, f_ci, that
00791     # is normal to the surface. If f_ci points toward the arm,
00792     # then it represents the robot pulling on the environment. If
00793     # f_ci points away from the arm, then it represents the robot
00794     # pushing on the environment.
00795     #
00796     # R_ci f_ci = f_norm_scalar
00797     # R_ci is size 1 x 3
00798     # R_c is size n x 3n
00799     # 
00800     Rc_l = []
00801     for n_ci in n_l:
00802       # n_ci is a unit vector that is normal to the surface of
00803       # the robot and points away from the surface of the robot.
00804       R_ci = np.matrix(np.zeros((1,3)))
00805       R_ci[:] = n_ci[:].T
00806       Rc_l.append(R_ci)
00807     return Rc_l
00808 
00809   ## Compute bounds for delta_f
00810   # @retval delta_f_min Minimum bound
00811   # @retval delta_f_max Maximum bound
00812   def deltaForceBounds(self, f_n, 
00813                      max_pushing_force, max_pulling_force,
00814                      max_pushing_force_increase, max_pushing_force_decrease, 
00815                      min_decrease_when_over_max_force, 
00816                      max_decrease_when_over_max_force):
00817     # Compute bounds for delta_f:  delta_f_max and delta_f_min
00818     #
00819     # all inputs should be positive
00820     assert (max_pushing_force >= 0.0), "delta_f_bounds: max_pushing_force = %f < 0.0" % max_pushing_force
00821     assert (max_pushing_force_increase >= 0.0), "delta_f_bounds: max_pushing_force_increase = %f < 0.0" % max_pushing_force_increase
00822     assert (max_pushing_force_decrease >= 0.0), "delta_f_bounds: max_pushing_force_decrease = %f < 0.0" % max_pushing_force_decrease
00823     assert (max_pulling_force >= 0.0), "delta_f_bounds: max_pulling_force = %f < 0.0" % max_pulling_force
00824     assert (min_decrease_when_over_max_force >= 0.0), "delta_f_bounds: min_decrease_when_over_max_force = %f < 0.0" % min_decrease_when_over_max_force
00825     assert (max_decrease_when_over_max_force >= 0.0), "delta_f_bounds: max_decrease_when_over_max_force = %f < 0.0" % max_decrease_when_over_max_force
00826     
00827     # Set delta_f_max. The change to the normal components of
00828     # the contact forces must be less than these
00829     # values. delta_f_max limits the magnitude of the force
00830     # with which the robot can push on the environment at each
00831     # of its contact locations.
00832     #
00833     # f_max is size n x 1
00834     #
00835     # Compute how much the contact force can change before it hits
00836     # the maximum, and limit the expected increase in force by
00837     # this quantity.
00838     n = f_n.shape[0]
00839     f_max = max_pushing_force * np.matrix(np.ones((n,1)))
00840     delta_f_max = f_max - f_n
00841     # Also incorporate constraint on the expected increase in the
00842     # contact force for each contact. This limits the rate of
00843     # increase in pushing force.
00844     delta_f_max = np.minimum(delta_f_max, 
00845                              max_pushing_force_increase * np.matrix(np.ones((n,1))))
00846 
00847     # Set delta_f_min. The change to the normal components of
00848     # the contact forces must be greater than these
00849     # values. delta_f_min limits the magnitude of the force
00850     # with which the robot can pull on the environment at each
00851     # of its contact locations.
00852     #
00853     # f_min is size n x 1
00854     #        
00855     f_min = -max_pulling_force * np.matrix(np.ones((n,1)))
00856     delta_f_min = f_min - f_n
00857     # Also incorporate constraint on the expected change of
00858     # the contact force for each contact
00859     delta_f_min = np.maximum(delta_f_min, 
00860                              -max_pushing_force_decrease * np.matrix(np.ones((n,1))))
00861 
00862     # # Setting negative values of delta_f_min to large negative
00863     # # numbers so that adhesive forces are not a binding constraint
00864     delta_f_min[np.where(delta_f_min<=0)]=-10000
00865 
00866     # If a force has exceeded the maximum use special constraints.
00867     over_max = f_n > max_pushing_force
00868     if over_max.any():
00869       # at least one of the contact forces is over the maximum allowed
00870       delta_f_max[over_max] = -min_decrease_when_over_max_force
00871       delta_f_min[over_max] = -max_decrease_when_over_max_force
00872 
00873     return delta_f_min, delta_f_max
00874 
00875   ## Publishes a list of Deltas for the desired joint positions.
00876   # @param ctrl_data List of desired joint position deltas to be published
00877   def publishDeltaControlValues(self, ctrl_data):
00878     msg = hrl_msgs.msg.FloatArrayBare()
00879     msg.data = ctrl_data
00880     self.delta_q_des_pub.publish(msg)
00881   
00882   ## Publish a desired joint position list directly (rather than deltas)
00883   # @param ctrl_data List of desired joint positions to be published (not deltas).
00884   def publishDesiredJointAngles(self, ctrl_data):
00885     msg = hrl_msgs.msg.FloatArrayBare()
00886     msg.data = ctrl_data
00887     self.q_des_pub.publish(msg)
00888 
00889   ## Main control function. Builds the control data structure and passes it to the control calculation routine
00890   def updateController(self):
00891     # Check for controller enabled
00892     if not self.mpc_enabled:
00893       return  
00894   
00895     # Check for haptic state msg timeout
00896     with self.state_lock:
00897       time_since_last_msg = rospy.Time.now() - self.last_msg_time # will always exist as we block on startup until a haptic state msg is heard.
00898 
00899     # Timeout based on time since the last heard message.
00900     # TODO: Replace this with a freshness parameter on the skin data. More flexible than a timeout.
00901 #    if time_since_last_msg.to_sec() > self.timeout:
00902 #      if self.waiting_to_resume == False:
00903 #        rospy.logwarn("MPC hasn't heard a haptic state messages for %s s. Stopping control effort." % str(time_since_last_msg.to_sec()))
00904 #        self.waiting_to_resume = True
00905 #        #return from updateController without actually doing anything
00906 #        return
00907 #    else:
00908 #      if self.waiting_to_resume == True:
00909 #        self.waiting_to_resume = False
00910 #        rospy.logwarn("MPC resumed control - haptic state messages received again.")
00911 #    
00912     # Listen to a state topic from the monitor node and respond to errors.
00913     with self.monitor_lock:
00914       mpc_state = copy.copy(self.mpc_state)
00915       mpc_error = copy.copy(self.mpc_error)
00916     
00917     if mpc_error != None and len(mpc_error) > 0:
00918       if self.waiting_for_no_errors == False:
00919         rospy.logwarn("HapticMPC: MPC monitor detected error conditions. Stopping control effort.\nState:\n%s\nErrors:\n%s" % (str(mpc_state), str(mpc_error)))
00920         self.waiting_for_no_errors = True
00921       return
00922     else:
00923       if self.waiting_for_no_errors == True:
00924         self.waiting_for_no_errors = False
00925         rospy.logwarn("HapticMPC: MPC resumed control - errors cleared.")
00926     
00927     # If good, run controller.
00928     mpc_data = self.initMPCData()
00929     desired_joint_pos = self.deltaQpJepGen(mpc_data) # deltas
00930     if desired_joint_pos == None:
00931       desired_joint_pos = [0.0] * len(self.joint_angles)
00932     self.publishDeltaControlValues(desired_joint_pos)
00933     
00934 
00935   ## Initialise the ROS communications - init node, subscribe to the robot state and goal pos, publish JEPs
00936   def initComms(self, node_name):
00937     rospy.init_node(node_name)
00938     self.robot_state_sub = rospy.Subscriber("/haptic_mpc/robot_state", haptic_msgs.RobotHapticState, self.robotStateCallback)
00939     self.goal_pose_sub = rospy.Subscriber("/haptic_mpc/traj_pose", geometry_msgs.msg.PoseStamped, self.goalPoseCallback)
00940     self.goal_posture_sub = rospy.Subscriber("/haptic_mpc/goal_posture", hrl_msgs.msg.FloatArray, self.goalPostureCallback)
00941     
00942     self.delta_q_des_pub= rospy.Publisher("/haptic_mpc/delta_q_des", hrl_msgs.msg.FloatArrayBare)
00943     self.q_des_pub = rospy.Publisher("/haptic_mpc/q_des", hrl_msgs.msg.FloatArrayBare)
00944     
00945     self.mpc_monitor_sub = rospy.Subscriber("/haptic_mpc/mpc_state", haptic_msgs.HapticMpcState, self.mpcMonitorCallback)
00946     
00947     self.mpc_weights_sub = rospy.Subscriber("haptic_mpc/weights", haptic_msgs.HapticMpcWeights, self.updateWeightsCallback)
00948     self.mpc_weights_pub = rospy.Publisher("haptic_mpc/current_weights", haptic_msgs.HapticMpcWeights, latch=True)
00949   
00950     self.enable_mpc_srv = rospy.Service("/haptic_mpc/enable_mpc", haptic_srvs.EnableHapticMPC, self.enableHapticMPC)
00951   
00952   ## Enable Haptic MPC service handler (default is enabled).
00953   def enableHapticMPC(req):
00954     if req.new_state == "enabled":
00955       self.mpc_enabled = True
00956     else:
00957       self.mpc_enabled = False
00958 
00959     return haptic_srvs.EnableHapticMPCResponse(self.mpc_state)  
00960 
00961   ## Handler for Ctrl-C signals. Some of the ROS spin loops don't respond well to
00962   # Ctrl-C without this. 
00963   def signal_handler(self, signal, frame):
00964     print 'Ctrl+C pressed - exiting'
00965     sys.exit(0)
00966 
00967 
00968   ## Start the control loop once the controller is initialised.
00969   def start(self):
00970     signal.signal(signal.SIGINT, self.signal_handler) # Catch Ctrl-Cs
00971     
00972     self.initRobot()
00973     self.initComms("haptic_mpc")
00974     rospy.sleep(1.0) # Delay to allow the ROS node to initialise properly.
00975     self.initControlParametersFromServer()
00976     
00977     r = rospy.Rate(self.frequency)
00978     self.time_step = 1.0/self.frequency # Default: 0.02s at 50 Hz
00979 
00980     rospy.loginfo("HapticMPC: Waiting for Robot Haptic State message")
00981     while not self.getJointAngles():
00982       r.sleep()
00983     rospy.loginfo("HapticMPC: Got Robot Haptic State message")
00984 
00985     rospy.loginfo("HapticMPC: Resetting desired joint angles to current position")
00986     self.publishDesiredJointAngles(self.getJointAngles())    
00987 
00988     # Main control loop
00989     rospy.loginfo("HapticMPC: Starting MPC")
00990     while not rospy.is_shutdown():
00991       self.updateController()
00992       #rospy.spin() # For debug - run once
00993       r.sleep()
00994 
00995 if __name__== "__main__":
00996   import optparse
00997   p = optparse.OptionParser()
00998   haptic_mpc_util.initialiseOptParser(p)
00999   opt = haptic_mpc_util.getValidInput(p)
01000   
01001   mpc_controller = HapticMPC(opt, "haptic_mpc")
01002   mpc_controller.start()
01003 


hrl_haptic_mpc
Author(s): Jeff Hawke, Phillip Grice, Marc Killpack, Advait Jain. Advisor and Co-author: Prof. Charlie Kemp. Healthcare Robotics Lab, Georgia Tech
autogenerated on Wed Nov 27 2013 12:27:09