这一页上的每个数字都是从下面那个可下载的 STL 实测出来的,不是从 .scad 源码读的。
这个区别很要紧:源码里有个 assert 保证眼睛走线口至少 20×10 mm,而导出的文件里那个口子根本不存在。
能不能用?能 —— 当试装肋笼用。 单一连通体,201.67 cm³,包围盒 123.73 × 128.00 × 155.87 mm。肋笼、耳罩安装台、后梁接口、眼圈这几处几何都在,尺寸与设计一致。
坏在哪:这个文件里没有眼窝背板。 显示窗、24×14 屏幕沉槽、排线槽、22×12 中央走线口、两个 Ø8 穿线孔,源码里全都写了,STL 里一个都没有。中央整块 Ø48 是空气。这是布尔实测的结论,不是看图猜的 —— 见第五节。
cost 查出来花了约 40 分钟探针。修法已经找到并用布尔验证过 —— 是两个必须同时做的改动,因为最显然的一行改法会让情况更糟。刻意没有落地:眼睛还要不要屏幕是个设计决定。第五节有数字。
另外要知道 frame 这里实测 201.67 cm³,那边标 245.73;quarter2 实测 52.28,那边标 88.98。同一套算法把 cam_frame 的 53.32 完全复现,所以不是算法问题,是那批数字属于另一次导出。第七节有完整对照。
它和仓库里 02-hardware/robot-head/v13-frame-first/stl/frame.stl 逐字节相同 —— md5 都是 33bfc8b6a7a1e676d79ffb7b50de4af9。仓库那份是源,这份是发布副本。
打印前先看 frame 是整只不分块的,本来就不是拿来打的 —— 它用来看整体。真正要打的是 quarter0..3。见第六节。
第一角投影,单位 mm。每个尺寸都由 measure.py 从 STL 实测,没有一个是抄 .scad 常量的。轮廓线是从网格提取的轮廓边加折边。
v13f_drawing.svg · front / right / top + eye socket section A–A
| 项 | 实测 | 源码标称 | 怎么测的 |
|---|---|---|---|
| 总宽 X | 123.73 | 124.0 | STL 包围盒 |
| 总深 Y | 128.00 | — | STL 包围盒 |
| 总高 Z | 155.87 | — | STL 包围盒,z 42.00..197.87 |
| 眼睛通光孔 Ø | 48.1 | 48 EYE_D | y=30 切片,过中心扫描 |
| 眼圈外径 | 59.9 | 60 EYE_D+2·RIM | 同一切片,实体外边界 |
| 眼睛中心高 Z | 104.0 | 104 EYE_ZC | 通光孔竖直跨度 |
| 耳罩安装面 | x = ±57 | — | x=56 有料、x=58 空 |
| 体积 | 201.67 cm³ | — | 全部三角形散度定理 |
全部用正交 z-buffer 直接从 frame.stl 渲染,共用同一比例尺,各视图可以互相比对。不经过 OpenSCAD —— 它们显示的是文件里有什么,不是源码说应该有什么。

Front · 正面(眼睛这一面)

Back · 背面(后梁接口)

Left · 左视(耳罩台)

Right · 右视

Top · 俯视

Bottom · 仰视(立柱开口)

Iso · 前左上

Iso rear · 后右上

Eye · 眼窝方向
看正面图和眼窝图:能透过眼圈直接看到背景。那就是第五节的发现,不用测也看得见。
| 特征 | STL 里有没有 | 说明 |
|---|---|---|
| 拱肋笼(4 个经线平面 = 8 条肋) | OK | 每个视图都看得到 |
| 耳罩安装台,左右各一 | OK | 面在 x=±57,环形(中间通) |
| 后梁接口 | OK | 见背面图 |
| 眼圈 + 镜筒 | OK | 外径 Ø59.9,孔 Ø48.1 |
| 眼圈侧面挂载孔(8× M3 底孔) | OK | 在凸出的 4mm 上,外面够得到 |
| 眼窝背板(Ø54 × 5) | 只剩外环 | 只剩 3mm 宽的环;中央 Ø48 是空的 |
| 显示窗 18×9 | 没有 | 这四项都在缺失的 Ø48 那块里 |
| 屏幕沉槽 24×14×2.6 | 没有 | |
| 中央走线口 22×12 | 没有 | |
| 两个 Ø8 穿线孔 | 没有 | |
| 网格封闭性 | 2 条非流形边 | 没有开放边界;21 290 条边是流形的,2 条是 4 度边。切片软件能处理;OpenSCAD 的 CGAL 拒绝导入。 |
这是点名要查的,所以把整条证据链摆出来,而不是只给个结论。
generate_head_v13.scad 里写着操作者的要求,还有一条 assert 强制它:
EYE_PORT_W = 22; // 宽(>= 20)
EYE_PORT_H = 12; // 高(>= 10)
assert(EYE_PORT_W >= 20 && EYE_PORT_H >= 10,
str("中央走线口 ", EYE_PORT_W, "x", EYE_PORT_H, " 小于操作者要求的 20x10"));
这条 assert 是通过的。它证明的是【常量】合规 —— 完全没有说那个开口能不能活到导出的实体里。
背板占 y 17..22。在其中四个平面上做射线投射占据判定,看眼睛正中那个点(x=0, z=104):
y = 17.3 centre solid? false 背板最前
y = 18.0 centre solid? false 沉槽下方 2.4mm 实心区
y = 19.5 centre solid? false
y = 21.5 centre solid? false
每一层都报出一个 48.00 × 48.00 mm 的洞,面积 1805.28 mm²。π·24² = 1809.6。那就是通光孔,占满了背板本该填的整个空间。
射线投射理论上可能被那两条非流形边骗到,所以把结论用另一条路重新推了一遍:拿重算的实体和背板标称包络求交:
intersection(){ head_frame(); cylinder(d=54, h=5) at y17..22 } = 2.427 cm³
intersection(){ head_frame(); cylinder(d=48, h=5) at y17..22 } = 0.024 cm³
对照(纯几何算)
∅54×5 整块背板 = 11.451 cm³
∅48..∅54 × 5 外环 = 2.403 cm³
∅48×5 中央(屏幕/沉槽/走线口都该在这块里) = 9.048 cm³
2.427 对纯外环的 2.403。背板就是个环,别的什么都没有。本该承载全部开口的那 9.048 cm³ 中央区实测 0.024 cm³ —— 那是 Ø48 圆柱面离散化的残渣。
两个独立的错误叠在一起。
module eye_cut() {
y1 = EYE_BP_Y; // 背板前表面 17 —— 往前 4mm 就收
translate([0, y1, EYE_ZC]) rotate([-90,0,0])
cylinder(d = EYE_D, h = (HEAD_YC + HEAD_R + 40) - y1);
注释把 y=17 叫作「背板前表面」。它不是 —— eye_backplate() 放在 y=17 然后往 +y 拉伸 5mm,所以 17 是板开始的地方。从 17 往前挖 Ø48,正好把板的整个中央挖掉。
module eye_barrel() { // ∅60 × 23 实心圆柱,y 17..40
translate([0, HEAD_YC + HEAD_R - EYE_DEPTH - 10, EYE_ZC]) rotate([-90, 0, 0])
cylinder(d = EYE_D + 2*EYE_RIM, h = EYE_DEPTH + 12, $fn = 96);
}
module eye_solid() { union() { eye_ring(); eye_barrel(); eye_backplate(); } }
eye_backplate() 仔细地把窗、槽、口、孔都 difference 掉了 —— 然后 union() 往上直接叠了一根横跨 y 17..40 的 Ø60 实心柱。所有开口在 head_frame() 还没跑的时候就被填回去了。
错误 A 看着像一行就能修:把通光孔起点从 EYE_BP_Y 挪到 EYE_BP_Y + BP_T。这个改法做出来测了:
中央 ∅48×5 区域内的料
修前 0.024 cm³ ← 等于没有背板
一行改法之后 9.046 cm³ ← 等于实心一块,开口还是没有
理论满料 9.048 cm³
板回来了,开口一个都还是没有,因为错误 B 没动:实心镜筒还在填。对一块职责就是过线的板来说,从「没有板」变成「一块没有孔的板」不算改进。
两个错误必须一起改:挪通光孔起点,同时把背板的开口从 eye_backplate() 里提成独立 module,好在 head_frame() 的 union 之后再减 —— 那时实心镜筒已经填不掉它们了。做出来测了:
中央 ∅48×5 区域内的料
修前 0.024 cm³ ← 没有背板
只改通光孔起点 9.046 cm³ ← 实心一块,开口还是没有
两个一起改 5.798 cm³ ← 背板回来了,开口也在
校验
开口实体总体积(单独导出) 3.337 cm³
9.048 − 3.337 = 5.711 cm³ 对 5.798,差 0.087 —— 排线槽有一段伸到 ∅48 之外,不计入中央区
跑测过 这个修法是布尔验证过的,不是讲道理讲出来的。但刻意没有落到仓库里:眼睛到底还要不要屏幕是个设计决定,而这一页发布的 STL 就是当前磁盘上的那一份。
frame 是整只,看用的。真正打印的是四块,切在 45°/135°/225°/315°,这样没有一条缝落在接口上 —— 耳罩台在 0° 和 180°,眼睛在 90°,后梁在 270°。
分缝图:45/135/225/315 刻意避开全部四个接口

quarter0 · 55.66 cm³ · 眼睛

quarter1 · 43.74 cm³ · 左耳罩台

quarter2 · 52.28 cm³ · 后梁板

quarter3 · 44.51 cm³ · 右耳罩台

cam_frame_fitted · 78.22 cm³ · 相机框(领结)。出线口是 24×12 的封闭通孔,不是外部可见的缺口 —— 翅膀为此做成中脊 30 / 上下缘 12,前后各留 3 mm 壁。与骨架+皮肤干涉实测 0.0000。打印用 2 层墙 + 10% 填充。

core_link · 22.13 cm³ · 内部承力链,整个头挂在它上面
它带眼睛,是最复杂的一块。先打它,在给另外三块投料之前先验证销孔配合。
另外:quarter0 正是第五节那个背板缺陷影响到的件。如果修复落地,要重打的就是它。
单边配合 0.175 mm。太紧就调大 PIN_FIT 重打一块。别硬敲。
每条缝 3 处。先插销对齐,再拧 M3。热熔螺母装在「结束面」那一侧。螺丝从加厚垫的远端面进刀 —— 那一面朝着笼子的空当,螺丝刀伸得进去。最后一条缝自然闭合。
core_link 是承力链:相机 → 云台耳板 → 后横梁 → 后盖内侧。整个头挂在它上面,所以它要在任何造型件之前装。
它能抓住渲染图看不出的悬空碎片:一块 0.16 cm³ 悬在空中的销,和长在本体上的销,在图上一模一样。
有必要直说:check_parts.py 查的是连通性。它抓不到缺失的背板,因为一个圆环是完美连通的。连通和完整是两个问题。
旧的 头部 V13 · 定稿 页上列了各件体积。对当前磁盘上的文件测同一个量,复现不出来。
| 件 | 现在实测 | 旧页面 | 差 |
|---|---|---|---|
| frame | 201.67 | 245.73 | −44.06 |
| quarter0 | 55.66 | 63.07 | −7.41 |
| quarter1 | 43.74 | 43.84 | −0.10 |
| quarter2 | 52.28 | 88.98 | −36.70 |
| quarter3 | 44.51 | 44.60 | −0.09 |
| cam_frame | 53.32 | 53.32 | 0.00 |
| core_link | 22.13 | 22.45 | −0.32 |
cam_frame 完全复现,说明测法没问题。frame 和 quarter2 差了几十 cm³,那是另一个模型,不是舍入。旧页面的体积那一列按过期处理;磁盘上的文件才是这一页测的东西。
另外 quarter0..3 合计 196.19,frame 是 201.67,差 5.48 cm³。方向和量级都符合拼缝间隙加销孔的预期,所以四块和整只是自洽的。
| 结论 | 证据等级 | 出处 |
|---|---|---|
| 包围盒、体积、三角数 | 跑测过 | measure.py · stlview.volume_cm3 |
| 通光孔 Ø48.1、眼圈 Ø59.9、中心 z=104.0 | 跑测过 | measure.py · 射线投射占据图 |
| 背板中央四个平面全空 | 跑测过 | inspect_eye_port.py |
| 外环 2.427 cm³、中央 0.024 cm³ | 跑测过 | OpenSCAD 布尔,从源码重算 |
| 一行改法得到 9.046 cm³ 实心 | 跑测过 | 对改过的 .scad 副本做 OpenSCAD 布尔 |
| 两个改法一起得到 5.798 cm³ 带孔背板 | 跑测过 | 对改过的副本做 OpenSCAD 布尔;开口单独导出交叉校验 |
| 修法没有落到仓库里 | 刻意如此 | 要先做设计决定;发布的 STL 就是磁盘上那份 |
| 2 条非流形边,无开放边界 | 跑测过 | 合并顶点后统计边的度数 |
| 渲染图与工程图 | 跑测过 | stlview.py · render_views.py · drawing.py |
全部脚本在 02-hardware/robot-head/v13-frame-first/。它们只读 STL 文件,别的都不读 —— 这正是关键:一个去读 .scad 的脚本会报告走线口是存在的。
check_parts.py 旁边加一个完整性探针。背板整个没了,连通性检查照样放行;得有东西去断言点名的特征真的存在。PART 默认值是 "skeleton",但 skeleton module 和 PART=="skeleton" 分支都已经不存在了 —— 这个件现在叫 frame / head_frame()。render_v13.sh 还在给五条渲染传 PART="skeleton",node_beam / node_pod / ko_rel 也还在调 skeleton()。
2026-09-09 对 2026-08-24 05:16 的 stl/frame.stl 实测,md5 33bfc8b6a7a1e676d79ffb7b50de4af9。
渲染图和工程图都由同一个文件生成;除引用的代码片段外,这一页没有任何内容取自 .scad 源码。