Belt Design example

In this example we want to choose which Synchronous belt will fit a given pulley arrangement.

# BasicBeltDesign.jl
# This example of BeltTransmission.jl solves a system of 5 pulleys, finding the belt length, idler position, angle of wrap on each pulley, and the transmission ratio matrix.

using Plots #everything ends up as a plot

#dependencies for function arguments
using Unitful, Unitful.DefaultSymbols
import Geometry2D

#bring this module in
using BeltTransmission

#describe the pulleys
uk = Geometry2D.UnitVector(0,0,1)
#a square of pulleys, arranged ccw from quadrant1
pA = SynchronousPulley( center=Geometry2D.Point( 100mm, 100mm), axis=uk, nGrooves=62, beltPitch=2mm, name="Drive" )
pB = SynchronousPulley( center=Geometry2D.Point(-100mm, 100mm), axis=uk, nGrooves=30, beltPitch=2mm, name="B" )
pC = SynchronousPulley( center=Geometry2D.Point(-100mm,-100mm), axis=uk, nGrooves=80, beltPitch=2mm, name="C" )
pD = SynchronousPulley( center=Geometry2D.Point( 100mm,-100mm), axis=uk, nGrooves=30, beltPitch=2mm, name="D" )
pE = PlainPulley( pitch=Geometry2D.Circle(   0u"mm",   0u"mm", 14u"mm"), axis=-uk, name="Idler") # -uk axis engages the backside of the belt
route = [pA, pB, pC, pD, pE]

# solve the system
solved = calculateRouteAngles(route)
println("Initial belt length is $(calculateBeltLength(solved))")

# plot the system
p = plot(solved, reuse=false)#, legend_background_color=:transparent, legend_position=:outerright)
display(p)

# generate a catalog of GT2 belts
belts = SynchronousBeltTable.generateBeltDataFrame(pitch=2u"mm", width=6u"mm", toothRange=500:10:700)

# filter by length
belt = SynchronousBeltTable.lookupLength( belts, calculateBeltLength(solved), pitch=2mm, width=6mm, n=1 )
belt = SynchronousBeltTable.dfRow2SyncBelt(belt) #convert out of DataFrame

# the chosen belt is 1090mm long while the initial belt length is 1093.72mm. Calling E an idler, let's move it in X until the belt length is correct
# See the JuMP example for a more advanced optimization, a for-loop suffices here
dx = 1u"mm" #initial starting movement
for i = 1:100 #this is a gradient descent optimization https://en.wikipedia.org/wiki/Gradient_descent
  global dx -= (belt.length - calculateBeltLength(solved) )/10
  global pE = PlainPulley( pitch=Geometry2D.Circle( 0u"mm" + dx,   0u"mm", 14u"mm"), axis=-uk, name="E")
  global route = [pA, pB, pC, pD, pE]
  global solved = calculateRouteAngles(route)
  # println("Iteration $i: l=$(calculateBeltLength(solved)) with dx=$dx")
end
println("Iteration 100: l=$(calculateBeltLength(solved)) with dx=$dx\n")
Initial belt length is 1.0501215661822083 m
Iteration 100: l=1.0599999998956586 m with dx=-0.0058704664503579414 m
p = plot!(solved, segmentColor=:cyan)#, legend_background_color=:transparent, legend_position=:outerright)

Here, the initial routing is shown with the magenta belt, and the fit to one of the available belt lengths in aqua. SynchronousPulleys are shown with dashed edges, hinting their teeth.

Outputs

Having solved the system, various data can be output according to the design's needs. Of course these can be directly accessed in the solved array to drive custom plotting or other analyses. Printing the pulley information from the belt routing displays the positions of all elements, and the angles of wrap on each pulley:

printRoute(solved)
SynchronousPulley[Drive] @ [100.000mm,100.000mm] r[19.735mm]=[62grooves] arrive[299.973°] depart[92.919°] aWrap[152.947°] lWrap[52.682mm]
SynchronousPulley[B] @ [-100.000mm,100.000mm] r[9.549mm]=[30grooves] arrive[92.919°] depart[175.436°] aWrap[82.516°] lWrap[13.753mm]
SynchronousPulley[C] @ [-100.000mm,-100.000mm] r[25.465mm]=[80grooves] arrive[175.436°] depart[274.564°] aWrap[99.129°] lWrap[44.057mm]
SynchronousPulley[D] @ [100.000mm,-100.000mm] r[9.549mm]=[30grooves] arrive[274.564°] depart[55.939°] aWrap[141.375°] lWrap[23.562mm]
PlainPulley[E] @ [-5.870mm,0.000mm] r[14.000mm] arrive[235.939°] depart[119.973°] aWrap[115.967°] lWrap[28.336mm]

And we can print the points of departure and arrival of each segment, along with their length and angle:

printSegments(route2Segments(solved))
FreeSegment: depart[Drive] [98.995mm, 119.710mm] --> arrive[B] [-100.486mm, 109.537mm] l[199.740mm]@[-177.081°]
FreeSegment: depart[B] [-109.519mm, 100.760mm] --> arrive[C] [-125.384mm, -97.974mm] l[199.366mm]@[-94.564°]
FreeSegment: depart[C] [-97.974mm, -125.384mm] --> arrive[D] [100.760mm, -109.519mm] l[199.366mm]@[4.564°]
FreeSegment: depart[D] [105.348mm, -92.089mm] --> arrive[E] [-13.711mm, -11.598mm] l[143.715mm]@[145.939°]
FreeSegment: depart[E] [-0.013m, 0.012m] --> arrive[Drive] [0.110m, 0.083m] l[0.142m]@[29.973°]

The transmission ratios between the pulleys, ordered A-E, are:

calculateRatios(solved)
5×5 Matrix{Float64}:
  1.0        2.06667   0.775      2.06667  -1.40966
  0.483871   1.0       0.375      1.0      -0.682093
  1.29032    2.66667   1.0        2.66667  -1.81891
  0.483871   1.0       0.375      1.0      -0.682093
 -0.709392  -1.46608  -0.549779  -1.46608   1.0

With A being the top-right pulley, it is larger than B, top-left, and smaller than the C, lower-left, leading A:B=2.07 and A:C=0.78. Pulley E rotates in the opposite direction and has therefore a negative transmission ratio of -1.4 relative to A.