建立方向盘项目初始版本
This commit is contained in:
134
scripts/build_mad_m9c12.py
Normal file
134
scripts/build_mad_m9c12.py
Normal file
@@ -0,0 +1,134 @@
|
||||
"""Build the approximate, editable MAD M9C12 model in FreeCAD.
|
||||
|
||||
Run via rebuild_mad_m9c12.FCMacro in the FreeCAD GUI. The model and STEP
|
||||
file are overwritten. See models/MAD_M9C12_说明.md for measurement limits.
|
||||
"""
|
||||
import math
|
||||
from pathlib import Path
|
||||
import FreeCAD as App
|
||||
import Part
|
||||
import Import
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
MODEL = ROOT / 'models' / 'MAD_M9C12.FCStd'
|
||||
STEP = ROOT / 'models' / 'MAD_M9C12.step'
|
||||
if not App.GuiUp:
|
||||
raise RuntimeError('Run through the GUI macro to save visibility and camera state')
|
||||
import FreeCADGui as Gui
|
||||
|
||||
doc = App.newDocument('MAD_M9C12')
|
||||
sheet = doc.addObject('Spreadsheet::Sheet', 'Parameters')
|
||||
sheet.Label = 'Parameters - mm and degrees (verify assumptions)'
|
||||
sheet.set('A1', 'Parameter'); sheet.set('B1', 'Value'); sheet.set('C1', 'Source / status')
|
||||
rows = [
|
||||
('BodyDiameter', '120 mm', 'User measurement: body outside diameter'),
|
||||
('BodyThickness', '30 mm', 'User measurement: body thickness; excludes boss and shaft'),
|
||||
('FrontPCD', '20 mm', 'User confirmed adapter inner alternating holes; verify with measurements'),
|
||||
('FrontHoleDiameter', '3 mm', 'Assumed M3 nominal diameter; verify'),
|
||||
('FrontHoleDepth', '5 mm', 'Placeholder blind depth; verify'),
|
||||
('FrontAngle', '0 deg', 'Assumed first hole phase; verify'),
|
||||
('RearInnerPCD', '50 mm', 'User ruler measurement; verify with calipers'),
|
||||
('RearOuterPCD', '76 mm', 'User ruler measurement; verify with calipers'),
|
||||
('RearHoleDiameter', '4 mm', 'M4 nominal diameter, no modeled thread'),
|
||||
('RearHoleDepth', '8 mm', 'Placeholder; M4x8 does not establish thread depth'),
|
||||
('RearInnerAngle', '45 deg', 'Assumed phase; verify'),
|
||||
('RearOuterAngle', '45 deg', 'Assumed relative phase; verify'),
|
||||
('BossDiameter', '26 mm', 'Visual approximation; front mounting holes enter its top face'),
|
||||
('BossHeight', '8 mm', 'Visual approximation, verify adapter clearance'),
|
||||
('ShaftDiameter', '8 mm', 'Visual approximation'),
|
||||
('ShaftHeight', '6 mm', 'Visual approximation, measured above boss'),
|
||||
('CenterHoleDiameter', '3 mm', 'Axial through hole; smooth nominal M3 diameter'),
|
||||
]
|
||||
for i, (name, value, meaning) in enumerate(rows, 2):
|
||||
sheet.set('A%d' % i, name); sheet.set('B%d' % i, value)
|
||||
sheet.set('C%d' % i, meaning); sheet.setAlias('B%d' % i, name)
|
||||
doc.recompute()
|
||||
|
||||
def cylinder(name, label, radius, height, z, x=None, y=None):
|
||||
obj = doc.addObject('Part::Cylinder', name)
|
||||
obj.Label = label
|
||||
obj.setExpression('Radius', radius)
|
||||
obj.setExpression('Height', height)
|
||||
obj.setExpression('Placement.Base.z', z)
|
||||
if x is not None:
|
||||
obj.setExpression('Placement.Base.x', x)
|
||||
obj.setExpression('Placement.Base.y', y)
|
||||
return obj
|
||||
|
||||
def pattern(name, pcd, angle, diameter, depth, z):
|
||||
holes = []
|
||||
for i in range(4):
|
||||
phase = 'Parameters.%s + %d deg' % (angle, i * 90)
|
||||
x = 'Parameters.%s / 2 * cos(%s)' % (pcd, phase)
|
||||
y = 'Parameters.%s / 2 * sin(%s)' % (pcd, phase)
|
||||
holes.append(cylinder(name + str(i + 1), name + ' hole ' + str(i + 1),
|
||||
'Parameters.%s / 2' % diameter,
|
||||
'Parameters.%s' % depth, z, x, y))
|
||||
group = doc.addObject('Part::Compound', name + 'Tools')
|
||||
group.Label = name + ' (four hole tools)'
|
||||
group.Links = holes
|
||||
return group
|
||||
|
||||
def checked(obj):
|
||||
doc.recompute()
|
||||
if obj.Shape.isNull() or not obj.Shape.isValid() or len(obj.Shape.Solids) != 1:
|
||||
raise RuntimeError('Invalid geometry: ' + obj.Name)
|
||||
|
||||
body = cylinder('MotorBody', 'Motor housing - approximate',
|
||||
'Parameters.BodyDiameter / 2', 'Parameters.BodyThickness', '0 mm')
|
||||
boss = cylinder('Boss', 'Front boss - approximate',
|
||||
'Parameters.BossDiameter / 2', 'Parameters.BossHeight', 'Parameters.BodyThickness')
|
||||
shaft = cylinder('Shaft', 'Shaft - approximate',
|
||||
'Parameters.ShaftDiameter / 2', 'Parameters.ShaftHeight',
|
||||
'Parameters.BodyThickness + Parameters.BossHeight')
|
||||
housing = doc.addObject('Part::MultiFuse', 'Housing')
|
||||
housing.Label = 'Housing, boss and shaft'
|
||||
housing.Shapes = [body, boss, shaft]
|
||||
checked(housing)
|
||||
|
||||
front = pattern('Front', 'FrontPCD', 'FrontAngle', 'FrontHoleDiameter', 'FrontHoleDepth',
|
||||
'Parameters.BodyThickness + Parameters.BossHeight - Parameters.FrontHoleDepth')
|
||||
cut_front = doc.addObject('Part::Cut', 'FrontHoles')
|
||||
cut_front.Label = 'Front 4 holes - phase and depth to verify'
|
||||
cut_front.Base = housing; cut_front.Tool = front
|
||||
checked(cut_front)
|
||||
rear_inner = pattern('RearInner', 'RearInnerPCD', 'RearInnerAngle', 'RearHoleDiameter', 'RearHoleDepth', '0 mm')
|
||||
cut_inner = doc.addObject('Part::Cut', 'RearInnerHoles')
|
||||
cut_inner.Label = 'Rear inner 4 holes - measured approximately'
|
||||
cut_inner.Base = cut_front; cut_inner.Tool = rear_inner
|
||||
checked(cut_inner)
|
||||
rear_outer = pattern('RearOuter', 'RearOuterPCD', 'RearOuterAngle', 'RearHoleDiameter', 'RearHoleDepth', '0 mm')
|
||||
final = doc.addObject('Part::Cut', 'Motor')
|
||||
final.Label = 'MAD M9C12 - approximate motor'
|
||||
final.Base = cut_inner; final.Tool = rear_outer
|
||||
checked(final)
|
||||
|
||||
center_bore = cylinder('CenterBore', 'Center bore tool',
|
||||
'Parameters.CenterHoleDiameter / 2',
|
||||
'Parameters.BodyThickness + Parameters.BossHeight + Parameters.ShaftHeight',
|
||||
'0 mm')
|
||||
center_cut = doc.addObject('Part::Cut', 'CenterThrough')
|
||||
center_cut.Label = 'MAD M9C12 - center through hole'
|
||||
center_cut.Base, center_cut.Tool = final, center_bore
|
||||
checked(center_cut)
|
||||
final = center_cut
|
||||
|
||||
for obj in doc.Objects:
|
||||
if obj.ViewObject:
|
||||
obj.ViewObject.Visibility = False
|
||||
final.ViewObject.Visibility = True
|
||||
final.ViewObject.ShapeColor = (0.47, 0.50, 0.53)
|
||||
final.ViewObject.Deviation = 0.05
|
||||
final.ViewObject.AngularDeflection = 10
|
||||
from pivy import coin
|
||||
view = Gui.activeDocument().activeView()
|
||||
# Set the camera directly so a hidden GUI also saves the intended view.
|
||||
view.getCameraNode().orientation.setValue(coin.SbRotation(0.364705, 0.279848, 0.115917, 0.880476))
|
||||
view.fitAll()
|
||||
doc.recompute()
|
||||
Gui.updateGui()
|
||||
view.saveImage(str(ROOT / 'models' / 'MAD_M9C12_预览.png'), 1400, 1000, 'White')
|
||||
doc.saveAs(str(MODEL))
|
||||
Import.export([final], str(STEP))
|
||||
print('MODEL_OK', MODEL, 'STEP', STEP, 'volume', final.Shape.Volume,
|
||||
'bounds', final.Shape.BoundBox)
|
||||
Reference in New Issue
Block a user