
第一次给学生讲傅里叶变换的时候我的PPT翻到第三页明显感觉到整个教室的空气凝住了。公式还是那个公式但缺了“过程”——看不见频率分量是怎么叠加进去的也看不见不同正弦波是如何层层逼近一个方波的。后来我找到了一种几乎能解决这个痛点的办法用 Python 的 manim 库把数学过程变成动画让函数曲线在坐标系里生长让向量跟着角度旋转让不等式区域一块块填色。这篇文章就是这个系列的第一篇我会从最基本的概念讲起不预设你已经会写 Python只要知道最基本的函数和类就够用。读完你不仅能理解 manim 的核心思路还能跑出第一个属于自己的数学动画如果你是教学者、科普作者或者单纯对“数学可视化”感兴趣那这系列内容应该能省你不少自己摸索的时间。1. 它解决的不只是“画图”而是“过程可视化”1.1 静态公式和动态演示之间差的是“叙事”大多数数学可视化工具擅长把一张图渲染得很漂亮坐标轴、曲线、散点都排得整整齐齐。但数学真正难讲的地方从来不是那个“最终图像”而是一个结果如何一步步变成另一个结果。比如说椭圆是怎么从圆拉伸出来的两个函数相乘之后的图像如果拖动其中一个参数形状会怎么连续变化这些内容如果只用静态图你得放好几张图来回对比学生脑子里的补间动画还得靠想象。manim 天生就是为这个过程设计的它把时间轴上的一系列状态拆成“场景帧”让对象可以创建、淡入、移动、变色、变形最后还能把整个过程渲染成视频。这和我用 PPT 一帧一帧手动做动画相比效率高太多了改一次数值就能重新渲染整段过程。1.2 manim 的出身与两个版本的“江湖”manim 的全称是 Mathematical Animation Engine最早是 3Blue1Brown 的 Grant Sanderson 为了做 YouTube 数学视频开发的一套 Python 脚本工具。你看到的很多“消失的曲线”“旋转的向量”“神经网络可视化”背后都是同一套逻辑用代码描述数学对象用动画函数驱动变化。现在社区里其实有两个分支在互相影响一个是 3b1b 维护的原版 manim基本围绕他自己的视频需求在更新另一个是 manim Community Edition简称 manim CE它更稳定、文档更完整、安装方式也更亲民是普通用户入门时最推荐的选择。所以你在搜索引擎里敲“manim官网中文版”的时候看到的站点未必都是同一套系统有些是 CE 版的文档翻译站有些则是古早版本的第三方教学站。我第一次入手时就被旧版代码误导过照着抄了一个TextMobject结果在新版 CE 里根本跑不通。因此后续文章里默认提到的 manim都是指社区版manim CE如果你看到网上教程用了TextMobject、Brace这些老写法大概率是两三年前的资料需要警惕。2. 想看到画面先过三关Python、FFmpeg 与第一条渲染命令2.1 安装前的环境检查清单manim 本质上是一个通用动画框架最常见的坑往往不是写代码而是运行环境缺系统依赖。我列一下自己实践下来最简洁的安装路径按顺序做基本能避免 80% 的问题。Python 版本建议 3.9 或更高版本。manim CE 对版本有明确要求过老的 Python 会导致依赖包安装失败。FFmpeg这是把连续帧编码为 mp4 的核心工具缺少它时 manim 会在渲染最后一步报错让你误以为是代码写错了。Pango / 文本渲染依赖manim 里的Text对象依赖系统级文本布局引擎缺少后中文或复杂排版可能直接无法显示。LaTeX可选但强烈建议如果以后想用MathTex渲染公式系统里需要 TeX 发行版。Windows 用户通常装 MikTeXmacOS 用户装 MacTeX 的 BasicTeX 版本就够了。安装 manim 本身只需要一行pip install manim如果是国内网络环境pip 明显较慢的时候也可以切换镜像源pip install manim -i https://pypi.tuna.tsinghua.edu.cn/simple安装完验证一下python -c import manim; print(manim.__version__)能正常输出版本号说明 Python 层面的依赖已经就绪。接下来验证 FFmpeg 是否可用ffmpeg -version如果提示找不到命令就需要手动安装或把它加入系统 PATH。我第一次在 Windows 上就是因为没配好 FFmpeg白白排查了半个小时的语法错误。2.2 极简 Demo渲染出第一个圆形环境没问题之后新建一个first_scene.py文件写下第一段最简脚本from manim import Scene, Circle, Create class FirstScene(Scene): def construct(self): circle Circle(colorYELLOW) self.play(Create(circle)) self.wait(2)在终端运行manim -pql first_scene.py FirstScene这里的-p表示播放渲染完的视频-ql表示使用最低质量渲染速度最快。如果一切正常你会看到黄色圆圈从一个点开始沿着轮廓被“画”出来然后保持两秒。从这一小段代码已经能理解 manim 的基本运转方式了定义一个场景类在构造方法里写对象和动画再用命令行指定要渲染哪个文件里的哪个场景。后面所有复杂内容都是在这个框架上增加更多对象和动画种类。3. Scene、Mobject、Animation一句话解释 manim 的“剧本”3.1 Scene会动的那一整台舞台每段视频里的一个完整镜头在 manim 里叫做Scene。它是一个画布也是一个时间线容器。你写一个类继承Scene然后在construct()里安排对象出现和变化的时间顺序。manim 在渲染时会先执行construct()把所有指令转换成一系列画面帧最后交给 FFmpeg 合成视频。换句话说construct()就是你给摄像机下的一整套指令“先升起这一幕再让主角入场等观众看清再切下一个对象”。在基本用法里我们只需要关注三个方法self.add()把对象直接放到画布上没有任何动画瞬间出现。self.play()将一个或多个动画动作播放出来这个过程受时间控制。self.wait()画面保持静默一段时间给人“留白”观看的时间。这三个方法构成了所有 manim 场景的时间骨架。3.2 Mobject画布上所有会动的东西我在实际教学中最常跟学员强调的一句话是先忘掉“画布上的对象”这个概念把一切可视化元素理解为数学对象。manim 里所有能放到场景里的东西都叫MobjectMathematical Object。它不是只能画几何图形文本、公式、坐标轴、函数曲线甚至一张图片都可以被封装成某个 Mobject 的子类。日常工作里最常用的几个基础 Mobject 包括几何形状Circle、Square、Triangle、Line、Arrow文本类Text直接显示文字、MathTex渲染 LaTeX 公式、Tex渲染纯 LaTeX 内容坐标类Axes带刻度的坐标轴系统、NumberPlane网格平面、PolarPlane极坐标平面容器类VGroup/Group可以把多个 Mobject 组合成一个整体统一移动、缩放甚至隐藏每个 Mobject 都自带一套定位与变换方法比如.shift()平移、.to_edge()靠边、.scale()缩放、.rotate()旋转、.set_color()变色。这套方法链的设计思路非常像在操作一个“几何积木”你不需要知道内部坐标怎么算只需要告诉它“往右偏一点”或者“放到左上角”manim 会帮你维护坐标变换关系。3.3 Animation对象如何出现、变化、消失对象摆好了真正让数学“活”起来的是Animation这一类指令。它描述的是“一个对象从状态 A 变成状态 B 的过程”。同样是让一个圆形显示在画布上用self.add(circle)是瞬移式闪出用self.play(Create(circle))则是沿着轮廓画出来用self.play(FadeIn(circle))是透明度从 0 到 1 的淡入。不同的动画给人的情绪是完全不同的这也是 manim 讲故事的精髓。常用动画我先列一个速查表方便你后续看教程时快速对号入座动画类名作用典型场景Create沿着路径逐笔“画出”对象几何图形、函数曲线Write逐字写出文本/公式公式推导开场FadeIn/FadeOut淡入/淡出切换前后画面Transform将一个对象平滑变形成另一个对象圆变椭圆、图像连续过渡Indicate短暂高亮引起注意强调某个关键项MoveToTarget对象移动到预设目标位置布局调整用动画来承载数学含义才是 manim 的核心。比如讲解“变形”的时候你不需要两张图说明前后对比直接让一个圆在播放中变成椭圆观众能直观感觉参数变化带来的几何变化这就是“过程可视化”的威力。3.4 坐标系与排列Mobject 之间如何对齐manim 画布背后有一套自己的坐标系中心是ORIGIN横向是RIGHT/LEFT纵向是UP/DOWN。默认的 14 格宽坐标系里位置可以简写成UP*2 RIGHT*3这样的向量表达式。初学者最容易忽略的是Mobject的定位方法非常依赖“锚点”概念.to_edge(UP)是把对象上边缘贴到画布顶部.to_corner(UR)是放到右上角.next_to(square, RIGHT)是放在另一个对象的右侧。这套排列方法让复杂场景的排版变得异常简单你不需要手算像素坐标只需要描述对象之间的相对位置manim 会自动计算。4. 从点到面做一个“函数曲线生长”动画串起全部基本概念4.1 场景效果与完整代码前面单讲了概念这一小节我们把它们组合起来做一个非常典型的小场景显示坐标轴然后让一条正弦函数曲线从左到右“长”出来最后在画面右上角显示函数表达式。这个例子几乎覆盖了动态数学演示的核心套路也是我给学生讲基本概念时最喜欢用的开场。import numpy as np from manim import ( Scene, Axes, MathTex, Create, Write, UP, RIGHT, YELLOW, WHITE ) class SineIntro(Scene): def construct(self): # 1. 创建坐标轴 axes Axes( x_range[-1, 9, 1], y_range[-1.5, 1.5, 0.5], x_length8, y_length4, axis_config{include_numbers: True}, ) # 2. 创建函数曲线 sine_curve axes.plot( lambda x: np.sin(x), x_range[-0.5, 8], colorYELLOW, ) # 3. 创建右上角的公式标签 formula MathTex(ry \sin(x)) formula.to_corner(UP RIGHT) formula.set_color(WHITE) # 4. 按顺序播放 self.play(Create(axes)) self.play(Create(sine_curve)) self.play(Write(formula)) self.wait(2)运行命令是manim -pql sine_intro.py SineIntro如果 LaTeX 没配置好MathTex这一步可能会报错你可以临时把公式改成Text(y sin(x))跑通流程之后再回头装 TeX 系统。4.2 逐段拆解这里其实埋了三个重要设计第一件事是Axes并不只是“画了个平面”它还是一个能映射函数的好工具。x_range与y_range决定了坐标轴显示的范围x_length与y_length决定图像在画布上的显示长度两者并存时manim 会自动协调缩放让坐标轴和函数曲线在显示上保持正确的比例关系。第二件事是曲线对象直接用了axes.plot()它接收一个 Python 函数作为参数内部自动采样并生成曲线对象这一层抽象把“展开坐标点”的过程完全封装了。第三件事我用Create来播放曲线的诞生过程用Write来逐字显示公式不是为了炫技而是为了让观看者的注意力跟着时间线走先看坐标环境再看数学关系本身最后看符号提炼。这个顺序本身就是一次小型教学导览。4.3add和play的顺序为什么不能乱用很多新手会图省事把公式直接用self.add(formula)放在前面然后再单独播放曲线动画。这会导致整个场景一开始就出现了所有内容观众失去焦点动画的意义就少了一大半。我自己的习惯是除非某个对象需要作为“背景”存在否则一律通过动画指令让它入场。背景对象用add重点对象用play这一“静”一“动”的搭配才符合视觉叙事的节奏。如果你觉得Create(axes)每次都弹出来太普通还可以尝试FadeIn(axes, shiftUP)让坐标轴从上方轻轻落入画面观感会更自然一些。5. 渲染输出与日常调试命令行参数的阅读理解5.1 质量参数和预览参数能省出一倍时间manim 的渲染质量参数是四个字母l、m、h、k分别对应低清、高清、超清和 4K。它们不仅影响分辨率还直接影响渲染耗时。我在改代码阶段永远使用-pql或-pqm只有在最后要分享给别人看时才会用-pqh甚至-pqk跑一次最终版。还有一个特别实用的参数是-s它代表只渲染最后一帧并保存为静态图片适合快速检查对象最终位置合不合适还有--formatgif可以把输出格式改成 GIF方便直接嵌入网页或幻灯片演示时不用播放视频。常用组合我整理成了一份速查命令含义manim -pql file.py SceneName最低质量渲染并自动播放manim -pqm file.py SceneName中等质量渲染manim -s -qk file.py SceneName只渲染最后一帧4K 画质manim -pql --formatgif file.py SceneName输出为 GIF 并播放5.2 输出目录和缓存机制别让旧素材骗了你默认情况下manim 会在当前目录生成一个media文件夹里面按“视频/图像/音轨”和“场景类名”分了好多层。有人会觉得这个目录乱但其实它是故意按“项目-场景”维度隔离的方便同时管理多个实验内容。渲染过的场景会被缓存当代码没变化时再次运行会直接复用缓存。这个机制大部分时候是好事但也带来一个很坑的现象如果你反复运行同一个场景却发现自己改的代码没有生效很可能是 manim 认为缓存没有失效。我遇到过不只一次这种情况最后只能手动删除media文件夹再重跑。建议你在排查“改代码无效果”时先把media/videos/你的场景类名下对应文件删掉或者直接清空media再验证避免在错误的文件上反复纠结。5.3 渲染崩溃时先看错误输出的最后五条初学时最容易在报错面前手足无措。我的调试经验是千万别从头一行行看堆栈直接翻到错误信息最后几行。manim 报错通常会把真正的原因明确写在末尾比如“文件名找不到”“LaTeX 报错”“缺少 FFmpeg”。大部分错误可以归纳成三大类环境依赖缺失、Python 语法错误、类名或文件路径不对。环境问题靠安装依赖解决语法问题看最后一行指向的代码行号类名问题则对照命令行参数是否跟文件里的 class 名称一致。把这三类背下来常规报错基本都能自己搞定。6. 新手翻车复盘四个最容易被搜索词误导的坑6.1 版本不对照着教程抄也跑不通网上的 manim 教程鱼龙混杂很大一部分还停留在 3b1b 原版时代。那时候的写法是用TextMobject显示公式、SimpleLiftingMobject之类现在已经不存在的类而 manim CE 引入了一套更规范的对象体系。新手一旦复制了旧代码很容易得到“ModuleNotFoundError”或者“AttributeError”报错。我的建议是看任何教程前先看它发布时间和文档对应的版本manim 社区版当前文档都标明“Community Edition”而原版的内容只服务于 3Blue1Brown 自己的工作流。搜索“manim官网中文版”时也要留意站点是否把 CE 和旧版混合介绍优先以官方文档为准。6.2 中文文字显示不全或直接消失manim 的英文文本渲染非常顺滑但中文会遇到字体问题。直接用Text(你好数学)有时会出现空白方块原因是系统缺少合适的中文字体manim 找不到能渲染中文字形的字体文件。解决办法是显式指定字体比如Text(你好数学, fontMicrosoft YaHei)在 mac 上可以换成PingFang SC在 Linux 上可以使用Noto Sans CJK SC。另外要特别注意MathTex走的是 LaTeX 渲染不是字体系统如果要在公式中混入中文通常会报错。最稳妥的方式是中文说明用Text数学公式用MathTex两个对象通过.next_to()拼接位置这样既能保证渲染稳定也方便修订措辞。6.3 文件名、类名、命令行参数三者必须对得上manim 命令行里面写的类名必须和文件里定义的 class 名称一字不差且大小写一致。比如文件叫sine_intro.py里面定义的类叫SineIntro命令行要写SineIntro而不是sine_intro。报错信息通常类似“Cannot find scene class”这不算难排查但特别容易在初学者反复改命名时出现。还有一个隐藏小坑一个文件可以定义多个 Scene 类命令行如果不指定类名manim 会给出该文件的全部候选项并询问你要渲染哪一个所以脚本中类名不要随手起重复也不要每次都依赖“常见名匹配”。6.4 缓存导致“改了代码却还看到旧动画”前面提到过media缓存的问题这里单独拉出来说是因为它太有迷惑性。我见过有朋友改完参数后整段动画的输出画面跟改动前几乎一样于是开始怀疑是不是自己的代码没保存反反复复改了一堆无关内容。实际上 manim 的缓存验证不够严格的时候确实会出现旧渲染文件被直接复用的情况。遇到这种状况最粗暴也最有效的办法是先删除media目录再重新运行或者给场景类名加一个版本后缀比如SineIntroV2。这个方法不优雅但很可靠至少能帮你确定到底是不是缓存干扰节省排查时间。就我自己而言从第一次跑通Circle到做出第一个完整教学视频中间反反复复和这些基础概念缠斗了好几个晚上。现在回头看manim 真正的学习曲线不在于 Python 语法有多难而在于你愿不愿意接受一种全新的动画叙事方式场景、对象、动画三者始终在一起你写的每一行代码其实都在安排“数学如何在时间中展开”。这篇先把舞台搭起来下一篇可以进入更实用的动画对象操作比如用.shift()和.next_to()精确调整画面布局再到图形变换与复杂公式动画逐渐做出真正能放进课堂的演示案例。