建立方向盘项目初始版本
This commit is contained in:
88
scripts/build_kfl001_plate_draft.py
Normal file
88
scripts/build_kfl001_plate_draft.py
Normal file
@@ -0,0 +1,88 @@
|
||||
"""Editable KFL001 bearing mounting plate -- provisional concept only.
|
||||
|
||||
Run with rebuild_kfl001_plate_draft.FCMacro in a hidden FreeCAD instance.
|
||||
Both assembly plates have the same geometry; flip the second when fitting.
|
||||
The centre cut is an unverified placeholder and MUST NOT be used for CNC.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import os, time, traceback, json
|
||||
import FreeCAD as A, FreeCADGui as G, Part, Import
|
||||
|
||||
R=Path(r'C:\projects\wheel')
|
||||
O=R/'models'/'kfl001_plate_draft'
|
||||
O.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
d=A.newDocument('KFL001_Plate_Draft')
|
||||
d.Label='KFL001 8mm plate DRAFT - centre cut unverified'
|
||||
s=d.addObject('Spreadsheet::Sheet','Parameters')
|
||||
s.Label='Parameters - centre diameter UNVERIFIED'
|
||||
rows=[('PlateWidth','76 mm','Overall width'),('PlateHeight','80 mm','Overall height'),
|
||||
('Thickness','8 mm','Plate thickness'),('AxisFromBottom','51 mm','Axle height above lower edge'),
|
||||
('CenterClearance','39 mm','TEMPORARY ONLY - verify real KFL back geometry'),
|
||||
('BearingPitch','48 mm','Bolt centre spacing for KFL001'),
|
||||
('BearingHole','7 mm','Bearing mount clearance diameter'),
|
||||
('SlotPitch','47 mm','Centre spacing of lower mounting slots'),
|
||||
('SlotBottom','9 mm','Mounting slot centre above plate lower edge'),
|
||||
('SlotLength','9.5 mm','Horizontal slot overall length'),
|
||||
('SlotWidth','5.5 mm','Horizontal slot width')]
|
||||
for row,(name,val,note) in enumerate(rows,2):
|
||||
s.set('A%d'%row,name);s.set('B%d'%row,val);s.set('C%d'%row,note);s.setAlias('B%d'%row,name)
|
||||
def expr(o,p,v):o.setExpression(p,v)
|
||||
plate=d.addObject('Part::Box','PlateBlank');plate.Label='76 x 80 x 8 plate blank'
|
||||
expr(plate,'Length','Parameters.PlateWidth');expr(plate,'Width','Parameters.PlateHeight');expr(plate,'Height','Parameters.Thickness')
|
||||
expr(plate,'Placement.Base.x','-Parameters.PlateWidth / 2')
|
||||
def cyl(name,x,y,r):
|
||||
o=d.addObject('Part::Cylinder',name);expr(o,'Radius',r);expr(o,'Height','Parameters.Thickness + 0.2 mm');expr(o,'Placement.Base.x',x);expr(o,'Placement.Base.y',y);o.Placement.Base.z=-.1;return o
|
||||
tools=[]
|
||||
tools.append(cyl('TEMP_CentreHole','0 mm','Parameters.AxisFromBottom','Parameters.CenterClearance / 2'))
|
||||
for side in (-1,1):
|
||||
sign='-' if side<0 else ''
|
||||
tools.append(cyl('BearingHole_'+('L' if side<0 else 'R'),sign+'Parameters.BearingPitch / 2','Parameters.AxisFromBottom','Parameters.BearingHole / 2'))
|
||||
cx=sign+'Parameters.SlotPitch / 2';tag='L' if side<0 else 'R'
|
||||
tools.append(cyl('Slot'+tag+'End1','(%s) - (Parameters.SlotLength - Parameters.SlotWidth) / 2'%cx,'Parameters.SlotBottom','Parameters.SlotWidth / 2'))
|
||||
tools.append(cyl('Slot'+tag+'End2','(%s) + (Parameters.SlotLength - Parameters.SlotWidth) / 2'%cx,'Parameters.SlotBottom','Parameters.SlotWidth / 2'))
|
||||
b=d.addObject('Part::Box','Slot'+tag+'Middle')
|
||||
expr(b,'Length','Parameters.SlotLength - Parameters.SlotWidth');expr(b,'Width','Parameters.SlotWidth');expr(b,'Height','Parameters.Thickness + 0.2 mm')
|
||||
expr(b,'Placement.Base.x','(%s) - (Parameters.SlotLength - Parameters.SlotWidth) / 2'%cx)
|
||||
expr(b,'Placement.Base.y','Parameters.SlotBottom - Parameters.SlotWidth / 2');b.Placement.Base.z=-.1
|
||||
tools.append(b)
|
||||
d.recompute()
|
||||
cutters=d.addObject('Part::MultiFuse','Cutters');cutters.Shapes=tools;d.recompute()
|
||||
final=d.addObject('Part::Cut','Plate_DRAFT');final.Label='DRAFT bearing plate - centre 39mm NOT verified';final.Base=plate;final.Tool=cutters
|
||||
d.recompute()
|
||||
assert not final.Shape.isNull() and final.Shape.isValid() and len(final.Shape.Solids)==1
|
||||
for o in d.Objects:
|
||||
if o.ViewObject:o.ViewObject.Visibility=False
|
||||
final.ViewObject.Visibility=True;final.ViewObject.ShapeColor=(.71,.74,.77);final.ViewObject.LineColor=(.12,.12,.12)
|
||||
G.activateWorkbench('PartWorkbench');view=G.activeDocument().activeView();view.viewAxonometric();G.updateGui();view.fitAll();time.sleep(.3);G.updateGui();view.saveImage(str(O/'plate_isometric.png'),1400,1100,'White')
|
||||
view.viewTop();G.updateGui();view.fitAll();time.sleep(.3);G.updateGui();view.saveImage(str(O/'plate_top.png'),1400,1100,'White')
|
||||
d.saveAs(str(O/'KFL001_plate_DRAFT.FCStd'));Import.export([final],str(O/'KFL001_plate_DRAFT.step'))
|
||||
baseline=final.Shape.Volume
|
||||
s.set('B6','38 mm');d.recompute()
|
||||
assert final.Shape.isValid() and abs(final.Shape.Volume-baseline)>0.1
|
||||
changed=final.Shape.Volume
|
||||
s.set('B6','39 mm');d.recompute()
|
||||
assert final.Shape.isValid() and abs(final.Shape.Volume-baseline)<1e-5
|
||||
A.closeDocument(d.Name)
|
||||
dd=A.openDocument(str(O/'KFL001_plate_DRAFT.FCStd'));dd.recompute();ff=dd.getObject('Plate_DRAFT')
|
||||
assert ff.Shape.isValid() and len(ff.Shape.Solids)==1 and abs(ff.Shape.Volume-baseline)<1e-5
|
||||
imported=Part.read(str(O/'KFL001_plate_DRAFT.step'))
|
||||
assert imported.isValid() and len(imported.Solids)==1
|
||||
# At the middle Z plane compare the STEP vs native shape by mutual differences.
|
||||
stepdiff1=ff.Shape.cut(imported).Volume;stepdiff2=imported.cut(ff.Shape).Volume
|
||||
assert stepdiff1<1e-4 and stepdiff2<1e-4
|
||||
angles=[(3.5,24,51),(3.5,-24,51),(19.5,0,51)]
|
||||
cylinders=[]
|
||||
for f in imported.Faces:
|
||||
surf=f.Surface
|
||||
if hasattr(surf,'Radius') and hasattr(surf,'Center'):
|
||||
cylinders.append({'radius':round(float(surf.Radius),5),'x':round(float(surf.Center.x),5),'y':round(float(surf.Center.y),5),'z_min':round(f.BoundBox.ZMin,5),'z_max':round(f.BoundBox.ZMax,5)})
|
||||
for r,x,y in angles:
|
||||
assert any(abs(c['radius']-r)<1e-4 and abs(c['x']-x)<1e-4 and abs(c['y']-y)<1e-4 and c['z_min']<=.001 and c['z_max']>=7.999 for c in cylinders), (r,x,y,cylinders)
|
||||
info={'fcstd_valid':True,'step_valid':True,'solid_count':len(imported.Solids),'volume':baseline,'diameter_parameter_test_38':changed,'diameter_test_restored':True,'step_old_minus_new':stepdiff1,'step_new_minus_old':stepdiff2,'dimensions_mm':{'width':76,'height':80,'thickness':8,'axis_from_bottom':51,'center_diameter_provisional':39,'bearing_pitch':48,'bearing_hole':7,'slot_pitch':47,'slot_bottom':9,'slot_length':9.5,'slot_width':5.5},'cylinder_faces':cylinders,'nominal_m5_slot_range_mm':4.5,'quantity_identical_plates':2,'center_UNVERIFIED':True}
|
||||
(O/'validation.json').write_text(json.dumps(info,ensure_ascii=False,indent=2),encoding='utf-8')
|
||||
A.closeDocument(dd.Name)
|
||||
(O/'DONE.txt').write_text('ok',encoding='utf-8')
|
||||
os._exit(0)
|
||||
except Exception:
|
||||
(O/'ERROR.txt').write_text(traceback.format_exc(),encoding='utf-8');os._exit(1)
|
||||
Reference in New Issue
Block a user