建立方向盘项目初始版本

This commit is contained in:
2026-10-29 22:04:55 +08:00
commit 9962cbfbce
834 changed files with 1519203 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
from pathlib import Path
import os, math, json, time, traceback
import FreeCAD as A, FreeCADGui as G, Part, Import
V=A.Vector
root=Path(r'C:\projects\wheel');out=root/'models'/'fan_direct_motor_side_130';out.mkdir(parents=True,exist_ok=True)
try:
d=A.openDocument(str(root/'models'/'integer_profiles'/'Assembly.FCStd'));d.recompute()
rails=[d.getObject('Rail0'),d.getObject('Rail1')]; corners=[d.getObject('MotorBaseCorner0'),d.getObject('MotorBaseCorner1')]
xs=[(o.Shape.BoundBox.XMin+o.Shape.BoundBox.XMax)/2 for o in rails];spacing=xs[1]-xs[0]
theta=math.degrees(math.acos(spacing/(105*math.sqrt(2))))-45
oldrails=[o.Shape.copy() for o in rails]
for i,rail in enumerate(rails):
rail.Placement=A.Placement();rail.Shape=Part.makeBox(20,130,20,V(xs[i]-10,-55,0));rail.Label='2020 motor support 130mm'
for i,obj in enumerate(corners):
orig=obj.Shape.copy();orig.rotate(V(xs[i],0,10),V(0,1,0),180);obj.Placement=A.Placement();obj.Shape=orig;obj.Label='Motor side 2020-3030 connecting corner'
d.recompute()
visible=[o for o in d.Objects if hasattr(o,'Shape') and not o.Shape.isNull() and o.ViewObject.Visibility]
pairs=[];hits=[];skip={'Rail0','Rail1','MotorBaseCorner0','MotorBaseCorner1'}
for i,c in enumerate(corners):
for o in visible:
if o==c:continue
if c.Shape.BoundBox.intersect(o.Shape.BoundBox):
vol=c.Shape.common(o.Shape).Volume
if vol>1e-4:hits.append([c.Name,o.Name,vol])
if o.Name in ('Motor','Rail%d'%i,'NewRail%d'%i,'Corner%d'%(i*2),'Corner%d'%(i*2+1)):
pairs.append([c.Name,o.Name,c.Shape.distToShape(o.Shape)[0]])
assert c.Shape.distToShape(rails[i].Shape)[0]<1e-5
assert c.Shape.distToShape(d.getObject('NewRail%d'%i).Shape)[0]<1e-5
for i,rail in enumerate(rails):
extra=rail.Shape.cut(oldrails[i])
for o in visible:
if o in rails or o in corners:continue
if extra.BoundBox.intersect(o.Shape.BoundBox):
vol=extra.common(o.Shape).Volume
if vol>1e-4:hits.append(['extra_'+rail.Name,o.Name,vol])
fan=Part.makeBox(120,120,25,V(-60,-60,20)).cut(Part.makeCylinder(56,25.04,V(0,0,19.98)))
for x in (-52.5,52.5):
for y in (-52.5,52.5):fan=fan.cut(Part.makeCylinder(2.2,25.04,V(x,y,19.98)))
fan=fan.removeSplitter();fan.rotate(V(0,0,0),V(0,0,1),theta);fan.translate(V(0,10,0))
for o in visible:
if fan.BoundBox.intersect(o.Shape.BoundBox):
vol=fan.common(o.Shape).Volume
if vol>1e-4:hits.append(['DirectFan',o.Name,vol])
if hits: raise Exception('COLLISION '+str(hits))
ff=d.addObject('PartDesign::Feature','DirectFan');ff.Shape=fan;ff.Label='120mm fan assumed geometry, rotated';ff.ViewObject.ShapeColor=(.22,.24,.27);ff.ViewObject.Transparency=18
holes=[];alpha=math.radians(theta);bolts=[]
for sign in (-1,1):
px=sign*52.5*(math.cos(alpha)-math.sin(alpha));py=10+sign*52.5*(math.cos(alpha)+math.sin(alpha));idx=0 if sign<0 else 1
assert abs(px-xs[idx])<1e-6 and -55<py<75
screw=Part.makeCylinder(2,25,V(px,py,20)).fuse(Part.makeCylinder(3.5,3,V(px,py,45))).removeSplitter()
assert screw.common(fan).Volume<1e-5
for o in visible:
if screw.BoundBox.intersect(o.Shape.BoundBox):
vol=screw.common(o.Shape).Volume
if vol>1e-4:hits.append(['bolt',idx,o.Name,vol])
b=d.addObject('PartDesign::Feature','FanBolt'+str(idx));b.Shape=screw;b.Label='M4 fastener illustrative, T-nut not modelled';b.ViewObject.ShapeColor=(.65,.66,.68);bolts.append(b)
holes.append({'x':px,'y':py,'distance_to_rail_end':min(py+55,75-py)})
if hits:raise Exception('SCREW COLLISION '+str(hits))
d.recompute();d.Label='Motor side corners, direct fan, 130mm profiles'
d.saveAs(str(out/'Assembly.FCStd'));obj=[o for o in d.Objects if hasattr(o,'Shape') and not o.Shape.isNull() and o.ViewObject.Visibility];Import.export(obj,str(out/'Assembly.step'))
view=G.activeDocument().activeView();view.viewAxonometric();G.updateGui();view.fitAll();time.sleep(.5);G.updateGui();view.saveImage(str(out/'assembly.png'),1800,1300,'White')
vis={o.Name:o.ViewObject.Visibility for o in d.Objects if hasattr(o,'Shape')};show={'Rail0','Rail1','NewRail0','NewRail1','MotorBaseCorner0','MotorBaseCorner1','DirectFan','FanBolt0','FanBolt1','Motor','OpenPillarBracket','Holder','Magnet','PCB','Chip','Corner0','Corner1','Corner2','Corner3'}
for o in d.Objects:
if hasattr(o,'Shape'):o.ViewObject.Visibility=o.Name in show
view.viewTop();G.updateGui();view.fitAll();time.sleep(.5);G.updateGui();view.saveImage(str(out/'fan_top.png'),1800,1300,'White')
view.viewAxonometric();G.updateGui();view.fitAll();time.sleep(.5);G.updateGui();view.saveImage(str(out/'fan_detail.png'),1800,1300,'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 all(d.getObject(n).Shape.isValid() for n in ['Rail0','Rail1','DirectFan','MotorBaseCorner0','MotorBaseCorner1']);assert all(abs(d.getObject('Rail%d'%i).Shape.BoundBox.YLength-130)<1e-6 for i in (0,1));step=Part.read(str(out/'Assembly.step'));assert step.isValid() and len(step.Solids)>0
data={'rail_length':130,'rail_limits':[-55,75],'fan_center_y':10,'motor_center_y':10,'fan_rotation_degrees':theta,'holes':holes,'corner_contact_distance':pairs,'fan_screw_extension_collisions':hits,'note':'Simplified fan, motor geometry; T nut and T slot, actual screw insertion trajectory, tooling, wires and fan hub unmodelled.'}
(out/'validation.json').write_text(json.dumps(data,ensure_ascii=False,indent=2),encoding='utf-8');A.closeDocument(d.Name);(out/'DONE.txt').write_text('ok');os._exit(0)
except Exception:
(out/'ERROR.txt').write_text(traceback.format_exc(),encoding='utf-8');os._exit(1)