"""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')