/* Simple 3dof leg solver. X,Y,Z are the length from the Coxa rotate to the endpoint. */ ik_sol_t legIK(int X, int Y, int Z){ ik_sol_t ans; // first, make this a 2DOF problem... by solving coxa ans.coxa = radToServo(atan2(X,Y)); long trueX = sqrt(sq((long)X)+sq((long)Y)) - L_COXA; long im = sqrt(sq((long)trueX)+sq((long)Z)); // length of imaginary leg // get femur angle above horizon... float q1 = -atan2(Z,trueX); long d1 = sq(L_FEMUR)-sq(L_TIBIA)+sq(im); long d2 = 2*L_FEMUR*im; float q2 = acos((float)d1/(float)d2); ans.femur = radToServo(q1+q2); // and tibia angle from femur... d1 = sq(L_FEMUR)-sq(im)+sq(L_TIBIA); d2 = 2*L_TIBIA*L_FEMUR; ans.tibia = radToServo(acos((float)d1/(float)d2)-1.57); return ans; }
uint8_t doArmIK(boolean fCartesian, int sIKX, int sIKY, int sIKZ, int sIKGA) { int t; int sol0; uint8_t bRet = IKS_SUCCESS; // assume success #ifdef DEBUG if (g_fDebugOutput) { Serial.print("("); Serial.print(sIKX, DEC); Serial.print(","); Serial.print(sIKY, DEC); Serial.print(","); Serial.print(sIKZ, DEC); Serial.print(","); Serial.print(sIKGA, DEC); Serial.print(")="); } #endif if (fCartesian) { // first, make this a 2DOF problem... by solving base sol0 = radToServo(atan2(sIKX,sIKY)); // remove gripper offset from base t = sqrt(sq((long)sIKX)+sq((long)sIKY)); // BUGBUG... Not sure about G here #define G 30 sol0 -= radToServo(atan2((G/2)-G_OFFSET,t)); } else { // We are in cylindrical mode, probably simply set t to the y we passed in... t = sIKY; #ifdef DEBUG sol0 = 0; #endif } // convert to sIKX/sIKZ plane, remove wrist, prepare to solve other DOF float flGripRad = (float)(sIKGA)*3.14159/180.0; long trueX = t - (long)((float)WristLength*cos(flGripRad)); long trueZ = sIKZ - BaseHeight - (long)((float)WristLength*sin(flGripRad)); long im = sqrt(sq(trueX)+sq(trueZ)); // length of imaginary arm float q1 = atan2(trueZ,trueX); // angle between im and X axis long d1 = sq(ShoulderLength) - sq(ElbowLength) + sq((long)im); long d2 = 2*ShoulderLength*im; float q2 = acos((float)d1/float(d2)); q1 = q1 + q2; int sol1 = radToServo(q1-1.57); d1 = sq(ShoulderLength)-sq((long)im)+sq(ElbowLength); d2 = 2*ElbowLength*ShoulderLength; q2 = acos((float)d1/(float)d2); int sol2 = radToServo(3.14-q2); // solve for wrist rotate int sol3 = radToServo(3.2 + flGripRad - q1 - q2 ); #ifdef DEBUG if (g_fDebugOutput) { Serial.print("<"); Serial.print(sol0, DEC); Serial.print(","); Serial.print(trueX, DEC); Serial.print(","); Serial.print(trueZ, DEC); Serial.print(","); Serial.print(sol1, DEC); Serial.print(","); Serial.print(sol2, DEC); Serial.print(","); Serial.print(sol3, DEC); Serial.print(">"); } #endif // Lets calculate the actual servo values. if (fCartesian) { sBase = min(max(512 - sol0, BASE_MIN), BASE_MAX); } sShoulder = min(max(512 - sol1, SHOULDER_MIN), SHOULDER_MAX); // Magic Number 819??? sElbow = min(max(819 - sol2, SHOULDER_MIN), SHOULDER_MAX); #define Wrist_Offset 512 sWrist = min(max(Wrist_Offset + sol3, WRIST_MIN), WRIST_MAX); // Remember our current IK positions g_sIKX = sIKX; g_sIKY = sIKY; g_sIKZ = sIKZ; g_sIKGA = sIKGA; // Simple test im can not exceed the length of the Shoulder+Elbow joints... if (im > (ShoulderLength + ElbowLength)) { if (g_bIKStatus != IKS_ERROR) { #ifdef DEBUG if (g_fDebugOutput) { Serial.println("IK Error"); } #endif MSound(2, 50, 3000, 50, 3000); } bRet = IKS_ERROR; } else if(im > (ShoulderLength + ElbowLength-IK_FUDGE)) { if (g_bIKStatus != IKS_WARNING) { #ifdef DEBUG if (g_fDebugOutput) { Serial.println("IK Warning"); } #endif MSound(1, 75, 2500); } bRet = IKS_WARNING; } return bRet; }