RasmahRasmah

Drawing a Spur Gear from Scratch

using Rasmah

Overview

A spur gear is the standard machine element of mechanical power transmission: two gears mesh and exchange torque in the ratio of their tooth counts — a 20-tooth gear driving a 10-tooth gear doubles the speed and halves the torque. Gears are everywhere a shaft turns: gearboxes, lathes, pumps, wind-turbine yaw drives, automotive timing trains.

This tutorial draws a real one from scratch, the way a CAD program would: first the tooth’s involute flank (the curve that makes gear teeth roll smoothly instead of sliding), then one tooth as a closed sketch, then the 20-tooth blank by a circular pattern over a root disc, an extrusion to the face width, and a boolean bore for the shaft. Along the way every drawing feature gets its moment: the profile sketch, the pattern, the extrusion, the boolean — and the result is verified against the module-system formulas from the machinery’s handbook: the outside diameter m(z+2), the tooth thickness at the pitch circle πm/2, and the mass of the finished part.

The module system

Gear dimensions are governed by one number: the module m (in millimetres), the ratio of the pitch diameter to the tooth count z. All the standard diameters follow from m and z — the pitch circle, where the two meshing gears touch:

\[ d = m z, \]

the addendum circle (the tooth tips, the outside diameter):

\[ d_a = m(z + 2), \]

the dedendum circle (the tooth roots):

\[ d_f = m(z - 2.5), \]

and the base circle from which the involute flanks unwind, set by the pressure angle (20° is the standard) d_b = d\cos 20°. Our gear: module 2, 20 teeth, 12 mm face width, with a Ø12 shaft bore.

m, z, φ = 2e-3, 20, deg2rad(20.0)          # module 2, 20 teeth, 20° pressure angle
rp = m * z / 2                              # pitch radius  (20 mm)
ra = rp + m                                 # addendum (tip) radius (22 mm)
rf = rp - 1.25 * m                          # dedendum (root) radius (17.5 mm)
rb = rp * cos(φ)                            # base radius (~18.8 mm)

println("pitch  diameter: ", round(2rp * 1e3; digits = 1), " mm")
println("outside diameter: ", round(2ra * 1e3; digits = 1), " mm  (= m(z+2) = 44 mm)")
println("root   diameter: ", round(2rf * 1e3; digits = 1), " mm")
println("base   diameter: ", round(2rb * 1e3; digits = 1), " mm")
pitch  diameter: 40.0 mm
outside diameter: 44.0 mm  (= m(z+2) = 44 mm)
root   diameter: 35.0 mm
base   diameter: 37.6 mm

The involute flank

Two gears roll without slipping when their teeth touch along a curve called the involute of the base circle. The picture behind it: wrap a string around the base circle, fix a pen at the string’s end, and unwind it while keeping it taut — the pen traces the involute. Unwinding by roll angle t places the pen at

\[ x(t) = r_b (\cos t + t \sin t), \qquad y(t) = r_b (\sin t - t \cos t), \]

which starts on the base circle (t = 0) and rolls out to the tooth tip at t = sqrt((r_a/r_b)² − 1). A real tooth is built from two mirrored involute flanks, offset from the tooth’s centerline by half the tooth’s angular thickness at the base circle, δ = πm/(2d) + inv φ (the involute function inv φ = tan φ − φ), closed by a tip arc and a root arc:

inv(a) = tan(a) - a
δ = (π * m / 2) / (2 * rp) + inv(φ)         # half tooth angle at the base circle
tmax = sqrt((ra / rb)^2 - 1)                # roll angle reaching the tooth tip

involute(t) = rb .* [cos(t) + t * sin(t), sin(t) - t * cos(t)]
rot(p, θ)   = [p[1]*cos(θ) - p[2]*sin(θ), p[1]*sin(θ) + p[2]*cos(θ)]

flank1 = [rot(involute(t), -δ) for t in range(0, tmax; length = 10)]
tip_a1 = -δ + inv(acos(rb / ra))            # flank tip angle
tip    = [[ra*cos(θ), ra*sin(θ)] for θ in range(tip_a1, -tip_a1; length = 9)]
flank2 = [[x, -y] for (x, y) in reverse(flank1)]
root   = [[rf*cos(θ), rf*sin(θ)] for θ in range(δ, -δ; length = 7)]

tooth = polygon_2d(vcat(flank1, tip[2:end], flank2[2:end], root[2:end]))
println("one tooth area: ", round(ustrip(uconvert(u"mm^2", area(tooth))); digits = 2), " mm²")
one tooth area: 12.83 mm²

That tooth is one closed polygon_2d sketch — an exact straight-edged profile whose area is the shoelace formula. Extruded to the face width, it is the single tooth of the gear:

vtk(extrude(tooth, 12e-3); n = 24)

From one tooth to the gear blank

The gear is the root disc plus 20 teeth spaced one angular pitch apart — the exact job of the circular pattern:

# 1. one tooth, 2. repeated 20× about the centre, 3. fused to the root disc
sketch = union(circle_2d(rf), circular_pattern(tooth, z))
gear   = extrude(sketch, 12e-3)
Extrude{Union2D{Circle2D{Float64}, CircularPattern2D{Polygon2D{Float64}, Int64}}, Float64}(Union2D{Circle2D{Float64}, CircularPattern2D{Polygon2D{Float64}, Int64}}(Circle2D{Float64}(0.0175), CircularPattern2D{Polygon2D{Float64}, Int64}(Polygon2D{Float64}([[0.01871185984919612, -0.0017536218542883628], [0.018754762491244293, -0.0017556986072587589], [0.0188836049802581, -0.0017541869593180259], [0.0190977059623901, -0.0017374870665281146], [0.019395210511419285, -0.0016941012664606987], [0.019773102610928947, -0.0016127395107191531], [0.0202272265066444, -0.001482423457236963], [0.02075231679676249, -0.0012925885067626604], [0.021342037072044656, -0.00103318308022131], [0.02198902686247645, -0.0006947644502195804]  …  [0.0190977059623901, 0.0017374870665281146], [0.0188836049802581, 0.0017541869593180259], [0.018754762491244293, 0.0017556986072587589], [0.01871185984919612, 0.0017536218542883628], [0.01746605390817197, 0.0010894773411276907], [0.017491511418299584, 0.000545003030721174], [0.0175, 0.0], [0.017491511418299584, -0.000545003030721174], [0.01746605390817197, -0.0010894773411276907], [0.017423652166549113, -0.00163289472382896]]), 20)), 0.012)
println("gear face area: ", round(ustrip(uconvert(u"mm^2", area(sketch))); digits = 1), " mm²")
println("handbook:       ", round((π * rf^2 + z * ustrip(area(tooth))) * 1e6; digits = 1), " mm²  (disc + z teeth)")
gear face area: 1217.1 mm²
handbook:       1218.7 mm²  (disc + z teeth)

The two agree to a fraction of a percent — the sketch area is the disc plus twenty tooth areas, the decomposition the handbook would write down. The patterned gear blank:

vtk(gear; n = 16)

The tooth thickness at the pitch circle

The defining gear check is the tooth thickness at the pitch circle: by construction it must equal half the circular pitch, πm/2 = 3.14 mm — this is the number that makes one gear’s tooth exactly fill half a pitch, so its mate fills the other half. Measure it from the sketch itself: scan the signed distance field along the pitch circle and count the angular span where material is present (teeth stick out past the root disc, so at r = r_p only teeth register):

θs = range(0, 2π; length = 4000)
inside = sketch.(rp .* cos.(θs), rp .* sin.(θs)) .< 0
thickness = rp * sum(inside) / 4000 * 2π / z        # per tooth
println("measured tooth thickness: ", round(thickness * 1e3; digits = 3), " mm")
println("handbook π m / 2:        ", round(π * m / 2 * 1e3; digits = 3), " mm")
measured tooth thickness: 3.135 mm
handbook π m / 2:        3.142 mm

The involute does what the theory says: the flank leaves exactly the right width of metal at the pitch circle for a conjugate mate to roll against.

The shaft bore and the mass

Finish the part the way a drawing office would: drill the shaft seat with a boolean difference, then weigh the result:

gear = difference(gear, cylinder(6e-3, 0.014))       # Ø12 bore through the hub

V = (ustrip(area(sketch)) - π * (6e-3)^2) * 0.012    # exact: face area − bore, × width
mass = density(steel) * V * 1e3
println("finished mass: ", round(mass; digits = 1), " g  (", round(V * 1e9; digits = 0), " mm³)")
finished mass: 104.0 g  (13248.0 mm³)

A 20-tooth steel gear blank of module 2 weighs about 104 g — the number the purchasing department asks for. The classic estimate a designer would use — a solid disc to the pitch diameter, minus the bore — comes out a bit heavier, because it fills in the tooth spaces:

V_blank = (π * rp^2 - π * (6e-3)^2) * 0.012
println("pitch-blank estimate: ", round(density(steel) * V_blank * 1e3; digits = 1), " g  (over by ",
        round((V_blank / V - 1) * 100; digits = 1), "%)")
pitch-blank estimate: 107.7 g  (over by 3.6%)

Verify against the handbook, and mesh

The last check is geometric: the outside diameter must be m(z+2) = 44 mm. Read it straight from the gear’s bounds, and mesh a copy of the finished part for display:

lo, hi = bounds(gear)
println("outside diameter: ", round((hi[1] - lo[1]) * 1e3; digits = 1), " mm  (handbook 44.0 mm)")
outside diameter: 44.0 mm  (handbook 44.0 mm)
sm = surface_mesh(gear; n = 20)

vtk(sm)

The bounds check is exact (44.0 mm), and the mass above came from the exact face-area arithmetic rather than the mesh — on a part with thin tooth tips a display mesh fine enough for volume would need to be much finer than the one used here, which is why the exact area-based volume is the number to trust. The part is done: one involute tooth, patterned, extruded, bored, verified — a parametric gear, ready to be mated with its partner in assembly mates or cut on a turning or milling machine.

A meshing pair

A gear alone transmits nothing — gears work in pairs. The construction above was one line away from parametric all along: pack it into a function of the tooth count, and the 20-tooth gear’s mate is a 10-tooth pinion made by the same recipe:

spur(z) = begin
    rp, ra, rf = m * z / 2, m * z / 2 + m, m * z / 2 - 1.25m
    rb = rp * cos(φ)
    δ = (π * m / 2) / (2 * rp) + inv(φ)
    tmax = sqrt((ra / rb)^2 - 1)
    involute(t) = rb .* [cos(t) + t * sin(t), sin(t) - t * cos(t)]
    rot(p, θ) = [p[1]*cos(θ) - p[2]*sin(θ), p[1]*sin(θ) + p[2]*cos(θ)]
    f1 = [rot(involute(t), -δ) for t in range(0, tmax; length = 10)]
    tip_a1 = -δ + inv(acos(rb / ra))
    tip = [[ra*cos(θ), ra*sin(θ)] for θ in range(tip_a1, -tip_a1; length = 9)]
    f2 = [[x, -y] for (x, y) in reverse(f1)]
    root = [[rf*cos(θ), rf*sin(θ)] for θ in range(δ, -δ; length = 7)]
    union(circle_2d(rf), circular_pattern(polygon_2d(vcat(f1, tip[2:end], f2[2:end], root[2:end])), z))
end

pinion = spur(10)
Union2D{Circle2D{Float64}, CircularPattern2D{Polygon2D{Float64}, Int64}}(Circle2D{Float64}(0.0075), CircularPattern2D{Polygon2D{Float64}, Int64}(Polygon2D{Float64}([[0.009258294922556257, -0.0016081658132536976], [0.00929464094312232, -0.0016122961208620422], [0.009404304132431054, -0.001616104417586764], [0.009586941749739022, -0.0016066773247084161], [0.009840505717465078, -0.0015712095887893312], [0.010161263437205654, -0.0014972039572971757], [0.01054384052869658, -0.0013726678374806074], [0.010981285052572756, -0.0011863044344969493], [0.01146515254282593, -0.0009276961201552371], [0.011985610946512555, -0.0005874778624241584]  …  [0.009586941749739022, 0.0016066773247084161], [0.009404304132431054, 0.001616104417586764], [0.00929464094312232, 0.0016122961208620422], [0.009258294922556257, 0.0016081658132536976], [0.007450756478512776, 0.0008580372357304208], [0.007487678999157409, 0.00042972456943615777], [0.0075, 0.0], [0.007487678999157409, -0.00042972456943615777], [0.007450756478512776, -0.0008580372357304208], [0.007389353750708224, -0.0012835307346901752]]), 10))

To mesh, the pinion’s gap must face the gear’s tooth: rotate the pinion by half of one angular pitch, and place its centre one centre-distance away — a = (z1 + z2)·m/2 = 30 mm, exactly r_p1 + r_p2:

pair = union(sketch, translate(rotate(pinion, π / 10), 30e-3, 0.0))

println("gear ratio:    ", 20, ":", 10, " = 2:1")
println("centre distance: ", round((20 + 10) * m / 2 * 1e3; digits = 1), " mm  = r_p1 + r_p2")
gear ratio:    20:10 = 2:1
centre distance: 30.0 mm  = r_p1 + r_p2
vtk(extrude(pair, 12e-3); n = 16)

The pair turns at 2:1 — the pinion does two revolutions for every one of the gear — and because both flanks came from the same involute, the teeth roll on each other without sliding or binding: that is the entire reason the involute exists. Swap the tooth count, and the same six lines draw a 15:12 reduction, a 60-tooth timing gear, or the idler in between.

Next steps