--- jupyter: description: 'An involute spur gear as a 3D CAD exercise: a dimensioned drawing with its gear data to model in planex2d or any CAD system, a solution from one pitch with involute flanks and root fillets repeated around the gear, a hub and a keyed bore, and the span over three teeth, the volume, mass and center of mass to check a model against.' nbsphinx: timeout: 1800 --- # Spur gear A spur gear turns a second gear on a parallel shaft. Its teeth have involute flanks. Where two involutes of the same pressure angle touch, the contact runs along one straight line as the gears turn, so the ratio of their speeds stays constant. The flanks follow from the gear's data, its module, number of teeth and pressure angle, which the drawing gives in a table, and they are drawn from the involute's formula. One pitch, a tooth and the half gaps beside it, is drawn once and repeated around the gear. Model the gear from the drawing in planex2d or in any CAD system, then check the span over three teeth, the volume, mass and center of mass against the values at the end of the page. A solution in planex2d follows. ![The spur gear exercise: a dimensioned drawing of a spur gear with 24 involute teeth, a hub and a keyed bore, in a full section and a view along its axis, with its gear data in a table and an isometric](../_static/gallery/spur-gear.png){.result} ```python import math import netgen.occ as occ import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Row, Views ``` ## The gear data The gear has a module of $m = 2$ and $z = 24$ teeth, a pressure angle of $\alpha = 20^\circ$ and no profile shift. Its pitch circle has the radius $m z / 2$, and the base circle, from which the involutes unwind, has the radius $r \cos\alpha$. The tip circle lies one module outside the pitch circle and the root circle 1.25 modules inside it: ```python m = 2 z = 24 alpha = math.radians(20) r = m * z / 2 # the pitch circle rb = r * math.cos(alpha) # the base circle ra = r + m # the tip circle rf = r - 1.25 * m # the root circle print("diameters: pitch", 2 * r, "| base", round(2 * rb, 3), "| tip", 2 * ra, "| root", 2 * rf) ``` ## The involute flanks The involute of the base circle is the path of the end of a string unwound from it. At a radius $\rho$, the string has unwound by the angle $\tan\alpha_\rho$, with $\alpha_\rho = \arccos(r_b / \rho)$, and the end lies $\mathrm{inv}\,\alpha_\rho = \tan\alpha_\rho - \alpha_\rho$ around the base circle from where the string left it, where $\mathrm{inv}$ is the involute function. On the pitch circle, a tooth is half a pitch thick, $\pi m / 2$, so its flanks lie $\pi / (2 z)$ on either side of the tooth's middle there, and $\pi / (2 z) + \mathrm{inv}\,\alpha$ at the base circle, the angle `beta`. Unwound by the angle $u$ in radians, the string's end lies at $r_b\,(\cos u + u \sin u,\ \sin u - u \cos u)$. The lower flank is this involute turned back by `beta`, and the upper flank is its mirror image across the $x$ axis. `Curve` takes both as formulas. Its angles are in degrees, so $u$ appears in radians only where it multiplies. Both run from the base circle at $u = 0$ out to the tip circle at $u_a = \sqrt{(r_a / r_b)^2 - 1}$: ```python def inv(a): return math.tan(a) - a beta = math.pi / (2 * z) + inv(alpha) # half the tooth's angle at the base circle ua = math.degrees(math.sqrt((ra / rb) ** 2 - 1)) # the roll angle at the tip circle, in degrees b = math.degrees(beta) lower = (f"{rb}*(cos(t - {b}) + t*pi/180*sin(t - {b}))", f"{rb}*(sin(t - {b}) - t*pi/180*cos(t - {b}))") upper = (lower[0], f"-({lower[1]})") def polar(rho, angle): return (rho * math.cos(angle), rho * math.sin(angle)) ``` ## One pitch One pitch, from the middle of the gap below the tooth to the middle of the gap above it, is a chain in a `QuickSketch`, with the tooth's middle on the $x$ axis. It runs along the root circle from the gap's middle, up a foot to the base circle, out along the lower flank to the tip circle, over the tip circle to the upper flank, back down the upper flank, down the other foot and along the root circle to the next gap's middle. The root circle here lies inside the base circle, where there is no involute, so the feet run radially. The base, pitch and tip circles are drawn dashed, as construction (Figure 1): ```python tip = 2 * (beta - inv(math.acos(rb / ra))) # the tooth's angle at the tip circle gear = px.QuickSketch() for radius in (rb, r, ra): gear.Circle(radius, construction=True) gear.Move(polar(rf, -math.pi / z)) gear.Arc(center=(0, 0), sweep=math.degrees(math.pi / z - beta), id="root_low") # around the gear's center gear.Line(polar(rb, -beta), id="foot_low") gear.Curve(lower, (0, ua), id="flank_low") # from the pen at its start, out along the involute gear.Arc(center=(0, 0), sweep=math.degrees(tip), id="tip") gear.Curve(upper, (ua, 0), id="flank_up") # from the tip back down to the base circle gear.Line(polar(rf, beta), id="foot_up") gear.Arc(center=(0, 0), sweep=math.degrees(math.pi / z - beta), id="root_up"); ``` ```python nbsphinx="hide-input" Row((gear, "One pitch, the base, pitch and tip circles dashed", {"fill": False, "view": ((18, 28), (-5, 5))}), caption="One pitch") ``` ## The root fillets The corners where the feet meet the root circle are rounded with a radius of 0.8. `Fillet` takes them as points, the feet's ends where the chain put them, and keeps the pieces it cuts off as construction (Figure 2): ```python gear.Fillet([polar(rf, -beta), polar(rf, beta)], 0.8); # the corners as points, exactly on the feet's ends ``` ```python nbsphinx="hide-input" Row((gear, "The root fillets of the pitch", {"fill": False, "view": ((20.5, 23.5), (-3, 3))}), caption="The root fillets") ``` ## The teeth `CircularPattern` repeats the pitch around the gear's center, 24 times in all, and the pitches join into one closed outline (Figure 3): ```python pitch = ["root_low", "Fillet.1", "foot_low", "flank_low", "tip", "flank_up", "foot_up", "Fillet.2", "root_up"] gear.CircularPattern(pitch, "origin", z, id="teeth") # the pitch and 23 copies face = gear.Face() print("edges", len(face.edges), "| area", round(face.mass, 4), "mm²") ``` ```python nbsphinx="hide-input" Row((face, "The gear's face, 24 teeth", {}), caption="The teeth") ``` ## The span over three teeth A gear is checked by its span $W_k$ over $k$ teeth, measured with a caliper whose jaws touch the flanks of $k$ teeth. The jaws touch where a line tangent to the base circle crosses the flanks, and that line crosses every involute at a right angle, so $W_k = m \cos\alpha\,\bigl(\pi\,(k - \tfrac{1}{2}) + z\,\mathrm{inv}\,\alpha\bigr)$, here for $k = 3$. The face cut by the tangent to the base circle at $(0, -r_b)$ gives the chords of that line through the teeth. The span runs from the left flank of the tooth left of the middle chord to the right flank of the tooth right of it (Figure 4): ```python k = 3 w = m * math.cos(alpha) * (math.pi * (k - 0.5) + z * inv(alpha)) chords = px.Line((-30, -rb), (30, -rb)) * face # the tangent's pieces inside the teeth ends = sorted(sorted((e.start.x, e.end.x)) for e in chords.edges) middle = min(range(len(ends)), key=lambda i: abs(sum(ends[i]))) # the chord through the tooth at the bottom span = ends[middle + 1][1] - ends[middle - 1][0] print("span over 3 teeth", round(span, 6), "| formula", round(w, 6)) ``` ```python nbsphinx="hide-input" Row((face, "The span along the tangent to the base circle", {"dashed": [px.Line((-20, -rb), (20, -rb))], "marks": [(ends[middle - 1][0], -rb), (ends[middle + 1][1], -rb)], "view": ((-14, 14), (-27, -16))}), caption="The span over three teeth") ``` ## The gear body The face is padded 20, the gear's width, along $z$. The hub, 40 across and 12 long, is padded from the gear's top face. The bore of 20 and the keyway, 6 wide and 22.8 deep from the far side of the bore, are pocketed from the hub's top. The bore goes to the last face it crosses, and the keyway runs through hub and gear, 32 deep (Figure 5): ```python body = px.Pad(face, 20) hub = px.QuickSketch(body.faces.Max(occ.Z)).Circle(20) body = body + px.Pad(hub, 12) bore = px.QuickSketch(body.faces.Max(occ.Z)).Circle(10) body = px.Pocket(body, bore, until=px.last) keyway = px.QuickSketch(body.faces.Max(occ.Z)).Move((-3, 0)).Rectangle(6, 22.8 - 10) # from the axis to its top body = px.Pocket(body, keyway, 12 + 20) ``` ```python nbsphinx="hide-input" Views(("The gear with its hub and keyed bore", [(body, "shade"), (body, "ink")]), angles=(-50, 0, 30), caption="The gear") ``` ## The checks The span over three teeth above checks the flanks. The solid's volume is compared with the volume from the areas, the face times the width of 20 and the hub's disc times 12, less the bore and the keyway over their length of 32. The keyway is the rectangle 6 wide from the axis up to its top, $22.8 - 10$, less the bore's area under it. The mass is for steel 42CrMo4 (7.85 g/cm³), and the center of mass is measured with the origin at the gear's center on its bottom face, to compare with your own model: ```python under = 3 * math.sqrt(10**2 - 3**2) + 10**2 * math.asin(3 / 10) # the bore's area above the axis, |x| < 3 volume = face.mass * 20 + math.pi * 20**2 * 12 - math.pi * 10**2 * 32 - (6 * (22.8 - 10) - under) * 32 c = body.center print("volume", round(body.mass), "mm³, from the areas", round(volume)) print("mass", round(volume * 7.85e-6, 3), "kg") print("center of mass", round(c.z, 2), "above the bottom face,", round(-c.y, 3), "off the axis, away from the keyway") ``` The gear as STEP, for another CAD system: ```python body.WriteStep("spur_gear.step") ``` The gear as STEP: [spur_gear.step](spur_gear.step) ```python nbsphinx="hidden" import sys sys.path.insert(0, "..") import matplotlib.pyplot as plt from drawings import Pictorial, SheetFigure, Table, View from gallery import save_figure # first angle: the full section through the axis in front, the view along the axis below it; the gear data in a table front = px.XZ # the front view: x to the right, z up half = body * occ.Box((-30, 0, -1), (30, 30, 40)) cut = [f for f in half.faces if abs(f.center.y) < 1e-6 and f.bounding_box[1].y - f.bounding_box[0].y < 1e-6] section = View([], solid=half, plane=front, hidden_edges=False, hatch=cut, axes=[((0, -4), (0, 36))], measures=[((-26, 20), (26, 20), 46, "⌀52"), ((-20, 32), (20, 32), 40, "⌀40"), ((-10, 0), (10, 0), -8, "⌀20"), ((26, 0), (26, 20), 34, 20), ((20, 20), (20, 32), 34, 12)]) axial = View([], solid=body, plane=px.XY, at=(0, -54), hidden_edges=False, circles=[((0, 0), r, "pitch")], axes=[((-30, 0), (30, 0)), ((0, -30), (0, 30))], measures=[((-3, 12.8), (3, 12.8), 16, 6), ((0, -10), (0, 12.8), -14, 22.8)]) data = Table([("Module m", 2), ("Number of teeth z", 24), ("Pressure angle α", "20°"), ("Profile shift x", 0), ("Pitch diameter d", 48), ("Root fillet radius", 0.8), ("Span over 3 teeth W", round(w, 3))], at=(44, 30), widths=(48, 16)) pictorial = Pictorial(body, at=(82, -54), width=45, angles=(-50, 0, 30)) drawing = SheetFigure([section, axial, data, pictorial], margin=20, notes=("planex2d.org", "3D exercise 14"), first_angle=True) print("scale", drawing.planex_scale, "| paper", [round(v * 25.4) for v in drawing.get_size_inches()], "mm") save_figure(drawing, "spur-gear", dpi=200) plt.close(drawing) ```