Commit 3d05704e authored by Julian Stirling's avatar Julian Stirling 🐧
Browse files

Merge branch 'manual-bowed-legs' into 'master'

Bowed legs for c270 manual

See merge request !530
parents 5c8f7b5a 5b891fde
Loading
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
---
Tags: knowledge
---
# Why cut some of the ladder ties in the stage legs of the manual microscope? 

The Microscope stage uses a system of parallelograms to move smoothly. In the stage legs, the parallelogram is formed by stiff bars at the sides and at the top and bottom, which are connected by four thin flexure hinges in the corners.

There are also ladder ties bridging between the stiff vertical side bars at intervals which help to stabilise printing. Most of these horizontal ties are the same length as the top and bottom of the leg (the top bar plus the flexure joint), so they form multiple smaller equivalent parallelograms, which do not affect the motion of the main leg structure.

![](../renders/brim_and_ties_manual4.png)

For the Manual Microscope the vertical side bars of the legs are bowed to give space for the camera module. This does not affect the geometry of the motion because the four flexure hinges remain at the corners of a parallelogram. However some of the ladder ties are now shorter and do not form equivalent parallelograms, so they need to be cut to avoid putting stress on the structure.
 No newline at end of file
+3 −1
Original line number Diff line number Diff line
@@ -12,12 +12,14 @@
![](renders/brim_and_ties{{var_body, default:}}1.png)
![](renders/brim_and_ties{{var_body, default:}}2.png)
![](renders/brim_and_ties{{var_body, default:}}3.png)
{{includetext: "![](renders/brim_and_ties{{var_body, default:}}4.png)", if: var_body is _manual}}

The [main body][{{includetext: "manual ", if: var_body is _manual}}main body](fromstep){cat: PrintedPart, qty:1} has some custom supports and a custom brim to remove. These are highlighted in red in the images.  

* Remove the brim with a [utility knife]{qty:1,cat:tool} and [precision wire cutters]{qty:1}
* Cut out the ties inside the actuator columns (6 total) with the [precision wire cutters]{qty:1}
* Cut out the ties for the rear legs (4 total) with the [precision wire cutters]{qty:1} and trim the stubs flush, so that they do not obstruct the stage movement 
* Cut out the ties for the rear legs (4 total) with the [precision wire cutters]{qty:1} and trim the stubs flush, so that they do not obstruct the stage movement {{includetext: "
* Cut out the top two ladder ties in the rear legs, where the legs are bowed (4 total) with the [precision wire cutters]{qty:1}[i](info_pages/why_cut_ladder_ties.md)", if: var_body is _manual}}

## Embed mounting nuts in the stage {pagestep}

+114 −37
Original line number Diff line number Diff line
@@ -18,57 +18,115 @@ function c270_camera_dict() = [["mount_height", 4.5],

function c270_camera_bottom_z() = -key_lookup("mount_height", c270_camera_dict());

// spacing of the lens mount holes at the sensor
/* Height of tallest components, plus clearance

:return: 6.5
*/
function c270_tallest_clearance() = 6.5; //tallest components on top of baord, plus clearance

/* Spacing of the lens mount holes at the sensor

:return: 8.25
*/
function c270_camera_hole_spacing() = 8.25;
// position of camera board hole near the sensor

/* Position of camera board hole near the sensor

:return: vector from optical axis to the mounting hole
*/
function c270_near_third_hole_pos() = [5,-9.5,0];
// position of camera board hole at the cable end

/* Position of camera board hole at the cable end

:return: vector from optical axis to the mounting hole
*/
function c270_far_third_hole_pos() = [-6,42.3,0];


module c270_cutout(beam_r=4.3, beam_h=4.5, cover=true){
module c270_cutout(beam_r=4.3, cover=true){
    //cut-out to fit Logitech C270 webcam
    //optical axis at (0,0)
    //top of PCB at (0,0,0)

    mount_h = key_lookup("mount_height", c270_camera_dict());

    inner_w = c270_dims().x;
    // inner size is outer size minus roof thickness
    in_h_main = mount_h - 1;
    in_h_tall = c270_tallest_clearance();
    block_h = 15;

    mirror([0,0,1]){ //parts cut out of the mount are z<0
        //beam clearance
        hull(){
            cube([8,8,6], center=true);
            translate_z(-beam_h){
            translate_z(-mount_h){
                cylinder(r=beam_r, h=2*tiny(), center=true);
            }
        }

        // with a cover below the camera, make the cut-out a bit longer
        cut_pos = (cover)? 27 : 25;
        cut_length = 37;
        cut_end = (cover)? 45.5 : 43.5;
        cut_mid = cut_end - cut_length / 2;
        cut_length_tall = cut_end - abs(c270_y_offset()) - 2;
        cut_mid_tall = cut_end - cut_length_tall / 2;

        difference(){
            union(){
                // component clearance, cable end
                translate([0, cut_pos, 15-3.5]){
                translate([0, cut_mid, block_h-in_h_main]){
                    rotate_y(180){
                        round_top_cube([19.5, 37, 15],1.5,center=true);
                        round_top_cube([inner_w, 37, block_h], 1.5, center=true);
                    }
                }
                translate([0, cut_mid_tall, block_h-in_h_tall]){
                    rotate_y(180){
                        intersection(){
                            round_top_cube([inner_w, cut_length_tall, block_h], 1.5, center=true);
                            slope_top_cube([inner_w, 2*cut_length_tall, block_h], slope_angle=30, slope_depth=3, center=true);
                        }
                    }
                }
                // component clearance, sensor end
                hull(){
                    translate([0, 5, 4]){
                        cube([19.5, 18, 15],center=true);
                    z = block_h/2 - in_h_main;
                    translate([0, 3, z]){
                        cube([inner_w, 16, block_h], center=true);
                    }
                    translate([0, -4, 4]){
                        cube([9, 9.5*2, 15],center=true);
                    translate([0, -4, z]){
                        cube([9, 9.5*2, block_h], center=true);
                    }
                }
            }
            union(){
                // add cube at third mounting hole, cable end
                translate(c270_far_third_hole_pos()-[0,0,10]){
                    cylinder(d=5, h=10);
                h_c = 10;
                translate(c270_far_third_hole_pos()-[0,0,h_c-tiny()]){
                    hull(){
                        hull(){
                            cylinder(d=4, h=h_c);
                            translate_z(-2){
                                cylinder(d=5, h=h_c);
                            }
                        }
                        translate_x(-10){
                            cylinder(d=5, h=h_c);
                        }
                        translate_y(10){
                            cylinder(d=5, h=h_c);
                        }
                    }
                }
                // add a pillar at the 'near third hole' place
                translate(c270_near_third_hole_pos()){
                    translate_z(-50){
                        cylinder(r=3, h=99, center = false);
                    hull(){
                        translate([1, -2.4, -(1.5 + mount_h/2)]){
                            cube([8, 10, mount_h], center=true);
                        }
                        translate([1.5, -2.4, tiny()]){
                            cube([6, 10, tiny()], center=true);
                        }
                    }
                }
                // add mount around the camera sensor,
@@ -104,16 +162,16 @@ hull-ed onto the lens assembly.
*/
module c270_camera_mount(screwhole=true){

    // camera dims
    // camera mount dims
    h = c270_dims().y;
    w = c270_dims().x;
    w = c270_dims().x + 1.5;
    //wider_ section around lens
    h_w = 26;
    w_w = 23.5;
    h_w = 2*abs(c270_y_offset());
    w_w = c270_dims().x + 2.5;

    y_offset = c270_y_offset();
    // extra room for cable if the mount has a cover below
    // when the screwholes are present from below
    // Extra room for cable if the mount has a cover below,
    // which is when the screwholes are not present from below.
    y_excess = (screwhole)? 0 : c270_mount_y_excess();

    mount_height = key_lookup("mount_height", c270_camera_dict());
@@ -121,13 +179,28 @@ module c270_camera_mount(screwhole=true){
        union(){
            // Cube over sensor for hulling to the lens mount, slightly raised
            // because the spheres making the round top are cut off in the STL representation
            translate([-w_w/2, y_offset-2, -mount_height]){
                round_top_cube([w_w, h_w+4, mount_height+2*tiny()], 2, $fn=16);
            rounding = 2;
            translate([-w_w/2, y_offset-rounding, -mount_height]){
                round_top_cube([w_w, h_w+rounding, mount_height+2*tiny()], rounding, $fn=16);
            }
            // Cube over the rest of the board, slightly lower down so that it
            // is not used for the lens mount.
            translate([-w/2, y_offset+y_excess, -mount_height]){
                round_top_cube([w, h, mount_height-2*tiny()], 2, $fn=16);
                round_top_cube([w, h, mount_height-2*tiny()], rounding, $fn=16);
            }
            // Cube over the end of the board, slightly higher up to give
            // more space for large components.
            start_pos = -y_offset + tiny();
            end_pos = h - (-y_offset) + y_excess;
            length = abs(end_pos - start_pos);
            centre = start_pos + (end_pos - start_pos) / 2;
            // outer size over tall components is inner plus roof thickness
            bulge = c270_tallest_clearance() + 1;
            translate([0, centre, -mount_height]){
                    intersection(){
                        round_top_cube([w, length, bulge], 2, center=true, $fn=16);
                        slope_top_cube([w, 2*length, bulge], slope_angle=30, slope_depth=3, center=true);
                    }
            }
        }
        translate_z(-mount_height){
@@ -196,8 +269,12 @@ It is designed to reach up to the camera platform when used with a lens spacer
module c270_backshell(){

    height = c270_backshell_height();
    wall_t = 0.75;
    shelf = 0.5;
    h = c270_dims().y;
    w = c270_dims().x;
    case_w = w + 2*wall_t;
    hollow_w = w - shelf;

    y_offset = c270_y_offset(); // y offset is -ve

@@ -215,27 +292,25 @@ module c270_backshell(){
            end_from_centre = 3; // distance from axis of end of the cover
            cover_length = h + y_offset + y_excess - end_from_centre;
            difference(){
                translate([-w/2, 0, 0]){
                    round_top_cube([w, cover_length+end_from_centre, height], 2, $fn=16);
                translate([-(case_w)/2, 0, 0]){
                    round_top_cube([case_w, cover_length+end_from_centre, height], 2, $fn=16);
                }
                cube([2*w, 2*end_from_centre, 99], center=true);
                // trim to length
            cube([2*case_w, 2*end_from_centre, 99], center=true);
            }

            // cut out space for PCB
            translate([-(w+1)/2, y_offset, -tiny()]){
                cube([w+1, h, 1+tiny()]);
            translate([-(w)/2, y_offset, -tiny()]){
                cube([w, h, 1+tiny()]);
            }

            difference(){
                //Hollow out main block
                union(){
                    translate([-(w-3)/2, -tiny(), -1.5]){
                        cube([w-3, h+y_offset+y_excess-2, height]);
                    translate([-(hollow_w)/2, -tiny(), -1.5]){
                        round_top_cube([hollow_w, h+y_offset+y_excess-2, height], 1.5, center=false);
                    }
                    // A tiny bit more space for cable
                    translate([-w/2+.7, y_offset+h-12, -1.5]){
                        cube([10, 10, height]);
                    }
                }
                // Subtract (from the hollowing-out subtraction) stand offs
                // for the backshell.
@@ -276,14 +351,16 @@ module c270_cable_exit(){
}

module c270_counterbore(){
    camera_mount_height = key_lookup("mount_height", c270_camera_dict());
    lens_base = camera_mount_height + 5;
    translate_z(c270_camera_bottom_z()-1){
        at_c270_hole_pattern(){
            cylinder(r=1.1, h=9, $fn=12);
            cylinder(r=1.1, h=lens_base, $fn=12);
        }
    }
    translate_z(c270_camera_bottom_z()+1){
        at_c270_hole_pattern(){
            cylinder(r=2.1, h=9, $fn=12);
            cylinder(r=2.1, h=lens_base-1, $fn=12);
        }
    }
}
+18 −5
Original line number Diff line number Diff line
@@ -88,10 +88,26 @@ module lens_gripper(lens_r=10, h=6, lens_h=3.5, base_r=undef, t=0.65, solid=fals
    trylinder_gripper(inner_r=lens_r, h=h, grip_h=lens_h, base_r=base_r, t=t, solid=solid, flare=flare);
}


/* A thin slice of the top of the camera mount

Used to hull() the RMS optics module or the lens spacer onto the body.

The area of the top slice is restricted to an area around the optical axis, to avoid massive hull.

:param optics_config: optics configuration dictionary - defined in optics_configurations.scad
*/
module camera_mount_top_slice(optics_config){
    // A thin slice of the top of the camera mount

    camera_type = key_lookup("camera_type", optics_config);
    top_area = (camera_type == "arducam_b0196")? 40 :
                    (camera_type == "logitech_c270")? 2 * abs(c270_y_offset()) : 100;

    thick_section(h=tiny(), center=false, shift=false){
        intersection(){
            camera_mount(optics_config);
            cube(top_area, center=true);
        }
    }
}

@@ -423,10 +439,7 @@ module lens_spacer(params, optics_config){
                }
                union(){
                    // cut out the optical path
                    z_offset_lens_spacer_optical_path = is_c270_spacer(optics_config) ?
                                                                    2.8: // to match the light trap to the mount aperture, C270
                                                                    0; // to match the light trap to the mount aperture, Picam 2 and B0196
                    optical_path(optics_config, lens_assembly_z, camera_mount_top_z=z_offset_lens_spacer_optical_path);
                    optical_path(optics_config, lens_assembly_z, camera_mount_top_z=camera_mount_height-tiny());
                    // cut out counterbores
                    translate_z(camera_mount_height){
                        camera_mount_counterbore(optics_config);
+159 −3
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@ use <../reflection_illuminator.scad>
use <./libdict.scad>
use <./microscope_parameters.scad> //All the geometric variables are now in here.
use <./lib_microscope_stand.scad>
use <./cameras/logitech_c270.scad>

$fn = 32;

// base_mounting_holes returns a list of the holes for mounting the microscope
@@ -71,6 +73,7 @@ module leg_flexures(params, brace){
module leg(params, brace=flex_dims().x){
    // The legs support the stage - this is either used directly
    // or via "actuator" to make the legs with levers

    fw=flex_dims().x;

    union(){
@@ -86,20 +89,71 @@ module leg(params, brace=flex_dims().x){
            }
        }
        leg_flexures(params, brace);
    }
}

module leg_ladder_ties(params){
    //thin links between legs
    flex_sep = upper_xy_flex_z(params)-lower_xy_flex_z();
    n = floor(flex_sep/leg_link_spacing());
    leg_inner_w = leg_outer_w(params) - 2 * leg_dims(params).x;
    if(n > 2){
        // adjust spacing so it is even
        link_space_adj = flex_sep/n;
        translate([0, leg_dims(params).y/2, lower_xy_flex_z()+link_space_adj]){
            repeat([0, 0, link_space_adj], n-1){
                    cube([leg_outer_w(params), 2, 0.5],center=true);
                cube([leg_inner_w + tiny(), 2, 0.5],center=true);
            }
        }
    }
}

module all_leg_ladder_ties(params, ties_only=false){
    // thin links between all four legs
    ties = key_lookup("print_ties", params);
    manual = ! (key_lookup("include_motor_lugs", params));

    flex_sep = upper_xy_flex_z(params)-lower_xy_flex_z();
    n = floor(flex_sep/leg_link_spacing());
    h = (n-2.5) * flex_sep/n;

    if (! ties_only){
        difference(){
            each_leg(params){
                leg_ladder_ties(params);
            }
            if (!ties && manual){
                translate([-999/2, -999, h]){
                    cube([999,999,999], center=false);
                }
            }
            if (manual){
                difference(){
                    c270_main_body_leg_cutout(params);
                    translate_x(-999/2){
                        cube([999,999,999], center=false);
                    }
                }
            }
        }
    }
    else if (manual){
        difference(){
            intersection(){
                each_rear_leg(params){
                    leg_ladder_ties(params);
                }
                translate_z(h + 999/2){
                    cube([999,999,999], center=true);
                }
            }
            // Cut out the camer space, slightly widened so that the ends of the 
            // ties are below the surface of the leg.
            scale([1.01,1,1]){
                c270_main_body_leg_cutout(params);
            }
        }
    }
}

/* A chamfer to be cut from the actuator legs using difference()
@@ -388,16 +442,117 @@ module xy_screw_seat(params, label=""){
               label=label);
}

function c270_cutout_params(params) = let(
    leg_width = leg_outer_w(params),
    leg_r = key_lookup("leg_r", params),
    leg_a = atan((leg_width/2) / leg_r), // angle subtended by the leg
    leg_side_r = sqrt(leg_r^2 + (leg_width/2)^2),
    leg_sep = 2 * leg_side_r * sin(45-leg_a),

    nominal_travel = upper_xy_flex_z(params) * flex_a(),
    clearance = 0.5,
    c270_lens_spacer_body_width = c270_dims().x + 1.5,
    c270_oversize = c270_lens_spacer_body_width + (nominal_travel + clearance)*2,
    cut_depth = (c270_oversize - leg_sep)/2,
    // The strengthening part should move by the leg thickness, in the x-direction
    cut_xy = leg_dims(params).x * sin(45),
    c270_z_space = 22,
    c270_mid_z = (upper_xy_flex_z(params) - 1) - (c270_z_space + 4)/2,
    dict = [
            ["leg_sep", leg_sep],
            ["c270_oversize", c270_oversize],
            ["cut_depth", cut_depth],
            ["cut_xy", cut_xy],
            ["c270_z_space", c270_z_space], // vertical height allowed for c270
            ["c270_mid_z", c270_mid_z],
            ["rounding", 2] // c270 cover rounding radius
            ]
) dict;

module c270_main_body_leg_cutout(params){
    leg_sep = key_lookup("leg_sep", c270_cutout_params(params));
    cut_round = key_lookup("rounding", c270_cutout_params(params));
    c270_oversize = key_lookup("c270_oversize", c270_cutout_params(params));
    c270_z_space = key_lookup("c270_z_space", c270_cutout_params(params));
    cut_depth= key_lookup("cut_depth", c270_cutout_params(params));
    c270_mid_z = key_lookup("c270_mid_z", c270_cutout_params(params));
    translate_z(c270_mid_z){
        rounded_cube([c270_oversize, 99, c270_z_space], cut_round, center=true);
        reflect_x(){
            translate([-(leg_sep/2 + (cut_depth - cut_round)), 0, c270_z_space/2 - cut_round]){
                rotate_x(90){
                    tangent_to_cylinder_plus(r=cut_round, angle=30, box=10);
                }
            }
        }
    }
}

module c270_main_body_leg_bulge(params){
    leg_r = key_lookup("leg_r", params);
    leg_width = leg_outer_w(params);
    // leg_sep = key_lookup("leg_sep", c270_cutout_params(params));
    cut_round = key_lookup("rounding", c270_cutout_params(params));
    c270_oversize = key_lookup("c270_oversize", c270_cutout_params(params));
    c270_z_space = key_lookup("c270_z_space", c270_cutout_params(params));
    // cut_depth= key_lookup("cut_depth", c270_cutout_params(params));
    cut_xy = key_lookup("cut_xy", c270_cutout_params(params));
    c270_mid_z = key_lookup("c270_mid_z", c270_cutout_params(params));
    translate([-cut_xy,cut_xy,0]){
        intersection(){
            leg_frame(params, 135){
                hull(){
                    intersection(){
                        leg(params);
                        // Hull the leg, so that there is a solid centre to use,
                        // but the top and bottom flexure blocks cause a problem
                        // so intersect above the lower block and below the top block
                        leg_block_avoid = key_lookup("leg_block_t", params) + 2;
                        translate([-leg_width, -2*leg_r, leg_block_avoid]){
                            cube([2*leg_width, 4*leg_r, upper_xy_flex_z(params)-leg_block_avoid], center=false);
                        }
                    }
                }
            }
            hull(){
                translate_z(c270_mid_z){
                    rounding = max(cut_round,5);
                    rounded_cube([c270_oversize, 4*leg_r, c270_z_space+4], rounding, center=true);
                    // A central object low down, to make sure that the part has a
                    // printable slope
                    translate_z(-c270_z_space){
                        rotate_x(90){
                            cylinder(d=10, h=4*leg_r, center=true);
                        }
                    }
                }
            }
        }
    }
}

module xy_legs_and_actuators(params){
    // This is the xy_actuators including the casing and all 4 legs

    // back legs
    manual = !(key_lookup("include_motor_lugs", params));

    // back legs - furthest from actuators
    difference(){
        reflect_x(){
            leg_frame(params, 135){
                leg(params);
            }
            if (manual){
                // bulge the legs to strengthen when cut for the c270 camera
                c270_main_body_leg_bulge(default_params());
            }
        }
        if (manual){
            // make space for the c270 camera
            c270_main_body_leg_cutout(default_params());
        }
    }
    //front legs and actuator columns
    //actuator columns and front legs - next to actuators
    xy_actuators(params);

    for(i = [0,1]){
@@ -506,6 +661,7 @@ module xy_positioning_system(params){
    // This module creates the main XY positioning mechanism. Including the actuator columns.
    ties = key_lookup("print_ties", params);
    xy_legs_and_actuators(params);
    all_leg_ladder_ties(params);
    internal_xy_structure(params);
    xy_stage_with_nut_traps(params);

Loading