建立方向盘项目初始版本
This commit is contained in:
116
scripts/build_open_encoder_option.py
Normal file
116
scripts/build_open_encoder_option.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""Concept test only: 50mm mounting with open slots, corners flat on motor."""
|
||||
from pathlib import Path
|
||||
import time, math, traceback
|
||||
import FreeCAD as A, FreeCADGui as G, Part, Import
|
||||
from pivy import coin
|
||||
R=Path(__file__).resolve().parent.parent
|
||||
O=R/'models'/'profile_mount_check'
|
||||
V=A.Vector
|
||||
|
||||
def cyl(r,h,x,y,z): return Part.makeCylinder(r,h,V(x,y,z))
|
||||
def add(doc,name,label,sh,color,trans=0):
|
||||
o=doc.addObject('PartDesign::Feature',name); o.Label=label; o.Shape=sh
|
||||
o.ViewObject.ShapeColor=color; o.ViewObject.LineColor=(.1,.1,.1); o.ViewObject.Transparency=trans
|
||||
return o
|
||||
|
||||
try:
|
||||
old=A.openDocument(str(O/'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)]+['MountM4_%d'%i for i in range(1,5)]
|
||||
shapes={n:old.getObject(n).Shape.copy() for n in keep}
|
||||
orig=old.getObject('Bracket').Shape.copy()
|
||||
shims=[old.getObject('Shim%d'%i).Shape.copy() for i in range(4)]
|
||||
A.closeDocument(old.Name)
|
||||
doc=A.newDocument('OpenSlot50')
|
||||
colors={'Motor':(.32,.34,.37),'Holder':(.98,.55,.10),'Magnet':(.8,.8,.8),'PCB':(.04,.65,.19),'Chip':(.1,.1,.1)}
|
||||
objects={}
|
||||
for n in keep:
|
||||
color=colors.get(n,(.84,.47,.13) if n.startswith('Corner') else (.55,.56,.60) if n.startswith('Rail') else (.8,.8,.8))
|
||||
objects[n]=add(doc,n,n,shapes[n],color,70 if n=='Motor' else 20 if n.startswith('Corner') else 0)
|
||||
pts=[]
|
||||
for i in range(4):
|
||||
a=math.radians(45+90*i);pts.append((25*math.cos(a),25*math.sin(a)))
|
||||
# Existing foot top is z=2.5 with 0.5 shim. Increase clamping surface to 3.5;
|
||||
# subtract motor-side angle brackets, keeping them directly on motor.
|
||||
bracket=orig
|
||||
for x,y in pts:
|
||||
bracket=bracket.fuse(cyl(4.5,1,x,y,2.5))
|
||||
# Add 0.2mm plan clearance to each metal foot and 0.2mm above it.
|
||||
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)
|
||||
# Four slots open in the same -Y direction; existing Ø4.4 clearance continues.
|
||||
for x,y in pts:
|
||||
slot=Part.makeBox(4.4,6,3.7,V(x-2.2,y-6,-.05)).fuse(cyl(2.2,3.7,x,y,-.05))
|
||||
bracket=bracket.cut(slot)
|
||||
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('support not a single solid')
|
||||
blue=add(doc,'OpenBracket','PCD50 4 open slots - concept',bracket,(.12,.43,.88))
|
||||
for i,sh in enumerate(shims):
|
||||
sh=sh.removeSplitter()
|
||||
if sh.isNull() or not sh.isValid():raise RuntimeError('shim invalid %d'%i)
|
||||
add(doc,'SlottedShim%d'%i,'Open 0.5mm shim - concept',sh,(.65,.7,.75))
|
||||
heads=[];shafts=[]
|
||||
for i,(x,y) in enumerate(pts):
|
||||
head=cyl(3.5,3,x,y,3.5);shaft=cyl(1.95,8.5,x,y,-5)
|
||||
heads.append(add(doc,'EncoderHead%d'%i,'M4 head Ø7 x 3 - assumed',head,(.8,.8,.8)))
|
||||
shafts.append(add(doc,'EncoderShaft%d'%i,'M4 shaft Ø3.9 - assumed',shaft,(.8,.8,.8)))
|
||||
doc.recompute()
|
||||
corners=[objects['Corner%d'%i] for i in range(4)]
|
||||
rails=[objects['Rail%d'%i] for i in range(2)]
|
||||
moving=[blue]+[doc.getObject('SlottedShim%d'%i) for i in range(4)]+heads+shafts
|
||||
checks=[]
|
||||
for o in moving:
|
||||
for c in corners+rails:
|
||||
if not o.Shape.BoundBox.intersect(c.Shape.BoundBox):continue
|
||||
vol=o.Shape.common(c.Shape).Volume
|
||||
if vol>.0001:checks.append('%s vs %s %.6f mm3'%(o.Name,c.Name,vol))
|
||||
for o in shafts:
|
||||
for c in [blue]+[doc.getObject('SlottedShim%d'%i) for i in range(4)]:
|
||||
if not o.Shape.BoundBox.intersect(c.Shape.BoundBox): continue
|
||||
vol=o.Shape.common(c.Shape).Volume
|
||||
if vol>.0001: checks.append('%s vs %s %.6f mm3'%(o.Name,c.Name,vol))
|
||||
# Effective supporting material under each head immediately below the top.
|
||||
areas=[]
|
||||
for x,y in pts:
|
||||
# Planar sampling avoids degenerate thin-cylinder OCC intersections.
|
||||
count=0; step=.1
|
||||
for ix in range(-35,36):
|
||||
for iy in range(-35,36):
|
||||
dx=ix*step;dy=iy*step
|
||||
if dx*dx+dy*dy<=3.5*3.5 and bracket.isInside(V(x+dx,y+dy,3.49),1e-7,True):count+=1
|
||||
areas.append(count*step*step)
|
||||
# Kinematic slide of support from y=+5 to its assembled position: only a
|
||||
# quick volume test of support vs angles + shafts, not a tool/hand check.
|
||||
slide=[]
|
||||
for n in range(11):
|
||||
dy=5.0-n*.5; moved=bracket.copy();moved.translate(V(0,dy,0));hits=[]
|
||||
for c in corners+rails+shafts:
|
||||
if moved.BoundBox.intersect(c.Shape.BoundBox):
|
||||
v=moved.common(c.Shape).Volume
|
||||
if v>.0001:hits.append('%s %.4f'%(c.Name,v))
|
||||
slide.append('dy=%.1f: %s'%(dy,', '.join(hits) if hits else 'no sampled volume overlap'))
|
||||
report=['CONCEPT ONLY; corners are flush to motor; no spacer under PCD76 corners.',
|
||||
'Support feet 0.5..3.5mm; shim 0.5mm; screw head 7mm dia x 3mm assumed; 0.2mm corner clearance.',
|
||||
'PCD50 screw slots open toward -Y; all four are parallel.',
|
||||
'Only 0.05mm theoretical slot-end clearance in motor corner as original direct check.',
|
||||
'Intersections of support/shims/screw envelope with corners and rails:']+(checks or ['none in checked pairs'])
|
||||
report+=['Approx material area under 7mm screw head using 0.1mm sampled grid (mm2): '+', '.join('%.3f'%x for x in areas),'Sampled lateral slide +Y to 0:']+slide
|
||||
(O/'OpenSlot50_check.txt').write_text('\n'.join(report)+'\n',encoding='utf8')
|
||||
view=G.activeDocument().activeView()
|
||||
def render(kind,name):
|
||||
if kind=='rear':view.viewTop()
|
||||
else:view.getCameraNode().orientation.setValue(coin.SbRotation(.880476,.115917,-.279848,-.364705))
|
||||
G.updateGui();time.sleep(.5);view.fitAll();G.updateGui();time.sleep(.5);view.saveImage(str(O/name),1600,1200,'White')
|
||||
render('rear','OpenSlot50_rear.png');render('axon','OpenSlot50_axon.png')
|
||||
doc.recompute();doc.saveAs(str(O/'OpenSlot50.FCStd'))
|
||||
Import.export([o for o in doc.Objects if hasattr(o,'Shape')],str(O/'OpenSlot50.step'))
|
||||
(O/'OPEN_SLOT_DONE.txt').write_text('ok\n',encoding='utf8')
|
||||
except Exception:
|
||||
(O/'OPEN_SLOT_ERROR.txt').write_text(traceback.format_exc(),encoding='utf8')
|
||||
raise
|
||||
Reference in New Issue
Block a user