Micro Quadcopter in OpenSCAD

by masterdezign in Workshop > 3D Printing

4948 Views, 36 Favorites, 0 Comments

Micro Quadcopter in OpenSCAD

Beatle-1 Quadcopter
04e.png

3D printing (known in the industry as additive manufacturing) is a process of
fabrication of a 3D object from a digital model. 3D printer prices have significantly dropped during past decade, so 3D printing is much more common nowadays. In this tutorial we will create a micro quadcopter frame to be 3D printed yourself.

Supplies

CAD design and fabrication:

  • OpenSCAD
  • Access to an FDM printer

Micro copter (optional):

  • 3D printed frame
  • F-1607 DC motors with propellers (4x)
  • Flight controller + remote control
  • 150 mAh LiPo battery

Basic OpenSCAD: Motor Compartment Design

01a.png

For starters, let's create emplacements for F-1607 DC motors. This part can be imagined as a shell around the motor, or a Boolean difference between the external box (gray cylinder) and the motor itself (red cylinder).

The motor dimensions given by Olimex are 7 mm diameter and 16.5 mm height. I have chosen shell size parameter to be 0.8 mm. I have lifted the inner cylinder by 0.8 mm (`translate`) so that the result is a box, not a tube. The red transparent cylinder only illustrates where the motor goes, so it will be not visible in the final rendering.

shell=0.8;

difference() {
  cylinder(d=7 + 2 * shell, h=16.5);
  translate([0, 0, shell])
    cylinder(d=7,  h=16.5);
}

How about the motor wires? We can carve out a side and a bottom holes using Boolean `difference` and `union` transformations.

module SlotF1607(h=16.5,
                 d=7,
                 shell=0.8,  // Shell thickness
                 slack=0.1   // Some extra space to accommodate a motor
                 ) {
    difference() {
       // Motor compartment
       cylinder(d=d + 2 * shell, h=16.5);
       union () {
           // Motor emplacement
           translate([0, 0, shell])
             cylinder(d=d + slack,  h=16.5);
           // Bottom hole for motor contacts/wires
           cylinder(d=6, h=h, center=true);
           // Wires hole
           translate([d/4, 0, 0])
               cube([d, 2.5, h * 3], center=true);
       }
   }
}

Last but not least you can see that I have also given some slack for a motor (0.1 mm) to more easily fit into its compartment.

Placing Motor Compartments

02b.png

Usually quadcopter size is characterized by its diagonal, i.e. the largest
distance between the centers of the propellers (thus the centers of the motors). Our microdrone is small and its diagonal will be only 92 mm (it can fit into a hand). The nearest distance between any two motors is then computed as sqrt(2) * (diagonal / 2). This distance is an edge of a square with 92 mm diagonal. Now, we can easily place the four motors on the vertices of the imaginary square.

diagonal = 92;
motors_dist = sqrt(2) * (0.5 * diagonal);

// Half distance between motors
half_dist = 0.5 * motors_dist;

for (i = [1, -1], j = [1, -1]) {
    // Place the motors
    translate([half_dist * i, half_dist * j, 0])
        rotate([0, 0, -45 * i * j - 90 * j])
            SlotF1607();
}

Note that `for (i = [1, -1], j = [1, -1])` is simply a shortcut of two nested loops, i.e. it is the same as

for (i = [1, -1])
  for (j = [1, -1])
    // ...

Motors Support

03a.png
03b.png

We would like to connect the motors using something like an arc. How to do that? First, create and place a small segment:

translate([41, 0, 0])
    square([1.2, 3.6]);

Then, by rotating this segment in space `rotate_extrude`, we obtain a 90-degree arc:

module arc() {
    rotate_extrude(angle=90, $fn=70)
        translate([41, 0, 0])
            square([1.2, 3.6]);
}

Let us link those arcs to the motors.

for (i = [1, -1], j = [1, -1]) {
    // Place the motors
    translate([half_dist * i, half_dist * j, 0])
        rotate([0, 0, -45 * i * j - 90 * j])
            SlotF1607(h=6.5);

    // Connect the motors
    translate([half_dist * j * (i + 1), half_dist * j * (i - 1), 0])
        rotate([0, 0, 45 * i + 90 * j])
            arc();
}

The Platform

04a.png

By now I have realized that to create the body I could reuse the same method as
for the motor emplacements to create an open box, a box with no top cover. However, the only distinction would be the shape (a Boolean difference between rectangular boxes rather than cylinders). Now, I would like to somehow generalize a method of creating an open box. Ideally, I would like to create boxes of any base shape (circle, square, etc.), thus parametrizing the method by a base shape.

To help me with this idea, OpenSCAD provides `children()` method to access child modules. The new operation could be described as

module openbox(delta=0.8) {
    difference() {
        resize_somehow(...)
            children();
        translate([0,0,delta])
            children();
    }
}

So for example the motor compartment could be created simply as

openbox(h=16.5, delta=shell) circle(d=7);

Where `openbox` would receive a 2D shape as a child (a circle, a square, or a
polygon) and transform it into a 3D shape (a box) of a given height using the recipe from above. So how to transform a 2D circle into a 3D cylinder? Using a common method called `linear_extrude`:

linear_extrude(16.5) circle(d=7);

This method is similar to `rotate_extrude` that we used for the arc. However,
it does not rotate the base 2D shape.

Now, how to create a larger version of the cylinder (becoming the shell)? One way to do that would be to use `scale()` or `resize()`. However, a more efficient method would be to operate on the original 2D object, before extruding it (that is actually the reason why our `openbox` was assigned to operate on 2D objects). The `offset()` transformation does exactly what we need to: enlarge the 2D object by a "delta" difference. Therefore, the complete `openbox` module becomes

module openbox(h=10, delta=0.8) {
    difference() {
        linear_extrude(h)
            // Create a larger base by offsetting the child module by delta
            offset(delta=delta)
                children();

        translate([0,0,delta])
            linear_extrude(h)
                children();
    }
}

Create a motor compartment as planned

openbox(h=16.5, delta=0.8) circle(d=7);

Here is how to refactor the SlotF1607 module, making use of `openbox`

module SlotF1607(h=16.5,     // Module height
                 d=7,        // Motor diameter
                 shell=0.8,  // Shell thickness
                 slack=0.1   // Some extra space to accommodate a motor
                 ) {
        difference() {
            // Motor compartment shell
            openbox(h=h, delta=shell) circle(d=d + slack);

            union () {
                // Bottom hole for motor contacts/wires
                cylinder(d=6, h=h, center=true);

                // Wires hole
                translate([d/4, 0, 0])
                    cube([d, 2.5, h * 3], center=true);
            }
        }
}

Similarly, create the body

openbox(h=3.6, delta=0.8) square([46, 23], center=true);

If it is hard to follow this section, note what we do in the line above is the same operation as with the cylinders in the very beginning:

shell=0.8;

difference() {
  cube([46 + 2 * shell, 23 + 2 * shell, 3.6]);
  translate([0, 0, shell])
    cube([46, 23, 3.6]);
}

Fine Tuning: Rounded Angles

04b.png
04c.png

We are essentially done. However, this design somewhat does not inspire me. Replacing the base square with a rounded one looks like a good idea. After all, we designed `openbox` to support any 2D base shape. Here is a common idiom: create a rounded square as a Minkowski sum.

module rounded_square(dim=[20,20], r=5) {
    minkowski() {
        square([dim[0] - 2 * r, dim[1] - 2 * r], center=true);
        circle(r=r);
    }
}

So here we go:

for (i = [1, -1], j = [1, -1]) {
    // Place the motors
    translate([half_dist * i, half_dist * j, 0])
        rotate([0, 0, -45 * i * j - 90 * j])
            SlotF1607(h=6.5);

    // Connect the motors
    translate([half_dist * j * (i + 1), half_dist * j * (i - 1), 0])
        rotate([0, 0, 45 * i + 90 * j])
            arc();
}

openbox(h=3.6, delta=0.8)
    rounded_square([46, 23]);

Much better!

Fine Tuning: Final Touch

honeycomb-array.png
04d.png
04e.png

Now, I would like to shave off a bit of weight if possible. So I will introduce some holes as e.g. in the honeycomb pattern. I will generate an array of hexagonal cylinders and subtract it from the platform. A hexagonal cylinder is simply a cylinder with six fragments, i.e. `cylinder($fn=6, …);`. Here is an array of those generated with two nested loops `for (i = [-M:M], j = [-N:N])`:

module honey_comb(h=10, M=10, N=4, d1=4, d2=4.6) {
    for (i = [-M:M], j = [-N:N]) {
        // Shift the row by d1/4
        if (abs(i) % 2 == 0)
            translate([i * d1, j * d2 + d1/4, -h])
                cylinder(d = d1, h = h * 2, $fn=6);
        // Shift the row by -d1/4
        else
            translate([i * d1, j * d2 - d1/4, -h])
                cylinder(d = d1, h = h * 2, $fn=6);
    }
}

Where `abs(i) % 2 == 0` simply checks if index `i` is an even value so that odd and even rows are shifted by a different amount.

Now I use an `intersection` with another shape to limit the span of the honeycomb pattern.

intersection() {
    honey_comb(h=11, M=6, N=4, d1=4.1, d2=4.9);

    // Limited by this volume
    translate([0,0,-0.5])
        linear_extrude(10)
            rounded_square([dim[0]-2.5, dim[1]-2.5], r=6);
}

Finally, I subtract the above pattern and some side holes.

// The platform
module platform(dim=[46, 23], shell=0.8) {
    difference() {
        openbox(h=3.6, delta=0.8) rounded_square(dim);

        union () {
            // Honey-comb patterned floor
            intersection() {
                honey_comb(h=11, M=6, N=4, d1=4.1, d2=4.9);

                // Limited by this volume
                translate([0,0,-0.5])
                    linear_extrude(10)
                        rounded_square([dim[0]-2.5, dim[1]-2.5], r=6);
            }

            // Side holes
            for (i = [-1,1])
                translate([12.5 * i, 0, shell + 4])
                    cube([11, 40, 8], center=true);
        }
    }
}

// The main module
module beatle1() {
    for (i = [1, -1], j = [1, -1]) {
        // Place the motors
        translate([half_dist * i, half_dist * j, 0])
            rotate([0, 0, -45 * i * j - 90 * j])
                SlotF1607(h=6.5);

        // Connect the motors
        translate([half_dist * j * (i + 1), half_dist * j * (i - 1), 0])
            rotate([0, 0, 45 * i + 90 * j])
                arc();
    }

    platform();
}

beatle1();

That was quite easy. Wasn’t it?

Fabrication

slicing.png
mini-quad-motor-direction-propeller-direction-rotation-spin.jpg

Next, open your favorite slicer software that often comes with a 3D printer.
You will need to specify 0.2 mm layer height, three perimeters, and 30% infill. After slicing (transforming into a series of commands that 3D printer can perform) the design can be inspected layer by layer and printed.

To build a complete micro drone, we need to solder motors to the flight controller. Attention: the motors are rotating in opposite directions (see schema by Oscar Liang). Also note that propellers are different. In order to match the propellers with the rotation direction, keep in mind that the propellers should push down the air. Motor wires could be easily detached from the controller. To prevent this it is a good idea to fix the wires to the frame.

The instructable is based on this post. I also made the complete design available on Thingiverse. Please let me know how can I improve this document for you!