9.2 Spur gears
“Involute teeth differ from the epicycloidal teeth … in having the entire side of the tooth, both face and flank, formed of a continuous curve.”
— Robert Willis, Principles of Mechanism, 1841
A spur gear passes rotation between parallel shafts through teeth cut straight across its rim. A pair of gears should turn at a fixed ratio, so that a motor at constant speed drives its load at constant speed, and the shape of the teeth decides whether it does. Almost every gear in use has flanks shaped as involutes of a circle [1].
Why the shape of a tooth matters
The gear of logos and icons has trapezoidal teeth. Figure 9.2.1 runs two such gears beside two gears with involute teeth. In both pairs the left gear turns at constant speed and the right gear is moved by the teeth alone. Both pairs have the same small play between the teeth, since straight teeth with the full thickness of half a pitch do not fit into each other at all.
In each frame the left gear is turned to its angle, and the right gear is turned back as far as it will go until a tooth meets a tooth: a load on its shaft holds it there. Bisection finds that angle, the largest lag at which the two outlines do not overlap.
The straight teeth drive the right gear in jerks. Its speed rises to 1.4 times the speed of the driver when a new pair of teeth takes over, falls to 0.7 as that pair rolls apart, and jumps back when the next pair arrives. A motor at constant speed then shakes its load twelve times per revolution, and every jump is an impact between two teeth. The involute pair turns at the same speed throughout, to the accuracy of the computation.
A constant ratio requires that the common normal of the two flanks at their point of contact passes through a fixed point on the line of centres at every instant [1]. This is the fundamental law of gearing. The involute satisfies it, and it goes on satisfying it when the centre distance is wrong.
The involute of a circle
The involutesv. evolvent is the curve traced by the end of a taut string as it unwinds from a cylinder. Writing \(r\) for the radius of that cylinder and \(\alpha\) for the angle through which the string has unwound, the point on the curve is the tangency point on the circle plus the unwound length \(r\alpha\) measured along the tangent,
\[ \begin{bmatrix} x(\alpha) \\ y(\alpha) \end{bmatrix} = r \begin{bmatrix} \sin\alpha \\ \cos\alpha \end{bmatrix} + r\alpha \begin{bmatrix} -\cos\alpha \\ \sin\alpha \end{bmatrix} \tag{9.2.1}\]
At \(\alpha = 0\) the curve lies on the base circle itself, at the cusp, and as \(\alpha\) grows it spirals outward. Figure 9.2.2 draws it.
The string also shows the property that matters for gearing. Differentiating 9.2.1 with respect to \(\alpha\), the terms in \(r\) cancel and leave \(r\alpha\,[\sin\alpha,\ \cos\alpha]^\mathsf{T}\), a vector along the radius to the tangency point. The end of the string therefore moves at right angles to the string, as the end of a rod pivoted at the tangency point would. The string is normal to the involute at every point, and the normal to an involute is always tangent to its base circle.
A crossed string is an ideal gear pair
Wind a string around two circles of radii \(r_{b1}\) and \(r_{b2}\) so that it crosses between them, as in Figure 9.2.3, and pull it taut. The string does not stretch, so it moves at one speed \(v\) along its whole length, and the rim of each circle moves with the string where the string leaves it. This gives
\[ v = \omega_1 r_{b1} = \omega_2 r_{b2}, \qquad \frac{\omega_1}{\omega_2} = \frac{r_{b2}}{r_{b1}} \tag{9.2.2}\]
The ratio is constant and fixed by the two radii alone, and the crossed string turns the circles in opposite directions, as two gears in mesh turn. This is already the gear pair we want, except that a string can only pull.
To make the pair push, we follow one point of the string. Seen from circle 1, the point moves away from the circle as the string unwinds, so it traces an involute of circle 1, fixed to that circle and turning with it. Seen from circle 2, the same point traces an involute of circle 2 as the string winds on.
Figure 9.2.4 draws both. The two involutes always pass through the point of the string, and at that point both are normal to the string, by the property of the previous section. Two curves with a common normal at a common point have a common tangent there, so they touch without crossing, and the figure shows them sliding on one another, always touching on the string.
Pushing one involute with the other therefore moves the circles exactly as the string did, and two teeth shaped as these involutes can replace the string. Their point of contact travels along the line the string occupied, and the force between them, normal to both flanks, acts along that line.
The construction has a property that no other tooth shape shares. Figure 9.2.5 moves the circles apart while they turn. The involutes belong to the circles and do not change, the string stays tangent to both, and the ratio stays \(r_{b2}/r_{b1}\), because 9.2.2 does not contain the distance between the centres. What changes is the angle of the string. The radii to the two tangency points are both perpendicular to the string and point in opposite directions, so together they span the projection of the centre distance \(a\) onto the normal of the string. Measured from the normal to the line of centres, the string therefore makes the angle
\[ \cos\varphi' = \frac{r_{b1} + r_{b2}}{a} \tag{9.2.3}\]
and pulling the circles apart tilts it. For gears this means that an error in the centre distance, from the housing, the bearings or a printed part that came out slightly large, leaves the ratio exactly as designed. The teeth meet at a different angle and with some play, which the chapter on gear transmissions quantifies. This tolerance is why the involute displaced the cycloid during the nineteenth century and why it is now the only profile in general use [1].
Anatomy of a gear tooth
A gear is the construction above with teeth cut into the circles. Each flank is a piece of an involute of the base circle (Figure 9.2.6), and the rest of the tooth is bounded by four concentric circles. The base circlesv. bascirkel of radius \(r_b\) is where the involutes start. The pitch circlesv. delningscirkel of radius \(r_p\) rolls on the pitch circle of the mating gear without sliding, and along it the teeth repeat at the circular pitchsv. delning \(p\), the arc from one tooth to the next. Two gears in mesh must have the same pitch, or the teeth of one would not fit the spaces of the other, and gear standards fix it through the modulesv. modul \(m = p/\pi\). Counting the circumference of the pitch circle tooth by tooth for \(N\) teeth gives
\[ \pi d = Np = N\pi m \quad\Rightarrow\quad d = mN, \qquad r_p = \frac{mN}{2} \tag{9.2.4}\]
so the module is the pitch diameter per tooth, in millimetres. The tip circlesv. topcirkel and the root circlesv. fotcirkel bound the tooth radially, the addendum \(h_a\) above the pitch circle and the dedendum \(h_f\) below it (Table 9.2.1).
The base circle follows from the mesh. Figure 9.2.8 starts from the picture of Figure 9.2.4, base circles and string, and puts the teeth on it. The string becomes the line of actionsv. ingreppslinje, the common tangent of the two base circles along which the teeth touch and push. It crosses the line of centres at the pitch point \(P\), where the two pitch circles touch, and the angle it makes with their common tangent is the pressure anglesv. ingreppsvinkel \(\varphi\): the angle \(\varphi'\) of 9.2.3 with the gears at their nominal distance. The radius \(OT\) to the point where the line of action touches the base circle is perpendicular to the line, and the radius \(OP\) is perpendicular to the common tangent, so the two radii meet at \(\varphi\) as well. The right triangle \(OTP\) of Figure 9.2.7 gives
\[ \boxed{ r_b = r_p\cos\varphi } \tag{9.2.5}\]
Code
import matplotlib.patheffects as pe
from matplotlib.patches import Arc
halo = [pe.withStroke(linewidth=3, foreground='white')] # labels stay legible over lines
BLUE, ORANGE = '#4472c4', '#c07a2c' # line of action and base circle, as in @fig-sg-mesh
phi_d = np.radians(20) # pressure angle
r_p = 1.0 # pitch radius, the scale of the drawing
r_b = r_p*np.cos(phi_d) # @eq-sg-base, the result the triangle shows
O, P = np.zeros(2), np.array([0, r_p]) # centre and pitch point
T = r_b*np.array([-np.sin(phi_d), np.cos(phi_d)]) # OT turned by phi from OP
d = np.array([np.cos(phi_d), np.sin(phi_d)]) # along the line of action, normal to OT
fig, ax = plt.subplots(figsize=(6, 5.6))
s = np.linspace(-0.5, 0.6, 300)
ax.plot(r_p*np.sin(s), r_p*np.cos(s), '--', color='k', lw=1) # pitch circle
ax.plot(r_b*np.sin(s), r_b*np.cos(s), color=ORANGE, lw=1.5) # base circle
ax.plot(*np.c_[T - 0.12*d, P + 0.55*d], color=BLUE, lw=2) # line of action
ax.plot([-0.45, 0.45], [r_p, r_p], color='0.4', lw=1) # common tangent at P
ax.plot([0, 0], [0, 1.2], ls='--', color='0.4', lw=0.8) # line of centres
ax.plot(*np.c_[O, T], color=ORANGE, lw=1.8) # radius OT
ax.plot(*np.c_[O, P], color='k', lw=1.8) # radius OP
u = -T/r_b # right angle at T: radius meets the line
ax.plot(*np.c_[T + 0.04*u, T + 0.04*(u + d), T + 0.04*d], color='k', lw=0.8)
# phi at O between OP and OT, and phi at P between the tangent and the line of action
ax.add_patch(Arc(O, 0.5, 0.5, theta1=90, theta2=90 + np.degrees(phi_d), color='k', lw=1.2))
ax.text(-0.05, 0.32, r'$\varphi$', fontsize=13, ha='center', va='center', path_effects=halo)
ax.add_patch(Arc(P, 0.5, 0.5, theta1=0, theta2=np.degrees(phi_d), color='k', lw=1.2))
ax.text(0.3, r_p + 0.05, r'$\varphi$', fontsize=13, ha='left', va='center', path_effects=halo)
for pt, name, off in ((O, '$O$', (0.0, -0.06)), (T, '$T$', (-0.065, -0.065)),
(P, '$P$', (-0.04, 0.06))):
ax.plot(*pt, 'o', color='k', ms=4)
ax.text(pt[0] + off[0], pt[1] + off[1], name, fontsize=13, ha='center',
va='center', path_effects=halo)
ax.text(*(0.55*T + [-0.08, 0]), '$r_b$', color=ORANGE, fontsize=13, ha='center',
path_effects=halo)
ax.text(0.04, 0.62, '$r_p$', fontsize=13, path_effects=halo)
ax.text(*(P + 0.42*d + [-0.02, 0.05]), 'line of action', color=BLUE, fontsize=11,
rotation=np.degrees(phi_d), rotation_mode='anchor', path_effects=halo)
ax.text(0.5, r_b*np.cos(0.55) - 0.08, 'base circle', color=ORANGE, fontsize=11,
ha='center', path_effects=halo)
ax.text(0.47, r_p*np.cos(0.5) + 0.07, 'pitch circle', fontsize=11, ha='center',
path_effects=halo)
ax.set_aspect('equal')
ax.set_xlim(-0.5, 0.65)
ax.set_ylim(-0.1, 1.25)
ax.axis('off')
plt.show() | circle | radius | measured from the pitch circle |
|---|---|---|
| tip | \(r_a = r_p + m\) | addendum \(h_a = m\) |
| pitch | \(r_p = mN/2\) | |
| base | \(r_b = r_p\cos\varphi\), 9.2.5 | |
| root | \(r_f = r_p - 1.25\,m\) | dedendum \(h_f = 1.25\,m\) |
The extra quarter module of the dedendum is the bottom clearance \(c_P = 0.25\,m\) that leaves room for the tip of the mating tooth. These proportions and the \(20°\) pressure angle are the standard basic rack tooth profile of ISO 53 [2].
With 9.2.2 and 9.2.4, the ratio of the pair is
\[ \frac{\omega_1}{\omega_2} = \frac{r_{b2}}{r_{b1}} = \frac{r_{p2}}{r_{p1}} = \frac{N_2}{N_1} \tag{9.2.6}\]
the ratio of the tooth numbers. It holds at every instant, since the base circles turn as if joined by the string. The smaller gear of a pair is the pinionsv. drev and the larger the wheelsv. hjul. In most drives the pinion sits on the motor shaft and drives the wheel, which then turns \(N_2/N_1\) times slower than the motor. The names refer to size alone: in a speed increaser, such as the gearbox of a wind turbine, the wheel drives the pinion.
The contact points in Figure 9.2.8 run along the line of action only between the two tip circles, since a tooth cannot touch outside its own tip. Successive teeth follow one another along the line at the base pitch \(p_b = p\cos\varphi\), and the length of contact measured in base pitches is the contact ratio, 1.6 for the 20 and 30 teeth of the figure.
Whether the involute reaches all the way down to the root depends on the tooth count. Setting \(r_f = r_b\) and solving for \(N\) gives the crossover
\[ N = \frac{2.5}{1 - \cos\varphi} \tag{9.2.7}\]
which is \(41.5\) teeth at \(20°\). Below that the root circle lies inside the base circle, and the part of the flank between root and base is not an involute, because no involute exists there. Above it the involute covers the full flank and the two circles have swapped places. The mating tooth never touches the flank inside the base circle, since the line of action ends where it touches the base circle.
Tooth thickness
The flanks lean towards each other as they rise, so a tooth is thinner at its tip than at its pitch circle. To find the thickness at any radius we need two facts about a point \(P\) of the involute: its distance from the centre, and the angle at which it lies. Both come from the triangle that the string forms with the centre \(O\) of the base circle (Figure 9.2.9).
Code
import matplotlib.patheffects as pe
from matplotlib.patches import Arc
halo = [pe.withStroke(linewidth=3, foreground='white')] # labels stay legible over lines
BLUE, ORANGE, INV, GREEN = '#236b8e', '#c07a2c', '#1f4e79', '#2e8b57'
r_b, alpha = 1.0, 1.2 # base radius, unwound angle [rad]
phi_r = np.arctan(alpha) # the angle of the triangle at O
psi = alpha - phi_r # polar angle of P measured from the cusp
def at(a, r): # angles run clockwise from the y-axis, as in @eq-involute
return np.array([r*np.sin(a), r*np.cos(a)])
def involute(a): # @eq-involute
return r_b*np.array([np.sin(a) - a*np.cos(a), np.cos(a) + a*np.sin(a)])
O, C, T, P = np.zeros(2), at(0, r_b), at(alpha, r_b), involute(alpha)
fig, ax = plt.subplots(figsize=(6, 5.2))
s = np.linspace(-0.25, 1.9, 300)
ax.plot(r_b*np.sin(s), r_b*np.cos(s), color='0.55', lw=1) # base circle
ax.plot(*involute(np.linspace(0, 1.45, 200)), color=INV, lw=2) # the involute
ax.plot([0, 0], [0, 1.75], ls='--', color='0.4', lw=0.8) # ray through the cusp
ax.plot(*np.c_[O, T], color=BLUE, lw=1.8) # radius OT
ax.plot(*np.c_[T, P], color=ORANGE, lw=1.8) # free string TP
ax.plot(*np.c_[O, P], color='k', lw=1.2) # radius OP
arc = np.linspace(0, alpha, 100) # the unwound arc CT
ax.plot(r_b*np.sin(arc), r_b*np.cos(arc), color=ORANGE, lw=3, alpha=0.45)
u, v = (O - T)/r_b, (P - T)/np.linalg.norm(P - T) # right angle at T: string meets radius
ax.plot(*np.c_[T + 0.07*u, T + 0.07*(u + v), T + 0.07*v], color='k', lw=0.8)
def angle(a0, a1, rad, col, text, rt): # angle at O between the rays at a0 and a1
ax.add_patch(Arc((0, 0), 2*rad, 2*rad, theta1=90 - np.degrees(a1),
theta2=90 - np.degrees(a0), color=col, lw=1.2))
ax.text(*at((a0 + a1)/2, rt), text, color=col, ha='center', va='center',
fontsize=13, path_effects=halo)
angle(0, psi, 0.62, GREEN, r'$\psi$', 0.75) # alpha = psi + phi_r, read off at O
angle(psi, alpha, 0.42, 'k', r'$\varphi_r$', 0.53)
angle(0, alpha, 0.22, BLUE, r'$\alpha$', 0.15)
for pt, name, off in ((O, '$O$', (-0.08, -0.06)), (C, '$C$', (-0.09, 0.05)),
(T, '$T$', (0.07, -0.03)), (P, '$P$', (0.0, 0.09))):
ax.plot(*pt, 'o', color='k', ms=4)
ax.text(pt[0] + off[0], pt[1] + off[1], name, fontsize=13, ha='center',
va='center', path_effects=halo)
ax.text(*((O + T)/2 + [0.05, 0.07]), '$r_b$', color=BLUE, fontsize=13, path_effects=halo)
ax.text(*((T + P)/2 + [0.07, 0.05]), r'$r_b\alpha$', color=ORANGE, fontsize=13,
path_effects=halo)
ax.text(*(0.72*P + [-0.09, 0.0]), '$r$', fontsize=13, path_effects=halo)
ax.text(*at(alpha*0.42, r_b - 0.1), r'arc $r_b\alpha$', color=ORANGE, fontsize=11,
ha='center', va='center', rotation=-np.degrees(alpha*0.42), path_effects=halo)
ax.set_aspect('equal')
ax.set_xlim(-0.35, 1.35)
ax.set_ylim(-0.2, 1.75)
ax.axis('off')
plt.show()The string is tangent to the base circle, so it leaves at right angles to the radius \(OT = r_b\), and its free length \(TP\) equals the arc \(CT\) it has unwound, \(r_b\alpha\). The triangle \(OTP\) therefore has a right angle at \(T\), and Pythagoras gives the radius of \(P\),
\[ r = r_b\sqrt{1+\alpha^2} \]
The angle of the triangle at \(O\) is the pressure angle \(\varphi_r\) of the involute at \(P\). The string \(TP\) is the normal to the flank at \(P\) (The involute of a circle), and the pressure angle is the angle between that normal and the tangent to the circle of radius \(r\) through \(P\). The normal is perpendicular to \(OT\) and the tangent is perpendicular to \(OP\), so the angle between them equals the angle between \(OT\) and \(OP\). Reading the triangle,
\[ \cos\varphi_r = \frac{OT}{OP} = \frac{r_b}{r}, \qquad \tan\varphi_r = \frac{TP}{OT} = \alpha \]
The first gives the pressure angle at any radius, equal to \(\varphi\) at the pitch circle (9.2.5). The second says that the involute reaches the radius \(r\) when the string has unwound through \(\alpha = \tan\varphi_r\).
The angle at which \(P\) lies follows from the angles at \(O\). Measured from the cusp, the tangency point \(T\) has turned through the unwound angle \(\alpha\), and \(P\) trails it by the angle of the triangle. The polar angle of \(P\) is therefore
\[ \boxed{\psi(\alpha) = \alpha - \varphi_r = \alpha - \arctan\alpha} \tag{9.2.8}\]
Inserting \(\alpha = \tan\varphi_r\) writes it in the pressure angle alone, \(\psi = \tan\varphi_r - \varphi_r\): the angle the string has unwound, less the angle of the triangle. This combination turns up so often in gear geometry that it has its own name, the involute functionsv. evolventfunktion, defined for any angle \(x\) as
\[ \operatorname{inv} x = \tan x - x \tag{9.2.9}\]
so that \(\psi = \operatorname{inv}\varphi_r\), the angle from the cusp to the point of the flank where the pressure angle is \(\varphi_r\). At the pitch circle \(\varphi_r\) equals the pressure angle \(\varphi\) of the gear, \(20°\) for a standard gear, and the angle from the cusp to the pitch circle is \(\operatorname{inv}\varphi = \tan 20° - 0.3491 = 0.01490\) rad, or \(0.8540°\). The angle \(x\) that is subtracted must be in radians, since it is compared with a tangent. The value is the same for every gear of that pressure angle, since the tooth count cancels out of \(r_p/r_b = \sec\varphi\).
The same triangle explains the reference line that the CAD model of a gear needs. Slide \(P\) along the involute until it lies on the pitch circle. The ray \(OP\) then runs to the point where the flank crosses the pitch circle, which is the datum line of the SolidWorks sketch. The ray \(OC\) runs to the cusp, and the angle between the two is \(\psi = \operatorname{inv}\varphi\), the \(0.85°\) that the sketch shows between its cusp line and its datum line. The tooth centreline and the mirror plane are measured from \(OP\), not from \(OC\). Figure 9.2.10 draws both in the style of the sketch.
MidAngle from \(OP\), and the half tooth mirrored about it, \(1.571\) mm thick at the pitch circle. Measured from \(OC\) instead (dashed red), the tooth is \(0.975\) mm thick.
Tooth thicknesssv. tandtjocklek is defined at the pitch circle, where standard proportions divide the circular pitch equally between tooth and space, \(t_p = \pi m/2\). Figure 9.2.11 measures every angle from the tooth centreline, at the centre \(O\) far below the tooth.
Code
def thickness_figure():
import matplotlib.patheffects as pe
from matplotlib.patches import Arc
halo = [pe.withStroke(linewidth=3, foreground='white')] # labels stay legible over lines
BLUE, ORANGE, INV, GREEN, STEEL = '#236b8e', '#c07a2c', '#1f4e79', '#2e8b57', '#bcd4e3'
N, m, phi = 40, 1.0, np.radians(20)
inv = lambda a: np.tan(a) - a # @eq-inv
r_p = m*N/2
r_b, r_a, r_f = r_p*np.cos(phi), r_p + m, r_p - 1.25*m
t_p = np.pi*m/2
r = 20.6 # a radius between pitch and tip
phi_r = np.arccos(r_b/r)
half_p = t_p/(2*r_p) # half tooth angle at the pitch circle
beta = half_p + inv(phi) # centreline to cusp
half_r = beta - inv(phi_r) # half tooth angle at r, @eq-thickness
def at(a, rr): # angle a from the tooth centreline (the y-axis), clockwise
return np.array([rr*np.sin(a), rr*np.cos(a)])
def flank(rr): # angle of the right flank at radius rr
return beta - inv(np.arccos(r_b/rr)) if rr > r_b else beta
rs = np.r_[np.linspace(r_f, r_b, 8), np.linspace(r_b, r_a, 120)[1:]]
right = np.array([at(flank(q), q) for q in rs])
left = right*[-1, 1]
tip = np.array([at(a, r_a) for a in np.linspace(flank(r_a), -flank(r_a), 30)])
root = np.array([at(a, r_f) for a in np.linspace(-beta, beta, 30)])
tooth = np.vstack([right, tip, left[::-1], root[::-1]])
fig, ax = plt.subplots(figsize=(6.4, 6.6))
ax.fill(*tooth.T, color=STEEL, ec=BLUE, lw=1.5)
span = np.linspace(-0.11, 0.11, 200)
for rr, name, ls, col in ((r_a, '$r_a$', '-', '0.35'), (r, '$r$', '-', 'k'), (r_p, '$r_p$', '--', 'k'),
(r_b, '$r_b$', '-', ORANGE), (r_f, '$r_f$', '-', '0.35')):
ax.plot(rr*np.sin(span), rr*np.cos(span), ls=ls, color=col, lw=0.9)
dy = -0.2 if rr == r_b else 0.0 # r_b and r_f nearly meet at 40 teeth
ax.text(rr*np.sin(0.11) + 0.08, rr*np.cos(0.11) + dy, name, color=col, va='center',
fontsize=12, path_effects=halo)
# the three rays from the centre O, far below: to the flank on the pitch circle, to the flank
# at r, and to the cusp; and the tooth centreline
r_lo, r_hi = 17.1, r_a + 0.35
ray = lambda a, col, ls, lw=0.9: ax.plot(*np.c_[at(a, r_lo), at(a, r_hi)], color=col, ls=ls, lw=lw)
ray(0, 'k', (0, (8, 2, 1, 2))) # tooth centreline
ray(half_p, 'k', (0, (8, 2, 1, 2))) # to the flank on the pitch circle
ray(half_r, GREEN, (0, (8, 2, 1, 2))) # to the flank at r
ray(beta, '0.45', (0, (8, 2, 1, 2))) # to the cusp
for a, rr in ((half_p, r_p), (half_r, r), (beta, r_b)):
ax.plot(*at(a, rr), 'o', color='k', ms=3.5)
ax.text(*at(beta, r_b) + [0.07, -0.13], '$C$', fontsize=12, path_effects=halo)
# thickness arcs t_p and t_r across the tooth
def thick(rr, half, label, col, dy):
a = np.linspace(-half, half, 60)
q = (rr + dy)*np.c_[np.sin(a), np.cos(a)]
ax.plot(*q.T, color=col, lw=1.4)
for end, sgn in ((q[0], 1), (q[-1], -1)):
ax.annotate('', xy=end, xytext=end + [sgn*0.12, 0], arrowprops=dict(arrowstyle='->', color=col, lw=1.2))
ax.text(0, rr + dy + 0.09, label, color=col, ha='center', va='bottom', fontsize=13, path_effects=halo)
thick(r_p, half_p, '$t_p$', BLUE, 0.0)
thick(r, half_r, '$t_r$', GREEN, 0.0)
# angles at O, drawn as angular dimensions below the tooth, like a sketch
def ang(a0, a1, rr, text, col):
a = np.linspace(a0, a1, 40)
ax.plot(rr*np.sin(a), rr*np.cos(a), color=col, lw=1)
for e, d in ((a0, 1), (a1, -1)):
p = at(e, rr)
ax.annotate('', xy=p, xytext=p + [d*0.08, 0], arrowprops=dict(arrowstyle='->', color=col, lw=1))
mid = at((a0 + a1)/2, rr)
ax.text(mid[0], mid[1] - 0.06, text, color=col, ha='center', va='top', fontsize=12,
path_effects=halo)
ang(0, half_p, 18.45, r'$\dfrac{t_p}{2r_p}$', BLUE)
ang(half_p, beta, 18.45, r'$\mathrm{inv}\,\varphi$', 'k')
ang(half_r, beta, 17.55, r'$\mathrm{inv}\,\varphi_r$', GREEN)
ax.set_aspect('equal')
ax.set_xlim(-1.25, 1.9)
ax.set_ylim(16.95, 21.55)
ax.axis('off')
plt.show()
thickness_figure()The flank crosses the pitch circle at the half tooth angle \(t_p/(2r_p)\). Its cusp lies \(\operatorname{inv}\varphi\) further out, since 9.2.8 gives \(\psi = \operatorname{inv}\varphi\) between the cusp and the pitch circle. At radius \(r\) the same flank lies \(\operatorname{inv}\varphi_r\) back from its cusp, so the half tooth angle there is \(t_p/(2r_p) + \operatorname{inv}\varphi - \operatorname{inv}\varphi_r\). The thickness at radius \(r\) is the arc it spans,
\[ t_r = 2r\left(\frac{t_p}{2 r_p} + \operatorname{inv}\varphi - \operatorname{inv}\varphi_r\right) \tag{9.2.10}\]
as in [3] and [4]. The angle \(\operatorname{inv}\varphi\) is small, but a CAD model that leaves it out makes every tooth of a 20-tooth gear a fifth too thin.
Example 1: the circles of a printed pinion
A pinion of \(N = 20\) teeth, module \(m = 1.5\) mm and pressure angle \(\varphi = 20°\), the gear of Figure 9.2.6, is to be printed with a 0.4 mm nozzle. Find its four radii, the pitch and the base pitch, and check that the tooth is at least two extrusion widths, 0.8 mm, thick at the tip.
The radii follow from Table 9.2.1. The thickness at the tip is 9.2.10 at \(r = r_a\), with the tooth thickness \(\pi m/2\) at the pitch circle and the pressure angle \(\varphi_a = \arccos(r_b/r_a)\) of the involute at the tip:
\[ t_a = 2r_a\left(\frac{\pi}{2N} + \operatorname{inv}\varphi - \operatorname{inv}\varphi_a\right) \tag{9.2.11}\]
Code
N, m = 20, 1.5 # teeth, module [mm]
phi = np.radians(20)
inv = lambda a: np.tan(a) - a # involute function, @eq-inv
r_p = m*N/2 # @eq-sg-module
r_b = r_p*np.cos(phi) # @eq-sg-base
r_a, r_f = r_p + m, r_p - 1.25*m # ISO 53: addendum m, dedendum 1.25 m
p = np.pi*m
p_b = p*np.cos(phi) # base pitch, along the line of action
phi_a = np.arccos(r_b/r_a) # pressure angle of the involute at the tip
t_a = 2*r_a*(np.pi/(2*N) + inv(phi) - inv(phi_a)) # @eq-sg-tipIn millimetres:
\[ \begin{aligned}r_p &=15.0,\quad r_b =14.1,\quad r_a =16.5,\quad r_f =13.13\\p &=4.712,\quad p_b =4.428,\quad t_a =1.042\end{aligned} \]
Figure 9.2.12 draws the top tooth to scale with its circles.
Code
import matplotlib.patheffects as pe
halo = [pe.withStroke(linewidth=3, foreground='white')] # labels stay legible over the teeth
P = gear_profile_points(N, m)[:, :2]
fig, ax = plt.subplots(figsize=(6, 4.2))
ax.fill(P[:, 0], P[:, 1], color='#bcd4e3', ec='#236b8e', lw=1.5)
w = 1.25*np.pi/N # the arcs end just past the middle of the next spaces
a = np.linspace(-w, w, 200)
for r, name, style in ((r_a, '$r_a$', '-'), (r_p, '$r_p$', '--'), (r_b, '$r_b$', '-'),
(r_f, '$r_f$', '-')):
col = '#c07a2c' if name == '$r_b$' else '0.35'
ax.plot(r*np.sin(a), r*np.cos(a), style, color=col, lw=1)
ax.text(r*np.sin(w) + 0.15, r*np.cos(w), name, color=col, va='center', path_effects=halo)
half = t_a/(2*r_a) # half the angle the tip subtends
xs = r_a*np.sin([-half, half])
ax.plot(xs, [r_a + 0.4]*2, color='k', lw=1)
ax.plot([xs, xs], [[r_a + 0.1]*2, [r_a + 0.55]*2], color='k', lw=0.8)
ax.text(0, r_a + 0.65, f'$t_a = {t_a:.2f}$ mm', ha='center', va='bottom')
ax.set_xlim(-r_a*np.sin(1.7*np.pi/N), r_a*np.sin(1.7*np.pi/N))
ax.set_ylim(r_f*np.cos(w) - 0.4, r_a + 1.3)
ax.set_aspect('equal')
ax.axis('off')
plt.show()The tip is 1.04 mm thick, two and a half extrusion widths, so the slicer can lay the tooth out to its tip. The root circle, at 13.13 mm, lies inside the base circle at 14.10 mm, as 9.2.7 predicts for 20 teeth, so the lowest millimetre of the flank is not an involute. That part never touches the mating gear. The base pitch of 4.43 mm is the distance between successive contact points along the line of action, and the contact ratio of the pair is the length of contact measured in it.
Cutting the teeth with a rack
Gears are seldom cut as drawn shapes. They are generated: a tool with straight-sided teeth, the basic rack of ISO 53, is rolled against the blank as if the two were in mesh, the pitch line of the rack rolling on the pitch circle of the gear without sliding, while the tool cuts (Figure 9.2.13). A racksv. kuggstång is a gear of infinite radius. Its base circle is infinite too, so its involutes are straight lines at the pressure angle, and a gear that meshes with a rack has involute flanks. The hob that cuts most gears is this rack wound into a helix, so that turning it advances the teeth without end [1].
Figure 9.2.13 does the same in the computer. The blank turns by \(\theta\) while the rack moves by \(r_p\theta\), and at each of 900 steps the outline of the rack is subtracted from the blank. Nothing in the computation knows about involutes, yet the flanks it leaves lie on the involutes drawn over them at the end. The tip radius \(0.38\,m\) of the rack rounds the root of the gear as it goes (The root fillet radius explains the value).
Generation explains why every gear of one module meshes with every other. Each gear is cut to mesh with the same rack, so an imaginary rack fits between any two of them in mesh, and their flanks touch where each would touch the rack. A pinion of 12 teeth, a wheel of 100 and a rack all run together if they share \(m\) and \(\varphi\). Since the pressure angle is standardised at \(20°\) almost everywhere, the module is the one number that decides whether two gears fit. Stock gears and gear configurators rely on it.
Undercut
The straight flanks of the rack generate involute only from the base circle outwards, since no involute exists inside it. Contact between rack and gear, like contact between two gears, takes place on the line of action, and only up to the point \(T\) where the line touches the base circle. A rack whose straight flanks reach beyond \(T\) cuts into the involute it has just generated, and the gear is undercutsv. underskärning. The straight part of the flank of the ISO 53 rack ends \(m\) above its pitch line. The point \(T\) lies \(r_p\sin\varphi\) from the pitch point along the line of action, which is inclined at \(\varphi\) to the pitch line, so it lies \(r_p\sin^2\varphi\) above the pitch line. The rack stays clear of \(T\) when \(m \le r_p\sin^2\varphi\), which with \(r_p = mN/2\) gives
\[ N \ge \frac{2}{\sin^2\varphi} \tag{9.2.12}\]
This is \(17.1\) at \(20°\), so gears of 18 teeth and more are free of undercut [4]. Figure 9.2.14 generates a pinion of 8 teeth. Below the base circle the tip of the cutter has hollowed out the root of both teeth, and it has narrowed them where the bending stress is largest.
A shaping machine generates teeth the same way, with a cutter that strokes up and down across the face of the blank while cutter and blank turn slowly together, as the rack and the blank of Figure 9.2.13 do (Figure 9.2.15).
Example 2: how few teeth can a pinion have?
The motor pinion is the smallest gear of a printed gearbox, and a small pinion buys a large ratio, 9.2.6. 9.2.12 rules out anything below 18 teeth, but it only says where undercut begins. Find how much the ISO 53 rack removes from pinions of 6 to 20 teeth with the generator of Figure 9.2.13, in two measures: how far above the base circle the involute has been cut away, and how much thinner the narrowest part of the root is than a tooth with radial flanks below the base circle, the tooth of Figure 9.2.12.
We generate each pinion at \(m = 1\), so that lengths come out in modules. For the first measure we walk up a true involute flank from the base circle until it meets the generated outline. For the second we measure the angular half width of a tooth on circles between the root and the base circle, take the smallest, and compare it with the half width \(\pi/(2N) + \operatorname{inv}\varphi\) of a radial flank.
Code
rows = []
for N in (6, 8, 10, 12, 14, 17, 20):
r_p, r_b, r_a, r_f = gear_radii(N, 1.0) # m = 1: lengths in modules
gear = generate(N, 1.0, 1440)[1][-1] # the blank after one revolution
centre = -np.pi/2 + np.pi/N # a tooth beside the first space cut
# one flank: the involute of @eq-involute with its cusp half a tooth plus inv(phi) from the centre
cusp = centre - (np.pi/(2*N) + inv(phi))
al = np.linspace(0, np.sqrt((r_a/r_b)**2 - 1), 400)
g = cusp + al
flank = r_b*np.column_stack([np.cos(g) + al*np.sin(g), np.sin(g) - al*np.cos(g)])
on_outline = [gear.exterior.distance(Point(q)) < 2e-3 for q in flank]
r_u = np.hypot(*flank[np.argmax(on_outline)]) # where the involute begins
hw = min(tooth_halfwidth(gear, r, centre) for r in np.linspace(r_f + 0.4, r_b, 60))
hw_radial = np.pi/(2*N) + inv(phi)
rows.append((N, r_u - r_b, 100*(hw/hw_radial - 1)))
undercut = pd.DataFrame(rows, columns=['$N$', 'involute cut away, $(r_u - r_b)/m$',
'narrowest root vs radial flank, %'])For each tooth count:
| \(N\) | involute cut away, \((r_u - r_b)/m\) | narrowest root vs radial flank, % |
|---|---|---|
| 6 | 0.077 | -15.869 |
| 8 | 0.044 | -7.736 |
| 10 | 0.021 | -3.698 |
| 12 | 0.001 | -1.568 |
| 14 | 0 | -0.488 |
| 17 | 0 | 0 |
| 20 | 0 | 0.036 |
Undercut starts below the \(17.1\) teeth of 9.2.12 but grows slowly. At 17 teeth it is too small to measure, and at 14 the cutter has taken half a percent off the root and left the involute whole. At 10 teeth the root is 3.7 % thinner and the involute begins 0.02 modules above the base circle, and at 8 and 6 teeth the root has lost 8 % and 16 %. The bending stress at the root grows roughly as the inverse square of the tooth thickness there, as Lewis’s equation assumes, so the 3.7 % raise it by about 8 % and the 16 % by about 40 %.
For a printed gearbox, pinions down to about 12 teeth lose little. The sliver of involute that is cut away matters less than the root, since it lies below the point where the tip of a mating gear starts to touch in most pairs. Below 10 teeth the root weakens quickly, and a larger pinion or a profile shift is the remedy.
Profile shift
A profile shiftsv. profilförskjutning withdraws the cutter from the blank by \(xm\). The line of the rack that rolls on the pitch circle is then no longer its datum line, where tooth and space are equally wide, but a line \(xm\) inside it (Figure 9.2.16). The pitch and base circles do not change, so a shifted gear still meshes with every gear of the same module and pressure angle. The tip and root circles move out by \(xm\), and the rack, whose flanks lean at \(\varphi\), leaves the tooth at the pitch circle wider by \(xm\tan\varphi\) on each side:
\[ t_p = m\left(\frac{\pi}{2} + 2x\tan\varphi\right) \tag{9.2.13}\]
[3]. The straight part of the rack then ends \((1-x)m\) above the line that rolls on the pitch circle, and the condition of 9.2.12 becomes \((1 - x)m \le r_p\sin^2\varphi\), or \(x \ge 1 - N\sin^2\varphi/2\): a shift of \(0.30\) frees a pinion of 12 teeth, and \(0.42\) a pinion of 10. Figure 9.2.16 cuts the pinion of 10 teeth with the generator of Figure 9.2.13 at three shifts.
Code
from shapely import affinity
from shapely.geometry import Polygon, box
from gear_geometry import basic_rack
import matplotlib.patheffects as pe
halo = [pe.withStroke(linewidth=3, foreground='white')] # labels stay legible over the teeth
N, m = 10, 1.0
r_p = m*N/2
r_b = r_p*np.cos(phi)
shifts = ((0.5, '#2e7d32'), (0.0, '#236b8e'), (-0.5, '#7b3f8c'))
def radial_tooth(x):
"""The tooth with involute flanks above the base circle and radial flanks below it,
centred on the y-axis: the tooth as it would be without undercut."""
r_a, r_f = r_p + (1 + x)*m, r_p - (1.25 - x)*m
psi_b = m*(np.pi/2 + 2*x*np.tan(phi))/(2*r_p) + inv(phi) # half angle at the base circle, @eq-sg-shift
r = np.linspace(max(r_b, r_f), r_a, 200)
psi = psi_b - inv(np.arccos(r_b/r)) # @eq-thickness
if r_f < r_b:
r, psi = np.r_[r_f, r], np.r_[psi_b, psi]
right = np.column_stack([r*np.sin(psi), r*np.cos(psi)])
return Polygon(np.r_[right, right[::-1]*[-1, 1], [[0, 0]]])
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(6, 8.5), gridspec_kw={'height_ratios': [1, 1.9]})
# the rack above the tooth, teeth down, its datum line x m above the rolling line
L = np.pi*m
for x, col in shifts:
rack = affinity.scale(basic_rack(m, 2), 1, -1, origin=(0, 0))
rack = affinity.translate(rack, np.pi*m/2, x*m).intersection(box(-L, -3, L, 1.6 + x*m))
ax1.plot(*rack.exterior.xy, color=col, lw=1.5)
ax1.plot([-L, L], [x*m]*2, color=col, lw=0.8, ls='-.')
ax1.text(L + 0.15, x*m, f'$x = {x:+.1f}$', color=col, va='center', path_effects=halo)
ax1.plot([-L - 0.6, L], [0, 0], '--', color='0.35', lw=1)
ax1.text(-L - 0.7, 0, 'rolls on the\npitch circle', ha='right', va='center', color='0.35', fontsize=9)
ax1.annotate('', xy=(-L - 0.25, 0.5*m), xytext=(-L - 0.25, 0),
arrowprops=dict(arrowstyle='->', color='0.35'))
ax1.text(-L - 0.35, 0.3*m, '$xm$', ha='right', va='bottom', color='0.35', path_effects=halo)
ax1.set_xlim(-L - 2.6, L + 1.6)
ax1.set_aspect('equal')
ax1.axis('off')
# the three teeth it generates, one over the other; hatched: what the rack took from the radial tooth
for x, col in shifts:
gear = generate(N, m, 1440, phi, x)[1][-1]
gear = affinity.rotate(gear, np.pi - np.pi/N, origin=(0, 0), use_radians=True) # a tooth on the y-axis
ax2.fill(*gear.exterior.xy, color='#bcd4e3', alpha=0.45, ec=col, lw=1.5)
r_f = r_p - (1.25 - x)*m
band = Point(0, 0).buffer(r_b, 256).difference(Point(0, 0).buffer(r_f + 0.3*m, 256)) # above the root fillet
cut = radial_tooth(x).intersection(band).difference(gear)
for g in getattr(cut, 'geoms', [cut]):
if g.area > 1e-3:
ax2.fill(*g.exterior.xy, fill=False, hatch='////', ec=col, lw=0)
ax2.text(0.95, r_p + (1 + x)*m, f'$x = {x:+.1f}$', color=col, va='center', path_effects=halo)
w = 1.3*np.pi/N
a = np.linspace(-w, w, 200)
for r, col, ls in ((r_p, '0.35', '--'), (r_b, '#c07a2c', '-')):
ax2.plot(r*np.sin(a), r*np.cos(a), ls, color=col, lw=1)
ax2.text(-1.95, r_p*np.cos(0.4) + 0.08, 'pitch circle', color='0.35', va='bottom', fontsize=9, path_effects=halo)
ax2.text(-1.95, r_b*np.cos(0.4) - 0.1, 'base circle', color='#c07a2c', va='top', fontsize=9, path_effects=halo)
ax2.annotate('undercut', xy=(0.68, 4.05), xytext=(1.2, 3.0), color='0.2', fontsize=9,
arrowprops=dict(arrowstyle='->', color='0.35', lw=0.8), path_effects=halo)
ax2.set_xlim(-2.1, 2.1)
ax2.set_ylim(2.6, 7)
ax2.set_aspect('equal')
ax2.axis('off')
plt.show()All three teeth are cut from the same involute, and the shift chooses which stretch of it the tooth uses. A negative shift moves the tooth inwards, towards the base circle, and the undercut deepens. A positive shift moves it outwards: the root thickens, the tip thins, and at \(x = 0.70\) the two flanks of the 10-tooth pinion meet in a point. The shift is bounded from both sides, by undercut below and by the thickness the tip needs above.
A shifted pinion meshes with an unshifted wheel only at a larger centre distance, since its thicker teeth push the wheel away. Giving the wheel the opposite shift, \(x_2 = -x_1\), keeps the standard centre distance: the wheel gives up the tooth thickness the pinion gains, and a wheel of many teeth can spare it, since it is far from undercut [3].
Both routes cost the pair some of its overlap. With \(x_2 = -x_1\) the pinion’s tip circle moves out by \(x_1m\) and the wheel’s moves in by as much, but a millimetre of radius at the pinion’s tip adds less to the path of contact than a millimetre at the wheel’s tip removes, since the pinion’s involute is steeper there. The contact ratio of the pinion of 10 teeth at \(x_1 = 0.42\) against a wheel of 40 teeth falls to 1.43, from the 1.54 an unshifted pair would have if undercut did not cut its involute away. Leaving the wheel unshifted costs more. The centre distance grows, the gears roll on circles slightly larger than their pitch circles, and the pressure angle at which they meet rises to \(\varphi_w\) given by
\[ \operatorname{inv}\varphi_w = \operatorname{inv}\varphi + \frac{2(x_1 + x_2)\tan\varphi}{N_1 + N_2} \tag{9.2.14}\]
[3]. For the same pair \(\varphi_w = 22.3°\) and the centre distance grows by \(0.40m\). The flank force leans further from the tangent, so for the same torque the radial force on the bearings grows by \(\tan\varphi_w/\tan\varphi - 1 = 13\,\%\), and the contact ratio falls to 1.39. On the tooth itself the cost is the thinner tip, which Example 3 bounds for a printer.
Example 3: the smallest printed pinion
The pinion of Example 1 has 20 teeth at \(m = 1.5\) mm. A smaller pinion of the same module gives a larger ratio. Find, for 10 to 17 teeth, the shifts that leave the pinion free of undercut and its tip at least two extrusion widths, 0.8 mm, thick, and check the tip of the generated teeth.
The smallest shift is \(x_\min = 1 - N\sin^2\varphi/2\) from the undercut condition above. The largest follows from the tip thickness of 9.2.11 with the shifted tooth: the tip radius becomes \(r_a = r_p + (1+x)m\), and the half angle \(\pi/(2N)\) of the tooth at the pitch circle becomes \(t_p/(2r_p)\) with \(t_p\) from 9.2.13:
\[ t_a = 2r_a\left(\frac{t_p}{2r_p} + \operatorname{inv}\varphi - \operatorname{inv}\varphi_a\right), \qquad \varphi_a = \arccos\frac{r_b}{r_a} \tag{9.2.15}\]
The tip thins as \(x\) grows, so \(x_\max\) is the root of \(t_a(x) = 0.8\) mm, which we find numerically. To check it we cut each pinion at \(x_\max\) with the generator and measure its tooth on a circle \(2\,\mu\)m inside the tip.
Code
from scipy.optimize import brentq
m, t_min = 1.5, 0.8 # module, two extrusion widths [mm]
def tip_thickness(N, x):
r_p = m*N/2
r_b, r_a = r_p*np.cos(phi), r_p + (1 + x)*m
t_p = m*(np.pi/2 + 2*x*np.tan(phi)) # @eq-sg-shift
return 2*r_a*(t_p/(2*r_p) + inv(phi) - inv(np.arccos(r_b/r_a))) # @eq-sg-tip-shift
rows = []
for N in range(10, 18):
x_min = 1 - N*np.sin(phi)**2/2 # the rack clears T
x_max = brentq(lambda x: tip_thickness(N, x) - t_min, -1, 1.5)
gear = generate(N, m, 1440, phi, x_max)[1][-1]
r = m*N/2 + (1 + x_max)*m - 0.002 # just inside the tip circle
t_gen = 2*r*tooth_halfwidth(gear, r, -np.pi/2 + np.pi/N)
rows.append((N, x_min, x_max, t_gen))
shift = pd.DataFrame(rows, columns=['$N$', r'$x_\min$', r'$x_\max$',
r'generated $t_a$ at $x_\max$, mm'])For each tooth count, with \(m = 1.5\) mm:
| \(N\) | \(x_\min\) | \(x_\max\) | generated \(t_a\) at \(x_\max\), mm |
|---|---|---|---|
| 10 | 0.415 | 0.084 | 0.803 |
| 11 | 0.357 | 0.119 | 0.803 |
| 12 | 0.298 | 0.152 | 0.803 |
| 13 | 0.24 | 0.184 | 0.803 |
| 14 | 0.181 | 0.215 | 0.803 |
| 15 | 0.123 | 0.245 | 0.803 |
| 16 | 0.064 | 0.274 | 0.803 |
| 17 | 0.006 | 0.302 | 0.803 |
The generated tips are 0.803 mm thick, the 0.8 mm of 9.2.15 plus the 3 µm the flank widens over the 2 µm between the measuring circle and the tip. Up to 13 teeth \(x_\max\) lies below \(x_\min\), and no shift gives both a full involute and a printable tip. The two bounds cross between 13 and 14 teeth, and a pinion of 14 teeth with \(x = 0.2\) is the smallest that meets both. The upper bound depends on the module, since the 0.8 mm is fixed by the nozzle, while the lower bound does not.
At \(m = 1.5\) mm the smallest printed pinion free of undercut has 14 teeth and a shift of \(0.2\). Where a little undercut is acceptable, as Example 2 suggests, a pinion of 12 teeth at \(x = 0.15\) keeps its 0.8 mm tip and is less undercut than an unshifted pinion of 14 teeth: the rack reaches \(0.15m\) past \(T\), against \(0.18m\).
The tooth profile in CAD
The SolidWorks tutorial builds a gear from \(m\), \(N\) and \(\varphi\) alone. Its expressions rest on three results of this chapter: one curve can carry both the involute and the radial line below it, the tooth is located from the pitch circle, and the root fillet is sized to fit the tooth space.
One curve for the whole flank
With \(v = t\,t_\text{tip}\), where \(t_\text{tip} = \sqrt{(r_a/r_b)^2 - 1}\) is the value of \(\alpha\) at which 9.2.1 reaches the tip circle, and with \(v^{+} = \max(v,0)\) and \(v^{-} = \min(v,0)\), the flank of the tutorial is
\[ x(t) = r_b\left[\sin v^{+} - v^{+}\cos v^{+}\right], \qquad y(t) = r_b\left[\cos v^{+} + v^{+}\sin v^{+}\right] + r_b\,v^{-} \tag{9.2.16}\]
For \(t > 0\) we have \(v^{-} = 0\), and this is the involute of 9.2.1. For \(t < 0\) we have \(v^{+} = 0\): the involute terms freeze at the cusp \((0, r_b)\) and the last term slides the point straight down the \(y\) axis. One entity therefore carries the involute above the base circle and the radial line below it, and the sketch keeps the same entities as \(N\) crosses the \(41.5\) teeth of 9.2.7 (Figure 9.2.17).
A CAD equation accepts no conditional, so the maximum and the minimum are written with \(\sqrt{v^2} = |v|\),
\[ \max(v, 0) = \frac{v + \sqrt{v^2}}{2}, \qquad \min(v, 0) = \frac{v - \sqrt{v^2}}{2} \tag{9.2.17}\]
in SolidWorks syntax (v + sqr(v ^ 2)) / 2 and (v - sqr(v ^ 2)) / 2.
Code
import matplotlib.patheffects as pe
from gear_geometry import unified_curve
halo = [pe.withStroke(linewidth=3, foreground='white')] # labels stay legible over the arcs
fig, axes = plt.subplots(2, 1, figsize=(6, 8))
for ax, N_val in zip(axes, (20, 50)):
r_p, r_b, r_a, r_f = gear_radii(N_val, 1)
t_tip = np.sqrt((r_a/r_b)**2 - 1)
t = np.linspace(-1, 1, 2001)
x, y = unified_curve(t, r_b, t_tip)
ax.plot(x[t >= 0], y[t >= 0], color='#236b8e', lw=2) # v > 0: the involute
ax.plot(x[t <= 0], y[t <= 0], color='0.55', lw=2) # v < 0: the radial line
ax.plot(0, r_b, 'o', color='#236b8e', ms=4) # the cusp joins the two
for r, name in ((r_a, '$r_a$'), (r_p, '$r_p$'), (r_b, '$r_b$'), (r_f, '$r_f$')):
col = '#c07a2c' if name == '$r_b$' else '0.35'
a = np.linspace(-1.4/r, 1.4/r, 100) # arcs 2.8 mm long
ax.plot(r*np.sin(a), r*np.cos(a), '--' if name == '$r_p$' else '-', color=col, lw=1)
side = -1 if name == '$r_b$' else 1 # base label left, the rest right
ax.text(side*1.5, r*np.cos(a[-1]), name, color=col, va='center',
ha='right' if side < 0 else 'left', path_effects=halo)
where = 'inside' if r_f < r_b else 'outside'
ax.set_title(f'$N = {N_val}$: root circle {where} the base circle', fontsize=11)
ax.set_xlim(-2.2, 2.2)
ax.set_ylim(r_f - 1.0, r_a + 0.3)
ax.set_aspect('equal')
ax.set_xlabel('$x$ (mm)')
ax.set_ylabel('$y$ (mm)')
plt.tight_layout()
plt.show()The datum on the pitch circle
Setting \(r = r_b\) in 9.2.10 makes \(\operatorname{inv}\varphi_b = 0\), and the bracket loses the term that distinguishes one radius from another: the base circle carries no information about the tooth thickness. The datum line of the CAD sketch therefore runs to the point where the flank crosses the pitch circle, \(OP\) in Figure 9.2.9, and the tooth centreline lies a quarter of the angular pitch, \(\pi/(2N)\), beyond it, since tooth and space are equally wide there. Seen from the cusp line \(OC\), the centreline lies at \(\pi/(2N) + \operatorname{inv}\varphi\).
Taking the datum to the cusp, where the curve visibly starts and the point is easy to snap to, drops \(\operatorname{inv}\varphi\) from the half tooth angle, and every tooth comes out \(2\operatorname{inv}\varphi = 1.71°\) too thin. At \(N = 20\) and \(m = 1\) the pitch circle thickness is \(1.273\) mm against \(1.571\) mm, 19 % short: invisible by eye, and in mesh \(0.3\) mm of play at every tooth. Mick of Kusel Design, Australia, found this error in an earlier version of the tutorial.
The root fillet radius
A hobbed gear gets its root fillet from the tip radius of the generating rack, which ISO 53 sets at \(\rho_{fP} = 0.38\,m\), with \(0.30\,m\), \(0.25\,m\) and \(0.39\,m\) as alternatives [2]. According to ISO 53 [2], the largest fillet that the bottom clearance \(c_P\) of a \(20°\) rack admits, for \(c_P < 0.295\,m\), is
\[ \rho_{fP\max} = \frac{c_P}{1 - \sin\alpha_P} \]
and the standard clearance \(c_P = 0.25\,m\) returns \(0.38\,m\): the tabulated value is the largest fillet that fits. The CAD model applies the same rule to the gear without simulating the cutter. Subtracting the tooth thickness of 9.2.10 from the circular pitch at the base circle leaves a space whose half width is
\[ \frac{e_b}{2} = r_b\left(\frac{\pi}{2N} - \operatorname{inv}\varphi\right) \tag{9.2.18}\]
and the model takes \(R_f = 0.9\,e_b/2\). The fillet sits at the root, where the space is narrower than at the base circle when \(r_f < r_b\). The two fillets of one space clear each other when
\[ \arcsin\frac{R_f}{r_f + R_f} < \beta, \qquad \beta = \frac{\pi}{2N} - \operatorname{inv}\varphi \tag{9.2.19}\]
or \(R_f < r_f \sin\beta / (1 - \sin\beta)\). A fillet tangent to a radial flank touches it at the radius \(\sqrt{r_f^2 + 2 r_f R_f}\); where that exceeds \(r_b\) there is no radial flank to touch, and the fillet meets the involute. Table 9.2.2 evaluates both at \(m = 1\) mm.
Code
phi = np.radians(20)
inv_phi = np.tan(phi) - phi # @eq-inv at the pitch circle
rows = []
for N in (8, 10, 20, 25, 26, 42, 60, 80, 100):
r_p, r_b, r_a, r_f = gear_radii(N, 1.0) # m = 1: lengths in mm
beta = np.pi/(2*N) - inv_phi # half the tooth space, as an angle
R_f = 0.9*r_b*beta # R_fillet, 0.9 of @eq-space
R_max = r_f*np.sin(beta)/(1 - np.sin(beta)) # largest fillet, @eq-clearance
r_t = np.sqrt(r_f**2 + 2*r_f*R_f) # tangency on a radial flank
rows.append((N, R_f, R_max, R_f/R_max, 'involute' if r_t > r_b else 'radial line'))
fillets = pd.DataFrame(rows, columns=['$N$', '$R_f$ (mm)', 'largest $R_f$ (mm)',
'ratio', 'fillet meets'])| \(N\) | \(R_f\) (mm) | largest \(R_f\) (mm) | ratio | fillet meets |
|---|---|---|---|---|
| 8 | 0.614 | 0.606 | 1.014 | radial line |
| 10 | 0.601 | 0.619 | 0.971 | radial line |
| 20 | 0.538 | 0.594 | 0.906 | radial line |
| 25 | 0.507 | 0.566 | 0.895 | radial line |
| 26 | 0.5 | 0.56 | 0.893 | involute |
| 42 | 0.4 | 0.454 | 0.879 | involute |
| 60 | 0.286 | 0.328 | 0.873 | involute |
| 80 | 0.16 | 0.184 | 0.869 | involute |
| 100 | 0.034 | 0.039 | 0.867 | involute |
The factor \(0.9\) leaves a margin of 9 % at \(N = 20\) and 3 % at \(N = 10\); at \(N = 8\) the two fillets of a space overlap. As a feature on the solid this is harmless, since the two blends merge into one rounded root, what ISO 53 calls a full root radius. Above about \(N = 25\) the fillet meets the involute, and a fillet feature on the root edge finds that point itself, which is why the tutorial fillets the solid and not the sketch. At the other end 9.2.18 shrinks with \(N\) and vanishes at \(\pi/(2N) = \operatorname{inv}\varphi\), \(N = 105\); from about \(N = 42\) the fillet is thinner than a \(0.4\) mm extrusion width, so the printed part has none.
Limits of the CAD model
The model has no cutter and draws the full involute, so below 18 teeth it is a valid involute gear but not the undercut gear a hob would cut; Example 2 shows that the two differ little down to about 12 teeth. It assumes no profile shift. A shift \(x\) adds \(2x\tan\varphi/N\) (in radians) to the quarter pitch from the datum to the centreline, subtracts it inside the bracket of 9.2.18, and moves the tip and root circles out by \(xm\). It has no backlash and no tip relief, so a real pair needs the teeth thinned slightly.
Toolbox gears
SolidWorks ships spur, helical and bevel gears in its Toolbox library, sized from the module and the tooth count. Its help pages warn against using them for anything but a picture.
“Toolbox gears are representations for machine design purposes. They are not true involute gears that you can use for manufacturing.” [5]
The help does not say what the teeth are instead. Opening the Toolbox spur gear (spur gear_iso.sldprt) through the SolidWorks API shows it. The sketch that cuts the tooth space holds six arcs and four lines, with no spline and no equation driven curve, and the finished solid has planar and cylindrical faces only, where an extruded involute would leave B-spline faces. The Toolbox equation "Flank_rad@TooCutSke" = "Num_teeth@HoldingSke" / "Pitch@HoldingSke" / 5, in which Pitch is the diametral pitch \(1/m\), makes every flank a circular arc of radius
\[ R = \frac{d}{5} = \frac{Nm}{5} \tag{9.2.20}\]
through the point \(P\) where the flank crosses the pitch circle. Its centre \(M\) lies on the base circle, so the triangle \(OPM\) has the sides \(r_p\), \(r_b\) and \(R\), and the cosine rule places \(M\) at the angle
\[ \gamma = \arccos\frac{r_p^2 + r_b^2 - R^2}{2 r_p r_b} \tag{9.2.21}\]
from \(OP\), past the tooth centreline. For the seed part, \(N = 12\) and \(m = 6.35\) mm, this is \(\gamma = 23.54°\), and it reproduces the arc centre of the sketch to \(0.001\) mm.
The involute through \(P\) has its centre of curvature at the point \(T\) of Figure 9.2.9, and its radius of curvature there is the free string, \(TP = r_b\tan\varphi = r_p\sin\varphi = 0.171\,d\). The arc radius is \(0.2/0.171 = 1.17\) times as large, so the arc is 17 % flatter at the pitch circle for every tooth count. Away from \(P\) the two curves separate: the radius of curvature of the involute grows from zero at the base circle, and the arc keeps \(R\). Writing \(\bm{X}(r)\) for the point at radius \(r\) of the involute of 9.2.1 through the same pitch point, the deviation of the arc along its normal is
\[ \delta(r) = |\bm{X}(r) - \bm{M}| - R \tag{9.2.22}\]
positive where the arc lies inside the involute and the Toolbox tooth is too thin, negative where it is too thick (Figure 9.2.18).
Code
from shapely.geometry import Polygon
from gear_geometry import toolbox_arc, toolbox_gear, follower_lag
inv = lambda a: np.tan(a) - a # @eq-inv
orange, blue = '#c07a2c', '#236b8e'
def flank(N, m, n=2000):
"""Radii from base to tip, the involute through the pitch point, and the deviation of
the Toolbox arc from it (@eq-sg-toolbox-dev). Tooth centred on the y axis."""
r_p, r_b, r_a, r_f = gear_radii(N, m)
r = np.linspace(r_b, r_a, n)
psi = np.pi/(2*N) + inv(phi) - inv(np.arccos(r_b/r)) # polar angle from the centreline
X = np.column_stack([r*np.sin(psi), r*np.cos(psi)])
M, R = toolbox_arc(N, m)
return r, X, np.linalg.norm(X - M, axis=1) - R
def r_sap(N, m):
"""Start of the active profile against an equal gear, where the tip circle of the
mate crosses the line of action."""
r_p, r_b, r_a, r_f = gear_radii(N, m)
s = 2*r_p*np.sin(phi) - np.sqrt(r_a**2 - r_b**2) # distance from T along the line
return np.sqrt(r_b**2 + max(s, 0)**2)
fig, axes = plt.subplots(2, 1, figsize=(6, 9), gridspec_kw={'height_ratios': [1.1, 1]})
ax = axes[0]
N, m = 12, 1
r_p, r_b, r_a, r_f = gear_radii(N, m)
r, X, d = flank(N, m)
M, R = toolbox_arc(N, m)
psi_P = np.pi/(2*N)
P = r_p*np.array([np.sin(psi_P), np.cos(psi_P)])
T = r_b*np.array([np.sin(psi_P - phi), np.cos(psi_P - phi)])
a = np.arctan2(*(P - M)[::-1]) + np.linspace(-0.8, 0.8, 2000)
arc = M + R*np.column_stack([np.cos(a), np.sin(a)])
arc = arc[(np.hypot(*arc.T) >= r_f) & (np.hypot(*arc.T) <= r_a)] # root to tip, as Toolbox
for s in (1, -1): # right flank, and its mirror image
ax.plot(s*arc[:, 0], arc[:, 1], color=orange, lw=2.5 if s > 0 else 1, alpha=1 if s > 0 else 0.5)
ax.plot(s*X[:, 0], X[:, 1], color=blue, lw=1.2 if s > 0 else 0.6, alpha=1 if s > 0 else 0.5)
for rr, ls in ((r_a, '-'), (r_p, '--'), (r_b, '-'), (r_f, '-')):
c = np.linspace(-0.45, 0.45, 200)
ax.plot(rr*np.sin(c), rr*np.cos(c), ls, color='0.6', lw=0.8)
ax.plot(*np.array([M, P]).T, '--', color=orange, lw=0.8)
ax.plot(*np.array([T, P]).T, color=blue, lw=0.8)
for pt, name, off in ((P, '$P$', (0.08, 0.05)), (M, '$M$', (-0.25, -0.05)), (T, '$T$', (0.05, -0.25))):
ax.plot(*pt, 'o', color='k', ms=3.5)
ax.text(pt[0] + off[0], pt[1] + off[1], name, path_effects=halo)
ax.text(*(M + P)/2 + np.array([-0.15, 0.2]), '$R = d/5$', color=orange, ha='right', path_effects=halo)
ax.text(*(T + P)/2 + np.array([0.12, -0.12]), r'$r_p\sin\varphi$', color=blue, path_effects=halo)
for rr, name in ((r_a, '$r_a$'), (r_p, '$r_p$'), (r_b, '$r_b$'), (r_f, '$r_f$')):
ax.text(0.45*rr + 0.1, rr*np.cos(0.45), name, color='0.35', va='center', path_effects=halo)
ax.set_xlim(-2.2, 3.7)
ax.set_ylim(r_f - 0.3, r_a + 0.3)
ax.set_aspect('equal')
ax.set_xlabel('$x$ (mm)')
ax.set_ylabel('$y$ (mm)')
ax.set_title('$N = 12$, $m = 1$ mm: involute (blue) and Toolbox arc (orange)', fontsize=11)
ax = axes[1]
for N, col in ((12, blue), (20, 'k'), (50, '0.6')):
r_p, r_b, r_a, r_f = gear_radii(N, 1)
r, X, d = flank(N, 1)
on = r >= r_sap(N, 1) # the part of the flank that meshes
ax.plot(r[on] - r_p, d[on], color=col, lw=2, label=f'$N = {N}$')
ax.plot(r[~on] - r_p, d[~on], '--', lw=1, color=col)
ax.axhline(0, color='0.6', lw=0.8)
ax.axvline(0, color='0.6', lw=0.8, ls='--')
ax.text(0.03, ax.get_ylim()[0]*0.9, 'pitch circle', color='0.35', rotation=90, va='bottom')
ax.set_xlabel(r'$(r - r_p)/m$')
ax.set_ylabel(r'$\delta/m$')
ax.legend(frameon=False, loc='upper left')
plt.tight_layout()
plt.show()
rows = []
for N in (10, 12, 15, 20, 30, 50, 100):
r, X, d = flank(N, 1)
d = d[r >= r_sap(N, 1)] # active profile only
worst = np.abs(d).max()
rows.append((N, round(d.min(), 3), round(d.max(), 3), round(0.1/worst, 1), round(0.05/worst, 1)))
toolbox = pd.DataFrame(rows, columns=['$N$', r'thickest, $\min\delta/m$', r'thinnest, $\max\delta/m$',
r'$m$ (mm) at $\lvert\delta\rvert = 0.1$ mm', r'$m$ (mm) at $\lvert\delta\rvert = 0.05$ mm'])Low on the flank the arc lies outside the involute and the Toolbox tooth is too thick; at the tip of a small pinion it lies inside, and the tooth is thinned as if relieved. Only the active profile counts, from the point where the tip of the mate first touches to the tip. Table 9.2.3 gives the extremes there.
| \(N\) | thickest, \(\min\delta/m\) | thinnest, \(\max\delta/m\) | \(m\) (mm) at \(\lvert\delta\rvert = 0.1\) mm | \(m\) (mm) at \(\lvert\delta\rvert = 0.05\) mm |
|---|---|---|---|---|
| 10 | -0.015 | 0.063 | 1.6 | 0.8 |
| 12 | -0.018 | 0.043 | 2.3 | 1.2 |
| 15 | -0.021 | 0.026 | 3.8 | 1.9 |
| 20 | -0.02 | 0.013 | 4.9 | 2.5 |
| 30 | -0.017 | 0.004 | 5.8 | 2.9 |
| 50 | -0.013 | 0.001 | 7.7 | 3.8 |
| 100 | -0.009 | 0.002 | 11.1 | 5.6 |
From 20 teeth up the error stays within \(0.02\,m\), the same on every tooth. In the test of Figure 9.2.1, with the play of the hook, a 20-tooth Toolbox pair turns at a ratio that swings between 0.94 and 1.06 within each tooth pitch, and a 12-tooth pair between 0.985 and 1.016 (Figure 9.2.19). The follower falls behind and catches up by \(0.07°\) per tooth, \(0.012\,m\) along the pitch circle: a small copy of the jerk of the straight teeth. Like the error itself, it does not depend on the module.
Code
N, m = 20, 1
r_p, p = m*N/2, 2*np.pi/N
th = np.linspace(0, p, 121) # one angular pitch of the driver
shapes = {'involute': Polygon(gear_profile_points(N, m)[:, :2]).buffer(-0.05*m, 64),
'Toolbox arc': toolbox_gear(N, m).buffer(-0.05*m, 64)} # the play of @fig-sg-hook
fig, ax = plt.subplots(figsize=(6, 3.4))
for (k, g), col in zip(shapes.items(), (blue, orange)):
lag = follower_lag(g, N, r_p, th)
ax.plot(np.degrees(th), 1 + np.gradient(lag, th), color=col, lw=2, label=k)
ax.set_xlim(0, np.degrees(p))
ax.set_xlabel(r'$\theta_1$ (°)')
ax.set_ylabel(r'$\omega_2/\omega_1$')
ax.legend(frameon=False, loc='lower left')
plt.tight_layout()
plt.show()Whether this matters for a printed or cut gear depends on what the process adds of its own. The error grows with the module and the tolerance of a process does not, and the last two columns of Table 9.2.3 give the module at which they meet.
The reference FFF profile of this book, a 0.4 mm nozzle at 0.2 mm layers, holds each surface to about ±0.1 mm [6]. A 20-tooth Toolbox gear reaches that at \(m = 4.9\) mm, a pitch diameter of 98 mm, larger than most printed drives. For the pinion of Example 1, at \(m = 1.5\) mm, the error is \(0.03\) mm, under a third of the tolerance, and the slicer lays its roads along the arc as it would along the involute. Pinions of fewer teeth at the same module are the exception. The tip of a Toolbox tooth is \(2\delta\) thinner than the involute tooth, by \(0.13\) mm for 12 teeth and \(0.19\) mm for 10, enough to take a tip sized to exactly the two extrusion widths of Example 3 below them.
A 0.2 mm nozzle lays roads half as wide, usually at 0.1 mm layers. If the deviation per surface shrinks with the road to about ±0.05 mm, the Toolbox error reaches it at \(m \approx 2.5\) mm. A fine nozzle is chosen for fine teeth, though: two of its extrusion widths make a tip half as thick, so it allows about half the module. At that module, \(m = 0.75\) mm, the error is \(0.016\) mm. For the modules each nozzle is used for, the print hides the arc.
A resin printer cures each layer through a screen of pixels, 19 to 50 µm on current desktop machines, or traces it with a laser spot of about 85 µm. A smaller pixel does not by itself give a more accurate part [7]. Resin suits small gears, and at \(m = 0.5\) mm the Toolbox error is 10 µm, below one pixel. From \(m = 2\) mm it exceeds 40 µm, two pixels of a fine screen, and the printer reproduces the arc instead of hiding it.
A laser cutter follows the outline as drawn, arcs as arcs, so nothing smooths the error away. Its own deviations come from the kerf, about 0.2 mm in 3 mm acrylic and wider on curves, which the outline is offset by half to compensate, and from a beam that tapers through the sheet. Together a CO2 machine holds dimensions to about ±0.13 mm [8]. Gears cut from 3 to 6 mm acrylic or plywood are coarse, and at \(m = 3\) mm the Toolbox error of \(0.06\) mm is half that accuracy. A gear cut from steel plate to carry load is a manufactured gear in the sense of the warning above, and its outline has to be the involute.
None of these processes lets the arc decide the quality of a small gear, but the jumps of Figure 9.2.19 are the same fraction at every size. The SolidWorks tutorial of the next chapter draws the flank from 9.2.16, so the part carries no profile error of its own, and the only error left is what the printer or the cutter adds.
Problems
Problem 1: A 15-tooth pinion. A pinion like the one of Figure 9.2.6 has \(N = 15\) teeth and module \(m = 1\) mm at \(\varphi = 20°\). Find its pitch, base, tip and root radii, and decide whether its involute reaches the root circle.
Answer: \(r_p = 7.5\) mm, \(r_b = 7.5\cos 20° = 7.048\) mm, \(r_a = 8.5\) mm, \(r_f = 6.25\) mm. The root circle lies inside the base circle, since \(15 < 41.5\) by 9.2.7, so the involute stops 0.8 mm above the root and the flank below it is radial.
Problem 2: The string at its limit. The circles of Figure 9.2.5 have \(r_{b1} = 1.2\) and \(r_{b2} = 2.0\). At what centre distance does the string make the standard angle \(\varphi' = 20°\)? What happens to the string as the circles are brought closer still?
Answer: 9.2.3 gives \(a = (r_{b1} + r_{b2})/\cos 20° = 3.2/0.9397 = 3.405\). As \(a\) falls towards \(r_{b1} + r_{b2} = 3.2\) the angle falls to zero and the free string to nothing; closer than that the circles overlap and no crossed string exists. A gear pair binds long before, when the teeth run out of play.
Problem 3: The old pressure angle. Gears were long made with a pressure angle of \(14.5°\). How many teeth does a pinion cut by a rack need at that angle to be free of undercut, in the sense of 9.2.12 and Figure 9.2.14?
Answer: \(N \ge 2/\sin^2 14.5° = 2/0.0627 = 31.9\), so 32 teeth, against 18 at \(20°\). The larger pressure angle moves the point \(T\) out along the line of action and lets far smaller pinions be cut without undercut.
Problem 4: The fillet of a 30-tooth gear. For a gear of \(N = 30\) teeth at \(m = 1\) mm and \(\varphi = 20°\), evaluate the fillet radius \(R_f = 0.9\,e_b/2\) of 9.2.18, and decide whether the fillet meets the radial line or the involute (Table 9.2.2).
Answer: With \(r_b = 14.095\) mm and \(\pi/60 - \operatorname{inv}20° = 0.03746\), \(R_f = 0.9 \cdot 14.095 \cdot 0.03746 = 0.475\) mm. The fillet would touch a radial flank at \(\sqrt{13.75^2 + 2 \cdot 13.75 \cdot 0.475} = 14.22\) mm, above \(r_b\), so it meets the involute.
Problem 5: The wrong datum on a small pinion. A student draws the datum line of a pinion with \(N = 12\) and \(m = 2\) mm to the cusp \(C\) of Figure 9.2.9 instead of the pitch point \(P\). How thick is the tooth at the pitch circle, and by what fraction is it too thin?
Answer: The half tooth angle at the pitch circle becomes \(\pi/24 - \operatorname{inv}20° = 0.1309 - 0.0149 = 0.1160\) rad, so \(t_p = 2 \cdot 12 \cdot 0.1160 = 2.784\) mm against \(\pi m/2 = 3.142\) mm, 11 % thin. The relative error \(2N\operatorname{inv}\varphi/\pi\) grows with \(N\), so a large gear suffers more.
Further reading
Litvin and Fuentes [1] develop the theory of gearing from the law of gearing to tooth surfaces generated as envelopes of a cutter, the method behind Figure 9.2.13, and Buckingham [3] remains the classical reference for the geometry of spur gears, including undercut and profile shift. The animated introduction by Gergely Bencsik1 shows the crossed string and the rack in six minutes.
References
G. Bencsik, Involute Gears Explained, YouTube, https://www.youtube.com/watch?v=nrsCoQN6V4M.↩︎