建立方向盘项目初始版本
This commit is contained in:
147
scripts/build_encoder_concept.py
Normal file
147
scripts/build_encoder_concept.py
Normal file
@@ -0,0 +1,147 @@
|
||||
"""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')
|
||||
|
||||
Reference in New Issue
Block a user