One of the first thing I have printed is a new design for my 8DOF small quadruped robot. I like the embossed details on the hip pieces and the battery holder mech.

You can download the files from Pinshape or Thingiverse:

Let’s review how to we get Felix a 3D printed quadruped robot to walk. Start by downloading or cloning the code from GitHub (https://github.com/Traverso/Felix3D).

If you haven’t done it yet, get Node.js and install/setup Johnny-Five to communicate with your microcontroller/board.

Getting Felix to walk properly requires some tweaking. We will try to compensate in software for a number of small imperfections in hardware that eventually add up.

You have (or should had) already done a rough calibration of the servos and leg assembly. Now let’s test one leg at a time.

To increase precision get a sheet of graph paper, glue it to some cardboard to add stiffness. Place it between Felix legs like this:

Now copy/write your calibration offset to the index.js file

Run the index.js file. A reference to felix is injected to the REPL.
From the REPL you can call:

Update the values directly in the REPL and recalibrate until you get it right:

Now, control the home position of the leg:

Use the graph to check that the tip of the leg hit it’s mark: directly 110 millimeter beneath the hip joint.

Next, control the two furthest points of the step:

If the home position and the step-pose position are off, you can tweak the leg’s origin values:

Once you are satisfied with your angles offsets and your origin offset, remember to update the values in the index file. Repeat this steps for the next tree legs.

Now let’s try some walking. Press the “arrow up” key and Felix will start a creeping gait by running the forward method. Press the space bar to pause and resume the walk.

This method generates a walk cycle based on the current step properties, set the state and start running the scheduler.

The scheduler consumes an array of “frames”. The rate at which the frames are processed is controlled by the “speed” configuration value.

A frame consist of a command and a payload. Currently they are two types of frames, a control frame like:

{cmd:'loop',count:-1}

When the scheduler encounter this frame it loops back to the start of the scheduler and decrements the count value. If the count value reach zero the schedule continues with the next frame. For an infinite loop the count value can be set to -1.

When the scheduler encounter this frame, it will position each leg joint to the provided angles.

The generated walk cycle is a creeping gait (or static stable gait) where the body is moved forward at the same time that the stepping leg is being lifted.

An alternative (more stable, but slower cycle) can be implemented by shifting the center of mass first and then lifting the stepping leg.

To generate the cycle, a step is divided into four key-poses. From pose one to four the legs push the ground behind shifting the body along. In pose four the leg is lifted in a semicircle back to pose one.

A gait is described as an array of poses:

//expected leg order FR, FL, BR, BL
gait: [
[1,2,3,4],
[2,3,4,1],
[3,4,1,2],
[4,1,2,3]
]

Felix currently master two creep gaits, the “cat” and the “deer”. In the cat gait the sequence of lifted legs is:

front-right

back-left

front-left

back-right

In the deer gait the sequence is:

front-right

back-right

front-left

back-left

From one pose to the next the needed transition points are generated by two methods: “_linearTrajectory” and “_ellipticalTrajectory”.

Felix.prototype._linearTrajectory = function(line,
granularity,
skip_start_point){
var trajectory = [];
var granularity = granularity || 8;
//find the slope/delta
var delta_x = line.b.x - line.a.x;
var delta_y = line.b.y - line.a.y;
//calculate the distance between the two points
var distance = Math.sqrt( ((delta_x) * (delta_x)) +
((delta_y) * (delta_y)) );
if(distance == 0) return [];
//divide the line int the required number of points
//decrease the granularity one step to be able to include
//the end point
var skip = (skip_start_point)? 0:1;
var step_size = distance / (granularity - skip);
var c_step = (skip_start_point)? step_size:0;
for(var i=0;i < granularity;i++){
var inc = c_step / distance;
trajectory.push({
x:Math.round(line.a.x +
(inc * delta_x)),
y:Math.round(line.a.y +
(inc * delta_y))
});
c_step+= step_size;
}
return trajectory;
}
Felix.prototype._ellipticalTrajectory = function(arc,
granularity,
skip_start){
var trajectory = [];
granularity = granularity || 8;
//divide the angles int the required number of points
//decrease the granularity one step to be able to include
//the end point
var skip = (skip_start)? 0:1;
var step_size = (arc.end_angle - arc.start_angle) /
(granularity - skip);
var c_angle = arc.start_angle;
if(skip_start_point) c_angle+= step_size;
for(var i=0;i < granularity;i++){
var x = arc.origin.x + arc.radius.x *
Math.cos(Math.radians(c_angle));
var y = arc.origin.y + arc.radius.y *
Math.sin(Math.radians(c_angle));
trajectory.push({ x:Math.round(x),y:Math.round(y) });
c_angle+= step_size;
}
return trajectory;
}

The number of transition points in both methods are governed by the configuration variable “granularity”.

The points generated for the poses and transitions are then converted to angles using the Inverse kinematics method described in a previous post. This angles are added to the frame, and the frame is added to the scheduler.

For the next step is best not to have the thighs/femurs attached (to avoid the risk of striping the servos gears).

Edit the pin number in the script (legs.hip.pin, etc) to match your setup’s connections and run the script. This will set all servos to 90 degrees.

Now attach the thighs/femurs. The femurs should form a 90 degrees angle in relation to the body.

The shins should also form a 90 degrees angle, but be aware that the shin is slightly lifted as the angle should be from the tip of the shin to the femur (as seen in the graphic).

Due to the coarseness of the teeth on the servo horns the resulting angles might be off by a couple of degrees. To get it as close as possible edit the offset values in the script (legs.hip.offset, etc). For example if the front right femur is off by 2 degrees to the left set the offset value to legs.hip.offset = -2.

Run the script again and check (preferably with an angle ruler) that all segments are correctly positioned.

This is a short explanation of how to calculate the leg angles in our quadruped robot using basic trigonometry. If you don’t need the explanation but just want an example implementation scroll to the bottom to see the code sample.

As you can see in the graphic above, we want to find the angles A1 and A4:

A1 is the rotation angle of our first segment around the hip joint

A4 is the rotation angle of our second segment around the knee joint

What we know is L1, L2, P1 and P2 where:

L1 is the length of our femur

L2 is the length of our tibia

P1 is the origin point, or position of our hip (0,0)

P2 is the requested end position (or destination) for the feet

So, basically we want to how (much) does our hip and knee need to rotate in order for our feet to reach the requested end position.

We can calculate our knee angle using the law of cosines (https://en.wikipedia.org/wiki/Law_of_cosines). But to do so we need to know the length of the tree sides of the triangle L1, L2 & K.

So we start calculating our missing side K. We find the to segments H1 and H2 using the delta between our origin (P1) and our destination (P2):

int H1 = P2.x - P1.x; //delta on the x axis
int H2 = P2.y - P1.y; //delta on the y axis

//angle between L1 & L2 - the to segments, femur and tibia
float A1 = acos(( pow(L1,2) + pow(L2,2) - pow(K,2))/ (2 * L1 * L2));

Getting our hip angle is bit more convoluted. We need to find the angle A2 (no problem, having the tree sides is just more law of cosines), and A3 (another right-angled triangle – more Pythagoras).

//get the angle between the hypothenuse and the first segment (femur)
float A2 = acos( (pow(K,2) + pow(L1,2) - pow(L2,2)) / (2 * K * L1));

//get the angle between the hypothenuse and the x axis
float A3 = asin(H1/K);

Then we add this to angles and subtract the sum from 180 degrees and we are left with our hip angle.

float A4 = (PI / 2) - (A2 + A3);

Below you can see a function implemented for an Arduino sketch.

/**********************************************************
* Inverse Kinematic function for a two link planar system.
* Given the size of the two links an a desired position,
* it returns a pose struct with angles for both links
**********************************************************/
struct pose IK(int L1, int L2, struct point P2)
{
struct pose a;
struct point P1 = { HIP_ORIGIN_X, HIP_ORIGIN_Y }; //origin point (hip)

int H1 = P2.x – P1.x; //delta on the x axis
int H2 = P2.y – P1.y; //delta on the y axis

//this is the hypothenuse between the origin and the target
float K = sqrt( pow(H1,2) + pow(H2,2));

//the hypothenuse can not be larget the the sum of the segments
if( K > (L1 + L2 )) K = L1 + L2;

A contest on Pinshape (https://pinshape.com/contests/design-for-electronics-contest) got me to simplify the design of Felix and optimise it for 3D printing. The focus was on minimal support material, fast print time and optimal orientation on the print bed for best first layer adhesion. Other concerns where easy of assembly and sturdinnes of the final robot.

If you want to edit/customise the 3D model, you are free to copy my OnShape project using this link: https://cad.onshape.com/docume…

There ain’t much room for my current electronics setup. The servo connectors require a lot of space – even using angled pins. So I have ordered a feather board and a servo feather wing from Adafruit. The feather board has the added bonus that it can be controlled via Iphone/Android using Bluetooth.