叶序
叶序是向日葵中心那圈双螺旋图案,代码出奇地短。作者先把它画出来、用 Voronoi 剖分成格子,再把 89 颗可寻址 LED 装进每格里,做成一件会随声音发光的实体雕塑,记录了从草图到 PCB 的两版折腾。
中文
复制

我一直着迷于用代码去理解自然界中出现的图案,与宇宙中固有的涌现模式交流。
叶序就是其中一种图案,即出现在向日葵、多肉植物和其他植物中心的扩张双螺旋形状。它的代码出奇地简单:
// Inner and outer are throw-away,
// just for spacing the cells that are used
float[][] points = new float[numCells + numOuter + numInner][2];
float outerRadius = width / 2;
final int total = numCells + numInner + numOuter;
for (int i = 0; i < total; i++)
{
float f = i / float(total);
float a = i * 1.6180339887;
float distance = f * outerRadius;
float x = -cos(a * TWO_PI) * distance;
float y = sin(a * TWO_PI) * distance;
points[i][0] = x;
points[i][1] = y;
}
```python
在一个圆的径向线上取若干个等距的点,再让每个点按黄金比例的递增倍数依次旋转。
You get a neat point cloud out of this that already looks very much like a sunflower. Voronoi tessellating it looks quite interesting and seed-pod-like:

*The shape emerges when plotting the points*

*Voronoi tessellation result*
这样试了一阵之后我冒出个想法:如果把它做成实物,每个格子里放一颗可寻址 RGB LED 会怎样?一种细胞状的 LED 矩阵。我高中时做过一块矩形 neopixel 矩阵,但一直在等一个更有意思的形状做下一块。
## 从数字到实体
我把 processing 草图里的格子边缘数据导出,再用 CadQuery 库导入到一个 python 草图里。
Wrote some code to build physical geometry out of the data:
```python
# Create the negative space of a single cell
# shrunk with a negative offset-2d
cell_shape = (cq.Workplane("XY")
.polyline(vertices)
.close()
.offset2D(-wall_thickness/2, kind='intersection')
.extrude(total_height)
.translate((0, 0, base_height)))
# Cut it out of the total to create a shell with walls
total_shape = total_shape.cut(cell_shape)
# Cut the shape of the hole to fit the LED in the center of the cell
total_shape = total_shape.cut(
led_hole.rotate((0, 0, 0), (0, 0, 1), math.degrees(angle))
.translate((centerX, centerY, 0)))
这段代码先构建出所有合并 voronoi 单元的总体积,再从中减去每个单独单元收缩后的体积,然后在每个单元上挖出一个 LED 大小的孔。对所有单元都这样做,就得到了下面这个黄色形状。此时我还把几何体分成了四个大致相等的象限,每个象限都能放进我的 3D 打印机打印床。
我导出了 STEP 文件,并在 FreeCAD 中做了一些后处理,加上螺丝孔,还构建了一个薄版本,充当带有下方纸张的“面板”。我把纸剪下来粘到薄版本的底部,并用螺丝孔把它拧到单元壁上。

每个象限的 CadQuery 模型

面板模型,在 FreeCAD 中从 CadQuery 输出转换而来
我 3D 打印了这些形状,并找到了一些半透明纸张,它能很好地漫射光线,光线透过时看起来有机又俏皮。我很喜欢这种桑皮纸的外观,有着如此美丽的有机纤维。

焊接 89 个 LED 有点乏味。但我进入了心流状态,它变得令人放松。我把它接到手边一块闲置的 STM32 blackpill 板上,并用 SPI 写了一个小小的 neopixel 驱动。用一些基本测试图案就已经很酷了。
我构建了一个简单的 sketch 框架,让我能为这些 LED 编写 processing 风格的“shader”代码。使用一个查找表,把每个 LED 的浮点位置都当作位于单位圆上,我就能轻松写出像下面这样的代码,感觉就像片段着色器:
void radial_spirals(LEDBuffer leds) {
for (int i = 0; i < NUM_LEDS; i++) { // iterate every cell
// Get LED position from LUT
float x = led_positions[i][0];
float y = led_positions[i][1];
// Polar coordinates
float r = sqrtf(x*x + y*y);
float theta = atan2f(y, x); // Angle in radians [-π, π]
// Spiral pattern: combine angle and radius
float spiral = sinf(theta * 2.0f + r * 5.0f - seconds * 2.0f);
float brightness = (spiral + 1.0f) * 0.5f;
uint8_t level = (uint8_t)(brightness * 255.0f);
leds[i] = rgb(level, 0, 0);
}
}
相当巧妙。
我很快就找到了自己最喜欢的那种径向图案:一条脉动的正弦波,从中心向外调制亮度。后来做音频响应草图时,它成了主要视觉效果的实现方式。
加入音频
如果它能随着房间里的声音动态变化,而不是只播放预设图案,岂不是更酷?我希望它看起来是活的、有反应的。
驱动它的 STM32 板子支持浮点运算,也有 ARM DSP 指令,所以我可以跑傅里叶变换、做音频分析。我在面包板电路上接了一个数字 I2S 麦克风 INMP441。

它输出的是数字信号,接线简单,也不用担心模拟噪声。ARM CMSIS 库正好用来处理音频信号、做一些分析。加上自动增益控制和多频段拆分,可以分析不同频率上的能量,我就有了不错的基础,能写出对音频做出动态响应的代码。
经过大量调参,再加上一份非常混乱、塞满全局定时器和杂乱状态的草图,最终做出了这个音频响应程序,它至今仍是板子上运行的基础。听着 Jon Hopkins 的 Neon Pattern Drum,它像是活了过来。
收尾
只剩几个细节了。它需要一个更结实的安装面。我找了一块竹制砧板,铣成圆形;天然竹木的纹理和有机纸张的质感非常搭。

我把控制 MCU 和麦克风焊到一块洞洞板上,塞进一个 3D 打印的盒子里,上面装了一个吉他踏板开关。但电子部分很粗糙,容易受噪声影响,盒子一动 LED 就会闪。我决定学着做 PCB,换成一块正经的板子,上面装 blackpill、麦克风和按钮,还留了合适的位置放逻辑电平转换器,让单片机输出干净的 5V 信号。效果不错,做 PCB 也比我想的容易一些,至少这个复杂度是这样。

初版外壳,里面藏着一块乱糟糟的洞洞板

我设计的空白 PCB,还没焊上任何元件
做第二版
2026 年跨年派对上我把它带去了,大家似乎挺喜欢;有几个朋友还说想要一个。我于是动了做第二版的念头,希望更好组装、也更结实。刚学完 PCB 设计,我就决定试试把固定 LED 的 3D 打印板换成 PCB 背板。我微调了外形,改成五重对称——PCB 一次下单就是 5 片,与其 3D 打印四种不同的零件,不如订 5 片相同的 PCB,拼成一个环。

结果发现,用烙铁手工焊 neopixel LED 简直是噩梦升级版:单个 neopixel 的焊盘在元件底下,本来就是给加热整块 PCB 的回流焊准备的,不是给烙铁用的。项目就此搁置了一阵。
2026 年夏天,我去 Recurse Center 参加编程驻留,把项目零件也带上了。在那儿认识的一位朋友用热风焊台比我熟练得多,我们总算把所有焊接做完了。我又设计并 3D 打印了新的单元壁,可以卡在 PCB 上,再用螺丝把它们拧在一起。

在 Recurse 这一期结束前做完新版本后,我决定把它留在 Recurse Center 当装置。我换成了 ESP32,这样它能接入那里的 wifi,固件也用 Rust 重写了一遍。控制器会跑一个本地网站,RC 的人可以通过一个很小的 webassembly 后端和极简 API往上面传 sketch 和游戏。
接下来做什么?
这个项目我到现在也不算完全满意。
喜欢它的人不少,所以我很想多做几个——哪怕不对外卖,至少能送给朋友。但制作过程并不理想:两次焊接都很折磨人,原因还各不相同。贴纸的步骤很麻烦,纸本身又脆,一旦破损就很难修复。我有些改进思路值得一试:换用更容易手工焊接的 LED、走 PCB 组装、试试加一层透明亚克力保护纸张、用磁铁固定纸面板……未来要试的东西太多了。
眼下这个项目该歇一歇了,等时机合适,就上 version 3!