diff --git a/scripts/hpp_wholebody_motion/end_effector/bezier_constrained.py b/scripts/hpp_wholebody_motion/end_effector/bezier_constrained.py new file mode 100644 index 0000000000000000000000000000000000000000..87829acee38b0f40e51bc6546876d6263c346b42 --- /dev/null +++ b/scripts/hpp_wholebody_motion/end_effector/bezier_constrained.py @@ -0,0 +1,452 @@ +import hpp_wholebody_motion.config as cfg +import time +import os +from hpp_wholebody_motion.utils.polyBezier import * +import pinocchio as se3 +from pinocchio import SE3 +from pinocchio.utils import * +import numpy.linalg +from locomote import WrenchCone,SOC6,ContactSequenceHumanoid +import numpy as np +from numpy import array, zeros +from numpy.linalg import norm +from scipy.spatial import ConvexHull +from tools.disp_bezier import * +import hpp_spline +import hpp_bezier_com_traj as bezier_com +from hpp_wholebody_motion.utils import trajectories +import math +from tools.disp_bezier import * +import eigenpy +import quadprog +from numpy import array, dot, vstack, hstack, asmatrix, identity +import bezier_predef +import limb_rrt +eigenpy.switchToNumpyArray() + + +VERBOSE = True +DISPLAY = True +DISPLAY_RRT = True +DISPLAY_CONSTRAINTS = True + + + +#min (1/2)x' P x + q' x +#subject to G x <= h +#subject to C x = d +def quadprog_solve_qp(P, q, G=None, h=None, C=None, d=None): + #~ qp_G = .5 * (P + P.T) # make sure P is symmetric + qp_G = .5 * (P + P.T) # make sure P is symmetric + qp_a = -q + if C is not None: + if G is not None: + qp_C = -vstack([C, G]).T + qp_b = -hstack([d, h]) + else: + qp_C = -C.transpose() + qp_b = -d + meq = C.shape[0] + else: # no equality constraint + qp_C = -G.T + qp_b = -h + meq = 0 + return quadprog.solve_qp(qp_G, qp_a, qp_C, qp_b, meq)[0] + + +def writearray(f, a): + for i in range(a.shape[0]): + line = "" + for j in range(a.shape[1]-1): + line += str(a[i][j]) + " " + line+= str(a[i][-1])+"\n" + f.write(line) + f.write("\n") + +def saveProblem(pDef): + f = open("test","w") + # degree totaltime flag + f.write(str(pDef.degree)+"\n") + f.write(str(pDef.totalTime)+"\n") + f.write(str(int(pDef.flag))+"\n") + f.write("\n") + writearray(f, pDef.start) + writearray(f, pDef.end) + writearray(f, pDef.splits) + i = 0 + while(True): + try: + ineq = pDef.inequality(i) + writearray(f, ineq.A) + writearray(f, ineq.b) + i+=1 + except: + f.close() + return + f.write() + f.close() + + +def toRotationMatrix(q): + """ + Returns a (3*3) array (rotation matrix) + representing the same rotation as the (normalized) quaternion. + """ + rm=zeros((3,3)) + rm[0,0]=1-2*(q[2]**2+q[3]**2) + rm[0,1]=2*q[1]*q[2]-2*q[0]*q[3] + rm[0,2]=2*q[1]*q[3]+2*q[0]*q[2] + rm[1,0]=2*q[1]*q[2]+2*q[0]*q[3] + rm[1,1]=1-2*(q[1]**2+q[3]**2) + rm[1,2]=2*q[2]*q[3]-2*q[0]*q[1] + rm[2,0]=2*q[1]*q[3]-2*q[0]*q[2] + rm[2,1]=2*q[2]*q[3]+2*q[0]*q[1] + rm[2,2]=1-2*(q[1]**2+q[2]**2) + return rm + + +def rot_quat_x(client,a): + x = [1.,0.,0.] + return client.rotationQuaternion(x,a) + +def rot_mat_x(client,a): + x = [1.,0.,0.] + q = client.rotationQuaternion(x,a) + return toRotationMatrix(q) + + + +def display_box(viewer,client,a,b,y,z): + ar_a = array(a) + ar_b = array(b) + x_len = norm(ar_b - ar_a) + x_pos = ar_a + (ar_b - ar_a) / 2 + rootName = "constraints_" + list = viewer.client.gui.getNodeList() + i=0 + name = rootName + while list.count(name) > 0: + name=rootName+"_"+str(i) + i+=1 + + viewer.client.gui.addBox(name,x_len/ 2,y*x_len,z*x_len, [0.,1.,0.,0.3]) + config = x_pos.tolist()+rot_quat_x(client, ((ar_b - ar_a) / x_len).tolist() ) + viewer.client.gui.applyConfiguration(name,config) + viewer.client.gui.addToGroup(name,viewer.sceneName) + viewer.client.gui.refresh() + + +def to_ineq(client,a,b,y_r,z_r): + a_r = array(a); b_r = array(b); + normba = norm(b_r - a_r) + x_dir = (b_r - a_r) / normba + x_pos = a_r + (b_r - a_r) / 2 + + x = normba / 2. + y = y_r * normba; + z = z_r * normba; + + points = [ [x,-y,z], [x,-y,z], [-x,-y,z], [x,-y,-z], [x,y,-z], [x,y,z], [-x,y,z], [-x,y,-z] ] + #transform + rot = rot_mat_x(client,x_dir.tolist()) + points = [rot.dot(array(el)) + x_pos for el in points] + ineq = ConvexHull(points).equations + return ineq[:,:-1],-ineq[:,-1] + + + +##############################################" CALCUL ##################################"" + +# a et b sont les extremites du rrt +# y le poids initial alloue a la largeur (si la distance ab vaut 1, initialement la largeur vaut y) +# z le poids initial alloue a la hauteur (si la distance ab vaut 1, initialement la hauteur vaut y) +# sizeObject dimension de securite de l'effecteur +def large_col_free_box(client,a,b,y = 0.3 ,z = 0.3, sizeObject = 0.05, margin = 0.): + # margin distance is not so good, better expand and then reduce box + # d abord o nessaie de trouver une boite sans collsion + collision = True + a_r = array(a); b_r = array(b); y_r = y; z_r = z + x_dir = (b_r - a_r) / norm(b_r - a_r) + distance = 0 + maxiter = 100 + while(collision and maxiter > 0): + maxiter = maxiter -1 + distance = client.isBoxAroundAxisCollisionFree(a_r.tolist(),b_r.tolist(),[y_r,z_r],margin) + collision = not distance > 0 + if(collision): + y_r = y_r* 0.5; z_r = z_r* 0.5 #todo handle penetration to be smarter + if collision: + print "failed" + return -1 + # now for the largest box possible + else: + maxiter = 100 + while(not collision and distance > 0.01 and maxiter > 0): + maxiter = maxiter - 1 + #find meaning of distance + x_len = norm(b_r - a_r) + x_dir = (b_r - a_r) / x_len + scale = x_len + (distance) /(2* x_len) + x_pos = a_r + (b_r - a_r) / 2 + tmp_a_r = (x_pos - x_dir * scale * x_len / 2.) + tmp_b_r = (x_pos + x_dir * scale * x_len / 2.) + distance2 = client.isBoxAroundAxisCollisionFree(tmp_a_r.tolist(),tmp_b_r.tolist(),[y_r,z_r],margin) + collision = not distance2 > 0 + if not collision: + break + else: + if abs(distance2 - distance) < 0.01 or distance2 > distance: + break + a_r = tmp_a_r[:] + b_r = tmp_b_r[:] + distance = distance2 + # now we have reached maximum uniform scaling, so we play a bit along each axis. + eps = 0.05 + + + + maxiter = 20 + collision = False + while(not collision and maxiter>0): + maxiter = maxiter -1; + # start with b + tmp_b_r = b_r + x_dir * eps + # adapt scaling of y and z + x_len = norm(b_r - a_r) + tmp_y_r = (x_len * y_r) / (x_len + eps) + tmp_z_r = (x_len * z_r) / (x_len + eps) + distance = client.isBoxAroundAxisCollisionFree(a_r.tolist(),tmp_b_r.tolist(),[tmp_y_r,tmp_z_r],margin) + collision = not distance > 0 + if collision: + break + else: + b_r = tmp_b_r[:] + y_r = tmp_y_r + z_r = tmp_z_r + maxiter = 20 + collision = False + while(not collision and maxiter>0): + maxiter = maxiter -1; + # start with a + tmp_a_r = a_r - x_dir * eps + x_len = norm(b_r - a_r) + tmp_y_r = (x_len * y_r) / (x_len + eps) + tmp_z_r = (x_len * z_r) / (x_len + eps) + distance = client.isBoxAroundAxisCollisionFree(tmp_a_r.tolist(),b_r.tolist(),[tmp_y_r,tmp_z_r],margin) + collision = not distance > 0 + if collision: + break + else: + a_r = tmp_a_r[:] + y_r = tmp_y_r + z_r = tmp_z_r + + + maxiter = 50 + collision = False + while(not collision and maxiter>0): + maxiter = maxiter -1; + # start with a + tmp_y_r = y_r + y_r * 0.05 + distance = client.isBoxAroundAxisCollisionFree(a_r.tolist(),b_r.tolist(),[tmp_y_r,z_r],margin) + collision = not distance > 0 + if collision: + break + else: + y_r = tmp_y_r + + maxiter = 50 + collision = False + while(not collision and maxiter>0): + maxiter = maxiter -1; + # start with a + tmp_z_r = z_r + z_r * 0.05 + distance = client.isBoxAroundAxisCollisionFree(a_r.tolist(),b_r.tolist(),[tmp_y_r,z_r],margin) + collision = not distance > 0 + if collision: + break + else: + z_r = tmp_z_r + + #removing offset + + a_r = (a_r + x_dir*sizeObject/2.).tolist() + b_r = (b_r - x_dir*sizeObject/2.).tolist() + + return (a_r, b_r, y_r, z_r), to_ineq(client,a_r, b_r, y_r, z_r) + + +def computeInequalitiesAroundLine(fullBody,p_from,p_to,viewer): + (a, b, y, z),(H,h) = large_col_free_box(fullBody.clientRbprm.rbprm,p_from,p_to) + if DISPLAY_CONSTRAINTS : + display_box(viewer,fullBody.clientRbprm.rbprm,a,b,y,z) + return H,h.reshape(-1,1) + +def effPosFromConfig(fullBody,q,eeName): + fullBody.setCurrentConfig(q) + p = fullBody.getJointPosition(eeName) + return p[0:3] + +def computeProblemConstraints(pData,fullBody,pathId,t,eeName,viewer): + pDef = hpp_spline.problemDefinition() + # set up problem definition : + pDef.flag = int(hpp_spline.constraint_flag.INIT_POS) | int(hpp_spline.constraint_flag.INIT_VEL) |int(hpp_spline.constraint_flag.INIT_ACC)|int(hpp_spline.constraint_flag.INIT_JERK) |int(hpp_spline.constraint_flag.END_POS) | int(hpp_spline.constraint_flag.END_VEL) |int(hpp_spline.constraint_flag.END_ACC)|int(hpp_spline.constraint_flag.END_JERK) + pDef.costFlag = hpp_spline.derivative_flag.VELOCITY + pDef.start = pData.c0_ + pDef.end = pData.c1_ + curveConstraints = hpp_spline.curve_constraints() + curve_constraints.init_vel = pData.dc0_ + curve_constraints.init_acc = pData.ddc0_ + curve_constraints.init_jerk = pData.j0_ + curve_constraints.end_vel = pData.dc1_ + curve_constraints.end_acc = pData.ddc1_ + curve_constraints.end_jerk = pData.j1_ + pDef.curveConstraints = curveConstraints + # get all the waypoints from the limb-rrt + wps,t_norm = fullBody.client.problem.getWaypoints(pathId) + # approximate the switching times (infos from limb-rrt) + if len(t_norm)>2 : + splits=[] + for i in range(1,len(t_norm)-1): + ti = t_norm[i]*t + if ti > t: + ti = t + splits+= [ti] + if len(splits) > 1: + if splits[-1] == splits[-2] : + print "Error in bezier_constrained : two adjacent constrained have the same switch time !!" + pDef.splits = np.array([splits]) + if VERBOSE: + print "number of switch between constraints : ",len(splits) + print "splits timings : ",splits + else : + if VERBOSE : + print "Only one constraint set for the whole trajectory." + # compute constraints around each line of the limb-rrt solution : + q_from = wps[0] + p_from = effPosFromConfig(fullBody,q_from,eeName) + for i in range(1,len(wps)): + q_to = wps[i] + p_to = effPosFromConfig(fullBody,q_to,eeName) + A,b = computeInequalitiesAroundLine(fullBody,p_from,p_to,viewer) + if VERBOSE : + print "Inequalities computed." + pDef.addInequality(A,b) + p_from = p_to[::] + + return pDef + + +def generateConstrainedBezierTraj(time_interval,placement_init,placement_end,q_t,predefTraj,phase_previous,phase,phase_next,fullBody,phaseId,eeName,viewer): + t_total = time_interval[1]-time_interval[0] + predef_curves = predefTraj.curves + bezier_takeoff = predef_curves.curves[predef_curves.idFirstNonZero()] + bezier_landing = predef_curves.curves[predef_curves.idLastNonZero()] + id_middle = int(math.floor(len(predef_curves.curves)/2.)) + bezier_mid_predef= predef_curves.curves[id_middle] + pos_init = bezier_takeoff(bezier_takeoff.max()) + pos_end = bezier_landing(0) + if VERBOSE : + print "bezier takeoff end : ",pos_init + print "bezier landing init : ",pos_end + t_begin = predef_curves.times[id_middle] + t_middle = bezier_mid_predef.max() + t_end = t_begin + t_middle + if VERBOSE : + print "t begin : ",t_begin + print "t end : ",t_end + q_init = q_t[int(t_begin/cfg.IK_dt)] + q_end = q_t[int(t_end/cfg.IK_dt)] + + # compute limb-rrt path : + pathId = limb_rrt.generateLimbRRTPath(q_init,q_end,phase_previous,phase,phase_next,fullBody,phaseId) + + if viewer and cfg.DISPLAY_FEET_TRAJ and DISPLAY_RRT: + from hpp.gepetto import PathPlayer + pp = PathPlayer (viewer) + pp.displayPath(pathId,jointName=fullBody.getLinkNames(eeName)[0]) + + # compute constraints for the end effector trajectories : + pData = bezier_com.ProblemData() + pData.c0_ = bezier_takeoff(bezier_takeoff.max()) + pData.dc0_ = bezier_takeoff.derivate(bezier_takeoff.max(),1) + pData.ddc0_ = bezier_takeoff.derivate(bezier_takeoff.max(),2) + pData.j0_ = bezier_takeoff.derivate(bezier_takeoff.max(),3) + pData.c1_ = bezier_landing(0) + pData.dc1_ = bezier_landing.derivate(0,1) + pData.ddc1_ = bezier_landing.derivate(0,2) + pData.j1_ = bezier_landing.derivate(0,3) + pData.constraints_.flag_ = bezier_com.ConstraintFlag.INIT_POS | bezier_com.ConstraintFlag.INIT_VEL | bezier_com.ConstraintFlag.INIT_ACC | bezier_com.ConstraintFlag.END_ACC | bezier_com.ConstraintFlag.END_VEL | bezier_com.ConstraintFlag.END_POS | bezier_com.ConstraintFlag.INIT_JERK | bezier_com.ConstraintFlag.END_JERK + + + # now compute additional inequalities around the rrt path : + pDef = computeProblemConstraints(pData,fullBody,pathId,t_middle,eeName,viewer) + solved = False + # loop and increase the number of variable control point until a solution is found + flagData = pData.constraints_.flag_ + flags = [bezier_com.ConstraintFlag.ONE_FREE_VAR,None, bezier_com.ConstraintFlag.THREE_FREE_VAR,None,bezier_com.ConstraintFlag.FIVE_FREE_VAR] + numVars = 1 + while not solved and numVars <=5: + pData.constraints_.flag_ = flagData | flags[numVars-1] + ineqEff = bezier_com.computeEndEffectorConstraints(pData,t_middle) + Hg = bezier_com.computeEndEffectorCost(pData,t_middle) + res = bezier_com.computeEndEffector(pData,t_middle) #only used for comparison/debug ? + bezier_unconstrained = res.c_of_t + pDef.degree = bezier_unconstrained.degree + ineqConstraints = hpp_spline.generate_problem(pDef) + + # _ prefix = previous notation (in bezier_com_traj) + # min x' H x + 2 g' x + # subject to A*x <= b + _A = np.vstack([ineqEff.A,ineqConstraints.A]) + _b = np.vstack([ineqEff.b,ineqConstraints.b]) + _H = Hg.A + _g = Hg.b + #_H = ineqConstraints.cost.A + #_g = ineqConstraints.cost.b + + # quadprog notation : + #min (1/2)x' P x + q' x + #subject to G x <= h + #subject to C x = d + G = _A + h = _b.reshape((-1)) + P = _H * 2. + q = (_g *2.).flatten() + try : + if VERBOSE: + print "try to solve quadprog with "+str(numVars)+" variable" + res = quadprog_solve_qp(P,q,G,h) + solved = True + except ValueError: + if VERBOSE: + print "Failed." + numVars += 2 + if solved : + if VERBOSE : + print "Succeed." + else : + print "Constrained End effector Bezier method failed for all numbers of variables control points." + # TODO : what to do here ?? + + # retrieve the result of quadprog and create a bezier curve : + vars = np.split(res,numVars) + wps = bezier_unconstrained.waypoints() + id_firstVar = 4 + i=id_firstVar + for x in vars: + wps[:,i] = x + i +=1 + + bezier_middle = hpp_spline.bezier(wps,t_middle) + # create concatenation with takeoff/landing + curves = [] + curves.append(bezier_takeoff) + curves.append(bezier_middle) + curves.append(bezier_landing) + pBezier = PolyBezier(curves) + if VERBOSE : + print "time interval = ",time_interval[1]-time_interval[0] + print "polybezier length = ",pBezier.max() + ref_traj = trajectories.BezierTrajectory(pBezier,placement_init,placement_end,time_interval) + return ref_traj \ No newline at end of file