从算法到固件,做一个声音响应的 LED 阵列

一个硬件项目把向日葵的螺旋排布做成了会跟着音乐动的 LED 阵列,八十九颗可寻址灯珠,一颗一格。它适合当教程读,是因为每一步的材料都给了,算法几行,建模一段,固件一小块,踩过的坑也写了。整条流程是先生成点阵,再用点阵切出实体,把灯和控制器装进去,最后写音频分析和图案。

先用几行代码生成图案

螺旋排布的核心是黄金角。取一条半径上的若干点,每个点按序号乘以黄金角旋转,半径按比例拉开,就得到一个向日葵式的点云。

float a = i * 1.6180339887;
float distance = f * outerRadius;
float x = -cos(a * TWO_PI) * distance;
float y = sin(a * TWO_PI) * distance;

对这个点云做 Voronoi 分割,每格放一颗灯,格子之间留出壁厚。这一步全在代码里,改格子数、改内外圈就能换形状,比手画版快得多,也为后面的建模准备好了边界数据。

从点云到能打印的实体

把格子边界导出,用 CadQuery 这类参数化建模库生成实体。顺序是这样的。

  1. 合并所有格子得到整体。
  2. 从整体里切掉每个格子的收缩体,留下带墙的外壳。
  3. 在每格中心切一个灯珠大小的孔。
  4. 按四块拆分整体,每块能放进打印床。

导出 STEP 文件后在 FreeCAD 里后处理,加螺丝孔,再做一个贴纸用的薄面板。打印件配半透明纸做漫射,桑树皮的纤维在光下比塑料扩散片好看。

装配与第一次点亮

难点在焊接。可寻址灯珠的焊盘在灯珠底下,要用加热焊盘的方式焊,拿电烙铁逐个烫很折磨人。第一版把灯珠焊在洞洞板上,控制器是一块 STM32 开发板,装进 3D 打印外壳。问题有两个。洞洞板线路简陋,盒子一动灯就闪。噪声来自硬件,固件怎么过滤都救不回来。第二版改成自己做印刷电路板做背板,控制器、麦克风和电平转换一起上板,闪的问题消失。

固件的两块内容

灯的部分用 SPI 驱动。实现里有一个小框架,把每颗灯的位置查表成单位圆上的坐标,之后写图案就像写着色器。

音频部分靠控制器自带的浮点和 DSP 指令跑傅里叶变换。麦克风选 INMP441 这类数字 I2S 型号,直接出数字信号,没有模拟噪声。再用 CMSIS 库做自动增益和分频段能量,得到驱动图案的基础量。第一版是大量全局定时器和状态的乱账,能跑,后续改动都靠它。

第二版改了什么

第一版用四块不同形状的打印件拼圆。第二版改用印刷电路板做背板,取五重对称,一次下单五块相同板,拼成一圈。好处是装配整齐,代价是手工焊灯珠更难点。最终版换成带无线的 ESP32,固件用 Rust 重写。控制器起一个本地网页,现场的人可以上传自己写的草图和小游戏,后端只是一个很小的 WebAssembly 运行时。

做之前想清楚的三件事

灯珠选型决定装配方式。手工焊要选焊盘外露的型号,否则按第一版的经验会返工。漫射材料的耐久性比外观更重要,纸好看但破了难补,后面考虑用透明板材保护。噪声在硬件层面解决,布线、电源和接口选型比固件滤波更值得花时间。

常见问题

一定要用参数化建模吗?

格子数上百之后,手绘不现实。用代码生成边界再拉伸切割,改参数就能重新出图,这一步省下的时间最多。

没有 DSP 指令的控制器能做吗?

能,把采样和频谱分析放到上一级或者降低帧率。音频响应的重点是分频段能量,不是算法精度。

本地网页上传草图安全吗?

只在内网开放,别暴露到公网。接口最小化,上传的内容只当数据处理,不执行任意代码。