The next thing I wanted to accomplish was being able to translate and rotate the body while keeping the feet planted. For now this will simple enable the robot to wiggle around and “dance”, but eventually these movements will be necessary inputs for traversing uneven terrain.
The concept for this is pretty straightforward. If i want the body to translate right while keeping the feet planted, you simply move all the feet to the left relative to the body:
newFootPositionX = initialFootPositionX - translateX newFootPositionY = initialFootPositionY - translateY newFootPositionZ = initialFootPositionZ - translateZ
I’m subtracting the translation because the feet are moving opposite the direction i want the body to go in. ‘translateY’ could be an input from your controller. I using the Bioloid Commander library for easy access to the Commander controller inputs.
Rotations aren’t quite as simple. The concept is to take an angular input, and provide the X,Y,Z offsets necessary for each leg to create that body rotation. A perfect job for the rotation matrix!
Here is some good background info on rotation matrices. Its not nearly as hard as it looks. You simply apply one rotation matrix per axis of rotation. I wanted to be able to rotate on all three axis so i strung all three together.
(X,Y,Z)rotated = (X,Y,Z)initial*Rx*Ry*Rz
The angle pheta in each matrix is the input rotation angle for that axis. You do need some sort of software to crunch this up into closed form equations. I used Matlab which can do the symbolic matrix multiplication. Once I got my equations i was able to calculate my (X,Y,Z)rotated offsets and add them into the equations above to get:
newFootPositionX = initialFootPositionX - translateX - rotatedX newFootPositionY = initialFootPositionY - translateY - rotatedY newFootPositionZ = initialFootPositionZ - translateZ - rotatedZ
This *almost* works worked perfectly. The issue is that my corner legs were behaving funny. That i realized was because my math wasn’t taking into account their mounting angle. Basically each corner points 45 degrees out. I need to rotate all my coordinates for these legs and there’s no better tool than… the rotation matrix! This time its easier because this is a single axis rotation so there is only one matrix, Rz.
(X,Y,Z)coxaCorrected = (X,Y,Z)newFootPosition*Rz
Note that the 45deg input is the angle for the front right leg. The right rear leg would be 135deg, the left rear would be 225deg, and the upper left would be 315deg as you go around the clock.
Here’s the code:
/********************************************************************************************************** fodyFK() Calculates necessary foot position (leg space) to acheive commanded body rotations, translations, and gait inputs ***********************************************************************************************************/ void bodyFK(){ float sinRotX, cosRotX, sinRotY, cosRotY, sinRotZ, cosRotZ; int totalX, totalY, totalZ; int tempFootPosX[6], tempFootPosY[6], tempFootPosZ[6]; int bodyRotOffsetX[6], bodyRotOffsetY[6], bodyRotOffsetZ[6]; sinRotX = sin(radians(-commanderInput.bodyRotX)); cosRotX = cos(radians(-commanderInput.bodyRotX)); sinRotY = sin(radians(-commanderInput.bodyRotY)); cosRotY = cos(radians(-commanderInput.bodyRotY)); sinRotZ = sin(radians(-commanderInput.bodyRotZ)); cosRotZ = cos(radians(-commanderInput.bodyRotZ)); for( int legNum=0; legNum<6; legNum++){ totalX = leg[legNum].initialFootPos.x + leg[legNum].legBasePos.x; totalY = leg[legNum].initialFootPos.y + leg[legNum].legBasePos.y; totalZ = leg[legNum].initialFootPos.z + leg[legNum].legBasePos.z; bodyRotOffsetX[legNum] = round(( totalY*cosRotY*sinRotZ + totalY*cosRotZ*sinRotX*sinRotY + totalX*cosRotZ*cosRotY - totalX*sinRotZ*sinRotX*sinRotY - totalZ*cosRotX*sinRotY) - totalX); bodyRotOffsetY[legNum] = round( totalY*cosRotX*cosRotZ - totalX*cosRotX*sinRotZ + totalZ*sinRotX - totalY); bodyRotOffsetZ[legNum] = round(( totalY*sinRotZ*sinRotY - totalY*cosRotZ*cosRotY*sinRotX + totalX*cosRotZ*sinRotY + totalX*cosRotY*sinRotZ*sinRotX + totalZ*cosRotX*cosRotY) - totalZ); // Calculated foot positions to acheive xlation/rotation input. Not coxa mounting angle corrected tempFootPosX[legNum] = leg[legNum].initialFootPos.x + bodyRotOffsetX[legNum] - commanderInput.bodyTransX + leg[legNum].footPos.x; tempFootPosY[legNum] = leg[legNum].initialFootPos.y + bodyRotOffsetY[legNum] - commanderInput.bodyTransY + leg[legNum].footPos.y; tempFootPosZ[legNum] = leg[legNum].initialFootPos.z + bodyRotOffsetZ[legNum] - commanderInput.bodyTransZ + leg[legNum].footPos.z; } // Rotates X,Y about coxa to compensate for coxa mounting angles. leg[0].footPosCalc.x = round( tempFootPosY[0]*cos(radians(COXA_ANGLE)) - tempFootPosX[0]*sin(radians(COXA_ANGLE)) ); leg[0].footPosCalc.y = round( tempFootPosY[0]*sin(radians(COXA_ANGLE)) + tempFootPosX[0]*cos(radians(COXA_ANGLE)) ); leg[0].footPosCalc.z = tempFootPosZ[0]; leg[1].footPosCalc.x = round( tempFootPosY[1]*cos(radians(COXA_ANGLE*2)) - tempFootPosX[1]*sin(radians(COXA_ANGLE*2)) ); leg[1].footPosCalc.y = round( tempFootPosY[1]*sin(radians(COXA_ANGLE*2)) + tempFootPosX[1]*cos(radians(COXA_ANGLE*2)) ); leg[1].footPosCalc.z = tempFootPosZ[1]; leg[2].footPosCalc.x = round( tempFootPosY[2]*cos(radians(COXA_ANGLE*3)) - tempFootPosX[2]*sin(radians(COXA_ANGLE*3)) ); leg[2].footPosCalc.y = round( tempFootPosY[2]*sin(radians(COXA_ANGLE*3)) + tempFootPosX[2]*cos(radians(COXA_ANGLE*3)) ); leg[2].footPosCalc.z = tempFootPosZ[2]; leg[3].footPosCalc.x = round( tempFootPosY[3]*cos(radians(COXA_ANGLE*5)) - tempFootPosX[3]*sin(radians(COXA_ANGLE*5)) ); leg[3].footPosCalc.y = round( tempFootPosY[3]*sin(radians(COXA_ANGLE*5)) + tempFootPosX[3]*cos(radians(COXA_ANGLE*5)) ); leg[3].footPosCalc.z = tempFootPosZ[3]; leg[4].footPosCalc.x = round( tempFootPosY[4]*cos(radians(COXA_ANGLE*6)) - tempFootPosX[4]*sin(radians(COXA_ANGLE*6)) ); leg[4].footPosCalc.y = round( tempFootPosY[4]*sin(radians(COXA_ANGLE*6)) + tempFootPosX[4]*cos(radians(COXA_ANGLE*6)) ); leg[4].footPosCalc.z = tempFootPosZ[4]; leg[5].footPosCalc.x = round( tempFootPosY[5]*cos(radians(COXA_ANGLE*7)) - tempFootPosX[5]*sin(radians(COXA_ANGLE*7)) ); leg[5].footPosCalc.y = round( tempFootPosY[5]*sin(radians(COXA_ANGLE*7)) + tempFootPosX[5]*cos(radians(COXA_ANGLE*7)) ); leg[5].footPosCalc.z = tempFootPosZ[5]; }
NOW it works!
<insert video here>
Head back to the beginning of Project B.E.T.H.
Hi Dan,
I have been attempting to use your core code for my home built hexapod. Although it has been a steep learning curve on vector rotation and IK solving I think I am finally getting somewhere. However I do have on question; in my testing I sometimes request a leg to go to a position that the IK can’t solve, ending up with an invalid acos() lookup and the leg goes off to a very strange angle. Did you do anything to prevent this or have you any thoughts on how to do it?
PS Thank-you for your code it has given me a big head sart.
Andrew
Cool, I’m glad it helped. You bring up a good point. There are multiple solutions to the IK equations and some can give unexpected results. The geometry of the legs and where you move them affects when it happens. I actually haven’t had it pop up with my code and hardware, but I do make sure i don’t ask the legs to reach too far.
Robust code should have checks in place to ensure out of bounds foot positions and servo angles are handled. For example the legIK() function should check for invalid foot position inputs and results and the driveServos() function should filter for out of bounds servo angles. I’m thinking about redoing a lot of this code to make it more robust for the future, hopefully I’ll take the time to do it right!
Thanks for this! Did you ever get around to making a video of it in action? I’ve modified this for a quadruped I have been working on, and its acting a bit unusually (but not as bad as I had expected). I was just wondering if maybe that’s how yours works, too. Thanks!
Eric
Hey Eric,
Sorry for the delay. Have you made any progress since? Yep, my lastest posts should be of videos of the three gaits. It works pretty well, I think. Take a look and let me know what you think.