建立方向盘项目初始版本
This commit is contained in:
106
scripts/build_kfl_double_crossbar_concept.py
Normal file
106
scripts/build_kfl_double_crossbar_concept.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from pathlib import Path
|
||||
import os, time, traceback
|
||||
import FreeCAD as A, FreeCADGui as G, Part, Import
|
||||
from pivy import coin
|
||||
V=A.Vector
|
||||
R=Path(r'C:\projects\wheel'); O=R/'models'/'kfl_double_crossbar_concept'; O.mkdir(parents=True,exist_ok=True)
|
||||
def box(x,y,z,dx,dy,dz): return Part.makeBox(dx,dy,dz,V(x,y,z))
|
||||
def cyl(r,h,x,y,z,d=V(0,0,1)): return Part.makeCylinder(r,h,V(x,y,z),d)
|
||||
def slotx(x,y,z,r,h): return box(x-2,y-r,z,4,2*r,h).fuse(cyl(r,h,x-2,y,z)).fuse(cyl(r,h,x+2,y,z))
|
||||
def sloty(x,y,z,r,h): return box(x-r,y-2,z,2*r,4,h).fuse(cyl(r,h,x,y-2,z)).fuse(cyl(r,h,x,y+2,z))
|
||||
def add(d,n,s,col):
|
||||
o=d.addObject('PartDesign::Feature',n);o.Shape=s.removeSplitter();o.Label=n;o.ViewObject.ShapeColor=col;o.ViewObject.LineColor=(.15,.15,.15);return o
|
||||
def cv(a,b): return a.common(b).Volume if a.BoundBox.intersect(b.BoundBox) else 0.
|
||||
ok=False
|
||||
try:
|
||||
d=A.openDocument(str(R/'models'/'kfl_plate_b38_concept'/'Assembly.FCStd'));d.Label='Two crossbeams, KFL001 reversed - concept'
|
||||
for i in range(2):
|
||||
for j in range(2):
|
||||
for prefix in ('RealCorner','BoardFast','RailFast','Tnut'):
|
||||
n='%s%d%d'%(prefix,i,j)
|
||||
if d.getObject(n):d.removeObject(n)
|
||||
if d.getObject('AdjustPlate%d'%i):d.removeObject('AdjustPlate%d'%i)
|
||||
rail=d.getObject('NewRail%d'%i)
|
||||
x=(-50.6200577,50.6200577)[i]
|
||||
rail.Shape=box(x-10,-75,-215,20,20,235)
|
||||
rail.Label='Longitudinal 2020, schematic solid'
|
||||
d.recompute()
|
||||
plates=[]; angles=[]; bolts=[]; cross=[]
|
||||
for i,z in enumerate((-84.,-187.)):
|
||||
p=box(-38,-51,z,76,80,8).cut(cyl(19.5,8.04,0,0,z-.02))
|
||||
for x in (-24.,24.):p=p.cut(cyl(3.5,8.04,x,0,z-.02))
|
||||
for x in (-27.5,27.5):p=p.cut(slotx(x,-42,z-.02,2.75,8.04))
|
||||
plates.append(add(d,'CompactPlate%d'%i,p,(.72,.74,.78)))
|
||||
face=z+8 if i==0 else z
|
||||
z0=face if i==0 else face-20
|
||||
cz=z0+10
|
||||
cross.append(add(d,'Crossbeam%d'%i,box(-80,-95,z0,160,20,20),(.46,.49,.53)))
|
||||
for j,side in enumerate((-1,1)):
|
||||
x=side*27.5
|
||||
verticalz=face if i==0 else face-3
|
||||
upright=box(x-9,-75,verticalz,18,45,3).cut(sloty(x,-42,verticalz-.02,2.75,3.04))
|
||||
foot=box(x-9,-75,z0,18,3,20).cut(cyl(2.75,3.04,x,-75.02,cz,V(0,1,0)))
|
||||
angles.append(add(d,'LongAngle%d%d'%(i,j),upright.fuse(foot),(.95,.5,.12)))
|
||||
if i==0:
|
||||
stem=cyl(2.45,18,x,-42,z-4);head=cyl(4.25,3,x,-42,face+4);nut=cyl(4.25,3,x,-42,z-3);wz=face+3
|
||||
else:
|
||||
stem=cyl(2.45,18,x,-42,z-5);head=cyl(4.25,3,x,-42,face-7);nut=cyl(4.25,3,x,-42,z+8);wz=face-4
|
||||
washer=cyl(5,1,x,-42,wz).cut(cyl(2.75,1.02,x,-42,wz-.01))
|
||||
bolts.append(add(d,'BoardM5_%d%d'%(i,j),stem.fuse(head).fuse(nut).fuse(washer),(.14,.15,.17)))
|
||||
stem=cyl(2.45,10,x,-82,cz,V(0,1,0));head=cyl(4.25,3,x,-72,cz,V(0,1,0))
|
||||
bolts.append(add(d,'FootM5_%d%d'%(i,j),stem.fuse(head),(.14,.15,.17)))
|
||||
x0=-80 if side==-1 else 60.6200577
|
||||
vertx=-63.6200577 if side==-1 else 60.6200577
|
||||
hx=-70 if side==-1 else 70
|
||||
horiz=box(x0,-75,cz-9,19.3799423,3,18).cut(cyl(2.75,3.04,hx,-75.02,cz,V(0,1,0)))
|
||||
vert=box(vertx,-75,cz-9,3,20,18).cut(cyl(2.75,3.04,vertx-.02,-65,cz,V(1,0,0)))
|
||||
angles.append(add(d,'CrossAngle%d%d'%(i,j),horiz.fuse(vert),(.86,.48,.16)))
|
||||
bolts.append(add(d,'CrossTopM5_%d%d'%(i,j),cyl(2.45,10,hx,-82,cz,V(0,1,0)).fuse(cyl(4.25,3,hx,-72,cz,V(0,1,0))),(.14,.15,.17)))
|
||||
if side==-1: sh=cyl(2.45,10,-68,-65,cz,V(1,0,0)).fuse(cyl(4.25,3,-63.6200577,-65,cz,V(-1,0,0)))
|
||||
else: sh=cyl(2.45,10,58,-65,cz,V(1,0,0)).fuse(cyl(4.25,3,63.6200577,-65,cz,V(1,0,0)))
|
||||
bolts.append(add(d,'CrossSideM5_%d%d'%(i,j),sh,(.14,.15,.17)))
|
||||
d.recompute()
|
||||
tests=[]
|
||||
def check(a,b):
|
||||
ao=d.getObject(a);bo=d.getObject(b)
|
||||
if ao and bo:tests.append((a,b,cv(ao.Shape,bo.Shape)))
|
||||
for i in range(2):
|
||||
for n in ('KFLHousing%d'%i,'KFLInner%d'%i,'Motor','MotorFlange','WheelHub','Wheel70','SwitchEnvelope','NewRail0','NewRail1','Crossbeam%d'%i):check('CompactPlate%d'%i,n)
|
||||
for n in ('NewRail0','NewRail1','SwitchEnvelope','Motor','CamShaft','CamUpper','CamLower'):check('Crossbeam%d'%i,n)
|
||||
for j in range(2):
|
||||
long='LongAngle%d%d'%(i,j);crossa='CrossAngle%d%d'%(i,j)
|
||||
for n in ('CompactPlate%d'%i,'KFLHousing%d'%i,'KFLInner%d'%i,'Motor','MotorFlange','WheelHub','Wheel70','SwitchEnvelope','Crossbeam%d'%i,'NewRail0','NewRail1','BoardM5_%d%d'%(i,j),'FootM5_%d%d'%(i,j),crossa):check(long,n)
|
||||
for n in ('Crossbeam%d'%i,'NewRail%d'%j,'SwitchEnvelope','Motor','LongAngle%d%d'%(i,j),'CrossTopM5_%d%d'%(i,j),'CrossSideM5_%d%d'%(i,j)):check(crossa,n)
|
||||
check('BoardM5_%d%d'%(i,j),'CompactPlate%d'%i)
|
||||
check('FootM5_%d%d'%(i,j),'CrossTopM5_%d%d'%(i,j))
|
||||
lines=['CONCEPT ONLY: schematic profile envelopes and simplified hardware; not CNC/manufacturing drawings.',
|
||||
'Two plate sizes 76x80x8; 39 bore assumed, KFL bolt centres 48 and clearance holes 7. Shaft diameter 12 x 150; switch 60 mm envelope.',
|
||||
'Crossbars x -80..80, y -95..-75, z -76..-56 / -207..-187, each beneath longitudinal profiles y -75..-55.',
|
||||
'Four assumed bent metal angles: outer legs 45 x 20, width 18, thickness 3, actual bend radius unspecified; foot round hole diameter 5.5, vertical 9.5 x 5.5 slot Y; plate slot 9.5 x 5.5 X at y=-42.',
|
||||
'Proposed nominal M5 adjustment about +/-2mm each axis; requires testing of all actual washer, shaft, bearing and interference conditions. Each plate washer OD10 at slot lower travel remains within plate edge by nominal 2mm.',
|
||||
'T slots shown as SOLID 2020 ENVELOPES: fastener shanks intentionally enter solid beam; do not treat as interference free, actual T nut and bolt depth must be checked.',
|
||||
'Four standard corner angles join crossbars to long profiles, two per crossbar. Screw access, T nuts, actual slot geometry, bending radii, switch casing and strength unverified.',
|
||||
'Selected static common volume tests mm3; zero volume checks apply ONLY to modeled shapes and those specific pairs:']+[f'{a}/{b}={v:.5f}' for a,b,v in tests]
|
||||
(O/'geometry_check.txt').write_text('\n'.join(lines)+'\n',encoding='utf8')
|
||||
(O/'positive_intersections.txt').write_text('\n'.join(f'{a}/{b}: {v:.5f}' for a,b,v in tests if v>.001) or 'none',encoding='utf8')
|
||||
d.saveAs(str(O/'Assembly.FCStd'));Import.export([o for o in d.Objects if hasattr(o,'Shape')],str(O/'Assembly.step'))
|
||||
view=G.activeDocument().activeView();G.activateWorkbench('PartWorkbench')
|
||||
def save(name,mode):
|
||||
if mode=='side':view.viewFront()
|
||||
elif mode=='front':view.viewBottom()
|
||||
else:view.viewAxonometric();G.updateGui();view.getCameraNode().orientation.setValue(coin.SbRotation(.880476,.115917,-.279848,-.364705))
|
||||
G.updateGui();time.sleep(.3);view.fitAll();G.updateGui();time.sleep(.4);view.saveImage(str(O/name),1800,1250,'White')
|
||||
save('assembly.png','axon');save('side.png','side')
|
||||
visible={o.Name:o.ViewObject.Visibility for o in d.Objects if hasattr(o,'Shape')}
|
||||
show=set([o.Name for o in plates+angles+bolts+cross]+['NewRail0','NewRail1','KFLHousing0','KFLHousing1','KFLInner0','KFLInner1','CamShaft'])
|
||||
for o in d.Objects:
|
||||
if hasattr(o,'Shape'):o.ViewObject.Visibility=o.Name in show
|
||||
save('frame.png','axon');save('plate.png','front')
|
||||
for o in d.Objects:
|
||||
if hasattr(o,'Shape'):o.ViewObject.Visibility=visible.get(o.Name,True)
|
||||
d.recompute();d.saveAs(str(O/'Assembly.FCStd'));A.closeDocument(d.Name)
|
||||
(O/'DONE.txt').write_text('ok',encoding='utf8');ok=True
|
||||
except Exception:
|
||||
(O/'ERROR.txt').write_text(traceback.format_exc(),encoding='utf8')
|
||||
finally:
|
||||
os._exit(0 if ok else 1)
|
||||
Reference in New Issue
Block a user