From 1e0338a77f6369e1b386e6f8224edb00e64ba734 Mon Sep 17 00:00:00 2001 From: mattmcw Date: Fri, 6 Oct 2023 13:47:15 -0400 Subject: [PATCH] Update rack and pinion library to one that produces valid geometry. Make first pass at rack and pinion gear --- app/data/cfg.json | 2 +- app/package-lock.json | 2 +- app/package.json | 2 +- data/cfg.json | 2 +- package-lock.json | 4 +- package.json | 2 +- processing/mcopy/cfg.json | 2 +- scad/mcopy_projector.scad | 65 ++++- scad/rack_and_pinion.scad | 595 +++++++++++++++++--------------------- 9 files changed, 333 insertions(+), 343 deletions(-) diff --git a/app/data/cfg.json b/app/data/cfg.json index 714d080..0790b9d 100644 --- a/app/data/cfg.json +++ b/app/data/cfg.json @@ -1,5 +1,5 @@ { - "version": "1.8.42", + "version": "1.8.43", "ext_port": 1111, "profiles": { "mcopy": { diff --git a/app/package-lock.json b/app/package-lock.json index 55b5185..dc4b617 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "mcopy-app", - "version": "1.8.42", + "version": "1.8.43", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/app/package.json b/app/package.json index efb41ef..975c2a1 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "mcopy-app", - "version": "1.8.42", + "version": "1.8.43", "description": "GUI for the mcopy small gauge film optical printer platform", "main": "main.js", "scripts": { diff --git a/data/cfg.json b/data/cfg.json index 714d080..0790b9d 100644 --- a/data/cfg.json +++ b/data/cfg.json @@ -1,5 +1,5 @@ { - "version": "1.8.42", + "version": "1.8.43", "ext_port": 1111, "profiles": { "mcopy": { diff --git a/package-lock.json b/package-lock.json index edd80ed..63ce315 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mcopy", - "version": "1.8.42", + "version": "1.8.43", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mcopy", - "version": "1.8.42", + "version": "1.8.43", "license": "MIT", "dependencies": { "arduino": "file:app/lib/arduino", diff --git a/package.json b/package.json index 3d19b6f..667262d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mcopy", - "version": "1.8.42", + "version": "1.8.43", "description": "Small gauge film optical printer platform", "main": "build.js", "directories": { diff --git a/processing/mcopy/cfg.json b/processing/mcopy/cfg.json index 714d080..0790b9d 100644 --- a/processing/mcopy/cfg.json +++ b/processing/mcopy/cfg.json @@ -1,5 +1,5 @@ { - "version": "1.8.42", + "version": "1.8.43", "ext_port": 1111, "profiles": { "mcopy": { diff --git a/scad/mcopy_projector.scad b/scad/mcopy_projector.scad index 5ebb326..6a2980b 100644 --- a/scad/mcopy_projector.scad +++ b/scad/mcopy_projector.scad @@ -35,9 +35,9 @@ LEDPinSpacing = 2.54; LEDH = 8.6; ServoX = 55; -ServoY = 7; -ServoBoltD = 3.5; +ServoY = 7 + 2; ServoZ = 20; +ServoBoltD = 3.5; ServoSpaceZ = 9.5; ServoSpaceX = 48; ServoVoidX = 40.5; @@ -408,7 +408,7 @@ module panel (pos = [0, 0, 0], rot = [0, 0, 0], Mounts = "2020") { } nub_rails([28.25, 0, -5]); - servo_mount([33, 8, -45], [0, 90, 0]); + servo_mount([33, 9, -45], [0, 90, 0]); difference () { stepper_mount([0, 0, -(StepperMountZ / 2) - (PanelZ / 2)]); @@ -476,8 +476,8 @@ module servo_mount_bolts_void () { translate([ X, 0, -Z]) rotate([90, 90, 0]) cylinder(r = R(ServoBoltD), h = ServoY + 1, center = true, $fn = 60); translate([-X, 0, -Z]) rotate([90, 90, 0]) cylinder(r = R(ServoBoltD), h = ServoY + 1, center = true, $fn = 60); - translate([-X, -7, -Z]) rotate([90, 90, 0]) cylinder(r = R(6), h = 10, center = true, $fn = 60); - translate([-X, -7, Z]) rotate([90, 90, 0]) cylinder(r = R(6), h = 10, center = true, $fn = 60); + translate([-X, -6, -Z]) rotate([90, 90, 0]) cylinder(r = R(6), h = 10, center = true, $fn = 60); + translate([-X, -6, Z]) rotate([90, 90, 0]) cylinder(r = R(6), h = 10, center = true, $fn = 60); } module servo_mount_void () { @@ -491,7 +491,9 @@ module servo_mount (pos = [0, 0, 0], rot = [0, 0, 0]) { translate (pos) rotate (rot) { difference () { union () { - translate([1.5, 0, 0]) rotate([90, 0, 0]) rounded_cube([ServoX + 3, ServoZ+10, ServoY ], d = 4, center = true, $fn = 40); + translate([1.5, 0, 0]) rotate([90, 0, 0]) { + rounded_cube([ServoX + 3, ServoZ+10, ServoY ], d = 4, center = true, $fn = 40); + } difference () { translate([-34, 0, 0]) cube([17, ServoY, ServoZ + 10 ], center = true, $fn = 40); translate([-19, 0, 0]) cube([17, ServoY + 1, 20 ], center = true, $fn = 40); @@ -500,7 +502,8 @@ module servo_mount (pos = [0, 0, 0], rot = [0, 0, 0]) { servo_mount_void(); //angled void for motor - translate([0, 7.5, -15]) rotate([45, 0, 0]) cube([ServoX+20, 10, 10], center = true); + translate([25.5, 6.5, -15]) rotate([45, 0, 0]) cube([ServoX+20, 10, 10], center = true); + //cut off end translate([0, 0, 15.4]) cube([ServoX+30, 10, 10], center = true); //cut off top @@ -508,7 +511,10 @@ module servo_mount (pos = [0, 0, 0], rot = [0, 0, 0]) { bolt_and_cap_void([-35, 0, 20], [0, 0, 0], bolt = 15); translate([-35, 0, 6]) m3_nut(2.5); translate([-35, 5, 6]) cube([6.75, 10, 2.5], center = true); + //panel bolt void + translate([-40, 4.5, -17]) rotate([0, 90, 0]) cylinder(r = R(7), h = 25, center = true); } + } //debug //translate([(55 / 2)-17.5, 0, 0]) sphere(r = 6 / 2, $fn = 60); @@ -532,15 +538,44 @@ module servo_mount_cover (pos = [0, 0, 0], rot = [0, 0, 0]) { servo_mount_void(); servo_mount(); translate([-22.25 - 4, 0, -15.4]) cube([ServoX+30, 10, 10], center = true); + bolt_and_cap_void([-35, 0, 20], [0, 0, 0]); translate([-35, 0, 0]) cube([20, 20, 20], center = true); + translate([-51, 0, 10]) cube([20, 20, 20], center = true); } - } } -module projector () { - +module servo_gear (pos = [0, 0, 0], rot = [0, 0, 0]) { + translate(pos) rotate(rot) { + difference () { + union () { + translate([0, -32, -4.4]) rad_und_zahnstange(modul, laenge_stange, zahnzahl_ritzel, hoehe_stange, bohrung_ritzel, breite, eingriffswinkel, schraegungswinkel, zusammen_gebaut, optimiert); + cylinder(r = R(28), h = 8.8, center = true, $fn = 50); + } + cylinder(r = R(5.8), h = 40, center = true); + bolt_and_cap_void([0, 10, 8.8 - 3.5]); + } + //cylinder(r = R(1), h = 20, center = true); + } +} + +module nub_rack (pos = [0, 0, 0], rot = [0, 0, 0]) { + H = 9.25 + 2.75; + Len = 50; + translate(pos) rotate(rot) { + difference () { + union () { + translate([21.5, 4, -8.8 / 2]) zahnstange_und_rad(modul, laenge_stange, zahnzahl_ritzel, hoehe_stange, bohrung_ritzel, breite, eingriffswinkel, schraegungswinkel, zusammen_gebaut, optimiert); + translate([Len / 2, -H / 2, 0]) cube([Len, H, 8.8], center = true); + translate([(Len / 2) - 1.5, -H - (11 / 2) + 0.1, 0]) { + rotate([90, 0, 0]) cylinder(r = R(4.75), h = 11, center = true, $fn = 60); + } + } + //slot for bolt + //translate([Len / 2, 2, 8.8 - 1.5]) cube([Len + 1, H, 8.8], center = true); + } + } } module debug () { @@ -575,10 +610,12 @@ module debug () { } //color("red") translate([(-PanelX / 2) + 10, 0, (-PanelZ / 2) -10]) rotate([90, 0, 0]) 2020_tslot(PanelY); //orbital_mount([(-PanelX / 2) - 4.5, 0, 40], [0, 90, 0]); - color("red") servo_mount_cover([33, 8+10, -45], [0, 90, 0]); + //servo_mount_cover([33, 8+10, -45], [0, 90, 0]); + color([0.5,0.5,0,0.8]) servo_gear([33, 0, -32.5 + 7.75 - 10], [90, 0, 0]); + nub_rack([-6, 0, -15], [-90, 0, 0]); } -PART = "panel"; +PART = "nub_rackx"; if (PART == "gate_key") { gate_key(KeyRot = 0); @@ -586,12 +623,16 @@ if (PART == "gate_key") { rotate([180, 0, 0]) panel(); } else if (PART == "servo_mount_cover"){ servo_mount_cover([0, 0, 0], [0, 90, 0]); +} else if (PART == "servo_gear") { + servo_gear(); } else if (PART == "led_housing"){ LED_housing(); } else if (PART == "led_enclosure"){ LED_enclosure(); } else if (PART == "orbital_mount") { orbital_mount(); +} else if (PART == "nub_rack") { + nub_rack(); } else { debug(); } diff --git a/scad/rack_and_pinion.scad b/scad/rack_and_pinion.scad index f255df2..ea19729 100644 --- a/scad/rack_and_pinion.scad +++ b/scad/rack_and_pinion.scad @@ -1,338 +1,287 @@ -//This is a modification of " Public Domain Parametric Involute Spur Gear (and involute helical gear and involute rack) -// by Leemon Baird, 2011, Leemon@Leemon.com -//http://www.thingiverse.com/thing:5505 " +// Kopfspiel +spiel = 0.05; +// Hoehe des Zahnkopfes ueber dem Teilkreis +modul=1; +// Laenge der Zahnstange +laenge_stange=50; +// Anzahl der Radzaehne +zahnzahl_ritzel=32; +// Hoehe der Zahnstange bis zur Waelzgeraden +hoehe_stange=4; +// Durchmesser der Mittelbohrung des Stirnrads +bohrung_ritzel=4; +// Breite der Zaehne +breite=8.8; +// Eingriffswinkel, Standardwert = 20 grad gemaess DIN 867. Sollte nicht groesser als 45 grad sein. +eingriffswinkel=20; +// Schraegungswinkel zur Rotationsachse, Standardwert = 0 grad (Geradverzahnung) +schraegungswinkel=20; +// Komponenten zusammengebaut fuer Konstruktion oder auseinander zum 3D-Druck +zusammen_gebaut=0; +// Loecher zur Material-/Gewichtsersparnis bzw. Oberflaechenvergoesserung erzeugen, wenn Geometrie erlaubt +optimiert = 1; -//Modifications by racatack June 2016: -// v1.0, 6/24/16: -// -Incorporated 'module InvoluteGear_rack()' from the 11/11/15 comment by quisam2342 in thing:5505 to fix noted problems in rack generation at various tooth sizes -// -Added a section for an optional 'backboard' that can be used to stiffen an otherwise skinny rack -// -Added sections for optional Stop Blocks at either or both ends of a rack -// -Removed extra gears from the example since I only need one pinion. See thing:5505 example section to gain more insight into gear generation. -//v1.1, 6/28/16: -// -Added sections to optionally create mounting flanges on the bottom/backside of the rack. Flanges can be at either or both ends, or longitudinal. +/* Bibliothek fuer ein Zahstangen-Radpaar fuer Thingiverse Customizer -//TO GENERATE A CUSTOM RACK AND PINION ENTER INPUTS AT LINES 345-377 +Enthaelt die Module +zahnstange(modul, laenge, hoehe, breite, eingriffswinkel = 20, schraegungswinkel = 0) +stirnrad(modul, zahnzahl, breite, bohrung, eingriffswinkel = 20, schraegungswinkel = 0, optimiert = true) +zahnstange_und_ritzel (modul, laenge_stange, zahnzahl_ritzel, hoehe_stange, bohrung_ritzel, breite, eingriffswinkel=20, schraegungswinkel=0, zusammen_gebaut=true, optimiert=true) + +Autor: Dr Joerg Janssen +Stand: 6. Januar 2017 +Version: 2.0 +Lizenz: Creative Commons - Attribution, Non Commercial, Share Alike + +Erlaubte Module nach DIN 780: +0.05 0.06 0.08 0.10 0.12 0.16 +0.20 0.25 0.3 0.4 0.5 0.6 +0.7 0.8 0.9 1 1.25 1.5 +2 2.5 3 4 5 6 +8 10 12 16 20 25 +32 40 50 60 + +*/ -////////////////////////////////////////////////////////////////////////////////////////////// -// Public Domain Parametric Involute Spur Gear (and involute helical gear and involute rack) -// version 1.1 -// by Leemon Baird, 2011, Leemon@Leemon.com -//http://www.thingiverse.com/thing:5505 -// -// This file is public domain. Use it for any purpose, including commercial -// applications. Attribution would be nice, but is not required. There is -// no warranty of any kind, including its correctness, usefulness, or safety. -// -// This is parameterized involute spur (or helical) gear. It is much simpler and less powerful than -// others on Thingiverse. But it is public domain. I implemented it from scratch from the -// descriptions and equations on Wikipedia and the web, using Mathematica for calculations and testing, -// and I now release it into the public domain. -// -// http://en.wikipedia.org/wiki/Involute_gear -// http://en.wikipedia.org/wiki/Gear -// http://en.wikipedia.org/wiki/List_of_gear_nomenclature -// http://gtrebaol.free.fr/doc/catia/spur_gear.html -// http://www.cs.cmu.edu/~rapidproto/mechanisms/chpt7.html -// -// The module gear() gives an involute spur gear, with reasonable defaults for all the parameters. -// Normally, you should just choose the first 4 parameters, and let the rest be default values. -// The module gear() gives a gear in the XY plane, centered on the origin, with one tooth centered on -// the positive Y axis. The various functions below it take the same parameters, and return various -// measurements for the gear. The most important is pitch_radius, which tells how far apart to space -// gears that are meshing, and adendum_radius, which gives the size of the region filled by the gear. -// A gear has a "pitch circle", which is an invisible circle that cuts through the middle of each -// tooth (though not the exact center). In order for two gears to mesh, their pitch circles should -// just touch. So the distance between their centers should be pitch_radius() for one, plus pitch_radius() -// for the other, which gives the radii of their pitch circles. -// -// In order for two gears to mesh, they must have the same mm_per_tooth and pressure_angle parameters. -// mm_per_tooth gives the number of millimeters of arc around the pitch circle covered by one tooth and one -// space between teeth. The pitch angle controls how flat or bulged the sides of the teeth are. Common -// values include 14.5 degrees and 20 degrees, and occasionally 25. Though I've seen 28 recommended for -// plastic gears. Larger numbers bulge out more, giving stronger teeth, so 28 degrees is the default here. -// -// The ratio of number_of_teeth for two meshing gears gives how many times one will make a full -// revolution when the the other makes one full revolution. If the two numbers are coprime (i.e. -// are not both divisible by the same number greater than 1), then every tooth on one gear -// will meet every tooth on the other, for more even wear. So coprime numbers of teeth are good. -// -// The module rack() gives a rack, which is a bar with teeth. A rack can mesh with any -// gear that has the same mm_per_tooth and pressure_angle. -// -// Some terminology: -// The outline of a gear is a smooth circle (the "pitch circle") which has mountains and valleys -// added so it is toothed. So there is an inner circle (the "root circle") that touches the -// base of all the teeth, an outer circle that touches the tips of all the teeth, -// and the invisible pitch circle in between them. There is also a "base circle", which can be smaller than -// all three of the others, which controls the shape of the teeth. The side of each tooth lies on the path -// that the end of a string would follow if it were wrapped tightly around the base circle, then slowly unwound. -// That shape is an "involute", which gives this type of gear its name. -// -////////////////////////////////////////////////////////////////////////////////////////////// +/* [Hidden] */ +pi = 3.14159; +rad = 57.29578; +$fn = 96; -//An involute spur gear, with reasonable defaults for all the parameters. -//Normally, you should just choose the first 4 parameters, and let the rest be default values. -//Meshing gears must match in mm_per_tooth, pressure_angle, and twist, -//and be separated by the sum of their pitch radii, which can be found with pitch_radius(). -module gear ( - mm_per_tooth = 3, //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth - number_of_teeth = 11, //total number of teeth around the entire perimeter - thickness = 6, //thickness of gear in mm - hole_diameter = 3, //diameter of the hole in the center, in mm - twist = 0, //teeth rotate this many degrees from bottom of gear to top. 360 makes the gear a screw with each thread going around once - teeth_to_hide = 0, //number of teeth to delete to make this only a fraction of a circle - pressure_angle = 28, //Controls how straight or bulged the tooth sides are. In degrees. - clearance = 0.0, //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters) - backlash = 0.0 //gap between two meshing teeth, in the direction along the circumference of the pitch circle -) { - assign(pi = 3.1415926) - assign(p = mm_per_tooth * number_of_teeth / pi / 2) //radius of pitch circle - assign(c = p + mm_per_tooth / pi - clearance) //radius of outer circle - assign(b = p*cos(pressure_angle)) //radius of base circle - assign(r = p-(c-p)-clearance) //radius of root circle - assign(t = mm_per_tooth/2-backlash/2) //tooth thickness at pitch circle - assign(k = -iang(b, p) - t/2/p/pi*180) { //angle to where involute meets base circle on each side of tooth - difference() { - for (i = [0:number_of_teeth-teeth_to_hide-1] ) - rotate([0,0,i*360/number_of_teeth]) - linear_extrude(height = thickness, center = true, convexity = 10, twist = twist) - polygon( - points=[ - [0, -hole_diameter/10], - polar(r, -181/number_of_teeth), - polar(r, r= breite*1.5 && d > 2*bohrung); // ist Optimierung sinnvoll? + + // Zeichnung + union(){ + rotate([0,0,-phi_r-90*(1-spiel)/zahnzahl]){ // Zahn auf x-Achse zentrieren; + // macht Ausrichtung mit anderen Raedern einfacher + + linear_extrude(height = breite, twist = gamma){ + difference(){ + union(){ + zahnbreite = (180*(1-spiel))/zahnzahl+2*phi_r; + circle(rf); // Fusskreis + for (rot = [0:tau:360]){ + rotate (rot){ // "Zahnzahl-mal" kopieren und drehen + polygon(concat( // Zahn + [[0,0]], // Zahnsegment beginnt und endet im Ursprung + [for (rho = [0:schritt:rho_ra]) // von null Grad (Grundkreis) + // bis maximalen Evolventenwinkel (Kopfkreis) + pol_zu_kart(ev(rb,rho))], // Erste Evolventen-Flanke + + [pol_zu_kart(ev(rb,rho_ra))], // Punkt der Evolvente auf Kopfkreis + + [for (rho = [rho_ra:-schritt:0]) // von maximalen Evolventenwinkel (Kopfkreis) + // bis null Grad (Grundkreis) + pol_zu_kart([ev(rb,rho)[0], zahnbreite-ev(rb,rho)[1]])] + // Zweite Evolventen-Flanke + // (180*(1-spiel)) statt 180 Grad, + // um Spiel an den Flanken zu erlauben + ) + ); + } + } + } + circle(r = rm+r_loch*1.49); // "Bohrung" + } + } + } + // mit Materialersparnis + if (optimiert) { + linear_extrude(height = breite){ + difference(){ + circle(r = (bohrung+r_loch)/2); + circle(r = bohrung/2); // Bohrung + } + } + linear_extrude(height = (breite-r_loch/2 < breite*2/3) ? breite*2/3 : breite-r_loch/2){ + difference(){ + circle(r=rm+r_loch*1.51); + union(){ + circle(r=(bohrung+r_loch)/2); + for (i = [0:1:z_loch]){ + translate(kugel_zu_kart([rm,90,i*360/z_loch])) + circle(r = r_loch); + } + } + } + } + } + // ohne Materialersparnis + else { + linear_extrude(height = breite){ + difference(){ + circle(r = rm+r_loch*1.51); + circle(r = bohrung/2); + } + } + } + } +} + +/* Zahnstange und Ritzel + modul = Hoehe des Zahnkopfes ueber dem Teilkreis + laenge_stange = Laenge der Zahnstange + zahnzahl_ritzel = Anzahl der Radzaehne am Ritzel + hoehe_stange = Hoehe der Zahnstange bis zur Waelzgeraden + bohrung_ritzel = Durchmesser der Mittelbohrung des Ritzels + breite = Breite der Zaehne + eingriffswinkel = Eingriffswinkel, Standardwert = 20 grad gemaess DIN 867. Sollte nicht groesser als 45 grad sein. + schraegungswinkel = Schraegungswinkel, Standardwert = 0 grad (Geradverzahnung) + optimiert = Loecher zur Material-/Gewichtsersparnis bzw. Oberflaechenvergoesserung erzeugen, wenn Geometrie erlaubt (= 1, wenn wahr) + + Rack and Pinion + module = Height of the tooth head above the pitch circle + rack_length = Length of the rack + pinion_tooth_count = Number of teeth on the pinion + rack_height = Height of the rack up to the pitch line + pinion_bore = Diameter of the central bore of the pinion + width = Width of the teeth + contact_angle = Contact angle, default value = 20 degrees according to DIN 867. Should not exceed 45 degrees. + helix_angle = Helix angle, default value = 0 degrees (straight tooth) + optimized = Generate holes for material/weight saving or surface enhancement if geometry allows (= 1 if true) + */ +module zahnstange_und_rad (modul, laenge_stange, zahnzahl_ritzel, hoehe_stange, bohrung_ritzel, breite, eingriffswinkel=20, schraegungswinkel=0, zusammen_gebaut=true, optimiert=true) { + abstand = zusammen_gebaut? modul*zahnzahl_ritzel/2 : modul*zahnzahl_ritzel; + zahnstange(modul, laenge_stange, hoehe_stange, breite, eingriffswinkel, -schraegungswinkel); +} + +module rad_und_zahnstange (modul, laenge_stange, zahnzahl_ritzel, hoehe_stange, bohrung_ritzel, breite, eingriffswinkel=20, schraegungswinkel=0, zusammen_gebaut=true, optimiert=true) { + abstand = zusammen_gebaut? modul*zahnzahl_ritzel/2 : modul*zahnzahl_ritzel; + translate([0, abstand, 0]) { + if (istgerade(zahnzahl_ritzel)) { + rotate(90 + 180/zahnzahl_ritzel) { + stirnrad (modul, zahnzahl_ritzel, breite, bohrung_ritzel, eingriffswinkel, schraegungswinkel, optimiert); + } + } else { + rotate(a=90) { + stirnrad (modul, zahnzahl_ritzel, breite, bohrung_ritzel, eingriffswinkel, schraegungswinkel, optimiert); + } + } + } +} + +//zahnstange_und_rad (modul, laenge_stange, zahnzahl_ritzel, hoehe_stange, bohrung_ritzel, breite, eingriffswinkel, schraegungswinkel, zusammen_gebaut, optimiert); \ No newline at end of file