139 lines
10 KiB
Python
139 lines
10 KiB
Python
from pathlib import Path
|
|
import math, time, traceback, os
|
|
import FreeCAD as A, FreeCADGui as G, Part, Import
|
|
from pivy import coin
|
|
V=A.Vector
|
|
R=Path(r'C:\projects\wheel'); shaftlen=int(os.environ.get('SAME_FLANGE_SHAFT_LEN','100')); O=R/'models'/('kfl_same_flange_%d'%shaftlen); O.mkdir(parents=True,exist_ok=True)
|
|
def cy(r,h,x=0,y=0,z=0):return Part.makeCylinder(r,h,V(x,y,z))
|
|
def add(d,n,label,s,c,t=0):
|
|
o=d.addObject('PartDesign::Feature',n);o.Label=label;o.Shape=s;o.ViewObject.ShapeColor=c;o.ViewObject.LineColor=(.08,.08,.08);o.ViewObject.Transparency=t;return o
|
|
def booleanvol(a,b):
|
|
return a.common(b).Volume if a.BoundBox.intersect(b.BoundBox) else 0
|
|
try:
|
|
src=A.openDocument(str(R/'models'/'pcd50_open_pillar_v1'/'Assembly.FCStd'))
|
|
existing={o.Name:(o.Shape.copy(),tuple(o.ViewObject.ShapeColor),o.ViewObject.Transparency) for o in src.Objects if hasattr(o,'Shape') and not o.Shape.isNull()}
|
|
A.closeDocument(src.Name)
|
|
src=A.openDocument(str(R/'models'/'turn_signal_cam_v2'/'Assembly.FCStd'))
|
|
cam={n:src.getObject(n).Shape.copy() for n in ['Shaft','Key','Upper','Lower','Hardware0','Hardware1','Hardware2','Hardware3']}
|
|
A.closeDocument(src.Name)
|
|
for n,s in cam.items():s.translate(V(0,0,-108 if n=='Shaft' else -112))
|
|
cam['Shaft']=cy(6,shaftlen,0,0,-58-shaftlen).cut(Part.makeBox(4.1,3,14,V(-2.05,4,-119)))
|
|
doc=A.newDocument('KFL100_Schematic'); objs={}
|
|
for n,(s,c,t) in existing.items():
|
|
objs[n]=add(doc,n,'Prior existing '+n,s,c,55 if n=='Motor' else t)
|
|
for i,x in enumerate([-50.6200577,50.6200577]):
|
|
objs['NewRail%d'%i]=add(doc,'NewRail%d'%i,'Proposed 2020 longitudinal rail schematic',Part.makeBox(20,20,190,V(x-10,-75,-170)),(.48,.51,.55))
|
|
# Two vertical plates parallel to XY. The 30.2 mm bore is placeholder,
|
|
# with dummy bearing protrusion 29 mm OD; NOT a production bore.
|
|
plates=[]
|
|
for i,z in enumerate([-84,-150]):
|
|
s=Part.makeBox(140,90,8,V(-70,-55,z)).cut(cy(15.1,8.02,0,0,z-.01))
|
|
for x in (-24,24):s=s.cut(cy(3.5,8.02,x,0,z-.01))
|
|
o=add(doc,'Plate%d'%i,'8mm plate: hole 30.2 assumed',s,(.66,.69,.74));plates.append(o)
|
|
# Simplified illustrative corner brackets, contacting plate and rail only.
|
|
for j,x in enumerate([-50.6200577,50.6200577]):
|
|
a=Part.makeBox(16,12,3,V(x-8,-55,z-3))
|
|
b=Part.makeBox(16,3,11,V(x-8,-55,z-11))
|
|
o=add(doc,'L_%d_%d'%(i,j),'90deg bracket placeholder, fasteners omitted',a.fuse(b).removeSplitter(),(.88,.49,.16))
|
|
# Approximate KFL outside dimensions: 63 x 38 mm, pitch 48, flange
|
|
# ears 5.5, insert 14.5; protrusion 29mm OD and position are placeholders.
|
|
bearings=[]
|
|
for i,(plate_z,inner_z,sign) in enumerate([(-84,-88.5,1),(-150,-153,-1)]):
|
|
# face of plate towards outside axle area, mirrored.
|
|
if sign==1: face=plate_z+8; flangez=face; insertz=face-8
|
|
else: face=plate_z; flangez=face-5.5; insertz=face
|
|
ring=cy(19,5.5,0,0,flangez).cut(cy(9.5,5.52,0,0,flangez-.01))
|
|
for x in (-24,24):
|
|
ear=cy(7.5,5.5,x,0,flangez).fuse(Part.makeBox(12,15,5.5,V(min(x,0),-7.5,flangez)))
|
|
ring=ring.fuse(ear)
|
|
for x in (-24,24):ring=ring.cut(cy(3.5,5.52,x,0,flangez-.01))
|
|
ring=ring.cut(cy(9.5,5.52,0,0,flangez-.01))
|
|
ring=ring.fuse(cy(14.5,8,0,0,insertz).cut(cy(9.5,8.02,0,0,insertz-.01))).removeSplitter()
|
|
outer=add(doc,'KFLHousing%d'%i,'KFL001 housing: 29 protrusion ASSUMED',ring,(.45,.49,.53));bearings.append(outer)
|
|
inner=cy(9.2,14.5,0,0,inner_z).cut(cy(6.1,14.52,0,0,inner_z-.01))
|
|
bearings.append(add(doc,'KFLInner%d'%i,'KFL inner 14.5mm rotating schematic',inner,(.77,.77,.79)))
|
|
# M6 screws only illustrative straight 5.8mm shank; heads omitted.
|
|
for j,x in enumerate((-24,24)):
|
|
z0=plate_z-1 if sign==1 else flangez-1
|
|
o=add(doc,'KFLBolt%d_%d'%(i,j),'M6 shank schematic, heads omitted',cy(2.9,15.5,x,0,z0),(.18,.19,.20));objs[o.Name]=o
|
|
# Real shaft/cam from existing project.
|
|
colors={'Shaft':(.65,.66,.68),'Key':(.98,.65,.1),'Upper':(.05,.40,.80),'Lower':(.16,.62,.88)}
|
|
for n,s in cam.items():objs['Cam'+n]=add(doc,'Cam'+n,'Existing cam V2 '+n,s,colors.get(n,(.2,.2,.21)))
|
|
# Actual small conversion disk STEP: mirrored to front face at z=-38.
|
|
conv=Part.read(str(R/'models'/'转换盘1.step'));conv.rotate(V(),V(1,0,0),180);conv.translate(V(0,0,-38))
|
|
objs['Conversion']=add(doc,'Conversion','Existing conversion disk STEP; face position approx',conv,(.82,.83,.85))
|
|
# Six-hole keyed flange is a placeholder, the hub overlaps 12mm of shaft.
|
|
flange=cy(28.5,4,0,0,-50).cut(cy(6.2,4.02,0,0,-50.01))
|
|
for j in range(6):
|
|
a=2*math.pi*j/6+math.pi/6;flange=flange.cut(cy(2.75,4.02,24*math.cos(a),24*math.sin(a),-50.01))
|
|
hubradius=10
|
|
hub=cy(15,20,0,0,-70).cut(cy(6.2,20.02,0,0,-70.01));flange=flange.fuse(hub).removeSplitter()
|
|
objs['MotorFlange']=add(doc,'MotorFlange','MOTOR actual nominal OD57 flange 4mm, OD30 hub protrusion 20mm',flange,(.9,.66,.20))
|
|
# Front flange mirrors the motor-side unit: 57 disc, 4 thick, 30x20 hub.
|
|
# Front bearing housing occupies z=-155.5..-150; 0.5 mm nominal axial gap.
|
|
wh=cy(15,20,0,0,-176).cut(cy(6.2,20.02,0,0,-176.01))
|
|
whplate=cy(28.5,4,0,0,-180).cut(cy(6.2,4.02,0,0,-180.01))
|
|
for j in range(6):
|
|
a=2*math.pi*j/6; whplate=whplate.cut(cy(2.75,4.02,24*math.cos(a),24*math.sin(a),-180.01))
|
|
wh=wh.fuse(whplate).removeSplitter()
|
|
objs['WheelHub']=add(doc,'WheelHub','Wheel flange same 57x4 30x20 (key not modeled)',wh,(.9,.66,.20))
|
|
wheel70=Part.read(str(R/'models'/'70转接板.step'));wheel70.rotate(V(),V(1,0,0),180);wheel70.translate(V(0,0,-180))
|
|
objs['Wheel70']=add(doc,'Wheel70','Existing 70 adapter - contact and PCD48 phase shown',wheel70,(.82,.83,.85))
|
|
# Switch envelope is deliberately only transparent rectangular with generic
|
|
# centre bore; 50mm depth includes off-axis connector. Actual contours unknown.
|
|
sw=Part.makeBox(110,80,50,V(-55,-40,-138.5)).cut(cy(23.5,50.02,0,0,-138.51))
|
|
objs['SwitchEnvelope']=add(doc,'SwitchEnvelope','50mm switch bounding placeholder; 47mm bore ASSUMED',sw,(.45,.73,.43),80)
|
|
doc.recompute()
|
|
# Restrict checks to shapes actually modelled. Intentional shaft-to-hub clearance.
|
|
checks=[]
|
|
pairs=[('Plate%d'%i,'KFLHousing%d'%i,plates[i].Shape,bearings[2*i].Shape) for i in range(2)]
|
|
pairs += [('Plate%d'%i,'KFLInner%d'%i,plates[i].Shape,bearings[2*i+1].Shape) for i in range(2)]
|
|
pairs += [('SwitchEnvelope','Cam'+n,sw,cam[n]) for n in ('Upper','Lower','Hardware0','Hardware1','Hardware2','Hardware3')]
|
|
pairs += [('MotorFlange','Shaft',flange,cam['Shaft']),('WheelHub','Shaft',wh,cam['Shaft']),('WheelHub','70Adapter',wh,wheel70),('Plate0','Plate1',plates[0].Shape,plates[1].Shape)]
|
|
pairs += [('MotorFlange','RearBearing',flange,bearings[0].Shape),('MotorFlange','RearInner',flange,bearings[1].Shape),('MotorFlange','RearPlate',flange,plates[0].Shape),('WheelHub','FrontBearing',wh,bearings[2].Shape),('WheelHub','FrontInner',wh,bearings[3].Shape),('WheelHub','FrontPlate',wh,plates[1].Shape),('MotorFlange','Conversion',flange,conv)]
|
|
for a,b,sa,sb in pairs:
|
|
checks.append('%s / %s = %.6f mm3'%(a,b,booleanvol(sa,sb)))
|
|
# Probe through both mated plates at the nominal six 48mm bolt centers.
|
|
for j in range(6):
|
|
a=2*math.pi*j/6; probe=cy(1.0,12.0,24*math.cos(a),24*math.sin(a),-188)
|
|
checks.append('Wheel bolt path %d: shaft probe / flange %.8f / 70plate %.8f mm3'%(j,booleanvol(probe,wh),booleanvol(probe,wheel70)))
|
|
for j in range(6):
|
|
a=math.pi/6+2*math.pi*j/6; probe=cy(1.0,12.0,24*math.cos(a),24*math.sin(a),-50)
|
|
checks.append('Motor bolt path %d: probe / flange %.8f / converter %.8f mm3'%(j,booleanvol(probe,flange),booleanvol(probe,conv)))
|
|
for n,s in [('RearConflict',flange.common(bearings[0].Shape)),('FrontConflict',wh.common(bearings[2].Shape))]:
|
|
if not s.isNull() and s.Volume>.001: add(doc,n,'RED: nominal collision '+n,s,(1.0,.03,.03))
|
|
z_end=-58-shaftlen
|
|
front_overlap=max(0,min(-156,-58)-max(-176,z_end))
|
|
plate_overlap=max(0,min(-176,-58)-max(-180,z_end))
|
|
lines=['CONCEPT ONLY; NOT ASSEMBLY FIT VALIDATION.',
|
|
'Both flanges: disc OD57 thickness4, 6x PCD48 5.5 clearance; hub OD30 protrudes 20 from the base. Mirrored.',
|
|
'Shaft nominal 12 x %d; z=%.1f..-58.0; motor hub z=-70..-50; motor insertion 12mm assumed.'%(shaftlen,z_end),
|
|
'Wheel hub z=-176..-156, front plate z=-180..-176; hub engagement %.1f mm; plate bore overlap %.1f mm (this is not extra key engagement).'%(front_overlap,plate_overlap),
|
|
'Bearing positions, 50mm switch envelope and cam as previous shifted design; rear/front hub clearance to simplified outer bearing 0.5mm nominal.',
|
|
'KFL approximate 48 bolt pitch and 14.5 inner width, housing/protrusion guessed; 8mm plate 30.2 hole guessed; no real module shape, its 47mm hole is an assumption.',
|
|
'Wheel70 is real STEP, mirror/phase0, rear face in contact with front flange disc, PCD48 hole centers align nominally; bolt direction, washer and tool clearances not checked.',
|
|
'Real converter/70 STEP hole OD4.234, fastener M4 feasible only with washers and actual size checks; M5 cannot pass nominally; hole intersections/phase and actual hardware must be checked.',
|
|
'Shaft endpoint keys not modeled; front hub insertion alone does not establish permissible torque. Actual axial gap, M3 nut, alignments, float bearing arrangement, switch/fan/angles, bolt heads, tool accessibility, and fit/strength not verified.',
|
|
'Checked positive-volume intersections of listed pairs only:']+checks
|
|
(O/'geometry_check.txt').write_text('\n'.join(lines)+'\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(name,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/name),1800,1250,'White')
|
|
save('assembly.png','axon');save('side.png','side')
|
|
# Simplified focus hides rear encoder/rails so bearing through plate is visible.
|
|
for n,o in objs.items():
|
|
if n in existing or n.startswith('NewRail') or n=='Conversion':o.ViewObject.Visibility=False
|
|
for o in doc.Objects:
|
|
if o.Name.startswith('L_'):o.ViewObject.Visibility=False
|
|
save('focus.png','axon')
|
|
for o in doc.Objects:
|
|
if hasattr(o,'Shape'):o.ViewObject.Visibility=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
|