Compare commits
19 Commits
7403641a87
...
a31dd52392
Author | SHA1 | Date | |
---|---|---|---|
a31dd52392 | |||
74d5f2edcb | |||
affa70ede6 | |||
0bf09ea2fa | |||
39af6fb67d | |||
81622fbc52 | |||
869956c59c | |||
6404c5598d | |||
6eff8e54f4 | |||
e51d24e2ec | |||
53c1ad07cf | |||
b037d77545 | |||
c1d4f8e5cc | |||
0d0d52d5ac | |||
916fa74cb6 | |||
8648e9fcd1 | |||
850fe69847 | |||
8940f5965f | |||
f34775eb3f |
51
cad_script.py
Normal file
51
cad_script.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
from FreeCAD import Rotation, Base, newDocument
|
||||
|
||||
doc = newDocument()
|
||||
|
||||
|
||||
def create_blade(radius_rod, diameter):
|
||||
# set parameters (in mm)
|
||||
# central rod
|
||||
outer_radius = radius_rod
|
||||
inner_radius = outer_radius / 2
|
||||
height = diameter
|
||||
|
||||
# blades
|
||||
thickness = 1.5
|
||||
|
||||
rod = Part.makeCylinder(outer_radius, height, Base.Vector(0,0,0), Base.Vector(1,0,0))
|
||||
|
||||
hole_template = Part.makeCylinder(inner_radius, height+2, Base.Vector(-1,0,0), Base.Vector(1,0,0))
|
||||
|
||||
pierced_rod = rod.cut(hole_template)
|
||||
del hole_template # remove the template
|
||||
|
||||
# blades
|
||||
blade_template = Part.makeCylinder(height/2, thickness, Base.Vector(height/2,0, -thickness/2))
|
||||
#Part.show(blade_template)
|
||||
|
||||
blades1 = blade_template.cut(rod)
|
||||
blades2 = blades1.copy()
|
||||
blades2.Placement.Rotation = Rotation(0, 0, 60)
|
||||
blades3 = blades1.copy()
|
||||
blades3.Placement.Rotation = Rotation(0, 0, -60)
|
||||
|
||||
del blade_template # remove the template
|
||||
|
||||
# show all the stuff we created just now
|
||||
Part.show(pierced_rod)
|
||||
Part.show(blades1)
|
||||
Part.show(blades2)
|
||||
Part.show(blades3)
|
||||
|
||||
doc.recompute()
|
||||
|
||||
# small dispensers
|
||||
radius_rod = 4.0
|
||||
diameter = 34.0
|
||||
create_blade(radius_rod, diameter)
|
||||
|
||||
# big dispensers
|
||||
#radius_rod = 4.0
|
||||
#diameter = 94.0
|
||||
#create_blade(radius_rod, diameter)
|
0
prototype/__init__.py
Normal file
0
prototype/__init__.py
Normal file
BIN
prototype/big.amf
Normal file
BIN
prototype/big.amf
Normal file
Binary file not shown.
BIN
prototype/big_dispenser_blades.FCStd
Normal file
BIN
prototype/big_dispenser_blades.FCStd
Normal file
Binary file not shown.
|
@ -3,7 +3,79 @@ import matplotlib.pyplot as plt
|
|||
import math
|
||||
import operator
|
||||
|
||||
N = 5 # number of enclosed circles
|
||||
# scale in inkscape
|
||||
# 1 unit = 0.283 mm
|
||||
scale = 1000.0/282.222
|
||||
|
||||
def svg_circle(id, name, c, r):
|
||||
# create circle object in svg notation
|
||||
text = [' <circle\n',
|
||||
' id="circle{}"\n'.format(id),
|
||||
' inkscape:label="{}"\n'.format(name),
|
||||
' style="fill:none;stroke:#000000;stroke-width:0.1mm"\n',
|
||||
' r="{}mm"\n'.format(r),
|
||||
' cy="{}mm"\n'.format(c[1]),
|
||||
' cx="{}mm" />\n'.format(c[0])]
|
||||
|
||||
return text
|
||||
|
||||
def svg_half_circle(id, name, c, r, angle, orientation_flag=1):
|
||||
# draws half a circle centered at c with radius r
|
||||
# angle specifies how the half circle should be rotated
|
||||
# for the default angle of zero, it draws the top half of the circle
|
||||
|
||||
# convert angle to radians
|
||||
angle = angle/360.0 * 2.0 * np.pi
|
||||
|
||||
# compute starting point
|
||||
v = np.array([np.cos(angle), np.sin(angle)])
|
||||
begin = c + r * v # in millimeters
|
||||
begin *= scale # in svg units
|
||||
|
||||
# compute end point
|
||||
end = c - r * v # in millimeters
|
||||
end *= scale # in svg units
|
||||
|
||||
radius_scaled = r * scale # radius in svg units
|
||||
|
||||
text = [' <path \n '
|
||||
' id="path666" \n '
|
||||
' style="fill:none;stroke:#ff0000;stroke-width:0.60000002" \n'
|
||||
' d="M {} {} A {} {} 0 {} {} {} {}"'
|
||||
' />\n'.format(begin[0], begin[1], radius_scaled, radius_scaled, orientation_flag, orientation_flag,
|
||||
end[0], end[1])]
|
||||
|
||||
return text
|
||||
|
||||
def svg_arc(p1, p2, r, large_arc, sweep):
|
||||
begin = p1 * scale
|
||||
end = p2 * scale
|
||||
radius_scaled = r * scale
|
||||
text = [' <path \n '
|
||||
' id="path666" \n '
|
||||
' style="fill:none;stroke:#ff0000;stroke-width:0.60000002" \n'
|
||||
' d="M {} {} A {} {} 0 {} {} {} {}"'
|
||||
' />\n'.format(begin[0], begin[1], radius_scaled, radius_scaled, large_arc, sweep,
|
||||
end[0], end[1])]
|
||||
|
||||
return text
|
||||
|
||||
def svg_rectangle(id, name, c, width, heigth, angle):
|
||||
x = np.sqrt(c[0]**2 + c[1]**2) - width/2
|
||||
y = - heigth
|
||||
text = ['<g transform="rotate({})">\n '
|
||||
'<rect x="{}mm" y="{}mm" width="{}mm" height="{}mm" style="fill:none;stroke-width:0.1mm;stroke:rgb(0,0,0)" />\n '
|
||||
'</g>\n'
|
||||
.format(angle, x, y ,width, heigth)]
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def svg_line(p1, p2, width=1.0):
|
||||
text = ['<line x1="{}mm" y1="{}mm" x2="{}mm" y2="{}mm" style="stroke:rgb(0,0,0);stroke-width:{}mm" />'.format(p1[0], p1[1], p2[0], p2[1], width)]
|
||||
|
||||
return text
|
||||
|
||||
|
||||
# this function reads and processes data for optimal circle packaging obtained form packomania.com
|
||||
def read_circle_data(N):
|
||||
|
@ -26,6 +98,7 @@ def read_circle_data(N):
|
|||
|
||||
return radii[N], coords
|
||||
|
||||
|
||||
# this function sorts enclosed circle coordinates counter-clockwise w.r.t. the center point
|
||||
# TODO: there is a problem when circles are present that are not touching the boundary of the enclosing circle (e.g. N = 7)
|
||||
def sort_ccw(coords, center):
|
||||
|
@ -56,146 +129,547 @@ def compute_tangent_points(p, c, r):
|
|||
|
||||
return (T1x, T1y), (T2x, T2y)
|
||||
|
||||
# read radius and center coordinates for enclosed circles
|
||||
rtilde, coords = read_circle_data(N)
|
||||
|
||||
c = (0.0, 0.0) # center of big circle
|
||||
R = 1.0 # radius of big circle
|
||||
class PlateLayout:
|
||||
def __init__(self, N, plate_radius):
|
||||
self.N = N # number of enclosed circles
|
||||
self.plate_radius = plate_radius
|
||||
|
||||
plt.xlim((-1, 1))
|
||||
plt.ylim((-1, 1))
|
||||
plt.gca().set_aspect('equal', 'box')
|
||||
def compute_layout(self):
|
||||
# read radius and center coordinates for enclosed circles
|
||||
rtilde, coords = read_circle_data(self.N)
|
||||
|
||||
plt.ion()
|
||||
plt.show()
|
||||
c = (0.0, 0.0) # center of big circle
|
||||
R = 1.0 # radius of big circle
|
||||
|
||||
for p in coords:
|
||||
plt.plot(p[0], p[1], 'o')
|
||||
circle = plt.Circle(p, rtilde, fill=False)
|
||||
plt.gca().add_artist(circle)
|
||||
circle = plt.Circle(c, R, fill=False)
|
||||
plt.gca().add_artist(circle)
|
||||
plt.xlim((-1, 1))
|
||||
plt.ylim((-1, 1))
|
||||
plt.gca().set_aspect('equal', 'box')
|
||||
|
||||
plt.plot(c[0], c[1], 'o')
|
||||
plt.ion()
|
||||
plt.show()
|
||||
|
||||
coords_2 = []
|
||||
for k in range(0, N):
|
||||
p1 = coords[k]
|
||||
p2 = coords[(k+1) % N]
|
||||
for p in coords:
|
||||
plt.plot(p[0], p[1], 'o')
|
||||
circle = plt.Circle(p, rtilde, fill=False)
|
||||
plt.gca().add_artist(circle)
|
||||
circle = plt.Circle(c, R, fill=False)
|
||||
plt.gca().add_artist(circle)
|
||||
|
||||
# midpoint between center of two circles
|
||||
m = np.mean([p1, p2], axis=0)
|
||||
plt.plot(c[0], c[1], 'o')
|
||||
|
||||
# vector in direction of midpoint
|
||||
v = m - np.array(c)
|
||||
v = v/np.linalg.norm(v)
|
||||
#plt.plot(m[0], m[1], 'o')
|
||||
coords_2 = []
|
||||
for k in range(0, self.N):
|
||||
p1 = coords[k]
|
||||
p2 = coords[(k+1) % self.N]
|
||||
|
||||
# midpoint between center of two circles
|
||||
m = np.mean([p1, p2], axis=0)
|
||||
|
||||
# vector in direction of midpoint
|
||||
v = m - np.array(c)
|
||||
v = v/np.linalg.norm(v)
|
||||
#plt.plot(m[0], m[1], 'o')
|
||||
|
||||
|
||||
# optimization problem for computing position and radius for a maximal circle fitting in space between two big circles
|
||||
# and being fully contained in enclosing circle
|
||||
opti = casadi.Opti()
|
||||
# optimization problem for computing position and radius for a maximal circle fitting in space between two big circles
|
||||
# and being fully contained in enclosing circle
|
||||
opti = casadi.Opti()
|
||||
|
||||
r = opti.variable(1) # radius of new circle
|
||||
p = opti.variable(2) # center of new circle
|
||||
lamb = opti.variable(1) # distance of center of new circle to center of enclosing circle
|
||||
r = opti.variable(1) # radius of new circle
|
||||
p = opti.variable(2) # center of new circle
|
||||
lamb = opti.variable(1) # distance of center of new circle to center of enclosing circle
|
||||
|
||||
opti.minimize(-r)
|
||||
opti.subject_to(p == c + v * lamb)
|
||||
opti.subject_to((p[0] - p1[0])**2 + (p[1] - p1[1])**2 >= (rtilde + r)**2)
|
||||
opti.subject_to(R == lamb + r)
|
||||
opti.subject_to(r >= 0)
|
||||
opti.subject_to(r <= R)
|
||||
opti.minimize(-r)
|
||||
opti.subject_to(p == c + v * lamb)
|
||||
opti.subject_to((p[0] - p1[0])**2 + (p[1] - p1[1])**2 >= (rtilde + r)**2)
|
||||
opti.subject_to(R == lamb + r)
|
||||
opti.subject_to(r >= 0)
|
||||
opti.subject_to(r <= R)
|
||||
|
||||
opti.solver('ipopt')
|
||||
opti.solver('ipopt')
|
||||
|
||||
init_r = 0.1
|
||||
init_lamb = R - init_r
|
||||
init_p = c + v * init_lamb
|
||||
init_r = 0.1
|
||||
init_lamb = R - init_r
|
||||
init_p = c + v * init_lamb
|
||||
|
||||
opti.set_initial(r, init_r)
|
||||
opti.set_initial(p, init_p)
|
||||
opti.set_initial(lamb, init_lamb)
|
||||
opti.set_initial(r, init_r)
|
||||
opti.set_initial(p, init_p)
|
||||
opti.set_initial(lamb, init_lamb)
|
||||
|
||||
sol = opti.solve()
|
||||
sol = opti.solve()
|
||||
|
||||
p = sol.value(p)
|
||||
r = sol.value(r)
|
||||
lamb = sol.value(lamb)
|
||||
p = sol.value(p)
|
||||
r = sol.value(r)
|
||||
lamb = sol.value(lamb)
|
||||
|
||||
print("p = {}".format(p))
|
||||
print("r = {}".format(r))
|
||||
print("lambda = {}".format(lamb))
|
||||
print("v = {}".format(v))
|
||||
print("p = {}".format(p))
|
||||
print("r = {}".format(r))
|
||||
print("lambda = {}".format(lamb))
|
||||
print("v = {}".format(v))
|
||||
|
||||
coords_2.append(p)
|
||||
coords_2.append(p)
|
||||
|
||||
plt.plot(p[0], p[1], 'o')
|
||||
circle = plt.Circle(p, r, fill=False)
|
||||
plt.gca().add_artist(circle)
|
||||
plt.plot(p[0], p[1], 'o')
|
||||
circle = plt.Circle(p, r, fill=False)
|
||||
plt.gca().add_artist(circle)
|
||||
|
||||
tube1 = {}
|
||||
tube2 = {}
|
||||
|
||||
# postprocessing solution:
|
||||
# - output radii for circles
|
||||
# - output center coordinates, angle w.r.t. origin and distance from origin
|
||||
outer_radius = self.plate_radius # desired plate radius in meters
|
||||
tube1_radius = outer_radius * rtilde
|
||||
tube2_radius = outer_radius * r
|
||||
|
||||
print("\n------------------")
|
||||
print("optimal values")
|
||||
print("plate radius = {:6.3} m = {:6.2f} mm".format(outer_radius / 1000, outer_radius))
|
||||
print("big circles:")
|
||||
print(" radius = {:6.3} m = {:6.2f} mm".format(tube1_radius / 1000, tube1_radius))
|
||||
print(" diameter = {:6.3} m = {:6.2f} mm".format(2 * tube1_radius / 1000, 2 * tube1_radius))
|
||||
print("small circles:")
|
||||
print(" radius = {:6.3} m = {:6.2f} mm".format(tube2_radius, tube2_radius * 1000))
|
||||
print(" diameter = {:6.3} m = {:6.2f} mm".format(2*tube2_radius, 2*tube2_radius * 1000))
|
||||
|
||||
# compute coordinates and various measurements for fixed radii of plate and tubes
|
||||
self.target_plate_radius = 160.0
|
||||
self.target_center_hole_radius = 7.5
|
||||
self.target_radius_1 = 50.5
|
||||
self.target_radius_2 = 20.0
|
||||
teeth = 200
|
||||
|
||||
D = 2 * self.target_plate_radius
|
||||
self.plate_module = D/teeth
|
||||
|
||||
print("plate radius = {:6.2f} mm".format(self.target_plate_radius))
|
||||
print("big circle radius = {:6.2f} mm".format(self.target_radius_1))
|
||||
print("small circle radius = {:6.2f} mm".format(self.target_radius_2))
|
||||
|
||||
print("number of teeth: N = {}".format(teeth))
|
||||
print("pitch diameter: D = {} mm".format(D))
|
||||
print("module: M = {} mm".format(self.plate_module))
|
||||
|
||||
|
||||
# postprocessing solution:
|
||||
# - output radii for circles
|
||||
# - output center coordinates, angle w.r.t. origin and distance from origin
|
||||
outer_radius = 0.15 # desired plate radius in meters
|
||||
tube1_radius = outer_radius * rtilde
|
||||
tube2_radius = outer_radius * r
|
||||
# parameters for dispenser gears
|
||||
self.dispenser_module = 1.0
|
||||
dispenser_1_target_pitch_diameter_big = 50.0
|
||||
dispenser_1_target_pitch_diameter_small = 15.0
|
||||
dispenser_1_teeth_big = int(dispenser_1_target_pitch_diameter_big/self.dispenser_module)
|
||||
dispenser_1_teeth_small = int(dispenser_1_target_pitch_diameter_small/ self.dispenser_module)
|
||||
|
||||
print("\n------------------")
|
||||
print("plate radius = {:6.3} m = {:6.2f} mm".format(outer_radius, outer_radius * 1000))
|
||||
print("big circles:")
|
||||
print(" radius = {:6.3} m = {:6.2f} mm".format(tube1_radius, tube1_radius * 1000))
|
||||
print(" diameter = {:6.3} m = {:6.2f} mm".format(2*tube1_radius, 2*tube1_radius * 1000))
|
||||
print(" coordinates:")
|
||||
for k in range(0,N):
|
||||
x = coords[k][0] * 1000
|
||||
y = coords[k][1] * 1000
|
||||
dispenser_1_pitch_diameter_big = self.dispenser_module * dispenser_1_teeth_big
|
||||
dispenser_1_outer_diameter_big = dispenser_1_pitch_diameter_big + 2 * self.dispenser_module
|
||||
dispenser_1_pitch_diameter_small = self.dispenser_module * dispenser_1_teeth_small
|
||||
dispenser_1_outer_diameter_small = dispenser_1_pitch_diameter_small + 2 * self.dispenser_module
|
||||
|
||||
t1, t2 = compute_tangent_points((0,0),(x,y), rtilde * 1000)
|
||||
plt.plot(t1[0] / 1000, t1[1] / 1000, 'o')
|
||||
plt.plot(t2[0] / 1000, t2[1] / 1000, 'o')
|
||||
dispenser_2_target_pitch_diameter_big = 20.0
|
||||
dispenser_2_target_pitch_diameter_small = 15.0
|
||||
dispenser_2_teeth_big = int(dispenser_2_target_pitch_diameter_big / self.dispenser_module)
|
||||
dispenser_2_teeth_small = int(dispenser_2_target_pitch_diameter_small / self.dispenser_module)
|
||||
|
||||
angle = arctan2(y,x) * 360.0 / (2.0 * math.pi)
|
||||
dist = (x**2 + y**2)**0.5
|
||||
dispenser_2_pitch_diameter_big = self.dispenser_module * dispenser_2_teeth_big
|
||||
dispenser_2_outer_diameter_big = dispenser_2_pitch_diameter_big + 2 * self.dispenser_module
|
||||
dispenser_2_pitch_diameter_small = self.dispenser_module * dispenser_2_teeth_small
|
||||
dispenser_2_outer_diameter_small = dispenser_2_pitch_diameter_small + 2 * self.dispenser_module
|
||||
print("parameters for dispenser gears:")
|
||||
print(" big container:")
|
||||
print(" module = {}".format(self.dispenser_module))
|
||||
print(" pitch diameter big gear = {}".format(dispenser_1_pitch_diameter_big))
|
||||
print(" number of teeth big gear = {}".format(dispenser_1_teeth_big))
|
||||
print(" pitch diameter small gear = {}".format(dispenser_1_pitch_diameter_small))
|
||||
print(" number of teeth small gear = {}".format(dispenser_1_teeth_small))
|
||||
print(" small container:")
|
||||
print(" module = {}".format(self.dispenser_module))
|
||||
print(" pitch diameter big gear = {}".format(dispenser_2_pitch_diameter_big))
|
||||
print(" number of teeth big gear = {}".format(dispenser_2_teeth_big))
|
||||
print(" pitch diameter small gear = {}".format(dispenser_2_pitch_diameter_small))
|
||||
print(" number of teeth small gear = {}".format(dispenser_2_teeth_small))
|
||||
|
||||
angle_t1 = arctan2(t1[1], t1[0]) * 360.0 / (2.0 * math.pi)
|
||||
dist_t1 = (t1[0] ** 2 + t1[1] ** 2) ** 0.5
|
||||
# plot plate
|
||||
plt.figure(2)
|
||||
plt.plot(0.0, 0.0, 'o')
|
||||
circle = plt.Circle((0.0, 0.0), self.target_plate_radius, fill=False)
|
||||
plt.gca().add_artist(circle)
|
||||
plt.xlim((-self.target_plate_radius*1.1, self.target_plate_radius*1.1))
|
||||
plt.ylim((-self.target_plate_radius*1.1, self.target_plate_radius*1.1))
|
||||
plt.gca().set_aspect('equal', 'box')
|
||||
|
||||
angle_t2 = arctan2(t2[1], t2[0]) * 360.0 / (2.0 * math.pi)
|
||||
dist_t2 = (t2[0] ** 2 + t2[1] ** 2) ** 0.5
|
||||
print(" k = {}, (x,y) = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg, dist = {:8.3f} mm".format(k, x, y, angle, dist))
|
||||
print(" t1 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg, dist = {:8.3f} mm".format(t1[0], t1[1], angle_t1,
|
||||
dist_t1))
|
||||
print(" t2 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg, dist = {:8.3f} mm".format(t2[0], t2[1], angle_t2,
|
||||
dist_t2))
|
||||
print("\n")
|
||||
# plate coordinates
|
||||
print("plate coordinates: (x,y) = ({:8.3f}, {:8.3f})".format(0.0, 0.0))
|
||||
|
||||
print("small circles:")
|
||||
print(" radius = {:6.3} m = {:6.2f} mm".format(tube2_radius, tube2_radius * 1000))
|
||||
print(" diameter = {:6.3} m = {:6.2f} mm".format(2*tube2_radius, 2*tube2_radius * 1000))
|
||||
print(" coordinates:")
|
||||
for k in range(0,N):
|
||||
x = coords_2[k][0] * 1000
|
||||
y = coords_2[k][1] * 1000
|
||||
self.tube_1_coords = {}
|
||||
self.tube_2_coords = {}
|
||||
self.tube_1_angles = {}
|
||||
self.tube_2_angles = {}
|
||||
self.tube_1_tangents = {}
|
||||
self.tube_1_tangent_angles = {}
|
||||
self.tube_1_cuts = {}
|
||||
self.tube_2_tangents = {}
|
||||
self.tube_2_tangent_angles = {}
|
||||
self.tube_2_cuts = {}
|
||||
|
||||
t1, t2 = compute_tangent_points((0, 0), (x, y), r * 1000)
|
||||
plt.plot(t1[0] / 1000, t1[1] / 1000, 'o')
|
||||
plt.plot(t2[0] / 1000, t2[1] / 1000, 'o')
|
||||
print(" big circle coordinates:")
|
||||
for k in range(0,self.N):
|
||||
x = coords[k][0] * outer_radius
|
||||
y = coords[k][1] * outer_radius
|
||||
|
||||
angle = arctan2(y, x) * 360.0 / (2.0 * math.pi)
|
||||
dist = (x ** 2 + y ** 2) ** 0.5
|
||||
angle = arctan2(y, x) * 360.0 / (2.0 * math.pi)
|
||||
|
||||
angle_t1 = arctan2(t1[1], t1[0]) * 360.0 / (2.0 * math.pi)
|
||||
dist_t1 = (t1[0] ** 2 + t1[1] ** 2) ** 0.5
|
||||
self.tube_1_coords[k] = (x,y)
|
||||
self.tube_1_angles[k] = angle
|
||||
|
||||
angle_t2 = arctan2(t2[1], t2[0]) * 360.0 / (2.0 * math.pi)
|
||||
dist_t2 = (t2[0] ** 2 + t2[1] ** 2) ** 0.5
|
||||
print(" k = {}, (x,y) = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg, dist = {:8.3f} mm".format(k, x, y, angle, dist))
|
||||
print(" t1 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg, dist = {:8.3f} mm".format(t1[0], t1[1], angle_t1,
|
||||
dist_t1))
|
||||
print(" t2 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg, dist = {:8.3f} mm".format(t2[0], t2[1], angle_t2,
|
||||
dist_t2))
|
||||
print(" k = {}, (x,y) = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg".format(k, x, y, angle))
|
||||
|
||||
circle = plt.Circle((x,y), self.target_radius_1, fill=False)
|
||||
plt.gca().add_artist(circle)
|
||||
|
||||
p1 = dispenser_1_pitch_diameter_big
|
||||
p2 = dispenser_1_pitch_diameter_small
|
||||
a1 = self.dispenser_module
|
||||
a2 = self.dispenser_module
|
||||
offset_1 = sqrt((p1 / 2 + p2 / 2) ** 2 - (p1 / 2 + a1) ** 2) - p2 / 2 - a2
|
||||
print("dispenser 1 offset = {}".format(offset_1))
|
||||
|
||||
print(" big circle tangent points: ")
|
||||
for k in range(0, self.N):
|
||||
x = coords[k][0] * outer_radius
|
||||
y = coords[k][1] * outer_radius
|
||||
|
||||
t1, t2 = compute_tangent_points((0, 0), (x, y), self.target_radius_1)
|
||||
|
||||
angle1_rad = arctan2(t1[1], t1[0])
|
||||
angle2_rad = arctan2(t2[1], t2[0])
|
||||
angle1_deg = angle1_rad * 360.0 / (2.0 * math.pi)
|
||||
angle2_deg = angle2_rad * 360.0 / (2.0 * math.pi)
|
||||
|
||||
self.tube_1_tangents[k] = (t1, t2)
|
||||
self.tube_1_tangent_angles[k] = (angle1_deg, angle2_deg)
|
||||
|
||||
print(
|
||||
" k = {}, t1 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg\n t2 = ({:8.3f}, {:8.3f}), angle "
|
||||
"= {:8.3f} deg".format(k, t1[0], t1[1], angle1_deg, t2[0], t2[1], angle2_deg))
|
||||
|
||||
plt.plot(t1[0], t1[1], 'o')
|
||||
plt.plot(t2[0], t2[1], 'o')
|
||||
|
||||
## compute position of cut for dispenser gear
|
||||
# vector pointing from center in direction of tangent point
|
||||
v = np.array([math.cos(angle1_rad), math.sin(angle1_rad)])
|
||||
|
||||
cut_center = np.array(t1) - v * (offset_1 + dispenser_1_outer_diameter_small/2.0)
|
||||
self.tube_1_cuts[k] = {}
|
||||
self.tube_1_cuts[k]['center'] = cut_center
|
||||
self.tube_1_cuts[k]['tangent_point'] = t1
|
||||
self.tube_1_cuts[k]['angle_deg'] = angle1_deg
|
||||
self.tube_1_cuts[k]['length'] = dispenser_1_outer_diameter_small
|
||||
self.tube_1_cuts[k]['width'] = 5.0
|
||||
|
||||
plt.plot(cut_center[0], cut_center[1], 'o')
|
||||
|
||||
|
||||
pass
|
||||
|
||||
|
||||
print(" small circle coordinates:")
|
||||
for k in range(0,self.N):
|
||||
x = coords_2[k][0] * outer_radius
|
||||
y = coords_2[k][1] * outer_radius
|
||||
|
||||
angle = arctan2(y, x) * 360.0 / (2.0 * math.pi)
|
||||
|
||||
self.tube_2_coords[k] = (x, y)
|
||||
self.tube_2_angles[k] = angle
|
||||
|
||||
print(" k = {}, (x,y) = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg".format(k, x, y, angle))
|
||||
|
||||
circle = plt.Circle((x, y), self.target_radius_2, fill=False)
|
||||
plt.gca().add_artist(circle)
|
||||
|
||||
p1 = dispenser_2_pitch_diameter_big
|
||||
p2 = dispenser_2_pitch_diameter_small
|
||||
a1 = self.dispenser_module
|
||||
a2 = self.dispenser_module
|
||||
offset_2 = sqrt((p1 / 2 + p2 / 2) ** 2 - (p1 / 2 + a1) ** 2) - p2 / 2 - a2
|
||||
print("dispenser 2 offset = {}".format(offset_2))
|
||||
|
||||
print(" small circle tangent points: ")
|
||||
for k in range(0, self.N):
|
||||
x = coords_2[k][0] * outer_radius
|
||||
y = coords_2[k][1] * outer_radius
|
||||
|
||||
t1, t2 = compute_tangent_points((0, 0), (x, y), self.target_radius_2)
|
||||
angle1_rad = arctan2(t1[1], t1[0])
|
||||
angle2_rad = arctan2(t2[1], t2[0])
|
||||
angle1_deg = angle1_rad * 360.0 / (2.0 * math.pi)
|
||||
angle2_deg = angle2_rad * 360.0 / (2.0 * math.pi)
|
||||
self.tube_2_tangents[k] = (t1, t2)
|
||||
self.tube_2_tangent_angles[k] = (angle1_deg, angle2_deg)
|
||||
|
||||
print(" k = {}, t1 = ({:8.3f}, {:8.3f}), angle = {:8.3f} deg\n t2 = ({:8.3f}, {:8.3f}), angle "
|
||||
"= {:8.3f} deg".format(k, t1[0], t1[1], angle1_deg, t2[0], t2[1], angle2_deg))
|
||||
|
||||
plt.plot(t1[0], t1[1], 'o')
|
||||
plt.plot(t2[0], t2[1], 'o')
|
||||
|
||||
## compute position of cut for dispenser gear
|
||||
# vector pointing from center in direction of tangent point
|
||||
v = np.array([math.cos(angle1_rad), math.sin(angle1_rad)])
|
||||
|
||||
cut_center = np.array(t1) + v * (offset_2 + dispenser_2_outer_diameter_small/2.0)
|
||||
self.tube_2_cuts[k] = {}
|
||||
self.tube_2_cuts[k]['center'] = cut_center
|
||||
self.tube_2_cuts[k]['tangent_point'] = t1
|
||||
self.tube_2_cuts[k]['angle_deg'] = angle1_deg
|
||||
self.tube_2_cuts[k]['length'] = dispenser_2_outer_diameter_small
|
||||
self.tube_2_cuts[k]['width'] = 5.0
|
||||
|
||||
plt.plot(cut_center[0], cut_center[1], 'o')
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
input: file = svg with plate gear centered at (0,0)
|
||||
"""
|
||||
def generate_svg(self, file):
|
||||
|
||||
f = open(file)
|
||||
f_lines = f.readlines()
|
||||
f.close()
|
||||
|
||||
|
||||
circle_found = False
|
||||
for k in range(len(f_lines)):
|
||||
current_line = f_lines[k]
|
||||
|
||||
if 'transform' in current_line:
|
||||
# rotate gear such that teeth match the cut lines
|
||||
index = current_line.find('"\n')
|
||||
f_lines[k] = current_line[0:index] + " rotate(0.9)" + current_line[-2:]
|
||||
|
||||
if 'd="m ' in current_line:
|
||||
# remove center whole drawn by gear-dev
|
||||
gear_data = current_line
|
||||
index = gear_data.find('z') # end of path containing gear outline coordinates
|
||||
|
||||
f_lines[k] = gear_data[0:index+1] + gear_data[-2:]
|
||||
|
||||
|
||||
if '<circle' in current_line:
|
||||
circle_found = True
|
||||
|
||||
if circle_found and 'r=' in current_line:
|
||||
# adjust center hole radius
|
||||
f_lines[k] = ' r="{}mm"\n'.format(self.target_center_hole_radius)
|
||||
|
||||
# delete last line with </svg> command
|
||||
f_lines.remove(f_lines[-1])
|
||||
|
||||
# output plate as svg
|
||||
text = svg_circle(0, 'plate', (0,0), self.target_plate_radius)
|
||||
f_lines = f_lines + text
|
||||
|
||||
output_all = False
|
||||
if output_all:
|
||||
f_lines = self.output_whole(f_lines)
|
||||
else:
|
||||
f_lines = self.output_segment(f_lines, 2)
|
||||
|
||||
f_lines.append('</svg>\n')
|
||||
|
||||
# write new svg image
|
||||
fw = open('output.svg', 'w')
|
||||
fw.writelines(f_lines)
|
||||
fw.close()
|
||||
|
||||
pass
|
||||
|
||||
def output_segment(self, f_lines, k):
|
||||
|
||||
# k = which segment?
|
||||
k_next = (k + 1) % 5
|
||||
|
||||
# center hole
|
||||
a = self.tube_1_angles[k]
|
||||
a = a / 360.0 * 2.0 * np.pi
|
||||
vunit = np.array([np.cos(a), np.sin(a)])
|
||||
p1 = vunit * self.target_center_hole_radius
|
||||
|
||||
a2 = self.tube_1_angles[k_next]
|
||||
a2 = a2 / 360.0 * 2.0 * np.pi
|
||||
vunit2 = np.array([np.cos(a2), np.sin(a2)])
|
||||
p2 = vunit2 * self.target_center_hole_radius
|
||||
|
||||
text = svg_arc(p1, p2, self.target_center_hole_radius, 0, 1)
|
||||
f_lines = f_lines + text
|
||||
|
||||
|
||||
# big circles arcs
|
||||
c = self.tube_1_coords[k]
|
||||
angle = self.tube_1_angles[k]
|
||||
text = svg_half_circle(k, 'big circle', c, self.target_radius_1, angle)
|
||||
f_lines = f_lines + text
|
||||
|
||||
c = self.tube_1_coords[k_next]
|
||||
angle = self.tube_1_angles[k_next]
|
||||
text = svg_half_circle(k_next, 'big circle', c, self.target_radius_1, angle, orientation_flag=0)
|
||||
f_lines = f_lines + text
|
||||
|
||||
# small circle
|
||||
c = self.tube_2_coords[k]
|
||||
text = svg_circle(k, 'small circle', c, self.target_radius_2)
|
||||
f_lines = f_lines + text
|
||||
|
||||
# gear pos for big circle
|
||||
c = self.tube_1_tangents[k_next]
|
||||
circle_midpoint = self.tube_1_coords[k_next]
|
||||
v = np.array(c[0]) - np.array(circle_midpoint)
|
||||
v = v / np.linalg.norm(v)
|
||||
|
||||
marking_length = 5.0
|
||||
|
||||
p1 = c[0]
|
||||
p2 = c[0] + v * marking_length
|
||||
|
||||
text = svg_line(p1, p2)
|
||||
f_lines = f_lines + text
|
||||
|
||||
# rectangle for big circles
|
||||
c = self.tube_1_cuts[k_next]
|
||||
text = svg_rectangle(k_next, 'cut', c['center'], c['length'], c['width'], c['angle_deg'])
|
||||
f_lines = f_lines + text
|
||||
|
||||
# gear pos for small circle
|
||||
c = self.tube_2_tangents[k]
|
||||
circle_midpoint = self.tube_2_coords[k]
|
||||
v = np.array(c[0]) - np.array(circle_midpoint)
|
||||
v = v/np.linalg.norm(v)
|
||||
|
||||
marking_length = 5.0
|
||||
|
||||
p1 = c[0]
|
||||
p2 = c[0] + v * marking_length
|
||||
|
||||
text = svg_line(p1, p2)
|
||||
f_lines = f_lines + text
|
||||
|
||||
# rectangle for small circles
|
||||
c = self.tube_2_cuts[k]
|
||||
text = svg_rectangle(k, 'cut', c['center'], c['length'], c['width'], c['angle_deg'])
|
||||
f_lines = f_lines + text
|
||||
|
||||
# segment border (right)
|
||||
a = self.tube_1_angles[k]
|
||||
a = a / 360.0 * 2.0 * np.pi
|
||||
r1 = np.linalg.norm(np.array(self.tube_1_coords[k])) - self.target_radius_1
|
||||
vunit = np.array([np.cos(a), np.sin(a)])
|
||||
p1 = vunit * self.target_center_hole_radius
|
||||
p2 = vunit * r1
|
||||
text = svg_line(p1, p2, 0.1)
|
||||
f_lines = f_lines + text
|
||||
|
||||
r2 = np.linalg.norm(np.array(self.tube_1_coords[k])) + self.target_radius_1
|
||||
p3 = vunit * r2
|
||||
r3 = self.target_plate_radius - self.plate_module
|
||||
p4 = vunit * r3
|
||||
text = svg_line(p3, p4, 0.1)
|
||||
f_lines = f_lines + text
|
||||
|
||||
# segment border (left)
|
||||
a = self.tube_1_angles[k_next]
|
||||
a = a / 360.0 * 2.0 * np.pi
|
||||
r1 = np.linalg.norm(np.array(self.tube_1_coords[k_next])) - self.target_radius_1
|
||||
vunit = np.array([np.cos(a), np.sin(a)])
|
||||
p1 = vunit * self.target_center_hole_radius
|
||||
p2 = vunit * r1
|
||||
text = svg_line(p1, p2, 0.1)
|
||||
f_lines = f_lines + text
|
||||
|
||||
r2 = np.linalg.norm(np.array(self.tube_1_coords[k_next])) + self.target_radius_1
|
||||
p3 = vunit * r2
|
||||
r3 = self.target_plate_radius - self.plate_module
|
||||
p4 = vunit * r3
|
||||
text = svg_line(p3, p4, 0.1)
|
||||
f_lines = f_lines + text
|
||||
|
||||
return f_lines
|
||||
|
||||
def output_whole(self, f_lines):
|
||||
# output big circles as svg
|
||||
for k, c in self.tube_1_coords.items():
|
||||
text = svg_circle(k, 'big circle', c, self.target_radius_1)
|
||||
f_lines = f_lines + text
|
||||
pass
|
||||
|
||||
# output small circles as svg
|
||||
for k, c in self.tube_2_coords.items():
|
||||
text = svg_circle(k, 'small circle', c, self.target_radius_2)
|
||||
f_lines = f_lines + text
|
||||
pass
|
||||
|
||||
# gear markings for big cirlces and small circles
|
||||
for k, c in self.tube_1_tangents.items():
|
||||
circle_midpoint = self.tube_1_coords[k]
|
||||
v = np.array(c[0]) - np.array(circle_midpoint)
|
||||
v = v/np.linalg.norm(v)
|
||||
|
||||
marking_length = 5.0
|
||||
|
||||
p1 = c[0]
|
||||
p2 = c[0] + v * marking_length
|
||||
|
||||
text = svg_line(p1, p2)
|
||||
f_lines = f_lines + text
|
||||
|
||||
for k, c in self.tube_2_tangents.items():
|
||||
circle_midpoint = self.tube_2_coords[k]
|
||||
v = np.array(c[0]) - np.array(circle_midpoint)
|
||||
v = v/np.linalg.norm(v)
|
||||
|
||||
marking_length = 5.0
|
||||
|
||||
p1 = c[0]
|
||||
p2 = c[0] + v * marking_length
|
||||
|
||||
text = svg_line(p1, p2)
|
||||
f_lines = f_lines + text
|
||||
pass
|
||||
|
||||
# output cuts for big circles
|
||||
for k, c in self.tube_1_cuts.items():
|
||||
text = svg_rectangle(k, 'cut', c['center'], c['length'], c['width'], c['angle_deg'])
|
||||
f_lines = f_lines + text
|
||||
pass
|
||||
|
||||
# output cuts for small circles
|
||||
for k, c in self.tube_2_cuts.items():
|
||||
text = svg_rectangle(k, 'cut', c['center'], c['length'], c['width'], c['angle_deg'])
|
||||
f_lines = f_lines + text
|
||||
pass
|
||||
|
||||
# lines for manufacturing out of multiple pieces
|
||||
for k, a in self.tube_1_angles.items():
|
||||
a = a/360.0 * 2.0 * np.pi
|
||||
r1 = np.linalg.norm(np.array(self.tube_1_coords[k])) - self.target_radius_1
|
||||
vunit = np.array([np.cos(a), np.sin(a)])
|
||||
p1 = vunit * self.target_center_hole_radius
|
||||
p2 = vunit * r1
|
||||
text = svg_line(p1, p2, 0.1)
|
||||
f_lines = f_lines + text
|
||||
|
||||
r2 = np.linalg.norm(np.array(self.tube_1_coords[k])) + self.target_radius_1
|
||||
p3 = vunit * r2
|
||||
r3 = self.target_plate_radius - self.plate_module
|
||||
p4 = vunit * r3
|
||||
text = svg_line(p3, p4, 0.1)
|
||||
f_lines = f_lines + text
|
||||
|
||||
pass
|
||||
|
||||
return f_lines
|
||||
|
||||
|
||||
pass
|
386
prototype/cut_v1.svg
Normal file
386
prototype/cut_v1.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 96 KiB |
172
prototype/cut_v1_part.svg
Normal file
172
prototype/cut_v1_part.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 27 KiB |
415
prototype/dispenser_gears.svg
Normal file
415
prototype/dispenser_gears.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 82 KiB |
331
prototype/dispenser_gears_v1.svg
Normal file
331
prototype/dispenser_gears_v1.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 140 KiB |
18
prototype/notes.txt
Normal file
18
prototype/notes.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
27.08.2019:
|
||||
Next step: create template for lasercutter consisting of multiple segments (utilize rotation symmetry of the pieces -> compose plate of several segments)
|
||||
|
||||
11.09.2019:
|
||||
Radius for big containers was extendend a bit, because tubes seem to be bigger than 10cm in diameter
|
||||
Lasercut segments for plate. Profile: FLB_Sperrholz_4mm_Natur_new
|
||||
|
||||
Important: SVG produced by python script needs to be openend, transformed and saved in inkscape, otherwise import in CorelDraw fails
|
||||
|
||||
In progress:
|
||||
- automate generation of plate segment
|
||||
|
||||
TODO:
|
||||
- stack 2 plates for better stability (add cuts at small circles)
|
||||
- second ring above the first plate to fix the containers and hide the gear mechanic (or manufacure them from acrylic to show the mechanic)
|
||||
- think about if stacked plate is to thick for a single gear (i.e. we may need two gears in order to transfer motion from below the plate to dispensers)
|
||||
- shovels for dispensers by cutting segments from a sphere
|
||||
- connectors for segments (jigsaw puzzle style)
|
56
prototype/plate.py
Normal file
56
prototype/plate.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
import os
|
||||
from FreeCAD import Rotation, Base, newDocument
|
||||
|
||||
os.chdir('/home/bt304019/programming/imaginaerraum/cerealist/prototype')
|
||||
|
||||
from circles import PlateLayout
|
||||
|
||||
N = 5
|
||||
plate_radius = 150.0
|
||||
plate_layout = PlateLayout(N, plate_radius)
|
||||
tube1, tube2 = plate_layout.compute_layout()
|
||||
|
||||
print("tube1 = {}".format(tube1))
|
||||
print("tube2 = {}".format(tube2))
|
||||
|
||||
# use slightly smaller radius so we have some gap
|
||||
tube1['radius'] = 50.0
|
||||
tube2['radius'] = 20.0
|
||||
|
||||
doc = newDocument()
|
||||
|
||||
# set parameters (in mm)
|
||||
height = 1.0
|
||||
|
||||
# create plate
|
||||
plate = Part.makeCylinder(plate_radius, height, Base.Vector(0,0,0), Base.Vector(0,0,1))
|
||||
|
||||
|
||||
r = tube1['radius']
|
||||
for k in range(0,len(tube1['coords'].values())):
|
||||
print(k)
|
||||
v = Base.Vector(tube1['coords'][k][0], tube1['coords'][k][1], -1)
|
||||
big_circle_template = Part.makeCylinder(tube1['radius'], height+2, v, Base.Vector(0,0,1))
|
||||
|
||||
plate = plate.cut(big_circle_template)
|
||||
del big_circle_template # remove the template
|
||||
|
||||
Part.show(plate)
|
||||
|
||||
# blades
|
||||
blade_template = Part.makeCylinder(height/2, thickness, Base.Vector(height/2,0, -thickness/2))
|
||||
#Part.show(blade_template)
|
||||
|
||||
blades1 = blade_template.cut(rod)
|
||||
blades2 = blades1.copy()
|
||||
blades2.Placement.Rotation = Rotation(0, 0, 90)
|
||||
|
||||
del blade_template # remove the template
|
||||
|
||||
# show all the stuff we created just now
|
||||
Part.show(pierced_rod)
|
||||
Part.show(blades1)
|
||||
Part.show(blades2)
|
||||
|
||||
doc.recompute()
|
||||
|
142
prototype/plate_gear_v1.svg
Normal file
142
prototype/plate_gear_v1.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 24 KiB |
73
prototype/plate_sketch.svg
Normal file
73
prototype/plate_sketch.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 89 KiB |
BIN
prototype/plattenzuschnitt24.de.pdf
Normal file
BIN
prototype/plattenzuschnitt24.de.pdf
Normal file
Binary file not shown.
BIN
prototype/small.amf
Normal file
BIN
prototype/small.amf
Normal file
Binary file not shown.
BIN
prototype/small_dispenser_blades.FCStd
Normal file
BIN
prototype/small_dispenser_blades.FCStd
Normal file
Binary file not shown.
5
prototype/test.py
Normal file
5
prototype/test.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from circles import PlateLayout
|
||||
|
||||
p = PlateLayout(5, 150.0)
|
||||
p.compute_layout()
|
||||
p.generate_svg('plate_sketch.svg')
|
7
references.txt
Normal file
7
references.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
https://en.wikipedia.org/wiki/Circle_packing_in_a_circle
|
||||
|
||||
https://geargenerator.com/
|
||||
|
||||
http://hessmer.org/gears/InvoluteSpurGearBuilder.html
|
||||
|
||||
https://forum.linuxcnc.org/31-cad-cam/36773-freecad-gear-modeling-and-design
|
Loading…
Reference in New Issue
Block a user