建立方向盘项目初始版本

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,89 @@
"""Build mixed-thickness 4x4 sprue; run with FreeCAD bundled Python."""
from pathlib import Path
import json, math, zipfile, shutil, hashlib
import FreeCAD as App
import Part, Mesh, MeshPart
ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT/'models'/'pcd50_open_pillar_v1'
OUT = ROOT/'models'/'washer_grid_4x4'
OUT.mkdir(parents=True, exist_ok=True)
V=App.Vector
def load(name, thickness):
s=Part.read(str(SRC/name))
s.translate(V(-10,-10,0))
if s.isNull() or not s.isValid() or len(s.Solids)!=1 or abs(s.BoundBox.ZLength-thickness)>1e-6:
raise RuntimeError('Invalid source '+name)
# The original 90-degree opening is centered 22.5 degrees above +X.
# Rotate to the NE quadrant so bar attachments on the SW side miss it.
s.rotate(V(),V(0,0,1),22.5)
return s
source05=load('Shim05.step',.5)
source10=load('Shim10.step',1.0)
# Bars are 2x2 across their width/height. Offset attachment centerlines by
# 1.5mm away from the open quadrant. At that line, the nominal free length
# between the circular outer edges is 2.0mm; rod ends overlap by 0.8mm each.
offset=1.5
edge=math.sqrt(6.0**2-offset**2)
pitch=2*edge+2.0
rod_start=edge-.8
rod_length=pitch-2*rod_start
pads=[]
for row in range(4):
for col in range(4):
p=(source05 if row<2 else source10).copy()
p.translate(V(col*pitch,row*pitch,0))
pads.append(p)
rods=[]
endpoints=[]
for row in range(4):
for col in range(3):
rods.append(Part.makeBox(rod_length,2,2,V(col*pitch+rod_start,row*pitch-offset-1,0)))
endpoints.append((row*4+col,row*4+col+1,'H'))
for row in range(3):
for col in range(4):
rods.append(Part.makeBox(2,rod_length,2,V(col*pitch-offset-1,row*pitch+rod_start,0)))
endpoints.append((row*4+col,(row+1)*4+col,'V'))
if len(rods)!=24 or abs(rod_length-3.6)>1e-7: raise RuntimeError('Incorrect rods')
contacts=[]
for k,(a,b,d) in enumerate(endpoints):
va=rods[k].common(pads[a]).Volume; vb=rods[k].common(pads[b]).Volume
if min(va,vb)<0.2: raise RuntimeError('Weak or absent rod overlap %d: %.4f %.4f'%(k,va,vb))
contacts.append([round(va,5),round(vb,5)])
# Do not allow any bars into the 4.4mm bore. Bars are on the SW sides, so
# they also avoid the NE open quadrant; test that region on each pad.
for i,p in enumerate(pads):
x=(i%4)*pitch; y=(i//4)*pitch
bore=Part.makeCylinder(2.2,2,V(x,y,0))
opening=Part.makeBox(6.1,6.1,2,V(x,y,0))
for rod in rods:
if rod.common(bore).Volume>1e-6: raise RuntimeError('Rod blocks bore %d'%i)
if rod.common(opening).Volume>1e-6: raise RuntimeError('Rod blocks open quadrant %d'%i)
shape=pads[0].multiFuse(pads[1:]+rods).removeSplitter()
if shape.isNull() or not shape.isValid() or len(shape.Solids)!=1: raise RuntimeError('Not one valid solid')
if any(p.cut(shape).Volume>1e-5 for p in pads): raise RuntimeError('Washer removed')
doc=App.newDocument('Washers4x4')
obj=doc.addObject('Part::Feature','Grid');obj.Label='4x4 washers - 2mm links';obj.Shape=shape
doc.recompute()
doc.saveAs(str(OUT/'4x4_混合垫片.FCStd'))
Part.export([obj],str(OUT/'4x4_混合垫片.step'))
mesh=MeshPart.meshFromShape(Shape=shape,LinearDeflection=.03,AngularDeflection=.2,Relative=False)
mesh.write(str(OUT/'4x4_混合垫片.stl'))
App.closeDocument(doc.Name)
check=Mesh.Mesh(str(OUT/'4x4_混合垫片.stl'))
parts=check.getSeparateComponents()
if len(parts)!=1 or not parts[0].isSolid(): raise RuntimeError('STL is not one closed component')
shutil.copyfile(SRC/'EncoderBracket.stl',OUT/'编码器支架_单件.stl')
if hashlib.sha256((SRC/'EncoderBracket.stl').read_bytes()).digest()!=hashlib.sha256((OUT/'编码器支架_单件.stl').read_bytes()).digest():
raise RuntimeError('Bracket copy changed')
b=shape.BoundBox
data={'count':16,'washers_05':8,'washers_10':8,'rod_count':24,
'rod_width_mm':2.0,'rod_height_mm':2.0,'rod_total_length_mm':round(rod_length,6),
'rod_centerline_free_span_mm':2.0,'rod_nominal_end_overlap_mm':.8,
'rod_contact_volumes_mm3':contacts,'hole_opening_check':'passed',
'was_original_washer_geometry_retained':True,'closed_components':len(parts),
'bounds_mm':[b.XMin,b.XMax,b.YMin,b.YMax,b.ZMin,b.ZMax]}
(OUT/'几何检查.json').write_text(json.dumps(data,ensure_ascii=False,indent=2)+'\n',encoding='utf8')
print(json.dumps(data,ensure_ascii=False,indent=2))