建立方向盘项目初始版本
This commit is contained in:
147
scripts/build_pcd50_fit_concept.py
Normal file
147
scripts/build_pcd50_fit_concept.py
Normal file
@@ -0,0 +1,147 @@
|
||||
"""PCD50 square-open bracket, with PCD76 corner slots offset 2 mm.
|
||||
|
||||
Concept assembly only. Rebuild via rebuild_pcd50_fit_concept.FCMacro.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import math, time, traceback
|
||||
import FreeCAD as A, FreeCADGui as G, Part, Import
|
||||
from pivy import coin
|
||||
R=Path(__file__).resolve().parent.parent
|
||||
O=R/'models'/'pcd50_fit_concept'; O.mkdir(parents=True,exist_ok=True)
|
||||
V=A.Vector
|
||||
def cy(r,h,x,y,z): return Part.makeCylinder(r,h,V(x,y,z))
|
||||
def add(doc,n,label,s,c,t=0):
|
||||
o=doc.addObject('PartDesign::Feature',n); o.Label=label; o.Shape=s
|
||||
o.ViewObject.ShapeColor=c;o.ViewObject.LineColor=(.1,.1,.1);o.ViewObject.Transparency=t
|
||||
return o
|
||||
try:
|
||||
src=A.openDocument(str(R/'models'/'profile_mount_check'/'Direct.FCStd'))
|
||||
keep=['Motor','Holder','Magnet','PCB','Chip']+['PCBHead%d'%i for i in range(1,4)]+['Corner%d'%i for i in range(4)]+['Rail%d'%i for i in range(2)]
|
||||
shapes={n:src.getObject(n).Shape.copy() for n in keep}
|
||||
orig=src.getObject('Bracket').Shape.copy()
|
||||
shims=[src.getObject('Shim%d'%i).Shape.copy() for i in range(4)]
|
||||
A.closeDocument(src.Name)
|
||||
# The old Direct model moved each bracket 2.75mm outwards. Inwards by
|
||||
# 0.75mm makes the new shift 2.00mm, leaving nominal slot clearance.
|
||||
for n in ['Corner%d'%i for i in range(4)]+['Rail%d'%i for i in range(2)]:
|
||||
s=shapes[n]; cx=(s.BoundBox.XMin+s.BoundBox.XMax)/2
|
||||
s.translate(V(-.75 if cx>0 else .75,0,0))
|
||||
doc=A.newDocument('PCD50_Fit_2mm'); objs={}
|
||||
colors={'Motor':(.32,.34,.37),'Holder':(.98,.55,.1),'Magnet':(.8,.8,.8),'PCB':(.04,.65,.19),'Chip':(.1,.1,.1)}
|
||||
for n,s in shapes.items():
|
||||
c=colors.get(n,(.84,.47,.13) if n.startswith('Corner') else (.55,.56,.60) if n.startswith('Rail') else (.8,.8,.8))
|
||||
objs[n]=add(doc,n,n,s,c,70 if n=='Motor' else 20 if n.startswith('Corner') else 0)
|
||||
pts=[(25*math.cos(math.radians(45+90*i)),25*math.sin(math.radians(45+90*i))) for i in range(4)]
|
||||
# A 49mm frame leaves about 2.7mm beside the 8.2mm tool opening.
|
||||
# Its outer corners are notched to permit PCD76 fastening tool access.
|
||||
square=Part.makeBox(49,49,3.2,V(-24.5,-24.5,10.9)).cut(cy(16.8,3.22,0,0,10.89))
|
||||
p76=38/math.sqrt(2)
|
||||
for sx in (-1,1):
|
||||
for sy in (-1,1):
|
||||
square=square.cut(cy(5.2,3.24,sx*p76,sy*p76,10.88))
|
||||
bracket=orig.fuse(square)
|
||||
for x,y in pts:bracket=bracket.fuse(cy(4.5,1,x,y,2.5))
|
||||
cutters=[]
|
||||
for i in range(4):
|
||||
bb=shapes['Corner%d'%i].BoundBox
|
||||
cutters.append(Part.makeBox(bb.XLength+.4,bb.YLength+.4,3.22,V(bb.XMin-.2,bb.YMin-.2,-.01)))
|
||||
for cutter in cutters:bracket=bracket.cut(cutter)
|
||||
# Outward opening towards left/right per user sketch, with upper access.
|
||||
for x,y in pts:
|
||||
start=x if x>0 else x-10
|
||||
slot=Part.makeBox(10,4.4,3.7,V(start,y-2.2,-.05)).fuse(cy(2.2,3.7,x,y,-.05))
|
||||
upper=Part.makeBox(10,8.2,11,V(start,y-4.1,3.5)).fuse(cy(4.1,11,x,y,3.5))
|
||||
bracket=bracket.cut(slot).cut(upper)
|
||||
for j in range(4):shims[j]=shims[j].cut(slot)
|
||||
for j in range(4):
|
||||
for cutter in cutters:shims[j]=shims[j].cut(cutter)
|
||||
bracket=bracket.removeSplitter()
|
||||
if bracket.isNull() or not bracket.isValid() or len(bracket.Solids)!=1: raise RuntimeError('invalid bracket')
|
||||
b=add(doc,'OpenBracket','PCD50 square frame, outward open - concept',bracket,(.12,.43,.88))
|
||||
shimobjs=[]
|
||||
for i,s in enumerate(shims):
|
||||
s=s.removeSplitter()
|
||||
if s.isNull() or not s.isValid() or len(s.Solids)!=1:raise RuntimeError('invalid shim %d'%i)
|
||||
shimobjs.append(add(doc,'Shim50_%d'%i,'0.5mm open adjustment shim - concept',s,(.70,.70,.75)))
|
||||
heads=[];shafts=[];mount76=[];washers76=[]
|
||||
for i,(x,y) in enumerate(pts):
|
||||
heads.append(add(doc,'Head50_%d'%i,'M4 50 head dia 7, height 3 assumed',cy(3.5,3,x,y,3.5),(.8,.8,.8)))
|
||||
shafts.append(add(doc,'Shaft50_%d'%i,'M4x8 50 screw shank Ø4 concept',cy(2,8,x,y,-4.5),(.8,.8,.8)))
|
||||
p=38/math.sqrt(2)
|
||||
for sx in (-1,1):
|
||||
for sy in (-1,1):
|
||||
x,y=sx*p,sy*p
|
||||
w=cy(5,.8,x,y,3).cut(cy(2.15,.82,x,y,2.99)).removeSplitter()
|
||||
washers76.append(add(doc,'Washer76_%d'%len(washers76),'M4 washer Ø10 x 0.8 assumed',w,(.75,.75,.77)))
|
||||
s=cy(2,8,x,y,-4.2).fuse(cy(3.5,2.5,x,y,3.8)).removeSplitter()
|
||||
mount76.append(add(doc,'Mount76_%d'%len(mount76),'M4 76 shank Ø4 head Ø7 concept',s,(.8,.8,.8)))
|
||||
doc.recompute(); corners=[objs['Corner%d'%i] for i in range(4)]; rails=[objs['Rail%d'%i] for i in range(2)]
|
||||
pairs=[]
|
||||
for a in [b]+shimobjs+heads+shafts:
|
||||
for c in corners+rails:pairs.append((a,c))
|
||||
for a in shafts:
|
||||
for c in [b]+shimobjs:pairs.append((a,c))
|
||||
for a in mount76:
|
||||
for c in corners+rails+[b]+shimobjs+heads+shafts:pairs.append((a,c))
|
||||
for a in washers76:
|
||||
for c in corners+rails+[b]+shimobjs+heads+shafts+mount76:pairs.append((a,c))
|
||||
for a in [b]+shimobjs+heads+shafts:
|
||||
for n in ['Motor','Holder','Magnet','PCB','Chip']+['PCBHead%d'%i for i in range(1,4)]:pairs.append((a,objs[n]))
|
||||
errors=[]
|
||||
for a,c in pairs:
|
||||
if not a.Shape.BoundBox.intersect(c.Shape.BoundBox):continue
|
||||
vol=a.Shape.common(c.Shape).Volume
|
||||
if vol>.001:errors.append('%s / %s %.6fmm3'%(a.Name,c.Name,vol))
|
||||
# Record tool clearance against corner/support for four assumed 7.5mm
|
||||
# axial screwdriver envelopes. Start above the modelled screw heads.
|
||||
access=[]
|
||||
for i,(x,y) in enumerate(pts):
|
||||
tool=cy(3.75,20,x,y,6.51); hits=[]
|
||||
for o in [b]+corners+rails:
|
||||
if tool.BoundBox.intersect(o.Shape.BoundBox):
|
||||
v=tool.common(o.Shape).Volume
|
||||
if v>.001:hits.append('%s %.4f'%(o.Name,v))
|
||||
access.append('50 head %d: %s'%(i,', '.join(hits) if hits else 'clear (model envelope only)'))
|
||||
for sx in (-1,1):
|
||||
for sy in (-1,1):
|
||||
x,y=sx*p,sy*p; tool=cy(3.75,20,x,y,6.31); hits=[]
|
||||
for o in [b]+corners+rails:
|
||||
if tool.BoundBox.intersect(o.Shape.BoundBox):
|
||||
v=tool.common(o.Shape).Volume
|
||||
if v>.001:hits.append('%s %.4f'%(o.Name,v))
|
||||
access.append('76 head %+.0f,%+.0f: %s'%(sx,sy,', '.join(hits) if hits else 'clear (model envelope only)'))
|
||||
areas=[]
|
||||
for x,y in pts:
|
||||
count=0; step=.2
|
||||
for ix in range(-18,19):
|
||||
for iy in range(-18,19):
|
||||
dx,dy=ix*step,iy*step
|
||||
if dx*dx+dy*dy<=3.5**2 and bracket.isInside(V(x+dx,y+dy,3.49),1e-7,True):count+=1
|
||||
areas.append(count*step*step)
|
||||
report=['Concept only. PCD50 and PCD76 phase 45 deg assumed; 4x M4 Ø4.',
|
||||
'PCD76 slot 9.5 x 5.5 assumed; angle brackets remain flush and shifted 2.00mm outwards.',
|
||||
'Nominal M4 versus slot-end clearance: 0.75mm. 50 head Ø7 x 3 assumed.',
|
||||
'76 washer Ø10x0.8 assumed; M4x8 gives nominal 4.2mm engagement, motor hole depth unknown.',
|
||||
'0.5mm shim; 50 foot top 3.5mm; original PCB and magnet position unchanged.',
|
||||
'Detected positive volumes >0.001mm3:']+(errors or ['none in checked pairs'])
|
||||
report+=['Tool access, Ø7.5 envelope from z6.51:']+access+['Sampled 50 head contact areas, mm2 (not strength): '+', '.join('%.2f'%x for x in areas)]
|
||||
(O/'geometry_check.txt').write_text('\n'.join(report)+'\n',encoding='utf-8')
|
||||
G.activateWorkbench('PartWorkbench');view=G.activeDocument().activeView()
|
||||
def render(top,path):
|
||||
if top:view.viewTop()
|
||||
else:
|
||||
view.viewAxonometric();G.updateGui();time.sleep(.5)
|
||||
view.getCameraNode().orientation.setValue(coin.SbRotation(.880476,.115917,-.279848,-.364705))
|
||||
G.updateGui();time.sleep(.7);view.fitAll();G.updateGui();time.sleep(.7);G.updateGui();view.saveImage(str(O/path),1600,1200,'White')
|
||||
render(True,'rear.png');render(False,'assembly.png')
|
||||
states={o.Name:o.ViewObject.Visibility for o in doc.Objects if hasattr(o,'Shape')}
|
||||
for o in doc.Objects:
|
||||
if hasattr(o,'Shape'):o.ViewObject.Visibility=False
|
||||
b.ViewObject.Visibility=True;render(True,'bracket.png')
|
||||
for o in doc.Objects:
|
||||
if hasattr(o,'Shape'):o.ViewObject.Visibility=states[o.Name]
|
||||
render(False,'assembly.png');doc.recompute();doc.saveAs(str(O/'PCD50_Assembly.FCStd'))
|
||||
Import.export([o for o in doc.Objects if hasattr(o,'Shape')],str(O/'PCD50_Assembly.step'))
|
||||
(O/'DONE.txt').write_text('ok\n',encoding='utf-8')
|
||||
except Exception:
|
||||
(O/'ERROR.txt').write_text(traceback.format_exc(),encoding='utf-8');raise
|
||||
Reference in New Issue
Block a user