建立方向盘项目初始版本

This commit is contained in:
2026-10-29 22:04:55 +08:00
commit 9962cbfbce
834 changed files with 1519203 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
"""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'/'square_encoder_concept'
O.mkdir(parents=True,exist_ok=True)
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(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)]+['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('SquareOpen50')
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.
# Blue square outline in user's sketch: retain PCB ring's original height
# and pilot bosses while replacing the outer envelope with a 45mm frame.
square=Part.makeBox(45,45,3.2,V(-22.5,-22.5,10.9)).cut(cyl(16.8,3.22,0,0,10.89))
bracket=orig.fuse(square)
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 axial through slots each open outward toward the nearest side.
# Remove a larger opening in the upper frame for M4 head/tool 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(cyl(2.2,3.7,x,y,-.05))
upp=Part.makeBox(10,7.4,11,V(start,y-3.7,3.5)).fuse(cyl(3.7,11,x,y,3.5))
bracket=bracket.cut(slot)
bracket=bracket.cut(upp)
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','Square PCD50 frame with outward 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 sideways outward; 45mm square frame from sketch.',
'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/'SquareOpen50_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','SquareOpen50_rear.png');render('axon','SquareOpen50_axon.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
blue.ViewObject.Visibility=True
render('rear','SquareOpen50_bracket.png')
for o in doc.Objects:
if hasattr(o,'Shape'):o.ViewObject.Visibility=states[o.Name]
render('axon','SquareOpen50_axon.png')
doc.recompute();doc.saveAs(str(O/'SquareOpen50.FCStd'))
Import.export([o for o in doc.Objects if hasattr(o,'Shape')],str(O/'SquareOpen50.step'))
(O/'SQUARE_DONE.txt').write_text('ok\n',encoding='utf8')
except Exception:
(O/'SQUARE_ERROR.txt').write_text(traceback.format_exc(),encoding='utf8')
raise