3D 打印一个哨笛笛头,听起来不像什么难事——不就是个带孔的管子嘛。
这次全程用 opencode 辅助。整个过程并不顺利,走了好几次弯路。
一开始我以为很简单
我想做一个哨笛笛头:内径 2.2cm 到 1.8cm 的管子都能装上去,用来 3D 打印。第一反应是让 opencode 用 OpenSCAD 写一个参数化模型。
我给它就只说是哨笛的笛头,模型选择的是 DeepSeek V4 Flash,它好像是觉得哨笛就是个竖直的气槽——上面是吹口,中间一条窄风道往下走,前面开个窗口,下面是刀刃。于是它顺着这个印象,建了圆柱、锥形榫头,然后减去风道、减去窗口、减去音孔。
渲染出来看,外观还挺像回事——一个规规矩矩的圆柱,上面开了个槽。
可一剖开就露馅了。沿着对称面切一刀看内腔剖面:风道、窗口、刀刃一堆几何互相打架,剖面图支离破碎,根本不是做声音的结构。这个"表面光鲜、一剖就漏"的坑,是 AI 建模最容易踩的。
AI 不会告诉你它没把握,它只会很自信地把一个错的模型交给你。外表和三视图都能骗人,只有切开看内腔剖面,才看得出这玩意儿到底能不能出声。
查资料:真正的笛头长什么样
这一版被打回之后,我让 opencode 去搜真正的笛头结构。
它搜到了一个在哨笛圈很有名的资料:Guido Gonzato 的《The "Low-Tech" Whistle》。这份资料最大的价值是给了吹口每个部位的实测比例——风道该多宽、窗口该多大、刀刃上缘该磨成多少度、风道块该放到离刀刃多远。这回让 opencode 按这些比例重新建参,再剖开看,终于"看着像"了。但我还是高兴得太早——"看着像"不等于"是对的"。
关键的转折:方向搞反了
真正的问题在接笛身的那一端。
我一开始只是跟 opencode 说"做一个笛头",它顺着自己脑补的形状,把下端做成了凸出来的锥形榫头(插进管子里去)。但我要的是母口锥形插口——笛头开口朝下,把笛管往上顶进去,卡得越深越紧。这样不用精确量口径,插到哪段卡住就算哪段。
这一步是我中途把 opencode 拽回来的:它做反了。我跟它说"不对,应该是向下开口、越往里越紧,这样不同口径的管子都能往上插进去",它才重新改。
这个"凸还是凹"的差别,我在对话里反复纠结了好几次才真正确认。事后想想,这不能全怪 AI——需求里的方向性信息,AI 很容易理解偏。 尤其是涉及到具体形状的时候,当前这些模型其实未必具有多么好的空间感知。
所以那之后我学到一个习惯:让 AI 建模之前,先把"你要的东西到底是什么形状、怎么跟别的件配合"在对话里用一两句话钉死。 否则模型可能就按自己瞎猜的来,那自然越做越偏。
真正能吹的那一个:拿来一份成品
自己手搓懒得绘制,让模型自己做还终究心里没底。于是让模型直接在 MakerWorld 上找了一个已经验证能吹的成品:
哨笛笛头(D 调,内径 13mm),别自己造了,用这个改。
这是整个流程里最有价值的一步转折——不要从零造,找一个已经被证明能响的,改它。 把下载好的文件放进目录:平口吹嘴(金属打印)D_stls.zip,解压出来是一个 STL。
这时候 opencode 的用处就体现出来了——它能把一个黑盒 STL 读得明明白白:
- 水密性检查:是不是一个封闭的实体,能不能切片。
is_watertight为真。 - 是否多件:里面有没有断开成好几块。它确认是单一实体。
- 逐层切片:沿着轴线每切一刀,读出这一层的内径(音孔)和外径。
按它读出来的结果,这个 Celtic 笛头是:外径 14.5mm、内径 13mm、长 38.7mm 的一根直管,吹口在顶端,下端就是接笛身的那段音孔。
z 内音孔 外径
1mm 13.0 14.5 <- 下端接笛身
6mm 13.0 14.5
12mm 11.9 14.5 <- 进入风道/刀刃区
... ... ...
这就是 AI 相对擅长的事:把做好的东西用数据一层层进行解析。这种"读任意一个 3D 网格并量化它的每个截面"的活,人做很费劲,模型来做就简单易行,几步就完了。
改成 20mm:为什么是"等比放大",而不是"改内径"
我之前一心想把音孔从 13 改成 20mm,直觉上觉得是"把那个孔改大"。但 opencode 读完这个模型的结构后,给了一个更对的方案:
Celtic 这个笛头和笛身,是同一根管子首尾对接——外径和内径都连续,头上没有插口。所以最稳妥、也最不破坏发音的办法,是整个笛头等比放大:
20 / 13 = 1.538
把整个模型放大 1.538 倍。这样风道、窗口、刀刃、音孔全部按同一个比例同步放大,相对关系一点没变——所以它照样能响,只是音孔更大、音更低。这比"单独把某个孔改大"安全得多,因为单独改孔,很容易把风道和刀刃的比例破坏掉,那才是真的会不响。
放大后验证了一下:音孔正好是 20.0mm,外径 22.3mm,长 59.5mm。
水密: True
单一实体: True
音孔: 20.1 mm
外径: 22.3 mm, 长: 59.5 mm, 体积: 4.5 cm³
放大后的笛头,外形跟原版一模一样,只是整体大了一圈:

做成"母口锥形插口"
接下来要做的是母口锥形插口:开口放宽、往里收窄、楔紧。
做法是让 opencode 用 OpenSCAD 对那个放大的 STL 做布尔运算:先把下端音孔临时封成一个实心圆柱,再用一个锥体减去,得到一个"开口大、往深处小"的锥孔。
difference() {
import("celtic_head_20mm.stl");
translate([0,0,-29.7]) cylinder(h=17.5, r1=10.0, r2=9.0); // 开口Ø20 收到 Ø18
}
这中间又踩了个坑。这个 STL 有六万多个面,OpenSCAD 的 CGAL 布尔运算特别慢,第一次用默认超时跑,结果生成到一半被掐断——输出的 STL 是"半截"的:三角面数对不上,末尾还停在半句 facet normal 上。
facet count: 11466 <- 数得出来
outer loop: 11465 <- 差一个,说明最后一面没写完
把超时放宽到好几分钟重跑,这次才完整。算完再切片验证锥度,确实是从开口 20mm 一路收到 18mm:
z 深度 音孔
3mm 19.6
7mm 19.1
11mm 18.9
17mm 18.05
后来又想要一版 19.5 到 19 的,改两个参数重跑一遍就行:
cylinder(h=17.5, r1=9.75, r2=9.5); // 开口Ø19.5 收到 Ø19
这就体现参数化建模 + 脚本的好处:改的不是形状,是三个数字。

打印与自用
最后交付的三样东西是:可切片的 STL、剖切剖面图、尺寸和说明。
打印上有几个点要提醒:
- 层高别超过 0.16mm,因为风道才 1.2mm 宽,层高粗了风道就废了;
- 竖直摆、吹口朝上、插口朝下;
- 用 PETG 或 ABS,别用软料,软料会发闷。
另外很重要的一点:音高不是笛头定的,是笛身长度定的。 笛头只负责发出"哨声"这个机制,具体吹多高,取决于你接的管子多长。20mm 音孔想吹到 G 调低音,管身大约要 43–45cm,然后从管脚每 1mm 修短,用调音器调准。这一点很多人第一次做会搞反——以为是笛头决定音高,其实是管长决定音高。
经验:AI 到底帮上了什么,又帮不上什么
这事做完,我最深的体会不是"AI 多厉害",而是 AI 在一个正确方向上有多能打,在一个错误方向上就有多能跑偏。
帮得上忙的:
- 查资料:它搜到了 Gonzato 那份实测比例的资料,这是我自己翻半天都未必找得到的;
- 读黑盒网格:把一个陌生 STL 的水密性、单件性、每一层的内径外径,用数据量化出来;
- 几何计算:布尔运算、等比缩放、逐层切片验证锥度,这些又枯燥又容易算错的活,它干得很稳;
- 生成交付物:可打印 STL、剖面图、打印说明,一条龙。
帮不上忙、必须人来的:
- 定义需求:"笛头到底要怎么跟笛身配合——插口还是对接、口径多少"这种方向性的事,得自己钉死;
- 判断对不对:第一次那个"剖开就散"的错误结构,AI 自己觉得挺对,只有人和剖面图能判定它错了;
- 拿到被墙挡住的成品:MakerWorld 的反爬加登录,AI 翻不过去,得自己点一下下载;
- 用耳朵验收:风道差零点几毫米,AI 算不出来算不算"好听",只有吹了才知道。
所以这套流程的正确姿势是:我提问题、定标准、给参考、最后拍板;AI 负责查、算、读、改、出图。 顺序不能反。你先给一个模糊指令,它就顺着自己脑补的形状一路狂奔;你越先把"对的东西长什么样"给它,它越接近你要的。
这次下来,哨笛笛头算是打印出来了。回头再看,AI 是很好的辅助执行者,但"什么才算对了"这件事,建议还是得咱们人类来定。