From c0c78251b55ed3b96c01dbed3864f9aba1e5d474 Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Mon, 28 Sep 2026 12:08:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9EIK=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E6=B1=82=E8=A7=A3=E4=B8=8E=E8=BF=90=E5=8A=A8=E6=A8=A1=E6=8B=9F?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E9=9B=86=E6=88=90=E6=AD=A3=E9=80=86?= =?UTF-8?q?=E8=BF=90=E5=8A=A8=E5=AD=A6=E6=9F=A5=E7=9C=8B=E5=99=A8=EF=BC=8C?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=AE=9A=E4=BD=8D=E5=9B=BE=E6=88=AA=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/print_settings.md | 8 + src/build_viewer.py | 6 +- src/viewer_tpl.html | 203 +++++- web/arm-ik-solver.js | 386 +++++++++++ web/ik-glue.js | 404 ++++++++++++ web/index.html | 991 ++++++++++++++++++++++++++++- web/机器人手臂3D查看器.html | 991 ++++++++++++++++++++++++++++- 定位图/IK实时追踪_拖动即算即动.png | Bin 0 -> 170229 bytes 定位图/IK整合_静态模型视图.png | Bin 0 -> 143466 bytes 定位图/IK运动模拟_求解到位.png | Bin 0 -> 170598 bytes 机器人手臂3D查看器.html | 991 ++++++++++++++++++++++++++++- 11 files changed, 3955 insertions(+), 25 deletions(-) create mode 100644 web/arm-ik-solver.js create mode 100644 web/ik-glue.js create mode 100644 定位图/IK实时追踪_拖动即算即动.png create mode 100644 定位图/IK整合_静态模型视图.png create mode 100644 定位图/IK运动模拟_求解到位.png diff --git a/docs/print_settings.md b/docs/print_settings.md index da1a81d..98f9018 100644 --- a/docs/print_settings.md +++ b/docs/print_settings.md @@ -28,3 +28,11 @@ 4. **层高 0.2mm 不变**;P03/P04/P07 受力最狠,墙给 5 层,其余 4 层。 5. BOM 质量按 PLA 折算;换 PETG/ASA 参数不变,层间强度只会更好。 6. 立柱 P01 三段用三角形是唯一例外:轴压方向已知,Z 向斜撑跨层传力比螺旋体更对口。 + +## 3MF 交付(out/parts_3mf/) + +- 12 个 3MF 已按本表参数导出(P01_A 为你手作的参考文件,未覆盖)。网格按模型姿态、板中心放置,缩略图已剔除避免旧预览误导。 +- 生成的 3MF 严格按本表:注意你的参考文件 `layer_height=0.28`、顶壳 4 层,本表要求 **0.20 / 顶 5**,新文件已按参数单执行。 +- **局部实心区(Modifier)未烘进 3MF**:Bambu modifier schema 已确认可用,但各区几何需逐件定义;在 Bambu 里对对象右键→添加修饰器→覆盖 `sparse_infill_density=100%` 即可(区位见上表)。需要我烘进去说一声。 +- 重新生成:`python scratch/make_3mf.py`;闭环校验:`python scratch/verify_3mf.py`(逐项断言本表参数)。 + diff --git a/src/build_viewer.py b/src/build_viewer.py index bc64db3..ade502c 100644 --- a/src/build_viewer.py +++ b/src/build_viewer.py @@ -32,6 +32,8 @@ def rebuild_viewer(): DOCS + "/travel.json") else "[]" screws = io.open(DOCS + "/screws.json", encoding="utf-8").read() if os.path.exists( DOCS + "/screws.json") else "[]" + ik_solver = io.open(WEB + "/arm-ik-solver.js", encoding="utf-8").read() + ik_glue = io.open(WEB + "/ik-glue.js", encoding="utf-8").read() tpl = io.open(SRC + "/viewer_tpl.html", encoding="utf-8").read() print(f" 完成 ({len(data)/1024:.0f} KB) ✓", flush=True) @@ -40,7 +42,9 @@ def rebuild_viewer(): .replace("/*__DATA__*/", data) .replace("/*__SHEETS__*/", sheets) .replace("/*__TRAVEL__*/", trv) - .replace("/*__SCREWS__*/", screws)) + .replace("/*__SCREWS__*/", screws) + .replace("/*__IKSOLVER__*/", ik_solver) + .replace("/*__IKGLUE__*/", ik_glue)) assert "/*__" not in out, [l for l in out.split("\n") if "/*__" in l][:3] dst = WEB + "/机器人手臂3D查看器.html" io.open(dst, "w", encoding="utf-8").write(out) diff --git a/src/viewer_tpl.html b/src/viewer_tpl.html index 5acb1b0..449c8ff 100644 --- a/src/viewer_tpl.html +++ b/src/viewer_tpl.html @@ -9,7 +9,7 @@ html,body{margin:0;height:100%;background:var(--bg);color:var(--tx); font:13px/1.55 "Microsoft YaHei","Segoe UI",system-ui,sans-serif;overflow:hidden; -webkit-user-select:none;user-select:none} -#app{display:grid;grid-template-columns:274px minmax(430px,1fr);grid-template-rows:46px 1fr 26px; +#app{display:grid;grid-template-columns:322px minmax(430px,1fr);grid-template-rows:46px auto 1fr 26px; height:100%;min-width:0;overflow-x:auto} header{grid-column:1/-1;display:flex;align-items:center;gap:8px;padding:0 12px;flex-wrap:nowrap; overflow-x:auto;overflow-y:hidden;background:var(--pan);border-bottom:1px solid var(--line)} @@ -22,7 +22,7 @@ button:hover{border-color:var(--ac);color:#fff} button.on{background:#22406b;border-color:var(--ac)} aside{background:var(--pan);overflow:auto;padding:8px;min-width:0} #left{border-right:1px solid var(--line)} -#app.l{grid-template-columns:274px minmax(430px,1fr)} +#app.l{grid-template-columns:322px minmax(430px,1fr)} #stage{position:relative;overflow:hidden;min-width:0; background:radial-gradient(120% 120% at 50% 0%,#1b2028 0%,#0d0f13 70%)} canvas{display:block;width:100%;height:100%} @@ -30,7 +30,7 @@ footer{grid-column:1/-1;background:var(--pan);border-top:1px solid var(--line); display:flex;align-items:center;gap:14px;padding:0 12px;color:var(--dim);font-size:12px; white-space:nowrap;overflow:hidden} @media (max-width:1150px){ - #app{grid-template-columns:250px minmax(400px,1fr)} + #app{grid-template-columns:300px minmax(400px,1fr)} } @media (max-width:760px){ #app{grid-template-columns:minmax(0,1fr)} @@ -96,6 +96,95 @@ small{color:var(--dim)} #screws tbody tr.scrHl{background:#1d3f74;box-shadow:inset 3px 0 0 var(--warn)} .hudlink{pointer-events:auto;cursor:pointer;color:var(--ac);text-decoration:underline;margin-left:8px} @media (max-width:760px){#bomTab{top:auto;bottom:36px}#bomDock{top:46px;bottom:26px}} + +/* ===================== IK 控制条(新增) ===================== */ +#ikbar{grid-column:1/-1;display:flex;align-items:center;gap:6px;padding:0 10px;height:40px; + background:linear-gradient(180deg,#1c212b,#161922);border-bottom:1px solid var(--line); + overflow-x:auto;overflow-y:hidden;white-space:nowrap;flex-wrap:nowrap} +#ikbar .ik-t{color:var(--ac);font-weight:700;letter-spacing:.8px;flex:none;font-size:11.5px; + border:1px solid var(--ac);border-radius:4px;padding:1px 6px} +#ikbar .lb{color:var(--dim);flex:none;font-size:12px} +#ikbar input[type=number]{width:68px;background:#0d1015;color:var(--tx);border:1px solid var(--line); + border-radius:4px;padding:3px 5px;font:inherit;font-variant-numeric:tabular-nums;flex:none} +#ikbar input[type=number]:focus{outline:none;border-color:var(--ac)} +#ikbar .vsep{width:1px;height:20px;background:var(--line);flex:none;margin:0 3px} +#ikbar .st{color:var(--dim);flex:none;font-size:11.5px;font-variant-numeric:tabular-nums} +#ikbar .sep{color:#3b4453;margin:0 5px} +#ikbar .hint{color:var(--dim);font-size:11px;flex:none} +#ikbar .good{color:var(--ok)} #ikbar .bad{color:#ff6b6b} #ikbar .hi{color:var(--warn)} +#ikSolve{background:#1d3f74;border-color:var(--ac);color:#fff;font-weight:600} +#ikSolve:hover{background:#24508f} + +/* ===================== IK 结果面板(左栏顶部) ===================== */ +#ikpanel{margin:0 0 10px;border:1px solid var(--line);border-radius:6px;background:#141821;padding:7px 8px} +#ikpanel .ikh{display:flex;align-items:center;gap:6px;margin-bottom:5px} +#ikpanel .ikh h3{margin:0;font-size:12px;color:var(--ac);letter-spacing:.3px} +.badge{margin-left:auto;font-size:10.5px;padding:1px 7px;border-radius:9px;border:1px solid;flex:none; + white-space:nowrap} +.badge.wait{color:var(--dim);border-color:var(--line)} +.badge.goodb{color:#0c1a12;background:var(--ok);border-color:var(--ok)} +.badge.warnb{color:#241a06;background:var(--warn);border-color:var(--warn)} +.badge.badb{color:#2a0a0a;background:#ff6b6b;border-color:#ff6b6b} +#ikTab{font-size:11px} +#ikTab td.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap} +#ikTab td.dim{color:var(--dim);white-space:nowrap} +#ikpanel .kv{margin-top:6px} +#ikpanel .kv i{white-space:nowrap} +#ikpanel .kv span,#ikpanel .kv b{overflow-wrap:anywhere;word-break:break-word;min-width:0} +#ikpanel .good{color:var(--ok)} #ikpanel .bad{color:#ff6b6b} +#ikpanel .kv .hi{color:var(--warn);font-variant-numeric:tabular-nums} +#ikAltN{font-size:10.5px;color:var(--dim);margin:7px 0 3px;line-height:1.45;white-space:normal} +.altrow{display:flex;align-items:center;gap:5px;padding:2px 4px;border-radius:4px;cursor:pointer; + font-size:11px;font-variant-numeric:tabular-nums;color:var(--dim)} +.altrow:hover{background:rgba(76,154,255,.16)} +.altrow .ai{color:#5b6478;width:10px;flex:none;text-align:right} +.altrow b{color:var(--ac);width:54px;flex:none;text-align:right;font-weight:500} +.altrow .aq{overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +#ikpanel details{margin-top:8px;border-top:1px solid var(--line);padding-top:6px} +#ikpanel details>summary{cursor:pointer;color:var(--ac);font-size:11.5px;outline:none} +#ikpanel details .doc{margin-top:5px;font-size:11px;color:var(--dim);line-height:1.7;white-space:normal; + -webkit-user-select:text;user-select:text} +#ikpanel details .doc b{color:var(--tx)} +#ikpanel details .doc code{color:var(--warn);font-family:Consolas,"Courier New",monospace;font-size:10.5px} +#ikpanel details .doc ol{margin:3px 0 3px 16px;padding:0} +#ikpanel details .doc li{margin:3px 0} +#ikpanel details .doc hr{border:none;border-top:1px solid var(--line);margin:6px 0} +#ikpanel .src{font-size:10.5px;color:var(--dim);margin-top:5px;white-space:normal;line-height:1.5} +#ikChkBox{margin-top:7px} +#ikChkBox summary{margin-bottom:2px} + +/* ============ 视图模式:三维模型(静态) / 装配图 / 运动模拟 ============ */ +/* 三维模型 与 装配图 下,全部运动学 UI 一律隐藏 */ +body:not(.motion) #ikbar, +body:not(.motion) #ikpanel, +body:not(.motion) #joints, +body:not(.motion) #jointsHead, +body:not(.motion) #footIk{display:none!important} +/* 运动模拟只看运动:左栏去掉零件树与打印清单(其它模式保留) */ +body.motion #treeBlock{display:none!important} + +/* 运动模拟左栏紧凑化:目标一屏放下、不下拉(实测原需下拉 127px) */ +#ikpanel{margin:0 0 6px;padding:5px 8px} +#ikpanel .ikh{margin-bottom:3px} +#ikpanel #ikTab th,#ikpanel #ikTab td{padding:1px 4px} +#ikAltN{margin:5px 0 2px} +.altrow{padding:1px 4px} +#joints .j-box{margin-bottom:4px} +#joints input[type=range]{height:15px} +/* 显式行定位:IK 控制条在「三维模型/装配图」下被移除后, + 若不固定行号,网格自动排布会把页脚挤到 1fr 行并压扁(已实测) */ +#app>header{grid-row:1} +#ikbar{grid-row:2} +#left{grid-row:3} +#stage{grid-row:3} +#app>footer{grid-row:4} +/* BOM 抽屉:运动模拟多一条 IK 控制条,需整体下移 40px */ +#bomDock{top:46px} #bomTab{top:64px} +body.motion #bomDock{top:86px} body.motion #bomTab{top:100px} +@media (max-width:760px){ + #bomTab,body.motion #bomTab{top:auto;bottom:36px} + #bomDock,body.motion #bomDock{top:86px;bottom:26px} +}
@@ -104,7 +193,7 @@ small{color:var(--dim)} 人形机器人手臂 - + @@ -122,18 +211,118 @@ small{color:var(--dim)} +