傅里叶级数那个旋转圆叠出方波的动画,怎么做出来?
2026 年 9 月 20 日 · 极坐标⋅XYZ
傅里叶级数那个旋转圆叠出方波的动画,怎么做出来?
把想要的画面截一张图,丢给会写 Manim 的 AI,让它照着做。我们在极坐标⋅XYZ 的工作区里新建了一个 .py 文件,把一张 909×419 的截图加进 AI 面板,@ 上这个文件,再补一句「实现图片中的傅里叶变换动画。」,40 秒后动画就在预览区里播了。面板上写的是「AI 助手」,我们自己叫它 Manim Coding Agent:它读图、写 Manim 代码、在浏览器里试跑,跑完还会截几帧回头看画面。
这次它写了 100 行 Python,自己看了一遍画面,又改成 104 行,前后调用 5 次模型,扣 107 积分。电脑上没装 Python、LaTeX 或 ffmpeg。图都截自这次操作的录屏。

第一步:打开工作区,新建一个文件
进工作区,第一次会看到「正在唤醒数学引擎」的进度条,页面上写着首次加载约 8 到 15 秒,之后走缓存。这次打开的是一个已有的项目 MathAnimation,顶上标着「本机目录」,里面已经躺着十来个 .py。
点新建文件,弹窗上写着「不写后缀会自动补 .py」,名字填「傅里叶变换」。文件先空着,一行代码都不用写。
第二步:把参考图加进去,再补一句话
AI 输入框上面那段说明写了三种喂料方式:输入 @ 引用项目里的文件;在编辑器里选中代码,按 ⌘I 或右键加入对话;把图片拖进这一栏(或直接粘贴截图)。底下还有一行灰字说清楚了边界:AI 会看到当前项目的 .py 源码(太长时先给清单、要用再读)、你 @ 的文件与选中的片段、你上传的图片、渲染画面的截图;素材文件的内容和其它项目不会发送。
我们做了三件事:打 @ 选中刚建的傅里叶变换.py;把截图加进输入框,上面多出一个「909×419 · 23KB」的附件;然后打了一句话。发出去的原文是:
@傅里叶变换.py 实现图片中的傅里叶变换动画。

参考图是黑底的:顶上一行标题「傅里叶级数:一串旋转圆叠加出方波」,下面一行公式 f(t) = 4/π Σ(k 取奇数) (1/k)sin(2πkt),左下角一串大小不一的圆,一条虚线从圆链末端拉到右边一条带毛刺的方波上。
图里没有写的东西很多:叠几项、圆的半径怎么定、方波画几个周期、动画多长、什么颜色。这些后面都是它自己定的。

思考深度我们没动,输入框底下一直显示「思考:低」。
第三步:发送之后,它做了这些
点发送是录屏的第 30 秒。「过程」一栏最后停在 10 步,摊开是两轮:
- 发送后大约 8 秒,第一版代码就写进文件了,这一段的思考过程 1.98 万字;
- 写入 傅里叶变换.py,100 行;
- 在浏览器里试跑,跑出来的场景叫 FourierSquareWave,154 帧、10.3 秒;
- 检查画面,截了 5 帧;
- 再看一次画面;
- 写入 傅里叶变换.py,这次 104 行;
- 又跑一遍,又检查了一次画面。
第 71 秒收工,从点发送算起 40 秒。右上角的积分从 2,099 掉到 1,992,面板底下写着「调用 5 次 −107 积分」,旁边是「检查点 v2 · 2 版」,上下文用掉 22.6k / 128.0k。
中间那次「再看一次画面」之后它重写了一版,所以检查点有 v1 和 v2 两版,改坏了可以退回去。

第四步:核对它的汇报
它的汇报原文是这样一段:
在「傅里叶变换.py」里做出了参考图那样的动画:顶部标题 + 级数公式 f(t)=4/π·Σ_{k odd}(1/k)sin(2πkt),左侧用 6 个奇次谐波(k=1,3,5,7,9,11)的旋转圆链(半径 4/πk 递减)转动,笔尖的竖直高度正好等于级数部分和;一条细虚线把笔尖连到右侧用该级数实时画出的方波上,画出的波形向右滚动(笔尖固定在左侧),最终完整呈现约 3 个周期、带 Gibbs 抖动边缘的方波。全程约 10 秒,已确认文字、公式、图形都不出框、不重叠。若要更像原图,可把圆链画得再大一点或增加谐波项数。
数学这块对得上。方波的傅里叶级数就是只有奇次正弦项、系数按 1/k 掉,第 k 项写成旋转圆,半径正是 4/πk:第一个圆 1.27,第二个 0.42,正好是三分之一,画面上也是这个比例。把 6 项加起来,最高点算出来是 1.181,比平台高出 18%,这就是吉布斯现象,项数再多也压不下去,只会变窄。平台上那排小毛刺,每半个周期大概 6 个起伏,数量和 6 项也对得上。
「不出框」这句我们也按代码里的参数验了一遍。笔尖固定在 X_PEN = -1.3,每单位时间对应 DX = 2.5 的横向长度,跑满 T_END = 3.0,波形右端落在 6.2;854×480 的画幅右边缘是 7.11,没出去。圆链中心在 -4.9,最大的圆半径 1.27,左端 -6.17,也在框里。这次它说的是实话。
三条建议里,第三条说到点子上了:
让波形从右往左新点出现在最右端(笔尖在右侧绘制)
现在的画法是新点落在笔尖那儿、旧的往右推,所以从左往右看这条波形,时间是倒着走的。参考图本来就是这样,它照做了,这条建议就是把方向掉过来。另外两条是加到 20 项、把圆组放大一些,想要哪条点一下它接着改。
代码开头是一段「=== 可调参数 ===」,注释全是中文:TERMS = [1, 3, 5, 7, 9, 11] 是叠加的奇次谐波,T_END = 3.0 是跟踪时间长度(约等于画面里的方波周期数),TRACK_RUN = 7.0 是跟踪动画的秒数。想加项数、想多画两个周期,不用再跟 AI 说,改这几个数字就行。


顺带说说它写的是什么
Manim 是 3Blue1Brown 做《线性代数的本质》那批视频时写出来的数学动画引擎,名字来自 Mathematical Animation Engine:用 Python 把画面描述出来,渲染成视频。现在分两支,原作者自用的是 ManimGL,社区接手维护的叫 Manim Community,pip install manim 装的就是后者,文档和教程也都围着它转。工作区里跑的是社区版 0.20.1,AI 写出来的就是标准社区版语法。想先弄清楚这个工具本身,看《Manim 是什么?》。
Manim 代码的骨架是固定的:写一个类继承 Scene,在 construct() 方法里把东西一件件摆出来、让它们动。这次渲染出来的场景类就叫 FourierSquareWave,试跑那一行显示的名字来自这里。
画面上每个看得见的东西叫 Mobject,是 mathematical object 的缩写:圆是 Circle、线段是 Line、点是 Dot、文字是 Text、公式是 MathTex。它们大多属于 VMobject,用贝塞尔曲线描述的矢量图形,所以放大不糊,也能做形变。
让它们动起来有两条路。一次性的过渡用 self.play(Create(...))、self.play(Transform(a, b)) 这类动画类。像旋转圆链这种一个量连续变化、别的东西跟着重画的画面,常规写法是 updater:拿一个 ValueTracker 管时间,圆心的位置、虚线的两端、波形新增的点都挂在它后面,每帧重算一次。这份代码里的 SAMPLE_DT = 1 / 100,就是波形每隔多少时间取一个点;TRACK_RUN = 7.0 是这段跟踪动画跑几秒。
渲染档位挂在「运行」按钮旁边,这次显示的是 480p15,状态栏里写全了:854×480@15。154 帧除以 15 正好是 10.3 秒,和试跑那行报的数对得上。本地用命令行的话,对应的是 manim -ql、-qh 那几档。
浏览器里跑的是真 Manim 吗
是同一个 Python 包,没有翻译成 JavaScript 重写一遍。第一次打开时那张「正在唤醒数学引擎」的卡片上标着 WebAssembly、ES Module 动态导入、Canvas 2D、JSPI 栈切换,说的就是这套东西:
- Python 环境是编译成 WebAssembly 的 Pyodide 0.29.4(Python 3.13.2),在标签页里跑 manim 0.20.1,numpy 这些科学库也在里面,所以代码第 2 行的
import numpy as np直接就能用。 - Manim 在本地依赖的那些原生库浏览器里没有,我们逐个换掉了:cairo 的绘图落到 Canvas 2D,字体排版换成 opentype.js,LaTeX 公式交给 MathJax 排成 SVG,导出 MP4 用 ffmpeg.wasm,路径布尔运算用纯 Python 的实现顶掉 skia-pathops。顶上那行公式就是这么排出来的。
- 连着渲染上百帧很容易让标签页弹「页面无响应」,所以逐帧用 JSPI 栈切换让出事件循环,进度能一直刷新;浏览器不支持 JSPI 时退回阻塞渲染。
- 资源全部自托管,不走 CDN,国内打开不用挂代理。首次加载约 8 到 15 秒,之后走缓存。
对写动画的人来说,这一串替换只意味着一件事:你写的是标准 Manim 代码,浏览器里和本地是同一套语法,学到的东西拿到哪儿都还能用。
导出
预览区上面那一排就是导出:「无水印」开关、格式选 MP4、「下载」、「存到 output/」。试跑用的是 854×480、15 帧,要更清楚就调分辨率重渲。不用 AI 的话,写代码、渲染、导出都不扣积分。
花了多少,适合谁用
这次 107 积分,一轮对话,没有来回改。用 AI 要登录,新账号送 1,000 积分。
傅里叶这种题材,自己写 Manim 最费时间的不是数学,是把圆链一个个接起来、让笔尖和曲线同步、让波形按时间滚动这些实现细节。把参考图丢给它,省掉的主要是这一段。交回来的代码还是要自己看,尤其是它说「都对」的那几句,这次我们按参数算了一遍才敢信。
想把这段代码看懂,教程里每一课旁边都是能直接运行的代码。照着截图复原更复杂画面的例子,见《怎么照着一张截图,让 AI 复原一段复杂的 Manim 动画?》;完全不知道提示词怎么写的,可以从《不会写提示词,怎么用 AI 做出第一个 Manim 动画?》里的示例卡片开始。
常见追问(FAQ)
Q:一定要给图吗?只打字行不行? A:行。给图是因为手上正好有一张想复刻的画面,说得比打字快。只写一句话的例子,在上面那篇示例卡片的文章里。
Q:为什么只叠 6 项? A:参考图里没说项数,是它定的。代码第 5 行 TERMS = [1, 3, 5, 7, 9, 11],想要 20 项自己往里加,或者点它的第一条建议。
Q:平台上那些毛刺是画错了吗? A:不是。用有限项的正弦去凑一个有跳变的函数,跳变附近一定会冒出来,第一个尖峰比平台高约 18%,这叫吉布斯现象。加到 20 项、100 项也去不掉,只会变得又窄又密。
Q:它怎么知道要画 3 个周期? A:图里没写。它给的 T_END = 3.0,和参考图上能数出来的周期数是一致的。不满意就改这个数。
Q:生成的代码能拿到本地跑吗? A:能。写的是标准 Manim Community 代码,复制出去、本地装好 Manim 就能渲。
Q:Manim 社区版和 ManimGL 有什么区别?
A:ManimGL 是 3Blue1Brown 自己在用的那支,走 OpenGL 实时预览,接口跟着他做视频的需要改。Manim Community 是社区维护的分支,文档、教程、第三方插件都围着它转,pip install manim 装的就是它。工作区跑的是社区版 0.20.1。
Q:为什么是 854×480、15 帧这么低? A:试跑档位,图快。154 帧除以 15 就是 10.3 秒。要清楚的成片,在渲染那排把分辨率调上去重渲一次就行。
来源:极坐标⋅XYZ(jizuobiao.xyz),2026 年 9 月 20 日更新。文中的行数、帧数、积分和时间点,均取自当次操作的录屏;数学结论是我们自己算过一遍的。