148 lines
6.4 KiB
Python
148 lines
6.4 KiB
Python
"""Concept assembly only; run with FreeCAD GUI using rebuild_encoder_concept.FCMacro.
|
|
|
|
All placements except the explicitly supplied nominal dimensions are provisional.
|
|
Do not use this model as a fabrication drawing.
|
|
"""
|
|
import math
|
|
from pathlib import Path
|
|
import FreeCAD as App
|
|
import FreeCADGui as Gui
|
|
import Part
|
|
import Import
|
|
from pivy import coin
|
|
|
|
root = Path(__file__).resolve().parent.parent
|
|
out = root / 'models'
|
|
doc = App.newDocument('M9C12_EncoderConcept')
|
|
V = App.Vector
|
|
|
|
def cyl(r, h, x, y, z):
|
|
return Part.makeCylinder(r, h, V(x, y, z))
|
|
|
|
def item(name, label, shape, color, transparency=0):
|
|
obj = doc.addObject('PartDesign::Feature', name)
|
|
obj.Label = label
|
|
obj.Shape = shape
|
|
obj.ViewObject.ShapeColor = color
|
|
obj.ViewObject.LineColor = (0.13, 0.13, 0.13)
|
|
obj.ViewObject.Transparency = transparency
|
|
return obj
|
|
|
|
gray = (0.32, 0.35, 0.38)
|
|
blue = (0.20, 0.48, 0.80)
|
|
orange = (0.96, 0.55, 0.12)
|
|
green = (0.05, 0.55, 0.25)
|
|
silver = (0.78, 0.80, 0.82)
|
|
|
|
# The supplied existing model has rear at z=0 and front at z=30.
|
|
motor = item('Motor', '01 M9C12 original approximate model',
|
|
Part.read(str(out / 'MAD_M9C12.step')), gray)
|
|
motor.ViewObject.Transparency = 60
|
|
|
|
# User-supplied 27 mm rear interface is a flat mating face at z=0, not a
|
|
# claimed protruding shaft. Seat lies on the rear side (-Z).
|
|
# 2.2 mm screw head nests in an assumed 2.6 mm counterbore underneath the magnet.
|
|
seat = cyl(13.5, 7.5, 0, 0, -7.5)
|
|
seat = seat.cut(cyl(1.65, 7.52, 0, 0, -7.51))
|
|
seat = seat.cut(cyl(5.1, 3.0, 0, 0, -7.5)) # magnet pocket
|
|
seat = seat.cut(cyl(3.5, 2.6, 0, 0, -4.5)) # assumed M3 head pocket
|
|
holder = item('MagnetHolder', '02 Rotating 3D printed magnet holder - concept', seat, orange)
|
|
mag = item('Magnet', '03 Magnet 10 x 3 mm - magnetization to verify', cyl(5, 3.0, 0, 0, -7.5), silver)
|
|
|
|
# Board: 33 mm, 1.6 mm thickness assumed. Three holes as pictured.
|
|
boardshape = cyl(16.5, 1.6, 0, 0, -10.9)
|
|
board_holes = [(0, 13.49), (-11.71, -6.76), (11.71, -6.76)]
|
|
for x, y in board_holes:
|
|
boardshape = boardshape.cut(cyl(1.5, 1.62, x, y, -10.91))
|
|
board = item('EncoderPCB', '04 MT6826S PCB 33mm - thickness assumed', boardshape, green)
|
|
# 1 mm spacing from magnet face z=-7.5 to chip surface z=-8.5. Chip height and
|
|
# body size are visual approximations, not package dimensions.
|
|
chip = item('SensorChip', '05 Sensor face toward magnet - gap 1mm',
|
|
Part.makeBox(4, 5, 0.8, V(-2, -2.5, -9.3)), (0.10, 0.10, 0.11))
|
|
|
|
# Fixed stand: ring behind the PCB plus four slender uprights and 2 mm
|
|
# mounting pads attached to the assumed four 45-degree 50 mm PCD holes.
|
|
stand = cyl(20.75, 2.6, 0, 0, -13.5).cut(cyl(11, 2.62, 0, 0, -13.51))
|
|
for x, y in board_holes:
|
|
stand = stand.cut(cyl(1.65, 2.62, x, y, -13.51))
|
|
for i in range(4):
|
|
a = math.radians(45 + i * 90)
|
|
ux, uy = math.cos(a), math.sin(a)
|
|
px, py = 19 * ux, 19 * uy
|
|
x, y = 25 * ux, 25 * uy
|
|
post = cyl(1.6, 11.5, px, py, -13.5)
|
|
pad = cyl(4.8, 2, x, y, -2)
|
|
bridge = Part.makeBox(8, 3.2, 2, V(18, -1.6, -2))
|
|
bridge.rotate(V(0, 0, 0), V(0, 0, 1), 45 + i * 90)
|
|
stand = stand.fuse(post).fuse(pad).fuse(bridge)
|
|
stand = stand.cut(cyl(2.15, 2.02, x, y, -2.01))
|
|
stand = stand.removeSplitter()
|
|
support = item('EncoderBracket', '06 Fixed 3D printed bracket - PCD 50 concept', stand, blue)
|
|
|
|
# M3 head shown on rear at z=-4.5 and nominal shank through to front;
|
|
# actual length, head diameter, nut and hole fit are placeholders.
|
|
shaft = cyl(1.48, 45.9, 0, 0, -1.9)
|
|
head = cyl(3.0, 2.2, 0, 0, -4.1)
|
|
fastener = item('M3Bolt', '07 M3 fastener - head thickness 2.2mm', shaft.fuse(head), silver)
|
|
# A nut is a symbolic approximate hex in front of the original model.
|
|
points = [V(3.2*math.cos(math.radians(30+i*60)),
|
|
3.2*math.sin(math.radians(30+i*60)), 44) for i in range(6)]
|
|
points.append(points[0])
|
|
nut = Part.Face(Part.Wire(Part.makePolygon(points).Edges)).extrude(V(0,0,2.4))
|
|
nut = nut.cut(cyl(1.5, 2.42, 0, 0, 43.99))
|
|
nut_obj = item('M3Nut', '08 M3 nut - location indicative', nut, silver)
|
|
fasteners=[]
|
|
for i in range(4):
|
|
a = math.radians(45+i*90)
|
|
x,y=25*math.cos(a),25*math.sin(a)
|
|
# Nominal 8 mm length extends 2 mm through bracket and 6 into motor.
|
|
shank=cyl(1.95,8,x,y,-2)
|
|
screwhead=cyl(3.4,2.5,x,y,-4.5)
|
|
fasteners.append(item('M4Screw%d'%i, '09 M4x8 mounting screw %d - illustrative'%(i+1), shank.fuse(screwhead), silver))
|
|
|
|
doc.recompute()
|
|
for obj in [motor,holder,mag,board,chip,support,fastener,nut_obj]+fasteners:
|
|
if obj.Shape.isNull() or not obj.Shape.isValid():
|
|
raise RuntimeError('invalid: '+obj.Name)
|
|
|
|
view = Gui.activeDocument().activeView()
|
|
# Rear three-quarter view; adjust after visual inspection.
|
|
view.getCameraNode().orientation.setValue(coin.SbRotation(0.880476, 0.115917, -0.279848, -0.364705))
|
|
view.fitAll()
|
|
Gui.updateGui()
|
|
view.saveImage(str(out / 'M9C12_MT6826S_安装示意.png'), 1600, 1200, 'White')
|
|
# Diagram-only exploded view: move pieces outward along the axis, preserving
|
|
# their relative orientation. The saved assembly remains assembled.
|
|
for obj, dz in [(holder,-8), (mag,-15), (board,-25), (chip,-25), (support,-33)]:
|
|
obj.Placement = App.Placement(V(0, 0, dz), App.Rotation())
|
|
doc.recompute()
|
|
view.fitAll()
|
|
Gui.updateGui()
|
|
view.saveImage(str(out / 'M9C12_MT6826S_爆炸示意.png'), 1600, 1200, 'White')
|
|
for obj in [holder,mag,board,chip,support]:
|
|
obj.Placement = App.Placement()
|
|
doc.recompute()
|
|
view.fitAll()
|
|
Gui.updateGui()
|
|
# The principal conceptual parts should not physically overlap in this model;
|
|
# mere face contacts have zero common volume. Sensor fasteners are omitted.
|
|
parts = [motor,holder,mag,board,chip,support,fastener,nut_obj]+fasteners
|
|
warnings = []
|
|
for i, first in enumerate(parts):
|
|
for second in parts[i+1:]:
|
|
overlap = first.Shape.common(second.Shape).Volume
|
|
if overlap > 0.001:
|
|
warnings.append('%s / %s : %.4f mm3' % (first.Name, second.Name, overlap))
|
|
(out / 'M9C12_MT6826S_几何检查.txt').write_text(
|
|
'Concept only. Assumed dimensions are not production measurements.\n'
|
|
'Sensor face to magnet gap: 1.0 mm.\n'
|
|
+ ('Positive-volume intersections:\n' + '\n'.join(warnings) if warnings else
|
|
'No positive-volume intersections above 0.001 mm3 in simplified model.') + '\n',
|
|
encoding='utf-8')
|
|
doc.recompute()
|
|
doc.saveAs(str(out / 'M9C12_MT6826S_安装示意.FCStd'))
|
|
Import.export([motor,holder,mag,board,chip,support,fastener,nut_obj]+fasteners,
|
|
str(out / 'M9C12_MT6826S_安装示意.step'))
|
|
print('ENCODER_CONCEPT_OK')
|
|
|