news 2026/9/10 10:25:32

OpenMontage 数学动画实战:用 ManimGL 构建向量场可视化(箭头场、粒子流、梯度场与电偶极子)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenMontage 数学动画实战:用 ManimGL 构建向量场可视化(箭头场、粒子流、梯度场与电偶极子)

OpenMontage 数学动画实战:用 ManimGL 构建向量场可视化(箭头场、粒子流、梯度场与电偶极子)

【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage

ManimGL(Grant Sanderson 3Blue1Brown 版,基于 OpenGL 的动画引擎)中向量场可视化的价值在于:用箭头刻画空间每一点的"方向与大小",再用粒子沿着场流动,把抽象的场、梯度、电磁力变成可直接感知的画面。本文以 OpenMontage 仓库内manimgl-best-practices技能包中的向量场参考指南(references/vector_fields.md)为骨架,结合其配套的可运行示例(examples/vector_fields.py)与同目录规则文件逐行展开。读完你将掌握:如何手工铺设彩色箭头场、如何让粒子(含轨迹)沿场运动、如何可视化电荷产生的电场,以及如何用LaggedStartMap与 updater 闭包组织出 3Blue1Brown 风格的场动画,并直接跑通四个成品场景。

1. 这份指南在 OpenMontage 中处于什么位置

OpenMontage 的skills/体系里,"数学动画(Math Animation)"条目同时挂接了三套技能:manimce-best-practicesmanimgl-best-practicesmanim-composer(见 skills/INDEX.md)。其中manimgl-best-practices(仓库路径.agents/skills/manimgl-best-practices/)专门面向3Blue1Brown 版 ManimGL,它由一套分级资料构成:

  • SKILL.md:技能入口,定义了触发条件(用户提到 manimgl / 3b1b manim、代码出现from manimlib import *、执行manimglCLI 等)与 ManimGL / ManimCE 的差异速查表;
  • rules/:按主题拆分的规则文件(场景、mobject、动画、颜色、分组动画、CLI 等);
  • references/:按专题整理的参考指南(vector_fields.mdspring_mass_system.mdthree_d_surfaces.md等);
  • examples/:完整可运行示例,其中vector_fields.py就是本参考指南对应的"配菜",四类场景全部落地可跑。

需要特别留意的是:这套技能针对的是ManimGL(manimlibmanimglCLI),而不是 Manim Community Edition(manimimport /manimCLI)。两者 API 差异巨大(详见 SKILL.md 的差异表):ManimGL 用from manimlib import *InteractiveSceneShowCreationTex(R"...")self.frame;ManimCE 则对应manimSceneCreateMathTex。阅读任何示例前先确认运行环境属于哪一支,这是最常见的第一道坑。

2. 哪些用户需求会命中本参考

references/vector_fields.md开头即列出了本示例要响应的典型提问,这些提问在面向 Agent 的对话里就是触发它工作的 Query:

用户 Query对应解法
"Create a vector field visualization"(创建一个向量场可视化)手写Arrow网格场(旋转场起步)
"Show particles flowing through a field"(粒子在场中流动)add_updater+TracedPath轨迹
"Visualize electric field from charges"(由电荷可视化电场)库仑场逐点求和 + 模长着色
"Animate gradient descent"(动画演示梯度下降)标量场高度着色 + 指向最速上升的梯度箭头
"Show fluid flow"(展示流体流动)涡流场中多粒子流动(ParticleFlow

也就是说,凡是"空间的每个点带一个矢量、想让观众看到它如何驱动运动"的题材,都归它管:旋转场、涡旋、梯度、电场、流场,甚至线性动力系统(dx/dt = Ax)的方向场——后者的进阶实现可在同目录的 eigenvector_flow_field.py 中找到。

3. 3Blue1Brown 风格的场景思考流程

在动手写代码前,参考指南先用一句话提炼了核心概念,这正是 3Blue1Brown 式教学的起点:

核心概念(Core Concept):空间中每一点都有一个矢量,它同时表达方向与大小;粒子沿着场运动,从而"揭示"出场的流动形态。

据此可以归纳出该类场景的通用分镜套路:

  1. 铺基底:先放NumberPlane坐标网格(低透明度),交代"这是二维空间",同时给箭头提供空间参照系;
  2. 摆静态场:在采样网格点上绘制箭头,用箭头长度表方向与大小、用颜色表强度;
  3. 动起来:加入一个或多个粒子,让粒子位置随场的方向每秒更新,必要时叠加轨迹线;
  4. 收尾定格self.wait()留出观察时间,让流动形态被观众看清。

下文第 4、5 节的实现全部沿这条主线展开。

4. 技术实现:三个可移植的构建块

参考指南给出了三种"零高层依赖、直接拼基础 mobject"的写法——它把它们标注为Portable Approach,即只用ArrowVGroupDotinterpolate_color这类稳定对象,不依赖引擎内部的高层VectorField封装,因此跨版本更稳妥(同目录 vector_fields.py 的实际实现也印证了这一点:即便注释提到内置类,代码仍全部手写箭头)。

4.1 手工箭头场(Manual Arrow Field)

这是最通用的一块积木:对网格上的每个采样点计算场的分量,画一条从[x,y,0]指向[x+vx, y+vy, 0]的箭头,并按模长着色。参考指南的核心示例(以旋转场F(x,y)=(-y, x)为例):

arrows = VGroup() for x in np.arange(-3.5, 4, 0.7): for y in np.arange(-2.5, 3, 0.7): vx, vy = -y * 0.15, x * 0.15 # Rotation field arrow = Arrow( start=[x, y, 0], end=[x + vx, y + vy, 0], buff=0, stroke_width=2, ) # Color by magnitude mag = np.sqrt(vx**2 + vy**2) arrow.set_color(interpolate_color(BLUE, YELLOW, mag / 0.5)) arrows.add(arrow)

对照 vector_fields.py 中的SimpleVectorField.construct,实际成品在此基础上还有三处工程细节值得照抄:

  • 空间采样步长:x 向np.arange(-3.5, 4, 0.7)、y 向np.arange(-2.5, 3, 0.7),约 11×8 个采样点,既能看清旋转形态又不至于画面过密;
  • 跳过零矢量if abs(vx) < 0.01 and abs(vy) < 0.01: continue——旋转场在原点恰好为零,跳过可避免在原点画出一个零长度死箭头;
  • 箭头比例Arrow(..., buff=0, stroke_width=2, max_tip_length_to_length_ratio=0.3)buff=0让箭头紧贴起点不悬空,max_tip_length_to_length_ratio限制小箭头箭头的占比。

生成后的展示也有一套固定动作:先用LaggedStartMap(GrowArrow, arrows, lag_ratio=0.02, run_time=2)让箭头逐根"长出来"(见第 6.2 节),self.wait()定格后再叠粒子。

4.2 粒子沿场流动(Particle Following Field)

静态箭头只能表达"瞬间的场",流动的粒子才能表达"场对运动的驱动"。ManimGL 的做法是用updater:每帧回调follow_field(mob, dt),粒子沿field_func给出的方向位移dt的步长,再用TracedPath记录粒子走过的路径:

def follow_field(mob, dt): x, y = mob.get_center()[:2] vx, vy = field_func(x, y) mob.shift(np.array([vx, vy, 0]) * dt) dot.add_updater(follow_field) trail = TracedPath(dot.get_center, stroke_color=RED)

要点解析:

  • mob.get_center()[:2]取粒子当前 x、y 坐标;field_func(x, y)返回该点速度;
  • mob.shift(...) * dt本质是显式欧拉积分:位置 += 速度 × 帧时间。若想积分更稳,可对速度做归一化后再乘固定步长;
  • TracedPath(dot.get_center, ...)会在 dot 每次移动后自动追加路径点,得到一条随时间生长的轨迹线——这是 3Blue1Brown 粒子流视觉的标配;
  • updater 只在对象出现在场景里self.add)之后才被驱动,因此代码顺序必须是先add_updater,再self.add(trail, dot)收尾。

SimpleVectorField中落地的版本把旋转场系数从 0.15 调大到 0.5(vx, vy = -y * 0.5, x * 0.5),让粒子以可见的速度沿圆轨道绕原点旋转,并用trail = TracedPath(dot.get_center, stroke_color=RED, stroke_width=2)画出红色轨迹,最后self.wait(8)让观众完整看到一圈以上的轨道。

4.3 电偶极子场(Electric Dipole Field)

参考指南中的第三块积木是库仑定律场:正电荷在空间某点的贡献为E₁ = r₁/|r₁|³(方向从电荷指向场点),负电荷则反向,总场E = E₁ + E₂

def E_field(pos): r1, r2 = pos - q1_pos, pos - q2_pos d1, d2 = np.linalg.norm(r1), np.linalg.norm(r2) E1 = r1 / d1**3 # From + charge E2 = -r2 / d2**3 # From - charge return E1 + E2

若照抄到ElectricDipole场景并补充到 vector_fields.py 的工程版本,还需处理物理场可视化特有的两个"数值病":

  1. 场点与电荷重合时的奇点:库仑场在电荷处发散。源码在电荷周围挖空:跳过np.linalg.norm(pos - q1_pos) < 0.5(±0.5 半径内不画箭头),并对距离做软化d1 = np.linalg.norm(r1) + 0.1,避免分母过小导致箭头无限长;
  2. 强度跨越多个数量级:先归一化方向(E_norm = E / mag),再截断长度length = min(0.4, mag * 2),即箭头长度有上限 0.4,方向永远正确、长度只近似表达强度。

最终整场布局为:正电荷(红色+)在(-2,0),负电荷(蓝色-)在(2,0),采样x ∈ (-4, 4.5, 0.6)y ∈ (-3, 3.5, 0.6),逐点用interpolate_color(BLUE_E, YELLOW, min(mag*5, 1))上色——靠近电荷处场强、偏黄,远处偏深蓝。这样仅凭颜色就能"读"出偶极子场随距离衰减的特征。

5. 四个场景变体:一文件四场景的总览

vector_fields.py单个文件内定义四个可独立渲染的InteractiveScene子类,参考指南用表格总结了各自使命:

ScenePurpose(用途)
SimpleVectorFieldRotation field with particle(旋转场 + 单粒子绕行演示)
GradientFieldDemoScalar field + gradient arrows(标量场高度 + 梯度箭头)
ParticleFlowMultiple particles in vortex(涡流场中多粒子流动,模拟流体)
ElectricDipoleField from +/- charges(正负点电荷的电场)

每个场景对应的完整实现都已在源码中逐行可查:

  • SimpleVectorField:在NumberPlane(x_range=(-4,4,1), y_range=(-3,3,1))上铺旋转场箭头 →LaggedStartMap(GrowArrow, ...)生长 → 红色粒子沿场做圆周运动并拖出轨迹。
  • GradientFieldDemo:这是参考指南场景表中"标量场 + 梯度箭头"的范例,也在源码中补全。先采样x ∈ (-3.5,4,0.3)y ∈ (-2.5,3,0.3)的点阵,按标量场f(x,y)=-(x²+y²)(原点处为峰)归一化后以interpolate_color(BLUE_E, RED, t)着色铺成"高度热力图";再在粗网格(步长 0.8)上画梯度箭头(gx,gy)=(-2x,-2y),归一化到固定长度0.3,跳过原点 0.3 邻域,让每根白色箭头严格指向最速上升(对这座山而言即指向原点峰顶);最后用Tex(r"\nabla f = (-2x, -2y)")写在左上角并加黑色描边(set_backstroke(BLACK, 3))保证可读性。
  • ParticleFlow:涡流场v = (-y/r, x/r)(切向速度随半径衰减)中放入 15 个黄色粒子,初始等角分布在半径为 2 的圆上(angle = i * TAU / n_particles),各自挂 updater,配蓝色半透明轨迹(stroke_opacity=0.7),self.wait(10)展示整体旋转的涡旋形态。
  • ElectricDipole:即第 4.3 节,结构完整、可独立成段。

6. 关键模式:三个可复用的编码套路

6.1 模式一:按模长着色(Color by Magnitude)

箭头方向给"向哪",颜色给"多强"。参考指南给出了通用写法:

mag = np.linalg.norm([vx, vy]) color = interpolate_color(BLUE, YELLOW, min(mag * scale, 1)) arrow.set_color(color)

interpolate_color(c1, c2, alpha)在两种颜色之间按alpha ∈ [0,1]插值(0 为纯c1,1 为纯c2)。不同场景的"scale"需按场的量级标定:旋转场用mag / 0.5,偶极子场用min(mag * 5, 1),梯度场对点阵用(val + 25) / 25。把强度压到[0,1]是关键一步,否则靠近奇异点处会全部饱和成同一颜色而丢失信息。关于颜色常量的深浅梯度(BLUE_E … BLUE_A等)与更多上色技巧(渐变、RGB/Hex、GLSL 动态着色),可直接查规则文件 rules/colors.md。

6.2 模式二:LaggedStartMap 批量生长箭头

几十上百根箭头若同时"啪"地出现会非常突兀;逐根self.play又太慢。标准解法是LaggedStartMap——把同一个动画构造函数批量映射到 VGroup 的每个成员,并施加一个小的相位延迟:

self.play(LaggedStartMap(GrowArrow, arrows, lag_ratio=0.02, run_time=2))
  • lag_ratio控制相邻动画的错峰比例:约 0.01–0.02 会产生"波纹式"快速展开效果(参考指南中SimpleVectorField用 0.02/2s,偶极子场为画面更密用 0.01/3s);0等价于同时开始,1等价于完全串行(详见 rules/animation-groups.md);
  • 第一个参数是动画类构造器而非动画实例,第二个参数是目标VGroup
  • 同类手法也用于其它创建动画,如LaggedStartMap(ShowCreation, eigenlines, ...)LaggedStartMap(FadeIn, ...)(见 eigenvector_flow_field.py)。创建动画的全套选择(ShowCreation/Write/FadeIn/GrowFromCenter等)可参考 rules/creation-animations.md。

6.3 模式三:循环内 updater 的闭包陷阱(Closure for Updaters in Loops)

多粒子场景几乎必然在for循环里给每个粒子注册 updater,这里藏着 Python 经典的**晚绑定(late binding)**坑:若闭包内直接引用循环变量,所有粒子最终会共享循环结束时的同一个dot。参考指南给出的安全写法是"用一层工厂函数包住状态":

for i in range(n): dot = Dot(...) def make_updater(): # Closure captures current state def update(mob, dt): # use mob, not dot ... return update dot.add_updater(make_updater())

两点必须照做:

  1. 内层update内只用参数mob(它是运行时被驱动的那个对象),绝不引用外层循环变量dot
  2. 工厂函数make_updater()每次循环都被调用,为每个粒子生成独立的update函数,各自捕获自己的状态。

ParticleFlow中的实际形态即此模式的完整案例:每个粒子各自闭包捕获自己的轨迹,vx, vy = -y/r, x/r(r 加了 0.1 防除零)共同构成绕原点的涡旋流。

7. 运行与调试命令

参考指南附带的四个渲染命令(需在vector_fields.py所在目录执行,即技能包的.agents/skills/manimgl-best-practices/examples/下):

manimgl vector_fields.py SimpleVectorField -w manimgl vector_fields.py GradientFieldDemo -w manimgl vector_fields.py ParticleFlow -w manimgl vector_fields.py ElectricDipole -w

在此基础上,结合 rules/cli.md 的完整标志表,实际工作流通常这样推进:

# 1) 低质量快速试看(-l 480p/15fps),确认构图与节奏 manimgl vector_fields.py SimpleVectorField -l # 2) 交互式调试:在指定行数中断并进入 IPython 终端(-se 是 ManimGL 的招牌功能) manimgl vector_fields.py SimpleVectorField -l -se 30 # 3) 只出最后一帧检查静态效果(-s) manimgl vector_fields.py SimpleVectorField -s # 4) 成品渲染:高质量 1080p/60fps 写文件并自动打开(-h -o) manimgl vector_fields.py ElectricDipole -h -o

-se中断进入的交互终端里可直接调用play(...)add(...)remove(...)wait(...),配合checkpoint_paste()把剪贴板中的代码粘回来执行(checkpoint_paste(skip=True)跳过动画秒看结果),实现"改一句、跑一帧"的快速迭代;详见 SKILL.md 的 Interactive Development Workflow 小节。其它常用标志还包括--frame_rate(默认 60)、--format gif--transparent-n N(跳到第 N 个动画)等。若文件里只有一个 Scene 类,manimgl vector_fields.py(不带类名)会自动运行它;多个 Scene 时会弹出菜单选择。

8. 同族扩展:从向量场到特征向量与梯度下降

本参考并不孤立。向量场的视觉语言在技能包内部形成了一条连续的题材谱系:

  • 线性动力系统与特征方向:eigenvector_flow_field.py 用同样的"网格采样 + 箭头 + 模长着色"手法画dx/dt = Ax的方向场,再叠加np.linalg.eig计算出的特征向量直线(TEAL/YELLOW 高亮)与特征值标签,让观众直观看到"沿着特征方向流动保持直线";文件内第二个场景LinearSystemPhasePortrait还把三种矩阵(特征值全正=不稳定结点、全负=稳定结点、一正一负=鞍点)并排成三格相图对比;
  • 梯度下降:参考指南在"User Query Scenarios"里点名了"Animate gradient descent"。GradientFieldDemo(标量场高度热力图 + 指向最速上升的梯度箭头)正是其视觉前奏,技能包中另有examples/gradient_descent_basic.py承接该题材的完整动画;
  • 流场ParticleFlow的涡旋即最小可用的"流体流动"演示,把v = (-y/r, x/r)换成任意速度场即可表达不同的流动形态。

对 Agent 而言,一次典型的调用链是:收到"画一个向量场/展示电荷场/演示流场"类 Query → 命中manimgl-best-practices技能(触发条件见 SKILL.md 头部描述)→ 以references/vector_fields.md作为知识骨架、examples/vector_fields.py作为代码蓝本 → 按 Query 选题(第 2 节映射表)裁剪或改写对应场景 → 用第 7 节的命令渲染与调参。

9. 注意与许可

最后两点需要在使用与二次创作时始终牢记:

  1. 引擎分支:本文所有代码基于 ManimGL(from manimlib import *manimglCLI、InteractiveSceneShowCreation)。若换成 ManimCE,需同步替换 import、CLI、CreateMathTexself.camera.frame等一整组 API(差异对照表见 SKILL.md),不可逐行照搬。
  2. 派生来源许可:本技能包内的示例代码改编自 Grant Sanderson 的 3Blue1Brown 视频仓库,按CC BY-NC-SA 4.0授权:需署名(同时标注 3Blue1Brown 与改编者)、不可商用、衍生作品须采用相同许可(声明见 SKILL.md,完整文本见 LICENSE.txt)。在 OpenMontage 内按此技能生成并用于其自有视频管线时,请遵守该非商用约束。

至此,从空间每一点的一个矢量,到彩色箭头场、流动粒子与轨迹、梯度与电荷场,再到可复用的三类编码模式与调试命令,一套可移植、可扩展、可直接运行的向量场可视化能力就完整落地了。

【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 10:21:50

多模态与视觉大模型开发实战:从架构到微调部署

多模态和视觉大模型&#xff0c;说实话这两年已经快被聊烂了&#xff0c;但真正能在工程里把它们跑起来、调好、部署上线的人&#xff0c;依然稀缺。前两天我把一门标注“完结”的多模态与视觉大模型开发实战课从头到尾刷了一遍&#xff0c;又对照自己手里的几个项目反复验证&a…

作者头像 李华
网站建设 2026/9/10 10:21:28

从LPDDR1到LPDDR6:低功耗内存的带宽演进与信号完整性实战

前阵子公司做新平台预研&#xff0c;我拆了几台不同年代的手机&#xff0c;从能开机就不错的祖传智能机到最新旗舰&#xff0c;旁边同事忽然说了句&#xff1a;这些年 CPU 换了多少代&#xff0c;可要论变化最大又最没存在感的&#xff0c;其实是旁边那颗 LPDDR。确实&#xff…

作者头像 李华
网站建设 2026/9/10 10:19:25

PLC恒压供水PID闭环控制与参数整定实战解析

简介&#xff1a;这是一份基于西门子S7-1200 PLC的恒压供水PID闭环控制工程源码&#xff0c;面向工业自动化工程师、PLC编程学习者与恒压供水系统调试人员。资源围绕PID比例、积分、微分调节原理&#xff0c;给出完整的S7-1200项目及参数配置思路&#xff0c;帮助理解变频驱动水…

作者头像 李华
网站建设 2026/9/10 10:18:51

PaddlePaddle实现NeRF:从多视角照片到文物三维重建的极简指南

简介&#xff1a;面向文物保护与计算机视觉方向的毕业设计、课程作业场景&#xff0c;这份基于飞桨框架实现神经辐射场的文物重建系统源码&#xff0c;可将二维图像集重建为三维模型。项目代码完整、功能验证通过&#xff0c;适合计算机、人工智能、数据科学等相关专业学生用于…

作者头像 李华
网站建设 2026/9/10 10:18:42

CANN/GE图引擎API:获取流ID

GetStreamId 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前…

作者头像 李华