建立方向盘项目初始版本
This commit is contained in:
155
scripts/build_kfl_plate_adjust_concept.py
Normal file
155
scripts/build_kfl_plate_adjust_concept.py
Normal file
@@ -0,0 +1,155 @@
|
||||
from pathlib import Path
|
||||
import math, time, traceback
|
||||
import FreeCAD as A, FreeCADGui as G, Part, Import
|
||||
from pivy import coin
|
||||
|
||||
R=Path(r'C:\projects\wheel'); O=R/'models'/'kfl_plate_adjust_concept'; O.mkdir(parents=True,exist_ok=True)
|
||||
V=A.Vector
|
||||
def cylinder(r,h,p,d=V(0,0,1)): return Part.makeCylinder(r,h,p,d)
|
||||
def obj(doc,name,label,shape,color,trans=0):
|
||||
o=doc.addObject('PartDesign::Feature',name);o.Label=label;o.Shape=shape;o.ViewObject.ShapeColor=color;o.ViewObject.LineColor=(.12,.12,.12);o.ViewObject.Transparency=trans;return o
|
||||
def slot_z(x,y1,y2,r,z,h):
|
||||
return Part.makeBox(2*r,y2-y1,h,V(x-r,y1,z)).fuse(cylinder(r,h,V(x,y1,z))).fuse(cylinder(r,h,V(x,y2,z)))
|
||||
def slot_y(x1,x2,z,r,y,h):
|
||||
return Part.makeBox(x2-x1,h,2*r,V(x1,y,z-r)).fuse(cylinder(r,h,V(x1,y,z),V(0,1,0))).fuse(cylinder(r,h,V(x2,y,z),V(0,1,0)))
|
||||
def slot_xz(x1,x2,y,r,z,h):
|
||||
return Part.makeBox(x2-x1,2*r,h,V(x1,y-r,z)).fuse(cylinder(r,h,V(x1,y,z))).fuse(cylinder(r,h,V(x2,y,z)))
|
||||
def hex_prism(radius,length,p,d): return Part.makePolygon([]) if False else Part.makeCylinder(radius,length,p,d,360)
|
||||
def common(a,b):
|
||||
if not a.BoundBox.intersect(b.BoundBox): return 0.0
|
||||
return a.common(b).Volume
|
||||
try:
|
||||
doc=A.openDocument(str(R/'models'/'kfl_150_switch60_concept'/'Assembly.FCStd'))
|
||||
# Prior rails are solid envelopes; retain their positions but extend forward 10mm.
|
||||
for name in ('Plate0','Plate1','L_0_0','L_0_1','L_1_0','L_1_1'):
|
||||
if doc.getObject(name): doc.removeObject(name)
|
||||
doc.recompute()
|
||||
# Give foreground rails their actual representation as outline envelopes only.
|
||||
for i,x in enumerate((-50.6200577,50.6200577)):
|
||||
rail=doc.getObject('NewRail%d'%i);rail.Shape=Part.makeBox(20,20,235,V(x-10,-75,-215))
|
||||
rail.Label='2020 rail envelope; slots not modelled';rail.ViewObject.Transparency=25
|
||||
# KMT KFL001 A0=16, A=11.5, A1=5.5: relative to the mounting
|
||||
# plane, inner insert protrudes 10.5mm and housing protrudes 6mm.
|
||||
# Circular protrusion OD29 is still a geometric placeholder.
|
||||
rear=doc.getObject('KFLHousing0');rear.Shape=rear.Shape.cut(cylinder(20,2.02,V(0,0,-84.01))).removeSplitter();rear.Label='KFL housing 6mm nominal protrusion, OD29 assumed'
|
||||
front=doc.getObject('KFLHousing1');front.Shape=front.Shape.cut(cylinder(20,2.02,V(0,0,-181.01))).removeSplitter();front.Label='KFL housing 6mm nominal protrusion, OD29 assumed'
|
||||
inner=doc.getObject('KFLInner0');s=inner.Shape.copy();s.translate(V(0,0,2));inner.Shape=s;inner.Label='KFL insert 10.5mm nominal protrusion behind mounting plane'
|
||||
inner=doc.getObject('KFLInner1');s=inner.Shape.copy();s.translate(V(0,0,-1));inner.Shape=s;inner.Label='KFL insert 10.5mm nominal protrusion behind mounting plane'
|
||||
doc.recompute()
|
||||
plates=[]; corners=[]; boardfast=[]; railfast=[]; knuts=[]
|
||||
for i,z in enumerate((-84.,-187.)):
|
||||
shape=Part.makeBox(140,86,8,V(-70,-51,z)).cut(cylinder(15.1,8.04,V(0,0,z-.02)))
|
||||
for x in (-24.,24.):shape=shape.cut(cylinder(3.5,8.04,V(x,0,z-.02)))
|
||||
for x in (-50.6200577,50.6200577): shape=shape.cut(slot_xz(x-2,x+2,-42.75,2.75,z-.02,8.04))
|
||||
plates.append(obj(doc,'AdjustPlate%d'%i,'8mm plate, horizontal cross-slots, mounting aperture provisional',shape.removeSplitter(),(.72,.74,.78)))
|
||||
for j,x in enumerate((-50.6200577,50.6200577)):
|
||||
# Brackets on outer side of each plate, legs exactly meet each other.
|
||||
sign=1 if i==0 else -1
|
||||
face=z+8 if sign==1 else z
|
||||
zplate=face if sign==1 else face-3
|
||||
zfoot=face if sign==1 else face-20
|
||||
leg=Part.makeBox(18,20,3,V(x-9,-55,zplate))
|
||||
leg=leg.cut(slot_z(x,-45.75,-41.75,2.75,zplate-.02,3.04))
|
||||
foot=Part.makeBox(18,3,20,V(x-9,-55,zfoot))
|
||||
zb=face+10 if sign==1 else face-10
|
||||
foot=foot.cut(cylinder(2.75,3.04,V(x,-55.02,zb),V(0,1,0)))
|
||||
bracket=leg.fuse(foot).removeSplitter()
|
||||
corners.append(obj(doc,'RealCorner%d%d'%(i,j),'2020 L angle 20x20x18: assumed 9.5x5.5 long slot and 5.5 round',bracket,(.88,.48,.14)))
|
||||
# Plate M5 screw shank, head and nut: 5 mm diameter nominal.
|
||||
# 1 mm thick washers and 19 mm shank envelope (not a selected screw length).
|
||||
stem=cylinder(2.45,19,V(x,-42.75,z-4));
|
||||
if sign==1:
|
||||
head=cylinder(4.2,3,V(x,-42.75,face+4));nut=cylinder(4.3,3,V(x,-42.75,z-4));wash1=cylinder(5,1,V(x,-42.75,face+3));wash2=cylinder(5,1,V(x,-42.75,z-1))
|
||||
else:
|
||||
head=cylinder(4.2,3,V(x,-42.75,z-7));nut=cylinder(4.3,3,V(x,-42.75,z+9));wash1=cylinder(5,1,V(x,-42.75,z-4));wash2=cylinder(5,1,V(x,-42.75,z+8))
|
||||
boardfast.append(obj(doc,'BoardFast%d%d'%(i,j),'M5 plate fastener and 10mm washer envelopes',stem.fuse(head).fuse(nut).fuse(wash1).fuse(wash2),(.13,.14,.16)))
|
||||
# Foot screw goes into the rail; T-slot geometry omitted from the rail envelope.
|
||||
# Ø5.5 round hole at profile, low-profile Ø9.5 x 2.75 head, no washer.
|
||||
shank=cylinder(2.45,8,V(x,-60,zb),V(0,1,0));head=cylinder(4.75,2.75,V(x,-52,zb),V(0,1,0))
|
||||
railfast.append(obj(doc,'RailFast%d%d'%(i,j),'M5 low-profile 9.5x2.75 head at round hole; no washer',shank.fuse(head),(.13,.14,.16)))
|
||||
knuts.append(obj(doc,'Tnut%d%d'%(i,j),'2020 T nut location schematic inside rail',Part.makeBox(8,4,8,V(x-4,-61,zb-4)),(.7,.7,.72)))
|
||||
# Accurate KFL heads and nuts are not dimensioned, add nominal cylindrical envelopes.
|
||||
for i,z in enumerate((-84.,-187.)):
|
||||
for j,x in enumerate((-24.,24.)):
|
||||
if i==0: head=cylinder(5,3,V(x,0,-70.5));nut=cylinder(5,3,V(x,0,-87))
|
||||
else: head=cylinder(5,3,V(x,0,-195.5));nut=cylinder(5,3,V(x,0,-179))
|
||||
obj(doc,'KFLFastEnd%d%d'%(i,j),'M6 head / nut envelopes assumed',head.fuse(nut),(.15,.16,.18))
|
||||
doc.recompute()
|
||||
tests=[]
|
||||
def check(label,a,b):
|
||||
v=common(a.Shape,b.Shape);tests.append((label,v))
|
||||
for i in range(2):
|
||||
for n in ('KFLHousing%d'%i,'KFLInner%d'%i):check('Plate%d/%s'%(i,n),plates[i],doc.getObject(n))
|
||||
for j in range(2):
|
||||
k=2*i+j; check('Plate%d/corner%d%d'%(i,i,j),plates[i],corners[k]);check('boardfast%d%d/corner'%(i,j),boardfast[k],corners[k]);check('boardfast%d%d/plate'%(i,j),boardfast[k],plates[i]);check('railfast%d%d/corner'%(i,j),railfast[k],corners[k])
|
||||
check('boardfast%d%d/railfast'%(i,j),boardfast[k],railfast[k]);check('boardfast%d%d/rail'%(i,j),boardfast[k],doc.getObject('NewRail%d'%j));check('railfast%d%d/plate'%(i,j),railfast[k],plates[i])
|
||||
for n in ('Motor','SwitchEnvelope','MotorFlange','WheelHub','Wheel70'):
|
||||
if doc.getObject(n):
|
||||
check('corner%d%d/%s'%(i,j,n),corners[k],doc.getObject(n));check('boardfast%d%d/%s'%(i,j,n),boardfast[k],doc.getObject(n));check('railfast%d%d/%s'%(i,j,n),railfast[k],doc.getObject(n))
|
||||
# Whole solid rail envelopes include a drawn solid area where a T-slot exists.
|
||||
check('corner%d%d/ownRail'%(i,j),corners[k],doc.getObject('NewRail%d'%j))
|
||||
for i in range(2):
|
||||
for n in ('KFLHousing%d'%i,'KFLInner%d'%i):
|
||||
for other in (('MotorFlange' if i==0 else 'WheelHub'),'CamShaft','SwitchEnvelope'):
|
||||
if doc.getObject(n) and doc.getObject(other):check(n+'/'+other,doc.getObject(n),doc.getObject(other))
|
||||
for x in (-24,24):
|
||||
nut=doc.getObject('KFLFastEnd%d%d'%(i,0 if x<0 else 1))
|
||||
for n in ('MotorFlange','WheelHub','SwitchEnvelope'):
|
||||
if doc.getObject(n):check(nut.Name+'/'+n,nut,doc.getObject(n))
|
||||
traveltests=[]
|
||||
for i in range(4):
|
||||
for dy in (-2.,0.,1.):
|
||||
moved=boardfast[i].Shape.copy();moved.translate(V(0,dy,0))
|
||||
vol=common(moved,railfast[i].Shape)
|
||||
distance=moved.distToShape(railfast[i].Shape)[0]
|
||||
traveltests.append((boardfast[i].Name,dy,vol,distance))
|
||||
combined=[]
|
||||
for i in range(4):
|
||||
for dx in (-2.,0.,2.):
|
||||
for dy in (-2.,0.,1.):
|
||||
movedfast=boardfast[i].Shape.copy();movedfast.translate(V(0,dy,0))
|
||||
movedplate=plates[i//2].Shape.copy();movedplate.translate(V(dx,dy,0))
|
||||
overlaps=(common(movedfast,railfast[i].Shape),common(movedfast,corners[i].Shape),common(movedfast,movedplate),common(movedplate,corners[i].Shape),common(movedplate,doc.getObject('NewRail%d'%(i%2)).Shape))
|
||||
combined.append((i,dx,dy,max(overlaps)))
|
||||
doc.recompute()
|
||||
lines=['CONCEPT ONLY: NOT FOR CNC OR PRINT.',
|
||||
'Prior shaft 12x150, 60mm switch envelope, mirrored KFL and known motor assembly retained.',
|
||||
'8 mm plates: 140x86 envelope; central hole OD30.2 assumed, KFL holes OD7 at +/-24 (48mm pitch).',
|
||||
'KMT catalogue A0=16, A1=5.5, A=11.5: inner extends 10.5mm and housing 6mm from flange mounting plane. Housing projection OD29 assumed; not catalog-confirmed.',
|
||||
'Minimum nominal clearance between centre and KFL bolt holes: 24-15.1-3.5=5.4 mm; not a strength calculation.',
|
||||
'Whole plate height adjusts via 9.5x5.5 long slot in angle leg; initial bolt is 1mm off slot centre, nominal offsets -2 to +1mm. Plate to rail has 4mm nominal gap.',
|
||||
'Whole plate left/right adjusts via 9.5x5.5 slots on plates, crossing each slotted angle leg, about +/-2mm subject to fit.',
|
||||
'Axial positioning via sliding T-nuts in the 2020 profile. Brackets 20x20x18x3, 9.5x5.5 slot / 5.5 round as assumed stock.',
|
||||
'M5 head/nut envelopes include 10mm board washers; round-hole rail fasteners use low-profile 9.5mm head, no washer; T nuts, shape and actual extrusion grooves need review.',
|
||||
'Centre hole and bearing projection are assumptions: do NOT assume bearing moves in plate opening.',
|
||||
'Slots geometry offers horizontal +/-2mm, vertical -2..+1mm, but assembled shaft, flange and small gaps constrain safe motion. Loosen and check continuously; do not force alignment.',
|
||||
'KFL shape, fastener head/nut access, actual motor, electrical wiring, switch shape, stator etc not validated.',
|
||||
'Bearing locating/floating and structural strength remain unresolved.',
|
||||
'Positive-volume intersection checks in cubic mm:']+[f'{n}: {v:.6f}' for n,v in tests]
|
||||
lines+=['M5 hardware clearance over candidate vertical travel (cubic mm intersection and mm minimum separation):']+[f'{n} dy={dy:+.1f} intersection={v:.6f} distance={d:.6f}' for n,dy,v,d in traveltests]
|
||||
lines+=['Combined adjustment corners (plate dx/dy and M5s relative bracket; no mounted shaft or surrounding structure included):']+[f'corner={i} dx={dx:+.1f} dy={dy:+.1f} max_intersection={v:.6f}' for i,dx,dy,v in combined]
|
||||
lines+=['Nominal simplified axial clearances (mm): rear housing/flange %.6f; rear inner/flange %.6f; front housing/flange %.6f; front inner/flange %.6f.'%(doc.getObject('KFLHousing0').Shape.distToShape(doc.getObject('MotorFlange').Shape)[0],doc.getObject('KFLInner0').Shape.distToShape(doc.getObject('MotorFlange').Shape)[0],doc.getObject('KFLHousing1').Shape.distToShape(doc.getObject('WheelHub').Shape)[0],doc.getObject('KFLInner1').Shape.distToShape(doc.getObject('WheelHub').Shape)[0])]
|
||||
lines+=['KFL inner axial centre distance 104.5mm, switch envelope to inner end gaps rear15.5mm front14.5mm; computed from nominal adjusted model.']
|
||||
(O/'geometry_check.txt').write_text('\n'.join(lines)+'\n',encoding='utf8')
|
||||
# Red solids explicitly indicate any detected nonzero intersection.
|
||||
bad=[(n,v) for n,v in tests if v>0.001]
|
||||
(O/'checks_status.txt').write_text('INTERSECTIONS FOUND: '+str(bad)+'\n',encoding='utf8')
|
||||
doc.saveAs(str(O/'Assembly.FCStd'));Import.export([o for o in doc.Objects if hasattr(o,'Shape')],str(O/'Assembly.step'))
|
||||
view=G.activeDocument().activeView();G.activateWorkbench('PartWorkbench')
|
||||
def save(path,mode):
|
||||
if mode=='side': view.viewFront()
|
||||
else: view.viewAxonometric();G.updateGui();view.getCameraNode().orientation.setValue(coin.SbRotation(.880476,.115917,-.279848,-.364705))
|
||||
G.updateGui();time.sleep(.7);view.fitAll();G.updateGui();time.sleep(.7);view.saveImage(str(O/path),1800,1250,'White')
|
||||
save('assembly.png','axon');save('side.png','side')
|
||||
# Bearing/plates context only; hides shaft/end assemblies and green envelope, so holes visible.
|
||||
visible={o.Name:o.ViewObject.Visibility for o in doc.Objects if hasattr(o,'Shape')}
|
||||
show=set([o.Name for o in plates+corners+boardfast+railfast]+['KFLHousing0','KFLHousing1','KFLInner0','KFLInner1','KFLBolt0_0','KFLBolt0_1','KFLBolt1_0','KFLBolt1_1','KFLFastEnd00','KFLFastEnd01','KFLFastEnd10','KFLFastEnd11','NewRail0','NewRail1','CamShaft'])
|
||||
for o in doc.Objects:
|
||||
if hasattr(o,'Shape'):o.ViewObject.Visibility=o.Name in show
|
||||
save('adjustment.png','axon')
|
||||
for o in doc.Objects:
|
||||
if hasattr(o,'Shape'):o.ViewObject.Visibility=visible.get(o.Name,True)
|
||||
doc.recompute();doc.saveAs(str(O/'Assembly.FCStd'))
|
||||
(O/'DONE.txt').write_text('ok',encoding='utf8')
|
||||
except Exception:
|
||||
(O/'ERROR.txt').write_text(traceback.format_exc(),encoding='utf8');raise
|
||||
Reference in New Issue
Block a user