Sorfware for Lexy ( Hexapode )
Dependencies: mbed BLE_API X_NUCLEO_IDB0XA1 MODSERIAL
LIB/Hexapod.cpp
- Committer:
- Essenceia
- Date:
- 2016-08-20
- Revision:
- 5:58acbceb4f9e
- Parent:
- 1:8bab9152933e
File content as of revision 5:58acbceb4f9e:
#include "Hexapod.h" #include "COM/LOGGER.h" using namespace std; static Logger *Ordi = Logger::Instance(); void hexapod::step (float dt) { int ii, modt; float absspeed, speedsgn, ssfrac, ssfrac2; float cycletime, xpos, ypos, zpos, target[3]; float legraise; float turn_dist, thtpos, rpos, maxdist; float dist, tht0, turn_step, speed_step; // clamp speed and turning, just in case // hexlock.lock(); if (speed > MAX_SPEED) speed = MAX_SPEED; if (speed < -MAX_SPEED) speed = -MAX_SPEED; if (turning > 1.0) turning = 1.0; if (turning < -1.0) turning = -1.0; if (standheight < -2.0) standheight = -2.0; if (standheight > 2.0) standheight = 2.0; // make sure speed doesnt change too rapidly speed_step = -(smoothspeed - speed);// speed - smoothspeed if (speed_step > SPEED_SLEW*dt) speed_step = SPEED_SLEW*dt; if (speed_step < -SPEED_SLEW*dt) speed_step = -SPEED_SLEW*dt; smoothspeed += speed_step; // cap the rate at which turning can change turn_step = -(smoothturning - turning); if (turn_step > TURN_SLEW*dt) turn_step = TURN_SLEW*dt; if (turn_step < -TURN_SLEW*dt) turn_step = -TURN_SLEW*dt; smoothturning += turn_step; //hexlock.unlock(); // to control walking, modify speed and turning absspeed = fabs(smoothspeed); speedsgn = 1.0; if (smoothspeed < 0.0) speedsgn = -1.0; // walking speed is influenced by leg sweep and movement speed legraise = 1.0; if (absspeed < 0.05) legraise = absspeed/0.05; if (absspeed < 0.2) { sweepmodifier = absspeed*0.8/0.2; speedmodifier = 0.25; } else if (absspeed < 0.8) { sweepmodifier = 0.8; speedmodifier = absspeed/sweepmodifier; } else if (absspeed < 1.0) { sweepmodifier = absspeed; speedmodifier = 1.0; } else { sweepmodifier = 1.0; speedmodifier = absspeed; } speedmodifier *= speedsgn; if (ssrunning) { sstime += dt; ssfrac = sstime/SS_DURATION; for (ii=0; ii<6; ii++) { // compute final target target[0] = legpos[ii][0]*1.5; if (ii == 0 || ii == 2) target[1] = 14.0; if (ii == 1) target[1] = 18.0; if (ii == 3 || ii == 5) target[1] = -14.0; if (ii == 4) target[1] = -18.0; target[0] = legpos1[ii][0]; target[1] = legpos1[ii][1]; target[2] = -10. + standheight; // given final target, turn into current target if (ssfrac < 0.5) { ssfrac2 = ssfrac*2.0; if (ii % 2 == 0) { target[0] = ssx0[ii] + ssfrac2*(target[0]-ssx0[ii]); target[1] = ssy0[ii] + ssfrac2*(target[1]-ssy0[ii]); target[2] = ssz0[ii] + ssfrac2*(target[2]-ssz0[ii]) + 2.0*pow(sin(3.1416*ssfrac2),2); } else { target[0] = ssx0[ii]; target[1] = ssy0[ii]; target[2] = ssz0[ii]; } } else { ssfrac2 = (ssfrac-0.5)*2.0; if (ii % 2 == 0) { // don't modify targets } else { target[0] = ssx0[ii] + ssfrac2*(target[0]-ssx0[ii]); target[1] = ssy0[ii] + ssfrac2*(target[1]-ssy0[ii]); target[2] = ssz0[ii] + ssfrac2*(target[2]-ssz0[ii]) +2.0*pow(sin(3.1416*ssfrac2),2); } } IKSolve(ii,target); } if (sstime > SS_DURATION) { ssrunning = false; sstime = 0.0; } } else { // based on current turning, compute turning math if (fabs(smoothturning) <= TURN_TOL)turn_dist = tan((1.0-TURN_TOL)*3.1416/2.0)*50.; else if (fabs(smoothturning) > TURN_TOL)turn_dist = tan((1.0-smoothturning)*3.1416/2.0)*50.; // compute dist between turn_dist and farthest leg - blindage pour eviter out of range maxdist = 0.0; for (ii=0; ii<6; ii++) { dist = sqrt(pow(legpos1[ii][0],2) + pow(legpos1[ii][1]-turn_dist,2)); if (dist > maxdist) maxdist = dist; } // each leg can only sweep so much, so use this farthest leg // to determine the angle of sweep that every leg must do maxsweep = 8.*sweepmodifier/maxdist; if (turn_dist < 0.0) maxsweep = -maxsweep;// maxsweep is the angle of sweep for every leg, in radians // increment dead-reckoning position // turning radius is "turn_dist" dr_ang += dt*speedmodifier*maxsweep*2.0*0.83775/fdf; if (dr_ang > PI) dr_ang -= 2.*PI; if (dr_ang < -PI) dr_ang += 2.*PI; dr_xpos += maxsweep*turn_dist*dt*speedmodifier*2.0*cos(dr_ang)*0.83775/fdf; dr_ypos += maxsweep*turn_dist*dt*speedmodifier*2.0*sin(dr_ang)*0.83775/fdf; // dr_ang has about 20% error, likely systematic // dr_xpos,dr_ypos have more like 5% error, also likely systematic // increment fake time - pour reset bezier sur le gait time += dt*speedmodifier; if (time > 1.0) time -= 1.0; if (time < 0.0) time += 1.0; // loop through each leg to figure out where it should be right now for (ii=0; ii<6; ii++) { // where is this leg in the cycle of stepping? // the 0.5*ii is to completely de-sync legs - pour avoir deux groupes de jambes distincs // the other part is to adjust it more cycletime = fmod(time + 0.5*ii + (legpos[ii][0]-legpos[0][0])*0.0125, 1.0); // use bezier curve to either be up or down // b2d_walk_down goes between +/- 0.83775 if (cycletime < fdf) b_walk_down.get_pos(cycletime/fdf, &thtpos, &zpos); else b_walk_up.get_pos((cycletime-fdf)/(1.-fdf), &thtpos, &zpos); // convert thtpos into angle? thtpos *= maxsweep; // convert rpos to xpos,ypos dist = sqrt(pow(legpos1[ii][0],2) + pow(legpos1[ii][1]-turn_dist,2)); tht0 = atan2(legpos1[ii][1]-turn_dist,legpos1[ii][0]); xpos = dist*cos(thtpos+tht0); ypos = turn_dist + dist*sin(thtpos+tht0); // set up the IK target target[0] = xpos; target[1] = ypos; target[2] = -10.0 + zpos*3.0*legraise + standheight; if (standheight < 0.0) target[2] -= 1.7*zpos*standheight; // perform IK solve IKSolve(ii,target); // TODO: add error handling if IK fails to converge } } setServoAngles(); } void hexapod::safeStand () { int ii; float fkangles[3], fkpos[3]; // initialize sstime = 0.0; ssrunning = true; // store FK leg positions for (ii=0; ii<6; ii++) { fkangles[0] = angle[ii*3+0]; fkangles[1] = angle[ii*3+1]; fkangles[2] = angle[ii*3+2]; FKSolve(ii,fkangles,fkpos); ssx0[ii] = fkpos[0]; ssy0[ii] = fkpos[1]; ssz0[ii] = fkpos[2]; } } hexapod::hexapod(bool debugflag) { int ii; debug = debugflag; // init constants for each leg length[0] = 7.53; // in cm length[1] = 10; length[2] = 12.97; //angles de decalage causer par la forme de la structure coxaangle=0.703310743; //deja en radians femurangle = 0; tibiaangle = 0.258856711; //angles maximums - maximums and minimums -coax femur tibia anglelb[0] = 46*DEGTORAD; angleub[0] = 134*DEGTORAD; anglelb[1] = 10*DEGTORAD; // 90-44 angleub[1] = 170*DEGTORAD; anglelb[2] = 10*DEGTORAD; angleub[2] = 160*DEGTORAD; for (ii=0; ii<3; ii++) { anglelb[ii] -= 90.*DEGTORAD; // because servos are straight at 90 degrees angleub[ii] -= 90.*DEGTORAD; } // root of leg in xyz legpos[0][0] = 4.52; legpos[0][1] = 4.958; legpos[0][2] = 2.1; legpos[1][0] = 0.0; legpos[1][1] = 6.38; legpos[1][2] = legpos[0][2]; // symmetry legpos[2][0] = -legpos[0][0]; legpos[2][1] = legpos[0][1]; legpos[2][2] = legpos[0][2]; legpos[3][0] = legpos[0][0]; legpos[3][1] = -legpos[0][1]; legpos[3][2] = legpos[0][2]; legpos[4][0] = legpos[1][0]; legpos[4][1] = -legpos[1][1]; legpos[4][2] = legpos[1][2]; legpos[5][0] = legpos[2][0]; legpos[5][1] = legpos[3][1]; legpos[5][2] = legpos[0][2]; // which way do the coxa servos point legang[0] = 45.0*DEGTORAD; legang[1] = 90.0*DEGTORAD; legang[2] = 135.*DEGTORAD; legang[3] =-45.*DEGTORAD; legang[4] =-90.0*DEGTORAD; legang[5] =-135.0*DEGTORAD; // default target for each leg for (ii=0; ii<6; ii++) { legpos1[ii][0] = legpos[ii][0] + 12.0 * cos(legang[ii]); legpos1[ii][1] = legpos[ii][1] + 12.0 * sin(legang[ii]); legpos1[ii][2] = -10.0; } // initialize bezier curve gait // goes from +-1 in x and 0 to 1 in z b_walk_up.add_point(-0.83775,0); b_walk_up.add_point(-1.11701,0); b_walk_up.add_point(-1.39626,0); b_walk_up.add_point(0,3.2); b_walk_up.add_point(1.39626,0); b_walk_up.add_point(1.11701,0); b_walk_up.add_point(0.83775,0); b_walk_down.add_point(0.83775,0); b_walk_down.add_point(-0.83775,0); //hexlock.lock(); speed = 0.0; turning = 0.0; standheight = 2.0; // hexlock.unlock(); smoothspeed = 0.0; time = 0.0; fdf = 0.50; dr_xpos = 0.0; dr_ypos = 0.0; dr_ang = 0.0; } /* void hexapod::setAngles () { int ii; for (ii=0; ii<6; ii++) { angle[ii*3] = (servoangle[ii*3]-150.)*DEGTORAD; angle[ii*3+1] = (servoangle[ii*3+1]-150.)*DEGTORAD; angle[ii*3+2] = (servoangle[ii*3+2]-150.)*DEGTORAD; } } */ void hexapod::setServoAngles () { int ii; for (ii=0; ii<6; ii++) { servoangle[ii*3] = angle[ii*3]*RADTODEG + 90.; servoangle[ii*3+1] = angle[ii*3+1]*RADTODEG + 90.; servoangle[ii*3+2] = angle[ii*3+2]*RADTODEG + 90.; } } // target is a float[3] giving absolute position // return whether or not the solver was successful bool hexapod::IKSolve (int leg, float *target) { int iter; bool converged; float diff; float targetr, targetz, targetang; float fkpos[3], fkangles[3], J[2][2], inv[2][2], delta[2], p[2]; float posr, posz, ang1, ang2, det; //1. convert absolute position to polar around leg root targetz = target[2] - legpos[leg][2]; targetr = sqrt(pow(target[0]-legpos[leg][0],2) + pow(target[1]-legpos[leg][1],2)); targetang = atan2(target[1]-legpos[leg][1],target[0]-legpos[leg][0]) - legang[leg]; // atan2 [-pi:pi] //2. easy part: can the coxa servo get to the right angle? if (targetang > angleub[0] || targetang < anglelb[0]) return false; // else, go ahead and set coxa servo. One out of three angles done! angle[leg*3] = targetang; //3. begin 2-joint IK solver using jacobian pseudo-inverse // whenever we call FKSolve, need to convert to polar coords around leg root fkangles[0] = angle[leg*3]; // already solved for //3.1 starting point is influenced by actual current point // but this makes it safer in case the leg has somehow gone out of bounds fkangles[1] = angle[leg*3+1]*0.5; fkangles[2] = angle[leg*3+2]*0.5; FKSolve(leg, fkangles, fkpos); posz = fkpos[2] - legpos[leg][2]; posr = sqrt(pow(fkpos[0]-legpos[leg][0],2) + pow(fkpos[1]-legpos[leg][1],2)); diff = sqrt(pow(targetr-posr,2) + pow(targetz-posz,2)); //3.2 ITERATE converged = false; for (iter=0; iter<MAXITER && !converged; iter++) { // compute jacobian p[0] = targetr - posr; p[1] = targetz - posz; ang1 = fkangles[1]-femurangle; ang2 = fkangles[2]-tibiaangle; J[0][0] = -length[1]*sin(ang1) - length[2]*sin(ang1+ang2); // dr/dang1 J[1][0] = -length[2]*sin(ang1+ang2); // dr/dang2 J[0][1] = length[1]*cos(ang1) + length[2]*cos(ang1+ang2); // dz/dang2 J[1][1] = length[2]*cos(ang1+ang2); // dz/dang2 // compute inverse det = 1.0/(J[0][0]*J[1][1]-J[0][1]*J[1][0]); inv[0][0] = J[1][1]*det; inv[1][0] = -J[1][0]*det; inv[0][1] = -J[0][1]*det; inv[1][1] = J[0][0]*det; delta[0] = p[0]*inv[0][0] + p[1]*inv[0][1]; delta[1] = p[0]*inv[1][0] + p[1]*inv[1][1]; fkangles[1] += delta[0]*0.5; fkangles[2] += delta[1]*0.5; // enforce bounds if (fkangles[1] >= angleub[1]) // maxium {fkangles[1] = angleub[1]-ANGEPS; if (debug) Ordi->log("ang1ub "+Ordi->log_itos(leg)+" " +Ordi->log_itos( angleub[1]) );} if (fkangles[1] <= anglelb[1]) // minimums {fkangles[1] = anglelb[1]+ANGEPS; if (debug) Ordi->log("ang1lb "+Ordi->log_itos(leg)+" " +Ordi->log_itos( anglelb[1]) );} if (fkangles[2] >= angleub[2]) // maxium {fkangles[2] = angleub[2]-ANGEPS; if (debug) Ordi->log("ang2ub "+Ordi->log_itos(leg)+" " +Ordi->log_itos( angleub[2]) );} if (fkangles[2] <= anglelb[2]) // minimums {fkangles[2] = anglelb[2]+ANGEPS; if (debug)Ordi->log("ang2lb "+Ordi->log_itos(leg)+" " +Ordi->log_itos( anglelb[2]) );} // FK FKSolve(leg, fkangles, fkpos); posz = fkpos[2] - legpos[leg][2]; posr = sqrt(pow(fkpos[0]-legpos[leg][0],2) + pow(fkpos[1]-legpos[leg][1],2)); // convergence criteria diff = sqrt(pow(targetr-posr,2) + pow(targetz-posz,2)); //cout << iter << " " << diff << " " << posr << " " << posz << endl; if (diff < TOLERANCE) converged = true; // 1 mm tolerance } // converged? if (converged) { angle[leg*3+1] = fkangles[1]; angle[leg*3+2] = fkangles[2]; Logger::Instance()->log("IK solver reussi a trouver solution"); } return converged; } // forward kinematics in absolute coordinate system // given a float[3] angles, compute position and put in float[3] pos // input angles are in radians and are offset properly void hexapod::FKSolve (int leg, float *angles, float *pos) { float r, ang0, ang1, ang2; //ajustement celon la structure ang0 = angles[0]+legang[leg]; ang1 = angles[1]-femurangle; ang2 = angles[2]-tibiaangle; // conversion en coordonnes x,y,z pour savoire en fonction des angles la position du pied en fct de la root de la pate r = length[0] + length[1]*cos(ang1) + length[2]*cos(ang1+ang2); pos[0] = legpos[leg][0] + r*cos(ang0); pos[1] = legpos[leg][1] + r*sin(ang0); pos[2] = legpos[leg][2] + length[1]*sin(ang1) + length[2]*sin(ang1+ang2); } void hexapod::stand () // mettre le pied a 10cm en face de la root de l'hexapode, le corp a hauteur 10 cm du sol { int ii; float target[3]; for (ii=0; ii<6; ii++) { target[0] = legpos[ii][0] + 10.0*cos(legang[ii]); target[1] = legpos[ii][1] + 10.0*sin(legang[ii]); target[2] = -10.0; IKSolve(ii,target); } setServoAngles(); } void hexapod::sit ()// mettre le pied a 10cm en face de la root de l'hexapode, le corp a hauteur 5 cm du sol { int ii; float target[3]; for (ii=0; ii<6; ii++) { target[0] = legpos[ii][0] + 10.0*cos(legang[ii]); target[1] = legpos[ii][1] + 10.0*sin(legang[ii]); target[2] = -5.0; IKSolve(ii,target); } setServoAngles(); }