"""Flanged timing pulley, 2GT (GT2), 3GT (GT3) and MXL. Axis along Z, the bottom flange's outer face on the XY plane; a flange of diameter F, 1 mm thick, at each end of the width W; optionally the hub (diameter D, up to the overall length L) above the top flange. Set screws are not modelled. detail = "full": the body at its outside diameter O.D. with its tooth grooves cut in. MXL grooves follow the published rack: depth 0.64, bottom 0.67, flanks 20 degrees (28 under 24 teeth). 2GT and 3GT publish the groove depth only, so the rounded groove's width is a proportion for looks, not for design. detail = "simple": a plain cylinder at O.D. """ import math from steve import Model profile = "2GT" # Belt profile teeth = 20 # Number of teeth belt_width = 6 # mm Nominal belt width bore = 5 # mm Bore hub = False # Hub above the top flange (shape B) detail = "full" # full: tooth grooves; simple: a plain cylinder at the outside diameter # profile: {teeth: (P.D., O.D., D, F, E)}, D None: no hub offered. MISUMI FA Mechanical Components, High Torque Timing # Pulleys 2GT pp. 1381-1382, 3GT pp. 1383-1384, Timing Pulleys MXL pp. 1389-1390 (2014), Number of Teeth / Dimension tables. PULLEYS = { "2GT": { 14: (8.91, 8.40, None, 13, 6), 15: (9.55, 9.04, None, 14, 7), 16: (10.19, 9.68, None, 14, 7), 18: (11.46, 10.95, None, 16, 8), 20: (12.73, 12.22, None, 17, 9), 21: (13.37, 12.86, None, 18, 10), 22: (14.01, 13.50, 8, 18, 10), 24: (15.28, 14.77, 10, 20, 12), 25: (15.92, 15.41, 10, 20, 12), 26: (16.55, 16.04, 10, 21, 12), 28: (17.83, 17.32, 10, 23, 14), 30: (19.10, 18.59, 11, 24, 15), 32: (20.37, 19.86, 13, 25, 17), 34: (21.65, 21.14, 14, 27, 18), 36: (22.92, 22.41, 14, 27, 18), 38: (24.19, 23.68, 16, 29, 20), 40: (25.46, 24.95, 17, 30, 21), 44: (28.01, 27.50, 19, 32, 23), 48: (30.56, 30.05, 22, 35, 26), 50: (31.83, 31.32, 22, 36, 27), 60: (38.20, 37.69, 28, 42, 33), }, "3GT": { 16: (15.28, 14.52, None, 19, 11), 18: (17.19, 16.43, None, 21, 12), 20: (19.10, 18.34, None, 23, 14), 22: (21.01, 20.25, 12, 24, 16), 24: (22.92, 22.16, 14, 26, 18), 25: (23.87, 23.11, 14, 27, 18), 26: (24.83, 24.07, 15, 28, 19), 28: (26.74, 25.98, 17, 30, 21), 30: (28.65, 27.89, 19, 32, 23), 32: (30.56, 29.80, 20, 34, 25), 34: (32.47, 31.71, 22, 36, 27), 36: (34.38, 33.62, 24, 38, 29), 40: (38.20, 37.44, 28, 42, 33), 44: (42.02, 41.26, 30, 46, 36), 48: (45.84, 45.08, 32, 49, 40), 50: (47.75, 46.99, 34, 51, 42), 60: (57.30, 56.54, 36, 61, 52), }, "MXL": { 20: (12.94, 12.43, 9, 18, 11), 21: (13.58, 13.07, 9, 18, 11), 22: (14.23, 13.72, 9, 18, 11), 23: (14.88, 14.37, 11, 20, 13), 24: (15.52, 15.02, 11, 20, 13), 25: (16.17, 15.66, 11, 20, 13), 26: (16.82, 16.31, 12, 22, 14), 27: (17.46, 16.96, 12, 22, 14), 28: (18.11, 17.60, 12, 22, 14), 30: (19.40, 18.90, 14, 25, 16), 32: (20.70, 20.19, 14, 25, 16), 34: (21.99, 21.48, 14, 25, 16), 36: (23.29, 22.78, 16, 28, 18), 38: (24.58, 24.07, 16, 28, 18), 40: (25.87, 25.36, 18, 30, 20), 42: (27.17, 26.66, 18, 30, 20), 44: (28.46, 27.95, 20, 32, 23), 46: (29.75, 29.24, 20, 32, 23), 48: (31.05, 30.54, 22, 35, 25), 50: (32.34, 31.83, 22, 35, 25), 60: (38.81, 38.30, 28, 44, 32), 72: (46.57, 46.06, 30, 52, 40), }, } # profile: {nominal belt width: (A, W, L)}: Belt Nominal Width / Dimension tables of the same pages. WIDTHS = { "2GT": {6: (7.0, 10.3, 18.0), 9: (10.0, 13.3, 21.0)}, "3GT": {6: (7.3, 11.0, 19.0), 9: (10.3, 14.0, 22.0), 15: (16.3, 20.0, 28.0)}, "MXL": {4.8: (6.0, 10.0, 16.5), 6.4: (7.5, 11.5, 18.0), 9.5: (11.0, 15.0, 22.0), 12.7: (14.0, 18.0, 25.0)}, } FLANGE_THICKNESS = 1.0 # t1.0 on the same pages' drawings if teeth not in PULLEYS[profile]: raise ValueError(f"{profile} tooth counts in the source: {', '.join(str(t) for t in PULLEYS[profile])}.") if belt_width not in WIDTHS[profile]: raise ValueError(f"{profile} belt widths: {', '.join(str(w) for w in WIDTHS[profile])} mm.") pd, od, d_hub, f, e = PULLEYS[profile][teeth] a, w, l = WIDTHS[profile][belt_width] if hub and d_hub is None: raise ValueError(f"{profile} {teeth} teeth: the source offers no hub for this size.") if bore + 2 > e: raise ValueError(f"Bore {bore} leaves no wall under the teeth: at most {e - 2} mm.") # profile: groove depth, from the Standard Tooth Profile drawings of the same pages (2GT 0.75; 3GT 1.14; MXL 0.64). GROOVE_DEPTH = {"2GT": 0.75, "3GT": 1.14, "MXL": 0.64} PITCH = {"2GT": 2.0, "3GT": 3.0, "MXL": 2.032} GROOVE_WIDTH = 0.62 # x pitch: mouth of the rounded 2GT and 3GT grooves, a proportion for looks MXL_BOTTOM = 0.67 # bottom of the MXL groove, same pages if teeth not in PULLEYS[profile]: raise ValueError(f"{profile} tooth counts in the source: {', '.join(str(t) for t in PULLEYS[profile])}.") if belt_width not in WIDTHS[profile]: raise ValueError(f"{profile} belt widths: {', '.join(str(w) for w in WIDTHS[profile])} mm.") pd, od, d_hub, f, e = PULLEYS[profile][teeth] a, w, l = WIDTHS[profile][belt_width] if hub and d_hub is None: raise ValueError(f"{profile} {teeth} teeth: the source offers no hub for this size.") if bore + 2 > e: raise ValueError(f"Bore {bore} leaves no wall under the teeth: at most {e - 2} mm.") model = Model() radius = od / 2 depth = GROOVE_DEPTH[profile] if detail == "simple": with model.sketch("body_sketch", on="XY", name="Toothed body") as sk: sk.circle("body_od", (0, 0), radius) else: if profile == "MXL": flank = math.tan(math.radians(28 if teeth < 24 else 20)) half_bottom = MXL_BOTTOM / 2 groove = [(half_bottom + depth * flank, 0.0), (half_bottom, depth), (-half_bottom, depth), (-half_bottom - depth * flank, 0.0)] else: half_mouth = GROOVE_WIDTH * PITCH[profile] / 2 groove = [(half_mouth * math.cos(math.pi * i / 6), depth * math.sin(math.pi * i / 6)) for i in range(7)] outline = [] for n in range(teeth): centre = 2 * math.pi * n / teeth for u, v in groove: angle = centre - u / radius outline.append(((radius - v) * math.sin(angle), (radius - v) * math.cos(angle))) with model.sketch("body_sketch", on="XY", name="Toothed body") as sk: sk.polyline("body_outline", outline, closed=True) model.extrude("body", "body_sketch", distance=w, name="Toothed body") with model.sketch("flange_low_sketch", on="XY", name="Lower flange") as sk: sk.circle("flange_low_od", (0, 0), f / 2) model.extrude("flange_low", "flange_low_sketch", distance=FLANGE_THICKNESS, name="Lower flange") model.plane("flange_plane", on="XY", offset=w - FLANGE_THICKNESS, name="Upper flange face") with model.sketch("flange_high_sketch", on="flange_plane", name="Upper flange") as sk: sk.circle("flange_high_od", (0, 0), f / 2) model.extrude("flange_high", "flange_high_sketch", distance=FLANGE_THICKNESS, name="Upper flange") top = w if hub: model.plane("hub_plane", on="XY", offset=w, name="Hub base") with model.sketch("hub_sketch", on="hub_plane", name="Hub") as sk: sk.circle("hub_od", (0, 0), d_hub / 2) model.extrude("hub_body", "hub_sketch", distance=l - w, name="Hub") top = l with model.sketch("bore_sketch", on="XY", name="Bore") as sk: sk.circle("bore_d", (0, 0), bore / 2) model.extrude("bore_cut", "bore_sketch", distance=top, op="cut", name="Bore") model.material("aluminum-generic", color="#c9ccd0") result = model.result()