00001 # Copyright 2013 Georgia Tech Research Corporation 00002 # 00003 # Licensed under the Apache License, Version 2.0 (the "License"); 00004 # you may not use this file except in compliance with the License. 00005 # You may obtain a copy of the License at 00006 # 00007 # http://www.apache.org/licenses/LICENSE-2.0 00008 # 00009 # Unless required by applicable law or agreed to in writing, software 00010 # distributed under the License is distributed on an "AS IS" BASIS, 00011 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00012 # See the License for the specific language governing permissions and 00013 # limitations under the License. 00014 # 00015 # http://healthcare-robotics.com/ 00016 00017 00018 ## @package hrl_haptic_mpc 00019 # @author Advait Jain 00020 # @author Charlie Kemp 00021 # @author Jeff Hawke 00022 # @copyright Apache 2.0 00023 # 00024 # This module contains the core mathematics used for the Haptic MPC. 00025 00026 import openopt as pp # python-openopt package is required for the QP solver 00027 import itertools as it 00028 import numpy as np, math 00029 00030 00031 # Add an additional quadratic term that attempts to 00032 # minimize the magnitude of the delta_phi vector. This is 00033 # comparable to minimizing the jerk, since delta_phi is 00034 # proportional to the change in the joint torque at each 00035 # time step. 00036 # 00037 # 0.5 * delta_phi.T * 2.0 * H * delta_phi = 00038 # delta_phi.T * H * delta_phi = 00039 # delta_phi.T * (D2 + alpha * K_j.T * K_j) * delta_phi = 00040 # delta_phi.T * (D2 * delta_phi + alpha * K_j.T K_j * delta_phi) = 00041 # (delta_phi.T * D2 * delta_phi) + (alpha * delta_phi.T * K_j.T * K_j * delta_phi) 00042 # 00043 # The (alpha * delta_phi.T * K_j.T * K_j * delta_phi) term 00044 # makes the optimization want to reduce the magnitude of 00045 # additional torque applied to the joints prior to the 00046 # joint angles changing and achieving static equilibrium 00047 # (K_j * delta_phi) 00048 # 00049 # Alternatively, we could optimize with change in torque 00050 # expected by our model after static equilibrium is 00051 # achieved: 00052 # 00053 # delta_tau = K_j * (delta_phi - delta_theta) 00054 # and 00055 # delta_theta = P2 * K_j * delta_phi 00056 # so 00057 # delta_tau = K_j * (delta_phi - P2 * K_j * delta_phi) 00058 # delta_tau = K_j * (I - P2 * K_j) delta_phi 00059 # delta_tau = (K_j - K_j * P2 * K_j) delta_phi 00060 # which would imply we'd use 00061 # (K_j - K_j * P2 * K_j).T * (K_j - K_j * P2 * K_j) 00062 #D2 = D2 + (jerk_opt_weight * np.identity(m)) 00063 def min_jerk_quadratic_matrix(jerk_opt_weight, K_j): 00064 return (jerk_opt_weight * K_j.T * K_j) 00065 00066 # Calculate D2, D3, D4, D5, and D6 00067 def D_matrices(delta_x_g, delta_f_min, K_j, Rc_l, P3, P4_l): 00068 m = K_j.shape[0] 00069 n = delta_f_min.shape[0] 00070 00071 # D_2 = (P_3)^T P_3 00072 # (this is the matrix for the quadratic term of the cost function) 00073 # D_2 is size m x m 00074 D2 = P3.T * P3 00075 00076 # D_3 = -2 (delta_x_g)^T P_3 00077 # D_3 is size 1 x m 00078 D3 = -2.0 * delta_x_g.T * P3 00079 00080 # D_4 = R_c P_4 K_j 00081 # D_4 is size n x m = (n x 3n) (3n x m) (m x m) 00082 # R_ci * P_4i = (1 x 3) (3 x m) = (1 x m) 00083 # R_ci * P_4i * K_j = (1 x 3) (3 x m) (m x m) = (1 x m) 00084 D4 = np.matrix(np.zeros((n,m))) 00085 for row_num, R_ci, P4_i in it.izip(it.count(), Rc_l, P4_l): 00086 tmp = R_ci * P4_i * K_j 00087 D4[row_num,:] = tmp[:] 00088 00089 # D_5 = -D_4 00090 # D_5 is size n x m 00091 D5 = -D4 00092 00093 # D_6 = - delta_f_min 00094 # D_6 is size n x 1 00095 D6 = - delta_f_min 00096 00097 return D2, D3, D4, D5, D6 00098 00099 # Calculate P_0, P_1, P_2, P_3, and P_4 00100 def P_matrices(J_h, K_j, Kc_l, Jc_l): 00101 m = K_j.shape[0] 00102 00103 # P_0 = K_c J_c 00104 # P_0i is size 3 x m 00105 # P_0 is size 3n x m 00106 P0_l = [K_ci * J_ci for K_ci, J_ci in it.izip(Kc_l, Jc_l)] 00107 00108 # P_1 = (J_c)^T K_c J_c 00109 # P_1 = (J_c)^T P_0 00110 # P_1 is size m x m 00111 P1 = np.matrix(np.zeros((m,m))) 00112 for J_ci, P0_i in it.izip(Jc_l, P0_l): 00113 P1 = P1 + (J_ci.T * P0_i) 00114 00115 # P_2 = ((J_c)^T K_c J_c + K_j)^-1 00116 # P_2 = (P_1 + K_j)^-1 00117 # P_2 is size m x m 00118 tmp = P1 + K_j 00119 #P2 = np.linalg.inv(tmp) 00120 # Advait changed the inv to a pseudo-inverse with the hope 00121 # that it will make things a bit stabler (although nothing 00122 # bad has happened yet -- July 6, 2011.) 00123 # Advait copied this from epc_skin_old_code.py on July 19 00124 P2 = np.linalg.pinv(tmp) 00125 00126 # P_3 = J_h ((J_c)^T K_c J_c + K_j)^-1 K_j 00127 # P_3 = J_h P_2 K_j 00128 # P_3 is size 3 x m 00129 P3 = J_h * P2 * K_j 00130 00131 # P_4 = K_c J_c ((J_c)^T K_c J_c + K_j)^-1 00132 # P_4 = P_0 P_2 00133 # P_4i is size 3 x m 00134 # P_4 is size 3n x m 00135 P4_l = [P0_i * P2 for P0_i in P0_l] 00136 00137 return P0_l, P1, P2, P3, P4_l 00138 00139 # force_idx_l - index of the forces whose magintudes will be 00140 # summed up to compute the cost. 00141 # delta_fg: +ve Scalar (how much to decrease force in the normal direction) 00142 def force_magnitude_cost_matrices(P4_l, K_j, force_idx_l, Rc_l, delta_fg): 00143 # first writing code to compute the term in the cost function. 00144 # I will then pull out the matrix which when pre and post 00145 # multiplied with delta_phi will give the cost that we are 00146 # interested in. This matrix can then be used by the QP 00147 # solver. 00148 # 00149 #delta_fc_l = [P4_i*K_j*delta_phi for P4_i in P4_l] 00150 # 00151 #delta_fc_large_mag = 0. 00152 #for idx in large_force_idx_l: 00153 # d_fc_large = delta_fc_l[idx] 00154 # delta_fc_large_mag += (d_fc_large.T * d_fc_large)[0,0] 00155 00156 delta_fc_mat_l = [P4_i*K_j for P4_i in P4_l] 00157 # now multiplying each matrix in delta_fc_mat_l with delta_phi 00158 # will give us delta_fc_l 00159 00160 quad_m = np.matrix(np.zeros(K_j.shape)) 00161 lin_m = np.matrix(np.zeros(K_j.shape[0])) 00162 00163 for idx in force_idx_l: 00164 d_fc_large_mat = delta_fc_mat_l[idx] 00165 quad_m += d_fc_large_mat.T * d_fc_large_mat 00166 lin_m += -delta_fg * Rc_l[idx] * d_fc_large_mat 00167 00168 return quad_m, -2*lin_m 00169 00170 00171 # v - vector of joint angles (or equilibrium angles for Cody) 00172 # 2012-12-28 JHawke: Changed function definition to take explicit joint limits as an input rather than a kinematics object. 00173 def joint_limit_bounds(min_q, max_q, v): 00174 m = v.shape[0] 00175 00176 min_q = (np.matrix(min_q).T)[0:m] 00177 max_q = (np.matrix(max_q).T)[0:m] 00178 00179 delta_q_max = np.maximum(max_q - v, 0.) 00180 delta_q_min = np.minimum(min_q - v, 0.) 00181 00182 return delta_q_min, delta_q_max 00183 00184 # difference between theta and phi is not permitted to be larger than 00185 # max_diff or the current difference (whichever is greater) 00186 # theta, phi - mx1 np matrices 00187 def theta_phi_absolute_difference_bounds(theta, phi): 00188 max_diff = math.radians(15) # 15 degrees per joint 00189 fudge = math.radians(0.1) 00190 phi_min = theta - np.maximum(theta - fudge - phi, max_diff) 00191 phi_max = theta + np.maximum(phi + fudge - theta, max_diff) 00192 return phi_min-phi, phi_max-phi 00193 00194 ## Formulate the input parameters as a QP problem for the solver. 00195 # 2012-12-28 JHawke: Changed the function definition to take joint limit bounds as inputs rather than a kinematics object. 00196 def convert_to_qp(J_h, Jc_l, K_j, Kc_l, Rc_l, delta_f_min, 00197 delta_f_max, phi_curr, delta_x_g, f_n, q, 00198 min_q, max_q, jerk_opt_weight, max_force_mag): 00199 P0_l, P1, P2, P3, P4_l = P_matrices(J_h, K_j, Kc_l, Jc_l) 00200 D2, D3, D4, D5, D6 = D_matrices(delta_x_g, delta_f_min, K_j, Rc_l, 00201 P3, P4_l) 00202 m = K_j.shape[0] 00203 theta_curr = (np.matrix(q).T)[0:m] 00204 delta_theta_min, delta_theta_max = joint_limit_bounds(min_q, max_q, theta_curr) 00205 D7 = P2 * K_j 00206 00207 cost_quadratic_matrices = [] 00208 cost_linear_matrices = [] 00209 00210 # if some contact forces are above the allowed pushing 00211 # force, add quadratic terms to the cost function to 00212 # decrease this force. 00213 over_max = f_n.A1 > max_force_mag 00214 if over_max.any(): 00215 # at least one of the contact forces is over the maximum allowed 00216 idx_l = np.where(over_max)[0].tolist() 00217 00218 # len(idx_l) is used to normalize the weight 00219 # with the number of contacts - otherwise it 00220 # overpowered the other terms and moved quickly 00221 # May want to normalize so that don't use two 00222 # different for position or position+orientation 00223 if delta_x_g.shape[0] == 3: 00224 weight = 0.0005 / len(idx_l) 00225 elif delta_x_g.shape[0] == 6: 00226 weight = 0.005 / len(idx_l) 00227 00228 qmat, lmat = force_magnitude_cost_matrices(P4_l, K_j, idx_l, 00229 Rc_l, 0.10) 00230 cost_quadratic_matrices.append(qmat*weight) 00231 cost_linear_matrices.append(lmat*weight) 00232 00233 constraint_matrices = [D4, D5] 00234 constraint_vectors = [delta_f_max, D6] 00235 00236 K_j_t = K_j 00237 min_jerk_mat = min_jerk_quadratic_matrix(jerk_opt_weight, K_j_t) 00238 00239 cost_quadratic_matrices += [1. * D2, 1. * min_jerk_mat] 00240 cost_linear_matrices += [1. * D3] 00241 00242 # adding explicit contraint for joint limits. 00243 constraint_matrices.append(D7) 00244 constraint_vectors.append(delta_theta_max) 00245 constraint_matrices.append(-D7) 00246 constraint_vectors.append(-delta_theta_min) 00247 00248 # this section seems to have been added for PR2 00249 # should verify still makes sense for Cody 00250 delta_phi_min, delta_phi_max = joint_limit_bounds(min_q, max_q, phi_curr) 00251 delta_phi_min2, delta_phi_max2 = theta_phi_absolute_difference_bounds(np.matrix(q).T, phi_curr) 00252 00253 lb = np.maximum(delta_phi_min, delta_phi_min2) 00254 ub = np.minimum(delta_phi_max, delta_phi_max2) 00255 00256 max_per_joint_change = math.radians(0.5) 00257 lb = np.maximum(lb, -max_per_joint_change) 00258 ub = np.minimum(ub, max_per_joint_change) 00259 # end of section that seems to have been added for PR2 00260 00261 00262 # Allows JEP to go outside joint limits for 00263 # software simulated robot linkage 00264 # DEPRECATED 2013-01-13 J Hawke. NEVER allow the controller outside joint limits or why have them?! 00265 # if kinematics.arm_type == 'simulated': 00266 # lb = lb * 1000. 00267 # ub = ub * 1000. 00268 00269 return cost_quadratic_matrices, cost_linear_matrices, \ 00270 constraint_matrices, constraint_vectors, lb, ub 00271 00272 ## Formulate the input parameters as a QP problem for the solver. Now includes posture. 00273 # NB: The P/D matrices don't make a lot of sense without seeing the maths. 00274 # TODO: Fix this. 00275 def convert_to_qp_posture(J_h, Jc_l, K_j, Kc_l, Rc_l, delta_f_min, 00276 delta_f_max, phi_curr, delta_x_g, f_n, q, 00277 min_q, max_q, jerk_opt_weight, max_force_mag, delta_theta_des, 00278 posture_weight, position_weight, orient_weight, force_weight, 00279 force_reduction_goal): 00280 00281 ## Apply the pose weights: position & orientation. 00282 # These are sqrt'd as the quadratic term takes the form Jh^T * Jh, and the linear is delta_x_g * Jh 00283 delta_x_g[0:3] = delta_x_g[0:3]*np.sqrt(position_weight) 00284 delta_x_g[3:] = delta_x_g[3:]*np.sqrt(orient_weight) 00285 J_h[0:3] = J_h[0:3] * np.sqrt(position_weight) 00286 J_h[3:] = J_h[3:] * np.sqrt(orient_weight) 00287 00288 # Calculate various matrices. These are combinations of Jacobians/stiffness matrices and 00289 # match the maths in the derivation document. They're separated out so they're only 00290 # computed once. 00291 P0_l, P1, P2, P3, P4_l = P_matrices(J_h, K_j, Kc_l, Jc_l) 00292 D2, D3, D4, D5, D6 = D_matrices(delta_x_g, delta_f_min, K_j, Rc_l, 00293 P3, P4_l) 00294 00295 # D2 is the quadratic matrix for the cost function, D3 is the linear. 00296 # Cost = phi^t D2 phi + D3 phi 00297 00298 # POSE TERM: 00299 # Cost_pose = | delta_x_g - H * delta_phi |^2 00300 # = (phi^T * Q_pose * phi + L_pose * phi) 00301 # Q_pose = H^T H 00302 # L_pose = - 2 * delta_x_g^T * H) 00303 # where H = J_h * (J_c^T * K_c * J_c + K_j)^-1 * K_j 00304 # NB: The weight here is applied earlier to the Jacobian and desired goal as it's a weight on position/orientation, not joint angles. 00305 00306 Q_pose = D2 00307 L_pose = D3 00308 00309 # print "NEWD2******************" 00310 # print Q_pose 00311 # print "NEWD3 ***********" 00312 # print L_pose 00313 # 00314 # POSTURE TERM: 00315 # Cost_posture = posture_weight * | delta_theta_d - B * delta_phi |^2 00316 # Q_posture = B^T B 00317 # L_posture = -2 * delta_theta_d^T * B 00318 # B = (J_c^T K_c J_c + K_j)^-1 K_j ; P2 = (J_c^T K_c J_c + K_j)^-1 00319 # B = P2 K_j 00320 00321 B = P2 * K_j 00322 Q_posture = posture_weight * B.T * B # Quadratic matrix 00323 L_posture = posture_weight * (-2.0 * delta_theta_des.T * B) # Linear matrix 00324 00325 # D2 = Q_pose + Q_posture 00326 # D3 = L_pose + L_posture 00327 00328 00329 00330 00331 m = K_j.shape[0] 00332 theta_curr = (np.matrix(q).T)[0:m] 00333 delta_theta_min, delta_theta_max = joint_limit_bounds(min_q, max_q, theta_curr) 00334 D7 = P2 * K_j 00335 00336 00337 00338 00339 cost_quadratic_matrices = [] 00340 cost_linear_matrices = [] 00341 00342 00343 # FORCE REDUCTION TERM 00344 # if some contact forces are above the allowed pushing 00345 # force, add quadratic terms to the cost function to 00346 # decrease this force. 00347 over_max = f_n.A1 > max_force_mag 00348 if over_max.any(): 00349 # at least one of the contact forces is over the maximum allowed 00350 idx_l = np.where(over_max)[0].tolist() 00351 00352 # len(idx_l) is used to normalize the weight 00353 # with the number of contacts - otherwise it 00354 # overpowered the other terms and moved quickly 00355 # May want to normalize so that don't use two 00356 # different for position or position+orientation 00357 00358 weight = force_weight / len(idx_l) 00359 00360 # if delta_x_g.shape[0] == 3: 00361 # weight = 0.0005 / len(idx_l) 00362 # print "using position weight" 00363 # elif delta_x_g.shape[0] == 6: 00364 # weight = 0.005 / len(idx_l) 00365 00366 qmat, lmat = force_magnitude_cost_matrices(P4_l, K_j, idx_l, 00367 Rc_l, force_reduction_goal) 00368 cost_quadratic_matrices.append(qmat*weight) 00369 cost_linear_matrices.append(lmat*weight) 00370 00371 constraint_matrices = [D4, D5] 00372 constraint_vectors = [delta_f_max, D6] 00373 00374 K_j_t = K_j 00375 min_jerk_mat = min_jerk_quadratic_matrix(jerk_opt_weight, K_j_t) 00376 00377 # New matrices - merge Q_pose, Q_posture, etc 00378 cost_quadratic_matrices += [1. * Q_pose, 1. * Q_posture, 1. * min_jerk_mat] 00379 cost_linear_matrices += [1. * L_pose, 1. * L_posture] 00380 00381 # Original cost matrices 00382 # cost_quadratic_matrices += [1. * D2, 1. * min_jerk_mat] 00383 # cost_linear_matrices += [1. * D3] 00384 00385 # adding explicit contraint for joint limits. 00386 constraint_matrices.append(D7) 00387 constraint_vectors.append(delta_theta_max) 00388 constraint_matrices.append(-D7) 00389 constraint_vectors.append(-delta_theta_min) 00390 00391 # this section seems to have been added for PR2 00392 # should verify still makes sense for Cody 00393 delta_phi_min, delta_phi_max = joint_limit_bounds(min_q, max_q, phi_curr) 00394 delta_phi_min2, delta_phi_max2 = theta_phi_absolute_difference_bounds(np.matrix(q).T, phi_curr) 00395 00396 lb = np.maximum(delta_phi_min, delta_phi_min2) 00397 ub = np.minimum(delta_phi_max, delta_phi_max2) 00398 00399 max_per_joint_change = math.radians(0.5) 00400 lb = np.maximum(lb, -max_per_joint_change) 00401 ub = np.minimum(ub, max_per_joint_change) 00402 # end of section that seems to have been added for PR2 00403 00404 00405 # Allows JEP to go outside joint limits for 00406 # software simulated robot linkage 00407 # DEPRECATED 2013-01-13 J Hawke. NEVER allow the controller outside joint limits or why have them?! 00408 # Change the joint limits, not fudge the controller. 00409 # if kinematics.arm_type == 'simulated': 00410 # lb = lb * 1000. 00411 # ub = ub * 1000. 00412 00413 return cost_quadratic_matrices, cost_linear_matrices, \ 00414 constraint_matrices, constraint_vectors, lb, ub 00415 00416 00417 ## Set up and solve QP 00418 ## 00419 # In [3]: pp.QP? 00420 # Docstring: 00421 # QP: constructor for Quadratic Problem assignment 00422 # 1/2 x' H x + f' x -> min 00423 # subjected to 00424 # A x <= b 00425 # Aeq x = beq 00426 # lb <= x <= ub 00427 # 00428 # Examples of valid calls: 00429 # p = QP(H, f, <params as kwargs>) 00430 # p = QP(numpy.ones((3,3)), f=numpy.array([1,2,4]), <params as kwargs>) 00431 # p = QP(f=range(8)+15, H = numpy.diag(numpy.ones(8)), <params as kwargs>) 00432 # p = QP(H, f, A=A, Aeq=Aeq, b=b, beq=beq, lb=lb, ub=ub, <other params as kwargs>) 00433 # See also: /examples/qp_*.py 00434 # 00435 # INPUT: 00436 # H: size n x n matrix, symmetric, positive-definite 00437 # f: vector of length n 00438 # lb, ub: vectors of length n, some coords may be +/- inf 00439 # A: size m1 x n matrix, subjected to A * x <= b 00440 # Aeq: size m2 x n matrix, subjected to Aeq * x = beq 00441 # b, beq: vectors of lengths m1, m2 00442 # Alternatively to A/Aeq you can use Awhole matrix as it's described 00443 # in LP documentation (or both A, Aeq, Awhole) 00444 def solve_qp(cost_quadratic_matrices, cost_linear_matrices, 00445 constraint_matrices, constraint_vectors, lb, ub, 00446 debug_qp): 00447 total = np.zeros(cost_quadratic_matrices[0].shape) 00448 for cqm in cost_quadratic_matrices: 00449 total = total + cqm 00450 H = 2.0 * total 00451 # H is size m x m 00452 00453 total = np.zeros(cost_linear_matrices[0].shape) 00454 for clm in cost_linear_matrices: 00455 total = total + clm 00456 f = total.T 00457 # f is size 1 x m 00458 00459 A = np.concatenate(constraint_matrices) 00460 b = np.concatenate(constraint_vectors) 00461 00462 # iprint: do text output each iprint-th iteration You can 00463 # use iprint = 0 for final output only or iprint < 0 to 00464 # omit whole output In future warnings are intended to be 00465 # shown if iprint >= -1. 00466 if debug_qp: 00467 iprint_val = 1 00468 else: 00469 iprint_val = -1 00470 00471 # Result structure 00472 # http://openopt.org/OOFrameworkDoc 00473 # r = p.solve(nameOfSolver) 00474 # >>> dir(r) 00475 # ['__doc__', '__module__', 'advanced', 'elapsed', 00476 # 'evals', 'ff', 'isFeasible', 'istop', 'iterValues', 00477 # 'msg', 'rf', 'solverInfo', 'stopcase', 'xf'] 00478 # 00479 00480 opt_error = False 00481 feasible = None 00482 delta_phi_zero = np.matrix(np.zeros(lb.shape)) 00483 # this is Marc's fix for some cases in simulation when the arm 00484 # gets stuck and keeps setting delta_phi=0. Advait also saw 00485 # this problem in one case (Aug 23, 2011), and copied the next 00486 # line from the forked_simulation_files folder. 00487 #delta_phi_zero = np.matrix(np.random.normal(0.0, 0.003, lb.shape)) 00488 #delta_phi_zero = np.matrix(np.random.normal(0.0, 0.01, lb.shape)) 00489 00490 try: 00491 qp = pp.QP(H, f, A=A, b=b, lb=lb, ub=ub) 00492 qp.solve('cvxopt_qp', iprint = iprint_val) 00493 delta_phi_opt = np.matrix(qp.xf).T 00494 val_opt = qp.ff 00495 feasible = qp.isFeasible 00496 00497 if not feasible: 00498 print '=====================================' 00499 print 'QP did not find a feasible solution.' 00500 print '=====================================' 00501 opt_error = True 00502 delta_phi_opt = delta_phi_zero 00503 00504 if np.isnan(delta_phi_opt.sum()): 00505 print '*****************************************************' 00506 print 'ERROR: QP FAILED TO FIND A SOLUTION AND RETURNED NaN(s)' 00507 print '*****************************************************' 00508 00509 if qp.stopcase == 0: 00510 print 'maxIter, maxFuncEvals, maxTime or maxCPUTime have been exceeded, or the situation is unclear somehow else' 00511 delta_phi_opt = delta_phi_zero 00512 00513 if qp.stopcase == -1: 00514 print 'solver failed to solve the problem' 00515 delta_phi_opt = delta_phi_zero 00516 00517 except ValueError as inst: 00518 opt_error = True 00519 print type(inst) # the exception instance 00520 print inst.args # arguments stored in .args 00521 print inst # __str__ allows args to printed directly 00522 print "ValueError raised by OpenOpt and/or CVXOPT" 00523 print "Setting new equilibrium angles to be same as old." 00524 print "delta_phi = 0" 00525 print "phi[t+1] = phi[t]" 00526 delta_phi_opt = delta_phi_zero 00527 val_opt = np.nan 00528 00529 return delta_phi_opt, opt_error, feasible 00530 00531 00532 00533 00534 00535 00536 #---------- OLD functions: probably WON'T work anymore ---------------- 00537 00538 ## Returns unit length vectors, each of which corresponds with 00539 ## a face of a polytope. 00540 ## 00541 ## For now, use a regular polygon to ensure that the 00542 ## expected change is not too high. This will likely result 00543 ## in a lack of a feasible solution in some situations, 00544 ## such as when a very high force is encountered. 00545 #def polytope_faces(self, n_faces): 00546 # if n_faces <= 6: 00547 # polytope_faces = rp.cube_faces #6 sided polytope 00548 # elif n_faces <= 8: 00549 # polytope_faces = rp.octahedron_faces #6 sided polytope 00550 # elif n_faces <= 12: 00551 # polytope_faces = rp.dodecahedron_faces #12 sided polytope 00552 # elif n_faces <= 20: 00553 # polytope_faces = rp.icosahedron_faces #20 sided polytope 00554 # else: 00555 # # should precompute this a single time... 00556 # polytope_faces = None 00557 # for num_faces, faces in self.spheres: 00558 # if (n_faces <= num_faces) and (polytope_faces is None): 00559 # polytope_faces = faces 00560 # if polytope_faces is None: 00561 # # requested number of sides is greater than our 00562 # # biggest tessellation, so use the biggest 00563 # num_faces, faces = self.spheres[-1] 00564 # polytope_faces = faces 00565 # 00566 # num_faces, tmp = polytope_faces.shape 00567 # 00568 # return polytope_faces, num_faces 00569 00570 00571 00572