建立方向盘项目初始版本
This commit is contained in:
91
scripts/update_assembly_latest_standoff.FCMacro
Normal file
91
scripts/update_assembly_latest_standoff.FCMacro
Normal file
@@ -0,0 +1,91 @@
|
||||
from pathlib import Path
|
||||
import FreeCAD as A, FreeCADGui as G, Part, Import
|
||||
import math, json, os, time, traceback
|
||||
V=A.Vector
|
||||
root=Path(r'C:\projects\wheel')
|
||||
out=root/'models'/'assembly_latest_standoff'
|
||||
out.mkdir(parents=True,exist_ok=True)
|
||||
try:
|
||||
d=A.openDocument(str(root/'models'/'symmetric_corner_18_concept'/'Assembly_对称角码装配_概念.FCStd'))
|
||||
d.recompute()
|
||||
for n in ['EncoderPlateConcept']+['EncoderHex%d'%i for i in range(4)]+['EncoderSpacer%d'%i for i in range(4)]:
|
||||
o=d.getObject(n)
|
||||
if o: o.ViewObject.Visibility=False
|
||||
pdoc=A.openDocument(str(root/'models'/'encoder_standoff_plate_print'/'EncoderPlate.FCStd'))
|
||||
plate=pdoc.getObject('EncoderPlate').Shape.copy()
|
||||
plate.translate(V(0,10,11))
|
||||
po=d.addObject('PartDesign::Feature','FinalEncoderPlate')
|
||||
po.Shape=plate;po.Label='Encoder printable plate, 10mm standoffs + 1mm shim';po.ViewObject.ShapeColor=(.08,.30,.85)
|
||||
A.closeDocument(pdoc.Name)
|
||||
# Move sensor PCB and chip by the same 0.1 mm as the real plate.
|
||||
for n in ['PCB','Chip']+[o.Name for o in d.Objects if o.Name.startswith('PCBHead')]:
|
||||
o=d.getObject(n)
|
||||
if o:
|
||||
sh=o.Shape.copy();sh.translate(V(0,0,.1));o.Placement=A.Placement();o.Shape=sh
|
||||
def obj(name,shape,label,color):
|
||||
o=d.addObject('PartDesign::Feature',name);o.Shape=shape.removeSplitter();o.Label=label;o.ViewObject.ShapeColor=color;o.ViewObject.LineColor=(.12,.12,.12);return o
|
||||
fast=[]
|
||||
for i in range(4):
|
||||
a=math.radians(45+90*i);x=25*math.cos(a);y=10+25*math.sin(a)
|
||||
rr=7/(2*math.cos(math.pi/6))
|
||||
p=[V(x+rr*math.cos(math.radians(30+60*k)),y+rr*math.sin(math.radians(30+60*k)),0) for k in range(6)];p.append(p[0])
|
||||
body=Part.Face(Part.makePolygon(p)).extrude(V(0,0,10))
|
||||
body=body.cut(Part.makeCylinder(1.6,4,V(x,y,6)))
|
||||
# Placeholder stud length 5 mm, nominal diameter: actual hardware TBD.
|
||||
body=body.fuse(Part.makeCylinder(2,5,V(x,y,-5)))
|
||||
st=obj('BrassStandoff%d'%i,body,'M4 brass stand-off body 10 mm AF7, male 5 mm EXAMPLE',(.83,.65,.23));fast.append(st)
|
||||
sh=Part.makeCylinder(4.5,1,V(x,y,10)).cut(Part.makeCylinder(2.2,1,V(x,y,10)))
|
||||
wash=obj('StandoffShim%d'%i,sh,'M4 adjustment washer 1 mm assumed',(.75,.75,.76));fast.append(wash)
|
||||
screw=Part.makeCylinder(1.95,6.2,V(x,y,8)).fuse(Part.makeCylinder(3.5,2,V(x,y,14.2)))
|
||||
sc=obj('EncoderScrew%d'%i,screw,'M4 fixing screw placeholder; length not specified',(.65,.65,.68));fast.append(sc)
|
||||
d.recompute()
|
||||
objlist=[o for o in d.Objects if hasattr(o,'Shape') and not o.Shape.isNull() and o.ViewObject.Visibility]
|
||||
# Intersections for panel, standoff, washers and heads against unrelated visible objects.
|
||||
ignore={'Motor','PCB','Chip','Holder','Magnet'} # checked separately where appropriate; PCB/plate intentionally meet
|
||||
potential=[]
|
||||
for a in [po]+fast:
|
||||
for b in objlist:
|
||||
if a==b or b.Name in ignore or b in fast or b==po:continue
|
||||
if not a.Shape.BoundBox.intersect(b.Shape.BoundBox):continue
|
||||
v=a.Shape.common(b.Shape).Volume
|
||||
if v>0.001:potential.append([a.Name,b.Name,round(v,5)])
|
||||
# PCB and chip dimensions and actual modeled gap, if geometry is available.
|
||||
chip=d.getObject('Chip'); magnet=d.getObject('Magnet')
|
||||
gap=chip.Shape.distToShape(magnet.Shape)[0] if chip and magnet else None
|
||||
selected_names=[o.Name for o in objlist]
|
||||
counts={k:sum(n.startswith(k) for n in selected_names) for k in ('NewMotorCorner','NewMotorBase','NewSwitchBase','NewBeamBase','PurchasedAngle','SwitchAngle','BrassStandoff','StandoffShim')}
|
||||
assert counts['NewMotorCorner']==4 and counts['NewMotorBase']==2 and counts['NewSwitchBase']==2 and counts['NewBeamBase']==4
|
||||
assert counts['PurchasedAngle']==4 and counts['SwitchAngle']==2 and counts['BrassStandoff']==4 and counts['StandoffShim']==4
|
||||
corners=[d.getObject('NewMotorCorner%d%d'%(i,j)).Shape for i in range(2) for j in range(2)]
|
||||
post_corner_clearance=[min(d.getObject('BrassStandoff%d'%i).Shape.distToShape(c)[0] for c in corners) for i in range(4)]
|
||||
sensor_contact={}
|
||||
for name in ('PCB','Chip','Magnet'):
|
||||
o=d.getObject(name)
|
||||
if o: sensor_contact[name]={'distance_mm':po.Shape.distToShape(o.Shape)[0],'volume_mm3':po.Shape.common(o.Shape).Volume}
|
||||
d.Label='最新装配 - M4 铜柱编码器'
|
||||
d.recompute();d.saveAs(str(out/'Assembly.FCStd'))
|
||||
Import.export(objlist,str(out/'Assembly.step'))
|
||||
A.setActiveDocument(d.Name);view=G.activeDocument().activeView();G.activateWorkbench('PartWorkbench')
|
||||
view.viewAxonometric();view.fitAll();G.updateGui();time.sleep(.5);view.saveImage(str(out/'装配全景.png'),1800,1300,'White')
|
||||
# Back-side encoder detail excluding parts obstructing the view; restore visibility.
|
||||
vis={o.Name:o.ViewObject.Visibility for o in d.Objects if hasattr(o,'Shape')}
|
||||
show={'Motor','Holder','Magnet','PCB','Chip','FinalEncoderPlate','Rail0','Rail1'}
|
||||
show.update('BrassStandoff%d'%i for i in range(4));show.update('StandoffShim%d'%i for i in range(4));show.update('EncoderScrew%d'%i for i in range(4))
|
||||
show.update('NewMotorCorner%d%d'%(i,j) for i in range(2) for j in range(2));show.update('RailWasher%d%d'%(i,j) for i in range(2) for j in range(2))
|
||||
for o in d.Objects:
|
||||
if hasattr(o,'Shape'):o.ViewObject.Visibility=o.Name in show
|
||||
view.viewTop();view.fitAll();G.updateGui();time.sleep(.5);view.saveImage(str(out/'编码器背面.png'),1600,1200,'White')
|
||||
for o in d.Objects:
|
||||
if hasattr(o,'Shape'):o.ViewObject.Visibility=vis.get(o.Name,False)
|
||||
d.recompute();d.saveAs(str(out/'Assembly.FCStd'))
|
||||
A.closeDocument(d.Name)
|
||||
d=A.openDocument(str(out/'Assembly.FCStd'));d.recompute()
|
||||
assert d.getObject('FinalEncoderPlate').Shape.isValid()
|
||||
A.closeDocument(d.Name)
|
||||
step=Part.read(str(out/'Assembly.step'));assert step.isValid()
|
||||
report={'counts':counts,'new_component_intersections_mm3':potential,'stand_off_to_motor_corner_clearance_mm':post_corner_clearance,'panel_to_sensor_relations':sensor_contact,'magnet_to_chip_shape_distance_mm':gap,'panel_bottom_from_motor_mm':11,'stand_off_body_mm':10,'shim_mm':1,'stud_length_mm':'5 schematic only','visible_names':selected_names,'limitations':'No real slot, fastener threads, countersink, switch wiring or real sensor dimensions; not physical fit or strength verification.'}
|
||||
(out/'验证.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf8')
|
||||
(out/'完成.txt').write_text('ok',encoding='utf8')
|
||||
except Exception:
|
||||
(out/'错误.txt').write_text(traceback.format_exc(),encoding='utf8')
|
||||
os._exit(0)
|
||||
Reference in New Issue
Block a user