ae28d55f81
Source: RGB-D -> Dyn-HaMR -> L20 retargeting -> FoundationPose -> reference repair -> SPIDER, documented in docs/PIPELINE_LATEST.md and docs/SETUP_AND_WEIGHTS.md. Adds FoundationPose and nvdiffrast upstream snapshots, requirements/pipeline_venv.txt and the FoundationPose weight manifest/downloader. Assets (Git LFS): weights/ (WiLoR detector, HandFlow denoiser, UniDepth-L), FoundationPose checkpoints, HaMeR checkpoint, Dyn-HaMR HMP model and BMC constraints, L20 URDF/meshes, the 20260915_171525 D405 recording and the two box CADs. MANO models are not redistributed (third_party/hamer/_DATA/data/mano/README.txt). Environments, caches and run outputs excluded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
25 lines
2.3 KiB
Python
25 lines
2.3 KiB
Python
"""Repair CAD T junctions without geometry changes, then decompose without a 20-hull cap."""
|
|
from pathlib import Path
|
|
import numpy as np,trimesh,json,coacd
|
|
ROOT=Path(__file__).resolve().parents[1];O=ROOT/'output/collision_fix_20260915';C=O/'collision_v2';C.mkdir(parents=True,exist_ok=True)
|
|
report={};coacd.set_log_level('warn')
|
|
for name,file in [('upper','上半.stl'),('lower','下半.stl')]:
|
|
old=trimesh.load(ROOT/'docs'/file);m=old.copy();splits=0
|
|
for iteration in range(100):
|
|
count=np.bincount(m.edges_unique_inverse);edges=m.edges_unique[count==1];changed=False;v=m.vertices
|
|
for a,b in edges:
|
|
ab=v[b]-v[a];t=(v-v[a])@ab/(ab@ab);distance=np.linalg.norm(v-(v[a]+t[:,None]*ab),axis=1);inside=np.flatnonzero((t>1e-7)&(t<1-1e-7)&(distance<1e-8))
|
|
if len(inside)==0:continue
|
|
faces=m.faces.tolist();hit=[i for i,f in enumerate(faces) if a in f and b in f]
|
|
if len(hit)!=1:continue
|
|
face=faces.pop(hit[0]);k=next(k for k in range(3) if face[k] in [a,b] and face[(k+1)%3] in [a,b]);a,b,third=face[k],face[(k+1)%3],face[(k+2)%3];inside=sorted(inside,key=lambda x:np.linalg.norm(v[x]-v[a]));chain=[a,*inside,b];faces.extend([[int(x),int(y),int(third)] for x,y in zip(chain[:-1],chain[1:])]);m=trimesh.Trimesh(v.copy(),faces,process=False);splits+=1;changed=True;break
|
|
if not changed:break
|
|
assert np.array_equal(old.vertices,m.vertices) and abs(old.area-m.area)<1e-8 and abs(old.volume-m.volume)<1e-8
|
|
print(name,'closed',m.is_watertight,'splits',splits,'boundary',int(np.sum(np.bincount(m.edges_unique_inverse)==1)),flush=True)
|
|
assert m.is_watertight and m.is_winding_consistent
|
|
m.export(C/(name+'_closed.ply'))
|
|
parts=coacd.run_coacd(coacd.Mesh(m.vertices,m.faces),threshold=.03,max_convex_hull=96,preprocess_mode='off',resolution=1500,mcts_nodes=10,mcts_iterations=60,mcts_max_depth=3,merge=True,seed=0)
|
|
for i,(v,f) in enumerate(parts):trimesh.Trimesh(v,f,process=False).export(C/f'{name}_{i:03d}.obj')
|
|
report[name]={'parts':len(parts),'watertight':bool(m.is_watertight),'t_junction_splits':splits,'surface_area_change':float(m.area-old.area),'volume_change':float(m.volume-old.volume),'cad_volume':float(m.volume),'convex_sum_volume':float(sum(trimesh.Trimesh(v,f,process=False).volume for v,f in parts)),'normalized_concavity_threshold':.03,'part_cap':96}
|
|
(C/'manifest.json').write_text(json.dumps(report,indent=2));print(name,report[name],flush=True)
|